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  }