• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-system / 20948398990

13 Jan 2026 07:28AM UTC coverage: 80.39% (-0.8%) from 81.203%
20948398990

Pull #1773

github

web-flow
Merge d75f2288a into 62dd8e3fd
Pull Request #1773: Add AES256-GCM.

0 of 132 new or added lines in 2 files covered. (0.0%)

3 existing lines in 2 files now uncovered.

10843 of 13488 relevant lines covered (80.39%)

3510970.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.06
/src/crypto/aes256.cpp
1
/**
2
 *   Byte-oriented AES-256 implementation.
3
 *   All lookup tables replaced with 'on the fly' calculations. 
4
 *
5
 *   Copyright (c) 2007-2009 Ilya O. Levin, http://www.literatecode.com
6
 *   Other contributors: Hal Finney
7
 *
8
 *   Permission to use, copy, modify, and distribute this software for any
9
 *   purpose with or without fee is hereby granted, provided that the above
10
 *   copyright notice and this permission notice appear in all copies.
11
 *
12
 *   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
 *   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
 *   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
 *   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
 *   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
 *   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
 *   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
 */
20
#include <bitcoin/system/crypto/aes256.hpp>
21

22
#include <bitcoin/system/define.hpp>
23
#include <bitcoin/system/data/data.hpp>
24
#include <bitcoin/system/endian/endian.hpp>
25
#include <bitcoin/system/math/math.hpp>
26

