acvp: support RSA key generation tests.

Change-Id: I40bbf6d10fcfd1e0fb506bef44f4cd6e9d2daac5
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43644
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/util/fipstools/acvp/acvptool/subprocess/rsa.go b/util/fipstools/acvp/acvptool/subprocess/rsa.go
new file mode 100644
index 0000000..3133d91
--- /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 76442fa..6f450d4 100644
--- a/util/fipstools/acvp/acvptool/subprocess/subprocess.go
+++ b/util/fipstools/acvp/acvptool/subprocess/subprocess.go
@@ -94,6 +94,7 @@
 		"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 c782f67..48b96da 100644
--- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc
+++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -34,6 +34,7 @@
 #include <openssl/ecdsa.h>
 #include <openssl/hmac.h>
 #include <openssl/obj.h>
+#include <openssl/rsa.h>
 #include <openssl/sha.h>
 #include <openssl/span.h>
 
@@ -396,6 +397,34 @@
         }]
       },
       {
+        "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",
         "capabilities": [{
@@ -1003,6 +1032,28 @@
   return WriteReply(STDOUT_FILENO, Span<const uint8_t>(mac, mac_len));
 }
 
+static bool RSAKeyGen(const Span<const uint8_t> args[]) {
+  uint32_t bits;
+  if (args[0].size() != sizeof(bits)) {
+    return false;
+  }
+  memcpy(&bits, args[0].data(), sizeof(bits));
+
+  bssl::UniquePtr<RSA> 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 @@
     {"ECDSA/sigGen", 4, ECDSASigGen},
     {"ECDSA/sigVer", 7, ECDSASigVer},
     {"CMAC-AES", 3, CMAC_AES},
+    {"RSA/keyGen", 1, RSAKeyGen},
 };
 
 int main() {