acvp: test with internal nonce generation.

(Cherry-pick to FIPS branch from
https://boringssl-review.googlesource.com/c/boringssl/+/65227)

AES-GCM where the module generates the nonce itself is more directly
approved by FIPS. ACVP that instead of AES-GCM with an external nonce,
where compliance depends on a careful reading of the IG.

Change-Id: I8a3e926a28ae633a0b0c499cb35c321ccf9c0e30
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/65227
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/65327
diff --git a/util/fipstools/acvp/acvptool/subprocess/aead.go b/util/fipstools/acvp/acvptool/subprocess/aead.go
index e6585d1..c5170b5 100644
--- a/util/fipstools/acvp/acvptool/subprocess/aead.go
+++ b/util/fipstools/acvp/acvptool/subprocess/aead.go
@@ -32,12 +32,13 @@
 }
 
 type aeadTestGroup struct {
-	ID        uint64 `json:"tgId"`
-	Type      string `json:"testType"`
-	Direction string `json:"direction"`
-	KeyBits   int    `json:"keyLen"`
-	TagBits   int    `json:"tagLen"`
-	Tests     []struct {
+	ID          uint64 `json:"tgId"`
+	Type        string `json:"testType"`
+	Direction   string `json:"direction"`
+	KeyBits     int    `json:"keyLen"`
+	TagBits     int    `json:"tagLen"`
+	NonceSource string `json:"ivGen"`
+	Tests       []struct {
 		ID            uint64 `json:"tcId"`
 		PlaintextHex  string `json:"pt"`
 		CiphertextHex string `json:"ct"`
@@ -57,6 +58,7 @@
 	ID            uint64  `json:"tcId"`
 	CiphertextHex *string `json:"ct,omitempty"`
 	TagHex        string  `json:"tag,omitempty"`
+	NonceHex      string  `json:"iv,omitempty"`
 	PlaintextHex  *string `json:"pt,omitempty"`
 	Passed        *bool   `json:"testPassed,omitempty"`
 }
@@ -86,9 +88,24 @@
 			return nil, fmt.Errorf("test group %d has unknown direction %q", group.ID, group.Direction)
 		}
 
-		op := a.algo + "/seal"
-		if !encrypt {
-			op = a.algo + "/open"
+		var randnonce bool
+		switch group.NonceSource {
+		case "internal":
+			randnonce = true
+		case "external", "":
+			randnonce = false
+		default:
+			return nil, fmt.Errorf("test group %d has unknown nonce source %q", group.ID, group.NonceSource)
+		}
+
+		op := a.algo
+		if randnonce {
+			op += "-randnonce"
+		}
+		if encrypt {
+			op += "/seal"
+		} else {
+			op += "/open"
 		}
 
 		if group.KeyBits%8 != 0 || group.KeyBits < 0 {
@@ -174,14 +191,24 @@
 					ciphertextHex := hex.EncodeToString(result[0])
 					testResp.CiphertextHex = &ciphertextHex
 				} else {
-					ciphertext := result[0][:len(result[0])-tagBytes]
+					ciphertext := result[0]
+					if randnonce {
+						var nonce []byte
+						ciphertext, nonce = splitOffRight(ciphertext, 12)
+						testResp.NonceHex = hex.EncodeToString(nonce)
+					}
+					ciphertext, tag := splitOffRight(ciphertext, tagBytes)
 					ciphertextHex := hex.EncodeToString(ciphertext)
 					testResp.CiphertextHex = &ciphertextHex
-					tag := result[0][len(result[0])-tagBytes:]
 					testResp.TagHex = hex.EncodeToString(tag)
 				}
 			} else {
-				result, err := m.Transact(op, 2, uint32le(uint32(tagBytes)), key, append(input, tag...), nonce, aad)
+				ciphertext := append(input, tag...)
+				if randnonce {
+					ciphertext = append(ciphertext, nonce...)
+					nonce = []byte{}
+				}
+				result, err := m.Transact(op, 2, uint32le(uint32(tagBytes)), key, ciphertext, nonce, aad)
 				if err != nil {
 					return nil, err
 				}
@@ -205,3 +232,11 @@
 
 	return ret, nil
 }
+
+func splitOffRight(in []byte, suffixSize int) ([]byte, []byte) {
+	if len(in) < suffixSize {
+		panic("input too small to split")
+	}
+	split := len(in) - suffixSize
+	return in[:split], in[split:]
+}
diff --git a/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-GCM-randnonce.bz2 b/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-GCM-randnonce.bz2
new file mode 100644
index 0000000..edab948
--- /dev/null
+++ b/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-GCM-randnonce.bz2
Binary files differ
diff --git a/util/fipstools/acvp/acvptool/test/tests.json b/util/fipstools/acvp/acvptool/test/tests.json
index 421e253..6804b23 100644
--- a/util/fipstools/acvp/acvptool/test/tests.json
+++ b/util/fipstools/acvp/acvptool/test/tests.json
@@ -5,6 +5,7 @@
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-CTR.bz2", "Out": "expected/ACVP-AES-CTR.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-ECB.bz2", "Out": "expected/ACVP-AES-ECB.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-GCM.bz2", "Out": "expected/ACVP-AES-GCM.bz2"},
+{"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-GCM-randnonce.bz2", "Out": "expected/ACVP-AES-GCM-randnonce.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-GMAC.bz2", "Out": "expected/ACVP-AES-GMAC.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-KW.bz2", "Out": "expected/ACVP-AES-KW.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-KWP.bz2", "Out": "expected/ACVP-AES-KWP.bz2"},
diff --git a/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-GCM-randnonce.bz2 b/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-GCM-randnonce.bz2
new file mode 100644
index 0000000..0880f69
--- /dev/null
+++ b/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-GCM-randnonce.bz2
Binary files differ
diff --git a/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
index f417b64..a7494c2 100644
--- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc
+++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -297,7 +297,7 @@
         "algorithm": "ACVP-AES-GCM",
         "revision": "1.0",
         "direction": ["encrypt", "decrypt"],
-        "keyLen": [128, 192, 256],
+        "keyLen": [128, 256],
         "payloadLen": [{
           "min": 0, "max": 65536, "increment": 8
         }],
@@ -306,7 +306,8 @@
         }],
         "tagLen": [32, 64, 96, 104, 112, 120, 128],
         "ivLen": [96],
