github.com/cloudflare/circl@v1.5.0/kem/kyber/kat_test.go (about) 1 package kyber 2 3 // Code to generate the NIST "PQCsignKAT" test vectors. 4 // See PQCsignKAT_sign.c and randombytes.c in the reference implementation. 5 6 import ( 7 "bytes" 8 "crypto/sha256" 9 "fmt" 10 "strings" 11 "testing" 12 13 "github.com/cloudflare/circl/internal/nist" 14 "github.com/cloudflare/circl/kem/schemes" 15 ) 16 17 func TestPQCgenKATKem(t *testing.T) { 18 kats := []struct { 19 name string 20 want string 21 }{ 22 // Computed from reference implementation 23 {"Kyber1024", "89248f2f33f7f4f7051729111f3049c409a933ec904aedadf035f30fa5646cd5"}, 24 {"Kyber768", "a1e122cad3c24bc51622e4c242d8b8acbcd3f618fee4220400605ca8f9ea02c2"}, 25 {"Kyber512", "e9c2bd37133fcb40772f81559f14b1f58dccd1c816701be9ba6214d43baf4547"}, 26 27 // TODO crossreference with standard branch of reference implementation 28 // once they've added the final change: domain separation in K-PKE.KeyGen(). 29 {"ML-KEM-512", "a30184edee53b3b009356e1e31d7f9e93ce82550e3c622d7192e387b0cc84f2e"}, 30 {"ML-KEM-768", "729367b590637f4a93c68d5e4a4d2e2b4454842a52c9eec503e3a0d24cb66471"}, 31 {"ML-KEM-1024", "3fba7327d0320cb6134badf2a1bcb963a5b3c0026c7dece8f00d6a6155e47b33"}, 32 } 33 for _, kat := range kats { 34 t.Run(kat.name, func(t *testing.T) { 35 testPQCgenKATKem(t, kat.name, kat.want) 36 }) 37 } 38 } 39 40 func testPQCgenKATKem(t *testing.T, name, expected string) { 41 scheme := schemes.ByName(name) 42 if scheme == nil { 43 t.Fatal() 44 } 45 46 var seed [48]byte 47 kseed := make([]byte, scheme.SeedSize()) 48 eseed := make([]byte, scheme.EncapsulationSeedSize()) 49 for i := 0; i < 48; i++ { 50 seed[i] = byte(i) 51 } 52 f := sha256.New() 53 g := nist.NewDRBG(&seed) 54 55 // The "standard" branch reference implementation still uses Kyber 56 // as name instead of ML-KEM. 57 fmt.Fprintf(f, "# %s\n\n", strings.ReplaceAll(name, "ML-KEM-", "Kyber")) 58 for i := 0; i < 100; i++ { 59 g.Fill(seed[:]) 60 fmt.Fprintf(f, "count = %d\n", i) 61 fmt.Fprintf(f, "seed = %X\n", seed) 62 g2 := nist.NewDRBG(&seed) 63 64 if strings.HasPrefix(name, "ML-KEM") { 65 // https://github.com/pq-crystals/kyber/commit/830e0ba1a7fdba6cde03f8139b0d41ad2102b860 66 g2.Fill(kseed[:]) 67 } else { 68 // This is not equivalent to g2.Fill(kseed[:]). As the reference 69 // implementation calls randombytes twice generating the keypair, 70 // we have to do that as well. 71 g2.Fill(kseed[:32]) 72 g2.Fill(kseed[32:]) 73 } 74 75 g2.Fill(eseed) 76 pk, sk := scheme.DeriveKeyPair(kseed) 77 ppk, _ := pk.MarshalBinary() 78 psk, _ := sk.MarshalBinary() 79 ct, ss, _ := scheme.EncapsulateDeterministically(pk, eseed) 80 ss2, _ := scheme.Decapsulate(sk, ct) 81 if !bytes.Equal(ss, ss2) { 82 t.Fatal() 83 } 84 fmt.Fprintf(f, "pk = %X\n", ppk) 85 fmt.Fprintf(f, "sk = %X\n", psk) 86 fmt.Fprintf(f, "ct = %X\n", ct) 87 fmt.Fprintf(f, "ss = %X\n\n", ss) 88 } 89 if fmt.Sprintf("%x", f.Sum(nil)) != expected { 90 t.Fatalf("%s %x %s", name, f.Sum(nil), expected) 91 } 92 }