Skip to content

Commit

Permalink
crypto/internal/fips140test: add hmac DRBG ACVP tests
Browse files Browse the repository at this point in the history
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 golang#69642

Change-Id: Ia58979d691f912e2ed739a05efb719f580fbbf89
Reviewed-on: https://go-review.googlesource.com/c/go/+/639775
Reviewed-by: Michael Pratt <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
Auto-Submit: Filippo Valsorda <[email protected]>
Reviewed-by: Dmitri Shuralyov <[email protected]>
Reviewed-by: Filippo Valsorda <[email protected]>
  • Loading branch information
cpu authored and gopherbot committed Jan 10, 2025
1 parent 7255b94 commit 19e9231
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 3 deletions.
9 changes: 9 additions & 0 deletions src/crypto/internal/fips140/ecdsa/hmacdrbg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 12 additions & 1 deletion src/crypto/internal/fips140test/acvp_capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -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}]}
]
4 changes: 3 additions & 1 deletion src/crypto/internal/fips140test/acvp_test.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
]
49 changes: 48 additions & 1 deletion src/crypto/internal/fips140test/acvp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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() }),
}
)

Expand Down Expand Up @@ -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"
)
Expand Down

0 comments on commit 19e9231

Please sign in to comment.