]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/internal/fips140test: add hmac DRBG ACVP tests
authorDaniel McCarney <daniel@binaryparadox.net>
Thu, 2 Jan 2025 18:27:09 +0000 (13:27 -0500)
committerGopher Robot <gobot@golang.org>
Fri, 10 Jan 2025 22:12:31 +0000 (14:12 -0800)
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 <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
src/crypto/internal/fips140/ecdsa/hmacdrbg.go
src/crypto/internal/fips140test/acvp_capabilities.json
src/crypto/internal/fips140test/acvp_test.config.json
src/crypto/internal/fips140test/acvp_test.go

index 4f085e2801b79dab307f831b34ec4d19f78fc299..8f52091170249256a70ec9a78f035c1f5e4fb46f 100644 (file)
@@ -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 {
index 38ce3a39c4b30fb3c29f5d8968a55ddd605383ac..8a4a97758cce39dc99706797665dfe77d4b1f07b 100644 (file)
   {"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}]}
 ]
index f62743f0c5d1aab8b0d29980c9964fe2442505fc..dc4d714f19c6207aa2c2558a72b5e2419c517815 100644 (file)
@@ -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
index 70c2b7e718ddf4212f05ba5af46e620fb3eed7da..8dedb9a79159b5cd0720348b6cdeb715296a3f26 100644 (file)
@@ -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"
        )