acvp: add AES-CTR support.

Change-Id: I61ba8b0eb717d2e66f3e2b098819ce979b8aca88
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43126
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/util/fipstools/acvp/acvptool/acvp/acvp.go b/util/fipstools/acvp/acvptool/acvp/acvp.go
index 52d7488..55d2f0b 100644
--- a/util/fipstools/acvp/acvptool/acvp/acvp.go
+++ b/util/fipstools/acvp/acvptool/acvp/acvp.go
@@ -89,7 +89,7 @@
 				return conn, err
 			},
 		},
-		Timeout: 10 * time.Second,
+		Timeout: 30 * time.Second,
 	}
 
 	return &Server{client: client, prefix: prefix, totpFunc: totp, PrefixTokens: make(map[string]string)}
diff --git a/util/fipstools/acvp/acvptool/subprocess/block.go b/util/fipstools/acvp/acvptool/subprocess/block.go
index 69a6517..9e51078 100644
--- a/util/fipstools/acvp/acvptool/subprocess/block.go
+++ b/util/fipstools/acvp/acvptool/subprocess/block.go
@@ -23,9 +23,10 @@
 // blockCipher implements an ACVP algorithm by making requests to the subprocess
 // to encrypt and decrypt with a block cipher.
 type blockCipher struct {
-	algo      string
-	blockSize int
-	hasIV     bool
+	algo                    string
+	blockSize               int
+	inputsAreBlockMultiples bool
+	hasIV                   bool
 }
 
 type blockCipherVectorSet struct {
@@ -97,7 +98,7 @@
 
 		var mct bool
 		switch group.Type {
-		case "AFT":
+		case "AFT", "CTR":
 			mct = false
 		case "MCT":
 			mct = true
@@ -132,7 +133,7 @@
 				return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
 			}
 
-			if len(input)%b.blockSize != 0 {
+			if b.inputsAreBlockMultiples && len(input)%b.blockSize != 0 {
 				return nil, fmt.Errorf("test case %d/%d has input of length %d, but expected multiple of %d", group.ID, test.ID, len(input), b.blockSize)
 			}
 
diff --git a/util/fipstools/acvp/acvptool/subprocess/subprocess.go b/util/fipstools/acvp/acvptool/subprocess/subprocess.go
index 431c315..af757d5 100644
--- a/util/fipstools/acvp/acvptool/subprocess/subprocess.go
+++ b/util/fipstools/acvp/acvptool/subprocess/subprocess.go
@@ -76,8 +76,9 @@
 		"SHA2-256":      &hashPrimitive{"SHA2-256", 32},
 		"SHA2-384":      &hashPrimitive{"SHA2-384", 48},
 		"SHA2-512":      &hashPrimitive{"SHA2-512", 64},
-		"ACVP-AES-ECB":  &blockCipher{"AES", 16, false},
-		"ACVP-AES-CBC":  &blockCipher{"AES-CBC", 16, true},
+		"ACVP-AES-ECB":  &blockCipher{"AES", 16, true, false},
+		"ACVP-AES-CBC":  &blockCipher{"AES-CBC", 16, true, true},
+		"ACVP-AES-CTR":  &blockCipher{"AES-CTR", 16, false, true},
 		"HMAC-SHA-1":    &hmacPrimitive{"HMAC-SHA-1", 20},
 		"HMAC-SHA2-224": &hmacPrimitive{"HMAC-SHA2-224", 28},
 		"HMAC-SHA2-256": &hmacPrimitive{"HMAC-SHA2-256", 32},
diff --git a/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
index 6ea5143..043d375 100644
--- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc
+++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -171,6 +171,18 @@
         "keyLen": [128, 192, 256]
       },
       {
+        "algorithm": "ACVP-AES-CTR",
+        "revision": "1.0",
+        "direction": ["encrypt", "decrypt"],
+        "keyLen": [128, 192, 256],
+        "payloadLen": [{
+          "min": 8, "max": 128, "increment": 8
+        }],
+        "incrementalCounter": true,
+        "overflowCounter": true,
+        "performCounterTests": true
+      },
+      {
         "algorithm": "ACVP-AES-CBC",
         "revision": "1.0",
         "direction": ["encrypt", "decrypt"],
@@ -379,6 +391,26 @@
   return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
 }
 
+static bool AES_CTR(const Span<const uint8_t> args[]) {
+  AES_KEY key;
+  if (AES_set_encrypt_key(args[0].data(), args[0].size() * 8, &key) != 0) {
+    return false;
+  }
+  if (args[2].size() != AES_BLOCK_SIZE) {
+    return false;
+  }
+  uint8_t iv[AES_BLOCK_SIZE];
+  memcpy(iv, args[2].data(), AES_BLOCK_SIZE);
+
+  std::vector<uint8_t> out;
+  out.resize(args[1].size());
+  unsigned num = 0;
+  uint8_t ecount_buf[AES_BLOCK_SIZE];
+  AES_ctr128_encrypt(args[1].data(), out.data(), args[1].size(), &key, iv,
+                     ecount_buf, &num);
+  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
+}
+
 template <const EVP_MD *HashFunc()>
 static bool HMAC(const Span<const uint8_t> args[]) {
   const EVP_MD *const md = HashFunc();
@@ -624,6 +656,8 @@
     {"AES/decrypt", 2, AES<AES_set_decrypt_key, AES_decrypt>},
     {"AES-CBC/encrypt", 3, AES_CBC<AES_set_encrypt_key, AES_ENCRYPT>},
     {"AES-CBC/decrypt", 3, AES_CBC<AES_set_decrypt_key, AES_DECRYPT>},
+    {"AES-CTR/encrypt", 3, AES_CTR},
+    {"AES-CTR/decrypt", 3, AES_CTR},
     {"HMAC-SHA-1", 2, HMAC<EVP_sha1>},
     {"HMAC-SHA2-224", 2, HMAC<EVP_sha224>},
     {"HMAC-SHA2-256", 2, HMAC<EVP_sha256>},