From: Daniel McCarney Date: Thu, 19 Dec 2024 19:02:05 +0000 (-0500) Subject: crypto/internal/fips140test: add KAS-ECC-SSC ACVP tests X-Git-Tag: go1.25rc1~1094 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ee8db080c8ca99bae0288f4cf19110cdfb179e35;p=gostls13.git crypto/internal/fips140test: add KAS-ECC-SSC ACVP tests Adds ACVP test coverage for the Sp800-56Ar3 KAS-ECC-SSC algorithm based on the NIST spec: https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ssc-ecc.html There's no acvp_test.config.json update for this algorithm as one test type type requires random key generation and can't be separated from the test type that doesn't, making it a bad fit for static data testing. Updates #69642 Change-Id: I3b6538fad1c1e5c8b14b638ff3b933f11e98f75a Reviewed-on: https://go-review.googlesource.com/c/go/+/637916 Reviewed-by: Roland Shoemaker LUCI-TryBot-Result: Go LUCI Auto-Submit: Filippo Valsorda Reviewed-by: Filippo Valsorda Reviewed-by: Dmitri Shuralyov --- diff --git a/src/crypto/internal/fips140test/acvp_capabilities.json b/src/crypto/internal/fips140test/acvp_capabilities.json index 02940a2e6f..5b31850087 100644 --- a/src/crypto/internal/fips140test/acvp_capabilities.json +++ b/src/crypto/internal/fips140test/acvp_capabilities.json @@ -63,5 +63,7 @@ {"algorithm":"TLS-v1.2","mode":"KDF","revision":"RFC7627","hashAlg":["SHA2-256","SHA2-384","SHA2-512"]}, {"algorithm":"TLS-v1.3","mode":"KDF","revision":"RFC8446","hmacAlg":["SHA2-256","SHA2-384"],"runningMode":["DHE","PSK","PSK-DHE"]}, - {"algorithm":"kdf-components","mode":"ssh","revision":"1.0","hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512"],"cipher":["AES-128","AES-192","AES-256"]} + {"algorithm":"kdf-components","mode":"ssh","revision":"1.0","hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512"],"cipher":["AES-128","AES-192","AES-256"]}, + + {"algorithm":"KAS-ECC-SSC","revision":"Sp800-56Ar3","scheme":{"ephemeralUnified":{"kasRole":["initiator","responder"]},"staticUnified":{"kasRole":["initiator","responder"]}},"domainParameterGenerationMethods":["P-224","P-256","P-384","P-521"]} ] diff --git a/src/crypto/internal/fips140test/acvp_test.go b/src/crypto/internal/fips140test/acvp_test.go index ded66b79ae..8c51538cab 100644 --- a/src/crypto/internal/fips140test/acvp_test.go +++ b/src/crypto/internal/fips140test/acvp_test.go @@ -26,6 +26,7 @@ import ( "crypto/internal/fips140" "crypto/internal/fips140/aes" "crypto/internal/fips140/aes/gcm" + "crypto/internal/fips140/ecdh" "crypto/internal/fips140/ecdsa" "crypto/internal/fips140/ed25519" "crypto/internal/fips140/edwards25519" @@ -123,6 +124,8 @@ var ( // https://pages.nist.gov/ACVP/draft-hammett-acvp-kdf-tls-v1.3.html#section-7.2 // SSH KDF algorithm capabilities: // https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#section-7.2 + // ECDH algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ssc-ecc.html //go:embed acvp_capabilities.json capabilitiesJson []byte @@ -251,6 +254,11 @@ var ( "SSHKDF/SHA2-384/server": cmdSshKdfAft(func() fips140.Hash { return sha512.New384() }, ssh.ServerKeys), "SSHKDF/SHA2-512/client": cmdSshKdfAft(func() fips140.Hash { return sha512.New() }, ssh.ClientKeys), "SSHKDF/SHA2-512/server": cmdSshKdfAft(func() fips140.Hash { return sha512.New() }, ssh.ServerKeys), + + "ECDH/P-224": cmdEcdhAftVal(ecdh.P224()), + "ECDH/P-256": cmdEcdhAftVal(ecdh.P256()), + "ECDH/P-384": cmdEcdhAftVal(ecdh.P384()), + "ECDH/P-521": cmdEcdhAftVal(ecdh.P521()), } ) @@ -1413,6 +1421,45 @@ func cmdSshKdfAft(hFunc func() fips140.Hash, direction ssh.Direction) command { } } +func cmdEcdhAftVal[P ecdh.Point[P]](curve *ecdh.Curve[P]) command { + return command{ + requiredArgs: 3, // X, Y, private key (empty for Val type tests) + handler: func(args [][]byte) ([][]byte, error) { + peerX := args[0] + peerY := args[1] + rawSk := args[2] + + uncompressedPk := append([]byte{4}, append(peerX, peerY...)...) // 4 for uncompressed point format + pk, err := ecdh.NewPublicKey(curve, uncompressedPk) + if err != nil { + return nil, fmt.Errorf("invalid peer public key x,y: %v", err) + } + + var sk *ecdh.PrivateKey + if len(rawSk) > 0 { + sk, err = ecdh.NewPrivateKey(curve, rawSk) + } else { + sk, err = ecdh.GenerateKey(curve, rand.Reader) + } + if err != nil { + return nil, fmt.Errorf("private key error: %v", err) + } + + pubBytes := sk.PublicKey().Bytes() + coordLen := (len(pubBytes) - 1) / 2 + x := pubBytes[1 : 1+coordLen] + y := pubBytes[1+coordLen:] + + secret, err := ecdh.ECDH(curve, sk, pk) + if err != nil { + return nil, fmt.Errorf("key agreement failed: %v", err) + } + + return [][]byte{x, y, secret}, nil + }, + } +} + func TestACVP(t *testing.T) { testenv.SkipIfShortAndSlow(t)