Software APIs
ghash_unittest.cc
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #include "sw/device/lib/crypto/impl/aes_gcm/ghash.h"
6 
7 #include <array>
8 
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
11 
12 namespace ghash_unittest {
13 namespace {
14 using ::testing::ElementsAreArray;
15 
16 TEST(Ghash, McGrawViegaTestCase1) {
17  // GHASH computation from test case 1 of:
18  // https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
19  //
20  // H: 66e94bd4ef8a2c3b884cfa59ca342b2e
21  // A: empty
22  // C: empty
23  // GHASH(H,A,C): 00000000000000000000000000000000
24  std::array<uint32_t, 4> H = {
25  0xd44be966,
26  0x3b2c8aef,
27  0x59fa4c88,
28  0x2e2b34ca,
29  };
30  std::array<uint32_t, 4> exp_result = {0, 0, 0, 0};
31 
32  // Compute GHASH(H, A, C).
33  ghash_context_t ctx;
34  ghash_init_subkey(H.data(), &ctx);
35  ghash_init(&ctx);
36  uint32_t result[kGhashBlockNumWords];
37  ghash_final(&ctx, result);
38 
39  EXPECT_THAT(result, testing::ElementsAreArray(exp_result));
40 }
41 
42 TEST(Ghash, ProcessFullBlocksOneByte) {
43  // H: 66e94bd4ef8a2c3b884cfa59ca342b2e
44  std::array<uint32_t, 4> H = {
45  0xd44be966,
46  0x3b2c8aef,
47  0x59fa4c88,
48  0x2e2b34ca,
49  };
50  // Partial block (empty).
51  ghash_block_t partial = {.data = {0}};
52  size_t partial_len = 0;
53  // Input data (too short for a full block).
54  uint32_t input_word = 0xaabbccdd;
55  uint8_t *input = (unsigned char *)&input_word;
56  size_t input_len = sizeof(input_word);
57  // All-zero block for comparison.
58  std::array<uint32_t, 4> zero_block = {0, 0, 0, 0};
59 
60  // Initialize context.
61  ghash_context_t ctx;
62  ghash_init_subkey(H.data(), &ctx);
63  ghash_init(&ctx);
64  EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(zero_block));
65  ghash_process_full_blocks(&ctx, partial_len, &partial, input_len, input);
66  EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(zero_block));
67  EXPECT_EQ(partial.data[0], input_word);
68  EXPECT_EQ(partial.data[1], 0);
69  EXPECT_EQ(partial.data[2], 0);
70  EXPECT_EQ(partial.data[3], 0);
71 }
72 
73 TEST(Ghash, Mul1) {
74  // Multiply the hash subkey by 1.
75  // H: 66e94bd4ef8a2c3b884cfa59ca342b2e
76  // GHASH(H,1) = H
77  std::array<uint32_t, 4> H = {
78  0xd44be966,
79  0x3b2c8aef,
80  0x59fa4c88,
81  0x2e2b34ca,
82  };
83  std::array<uint32_t, 4> zero = {0, 0, 0, 0};
84  // Big-endian form of 1.
85  uint8_t one = 0x80;
86 
87  ghash_context_t ctx;
88  ghash_init_subkey(H.data(), &ctx);
89  ghash_init(&ctx);
90  EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(zero));
91 
92  ghash_block_t partial = {.data = {0}};
93  size_t partial_len = 0;
94  size_t input_len = 1;
95  uint8_t *input = &one;
96  ghash_process_full_blocks(&ctx, partial_len, &partial, input_len, input);
97  EXPECT_LT(input_len, kGhashBlockNumBytes - partial_len);
98  EXPECT_EQ(partial.data[0], one);
99  EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(zero));
100 
101  ghash_update(&ctx, 1, &one);
102  EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(H));
103  uint32_t result[kGhashBlockNumWords];
104  ghash_final(&ctx, result);
105 
106  EXPECT_THAT(result, testing::ElementsAreArray(H));
107 }
108 
109 TEST(Ghash, McGrawViegaTestCase2) {
110  // GHASH computation from test case 2 of:
111  // https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
112  //
113  // H: 66e94bd4ef8a2c3b884cfa59ca342b2e
114  // A: empty
115  // C: 0388dace60b6a392f328c2b971b2fe78
116  // GHASH(H,A,C): f38cbb1ad69223dcc3457ae5b6b0f885
117  std::array<uint32_t, 4> H = {
118  0xd44be966,
119  0x3b2c8aef,
120  0x59fa4c88,
121  0x2e2b34ca,
122  };
123  std::array<uint32_t, 0> A = {};
124  std::array<uint32_t, 4> C = {
125  0xceda8803,
126  0x92a3b660,
127  0xb9c228f3,
128  0x78feb271,
129  };
130  std::array<uint32_t, 4> exp_result = {
131  0x1abb8cf3,
132  0xdc2392d6,
133  0xe57a45c3,
134  0x85f8b0b6,
135  };
136 
137  // Encode bitlengths of A and C as big-endian 64-bit integers.
138  std::array<uint64_t, 2> bitlengths = {
139  A.size() * sizeof(uint32_t) * 8,
140  C.size() * sizeof(uint32_t) * 8,
141  };
142  bitlengths[0] = __builtin_bswap64(bitlengths[0]);
143  bitlengths[1] = __builtin_bswap64(bitlengths[1]);
144 
145  // Compute GHASH(H, A, C).
146  ghash_context_t ctx;
147  ghash_init_subkey(H.data(), &ctx);
148  ghash_init(&ctx);
149  ghash_update(&ctx, A.size() * sizeof(uint32_t), (unsigned char *)A.data());
150  ghash_update(&ctx, C.size() * sizeof(uint32_t), (unsigned char *)C.data());
151  ghash_update(&ctx, bitlengths.size() * sizeof(uint64_t),
152  (unsigned char *)bitlengths.data());
153  uint32_t result[kGhashBlockNumWords];
154  ghash_final(&ctx, result);
155 
156  EXPECT_THAT(result, testing::ElementsAreArray(exp_result));
157 }
158 
159 TEST(Ghash, ContextReset) {
160  // Run a test case twice to ensure that (a) the hash state is properly reset
161  // by `init()`, so that the result is correct both times and (b) the hash
162  // subkey is not cleared by `init()`.
163  std::array<uint32_t, 4> H = {
164  0xd44be966,
165  0x3b2c8aef,
166  0x59fa4c88,
167  0x2e2b34ca,
168  };
169  std::array<uint32_t, 0> A = {};
170  std::array<uint32_t, 4> C = {
171  0xceda8803,
172  0x92a3b660,
173  0xb9c228f3,
174  0x78feb271,
175  };
176  std::array<uint32_t, 4> exp_result = {
177  0x1abb8cf3,
178  0xdc2392d6,
179  0xe57a45c3,
180  0x85f8b0b6,
181  };
182 
183  // Encode bitlengths of A and C as big-endian 64-bit integers.
184  std::array<uint64_t, 2> bitlengths = {
185  A.size() * sizeof(uint32_t) * 8,
186  C.size() * sizeof(uint32_t) * 8,
187  };
188  bitlengths[0] = __builtin_bswap64(bitlengths[0]);
189  bitlengths[1] = __builtin_bswap64(bitlengths[1]);
190 
191  // Initialize the hash subkey (should only need to do this once).
192  ghash_context_t ctx;
193  ghash_init_subkey(H.data(), &ctx);
194 
195  // Compute GHASH(H, A, C).
196  ghash_init(&ctx);
197  ghash_update(&ctx, A.size() * sizeof(uint32_t), (unsigned char *)A.data());
198  ghash_update(&ctx, C.size() * sizeof(uint32_t), (unsigned char *)C.data());
199  ghash_update(&ctx, bitlengths.size() * sizeof(uint64_t),
200  (unsigned char *)bitlengths.data());
201  uint32_t result[kGhashBlockNumWords];
202  ghash_final(&ctx, result);
203 
204  EXPECT_THAT(result, testing::ElementsAreArray(exp_result));
205 
206  // Compute GHASH(H, A, C) a second time.
207  ghash_init(&ctx);
208  ghash_update(&ctx, A.size() * sizeof(uint32_t), (unsigned char *)A.data());
209  ghash_update(&ctx, C.size() * sizeof(uint32_t), (unsigned char *)C.data());
210  ghash_update(&ctx, bitlengths.size() * sizeof(uint64_t),
211  (unsigned char *)bitlengths.data());
212  ghash_final(&ctx, result);
213 
214  EXPECT_THAT(result, testing::ElementsAreArray(exp_result));
215 }
216 
217 TEST(Ghash, McGrawViegaTestCase18) {
218  // GHASH computation from test case 18 of:
219  // https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
220  //
221  // H: acbef20579b4b8ebce889bac8732dad7
222  // A: feedfacedeadbeeffeedfacedeadbeefabaddad2
223  // C:
224  // 5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f
225  // GHASH(H,A,C): d5ffcf6fc5ac4d69722187421a7f170b
226  std::array<uint32_t, 4> H = {
227  0x05f2beac,
228  0xebb8b479,
229  0xac9b88ce,
230  0xd7da3287,
231  };
232  std::array<uint32_t, 5> A = {
233  0xcefaedfe, 0xefbeadde, 0xcefaedfe, 0xefbeadde, 0xd2daadab,
234  };
235  std::array<uint32_t, 15> C = {
236  0x2fef8d5a, 0xf1539e0c, 0x53785df7, 0x202a9e65, 0x2ab2b2ee,
237  0x1964deaf, 0x4fab58a0, 0xf46b746f, 0xb7c3c00f, 0x4544f280,
238  0xf1eba32d, 0xde2cd8c5, 0x978941a2, 0x2ef80e20, 0x3f7eae44,
239  };
240  std::array<uint32_t, 4> exp_result = {
241  0x6fcfffd5,
242  0x694dacc5,
243  0x42872172,
244  0x0b177f1a,
245  };
246 
247  // Encode bitlengths of A and C as big-endian 64-bit integers.
248  std::array<uint64_t, 2> bitlengths = {
249  A.size() * sizeof(uint32_t) * 8,
250  C.size() * sizeof(uint32_t) * 8,
251  };
252  bitlengths[0] = __builtin_bswap64(bitlengths[0]);
253  bitlengths[1] = __builtin_bswap64(bitlengths[1]);
254 
255  // Compute GHASH(H, A, C).
256  ghash_context_t ctx;
257  ghash_init_subkey(H.data(), &ctx);
258  ghash_init(&ctx);
259  ghash_update(&ctx, A.size() * sizeof(uint32_t), (unsigned char *)A.data());
260  ghash_update(&ctx, C.size() * sizeof(uint32_t), (unsigned char *)C.data());
261  ghash_update(&ctx, bitlengths.size() * sizeof(uint64_t),
262  (unsigned char *)bitlengths.data());
263  uint32_t result[kGhashBlockNumWords];
264  ghash_final(&ctx, result);
265 
266  EXPECT_THAT(result, testing::ElementsAreArray(exp_result));
267 }
268 
269 } // namespace
270 } // namespace ghash_unittest