• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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