1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_crypto/aes.h"
16
17 #include <algorithm>
18 #include <iterator>
19
20 #include "pw_assert/assert.h"
21 #include "pw_containers/vector.h"
22 #include "pw_crypto/aes_cmac.h"
23 #include "pw_status/status.h"
24 #include "pw_unit_test/framework.h"
25
26 #define EXPECT_OK(expr) EXPECT_EQ(pw::OkStatus(), (expr))
27 #define STR_TO_BYTES(str) (as_bytes(span(str).subspan<0, sizeof(str) - 1>()))
28
29 namespace pw::crypto::aes {
30 namespace {
31
32 using backend::AesOperation;
33 using backend::SupportedKeySize;
34 using internal::BackendSupports;
35 using unsafe::aes::EncryptBlock;
36 using Cmac = aes_cmac::Cmac;
37
38 template <typename T>
ZeroOut(T & container)39 void ZeroOut(T& container) {
40 std::fill(std::begin(container), std::end(container), std::byte{0});
41 }
42
43 // Create a view (`span<const T>`, as opposed to a `span<T>`) of a `pw::Vector`.
44 template <typename T>
View(pw::Vector<T> & vector)45 span<const T> View(pw::Vector<T>& vector) {
46 return span(vector.begin(), vector.end());
47 }
48
49 template <typename T, size_t N>
SpanToArray(const span<T,N> & span)50 std::array<typename std::remove_cv<T>::type, N> SpanToArray(
51 const span<T, N>& span) {
52 static_assert(N != dynamic_extent, "Must have statically-known size.");
53 static_assert(std::is_trivially_copyable<T>::value, "Must be memcpy-able.");
54
55 std::array<typename std::remove_cv<T>::type, N> result;
56 std::memcpy(result.data(), span.data(), sizeof(result));
57
58 return result;
59 }
60
61 template <typename T>
ToArray(const T & container)62 auto ToArray(const T& container) {
63 return SpanToArray(span(container));
64 }
65
66 // Intentionally chosen to not be a valid AES key size, but larger than the
67 // largest AES key size.
68 constexpr size_t kMaxVectorSize = 503;
69
TEST(Aes,Cmac)70 TEST(Aes, Cmac) {
71 constexpr auto kCmacOp = AesOperation::kCmac;
72
73 span<const std::byte> msg1 = STR_TO_BYTES("Hello");
74 span<const std::byte> msg2 = STR_TO_BYTES(", world!");
75 span<const std::byte> msg_full = STR_TO_BYTES("Hello, world!");
76 span<const std::byte> msg_empty{};
77
78 // Output buffer for computed MAC.
79 Block mac;
80 // Ensure dynamically-sized keys will work.
81 Vector<std::byte, kMaxVectorSize> dynamic_key;
82
83 auto reset = [&] {
84 ZeroOut(mac);
85 ZeroOut(dynamic_key);
86 dynamic_key.clear();
87 };
88
89 if constexpr (BackendSupports<kCmacOp>(SupportedKeySize::k128)) {
90 span<const std::byte, 16> key = STR_TO_BYTES(
91 "\x1E\xE4\x01\x50\x0D\xFB\x92\x84\x3A\x73\xBB\xCD\xA6\x6E\x94\xC4");
92 Block expected = SpanToArray(STR_TO_BYTES(
93 "\x30\xF5\xFB\xB8\xB9\x12\x38\xF9\x18\x5D\xA2\x8C\xD8\xAD\x13\xEF"));
94
95 reset();
96 EXPECT_OK(Cmac(key).Update(msg1).Update(msg2).Final(mac));
97 EXPECT_EQ(mac, expected);
98
99 reset();
100 EXPECT_OK(Cmac(key).Update(msg_full).Final(mac));
101 EXPECT_EQ(mac, expected);
102
103 reset();
104 EXPECT_OK(Cmac(key).Update(msg_empty).Final(mac));
105
106 reset();
107 std::copy(key.begin(), key.end(), std::back_inserter(dynamic_key));
108 EXPECT_OK(Cmac(dynamic_key).Update(msg_full).Final(mac));
109 EXPECT_EQ(mac, expected);
110 }
111
112 if constexpr (BackendSupports<kCmacOp>(SupportedKeySize::k192)) {
113 span<const std::byte, 24> key = STR_TO_BYTES(
114 "\x3B\x42\x7C\xB3\xCE\x89\x14\xFC\x89\x78\x00\x84\xC2\x29\x10\xC5"
115 "\xC8\x42\x46\xE5\x2D\x16\x53\xC8");
116 Block expected = SpanToArray(STR_TO_BYTES(
117 "\x0E\xE7\xD6\xCD\x29\xBF\x35\xBE\xEA\xFF\x7C\xC6\xAE\x52\xC7\xBD"));
118
119 reset();
120 EXPECT_OK(Cmac(key).Update(msg1).Update(msg2).Final(mac));
121 EXPECT_EQ(mac, expected);
122
123 reset();
124 EXPECT_OK(Cmac(key).Update(msg_full).Final(mac));
125 EXPECT_EQ(mac, expected);
126
127 reset();
128 EXPECT_OK(Cmac(key).Update(msg_empty).Final(mac));
129
130 reset();
131 std::copy(key.begin(), key.end(), std::back_inserter(dynamic_key));
132 EXPECT_OK(Cmac(dynamic_key).Update(msg_full).Final(mac));
133 EXPECT_EQ(mac, expected);
134 }
135
136 if constexpr (BackendSupports<kCmacOp>(SupportedKeySize::k256)) {
137 span<const std::byte, 32> key = STR_TO_BYTES(
138 "\x64\xC9\x63\x43\x83\xF8\xFA\xC9\xEC\x15\x3B\xBF\x04\xDD\x80\xB4"
139 "\x20\x4B\x05\x87\xD6\x94\x65\xFA\x49\x5E\x48\x4D\x85\x6A\x58\x03");
140 Block expected = SpanToArray(STR_TO_BYTES(
141 "\x76\x4D\x24\x2E\xF2\x0A\x94\xD4\xF3\x42\xCD\x46\x71\x4A\xC0\x4E"));
142
143 reset();
144 EXPECT_OK(Cmac(key).Update(msg1).Update(msg2).Final(mac));
145 EXPECT_EQ(mac, expected);
146
147 reset();
148 EXPECT_OK(Cmac(key).Update(msg_full).Final(mac));
149 EXPECT_EQ(mac, expected);
150
151 reset();
152 EXPECT_OK(Cmac(key).Update(msg_empty).Final(mac));
153
154 reset();
155 std::copy(key.begin(), key.end(), std::back_inserter(dynamic_key));
156 EXPECT_OK(Cmac(dynamic_key).Update(msg_full).Final(mac));
157 EXPECT_EQ(mac, expected);
158 }
159 }
160
TEST(Aes,UnsafeEncryptApi)161 TEST(Aes, UnsafeEncryptApi) {
162 constexpr auto kRawEncryptBlockOp = AesOperation::kUnsafeEncryptBlock;
163 ConstBlockSpan message_block = STR_TO_BYTES("hello, world!\0\0\0");
164
165 // Ensure various output types work correctly.
166 std::byte encrypted_carr[16];
167 std::array<std::byte, 16> encrypted_arr;
168 Block encrypted_block;
169
170 // Ensure dynamically-sized keys will work.
171 Vector<std::byte, kMaxVectorSize> dynamic_key;
172
173 auto reset = [&] {
174 ZeroOut(dynamic_key);
175 ZeroOut(encrypted_carr);
176 ZeroOut(encrypted_arr);
177 ZeroOut(encrypted_block);
178
179 dynamic_key.clear();
180 };
181
182 if constexpr (BackendSupports<kRawEncryptBlockOp>(SupportedKeySize::k128)) {
183 span<const std::byte, 16> key = STR_TO_BYTES(
184 "\x13\xA2\x27\x93\x8D\x1D\x89\x46\x07\x4C\xA0\x71\xF2\xF7\x54\xC5");
185 Block expected = SpanToArray(STR_TO_BYTES(
186 "\xC0\x9A\x54\x34\xFD\xB8\xB4\x37\xAD\x84\x67\x60\x79\x8D\xCE\x40"));
187
188 reset();
189 EXPECT_OK(EncryptBlock(key, message_block, encrypted_carr));
190 EXPECT_EQ(ToArray(encrypted_carr), expected);
191 EXPECT_OK(EncryptBlock(key, message_block, encrypted_arr));
192 EXPECT_EQ(encrypted_arr, expected);
193 EXPECT_OK(EncryptBlock(key, message_block, encrypted_block));
194 EXPECT_EQ(encrypted_block, expected);
195
196 reset();
197 std::copy(key.begin(), key.end(), std::back_inserter(dynamic_key));
198 EXPECT_OK(EncryptBlock(View(dynamic_key), message_block, encrypted_block));
199 EXPECT_EQ(encrypted_block, expected);
200 }
201
202 if constexpr (BackendSupports<kRawEncryptBlockOp>(SupportedKeySize::k192)) {
203 span<const std::byte, 24> key = STR_TO_BYTES(
204 "\x2B\x43\x70\x51\xBF\x91\xF0\xFD\x4E\x9B\x89\xB7\x35\x40\xD4\x1B"
205 "\x15\xBC\xD7\xC2\x22\xBC\x03\x76");
206 Block expected = SpanToArray(STR_TO_BYTES(
207 "\x35\x45\xC1\xA5\x89\x73\x1F\x28\x2E\x92\xAC\x24\x37\x85\xFC\xCA"));
208
209 reset();
210 EXPECT_OK(EncryptBlock(key, message_block, encrypted_carr));
211 EXPECT_EQ(ToArray(encrypted_carr), expected);
212 EXPECT_OK(EncryptBlock(key, message_block, encrypted_arr));
213 EXPECT_EQ(encrypted_arr, expected);
214 EXPECT_OK(EncryptBlock(key, message_block, encrypted_block));
215 EXPECT_EQ(encrypted_block, expected);
216
217 reset();
218 std::copy(key.begin(), key.end(), std::back_inserter(dynamic_key));
219 EXPECT_OK(EncryptBlock(View(dynamic_key), message_block, encrypted_block));
220 EXPECT_EQ(encrypted_block, expected);
221 }
222
223 if constexpr (BackendSupports<kRawEncryptBlockOp>(SupportedKeySize::k256)) {
224 span<const std::byte, 32> key = STR_TO_BYTES(
225 "\xA4\xB9\x15\x76\xF2\x16\x67\xB0\x33\x5E\xA6\x8D\xBD\x23\xDF\x29"
226 "\x84\xBF\x8D\xBE\x56\x77\x13\x28\x14\x55\xD9\x75\xDD\xEE\x4E\x0B");
227 Block expected = SpanToArray(STR_TO_BYTES(
228 "\x9B\xC4\x12\x39\xB7\x2A\xA1\x14\xB3\x6E\x6C\xAE\x2C\x7f\xDD\xE7"));
229
230 reset();
231 EXPECT_OK(EncryptBlock(key, message_block, encrypted_carr));
232 EXPECT_EQ(ToArray(encrypted_carr), expected);
233 EXPECT_OK(EncryptBlock(key, message_block, encrypted_arr));
234 EXPECT_EQ(encrypted_arr, expected);
235 EXPECT_OK(EncryptBlock(key, message_block, encrypted_block));
236 EXPECT_EQ(encrypted_block, expected);
237
238 reset();
239 std::copy(key.begin(), key.end(), std::back_inserter(dynamic_key));
240 EXPECT_OK(EncryptBlock(View(dynamic_key), message_block, encrypted_block));
241 EXPECT_EQ(encrypted_block, expected);
242 }
243 }
244
245 } // namespace
246 } // namespace pw::crypto::aes
247