// Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !fips140v1.0 && !fips140v1.26 package fipstest import ( "crypto/internal/fips140/bigmod" "crypto/internal/fips140/rsa" "crypto/internal/fips140/sha256" "crypto/internal/fips140/sha3" "crypto/internal/fips140/sha512" "crypto/rand" _ "embed" "encoding/binary" "errors" "fmt" "hash" "math/big" ) //go:embed acvp_capabilities_fips140v1.28.json var capabilitiesJson []byte var testConfigFile = "acvp_test_fips140v1.28.config.json" func init() { // RSA keyGen for keyFormat=crt capability. Previous versions used standard commands["RSA/keyGen/crt"] = cmdRsaKeyGenCrtAft() // RSA sigGen additional hash algorithms (adds SHA2-512 truncated hashes, SHA-3) // Support for PSS saltLen argument. commands["RSA/sigGen/SHA2-224/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha256.New224() }, "SHA-224", false) commands["RSA/sigGen/SHA2-256/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha256.New() }, "SHA-256", false) commands["RSA/sigGen/SHA2-384/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha512.New384() }, "SHA-384", false) commands["RSA/sigGen/SHA2-512/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha512.New() }, "SHA-512", false) commands["RSA/sigGen/SHA2-512/224/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha512.New512_224() }, "SHA-512/224", false) commands["RSA/sigGen/SHA2-512/256/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha512.New512_256() }, "SHA-512/256", false) commands["RSA/sigGen/SHA3-224/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha3.New224() }, "SHA3-224", false) commands["RSA/sigGen/SHA3-256/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha3.New256() }, "SHA3-256", false) commands["RSA/sigGen/SHA3-384/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha3.New384() }, "SHA3-384", false) commands["RSA/sigGen/SHA3-512/pkcs1v1.5"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha3.New512() }, "SHA3-512", false) commands["RSA/sigGen/SHA2-512/224/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha512.New512_224() }, "SHA-512/224", true) commands["RSA/sigGen/SHA2-512/256/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha512.New512_256() }, "SHA-512/256", true) commands["RSA/sigGen/SHA3-224/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha3.New224() }, "SHA3-224", true) commands["RSA/sigGen/SHA3-256/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha3.New256() }, "SHA3-256", true) commands["RSA/sigGen/SHA3-384/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha3.New384() }, "SHA3-384", true) commands["RSA/sigGen/SHA3-512/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha3.New512() }, "SHA3-512", true) commands["RSA/sigGen/SHA2-224/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha256.New224() }, "SHA-224", true) commands["RSA/sigGen/SHA2-256/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha256.New() }, "SHA-256", true) commands["RSA/sigGen/SHA2-384/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha512.New384() }, "SHA-384", true) commands["RSA/sigGen/SHA2-512/pss"] = cmdRsaSigGenSaltLenAft(func() hash.Hash { return sha512.New() }, "SHA-512", true) // RSA sigGen with pubExpMode=random capability. Previous versions used fixed. // Also adds SHA2-512 truncated hashes. Notably SHA-3 is not supported by ACVP // for RSA sigVer. commands["RSA/sigVer/SHA2-224/pkcs1v1.5"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha256.New224() }, "SHA-224", false) commands["RSA/sigVer/SHA2-256/pkcs1v1.5"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha256.New() }, "SHA-256", false) commands["RSA/sigVer/SHA2-384/pkcs1v1.5"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha512.New384() }, "SHA-384", false) commands["RSA/sigVer/SHA2-512/pkcs1v1.5"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha512.New() }, "SHA-512", false) commands["RSA/sigVer/SHA2-512/224/pkcs1v1.5"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha512.New512_224() }, "SHA-512/224", false) commands["RSA/sigVer/SHA2-512/256/pkcs1v1.5"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha512.New512_256() }, "SHA-512/256", false) commands["RSA/sigVer/SHA2-224/pss"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha256.New224() }, "SHA-224", true) commands["RSA/sigVer/SHA2-256/pss"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha256.New() }, "SHA-256", true) commands["RSA/sigVer/SHA2-384/pss"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha512.New384() }, "SHA-384", true) commands["RSA/sigVer/SHA2-512/pss"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha512.New() }, "SHA-512", true) commands["RSA/sigVer/SHA2-512/224/pss"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha512.New512_224() }, "SHA-512/224", true) commands["RSA/sigVer/SHA2-512/256/pss"] = cmdRsaSigVerRandExpAft(func() hash.Hash { return sha512.New512_256() }, "SHA-512/256", true) // KTS-IFC with keyGenerationMethods=rsakpg2-crt (random exponent, CRT format). // Previous version used rsakpg1-basic (fixed exponent, basic format). commands["KTS-IFC/SHA2-224/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha256.New224() }) commands["KTS-IFC/SHA2-256/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha256.New() }) commands["KTS-IFC/SHA2-384/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha512.New384() }) commands["KTS-IFC/SHA2-512/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha512.New() }) commands["KTS-IFC/SHA2-512/224/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha512.New512_224() }) commands["KTS-IFC/SHA2-512/256/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha512.New512_256() }) commands["KTS-IFC/SHA3-224/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha3.New224() }) commands["KTS-IFC/SHA3-256/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha3.New256() }) commands["KTS-IFC/SHA3-384/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha3.New384() }) commands["KTS-IFC/SHA3-512/responder/crt"] = cmdKtsIfcResponderRandExpCrtAft(func() hash.Hash { return sha3.New512() }) } func cmdRsaKeyGenCrtAft() command { return command{ requiredArgs: 1, // Modulus bit-size handler: func(args [][]byte) ([][]byte, error) { bitSize := binary.LittleEndian.Uint32(args[0]) key, err := getRSAKey((int)(bitSize)) if err != nil { return nil, fmt.Errorf("generating RSA key: %w", err) } N, e, d, P, Q, dP, dQ, qInv := key.Export() eBytes := make([]byte, 4) binary.BigEndian.PutUint32(eBytes, uint32(e)) return [][]byte{eBytes, P, Q, N, d, dP, dQ, qInv}, nil }, } } func cmdRsaSigGenSaltLenAft(hashFunc func() hash.Hash, hashName string, pss bool) command { return command{ requiredArgs: 3, // Modulus bit-size, message, saltLen handler: func(args [][]byte) ([][]byte, error) { bitSize := binary.LittleEndian.Uint32(args[0]) msg := args[1] saltLen := binary.LittleEndian.Uint32(args[2]) key, err := getRSAKey((int)(bitSize)) if err != nil { return nil, fmt.Errorf("generating RSA key: %w", err) } h := hashFunc() h.Write(msg) digest := h.Sum(nil) var sig []byte if !pss { sig, err = rsa.SignPKCS1v15(key, hashName, digest) if err != nil { return nil, fmt.Errorf("signing RSA message: %w", err) } } else { sig, err = rsa.SignPSS(rand.Reader, key, hashFunc(), digest, (int)(saltLen)) if err != nil { return nil, fmt.Errorf("signing RSA message: %w", err) } } N, e, _, _, _, _, _, _ := key.Export() eBytes := make([]byte, 4) binary.BigEndian.PutUint32(eBytes, uint32(e)) return [][]byte{N, eBytes, sig}, nil }, } } func cmdRsaSigVerRandExpAft(hashFunc func() hash.Hash, hashName string, pss bool) command { return command{ requiredArgs: 4, // n, e, message, signature handler: func(args [][]byte) ([][]byte, error) { nBytes := args[0] eBytes := args[1] msg := args[2] sig := args[3] n, err := bigmod.NewModulus(nBytes) if err != nil { return nil, fmt.Errorf("invalid RSA modulus: %w", err) } h := hashFunc() h.Write(msg) digest := h.Sum(nil) if len(eBytes) < 32/8 || (len(eBytes) == 32/8 && eBytes[0] < 0x80) { paddedE := make([]byte, 4) copy(paddedE[4-len(eBytes):], eBytes) e := int(binary.BigEndian.Uint32(paddedE)) pub := &rsa.PublicKey{ N: n, E: e, } if !pss { err = rsa.VerifyPKCS1v15(pub, hashName, digest, sig) } else { err = rsa.VerifyPSS(pub, hashFunc(), digest, sig) } } else { pub := &rsa.TestingOnlyLargeExponentPublicKey{ N: n, E: eBytes, } if !pss { err = rsa.TestingOnlyLargeExponentVerifyPKCS1v15(pub, hashName, digest, sig) } else { err = rsa.TestingOnlyLargeExponentVerifyPSS(pub, hashFunc(), digest, sig) } } if err != nil { return [][]byte{{0}}, nil } return [][]byte{{1}}, nil }, } } func cmdKtsIfcResponderRandExpCrtAft(h func() hash.Hash) command { return command{ requiredArgs: 8, // n bytes, e bytes, p bytes, q bytes, dmp1 bytes, dmq1 bytes, iqmp bytes, c bytes handler: func(args [][]byte) ([][]byte, error) { nBytes := args[0] eBytes := args[1] pBytes := args[2] qBytes := args[3] dmp1Bytes := args[4] dmq1Bytes := args[5] iqmpBytes := args[6] cBytes := args[7] // We need to compute 'd' from the CRT values for NewPrivateKeyWithPrecomputation to // check consistency. p, q := new(big.Int).SetBytes(pBytes), new(big.Int).SetBytes(qBytes) dP, dQ := new(big.Int).SetBytes(dmp1Bytes), new(big.Int).SetBytes(dmq1Bytes) pMinus1, qMinus1 := new(big.Int).Sub(p, big.NewInt(1)), new(big.Int).Sub(q, big.NewInt(1)) x, y := new(big.Int), new(big.Int) gcd := new(big.Int).GCD(x, y, pMinus1, qMinus1) diff := new(big.Int).Sub(dQ, dP) if new(big.Int).Mod(diff, gcd).Sign() != 0 { return nil, errors.New("inconsistent CRT parameters") } lcm := new(big.Int).Mul(pMinus1, qMinus1) lcm.Div(lcm, gcd) d := new(big.Int).Mul(x, pMinus1) d.Mul(d, diff).Div(d, gcd) d.Add(d, dP).Mod(d, lcm) var dkm []byte var err error if len(eBytes) < 32/8 || (len(eBytes) == 32/8 && eBytes[0] < 0x80) { // Exponent fits in an int32, use standard PrivateKey. paddedE := make([]byte, 4) copy(paddedE[4-len(eBytes):], eBytes) e := int(binary.BigEndian.Uint32(paddedE)) priv, err := rsa.NewPrivateKeyWithPrecomputation(nBytes, e, d.Bytes(), pBytes, qBytes, dmp1Bytes, dmq1Bytes, iqmpBytes) if err != nil { return nil, fmt.Errorf("failed to create private key: %v", err) } dkm, err = rsa.DecryptOAEP(h(), h(), priv, cBytes, nil) } else { // Large exponent, use TestingOnlyLargeExponentPrivateKey. priv, err := rsa.TestingOnlyNewLargeExponentPrivateKeyWithPrecomputation(nBytes, eBytes, d.Bytes(), pBytes, qBytes, dmp1Bytes, dmq1Bytes, iqmpBytes) if err != nil { return nil, fmt.Errorf("failed to create private key: %v", err) } dkm, err = rsa.TestingOnlyLargeExponentDecryptOAEP(h(), h(), priv, cBytes, nil) } if err != nil { return nil, fmt.Errorf("OAEP decryption failed: %v", err) } return [][]byte{dkm}, nil }, } }