mirror of
https://github.com/golang/go.git
synced 2025-10-19 19:13:18 +00:00
crypto/internal/fips140test: add ctr DRBG ACVP tests
Adds ACVP test coverage for the SP 800-90A rev 1 ctrDRBG algorithm based on the NIST spec: https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.2 The implementation in our FIPS module is a minimal implementation tailored to the specific needs of stdlib crypto. As a result we customize the ACVP capability registration so that: * predResistanceEnabled is false * only mode AES-256 is supported * for that mode, * derFuncEnabled is false * persoStringLen is 0 to disable personalization * additionalInputLen is 384 to match the [48]byte argument in our API Other capability values are chosen based on Table 4's ctrDRBG AES-256 w/o `derFuncEnabled` row: https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.4 We do enable reseed in the capability, necessitating two acvptool commands: one that expects only 6 args and doesn't reseed ("ctrDRBG/AES-256"), and one that expects 8 args and does ("ctrDRBG-reseed/AES-256"). Updates #69642 Change-Id: I0f01a2f9496f45b130ee7d10916708093236f473 Reviewed-on: https://go-review.googlesource.com/c/go/+/639795 Reviewed-by: Roland Shoemaker <roland@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Auto-Submit: Filippo Valsorda <filippo@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
283296195b
commit
0bc57a3e7f
3 changed files with 158 additions and 34 deletions
|
@ -44,6 +44,8 @@
|
||||||
{"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-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}]},
|
{"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}]},
|
||||||
|
|
||||||
|
{"algorithm":"ctrDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":true,"capabilities":[{"mode":"AES-256","derFuncEnabled":false,"entropyInputLen":[384],"nonceLen":[0],"persoStringLen":[0],"additionalInputLen":[384],"returnedBitsLen":128}]},
|
||||||
|
|
||||||
{"algorithm":"EDDSA","mode":"keyGen","revision":"1.0","curve":["ED-25519"]},
|
{"algorithm":"EDDSA","mode":"keyGen","revision":"1.0","curve":["ED-25519"]},
|
||||||
{"algorithm":"EDDSA","mode":"keyVer","revision":"1.0","curve":["ED-25519"]},
|
{"algorithm":"EDDSA","mode":"keyVer","revision":"1.0","curve":["ED-25519"]},
|
||||||
{"algorithm":"EDDSA","mode":"sigGen","revision":"1.0","pure":true,"preHash":true,"contextLength":[{"min":0,"max":255,"increment":1}],"curve":["ED-25519"]},
|
{"algorithm":"EDDSA","mode":"sigGen","revision":"1.0","pure":true,"preHash":true,"contextLength":[{"min":0,"max":255,"increment":1}],"curve":["ED-25519"]},
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
|
|
||||||
{"Wrapper": "go", "In": "vectors/hmacDRBG.bz2", "Out": "expected/hmacDRBG.bz2"},
|
{"Wrapper": "go", "In": "vectors/hmacDRBG.bz2", "Out": "expected/hmacDRBG.bz2"},
|
||||||
|
|
||||||
|
{"Wrapper": "go", "In": "vectors/ctrDRBG.bz2", "Out": "expected/ctrDRBG.bz2"},
|
||||||
|
|
||||||
{"Wrapper": "go", "In": "vectors/EDDSA.bz2", "Out": "expected/EDDSA.bz2"},
|
{"Wrapper": "go", "In": "vectors/EDDSA.bz2", "Out": "expected/EDDSA.bz2"},
|
||||||
|
|
||||||
{"Wrapper": "go", "In": "vectors/ECDSA.bz2", "Out": "expected/ECDSA.bz2"},
|
{"Wrapper": "go", "In": "vectors/ECDSA.bz2", "Out": "expected/ECDSA.bz2"},
|
||||||
|
@ -46,5 +48,6 @@
|
||||||
|
|
||||||
{"Wrapper": "go", "In": "vectors/TLS-v1.2.bz2", "Out": "expected/TLS-v1.2.bz2"},
|
{"Wrapper": "go", "In": "vectors/TLS-v1.2.bz2", "Out": "expected/TLS-v1.2.bz2"},
|
||||||
{"Wrapper": "go", "In": "vectors/TLS-v1.3.bz2", "Out": "expected/TLS-v1.3.bz2"},
|
{"Wrapper": "go", "In": "vectors/TLS-v1.3.bz2", "Out": "expected/TLS-v1.3.bz2"},
|
||||||
|
|
||||||
{"Wrapper": "go", "In": "vectors/kdf-components.bz2", "Out": "expected/kdf-components.bz2"}
|
{"Wrapper": "go", "In": "vectors/kdf-components.bz2", "Out": "expected/kdf-components.bz2"}
|
||||||
]
|
]
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"crypto/internal/fips140"
|
"crypto/internal/fips140"
|
||||||
"crypto/internal/fips140/aes"
|
"crypto/internal/fips140/aes"
|
||||||
"crypto/internal/fips140/aes/gcm"
|
"crypto/internal/fips140/aes/gcm"
|
||||||
|
"crypto/internal/fips140/drbg"
|
||||||
"crypto/internal/fips140/ecdh"
|
"crypto/internal/fips140/ecdh"
|
||||||
"crypto/internal/fips140/ecdsa"
|
"crypto/internal/fips140/ecdsa"
|
||||||
"crypto/internal/fips140/ed25519"
|
"crypto/internal/fips140/ed25519"
|
||||||
|
@ -125,7 +126,9 @@ var (
|
||||||
// SSH KDF algorithm capabilities:
|
// SSH KDF algorithm capabilities:
|
||||||
// https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#section-7.2
|
// https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#section-7.2
|
||||||
// ECDH algorithm capabilities:
|
// ECDH algorithm capabilities:
|
||||||
// https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ssc-ecc.html
|
// 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
|
||||||
//go:embed acvp_capabilities.json
|
//go:embed acvp_capabilities.json
|
||||||
capabilitiesJson []byte
|
capabilitiesJson []byte
|
||||||
|
|
||||||
|
@ -259,6 +262,9 @@ var (
|
||||||
"ECDH/P-256": cmdEcdhAftVal(ecdh.P256()),
|
"ECDH/P-256": cmdEcdhAftVal(ecdh.P256()),
|
||||||
"ECDH/P-384": cmdEcdhAftVal(ecdh.P384()),
|
"ECDH/P-384": cmdEcdhAftVal(ecdh.P384()),
|
||||||
"ECDH/P-521": cmdEcdhAftVal(ecdh.P521()),
|
"ECDH/P-521": cmdEcdhAftVal(ecdh.P521()),
|
||||||
|
|
||||||
|
"ctrDRBG/AES-256": cmdCtrDrbgAft(),
|
||||||
|
"ctrDRBG-reseed/AES-256": cmdCtrDrbgReseedAft(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1103,39 +1109,6 @@ 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 lookupCurve(name string) (elliptic.Curve, error) {
|
func lookupCurve(name string) (elliptic.Curve, error) {
|
||||||
var c elliptic.Curve
|
var c elliptic.Curve
|
||||||
|
|
||||||
|
@ -1460,6 +1433,152 @@ func cmdEcdhAftVal[P ecdh.Point[P]](curve *ecdh.Curve[P]) 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 cmdCtrDrbgAft() command {
|
||||||
|
return command{
|
||||||
|
requiredArgs: 6, // Output length, entropy, personalization, ad1, ad2, nonce
|
||||||
|
handler: func(args [][]byte) ([][]byte, error) {
|
||||||
|
return acvpCtrDrbg{
|
||||||
|
outLen: binary.LittleEndian.Uint32(args[0]),
|
||||||
|
entropy: args[1],
|
||||||
|
personalization: args[2],
|
||||||
|
ad1: args[3],
|
||||||
|
ad2: args[4],
|
||||||
|
nonce: args[5],
|
||||||
|
}.process()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdCtrDrbgReseedAft() command {
|
||||||
|
return command{
|
||||||
|
requiredArgs: 8, // Output length, entropy, personalization, reseedAD, reseedEntropy, ad1, ad2, nonce
|
||||||
|
handler: func(args [][]byte) ([][]byte, error) {
|
||||||
|
return acvpCtrDrbg{
|
||||||
|
outLen: binary.LittleEndian.Uint32(args[0]),
|
||||||
|
entropy: args[1],
|
||||||
|
personalization: args[2],
|
||||||
|
reseedAd: args[3],
|
||||||
|
reseedEntropy: args[4],
|
||||||
|
ad1: args[5],
|
||||||
|
ad2: args[6],
|
||||||
|
nonce: args[7],
|
||||||
|
}.process()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type acvpCtrDrbg struct {
|
||||||
|
outLen uint32
|
||||||
|
entropy []byte
|
||||||
|
personalization []byte
|
||||||
|
ad1 []byte
|
||||||
|
ad2 []byte
|
||||||
|
nonce []byte
|
||||||
|
reseedAd []byte // May be empty for no reseed
|
||||||
|
reseedEntropy []byte // May be empty for no reseed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args acvpCtrDrbg) process() ([][]byte, error) {
|
||||||
|
// Our capability describes no personalization support.
|
||||||
|
if len(args.personalization) > 0 {
|
||||||
|
return nil, errors.New("personalization string not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our capability describes no derivation function support, so the nonce
|
||||||
|
// should be empty.
|
||||||
|
if len(args.nonce) > 0 {
|
||||||
|
return nil, errors.New("unexpected nonce value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our capability describes entropy input len of 384 bits.
|
||||||
|
entropy, err := require48Bytes(args.entropy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("entropy: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our capability describes additional input len of 384 bits.
|
||||||
|
ad1, err := require48Bytes(args.ad1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("AD1: %w", err)
|
||||||
|
}
|
||||||
|
ad2, err := require48Bytes(args.ad2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("AD2: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
withReseed := len(args.reseedAd) > 0
|
||||||
|
var reseedAd, reseedEntropy *[48]byte
|
||||||
|
if withReseed {
|
||||||
|
// Ditto RE: entropy and additional data lengths for reseeding.
|
||||||
|
if reseedAd, err = require48Bytes(args.reseedAd); err != nil {
|
||||||
|
return nil, fmt.Errorf("reseed AD: %w", err)
|
||||||
|
}
|
||||||
|
if reseedEntropy, err = require48Bytes(args.reseedEntropy); err != nil {
|
||||||
|
return nil, fmt.Errorf("reseed entropy: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our capabilities describe no prediction resistance and allow both
|
||||||
|
// reseed and no reseed, so the test procedure is:
|
||||||
|
// * Instantiate DRBG
|
||||||
|
// * Reseed (if enabled)
|
||||||
|
// * Generate but don't output
|
||||||
|
// * Generate output
|
||||||
|
// * Uninstantiate
|
||||||
|
// See Table 7 in draft-vassilev-acvp-drbg
|
||||||
|
out := make([]byte, args.outLen)
|
||||||
|
ctrDrbg := drbg.NewCounter(entropy)
|
||||||
|
if withReseed {
|
||||||
|
ctrDrbg.Reseed(reseedEntropy, reseedAd)
|
||||||
|
}
|
||||||
|
ctrDrbg.Generate(out, ad1)
|
||||||
|
ctrDrbg.Generate(out, ad2)
|
||||||
|
|
||||||
|
return [][]byte{out}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify input is 48 byte slice, and cast it to a pointer to a fixed-size array
|
||||||
|
// of 48 bytes, or return an error.
|
||||||
|
func require48Bytes(input []byte) (*[48]byte, error) {
|
||||||
|
if inputLen := len(input); inputLen != 48 {
|
||||||
|
return nil, fmt.Errorf("invalid length: %d", inputLen)
|
||||||
|
}
|
||||||
|
return (*[48]byte)(input), nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestACVP(t *testing.T) {
|
func TestACVP(t *testing.T) {
|
||||||
testenv.SkipIfShortAndSlow(t)
|
testenv.SkipIfShortAndSlow(t)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue