acvp: RSA signature generation tests. Change-Id: Ibc794a66ea9b04e2d48c2124d52234a0bed10aff Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43625 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: Adam Langley <agl@google.com>
diff --git a/util/fipstools/acvp/acvptool/subprocess/rsa.go b/util/fipstools/acvp/acvptool/subprocess/rsa.go index 3133d91..0825c04 100644 --- a/util/fipstools/acvp/acvptool/subprocess/rsa.go +++ b/util/fipstools/acvp/acvptool/subprocess/rsa.go
@@ -57,6 +57,36 @@ D string `json:"d"` } +type rsaSigGenTestVectorSet struct { + Groups []rsaSigGenGroup `json:"testGroups"` +} + +type rsaSigGenGroup struct { + ID uint64 `json:"tgId"` + Type string `json:"testType"` + SigType string `json:"sigType"` + ModulusBits uint32 `json:"modulo"` + Hash string `json:"hashAlg"` + Tests []rsaSigGenTest `json:"tests"` +} + +type rsaSigGenTest struct { + ID uint64 `json:"tcId"` + MessageHex string `json:"message"` +} + +type rsaSigGenTestGroupResponse struct { + ID uint64 `json:"tgId"` + N string `json:"n"` + E string `json:"e"` + Tests []rsaSigGenTestResponse `json:"tests"` +} + +type rsaSigGenTestResponse struct { + ID uint64 `json:"tcId"` + Sig string `json:"signature"` +} + func processKeyGen(vectorSet []byte, m Transactable) (interface{}, error) { var parsed rsaKeyGenTestVectorSet if err := json.Unmarshal(vectorSet, &parsed); err != nil { @@ -98,6 +128,57 @@ return ret, nil } +func processSigGen(vectorSet []byte, m Transactable) (interface{}, error) { + var parsed rsaSigGenTestVectorSet + if err := json.Unmarshal(vectorSet, &parsed); err != nil { + return nil, err + } + + var ret []rsaSigGenTestGroupResponse + + for _, group := range parsed.Groups { + // GDT means "Generated data test", i.e. "please generate an RSA signature". + const expectedType = "GDT" + if group.Type != expectedType { + return nil, fmt.Errorf("RSA SigGen test group has type %q, but only generation tests (%q) are supported", group.Type, expectedType) + } + + response := rsaSigGenTestGroupResponse{ + ID: group.ID, + } + + operation := "RSA/sigGen/" + group.Hash + "/" + group.SigType + + for _, test := range group.Tests { + msg, err := hex.DecodeString(test.MessageHex) + if err != nil { + return nil, fmt.Errorf("test case %d/%d contains invalid hex: %s", group.ID, test.ID, err) + } + + results, err := m.Transact(operation, 3, uint32le(group.ModulusBits), msg) + if err != nil { + return nil, err + } + + if len(response.N) == 0 { + response.N = hex.EncodeToString(results[0]) + response.E = hex.EncodeToString(results[1]) + } else if response.N != hex.EncodeToString(results[0]) { + return nil, fmt.Errorf("module wrapper returned different RSA keys for the same SigGen configuration") + } + + response.Tests = append(response.Tests, rsaSigGenTestResponse{ + ID: test.ID, + Sig: hex.EncodeToString(results[2]), + }) + } + + ret = append(ret, response) + } + + return ret, nil +} + type rsa struct{} func (*rsa) Process(vectorSet []byte, m Transactable) (interface{}, error) { @@ -109,6 +190,8 @@ switch parsed.Mode { case "keyGen": return processKeyGen(vectorSet, m) + case "sigGen": + return processSigGen(vectorSet, m) default: return nil, fmt.Errorf("Unknown RSA mode %q", parsed.Mode) }
diff --git a/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/util/fipstools/acvp/modulewrapper/modulewrapper.cc index 48b96da..aad2a3c 100644 --- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc +++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -12,6 +12,7 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <map> #include <string> #include <vector> @@ -425,6 +426,123 @@ }] }, { + "algorithm": "RSA", + "mode": "sigGen", + "revision": "FIPS186-4", + "capabilities": [{ + "sigType": "pkcs1v1.5", + "properties": [{ + "modulo": 2048, + "hashPair": [{ + "hashAlg": "SHA2-224" + }, { + "hashAlg": "SHA2-256" + }, { + "hashAlg": "SHA2-384" + }, { + "hashAlg": "SHA2-512" + }, { + "hashAlg": "SHA-1" + }] + }] + },{ + "sigType": "pkcs1v1.5", + "properties": [{ + "modulo": 3072, + "hashPair": [{ + "hashAlg": "SHA2-224" + }, { + "hashAlg": "SHA2-256" + }, { + "hashAlg": "SHA2-384" + }, { + "hashAlg": "SHA2-512" + }, { + "hashAlg": "SHA-1" + }] + }] + },{ + "sigType": "pkcs1v1.5", + "properties": [{ + "modulo": 4096, + "hashPair": [{ + "hashAlg": "SHA2-224" + }, { + "hashAlg": "SHA2-256" + }, { + "hashAlg": "SHA2-384" + }, { + "hashAlg": "SHA2-512" + }, { + "hashAlg": "SHA-1" + }] + }] + },{ + "sigType": "pss", + "properties": [{ + "modulo": 2048, + "hashPair": [{ + "hashAlg": "SHA2-224", + "saltLen": 28 + }, { + "hashAlg": "SHA2-256", + "saltLen": 32 + }, { + "hashAlg": "SHA2-384", + "saltLen": 48 + }, { + "hashAlg": "SHA2-512", + "saltLen": 64 + }, { + "hashAlg": "SHA-1", + "saltLen": 20 + }] + }] + },{ + "sigType": "pss", + "properties": [{ + "modulo": 3072, + "hashPair": [{ + "hashAlg": "SHA2-224", + "saltLen": 28 + }, { + "hashAlg": "SHA2-256", + "saltLen": 32 + }, { + "hashAlg": "SHA2-384", + "saltLen": 48 + }, { + "hashAlg": "SHA2-512", + "saltLen": 64 + }, { + "hashAlg": "SHA-1", + "saltLen": 20 + }] + }] + },{ + "sigType": "pss", + "properties": [{ + "modulo": 4096, + "hashPair": [{ + "hashAlg": "SHA2-224", + "saltLen": 28 + }, { + "hashAlg": "SHA2-256", + "saltLen": 32 + }, { + "hashAlg": "SHA2-384", + "saltLen": 48 + }, { + "hashAlg": "SHA2-512", + "saltLen": 64 + }, { + "hashAlg": "SHA-1", + "saltLen": 20 + }] + }] + }] + }, + { "algorithm": "CMAC-AES", "revision": "1.0", "capabilities": [{ @@ -1032,6 +1150,28 @@ return WriteReply(STDOUT_FILENO, Span<const uint8_t>(mac, mac_len)); } +static std::map<unsigned, bssl::UniquePtr<RSA>>& CachedRSAKeys() { + static std::map<unsigned, bssl::UniquePtr<RSA>> keys; + return keys; +} + +static RSA* GetRSAKey(unsigned bits) { + auto it = CachedRSAKeys().find(bits); + if (it != CachedRSAKeys().end()) { + return it->second.get(); + } + + bssl::UniquePtr<RSA> key(RSA_new()); + if (!RSA_generate_key_fips(key.get(), bits, nullptr)) { + abort(); + } + + RSA *const ret = key.get(); + CachedRSAKeys().emplace(static_cast<unsigned>(bits), std::move(key)); + + return ret; +} + static bool RSAKeyGen(const Span<const uint8_t> args[]) { uint32_t bits; if (args[0].size() != sizeof(bits)) { @@ -1050,8 +1190,52 @@ RSA_get0_key(key.get(), &n, &e, &d); RSA_get0_factors(key.get(), &p, &q); - return WriteReply(STDOUT_FILENO, BIGNUMBytes(e), BIGNUMBytes(p), - BIGNUMBytes(q), BIGNUMBytes(n), BIGNUMBytes(d)); + if (!WriteReply(STDOUT_FILENO, BIGNUMBytes(e), BIGNUMBytes(p), BIGNUMBytes(q), + BIGNUMBytes(n), BIGNUMBytes(d))) { + return false; + } + + CachedRSAKeys().emplace(static_cast<unsigned>(bits), std::move(key)); + return true; +} + +template<const EVP_MD *(MDFunc)(), bool UsePSS> +static bool RSASigGen(const Span<const uint8_t> args[]) { + uint32_t bits; + if (args[0].size() != sizeof(bits)) { + return false; + } + memcpy(&bits, args[0].data(), sizeof(bits)); + const Span<const uint8_t> msg = args[1]; + + RSA *const key = GetRSAKey(bits); + const EVP_MD *const md = MDFunc(); + uint8_t digest_buf[EVP_MAX_MD_SIZE]; + unsigned digest_len; + if (!EVP_Digest(msg.data(), msg.size(), digest_buf, &digest_len, md, NULL)) { + return false; + } + + std::vector<uint8_t> sig(RSA_size(key)); + size_t sig_len; + if (UsePSS) { + if (!RSA_sign_pss_mgf1(key, &sig_len, sig.data(), sig.size(), digest_buf, + digest_len, md, md, -1)) { + return false; + } + } else { + unsigned sig_len_u; + if (!RSA_sign(EVP_MD_type(md), digest_buf, digest_len, sig.data(), + &sig_len_u, key)) { + return false; + } + sig_len = sig_len_u; + } + + sig.resize(sig_len); + + return WriteReply(STDOUT_FILENO, BIGNUMBytes(RSA_get0_n(key)), + BIGNUMBytes(RSA_get0_e(key)), sig); } static constexpr struct { @@ -1095,6 +1279,16 @@ {"ECDSA/sigVer", 7, ECDSASigVer}, {"CMAC-AES", 3, CMAC_AES}, {"RSA/keyGen", 1, RSAKeyGen}, + {"RSA/sigGen/SHA2-224/pkcs1v1.5", 2, RSASigGen<EVP_sha224, false>}, + {"RSA/sigGen/SHA2-256/pkcs1v1.5", 2, RSASigGen<EVP_sha256, false>}, + {"RSA/sigGen/SHA2-384/pkcs1v1.5", 2, RSASigGen<EVP_sha384, false>}, + {"RSA/sigGen/SHA2-512/pkcs1v1.5", 2, RSASigGen<EVP_sha512, false>}, + {"RSA/sigGen/SHA-1/pkcs1v1.5", 2, RSASigGen<EVP_sha1, false>}, + {"RSA/sigGen/SHA2-224/pss", 2, RSASigGen<EVP_sha224, true>}, + {"RSA/sigGen/SHA2-256/pss", 2, RSASigGen<EVP_sha256, true>}, + {"RSA/sigGen/SHA2-384/pss", 2, RSASigGen<EVP_sha384, true>}, + {"RSA/sigGen/SHA2-512/pss", 2, RSASigGen<EVP_sha512, true>}, + {"RSA/sigGen/SHA-1/pss", 2, RSASigGen<EVP_sha1, true>}, }; int main() {