Skip to content

Commit ea54d8a

Browse files
cpugopherbot
authored andcommitted
crypto/internal/fips140test: add RSA ACVP tests
Adds ACVP test coverage for the RSA algorithm based on the NIST spec: https://pages.nist.gov/ACVP/draft-celi-acvp-rsa.html Includes coverage for keyGen, sigGen and sigVer across a variety of modulus sizes. For sigGen and sigVer both PKCS1v1.5 and PSS are supported with a variety of SHA2 digests. The static test data from go-acvp only includes sigVer vectors/expected. The keyGen and sigGen test types aren't amenable to fixed data testing. Updates #69642 Change-Id: Ia61a69115f2d2a984b95435a37d4c9c6db90a89a Reviewed-on: https://go-review.googlesource.com/c/go/+/642135 Reviewed-by: Filippo Valsorda <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]>
1 parent 0c94c5f commit ea54d8a

File tree

3 files changed

+152
-3
lines changed

3 files changed

+152
-3
lines changed

src/crypto/internal/fips140test/acvp_capabilities.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,9 @@
6969

7070
{"algorithm":"KAS-ECC-SSC","revision":"Sp800-56Ar3","scheme":{"ephemeralUnified":{"kasRole":["initiator","responder"]},"staticUnified":{"kasRole":["initiator","responder"]}},"domainParameterGenerationMethods":["P-224","P-256","P-384","P-521"]},
7171

72-
{"algorithm":"KDF","revision":"1.0","capabilities":[{"kdfMode":"counter","macMode":["CMAC-AES128","CMAC-AES192","CMAC-AES256"],"supportedLengths":[256],"fixedDataOrder":["before fixed data"],"counterLength":[16]}]}
72+
{"algorithm":"KDF","revision":"1.0","capabilities":[{"kdfMode":"counter","macMode":["CMAC-AES128","CMAC-AES192","CMAC-AES256"],"supportedLengths":[256],"fixedDataOrder":["before fixed data"],"counterLength":[16]}]},
73+
74+
{"algorithm":"RSA","mode":"keyGen","revision":"FIPS186-5","infoGeneratedByServer":true,"pubExpMode":"fixed","fixedPubExp":"010001","keyFormat":"standard","capabilities":[{"randPQ":"probable","properties":[{"modulo":2048,"primeTest":["2powSecStr"]},{"modulo":3072,"primeTest":["2powSecStr"]},{"modulo":4096,"primeTest":["2powSecStr"]}]}]},
75+
{"algorithm":"RSA","mode":"sigGen","revision":"FIPS186-5","capabilities":[{"sigType":"pkcs1v1.5","properties":[{"modulo":2048,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]},{"modulo":3072,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]},{"modulo":4096,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]}]},{"sigType":"pss","properties":[{"maskFunction":["mgf1"],"modulo":2048,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]},{"maskFunction":["mgf1"],"modulo":3072,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]},{"maskFunction":["mgf1"],"modulo":4096,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]}]}]},
76+
{"algorithm":"RSA","mode":"sigVer","revision":"FIPS186-5","pubExpMode":"fixed","fixedPubExp":"010001","capabilities":[{"sigType":"pkcs1v1.5","properties":[{"modulo":2048,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]}]},{"sigType":"pkcs1v1.5","properties":[{"modulo":3072,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]}]},{"sigType":"pkcs1v1.5","properties":[{"modulo":4096,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]}]},{"sigType":"pss","properties":[{"maskFunction":["mgf1"],"modulo":2048,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]}]},{"sigType":"pss","properties":[{"maskFunction":["mgf1"],"modulo":3072,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]}]},{"sigType":"pss","properties":[{"maskFunction":["mgf1"],"modulo":4096,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]}]}]}
7377
]

src/crypto/internal/fips140test/acvp_test.config.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,7 @@
4949
{"Wrapper": "go", "In": "vectors/TLS-v1.2.bz2", "Out": "expected/TLS-v1.2.bz2"},
5050
{"Wrapper": "go", "In": "vectors/TLS-v1.3.bz2", "Out": "expected/TLS-v1.3.bz2"},
5151

52-
{"Wrapper": "go", "In": "vectors/kdf-components.bz2", "Out": "expected/kdf-components.bz2"}
52+
{"Wrapper": "go", "In": "vectors/kdf-components.bz2", "Out": "expected/kdf-components.bz2"},
53+
54+
{"Wrapper": "go", "In": "vectors/RSA.bz2", "Out": "expected/RSA.bz2"}
5355
]

src/crypto/internal/fips140test/acvp_test.go

