]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/internal/fips140test: add counter KDF ACVP tests
authorDaniel McCarney <daniel@binaryparadox.net>
Thu, 19 Dec 2024 17:45:57 +0000 (12:45 -0500)
committerGopher Robot <gobot@golang.org>
Mon, 10 Feb 2025 21:55:28 +0000 (13:55 -0800)
Adds ACVP test coverage for the SP 800-108r1 KDF counter mode algorithm
based on the NIST spec:

  https://pages.nist.gov/ACVP/draft-celi-acvp-kbkdf.html

The implementation in our FIPS module fixes some parameters, requiring
tailoring of the advertised capability to match. Notably:

* We only support macModes CMAC-AES-128, -192, and -256
* We only support supportedLengths 256 (matching the [32]byte output
  from CounterKDF.DeriveKey)
* We only support fixedDataOrder "before fixed data"
* We only support counterLength 16

No acvp_test.config.json update accompanies this support because the
ACVP tests for this algorithm aren't amenable to fixed data testing.

Updates #69642

Change-Id: I9e02d6c8cb6e209ac8e4c9fba926fffbad916098
Reviewed-on: https://go-review.googlesource.com/c/go/+/639776
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/crypto/internal/fips140test/acvp_capabilities.json
src/crypto/internal/fips140test/acvp_test.go

index e2a49530fa5a82b6e08d463eea332cf94b6760a7..ecfb6b9e0fef0b34ad41df68706e9d6726c99379 100644 (file)
@@ -67,5 +67,7 @@
   {"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":"KAS-ECC-SSC","revision":"Sp800-56Ar3","scheme":{"ephemeralUnified":{"kasRole":["initiator","responder"]},"staticUnified":{"kasRole":["initiator","responder"]}},"domainParameterGenerationMethods":["P-224","P-256","P-384","P-521"]}
+  {"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"]},
+
+  {"algorithm":"KDF","revision":"1.0","capabilities":[{"kdfMode":"counter","macMode":["CMAC-AES128","CMAC-AES192","CMAC-AES256"],"supportedLengths":[256],"fixedDataOrder":["before fixed data"],"counterLength":[16]}]}
 ]
index 62a7dee6eb4510bb92b76ebd806fbd0bcf9faece..e76d2daf1ce52e93604211eedb9c44c3cd62a72c 100644 (file)
@@ -129,6 +129,8 @@ var (
        //   https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ssc-ecc.html#section-7.3
        // HMAC DRBG and CTR DRBG algorithm capabilities:
        //   https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.2
+       // KDF-Counter algorithm capabilities:
+       //   https://pages.nist.gov/ACVP/draft-celi-acvp-kbkdf.html#section-7.3
        //go:embed acvp_capabilities.json
        capabilitiesJson []byte
 
@@ -265,6 +267,8 @@ var (
 
                "ctrDRBG/AES-256":        cmdCtrDrbgAft(),
                "ctrDRBG-reseed/AES-256": cmdCtrDrbgReseedAft(),
+
+               "KDF-counter": cmdKdfCounterAft(),
        }
 )
 
@@ -1579,6 +1583,57 @@ func require48Bytes(input []byte) (*[48]byte, error) {
        return (*[48]byte)(input), nil
 }
 
+func cmdKdfCounterAft() command {
+       return command{
+               requiredArgs: 5, // Number output bytes, PRF name, counter location string, key, number of counter bits
+               handler: func(args [][]byte) ([][]byte, error) {
+                       outputBytes := binary.LittleEndian.Uint32(args[0])
+                       prf := args[1]
+                       counterLocation := args[2]
+                       key := args[3]
+                       counterBits := binary.LittleEndian.Uint32(args[4])
+
+                       if outputBytes != 32 {
+                               return nil, fmt.Errorf("KDF received unsupported output length %d bytes", outputBytes)
+                       }
+                       if !bytes.Equal(prf, []byte("CMAC-AES128")) && !bytes.Equal(prf, []byte("CMAC-AES192")) && !bytes.Equal(prf, []byte("CMAC-AES256")) {
+                               return nil, fmt.Errorf("KDF received unsupported PRF %q", string(prf))
+                       }
+                       if !bytes.Equal(counterLocation, []byte("before fixed data")) {
+                               return nil, fmt.Errorf("KDF received unsupported counter location %q", string(counterLocation))
+                       }
+                       // The spec doesn't describe the "deferred" property for a KDF counterMode test case.
+                       // BoringSSL's acvptool sends an empty key when deferred=true, but with the capabilities
+                       // we register all test cases ahve deferred=false and provide a key from the populated
+                       // keyIn property.
+                       if len(key) == 0 {
+                               return nil, errors.New("deferred test cases are not supported")
+                       }
+                       if counterBits != 16 {
+                               return nil, fmt.Errorf("KDF received unsupported counter length %d", counterBits)
+                       }
+
+                       block, err := aes.New(key)
+                       if err != nil {
+                               return nil, fmt.Errorf("failed to create cipher: %v", err)
+                       }
+                       kdf := gcm.NewCounterKDF(block)
+
+                       var label byte
+                       var context [12]byte
+                       rand.Reader.Read(context[:])
+
+                       result := kdf.DeriveKey(label, context)
+
+                       fixedData := make([]byte, 1+1+12) // 1 byte label, 1 null byte, 12 bytes context.
+                       fixedData[0] = label
+                       copy(fixedData[2:], context[:])
+
+                       return [][]byte{key, fixedData, result[:]}, nil
+               },
+       }
+}
+
 func TestACVP(t *testing.T) {
        testenv.SkipIfShortAndSlow(t)