Botan 3.13.0
Crypto and TLS for C&
nist_keywrap.cpp
Go to the documentation of this file.
1/*
2* (C) 2011,2017 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/nist_keywrap.h>
8
9#include <botan/block_cipher.h>
10#include <botan/exceptn.h>
11#include <botan/internal/int_utils.h>
12#include <botan/internal/loadstor.h>
13
14namespace Botan {
15
16namespace {
17
18std::vector<uint8_t> raw_nist_key_wrap(const uint8_t input[], size_t input_len, const BlockCipher& bc, uint64_t ICV) {
19 const size_t n = input_len / 8 + (input_len % 8 != 0 ? 1 : 0);
20
21 secure_vector<uint8_t> R(mul_or_throw<size_t>(8, n + 1, "NIST key wrap input too large"));
23
24 store_be(ICV, A.data());
25
26 copy_mem(&R[8], input, input_len);
27
28 for(size_t j = 0; j <= 5; ++j) {
29 for(size_t i = 1; i <= n; ++i) {
30 const uint32_t t = static_cast<uint32_t>((n * j) + i);
31
32 copy_mem(&A[8], &R[8 * i], 8);
33
34 bc.encrypt(A.data());
35 copy_mem(&R[8 * i], &A[8], 8);
36
37 uint8_t t_buf[4] = {0};
38 store_be(t, t_buf);
39 xor_buf(&A[4], t_buf, 4);
40 }
41 }
42
43 copy_mem(R.data(), A.data(), 8);
44
45 return std::vector<uint8_t>(R.begin(), R.end());
46}
47
48secure_vector<uint8_t> raw_nist_key_unwrap(const uint8_t input[],
49 size_t input_len,
50 const BlockCipher& bc,
51 uint64_t& ICV_out) {
52 if(input_len < 16 || input_len % 8 != 0) {
53 throw Invalid_Argument("Bad input size for NIST key unwrap");
54 }
55
56 const size_t n = (input_len - 8) / 8;
57
60
61 for(size_t i = 0; i != 8; ++i) {
62 A[i] = input[i];
63 }
64
65 copy_mem(R.data(), input + 8, input_len - 8);
66
67 for(size_t j = 0; j <= 5; ++j) {
68 for(size_t i = n; i != 0; --i) {
69 const uint32_t t = static_cast<uint32_t>((5 - j) * n + i);
70
71 uint8_t t_buf[4] = {0};
72 store_be(t, t_buf);
73
74 xor_buf(&A[4], t_buf, 4);
75
76 copy_mem(&A[8], &R[8 * (i - 1)], 8);
77
78 bc.decrypt(A.data());
79
80 copy_mem(&R[8 * (i - 1)], &A[8], 8);
81 }
82 }
83
84 ICV_out = load_be<uint64_t>(A.data(), 0);
85
86 return R;
87}
88
89} // namespace
90
91std::vector<uint8_t> nist_key_wrap(const uint8_t input[], size_t input_len, const BlockCipher& bc) {
92 if(bc.block_size() != 16) {
93 throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
94 }
95
96 if(input_len == 0 || input_len % 8 != 0) {
97 throw Invalid_Argument("Bad input size for NIST key wrap");
98 }
99
100 const uint64_t ICV = 0xA6A6A6A6A6A6A6A6;
101
102 if(input_len == 8) {
103 /*
104 * Special case for small inputs: if input == 8 bytes just use ECB
105 * (see RFC 3394 Section 2)
106 */
107 std::vector<uint8_t> block(16);
108 store_be(ICV, block.data());
109 copy_mem(block.data() + 8, input, input_len);
110 bc.encrypt(block);
111 return block;
112 } else {
113 return raw_nist_key_wrap(input, input_len, bc, ICV);
114 }
115}
116
117secure_vector<uint8_t> nist_key_unwrap(const uint8_t input[], size_t input_len, const BlockCipher& bc) {
118 if(bc.block_size() != 16) {
119 throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
120 }
121
122 if(input_len < 16 || input_len % 8 != 0) {
123 throw Invalid_Argument("Bad input size for NIST key unwrap");
124 }
125
126 const uint64_t ICV = 0xA6A6A6A6A6A6A6A6;
127
128 uint64_t ICV_out = 0;
130
131 if(input_len == 16) {
132 secure_vector<uint8_t> block(input, input + input_len);
133 bc.decrypt(block);
134
135 ICV_out = load_be<uint64_t>(block.data(), 0);
136 R.resize(8);
137 copy_mem(R.data(), block.data() + 8, 8);
138 } else {
139 R = raw_nist_key_unwrap(input, input_len, bc, ICV_out);
140 }
141
142 if(ICV_out != ICV) {
143 throw Invalid_Authentication_Tag("NIST key unwrap failed");
144 }
145
146 return R;
147}
148
149std::vector<uint8_t> nist_key_wrap_padded(const uint8_t input[], size_t input_len, const BlockCipher& bc) {
150 if(bc.block_size() != 16) {
151 throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
152 }
153
154 if(input_len == 0) {
155 throw Invalid_Argument("NIST KWP cannot accept empty inputs");
156 }
157
158 const uint64_t ICV = 0xA65959A600000000 | static_cast<uint32_t>(input_len);
159
160 if(input_len <= 8) {
161 /*
162 * Special case for small inputs: if input <= 8 bytes just use ECB
163 */
164 std::vector<uint8_t> block(16);
165 store_be(ICV, block.data());
166 copy_mem(block.data() + 8, input, input_len);
167 bc.encrypt(block);
168 return block;
169 } else {
170 return raw_nist_key_wrap(input, input_len, bc, ICV);
171 }
172}
173
174secure_vector<uint8_t> nist_key_unwrap_padded(const uint8_t input[], size_t input_len, const BlockCipher& bc) {
175 if(bc.block_size() != 16) {
176 throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
177 }
178
179 if(input_len < 16 || input_len % 8 != 0) {
180 throw Invalid_Argument("Bad input size for NIST key unwrap");
181 }
182
183 uint64_t ICV_out = 0;
185
186 if(input_len == 16) {
187 secure_vector<uint8_t> block(input, input + input_len);
188 bc.decrypt(block);
189
190 ICV_out = load_be<uint64_t>(block.data(), 0);
191 R.resize(8);
192 copy_mem(R.data(), block.data() + 8, 8);
193 } else {
194 R = raw_nist_key_unwrap(input, input_len, bc, ICV_out);
195 }
196
197 /*
198 The padded key wrap ICV is 0xA65959A6 || uint32(plaintext_length).
199
200 We know the expected ICV almost entirely: the top 32 bits are the
201 fixed constant and the bottom 32 bits encode the original plaintext
202 length, which is R.size() minus 0 to 7 bytes of padding. Compute
203 the ICV we'd expect for the zero-padding case and subtract ICV_out;
204 for a valid unwrap the difference is at most 7, and equals the padding.
205 For an invalid unwrap the unsigned subtraction wraps to a value > 7
206 (checked below), so the modular arithmetic here is intentional.
207 */
208 const uint64_t expected_ICV_max = 0xA65959A600000000 | static_cast<uint32_t>(R.size());
209 const uint64_t padding = expected_ICV_max - ICV_out;
210
211 if(padding > 7) {
212 throw Invalid_Authentication_Tag("NIST key unwrap failed");
213 }
214
215 // Verify padding bytes are zero
216 const uint64_t last_block = load_be<uint64_t>(R.data() + R.size() - 8, 0);
217 const uint64_t padding_mask = (static_cast<uint64_t>(1) << (padding * 8)) - 1;
218 if((last_block & padding_mask) != 0) {
219 throw Invalid_Authentication_Tag("NIST key unwrap failed");
220 }
221
222 R.resize(R.size() - static_cast<size_t>(padding));
223 return R;
224}
225
226} // namespace Botan
void encrypt(const uint8_t in[], uint8_t out[]) const
void decrypt(const uint8_t in[], uint8_t out[]) const
virtual size_t block_size() const =0
std::vector< uint8_t > nist_key_wrap(const uint8_t input[], size_t input_len, const BlockCipher &bc)
constexpr T mul_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:81
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
std::vector< uint8_t > nist_key_wrap_padded(const uint8_t input[], size_t input_len, const BlockCipher &bc)
secure_vector< uint8_t > nist_key_unwrap_padded(const uint8_t input[], size_t input_len, const BlockCipher &bc)
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:403
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504
secure_vector< uint8_t > nist_key_unwrap(const uint8_t input[], size_t input_len, const BlockCipher &bc)