From 19e923182e590ae6568c2c714f20f32512aeb3e3 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Thu, 2 Jan 2025 13:27:09 -0500 Subject: [PATCH] crypto/internal/fips140test: add hmac DRBG ACVP tests Adds ACVP test coverage for the hmacDRBG algorithm based on the NIST spec: https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.2 The HMAC DRBG algorithm in our fips module is a minimal implementation tailored for use for generating ECDSA nonces and so lives in crypto/internal/fips140/ecdsa. In order to be testable by crypto/internal/fips140test this changeset exports a ecdsa.TestingOnlyNewDrbg() constructor to support the ACVP use-case. All FIPS-compatible SHA2 and SHA3 digests are tested. The ACVP capability registration is customized to match the limited capabilities of our ecdsa-focused impl. Most notably: * reseedImplemented is false - we expect this impl to be invoked only once or twice per instantiation and do not support explicit reseeding. * predResistanceEnabled is false - this requires reseeding. * Per mode: * derFuncEnabled is always false - this is only used by ctrDRBG. * additionalInputLen is 0 for all modes - this is only used with preResistanceEnabled. The other capability values are chosen based on Table 4: https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.4 Updates #69642 Change-Id: Ia58979d691f912e2ed739a05efb719f580fbbf89 Reviewed-on: https://go-review.googlesource.com/c/go/+/639775 Reviewed-by: Michael Pratt LUCI-TryBot-Result: Go LUCI Auto-Submit: Filippo Valsorda Reviewed-by: Dmitri Shuralyov Reviewed-by: Filippo Valsorda --- src/crypto/internal/fips140/ecdsa/hmacdrbg.go | 9 ++++ .../fips140test/acvp_capabilities.json | 13 ++++- .../fips140test/acvp_test.config.json | 4 +- src/crypto/internal/fips140test/acvp_test.go | 49 ++++++++++++++++++- 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go index 4f085e2801..8f52091170 100644 --- a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go +++ b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go @@ -116,6 +116,15 @@ func newDRBG[H fips140.Hash](hash func() H, entropy, nonce []byte, s personaliza return d } +// TestingOnlyNewDRBG creates an SP 800-90A Rev. 1 HMAC_DRBG with a plain +// personalization string. +// +// This should only be used for ACVP testing. hmacDRBG is not intended to be +// used directly. +func TestingOnlyNewDRBG(hash func() fips140.Hash, entropy, nonce []byte, s []byte) *hmacDRBG { + return newDRBG(hash, entropy, nonce, plainPersonalizationString(s)) +} + func pad000(h *hmac.HMAC, writtenSoFar int) { blockSize := h.BlockSize() if rem := writtenSoFar % blockSize; rem != 0 { diff --git a/src/crypto/internal/fips140test/acvp_capabilities.json b/src/crypto/internal/fips140test/acvp_capabilities.json index 38ce3a39c4..8a4a97758c 100644 --- a/src/crypto/internal/fips140test/acvp_capabilities.json +++ b/src/crypto/internal/fips140test/acvp_capabilities.json @@ -26,5 +26,16 @@ {"algorithm":"PBKDF","capabilities":[{"iterationCount":[{"min":1,"max":10000,"increment":1}],"keyLen":[{"min":112,"max":4096,"increment":8}],"passwordLen":[{"min":8,"max":64,"increment":1}],"saltLen":[{"min":128,"max":512,"increment":8}],"hmacAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}],"revision":"1.0"}, {"algorithm":"ML-KEM","mode":"keyGen","revision":"FIPS203","parameterSets":["ML-KEM-768","ML-KEM-1024"]}, - {"algorithm":"ML-KEM","mode":"encapDecap","revision":"FIPS203","parameterSets":["ML-KEM-768","ML-KEM-1024"],"functions":["encapsulation","decapsulation"]} + {"algorithm":"ML-KEM","mode":"encapDecap","revision":"FIPS203","parameterSets":["ML-KEM-768","ML-KEM-1024"],"functions":["encapsulation","decapsulation"]}, + + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-224","derFuncEnabled":false,"entropyInputLen":[192],"nonceLen":[96],"persoStringLen":[192],"additionalInputLen":[0],"returnedBitsLen":224}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-256","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":256}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-384","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":384}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-512","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":512}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-512/224","derFuncEnabled":false,"entropyInputLen":[192],"nonceLen":[96],"persoStringLen":[192],"additionalInputLen":[0],"returnedBitsLen":224}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-512/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-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}]} ] diff --git a/src/crypto/internal/fips140test/acvp_test.config.json b/src/crypto/internal/fips140test/acvp_test.config.json index f62743f0c5..dc4d714f19 100644 --- a/src/crypto/internal/fips140test/acvp_test.config.json +++ b/src/crypto/internal/fips140test/acvp_test.config.json @@ -25,5 +25,7 @@ {"Wrapper": "go", "In": "vectors/PBKDF.bz2", "Out": "expected/PBKDF.bz2"}, - {"Wrapper": "go", "In": "vectors/ML-KEM.bz2", "Out": "expected/ML-KEM.bz2"} + {"Wrapper": "go", "In": "vectors/ML-KEM.bz2", "Out": "expected/ML-KEM.bz2"}, + + {"Wrapper": "go", "In": "vectors/hmacDRBG.bz2", "Out": "expected/hmacDRBG.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 70c2b7e718..8dedb9a791 100644 --- a/src/crypto/internal/fips140test/acvp_test.go +++ b/src/crypto/internal/fips140test/acvp_test.go @@ -23,6 +23,7 @@ import ( "bytes" "crypto/internal/cryptotest" "crypto/internal/fips140" + "crypto/internal/fips140/ecdsa" "crypto/internal/fips140/hmac" "crypto/internal/fips140/mlkem" "crypto/internal/fips140/pbkdf2" @@ -78,6 +79,8 @@ var ( // https://pages.nist.gov/ACVP/draft-celi-acvp-pbkdf.html#section-7.3 // ML-KEM algorithm capabilities: // 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 //go:embed acvp_capabilities.json capabilitiesJson []byte @@ -128,6 +131,17 @@ var ( "ML-KEM-1024/keyGen": cmdMlKem1024KeyGenAft(), "ML-KEM-1024/encap": cmdMlKem1024EncapAft(), "ML-KEM-1024/decap": cmdMlKem1024DecapAft(), + + "hmacDRBG/SHA2-224": cmdHmacDrbgAft(func() fips140.Hash { return sha256.New224() }), + "hmacDRBG/SHA2-256": cmdHmacDrbgAft(func() fips140.Hash { return sha256.New() }), + "hmacDRBG/SHA2-384": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New384() }), + "hmacDRBG/SHA2-512": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New() }), + "hmacDRBG/SHA2-512/224": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New512_224() }), + "hmacDRBG/SHA2-512/256": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New512_256() }), + "hmacDRBG/SHA3-224": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New224() }), + "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() }), } ) @@ -538,12 +552,45 @@ func cmdMlKem1024DecapAft() command { } } +func cmdHmacDrbgAft(h func() fips140.Hash) command { + return command{ + requiredArgs: 6, // Output length, entropy, personalization, ad1, ad2, nonce + handler: func(args [][]byte) ([][]byte, error) { + outLen := binary.LittleEndian.Uint32(args[0]) + entropy := args[1] + personalization := args[2] + ad1 := args[3] + ad2 := args[4] + nonce := args[5] + + // Our capabilities describe no additional data support. + if len(ad1) != 0 || len(ad2) != 0 { + return nil, errors.New("additional data not supported") + } + + // Our capabilities describe no prediction resistance (requires reseed) and no reseed. + // So the test procedure is: + // * Instantiate DRBG + // * Generate but don't output + // * Generate output + // * Uninstantiate + // See Table 7 in draft-vassilev-acvp-drbg + out := make([]byte, outLen) + drbg := ecdsa.TestingOnlyNewDRBG(h, entropy, nonce, personalization) + drbg.Generate(out) + drbg.Generate(out) + + return [][]byte{out}, nil + }, + } +} + func TestACVP(t *testing.T) { testenv.SkipIfShortAndSlow(t) const ( bsslModule = "boringssl.googlesource.com/boringssl.git" - bsslVersion = "v0.0.0-20241218033850-ca3146c56300" + bsslVersion = "v0.0.0-20250108043213-d3f61eeacbf7" goAcvpModule = "github.com/cpu/go-acvp" goAcvpVersion = "v0.0.0-20250102201911-6839fc40f9f8" ) -- 2.48.1