From 0c94c5fcae909de059ff5c9273e2839e0d5742bf Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Thu, 19 Dec 2024 12:45:57 -0500 Subject: [PATCH] crypto/internal/fips140test: add counter KDF ACVP tests 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 Reviewed-by: Dmitri Shuralyov Reviewed-by: Roland Shoemaker Auto-Submit: Filippo Valsorda LUCI-TryBot-Result: Go LUCI --- .../fips140test/acvp_capabilities.json | 4 +- src/crypto/internal/fips140test/acvp_test.go | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/crypto/internal/fips140test/acvp_capabilities.json b/src/crypto/internal/fips140test/acvp_capabilities.json index e2a49530fa..ecfb6b9e0f 100644 --- a/src/crypto/internal/fips140test/acvp_capabilities.json +++ b/src/crypto/internal/fips140test/acvp_capabilities.json @@ -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]}]} ] diff --git a/src/crypto/internal/fips140test/acvp_test.go b/src/crypto/internal/fips140test/acvp_test.go index 62a7dee6eb..e76d2daf1c 100644 --- a/src/crypto/internal/fips140test/acvp_test.go +++ b/src/crypto/internal/fips140test/acvp_test.go @@ -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) -- 2.50.0