1 /* Copyright (c) 2015, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #include <stdio.h>
16
17 #include <algorithm>
18 #include <vector>
19
20 #include <gtest/gtest.h>
21
22 #include <openssl/cipher.h>
23 #include <openssl/cmac.h>
24 #include <openssl/mem.h>
25
26 #include "../test/file_test.h"
27 #include "../test/test_util.h"
28 #include "../test/wycheproof_util.h"
29
30
test(const char * name,const uint8_t * key,size_t key_len,const uint8_t * msg,size_t msg_len,const uint8_t * expected)31 static void test(const char *name, const uint8_t *key, size_t key_len,
32 const uint8_t *msg, size_t msg_len, const uint8_t *expected) {
33 SCOPED_TRACE(name);
34
35 // Test the single-shot API.
36 uint8_t out[16];
37 ASSERT_TRUE(AES_CMAC(out, key, key_len, msg, msg_len));
38 EXPECT_EQ(Bytes(expected, sizeof(out)), Bytes(out));
39
40 bssl::UniquePtr<CMAC_CTX> ctx(CMAC_CTX_new());
41 ASSERT_TRUE(ctx);
42 ASSERT_TRUE(CMAC_Init(ctx.get(), key, key_len, EVP_aes_128_cbc(), NULL));
43
44 for (unsigned chunk_size = 1; chunk_size <= msg_len; chunk_size++) {
45 SCOPED_TRACE(chunk_size);
46
47 ASSERT_TRUE(CMAC_Reset(ctx.get()));
48
49 size_t done = 0;
50 while (done < msg_len) {
51 size_t todo = std::min(msg_len - done, static_cast<size_t>(chunk_size));
52 ASSERT_TRUE(CMAC_Update(ctx.get(), msg + done, todo));
53 done += todo;
54 }
55
56 size_t out_len;
57 ASSERT_TRUE(CMAC_Final(ctx.get(), out, &out_len));
58 EXPECT_EQ(Bytes(expected, sizeof(out)), Bytes(out, out_len));
59 }
60
61 // Test that |CMAC_CTX_copy| works.
62 ASSERT_TRUE(CMAC_Reset(ctx.get()));
63 size_t chunk = msg_len / 2;
64 ASSERT_TRUE(CMAC_Update(ctx.get(), msg, chunk));
65 bssl::UniquePtr<CMAC_CTX> ctx2(CMAC_CTX_new());
66 ASSERT_TRUE(ctx2);
67 ASSERT_TRUE(CMAC_CTX_copy(ctx2.get(), ctx.get()));
68 ASSERT_TRUE(CMAC_Update(ctx2.get(), msg + chunk, msg_len - chunk));
69 size_t out_len;
70 ASSERT_TRUE(CMAC_Final(ctx2.get(), out, &out_len));
71 EXPECT_EQ(Bytes(expected, sizeof(out)), Bytes(out, out_len));
72 }
73
TEST(CMACTest,RFC4493TestVectors)74 TEST(CMACTest, RFC4493TestVectors) {
75 static const uint8_t kKey[16] = {
76 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
77 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
78 };
79 static const uint8_t kOut1[16] = {
80 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28,
81 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46,
82 };
83 static const uint8_t kMsg2[] = {
84 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
85 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
86 };
87 static const uint8_t kOut2[16] = {
88 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
89 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c,
90 };
91 static const uint8_t kMsg3[] = {
92 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
93 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
94 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
95 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
96 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
97 };
98 static const uint8_t kOut3[16] = {
99 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30,
100 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27,
101 };
102 static const uint8_t kMsg4[] = {
103 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
104 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
105 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
106 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
107 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
108 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
109 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
110 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
111 };
112 static const uint8_t kOut4[16] = {
113 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92,
114 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe,
115 };
116
117 test("RFC 4493 #1", kKey, sizeof(kKey), NULL, 0, kOut1);
118 test("RFC 4493 #2", kKey, sizeof(kKey), kMsg2, sizeof(kMsg2), kOut2);
119 test("RFC 4493 #3", kKey, sizeof(kKey), kMsg3, sizeof(kMsg3), kOut3);
120 test("RFC 4493 #4", kKey, sizeof(kKey), kMsg4, sizeof(kMsg4), kOut4);
121 }
122
TEST(CMACTest,Wycheproof)123 TEST(CMACTest, Wycheproof) {
124 FileTestGTest("third_party/wycheproof_testvectors/aes_cmac_test.txt",
125 [](FileTest *t) {
126 std::string key_size, tag_size;
127 ASSERT_TRUE(t->GetInstruction(&key_size, "keySize"));
128 ASSERT_TRUE(t->GetInstruction(&tag_size, "tagSize"));
129 WycheproofResult result;
130 ASSERT_TRUE(GetWycheproofResult(t, &result));
131 std::vector<uint8_t> key, msg, tag;
132 ASSERT_TRUE(t->GetBytes(&key, "key"));
133 ASSERT_TRUE(t->GetBytes(&msg, "msg"));
134 ASSERT_TRUE(t->GetBytes(&tag, "tag"));
135
136 const EVP_CIPHER *cipher;
137 switch (atoi(key_size.c_str())) {
138 case 128:
139 cipher = EVP_aes_128_cbc();
140 break;
141 case 192:
142 cipher = EVP_aes_192_cbc();
143 break;
144 case 256:
145 cipher = EVP_aes_256_cbc();
146 break;
147 default:
148 // Some test vectors intentionally give the wrong key size. Our API
149 // requires the caller pick the sized CBC primitive, so these tests
150 // aren't useful for us.
151 EXPECT_EQ(WycheproofResult::kInvalid, result);
152 return;
153 }
154
155 size_t tag_len = static_cast<size_t>(atoi(tag_size.c_str())) / 8;
156
157 uint8_t out[16];
158 bssl::UniquePtr<CMAC_CTX> ctx(CMAC_CTX_new());
159 ASSERT_TRUE(ctx);
160 ASSERT_TRUE(CMAC_Init(ctx.get(), key.data(), key.size(), cipher, NULL));
161 ASSERT_TRUE(CMAC_Update(ctx.get(), msg.data(), msg.size()));
162 size_t out_len;
163 ASSERT_TRUE(CMAC_Final(ctx.get(), out, &out_len));
164 // Truncate the tag, if requested.
165 out_len = std::min(out_len, tag_len);
166
167 if (result == WycheproofResult::kValid) {
168 EXPECT_EQ(Bytes(tag), Bytes(out, out_len));
169
170 // Test the streaming API as well.
171 ASSERT_TRUE(CMAC_Reset(ctx.get()));
172 for (uint8_t b : msg) {
173 ASSERT_TRUE(CMAC_Update(ctx.get(), &b, 1));
174 }
175 ASSERT_TRUE(CMAC_Final(ctx.get(), out, &out_len));
176 out_len = std::min(out_len, tag_len);
177 EXPECT_EQ(Bytes(tag), Bytes(out, out_len));
178 } else {
179 // Wycheproof's invalid tests assume the implementation internally does
180 // the comparison, whereas our API only computes the tag. Check that
181 // they're not equal, but these tests are mostly not useful for us.
182 EXPECT_NE(Bytes(tag), Bytes(out, out_len));
183 }
184 });
185 }
186
RunCAVPTest(const char * path,const EVP_CIPHER * cipher,bool is_3des)187 static void RunCAVPTest(const char *path, const EVP_CIPHER *cipher,
188 bool is_3des) {
189 FileTestGTest(path, [&](FileTest *t) {
190 t->IgnoreAttribute("Count");
191 t->IgnoreAttribute("Klen");
192 std::string t_len, m_len, result;
193 ASSERT_TRUE(t->GetAttribute(&t_len, "Tlen"));
194 ASSERT_TRUE(t->GetAttribute(&m_len, "Mlen"));
195 ASSERT_TRUE(t->GetAttribute(&result, "Result"));
196 std::vector<uint8_t> key, msg, mac;
197 if (is_3des) {
198 std::vector<uint8_t> key2, key3;
199 ASSERT_TRUE(t->GetBytes(&key, "Key1"));
200 ASSERT_TRUE(t->GetBytes(&key2, "Key2"));
201 ASSERT_TRUE(t->GetBytes(&key3, "Key3"));
202 key.insert(key.end(), key2.begin(), key2.end());
203 key.insert(key.end(), key3.begin(), key3.end());
204 } else {
205 ASSERT_TRUE(t->GetBytes(&key, "Key"));
206 }
207 ASSERT_TRUE(t->GetBytes(&msg, "Msg"));
208 ASSERT_TRUE(t->GetBytes(&mac, "Mac"));
209
210 // CAVP's uses a non-empty Msg attribute and zero Mlen for the empty string.
211 if (atoi(m_len.c_str()) == 0) {
212 msg.clear();
213 } else {
214 EXPECT_EQ(static_cast<size_t>(atoi(m_len.c_str())), msg.size());
215 }
216
217 size_t tag_len = static_cast<size_t>(atoi(t_len.c_str()));
218
219 uint8_t out[16];
220 bssl::UniquePtr<CMAC_CTX> ctx(CMAC_CTX_new());
221 ASSERT_TRUE(ctx);
222 ASSERT_TRUE(CMAC_Init(ctx.get(), key.data(), key.size(), cipher, NULL));
223 ASSERT_TRUE(CMAC_Update(ctx.get(), msg.data(), msg.size()));
224 size_t out_len;
225 ASSERT_TRUE(CMAC_Final(ctx.get(), out, &out_len));
226 // Truncate the tag, if requested.
227 out_len = std::min(out_len, tag_len);
228
229 ASSERT_FALSE(result.empty());
230 if (result[0] == 'P') {
231 EXPECT_EQ(Bytes(mac), Bytes(out, out_len));
232
233 // Test the streaming API as well.
234 ASSERT_TRUE(CMAC_Reset(ctx.get()));
235 for (uint8_t b : msg) {
236 ASSERT_TRUE(CMAC_Update(ctx.get(), &b, 1));
237 }
238 ASSERT_TRUE(CMAC_Final(ctx.get(), out, &out_len));
239 out_len = std::min(out_len, tag_len);
240 EXPECT_EQ(Bytes(mac), Bytes(out, out_len));
241 } else {
242 // CAVP's invalid tests assume the implementation internally does the
243 // comparison, whereas our API only computes the tag. Check that they're
244 // not equal, but these tests are mostly not useful for us.
245 EXPECT_NE(Bytes(mac), Bytes(out, out_len));
246 }
247 });
248 }
249
TEST(CMACTest,CAVPAES128)250 TEST(CMACTest, CAVPAES128) {
251 RunCAVPTest("crypto/cmac/cavp_aes128_cmac_tests.txt", EVP_aes_128_cbc(),
252 false);
253 }
254
TEST(CMACTest,CAVPAES192)255 TEST(CMACTest, CAVPAES192) {
256 RunCAVPTest("crypto/cmac/cavp_aes192_cmac_tests.txt", EVP_aes_192_cbc(),
257 false);
258 }
259
TEST(CMACTest,CAVPAES256)260 TEST(CMACTest, CAVPAES256) {
261 RunCAVPTest("crypto/cmac/cavp_aes256_cmac_tests.txt", EVP_aes_256_cbc(),
262 false);
263 }
264
TEST(CMACTest,CAVP3DES)265 TEST(CMACTest, CAVP3DES) {
266 RunCAVPTest("crypto/cmac/cavp_3des_cmac_tests.txt", EVP_des_ede3_cbc(), true);
267 }
268