acvp: add AES-CCM support.
Change-Id: Ia8cbfd0b8f0f3932aea20e801e031d8df318f386
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43286
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/util/fipstools/acvp/acvptool/subprocess/aead.go b/util/fipstools/acvp/acvptool/subprocess/aead.go
index 0c38b85..e6585d1 100644
--- a/util/fipstools/acvp/acvptool/subprocess/aead.go
+++ b/util/fipstools/acvp/acvptool/subprocess/aead.go
@@ -23,7 +23,8 @@
// aead implements an ACVP algorithm by making requests to the subprocess
// to encrypt and decrypt with an AEAD.
type aead struct {
- algo string
+ algo string
+ tagMergedWithCiphertext bool
}
type aeadVectorSet struct {
@@ -120,18 +121,6 @@
return nil, fmt.Errorf("failed to decode aad in test case %d/%d: %s", group.ID, test.ID, err)
}
- var tag []byte
- if !encrypt {
- if tag, err = hex.DecodeString(test.TagHex); err != nil {
- return nil, fmt.Errorf("failed to decode tag in test case %d/%d: %s", group.ID, test.ID, err)
- }
- if len(tag) != tagBytes {
- return nil, fmt.Errorf("tag in test case %d/%d is %d bytes long, but should be %d", group.ID, test.ID, len(tag), tagBytes)
- }
- } else if len(test.TagHex) != 0 {
- return nil, fmt.Errorf("test case %d/%d has unexpected tag input", group.ID, test.ID)
- }
-
var inputHex, otherHex string
if encrypt {
inputHex, otherHex = test.PlaintextHex, test.CiphertextHex
@@ -148,6 +137,27 @@
return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
}
+ var tag []byte
+ if a.tagMergedWithCiphertext {
+ if len(test.TagHex) != 0 {
+ return nil, fmt.Errorf("test case %d/%d has unexpected tag input (should be merged into ciphertext)", group.ID, test.ID)
+ }
+ if !encrypt && len(input) < tagBytes {
+ return nil, fmt.Errorf("test case %d/%d has ciphertext shorter than the tag, but the tag should be included in it", group.ID, test.ID)
+ }
+ } else {
+ if !encrypt {
+ if tag, err = hex.DecodeString(test.TagHex); err != nil {
+ return nil, fmt.Errorf("failed to decode tag in test case %d/%d: %s", group.ID, test.ID, err)
+ }
+ if len(tag) != tagBytes {
+ return nil, fmt.Errorf("tag in test case %d/%d is %d bytes long, but should be %d", group.ID, test.ID, len(tag), tagBytes)
+ }
+ } else if len(test.TagHex) != 0 {
+ return nil, fmt.Errorf("test case %d/%d has unexpected tag input", group.ID, test.ID)
+ }
+ }
+
testResp := aeadTestResponse{ID: test.ID}
if encrypt {
@@ -160,12 +170,16 @@
return nil, fmt.Errorf("ciphertext from subprocess for test case %d/%d is shorter than the tag (%d vs %d)", group.ID, test.ID, len(result[0]), tagBytes)
}
- ciphertext := result[0][:len(result[0])-tagBytes]
- ciphertextHex := hex.EncodeToString(ciphertext)
- tag := result[0][len(result[0])-tagBytes:]
-
- testResp.CiphertextHex = &ciphertextHex
- testResp.TagHex = hex.EncodeToString(tag)
+ if a.tagMergedWithCiphertext {
+ ciphertextHex := hex.EncodeToString(result[0])
+ testResp.CiphertextHex = &ciphertextHex
+ } else {
+ ciphertext := result[0][:len(result[0])-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)
if err != nil {
diff --git a/util/fipstools/acvp/acvptool/subprocess/subprocess.go b/util/fipstools/acvp/acvptool/subprocess/subprocess.go
index 9c554c2..c713ea8 100644
--- a/util/fipstools/acvp/acvptool/subprocess/subprocess.go
+++ b/util/fipstools/acvp/acvptool/subprocess/subprocess.go
@@ -79,9 +79,10 @@
"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},
- "ACVP-AES-GCM": &aead{"AES-GCM"},
- "ACVP-AES-KW": &aead{"AES-KW"},
- "ACVP-AES-KWP": &aead{"AES-KWP"},
+ "ACVP-AES-GCM": &aead{"AES-GCM", false},
+ "ACVP-AES-CCM": &aead{"AES-CCM", true},
+ "ACVP-AES-KW": &aead{"AES-KW", false},
+ "ACVP-AES-KWP": &aead{"AES-KWP", false},
"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 961eaf7..ff5ef6c 100644
--- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc
+++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -236,6 +236,21 @@
"payloadLen": [{"min": 8, "max": 1024, "increment": 8}]
},
{
+ "algorithm": "ACVP-AES-CCM",
+ "revision": "1.0",
+ "direction": [
+ "encrypt",
+ "decrypt"
+ ],
+ "keyLen": [
+ 128
+ ],
+ "payloadLen": [{"min": 0, "max": 256, "increment": 8}],
+ "ivLen": [104],
+ "tagLen": [32],
+ "aadLen": [{"min": 0, "max": 1024, "increment": 8}]
+ },
+ {
"algorithm": "HMAC-SHA-1",
"revision": "1.0",
"keyLen": [{
@@ -495,7 +510,41 @@
return true;
}
-static bool AESGCMSeal(const Span<const uint8_t> args[]) {
+static bool AESCCMSetup(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)) {
+ fprintf(stderr, "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));
+ if (tag_len_32 != 4) {
+ fprintf(stderr, "AES-CCM only supports 4-byte tags, but %u was requested\n",
+ static_cast<unsigned>(tag_len_32));
+ return false;
+ }
+
+ if (key.size() != 16) {
+ fprintf(stderr,
+ "AES-CCM only supports 128-bit keys, but %u bits were given\n",
+ static_cast<unsigned>(key.size() * 8));
+ return false;
+ }
+
+ if (!EVP_AEAD_CTX_init(ctx, EVP_aead_aes_128_ccm_bluetooth(), key.data(),
+ key.size(), tag_len_32, nullptr)) {
+ fprintf(stderr, "Failed to setup AES-CCM with tag length %u\n",
+ static_cast<unsigned>(tag_len_32));
+ return false;
+ }
+
+ return true;
+}
+
+template <bool (*SetupFunc)(EVP_AEAD_CTX *ctx, Span<const uint8_t> tag_len_span,
+ Span<const uint8_t> key)>
+static bool AEADSeal(const Span<const uint8_t> args[]) {
Span<const uint8_t> tag_len_span = args[0];
Span<const uint8_t> key = args[1];
Span<const uint8_t> plaintext = args[2];
@@ -503,7 +552,7 @@
Span<const uint8_t> ad = args[4];
bssl::ScopedEVP_AEAD_CTX ctx;
- if (!AESGCMSetup(ctx.get(), tag_len_span, key)) {
+ if (!SetupFunc(ctx.get(), tag_len_span, key)) {
return false;
}
@@ -523,7 +572,9 @@
return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
}
-static bool AESGCMOpen(const Span<const uint8_t> args[]) {
+template <bool (*SetupFunc)(EVP_AEAD_CTX *ctx, Span<const uint8_t> tag_len_span,
+ Span<const uint8_t> key)>
+static bool AEADOpen(const Span<const uint8_t> args[]) {
Span<const uint8_t> tag_len_span = args[0];
Span<const uint8_t> key = args[1];
Span<const uint8_t> ciphertext = args[2];
@@ -531,7 +582,7 @@
Span<const uint8_t> ad = args[4];
bssl::ScopedEVP_AEAD_CTX ctx;
- if (!AESGCMSetup(ctx.get(), tag_len_span, key)) {
+ if (!SetupFunc(ctx.get(), tag_len_span, key)) {
return false;
}
@@ -916,12 +967,14 @@
{"AES-CBC/decrypt", 3, AES_CBC<AES_set_decrypt_key, AES_DECRYPT>},
{"AES-CTR/encrypt", 3, AES_CTR},
{"AES-CTR/decrypt", 3, AES_CTR},
- {"AES-GCM/seal", 5, AESGCMSeal},
- {"AES-GCM/open", 5, AESGCMOpen},
+ {"AES-GCM/seal", 5, AEADSeal<AESGCMSetup>},
+ {"AES-GCM/open", 5, AEADOpen<AESGCMSetup>},
{"AES-KW/seal", 5, AESKeyWrapSeal},
{"AES-KW/open", 5, AESKeyWrapOpen},
{"AES-KWP/seal", 5, AESPaddedKeyWrapSeal},
{"AES-KWP/open", 5, AESPaddedKeyWrapOpen},
+ {"AES-CCM/seal", 5, AEADSeal<AESCCMSetup>},
+ {"AES-CCM/open", 5, AEADOpen<AESCCMSetup>},
{"HMAC-SHA-1", 2, HMAC<EVP_sha1>},
{"HMAC-SHA2-224", 2, HMAC<EVP_sha224>},
{"HMAC-SHA2-256", 2, HMAC<EVP_sha256>},