From f99a214d96c14c5c4287a39f99342ae895194cc0 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Thu, 14 Nov 2024 16:00:43 -0500 Subject: [PATCH] crypto/internal/fips140test: add EDDSA ACVP tests This commit adds ACVP test coverage for EDDSA (Ed25519, and HashEd25519/Ed25519ph) for the keyGen, keyVer, sigGen, and sigVer capabilities. Updates #69642 Change-Id: I5122d86180bd4d2f7d94570a6dc939808aa24fc4 Reviewed-on: https://go-review.googlesource.com/c/go/+/621135 Reviewed-by: Roland Shoemaker LUCI-TryBot-Result: Go LUCI Reviewed-by: Filippo Valsorda Auto-Submit: Filippo Valsorda Reviewed-by: Cherry Mui --- .../fips140test/acvp_capabilities.json | 7 +- .../fips140test/acvp_test.config.json | 4 +- src/crypto/internal/fips140test/acvp_test.go | 129 ++++++++++++++++++ 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/src/crypto/internal/fips140test/acvp_capabilities.json b/src/crypto/internal/fips140test/acvp_capabilities.json index 8a4a97758c..368c7809de 100644 --- a/src/crypto/internal/fips140test/acvp_capabilities.json +++ b/src/crypto/internal/fips140test/acvp_capabilities.json @@ -37,5 +37,10 @@ {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-224","derFuncEnabled":false,"entropyInputLen":[192],"nonceLen":[96],"persoStringLen":[192],"additionalInputLen":[0],"returnedBitsLen":224}]}, {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-256","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":256}]}, {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-384","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":384}]}, - {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-512","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":512}]} + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-512","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":512}]}, + + {"algorithm":"EDDSA","mode":"keyGen","revision":"1.0","curve":["ED-25519"]}, + {"algorithm":"EDDSA","mode":"keyVer","revision":"1.0","curve":["ED-25519"]}, + {"algorithm":"EDDSA","mode":"sigGen","revision":"1.0","pure":true,"preHash":true,"contextLength":[{"min":0,"max":255,"increment":1}],"curve":["ED-25519"]}, + {"algorithm":"EDDSA","mode":"sigVer","revision":"1.0","pure":true,"preHash":true,"curve":["ED-25519"]} ] diff --git a/src/crypto/internal/fips140test/acvp_test.config.json b/src/crypto/internal/fips140test/acvp_test.config.json index dc4d714f19..2afd457f46 100644 --- a/src/crypto/internal/fips140test/acvp_test.config.json +++ b/src/crypto/internal/fips140test/acvp_test.config.json @@ -27,5 +27,7 @@ {"Wrapper": "go", "In": "vectors/ML-KEM.bz2", "Out": "expected/ML-KEM.bz2"}, - {"Wrapper": "go", "In": "vectors/hmacDRBG.bz2", "Out": "expected/hmacDRBG.bz2"} + {"Wrapper": "go", "In": "vectors/hmacDRBG.bz2", "Out": "expected/hmacDRBG.bz2"}, + + {"Wrapper": "go", "In": "vectors/EDDSA.bz2", "Out": "expected/EDDSA.bz2"} ] \ No newline at end of file diff --git a/src/crypto/internal/fips140test/acvp_test.go b/src/crypto/internal/fips140test/acvp_test.go index 8dedb9a791..b160f60d17 100644 --- a/src/crypto/internal/fips140test/acvp_test.go +++ b/src/crypto/internal/fips140test/acvp_test.go @@ -24,6 +24,8 @@ import ( "crypto/internal/cryptotest" "crypto/internal/fips140" "crypto/internal/fips140/ecdsa" + "crypto/internal/fips140/ed25519" + "crypto/internal/fips140/edwards25519" "crypto/internal/fips140/hmac" "crypto/internal/fips140/mlkem" "crypto/internal/fips140/pbkdf2" @@ -81,6 +83,8 @@ var ( // https://pages.nist.gov/ACVP/draft-celi-acvp-ml-kem.html#section-7.3 // HMAC DRBG algorithm capabilities: // https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.2 + // EDDSA algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-7 //go:embed acvp_capabilities.json capabilitiesJson []byte @@ -142,6 +146,11 @@ var ( "hmacDRBG/SHA3-256": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New256() }), "hmacDRBG/SHA3-384": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New384() }), "hmacDRBG/SHA3-512": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New512() }), + + "EDDSA/keyGen": cmdEddsaKeyGenAft(), + "EDDSA/keyVer": cmdEddsaKeyVerAft(), + "EDDSA/sigGen": cmdEddsaSigGenAftBft(), + "EDDSA/sigVer": cmdEddsaSigVerAft(), } ) @@ -397,6 +406,126 @@ func cmdPbkdf() command { } } +func cmdEddsaKeyGenAft() command { + return command{ + requiredArgs: 1, // Curve name + handler: func(args [][]byte) ([][]byte, error) { + if string(args[0]) != "ED-25519" { + return nil, fmt.Errorf("unsupported EDDSA curve: %q", args[0]) + } + + sk, err := ed25519.GenerateKey() + if err != nil { + return nil, fmt.Errorf("generating EDDSA keypair: %w", err) + } + + // EDDSA/keyGen/AFT responses are d & q, described[0] as: + // d The encoded private key point + // q The encoded public key point + // + // Contrary to the description of a "point", d is the private key + // seed bytes per FIPS.186-5[1] A.2.3. + // + // [0]: https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-9.1 + // [1]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf + return [][]byte{sk.Seed(), sk.PublicKey()}, nil + }, + } +} + +func cmdEddsaKeyVerAft() command { + return command{ + requiredArgs: 2, // Curve name, Q + handler: func(args [][]byte) ([][]byte, error) { + if string(args[0]) != "ED-25519" { + return nil, fmt.Errorf("unsupported EDDSA curve: %q", args[0]) + } + + // Verify the point is on the curve. The higher-level ed25519 API does + // this at signature verification time so we have to use the lower-level + // edwards25519 package to do it here in absence of a signature to verify. + if _, err := new(edwards25519.Point).SetBytes(args[1]); err != nil { + return [][]byte{{0}}, nil + } + + return [][]byte{{1}}, nil + }, + } +} + +func cmdEddsaSigGenAftBft() command { + return command{ + requiredArgs: 5, // Curve name, private key seed, message, prehash, context + handler: func(args [][]byte) ([][]byte, error) { + if string(args[0]) != "ED-25519" { + return nil, fmt.Errorf("unsupported EDDSA curve: %q", args[0]) + } + + sk, err := ed25519.NewPrivateKeyFromSeed(args[1]) + if err != nil { + return nil, fmt.Errorf("error creating private key: %w", err) + } + msg := args[2] + prehash := args[3] + context := string(args[4]) + + var sig []byte + if prehash[0] == 1 { + h := sha512.New() + h.Write(msg) + msg = h.Sum(nil) + + // With ed25519 the context is only specified for sigGen tests when using prehashing. + // See https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-8.6 + sig, err = ed25519.SignPH(sk, msg, context) + if err != nil { + return nil, fmt.Errorf("error signing message: %w", err) + } + } else { + sig = ed25519.Sign(sk, msg) + } + + return [][]byte{sig}, nil + }, + } +} + +func cmdEddsaSigVerAft() command { + return command{ + requiredArgs: 5, // Curve name, message, public key, signature, prehash + handler: func(args [][]byte) ([][]byte, error) { + if string(args[0]) != "ED-25519" { + return nil, fmt.Errorf("unsupported EDDSA curve: %q", args[0]) + } + + msg := args[1] + pk, err := ed25519.NewPublicKey(args[2]) + if err != nil { + return nil, fmt.Errorf("invalid public key: %w", err) + } + sig := args[3] + prehash := args[4] + + if prehash[0] == 1 { + h := sha512.New() + h.Write(msg) + msg = h.Sum(nil) + // Context is only specified for sigGen, not sigVer. + // See https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-8.6 + err = ed25519.VerifyPH(pk, msg, sig, "") + } else { + err = ed25519.Verify(pk, msg, sig) + } + + if err != nil { + return [][]byte{{0}}, nil + } + + return [][]byte{{1}}, nil + }, + } +} + func lookupHash(name string) (func() fips140.Hash, error) { var h func() fips140.Hash -- 2.51.0