-        "ivGen": "external"
+        "ivGen": "internal",
+        "ivGenMode": "8.2.2"
       },
       {
         "algorithm": "ACVP-AES-GMAC",
@@ -1093,13 +1094,12 @@
 
 static bool AESGCMSetup(EVP_AEAD_CTX *ctx, Span<const uint8_t> tag_len_span,
                         Span<const uint8_t> key) {
-  uint32_t tag_len_32;
-  if (tag_len_span.size() != sizeof(tag_len_32)) {
+  if (tag_len_span.size() != sizeof(uint32_t)) {
     LOG_ERROR("Tag size value is %u bytes, not an uint32_t\n",
               static_cast<unsigned>(tag_len_span.size()));
     return false;
   }
-  memcpy(&tag_len_32, tag_len_span.data(), sizeof(tag_len_32));
+  const uint32_t tag_len_32 = CRYPTO_load_u32_le(tag_len_span.data());
 
   const EVP_AEAD *aead;
   switch (key.size()) {
@@ -1113,7 +1113,8 @@
       aead = EVP_aead_aes_256_gcm();
       break;
     default:
-      LOG_ERROR("Bad AES-GCM key length %u\n", static_cast<unsigned>(key.size()));
+      LOG_ERROR("Bad AES-GCM key length %u\n",
+                static_cast<unsigned>(key.size()));
       return false;
   }
 
@@ -1127,6 +1128,41 @@
   return true;
 }
 
+static bool AESGCMRandNonceSetup(EVP_AEAD_CTX *ctx,
+                                 Span<const uint8_t> tag_len_span,
+                                 Span<const uint8_t> key) {
+  if (tag_len_span.size() != sizeof(uint32_t)) {
+    LOG_ERROR("Tag size value is %u bytes, not an uint32_t\n",
+              static_cast<unsigned>(tag_len_span.size()));
+    return false;
+  }
+  const uint32_t tag_len_32 = CRYPTO_load_u32_le(tag_len_span.data());
+
+  const EVP_AEAD *aead;
+  switch (key.size()) {
+    case 16:
+      aead = EVP_aead_aes_128_gcm_randnonce();
+      break;
+    case 32:
+      aead = EVP_aead_aes_256_gcm_randnonce();
+      break;
+    default:
+      LOG_ERROR("Bad AES-GCM key length %u\n",
+                static_cast<unsigned>(key.size()));
+      return false;
+  }
+
+  constexpr size_t kNonceLength = 12;
+  if (!EVP_AEAD_CTX_init(ctx, aead, key.data(), key.size(),
+                         tag_len_32 + kNonceLength, nullptr)) {
+    LOG_ERROR("Failed to setup AES-GCM with tag length %u\n",
+              static_cast<unsigned>(tag_len_32));
+    return false;
+  }
+
+  return true;
+}
+
 static bool AESCCMSetup(EVP_AEAD_CTX *ctx, Span<const uint8_t> tag_len_span,
                         Span<const uint8_t> key) {
   uint32_t tag_len_32;
@@ -2067,6 +2103,8 @@
     {"AES-CTR/decrypt", 4, AES_CTR},
     {"AES-GCM/seal", 5, AEADSeal<AESGCMSetup>},
     {"AES-GCM/open", 5, AEADOpen<AESGCMSetup>},
+    {"AES-GCM-randnonce/seal", 5, AEADSeal<AESGCMRandNonceSetup>},
+    {"AES-GCM-randnonce/open", 5, AEADOpen<AESGCMRandNonceSetup>},
     {"AES-KW/seal", 5, AESKeyWrapSeal},
     {"AES-KW/open", 5, AESKeyWrapOpen},
     {"AES-KWP/seal", 5, AESPaddedKeyWrapSeal},