Lines changed: 144 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"crypto/internal/fips140"
2727
"crypto/internal/fips140/aes"
2828
"crypto/internal/fips140/aes/gcm"
29+
"crypto/internal/fips140/bigmod"
2930
"crypto/internal/fips140/drbg"
3031
"crypto/internal/fips140/ecdh"
3132
"crypto/internal/fips140/ecdsa"
@@ -35,6 +36,7 @@ import (
3536
"crypto/internal/fips140/hmac"
3637
"crypto/internal/fips140/mlkem"
3738
"crypto/internal/fips140/pbkdf2"
39+
"crypto/internal/fips140/rsa"
3840
"crypto/internal/fips140/sha256"
3941
"crypto/internal/fips140/sha3"
4042
"crypto/internal/fips140/sha512"
@@ -131,6 +133,8 @@ var (
131133
// https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.2
132134
// KDF-Counter algorithm capabilities:
133135
// https://pages.nist.gov/ACVP/draft-celi-acvp-kbkdf.html#section-7.3
136+
// RSA algorithm capabilities:
137+
// https://pages.nist.gov/ACVP/draft-celi-acvp-rsa.html#section-7.3
134138
//go:embed acvp_capabilities.json
135139
capabilitiesJson []byte
136140

@@ -269,6 +273,26 @@ var (
269273
"ctrDRBG-reseed/AES-256": cmdCtrDrbgReseedAft(),
270274

271275
"KDF-counter": cmdKdfCounterAft(),
276+
277+
"RSA/keyGen": cmdRsaKeyGenAft(),
278+
279+
"RSA/sigGen/SHA2-224/pkcs1v1.5": cmdRsaSigGenAft(func() fips140.Hash { return sha256.New224() }, "SHA-224", false),
280+
"RSA/sigGen/SHA2-256/pkcs1v1.5": cmdRsaSigGenAft(func() fips140.Hash { return sha256.New() }, "SHA-256", false),
281+
"RSA/sigGen/SHA2-384/pkcs1v1.5": cmdRsaSigGenAft(func() fips140.Hash { return sha512.New384() }, "SHA-384", false),
282+
"RSA/sigGen/SHA2-512/pkcs1v1.5": cmdRsaSigGenAft(func() fips140.Hash { return sha512.New() }, "SHA-512", false),
283+
"RSA/sigGen/SHA2-224/pss": cmdRsaSigGenAft(func() fips140.Hash { return sha256.New224() }, "SHA-224", true),
284+
"RSA/sigGen/SHA2-256/pss": cmdRsaSigGenAft(func() fips140.Hash { return sha256.New() }, "SHA-256", true),
285+
"RSA/sigGen/SHA2-384/pss": cmdRsaSigGenAft(func() fips140.Hash { return sha512.New384() }, "SHA-384", true),
286+
"RSA/sigGen/SHA2-512/pss": cmdRsaSigGenAft(func() fips140.Hash { return sha512.New() }, "SHA-512", true),
287+
288+
"RSA/sigVer/SHA2-224/pkcs1v1.5": cmdRsaSigVerAft(func() fips140.Hash { return sha256.New224() }, "SHA-224", false),
289+
"RSA/sigVer/SHA2-256/pkcs1v1.5": cmdRsaSigVerAft(func() fips140.Hash { return sha256.New() }, "SHA-256", false),
290+
"RSA/sigVer/SHA2-384/pkcs1v1.5": cmdRsaSigVerAft(func() fips140.Hash { return sha512.New384() }, "SHA-384", false),
291+
"RSA/sigVer/SHA2-512/pkcs1v1.5": cmdRsaSigVerAft(func() fips140.Hash { return sha512.New() }, "SHA-512", false),
292+
"RSA/sigVer/SHA2-224/pss": cmdRsaSigVerAft(func() fips140.Hash { return sha256.New224() }, "SHA-224", true),
293+
"RSA/sigVer/SHA2-256/pss": cmdRsaSigVerAft(func() fips140.Hash { return sha256.New() }, "SHA-256", true),
294+
"RSA/sigVer/SHA2-384/pss": cmdRsaSigVerAft(func() fips140.Hash { return sha512.New384() }, "SHA-384", true),
295+
"RSA/sigVer/SHA2-512/pss": cmdRsaSigVerAft(func() fips140.Hash { return sha512.New() }, "SHA-512", true),
272296
}
273297
)
274298

@@ -1634,14 +1658,133 @@ func cmdKdfCounterAft() command {
16341658
}
16351659
}
16361660

