From 035d3c8f530cadfb89654e27065c0e5230e36f69 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Sat, 19 Oct 2024 14:10:28 -0400 Subject: [PATCH] crypto/internal/fips140test: add SHAKE-* ACVP tests This commit adds ACVP test coverage for SHAKE-128 and SHAKE-256 based on the NIST spec: https://pages.nist.gov/ACVP/draft-celi-acvp-sha3.html Updates #69642 Change-Id: Ia6899def452fcb63a03603b7919fcb0c3576474b Reviewed-on: https://go-review.googlesource.com/c/go/+/622395 Reviewed-by: Dmitri Shuralyov TryBot-Bypass: Dmitri Shuralyov Auto-Submit: Dmitri Shuralyov Reviewed-by: Cherry Mui Reviewed-by: Dmitri Shuralyov --- .../fips140test/acvp_capabilities.json | 3 + .../fips140test/acvp_test.config.json | 3 + src/crypto/internal/fips140test/acvp_test.go | 77 +++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/src/crypto/internal/fips140test/acvp_capabilities.json b/src/crypto/internal/fips140test/acvp_capabilities.json index 117bd9e30b..77f21c3347 100644 --- a/src/crypto/internal/fips140test/acvp_capabilities.json +++ b/src/crypto/internal/fips140test/acvp_capabilities.json @@ -11,6 +11,9 @@ {"algorithm":"SHA3-384","messageLength":[{"increment":8,"max":65528,"min":0}],"revision":"2.0"}, {"algorithm":"SHA3-512","messageLength":[{"increment":8,"max":65528,"min":0}],"revision":"2.0"}, + {"algorithm":"SHAKE-128","inBit":false,"outBit":false,"inEmpty":true,"outputLen":[{"min":16,"max":65536,"increment":8}],"revision":"1.0"}, + {"algorithm":"SHAKE-256","inBit":false,"outBit":false,"inEmpty":true,"outputLen":[{"min":16,"max":65536,"increment":8}],"revision":"1.0"}, + {"algorithm":"HMAC-SHA2-224","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":224,"min":32}],"revision":"1.0"}, {"algorithm":"HMAC-SHA2-256","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":256,"min":32}],"revision":"1.0"}, {"algorithm":"HMAC-SHA2-384","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":384,"min":32}],"revision":"1.0"}, diff --git a/src/crypto/internal/fips140test/acvp_test.config.json b/src/crypto/internal/fips140test/acvp_test.config.json index c2bb9fc662..1cdfd8f85e 100644 --- a/src/crypto/internal/fips140test/acvp_test.config.json +++ b/src/crypto/internal/fips140test/acvp_test.config.json @@ -11,6 +11,9 @@ {"Wrapper": "go", "In": "vectors/SHA3-384.bz2", "Out": "expected/SHA3-384.bz2"}, {"Wrapper": "go", "In": "vectors/SHA3-512.bz2", "Out": "expected/SHA3-512.bz2"}, + {"Wrapper": "go", "In": "vectors/SHAKE-128.bz2", "Out": "expected/SHAKE-128.bz2"}, + {"Wrapper": "go", "In": "vectors/SHAKE-256.bz2", "Out": "expected/SHAKE-256.bz2"}, + {"Wrapper": "go", "In": "vectors/HMAC-SHA2-224.bz2", "Out": "expected/HMAC-SHA2-224.bz2"}, {"Wrapper": "go", "In": "vectors/HMAC-SHA2-256.bz2", "Out": "expected/HMAC-SHA2-256.bz2"}, {"Wrapper": "go", "In": "vectors/HMAC-SHA2-384.bz2", "Out": "expected/HMAC-SHA2-384.bz2"}, diff --git a/src/crypto/internal/fips140test/acvp_test.go b/src/crypto/internal/fips140test/acvp_test.go index 2f425effd5..23dcdecd93 100644 --- a/src/crypto/internal/fips140test/acvp_test.go +++ b/src/crypto/internal/fips140test/acvp_test.go @@ -95,6 +95,8 @@ const ( var ( // SHA2 algorithm capabilities: // https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#section-7.2 + // SHA3 and SHAKE algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-sha3.html#name-sha3-and-shake-algorithm-ca // HMAC algorithm capabilities: // https://pages.nist.gov/ACVP/draft-fussell-acvp-mac.html#section-7 // PBKDF2 algorithm capabilities: @@ -140,6 +142,17 @@ var ( "SHA3-512": cmdHashAft(sha3.New512()), "SHA3-512/MCT": cmdSha3Mct(sha3.New512()), + // Note: SHAKE AFT and VOT test types can be handled by the same command + // handler impl, but use distinct acvptool command names, and so are + // registered twice with the same digest: once under "SHAKE-xxx" for AFT, + // and once under"SHAKE-xxx/VOT" for VOT. + "SHAKE-128": cmdShakeAftVot(sha3.NewShake128()), + "SHAKE-128/VOT": cmdShakeAftVot(sha3.NewShake128()), + "SHAKE-128/MCT": cmdShakeMct(sha3.NewShake128()), + "SHAKE-256": cmdShakeAftVot(sha3.NewShake256()), + "SHAKE-256/VOT": cmdShakeAftVot(sha3.NewShake256()), + "SHAKE-256/MCT": cmdShakeMct(sha3.NewShake256()), + "HMAC-SHA2-224": cmdHmacAft(func() fips140.Hash { return sha256.New224() }), "HMAC-SHA2-256": cmdHmacAft(func() fips140.Hash { return sha256.New() }), "HMAC-SHA2-384": cmdHmacAft(func() fips140.Hash { return sha512.New384() }), @@ -410,6 +423,70 @@ func cmdSha3Mct(h fips140.Hash) command { } } +func cmdShakeAftVot(h *sha3.SHAKE) command { + return command{ + requiredArgs: 2, // Message, output length (bytes) + handler: func(args [][]byte) ([][]byte, error) { + msg := args[0] + + outLenBytes := binary.LittleEndian.Uint32(args[1]) + digest := make([]byte, outLenBytes) + + h.Reset() + h.Write(msg) + h.Read(digest) + + return [][]byte{digest}, nil + }, + } +} + +func cmdShakeMct(h *sha3.SHAKE) command { + return command{ + requiredArgs: 4, // Seed message, min output length (bytes), max output length (bytes), output length (bytes) + handler: func(args [][]byte) ([][]byte, error) { + md := args[0] + minOutBytes := binary.LittleEndian.Uint32(args[1]) + maxOutBytes := binary.LittleEndian.Uint32(args[2]) + + outputLenBytes := binary.LittleEndian.Uint32(args[3]) + if outputLenBytes < 2 { + return nil, fmt.Errorf("invalid output length: %d", outputLenBytes) + } + + rangeBytes := maxOutBytes - minOutBytes + 1 + if rangeBytes == 0 { + return nil, fmt.Errorf("invalid maxOutBytes and minOutBytes: %d, %d", maxOutBytes, minOutBytes) + } + + for i := 0; i < 1000; i++ { + // "The MSG[i] input to SHAKE MUST always contain at least 128 bits. If this is not the case + // as the previous digest was too short, append empty bits to the rightmost side of the digest." + boundary := min(len(md), 16) + msg := make([]byte, 16) + copy(msg, md[:boundary]) + + // MD[i] = SHAKE(MSG[i], OutputLen * 8) + h.Reset() + h.Write(msg) + digest := make([]byte, outputLenBytes) + h.Read(digest) + md = digest + + // RightmostOutputBits = 16 rightmost bits of MD[i] as an integer + // OutputLen = minOutBytes + (RightmostOutputBits % Range) + rightmostOutput := uint32(md[outputLenBytes-2])<<8 | uint32(md[outputLenBytes-1]) + outputLenBytes = minOutBytes + (rightmostOutput % rangeBytes) + } + + encodedOutputLenBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(encodedOutputLenBytes, outputLenBytes) + + return [][]byte{md, encodedOutputLenBytes}, nil + }, + } +} + func cmdHmacAft(h func() fips140.Hash) command { return command{ requiredArgs: 2, // Message and key -- 2.51.0