From 2e22d1b3cb2415ffdf229d7039489f7fcd971546 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Thu, 22 Oct 2020 07:56:36 -0700 Subject: [PATCH] acvp: support RSA key generation tests. Change-Id: I40bbf6d10fcfd1e0fb506bef44f4cd6e9d2daac5 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43644 Reviewed-by: David Benjamin --- .../fipstools/acvp/acvptool/subprocess/rsa.go | 115 ++++++++++++++++++ .../acvp/acvptool/subprocess/subprocess.go | 1 + .../acvp/modulewrapper/modulewrapper.cc | 52 ++++++++ 3 files changed, 168 insertions(+) create mode 100644 util/fipstools/acvp/acvptool/subprocess/rsa.go diff --git a/util/fipstools/acvp/acvptool/subprocess/rsa.go b/util/fipstools/acvp/acvptool/subprocess/rsa.go new file mode 100644 index 000000000..3133d915f --- /dev/null +++ b/util/fipstools/acvp/acvptool/subprocess/rsa.go @@ -0,0 +1,115 @@ +// Copyright (c) 2020, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package subprocess + +import ( + "encoding/hex" + "encoding/json" + "fmt" +) + +// See https://usnistgov.github.io/ACVP/draft-celi-acvp-rsa.html#section-7.4 +// although, at the time of writing, that spec doesn't match what the NIST demo +// server actually produces. This code matches the server. + +type rsaTestVectorSet struct { + Mode string `json:"mode"` +} + +type rsaKeyGenTestVectorSet struct { + Groups []rsaKeyGenGroup `json:"testGroups"` +} + +type rsaKeyGenGroup struct { + ID uint64 `json:"tgId"` + Type string `json:"testType"` + ModulusBits uint32 `json:"modulo"` + Tests []rsaKeyGenTest `json:"tests"` +} + +type rsaKeyGenTest struct { + ID uint64 `json:"tcId"` +} + +type rsaKeyGenTestGroupResponse struct { + ID uint64 `json:"tgId"` + Tests []rsaKeyGenTestResponse `json:"tests"` +} + +type rsaKeyGenTestResponse struct { + ID uint64 `json:"tcId"` + E string `json:"e"` + P string `json:"p"` + Q string `json:"q"` + N string `json:"n"` + D string `json:"d"` +} + +func processKeyGen(vectorSet []byte, m Transactable) (interface{}, error) { + var parsed rsaKeyGenTestVectorSet + if err := json.Unmarshal(vectorSet, &parsed); err != nil { + return nil, err + } + + var ret []rsaKeyGenTestGroupResponse + + for _, group := range parsed.Groups { + // GDT means "Generated data test", i.e. "please generate an RSA key". + const expectedType = "GDT" + if group.Type != expectedType { + return nil, fmt.Errorf("RSA KeyGen test group has type %q, but only generation tests (%q) are supported", group.Type, expectedType) + } + + response := rsaKeyGenTestGroupResponse{ + ID: group.ID, + } + + for _, test := range group.Tests { + results, err := m.Transact("RSA/keyGen", 5, uint32le(group.ModulusBits)) + if err != nil { + return nil, err + } + + response.Tests = append(response.Tests, rsaKeyGenTestResponse{ + ID: test.ID, + E: hex.EncodeToString(results[0]), + P: hex.EncodeToString(results[1]), + Q: hex.EncodeToString(results[2]), + N: hex.EncodeToString(results[3]), + D: hex.EncodeToString(results[4]), + }) + } + + ret = append(ret, response) + } + + return ret, nil +} + +type rsa struct{} + +func (*rsa) Process(vectorSet []byte, m Transactable) (interface{}, error) { + var parsed rsaTestVectorSet + if err := json.Unmarshal(vectorSet, &parsed); err != nil { + return nil, err + } + + switch parsed.Mode { + case "keyGen": + return processKeyGen(vectorSet, m) + default: + return nil, fmt.Errorf("Unknown RSA mode %q", parsed.Mode) + } +} diff --git a/util/fipstools/acvp/acvptool/subprocess/subprocess.go b/util/fipstools/acvp/acvptool/subprocess/subprocess.go index 76442fad6..6f450d420 100644 --- a/util/fipstools/acvp/acvptool/subprocess/subprocess.go +++ b/util/fipstools/acvp/acvptool/subprocess/subprocess.go @@ -94,6 +94,7 @@ func NewWithIO(cmd *exec.Cmd, in io.WriteCloser, out io.ReadCloser) *Subprocess "hmacDRBG": &drbg{"hmacDRBG", map[string]bool{"SHA-1": true, "SHA2-224": true, "SHA2-256": true, "SHA2-384": true, "SHA2-512": true}}, "KDF": &kdfPrimitive{}, "CMAC-AES": &keyedMACPrimitive{"CMAC-AES"}, + "RSA": &rsa{}, } m.primitives["ECDSA"] = &ecdsa{"ECDSA", map[string]bool{"P-224": true, "P-256": true, "P-384": true, "P-521": true}, m.primitives} diff --git a/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/util/fipstools/acvp/modulewrapper/modulewrapper.cc index c782f67a7..48b96da10 100644 --- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc +++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -395,6 +396,34 @@ static bool GetConfig(const Span args[]) { ] }] }, + { + "algorithm": "RSA", + "mode": "keyGen", + "revision": "FIPS186-4", + "infoGeneratedByServer": true, + "pubExpMode": "fixed", + "fixedPubExp": "010001", + "keyFormat": "standard", + "capabilities": [{ + "randPQ": "B.3.3", + "properties": [{ + "modulo": 2048, + "primeTest": [ + "tblC2" + ] + },{ + "modulo": 3072, + "primeTest": [ + "tblC2" + ] + },{ + "modulo": 4096, + "primeTest": [ + "tblC2" + ] + }] + }] + }, { "algorithm": "CMAC-AES", "revision": "1.0", @@ -1003,6 +1032,28 @@ static bool CMAC_AES(const Span args[]) { return WriteReply(STDOUT_FILENO, Span(mac, mac_len)); } +static bool RSAKeyGen(const Span args[]) { + uint32_t bits; + if (args[0].size() != sizeof(bits)) { + return false; + } + memcpy(&bits, args[0].data(), sizeof(bits)); + + bssl::UniquePtr key(RSA_new()); + if (!RSA_generate_key_fips(key.get(), bits, nullptr)) { + fprintf(stderr, "RSA_generate_key_fips failed for modulus length %u.\n", + bits); + return false; + } + + const BIGNUM *n, *e, *d, *p, *q; + 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)); +} + static constexpr struct { const char name[kMaxNameLength + 1]; uint8_t expected_args; @@ -1043,6 +1094,7 @@ static constexpr struct { {"ECDSA/sigGen", 4, ECDSASigGen}, {"ECDSA/sigVer", 7, ECDSASigVer}, {"CMAC-AES", 3, CMAC_AES}, + {"RSA/keyGen", 1, RSAKeyGen}, }; int main() {