1661+
func cmdRsaKeyGenAft() command {
1662+
return command{
1663+
requiredArgs: 1, // Modulus bit-size
1664+
handler: func(args [][]byte) ([][]byte, error) {
1665+
bitSize := binary.LittleEndian.Uint32(args[0])
1666+
1667+
key, err := getRSAKey((int)(bitSize))
1668+
if err != nil {
1669+
return nil, fmt.Errorf("generating RSA key: %w", err)
1670+
}
1671+
1672+
N, e, d, P, Q, _, _, _ := key.Export()
1673+
1674+
eBytes := make([]byte, 4)
1675+
binary.BigEndian.PutUint32(eBytes, uint32(e))
1676+
1677+
return [][]byte{eBytes, P, Q, N, d}, nil
1678+
},
1679+
}
1680+
}
1681+
1682+
func cmdRsaSigGenAft(hashFunc func() fips140.Hash, hashName string, pss bool) command {
1683+
return command{
1684+
requiredArgs: 2, // Modulus bit-size, message
1685+
handler: func(args [][]byte) ([][]byte, error) {
1686+
bitSize := binary.LittleEndian.Uint32(args[0])
1687+
msg := args[1]
1688+
1689+
key, err := getRSAKey((int)(bitSize))
1690+
if err != nil {
1691+
return nil, fmt.Errorf("generating RSA key: %w", err)
1692+
}
1693+
1694+
h := hashFunc()
1695+
h.Write(msg)
1696+
digest := h.Sum(nil)
1697+
1698+
var sig []byte
1699+
if !pss {
1700+
sig, err = rsa.SignPKCS1v15(key, hashName, digest)
1701+
if err != nil {
1702+
return nil, fmt.Errorf("signing RSA message: %w", err)
1703+
}
1704+
} else {
1705+
sig, err = rsa.SignPSS(rand.Reader, key, hashFunc(), digest, h.Size())
1706+
if err != nil {
1707+
return nil, fmt.Errorf("signing RSA message: %w", err)
1708+
}
1709+
}
1710+
1711+
N, e, _, _, _, _, _, _ := key.Export()
1712+
eBytes := make([]byte, 4)
1713+
binary.BigEndian.PutUint32(eBytes, uint32(e))
1714+
1715+
return [][]byte{N, eBytes, sig}, nil
1716+
},
1717+
}
1718+
}
1719+
1720+
func cmdRsaSigVerAft(hashFunc func() fips140.Hash, hashName string, pss bool) command {
1721+
return command{
1722+
requiredArgs: 4, // n, e, message, signature
1723+
handler: func(args [][]byte) ([][]byte, error) {
1724+
nBytes := args[0]
1725+
eBytes := args[1]
1726+
msg := args[2]
1727+
sig := args[3]
1728+
1729+
paddedE := make([]byte, 4)
1730+
copy(paddedE[4-len(eBytes):], eBytes)
1731+
e := int(binary.BigEndian.Uint32(paddedE))
1732+
1733+
n, err := bigmod.NewModulus(nBytes)
1734+
if err != nil {
1735+
return nil, fmt.Errorf("invalid RSA modulus: %w", err)
1736+
}
1737+
1738+
pub := &rsa.PublicKey{
1739+
N: n,
1740+
E: e,
1741+
}
1742+
1743+
h := hashFunc()
1744+
h.Write(msg)
1745+
digest := h.Sum(nil)
1746+
1747+
if !pss {
1748+
err = rsa.VerifyPKCS1v15(pub, hashName, digest, sig)
1749+
} else {
1750+
err = rsa.VerifyPSS(pub, hashFunc(), digest, sig)
1751+
}
1752+
if err != nil {
1753+
return [][]byte{{0}}, nil
1754+
}
1755+
1756+
return [][]byte{{1}}, nil
1757+
},
1758+
}
1759+
}
1760+
1761+
// rsaKeyCache caches generated keys by modulus bit-size.
1762+
var rsaKeyCache = map[int]*rsa.PrivateKey{}
1763+
1764+
// getRSAKey returns a cached RSA private key with the specified modulus bit-size
1765+
// or generates one if necessary.
1766+
func getRSAKey(bits int) (*rsa.PrivateKey, error) {
1767+
if key, exists := rsaKeyCache[bits]; exists {
1768+
return key, nil
1769+
}
1770+
1771+
key, err := rsa.GenerateKey(rand.Reader, bits)
1772+
if err != nil {
1773+
return nil, err
1774+
}
1775+
1776+
rsaKeyCache[bits] = key
1777+
return key, nil
1778+
}
1779+
16371780
func TestACVP(t *testing.T) {
16381781
testenv.SkipIfShortAndSlow(t)
16391782

16401783
const (
16411784
bsslModule = "boringssl.googlesource.com/boringssl.git"
16421785
bsslVersion = "v0.0.0-20250116010235-21f54b2730ee"
16431786
goAcvpModule = "github.com/cpu/go-acvp"
1644-
goAcvpVersion = "v0.0.0-20250102201911-6839fc40f9f8"
1787+
goAcvpVersion = "v0.0.0-20250110181646-e47fea3b5d7d"
16451788
)
16461789

16471790
// In crypto/tls/bogo_shim_test.go the test is skipped if run on a builder with runtime.GOOS == "windows"

0 commit comments

Comments
 (0)