]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/internal/fips140test: add KAS-ECC-SSC ACVP tests
authorDaniel McCarney <daniel@binaryparadox.net>
Thu, 19 Dec 2024 19:02:05 +0000 (14:02 -0500)
committerGopher Robot <gobot@golang.org>
Mon, 10 Feb 2025 21:29:42 +0000 (13:29 -0800)
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 <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
src/crypto/internal/fips140test/acvp_capabilities.json
src/crypto/internal/fips140test/acvp_test.go

index 02940a2e6f4b300b1dfcf8a6a32250ce3d14c87c..5b318500874d950f2f49a309c24eecda61d04c84 100644 (file)
@@ -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"]}
 ]
index ded66b79ae911bd5326b3f267b60c5d3de966010..8c51538cabd9ab4e43cec60c32d8ab67dfdaab34 100644 (file)
@@ -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)