27
namespace libbitcoin {
28
namespace system {
29
namespace aes256 {
30

31
struct context
32
{
33
    secret key;
34
    secret enckey;
35
    secret deckey;
36
};
37

38
constexpr size_t rounds = 14;
39

40
constexpr data_array<to_bits(secret_size)> sbox
41
{
42
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
43
    0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
44
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
45
    0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
46
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
47
    0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
48
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
49
    0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
50
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
51
    0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
52
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
53
    0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
54
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
55
    0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
56
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
57
    0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
58
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
59
    0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
60
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
61
    0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
62
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
63
    0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
64
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
65
    0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
66
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
67
    0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
68
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
69
    0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
70
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
71
    0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
72
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
73
    0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
74
};
75

76
constexpr data_array<to_bits(secret_size)> sbox_inverse
77
{
78
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
79
    0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
80
    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
81
    0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
82
    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
83
    0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
84
    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
85
    0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
86
    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
87
    0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
88
    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
89
    0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
90
    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
91
    0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
92
    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
93
    0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
94
    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
95
    0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
96
    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
97
    0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
98
    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
99
    0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
100
    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
101
    0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
102
    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
103
    0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
104
    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
105
    0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
106
    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
107
    0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
108
    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
109
    0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
110
};
111

112
constexpr uint8_t f_enc_key(uint8_t byte) NOEXCEPT
1,394✔
113
{
114
    return shift_left(byte) ^ (get_left(byte) ? 0b0001'1011_u8 : zero);
1,394✔
115
}
116

117
constexpr uint8_t f_dec_key(uint8_t byte) NOEXCEPT
14✔
118
{
119
    return shift_right(byte) ^ (get_right(byte) ? 0b1000'1101_u8 : zero);
14✔
120
}
121

122
BC_PUSH_WARNING(NO_ARRAY_INDEXING)
123
BC_PUSH_WARNING(NO_DYNAMIC_ARRAY_INDEXING)
124

125
constexpr void sub_bytes(block& bytes) NOEXCEPT
28✔
126
{
127
    auto i = block_size;
28✔
128
    while (to_bool(i--))
476✔
129
        bytes[i] = sbox[bytes[i]];
448✔
130
}
28✔
131

132
constexpr void sub_bytes_inverse(block& bytes) NOEXCEPT
28✔
133
{
134
    auto i = block_size;
28✔
135
    while (to_bool(i--))
476✔
136
        bytes[i] = sbox_inverse[bytes[i]];
448✔
137
}
28✔
138

139
constexpr void add_round_key_lower(block& bytes, secret& key) NOEXCEPT
28✔
140
{
141
    auto i = block_size;
28✔
142
    while (to_bool(i--))
476✔
143
        bytes[i] ^= key[i];
448✔
144
}
28✔
145

146
constexpr void add_round_key_upper(block& bytes, secret& key) NOEXCEPT
28✔
147
{
148
    auto i = block_size;
28✔
149
    while (to_bool(i--))
476✔
150
        bytes[i] ^= key[i + block_size];
448✔
151
}
28✔
152

153
constexpr void add_round_key_copy(block& bytes, secret& key,
4✔
154
    secret& copy) NOEXCEPT
155
{
156
    auto i = block_size;
4✔
157
    while (to_bool(i--))
68✔
158
    {
159
        const auto j = i + block_size;
64✔
160
        copy[j] = key[j];
64✔
161
        copy[i] = key[i];
64✔
162
        bytes[i] ^= key[i];
64✔
163
    }
164
}
4✔
165

166
constexpr void shift_rows(block& bytes) NOEXCEPT
28✔
167
{
168
    uint8_t i, j;
28✔
169

170
    i = bytes[1];
28✔
171
    bytes[ 1] = bytes[5];
28✔
172
    bytes[ 5] = bytes[9];
28✔
173
    bytes[ 9] = bytes[13];
28✔
174
    bytes[13] = i;
28✔
175

176
    i = bytes[10];
28✔
177
    bytes[10] = bytes[2];
28✔
178
    bytes[ 2] = i;
28✔
179

180
    j = bytes[3];
28✔
181
    bytes[ 3] = bytes[15];
28✔
182
    bytes[15] = bytes[11];
28✔
183
    bytes[11] = bytes[7];
28✔
184
    bytes[ 7] = j;
28✔
185

186
    j = bytes[14];
28✔
187
    bytes[14] = bytes[6];
28✔
188
    bytes[ 6] = j;
28✔
189
}
28✔
190

191
constexpr void shift_rows_inverse(block& bytes) NOEXCEPT
28✔
192
{
193
    uint8_t i, j;
28✔
194

195
    i = bytes[1];
28✔
196
    bytes[ 1] = bytes[13];
28✔
197
    bytes[13] = bytes[9];
28✔
198
    bytes[ 9] = bytes[5];
28✔
199
    bytes[ 5] = i;
28✔
200

201
    i = bytes[2];
28✔
202
    bytes[ 2] = bytes[10];
28✔
203
    bytes[10] = i;
28✔
204

205
    j = bytes[3];
28✔
206
    bytes[ 3] = bytes[7];
28✔
207
    bytes[ 7] = bytes[11];
28✔
208
    bytes[11] = bytes[15];
28✔
209
    bytes[15] = j;
28✔
210

211
    j = bytes[6];
28✔
212
    bytes[ 6] = bytes[14];
28✔
213
    bytes[14] = j;
28✔
214
}
28✔
215

216
constexpr void mix_columns(block& bytes) NOEXCEPT
26✔
217
{
218
    uint8_t a, b, c, d, e;
26✔
219

220
    for (size_t i = 0; i < block_size; i += 4_size)
130✔
221
    {
222
        a = bytes[i + 0];
104✔
223
        b = bytes[i + 1];
104✔
224
        c = bytes[i + 2];
104✔
225
        d = bytes[i + 3];
104✔
226

227
        e = a ^ b ^ c ^ d;
104✔
228
        bytes[i + 0] ^= e ^ f_enc_key(a ^ b);
104✔
229
        bytes[i + 1] ^= e ^ f_enc_key(b ^ c);
104✔
230
        bytes[i + 2] ^= e ^ f_enc_key(c ^ d);
104✔
231
        bytes[i + 3] ^= e ^ f_enc_key(d ^ a);
104✔
232
    }
233
}
26✔
234

235
constexpr void mix_columns_inverse(block& bytes) NOEXCEPT
26✔
236
{
237
    uint8_t a, b, c, d, e, x, y, z;
26✔
238

239
    for (size_t i = 0; i < block_size; i += 4_size)
130✔
240
    {
241
        a = bytes[i + 0];
104✔
242
        b = bytes[i + 1];
104✔
243
        c = bytes[i + 2];
104✔
244
        d = bytes[i + 3];
104✔
245

246
        e = a ^ b ^ c ^ d;
104✔
247
        z = f_enc_key(e);
104✔
248
        x = e ^ f_enc_key(f_enc_key(z ^ a ^ c));
104✔
249
        y = e ^ f_enc_key(f_enc_key(z ^ b ^ d));
104✔
250

251
        bytes[i + 0] ^= x ^ f_enc_key(a ^ b);
104✔
252
        bytes[i + 1] ^= y ^ f_enc_key(b ^ c);
104✔
253
        bytes[i + 2] ^= x ^ f_enc_key(c ^ d);
104✔
254
        bytes[i + 3] ^= y ^ f_enc_key(d ^ a);
104✔
255
    }
256
}
26✔
257

258
constexpr void expand_key(secret& key, uint8_t& round) NOEXCEPT
42✔
259
{
260
    key[0] ^= sbox[key[29]] ^ round;
42✔
261
    key[1] ^= sbox[key[30]];
42✔
262
    key[2] ^= sbox[key[31]];
42✔
263
    key[3] ^= sbox[key[28]];
42✔
264
    round = f_enc_key(round);
42✔
265

266
    for (size_t i = 4; i < 16_size; i += 4_size)
168✔
267
    {
268
        key[i + 0] ^= key[i - 4];
126✔
269
        key[i + 1] ^= key[i - 3];
126✔
270
        key[i + 2] ^= key[i - 2];
126✔
271
        key[i + 3] ^= key[i - 1];
126✔
272
    }
273

274
    key[16] ^= sbox[key[12]];
42✔
275
    key[17] ^= sbox[key[13]];
42✔
276
    key[18] ^= sbox[key[14]];
42✔
277
    key[19] ^= sbox[key[15]];
42✔
278

279
    for (size_t i = 20; i < 32_size; i += 4_size)
168✔
280
    {
281
        key[i + 0] ^= key[i - 4];
126✔
282
        key[i + 1] ^= key[i - 3];
126✔
283
        key[i + 2] ^= key[i - 2];
126✔
284
        key[i + 3] ^= key[i - 1];
126✔
285
    }
286
}
42✔
287

288
constexpr void expand_key_inverse(secret& key, uint8_t& round) NOEXCEPT
14✔
289
{
290
    for (size_t i = 28; i > 16_size; i -= 4_size)
56✔
291
    {
292
        key[i + 0] ^= key[i - 4];
42✔
293
        key[i + 1] ^= key[i - 3];
42✔
294
        key[i + 2] ^= key[i - 2];
42✔
295
        key[i + 3] ^= key[i - 1];
42✔
296
    }
297

298
    key[16] ^= sbox[key[12]];
14✔
299
    key[17] ^= sbox[key[13]];
14✔
300
    key[18] ^= sbox[key[14]];
14✔
301
    key[19] ^= sbox[key[15]];
14✔
302

303
    for (size_t i = 12; i > 0_size; i -= 4_size)
56✔
304
    {
305
        key[i + 0] ^= key[i - 4];
42✔
306
        key[i + 1] ^= key[i - 3];
42✔
307
        key[i + 2] ^= key[i - 2];
42✔
308
        key[i + 3] ^= key[i - 1];
42✔
309
    }
310

311
    round = f_dec_key(round);
14✔
312
    key[0] ^= sbox[key[29]] ^ round;
14✔
313
    key[1] ^= sbox[key[30]];
14✔
314
    key[2] ^= sbox[key[31]];
14✔
315
    key[3] ^= sbox[key[28]];
14✔
316
}
14✔
317

318
BC_POP_WARNING()
319
BC_POP_WARNING()
320

321
constexpr void initialize(aes256::context& context, const secret& key) NOEXCEPT
4✔
322
{
323
    context.deckey = key;
4✔
324
    context.enckey = key;
4✔
325

326
    auto round = bit_lo<uint8_t>;
4✔
327
    for (size_t i = 0; i < sub1(bits<uint8_t>); ++i)
32✔
328
        expand_key(context.deckey, round);
28✔
329
}
4✔
330

331
constexpr void encrypt_block(aes256::context& context, block& bytes) NOEXCEPT
2✔
332
{
333
    add_round_key_copy(bytes, context.enckey, context.key);
2✔
334

335
    auto round = bit_lo<uint8_t>;
2✔
336
    for (size_t i = 0; i < sub1(rounds); ++i)
28✔
337
    {
338
        sub_bytes(bytes);
26✔
339
        shift_rows(bytes);
26✔
340
        mix_columns(bytes);
26✔
341

342
        if (is_even(i))
26✔
343
        {
344
            add_round_key_upper(bytes, context.key);
14✔
345
        }
346
        else
347
        {
348
            expand_key(context.key, round);
12✔
349
            add_round_key_lower(bytes, context.key);
12✔
350
        }
351
    }
352

353
    sub_bytes(bytes);
2✔
354
    shift_rows(bytes);
2✔
355
    expand_key(context.key, round);
2✔
356
    add_round_key_lower(bytes, context.key);
2✔
357
}
2✔
358

359
constexpr void decrypt_block(aes256::context& context, block& bytes) NOEXCEPT
2✔
360
{
361
    add_round_key_copy(bytes, context.deckey, context.key);
2✔
362

363
    shift_rows_inverse(bytes);
2✔
364
    sub_bytes_inverse(bytes);
2✔
365

366
    auto round = bit_hi<uint8_t>;
2✔
367
    for (size_t i = 0; i < sub1(rounds); ++i)
28✔
368
    {
369
        if (is_even(i))
26✔
370
        {
371
            expand_key_inverse(context.key, round);
14✔
372
            add_round_key_upper(bytes, context.key);
14✔
373
        }
374
        else
375
        {
376
            add_round_key_lower(bytes, context.key);
12✔
377
        }
378

379
        mix_columns_inverse(bytes);
26✔
380
        shift_rows_inverse(bytes);
26✔
381
        sub_bytes_inverse(bytes);
26✔
382
    }
383

384
    add_round_key_lower(bytes, context.key);
2✔
385
}
2✔
386

387
////constexpr void zeroize(aes256::context& context) NOEXCEPT
388
////{
389
////    context.key.fill(0);
390
////    context.enckey.fill(0);
391
////    context.deckey.fill(0);
392
////}
393

394
// published
395
// ----------------------------------------------------------------------------
396

397
void encrypt(block& bytes, const secret& key) NOEXCEPT
2✔
398
{
399
    aes256::context context;
2✔
400
    initialize(context, key);
2✔
401
    encrypt_block(context, bytes);
2✔
402
    ////zeroize(context);
403
}
2✔
404

405
void decrypt(block& bytes, const secret& key) NOEXCEPT
2✔
406
{
407
    aes256::context context;
2✔
408
    initialize(context, key);
2✔
409
    decrypt_block(context, bytes);
2✔
410
    ////zeroize(context);
411
}
2✔
412

413
// GCM implementation
414
// ----------------------------------------------------------------------------
415

416
template <typename Value, if_integer<Value> = true>
NEW
417
constexpr void bit_xor_into(Value& out, Value other) NOEXCEPT
×
418
{
NEW
419
    out = bit_xor(out, other);
×
NEW
420
}
×
421

422
template <size_t Size>
NEW
423
constexpr void array_xor_into(data_array<Size>& out,
×
424
    const data_array<Size>& other) NOEXCEPT
425
{
NEW
426
    for (size_t index{}; index < Size; ++index)
×
NEW
427
        bit_xor_into(out[index], other[index]);
×
NEW
428
}
×
429

430
template <size_t Size>
NEW
431
constexpr block array_xor(const data_array<Size>& left,
×
432
    const data_array<Size>& right) NOEXCEPT
433
{
NEW
434
    block out{ left };
×
NEW
435
    array_xor_into(out, right);
×
NEW
436
    return out;
×
437
}
438

439
template <size_t Size>
NEW
440
constexpr void array_shift_right_into(data_array<Size>& value) NOEXCEPT
×
441
{
442
    bool carry{};
NEW
443
    for (auto index = Size; is_nonzero(index); --index)
×
444
    {
445
        const auto offset = sub1(index);
NEW
446
        const bool next = get_right(value[offset]);
×
447
        shift_right_into(value[offset]);
NEW
448
        if (carry) set_left_into(value[offset]);
×
449
        carry = next;
450
    }
NEW
451
}
×
452

453
// GHASH multiplication in GF(2^128) with reduction polynomial 0xe1 << 120.
NEW
454
static block ghash_multiply(const block& left, block right) NOEXCEPT
×
455
{
NEW
456
    constexpr auto mask = 0b1110'0001_u8;
×
457

NEW
458
    block out{};
×
NEW
459
    for (size_t byte{}; byte < block_size; ++byte)
×
460
    {
NEW
461
        for (auto bit = bits<uint8_t>; is_nonzero(bit); --bit)
×
462
        {
NEW
463
            if (get_right(left[byte], sub1(bit)))
×
464
            {
NEW
465
                array_xor_into(out, right);
×
466
            }
467

NEW
468
            const bool carry = get_right(right[sub1(block_size)]);
×
NEW
469
            array_shift_right_into(right);
×
470

NEW
471
            if (carry)
×
472
            {
NEW
473
                bit_xor_into(right.front(), mask);
×
474
            }
475
        }
476
    }
477

NEW
478
    return out;
×
479
}
480

NEW
481
static void ghash_update(block& hash, const data_slice& data,
×
482
    const block& h_key) NOEXCEPT
483
{
NEW
484
    block block{};
×
NEW
485
    const auto size = data.size();
×
NEW
486
    const auto blocks = floored_divide(size, block_size);
×
487

NEW
488
    for (size_t index{}; index < blocks; ++index)
×
489
    {
NEW
490
        const auto begin = std::next(data.begin(), index * block_size);
×
NEW
491
        std::copy(begin, std::next(begin, block_size), block.begin());
×
NEW
492
        array_xor_into(hash, block);
×
NEW
493
        hash = ghash_multiply(hash, h_key);
×
494
    }
495

NEW
496
    if (is_nonzero(floored_modulo(size, block_size)))
×
497
    {
NEW
498
        block = {};
×
NEW
499
        const auto begin = std::next(data.begin(), blocks * block_size);
×
NEW
500
        std::copy(begin, data.end(), block.begin());
×
NEW
501
        array_xor_into(hash, block);
×
NEW
502
        hash = ghash_multiply(hash, h_key);
×
503
    }
NEW
504
}
×
505

NEW
506
static void increment_counter(block& counter) NOEXCEPT
×
507
{
NEW
508
    constexpr auto size = sizeof(uint32_t);
×
NEW
509
    constexpr auto offest = block_size - size;
×
NEW
510
    auto& slice = array_cast<uint8_t, size, offest>(counter);
×
NEW
511
    slice = to_big_endian(add1(from_big_endian(slice)));
×
NEW
512
}
×
513

NEW
514
static void ctr_encrypt(aes256::context& context, data_chunk& out,
×
515
    const data_slice& in, block counter) NOEXCEPT
516
{
NEW
517
    block stream{};
×
NEW
518
    const auto size = in.size();
×
NEW
519
    const auto blocks = floored_divide(size, block_size);
×
NEW
520
    out.resize(size);
×
521

NEW
522
    for (size_t block{}; block < blocks; ++block)
×
523
    {
NEW
524
        stream = counter;
×
NEW
525
        encrypt_block(context, stream);
×
NEW
526
        increment_counter(counter);
×
527

NEW
528
        for (size_t index{}; index < block_size; ++index)
×
529
        {
NEW
530
            const auto at = block * block_size + index;
×
NEW
531
            out[at] = bit_xor(in[at], stream[index]);
×
532
        }
533
    }
534

NEW
535
    if (const auto remain = floored_modulo(size, block_size);
×
536
        is_nonzero(remain))
537
    {
NEW
538
        stream = counter;
×
NEW
539
        encrypt_block(context, stream);
×
540

NEW
541
        for (size_t index{}; index < remain; ++index)
×
542
        {
NEW
543
            const auto at = blocks * block_size + index;
×
NEW
544
            out[at] = bit_xor(in[at], stream[index]);
×
545
        }
546
    }
NEW
547
}
×
548

NEW
549
constexpr size_t pad_size(size_t value_size) NOEXCEPT
×
550
{
NEW
551
    return (block_size - (value_size % block_size)) % block_size;
×
552
}
553

554
// GCM encrypt/decrypt
555
// ----------------------------------------------------------------------------
556

557
constexpr auto one32 = to_big_endian<uint32_t>(1);
558

NEW
559
bool gcm_encrypt(data_chunk& out, tag& out_tag, const data_slice& in,
×
560
    const secret& key, const nonce& nonce, const data_slice& aad) NOEXCEPT
561
{
NEW
562
    if (is_multiply_overflow<uint64_t>(aad.size(), byte_bits) ||
×
563
        is_multiply_overflow<uint64_t>(in.size(), byte_bits))
564
        return false;
565

NEW
566
    aes256::context context{};
×
NEW
567
    initialize(context, key);
×
568

569
    // Compute H = AES(0).
NEW
570
    block h_key{};
×
NEW
571
    encrypt_block(context, h_key);
×
572

573
    // Initialize counter: nonce || 0x00000001 (BE).
NEW
574
    block counter{};
×
NEW
575
    array_cast<uint8_t, 12, 0>(counter) = nonce;
×
NEW
576
    array_cast<uint8_t, 4, 12>(counter) = one32;
×
577

578
    // Encrypt plaintext to ciphertext (CTR mode).
NEW
579
    ctr_encrypt(context, out, in, counter);
×
580

581
    // Compute GHASH over aad || pad || out || pad || len(aad) || len(out).
NEW
582
    data_chunk pad{};
×
NEW
583
    block hash{};
×
584

NEW
585
    ghash_update(hash, aad, h_key);
×
NEW
586
    pad.resize(pad_size(aad.size()));
×
NEW
587
    if (!pad.empty())
×
NEW
588
        ghash_update(hash, pad, h_key);
×
589

NEW
590
    ghash_update(hash, out, h_key);
×
NEW
591
    pad.resize(pad_size(out.size()));
×
NEW
592
    if (!pad.empty())
×
NEW
593
        ghash_update(hash, pad, h_key);
×
594

595
    // Append lengths (out.size() is same as in.size() - overflows guarded).
NEW
596
    block lengths{};
×
NEW
597
    const auto aad_length = aad.size() * byte_bits;
×
NEW
598
    const auto out_length = out.size() * byte_bits;
×
NEW
599
    array_cast<uint8_t, 8, 0>(lengths) = to_big_endian<uint64_t>(aad_length);
×
NEW
600
    array_cast<uint8_t, 8, 8>(lengths) = to_big_endian<uint64_t>(out_length);
×
NEW
601
    ghash_update(hash, lengths, h_key);
×
602

603
    // Initialize counter: nonce || 0x00000001 (BE) (and encrypt).
NEW
604
    array_cast<uint8_t, 12, 0>(counter) = nonce;
×
NEW
605
    array_cast<uint8_t, 4, 12>(counter) = one32;
×
NEW
606
    encrypt_block(context, counter);
×
607

608
    // Tag = GHASH ^ E(counter0).
NEW
609
    out_tag = array_xor(hash, counter);
×
NEW
610
    return true;
×
611
}
612

NEW
613
bool gcm_decrypt(data_chunk& out, const tag& tag, const data_slice& in,
×
614
    const secret& key, const nonce& nonce, const data_slice& aad) NOEXCEPT
615
{
NEW
616
    if (is_multiply_overflow<uint64_t>(aad.size(), byte_bits) ||
×
617
        is_multiply_overflow<uint64_t>(in.size(), byte_bits))
618
        return false;
619

NEW
620
    aes256::context context{};
×
NEW
621
    initialize(context, key);
×
622

623
    // Compute H = AES(0).
NEW
624
    block h_key{};
×
NEW
625
    encrypt_block(context, h_key);
×
626

627
    // Compute GHASH over aad || pad || in || pad || len(aad) || len(in).
NEW
628
    data_chunk pad{};
×
NEW
629
    block hash{};
×
630

NEW
631
    ghash_update(hash, aad, h_key);
×
NEW
632
    pad.resize(pad_size(aad.size()));
×
NEW
633
    if (!pad.empty())
×
NEW
634
        ghash_update(hash, pad, h_key);
×
635

NEW
636
    ghash_update(hash, in, h_key);
×
NEW
637
    pad.resize(pad_size(in.size()));
×
NEW
638
    if (!pad.empty())
×
NEW
639
        ghash_update(hash, pad, h_key);
×
640

641
    // Append lengths (overflows guarded).
NEW
642
    block lengths{};
×
NEW
643
    const auto aad_length = aad.size() * byte_bits;
×
NEW
644
    const auto in_length = in.size() * byte_bits;
×
NEW
645
    array_cast<uint8_t, 8, 0>(lengths) = to_big_endian<uint64_t>(aad_length);
×
NEW
646
    array_cast<uint8_t, 8, 8>(lengths) = to_big_endian<uint64_t>(in_length);
×
NEW
647
    ghash_update(hash, lengths, h_key);
×
648

649
    // Initialize counter: nonce || 0x00000001 (BE) (and encrypt).
NEW
650
    block counter{};
×
NEW
651
    array_cast<uint8_t, 12, 0>(counter) = nonce;
×
NEW
652
    array_cast<uint8_t, 4, 12>(counter) = one32;
×
NEW
653
    encrypt_block(context, counter);
×
654

655
    // Verify tag = GHASH ^ E(counter0)
NEW
656
    if (array_xor(hash, counter) != tag)
×
657
        return false;
658

659
    // Decrypt ciphertext to plaintext (CTR mode, same as encrypt).
NEW
660
    ctr_encrypt(context, out, in, counter);
×
NEW
661
    return true;
×
662
}
663

664
} // namespace aes256
665
} // namespace system
666
} // namespace libbitcoin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc