github.com/cloudflare/circl@v1.5.0/ecc/bls12381/hash_test.go (about)

     1  package bls12381
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/cloudflare/circl/ecc/bls12381/ff"
    13  	"github.com/cloudflare/circl/internal/test"
    14  )
    15  
    16  type vectorHash struct {
    17  	SuiteID        string `json:"ciphersuite"`
    18  	CurveName      string `json:"curve"`
    19  	DST            string `json:"dst"`
    20  	IsRandomOracle bool   `json:"randomOracle"`
    21  	Vectors        []struct {
    22  		P   point  `json:"P"`
    23  		Msg string `json:"msg"`
    24  	} `json:"vectors"`
    25  	Field struct {
    26  		M string `json:"m"`
    27  		P string `json:"p"`
    28  	} `json:"field"`
    29  }
    30  
    31  type elm string
    32  
    33  func (e elm) toBytes(t *testing.T) (out []byte) {
    34  	var buf [ff.FpSize]byte
    35  	for _, s := range strings.Split(string(e), ",") {
    36  		x, err := hex.DecodeString(s[2:])
    37  		if err != nil {
    38  			t.Fatal(err)
    39  		}
    40  		copy(buf[ff.FpSize-len(x):ff.FpSize], x)
    41  		out = append(append([]byte{}, buf[:]...), out...)
    42  	}
    43  	return
    44  }
    45  
    46  type point struct {
    47  	X elm `json:"x"`
    48  	Y elm `json:"y"`
    49  }
    50  
    51  func (p point) toBytes(t *testing.T) []byte { return append(p.X.toBytes(t), p.Y.toBytes(t)...) }
    52  
    53  type hasher interface {
    54  	Encode(_, _ []byte)
    55  	Hash(_, _ []byte)
    56  	SetBytes([]byte) error
    57  	IsEqualTo(_ hasher) bool
    58  	IsRTorsion() bool
    59  }
    60  
    61  type g1Hasher struct{ *G1 }
    62  
    63  func (g g1Hasher) IsEqualTo(x hasher) bool { return g.IsEqual(x.(g1Hasher).G1) }
    64  func (g g1Hasher) IsRTorsion() bool        { return g.IsOnG1() }
    65  
    66  type g2Hasher struct{ *G2 }
    67  
    68  func (g g2Hasher) IsEqualTo(x hasher) bool { return g.IsEqual(x.(g2Hasher).G2) }
    69  func (g g2Hasher) IsRTorsion() bool        { return g.IsOnG2() }
    70  
    71  func (v *vectorHash) test(t *testing.T) {
    72  	var got, want hasher
    73  	if v.Field.M == "0x1" {
    74  		got, want = g1Hasher{new(G1)}, g1Hasher{new(G1)}
    75  	} else if v.Field.M == "0x2" {
    76  		got, want = g2Hasher{new(G2)}, g2Hasher{new(G2)}
    77  	}
    78  
    79  	dst := []byte(v.DST)
    80  
    81  	doHash := got.Encode
    82  	if v.IsRandomOracle {
    83  		doHash = got.Hash
    84  	}
    85  
    86  	for i, vi := range v.Vectors {
    87  		input := []byte(vi.Msg)
    88  		doHash(input, dst)
    89  
    90  		err := want.SetBytes(vi.P.toBytes(t))
    91  		test.CheckNoErr(t, err, "bad deserialization")
    92  
    93  		if !got.IsEqualTo(want) || !got.IsRTorsion() {
    94  			test.ReportError(t, got, want, v.SuiteID, i)
    95  		}
    96  	}
    97  }
    98  
    99  func readFile(t *testing.T, fileName string) *vectorHash {
   100  	jsonFile, err := os.Open(fileName)
   101  	if err != nil {
   102  		t.Fatalf("File %v can not be opened. Error: %v", fileName, err)
   103  	}
   104  	defer jsonFile.Close()
   105  	input, err := io.ReadAll(jsonFile)
   106  	if err != nil {
   107  		t.Fatalf("File %v can not be loaded. Error: %v", fileName, err)
   108  	}
   109  	v := new(vectorHash)
   110  	err = json.Unmarshal(input, v)
   111  	if err != nil {
   112  		t.Fatalf("File %v can not be parsed. Error: %v", fileName, err)
   113  	}
   114  	return v
   115  }
   116  
   117  func TestHashVectors(t *testing.T) {
   118  	// Test vectors from draft-irtf-cfrg-hash-to-curve:
   119  	// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11
   120  	//
   121  	// JSON files can be found at:
   122  	// https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/tree/draft-irtf-cfrg-hash-to-curve-10/poc/vectors
   123  
   124  	fileNames, err := filepath.Glob("./testdata/BLS12381*.json")
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  
   129  	for _, fileName := range fileNames {
   130  		v := readFile(t, fileName)
   131  		t.Run(v.SuiteID, v.test)
   132  	}
   133  }