]>
Commit | Line | Data |
---|---|---|
737375a2 AS |
1 | /* |
2 | * Copyright (C) 2016-2019 Andreas Steffen | |
19ef2aec TB |
3 | * |
4 | * Copyright (C) secunet Security Networks AG | |
737375a2 AS |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | */ | |
16 | ||
17 | #include "drbg_ctr.h" | |
18 | ||
a43407df AS |
19 | #define MAX_DRBG_REQUESTS 0xfffffffe /* 2^32 - 2 */ |
20 | #define MAX_DRBG_BYTES 0x00010000 /* 2^19 bits = 2^16 bytes */ | |
737375a2 AS |
21 | |
22 | typedef struct private_drbg_ctr_t private_drbg_ctr_t; | |
23 | ||
24 | /** | |
25 | * Private data of an drbg_ctr_t object. | |
26 | */ | |
27 | struct private_drbg_ctr_t { | |
28 | ||
29 | /** | |
30 | * Public drbg_ctr_t interface. | |
31 | */ | |
32 | drbg_ctr_t public; | |
33 | ||
34 | /** | |
35 | * DRBG type. | |
36 | */ | |
37 | drbg_type_t type; | |
38 | ||
39 | /** | |
40 | * Security strength in bits. | |
41 | */ | |
42 | uint32_t strength; | |
43 | ||
44 | /** | |
45 | * Number of requests for pseudorandom bits | |
46 | */ | |
47 | uint32_t reseed_counter; | |
48 | ||
49 | /** | |
50 | * Maximum number of requests for pseudorandom bits | |
51 | */ | |
52 | uint32_t max_requests; | |
53 | ||
54 | /** | |
55 | * True entropy source | |
56 | */ | |
57 | rng_t *entropy; | |
58 | ||
59 | /** | |
60 | * Block cipher in counter mode used by the DRBG | |
61 | */ | |
62 | crypter_t *crypter; | |
63 | ||
64 | /** | |
65 | * Internal state of HMAC: key | |
66 | */ | |
67 | chunk_t key; | |
68 | ||
69 | /** | |
70 | * Internal state of HMAC: value | |
71 | */ | |
72 | chunk_t value; | |
73 | ||
74 | /** | |
75 | * reference count | |
76 | */ | |
77 | refcount_t ref; | |
78 | ||
79 | }; | |
80 | ||
81 | METHOD(drbg_t, get_type, drbg_type_t, | |
82 | private_drbg_ctr_t *this) | |
83 | { | |
84 | return this->type; | |
85 | } | |
86 | ||
87 | METHOD(drbg_t, get_strength, uint32_t, | |
88 | private_drbg_ctr_t *this) | |
89 | { | |
90 | return this->strength; | |
91 | } | |
92 | ||
93 | static bool encrypt_ctr(private_drbg_ctr_t *this, chunk_t out) | |
94 | { | |
737375a2 AS |
95 | chunk_t bl = chunk_alloca(this->value.len); |
96 | chunk_t block; | |
97 | size_t delta, pos = 0; | |
737375a2 | 98 | |
737375a2 AS |
99 | if (!this->crypter->set_key(this->crypter, this->key)) |
100 | { | |
101 | return FALSE; | |
102 | } | |
103 | ||
104 | while (pos < out.len) | |
105 | { | |
106 | /* Increment counter by one */ | |
107 | chunk_increment(this->value); | |
108 | ||
109 | /* Copy current counter to input block */ | |
110 | delta = out.len - pos; | |
111 | block = (delta < this->value.len) ? | |
112 | bl : chunk_create(out.ptr + pos, this->value.len); | |
113 | memcpy(block.ptr, this->value.ptr, this->value.len); | |
114 | ||
115 | /* ECB encryption */ | |
86a4b95e | 116 | if (!this->crypter->encrypt(this->crypter, block, chunk_empty, NULL)) |
737375a2 AS |
117 | { |
118 | return FALSE; | |
119 | } | |
120 | ||
121 | /* Partial output block at the end? */ | |
122 | if (delta < this->value.len) | |
123 | { | |
124 | memcpy(out.ptr + pos, block.ptr, delta); | |
125 | } | |
126 | pos += this->value.len; | |
127 | } | |
128 | ||
129 | return TRUE; | |
130 | } | |
131 | ||
132 | /** | |
133 | * Update the internal state of the CTR_DRBG | |
134 | */ | |
135 | static bool update(private_drbg_ctr_t *this, chunk_t data) | |
136 | { | |
137 | chunk_t temp; | |
138 | ||
139 | if (data.len && data.len != (this->key.len + this->value.len)) | |
140 | { | |
141 | return FALSE; | |
142 | } | |
143 | temp = chunk_alloca(this->key.len + this->value.len); | |
144 | ||
145 | if (!encrypt_ctr(this, temp)) | |
146 | { | |
147 | return FALSE; | |
148 | } | |
149 | /* Apply data */ | |
150 | memxor(temp.ptr, data.ptr, data.len); | |
151 | ||
152 | /* Copy new key and value */ | |
153 | memcpy(this->key.ptr, temp.ptr, this->key.len); | |
154 | memcpy(this->value.ptr, temp.ptr + this->key.len, this->value.len); | |
155 | memwipe(temp.ptr, temp.len); | |
156 | DBG4(DBG_LIB, "CTR_DRBG K: %B", &this->key); | |
157 | DBG4(DBG_LIB, "CTR_DRBG V: %B", &this->value); | |
158 | ||
159 | return TRUE; | |
160 | } | |
161 | ||
162 | METHOD(drbg_t, reseed, bool, | |
163 | private_drbg_ctr_t *this) | |
164 | { | |
165 | chunk_t seed; | |
166 | bool success; | |
167 | ||
168 | seed = chunk_alloc(this->key.len + this->value.len); | |
169 | DBG2(DBG_LIB, "DRBG requests %u bytes of entropy", seed.len); | |
170 | ||
171 | if (!this->entropy->get_bytes(this->entropy, seed.len, seed.ptr)) | |
172 | { | |
173 | chunk_free(&seed); | |
174 | return FALSE; | |
175 | } | |
176 | DBG4(DBG_LIB, "reseed: %B", &seed); | |
177 | ||
178 | success = update(this, seed); | |
179 | chunk_clear(&seed); | |
180 | ||
181 | if (!success) | |
182 | { | |
183 | return FALSE; | |
184 | } | |
185 | this->reseed_counter = 1; | |
186 | ||
187 | return TRUE; | |
188 | } | |
189 | ||
190 | METHOD(drbg_t, generate, bool, | |
191 | private_drbg_ctr_t *this, uint32_t len, uint8_t *out) | |
192 | { | |
193 | chunk_t output; | |
194 | ||
a43407df AS |
195 | if (len > MAX_DRBG_BYTES) |
196 | { | |
197 | DBG1(DBG_LIB, "DRBG cannot generate more than %d bytes", MAX_DRBG_BYTES); | |
198 | return FALSE; | |
199 | } | |
200 | ||
737375a2 AS |
201 | if (this->reseed_counter > this->max_requests) |
202 | { | |
203 | if (!reseed(this)) | |
204 | { | |
205 | return FALSE; | |
206 | } | |
207 | } | |
208 | ||
209 | DBG2(DBG_LIB, "DRBG generates %u pseudorandom bytes", len); | |
210 | if (!out || len == 0) | |
211 | { | |
212 | return FALSE; | |
213 | } | |
214 | output = chunk_create(out, len); | |
215 | ||
216 | if (!encrypt_ctr(this, output)) | |
217 | { | |
218 | return FALSE; | |
219 | } | |
220 | DBG4(DBG_LIB, "CTR_DRBG Out: %B", &output); | |
221 | ||
222 | if (!update(this, chunk_empty)) | |
223 | { | |
224 | return FALSE; | |
225 | } | |
226 | this->reseed_counter++; | |
227 | ||
228 | return TRUE; | |
229 | } | |
230 | ||
231 | METHOD(drbg_t, get_ref, drbg_t*, | |
232 | private_drbg_ctr_t *this) | |
233 | { | |
234 | ref_get(&this->ref); | |
235 | return &this->public.interface; | |
236 | } | |
237 | ||
238 | METHOD(drbg_t, destroy, void, | |
239 | private_drbg_ctr_t *this) | |
240 | { | |
241 | if (ref_put(&this->ref)) | |
242 | { | |
11e9d2b8 | 243 | DESTROY_IF(this->entropy); |
737375a2 AS |
244 | this->crypter->destroy(this->crypter); |
245 | chunk_clear(&this->key); | |
246 | chunk_clear(&this->value); | |
247 | free(this); | |
248 | } | |
249 | } | |
250 | ||
251 | /** | |
252 | * See header | |
253 | */ | |
254 | drbg_ctr_t *drbg_ctr_create(drbg_type_t type, uint32_t strength, | |
255 | rng_t *entropy, chunk_t personalization_str) | |
256 | { | |
257 | private_drbg_ctr_t *this; | |
258 | encryption_algorithm_t crypter_type; | |
259 | crypter_t *crypter; | |
260 | chunk_t seed; | |
261 | size_t key_len, out_len, seed_len; | |
262 | uint32_t max_requests; | |
263 | bool success; | |
264 | ||
265 | switch (type) | |
266 | { | |
267 | case DRBG_CTR_AES128: | |
86a4b95e | 268 | crypter_type = ENCR_AES_ECB; |
737375a2 AS |
269 | key_len = 16; |
270 | break; | |
271 | case DRBG_CTR_AES192: | |
86a4b95e | 272 | crypter_type = ENCR_AES_ECB; |
737375a2 AS |
273 | key_len = 24; |
274 | break; | |
275 | case DRBG_CTR_AES256: | |
86a4b95e | 276 | crypter_type = ENCR_AES_ECB; |
737375a2 AS |
277 | key_len = 32; |
278 | break; | |
279 | default: | |
280 | DBG1(DBG_LIB, "%N not supported", drbg_type_names, type); | |
281 | return NULL; | |
282 | } | |
283 | ||
284 | if (strength > key_len * BITS_PER_BYTE) | |
285 | { | |
286 | DBG1(DBG_LIB, "%d bit block encryption key not sufficient for security " | |
e635d3dc | 287 | "strength of %u bits", key_len * BITS_PER_BYTE, strength); |
737375a2 AS |
288 | return NULL; |
289 | } | |
290 | ||
291 | crypter = lib->crypto->create_crypter(lib->crypto, crypter_type, key_len); | |
292 | if (!crypter) | |
293 | { | |
294 | DBG1(DBG_LIB, "creation of %N for DRBG failed", | |
295 | encryption_algorithm_names, crypter_type); | |
296 | return NULL; | |
297 | } | |
298 | out_len = crypter->get_block_size(crypter); | |
299 | seed_len = key_len + out_len; | |
300 | ||
301 | if (personalization_str.len > seed_len) | |
302 | { | |
303 | DBG1(DBG_LIB, "personalization string length of %d bytes is larger " | |
e635d3dc | 304 | "than seed length of %u bytes", personalization_str.len, seed_len); |
737375a2 AS |
305 | crypter->destroy(crypter); |
306 | return NULL; | |
307 | } | |
308 | ||
309 | max_requests = lib->settings->get_int(lib->settings, | |
310 | "%s.plugins.drbg.max_drbg_requests", | |
311 | MAX_DRBG_REQUESTS, lib->ns); | |
312 | ||
313 | INIT(this, | |
314 | .public = { | |
315 | .interface = { | |
316 | .get_type = _get_type, | |
317 | .get_strength = _get_strength, | |
318 | .reseed = _reseed, | |
319 | .generate = _generate, | |
320 | .get_ref = _get_ref, | |
321 | .destroy = _destroy, | |
322 | }, | |
323 | }, | |
324 | .type = type, | |
325 | .strength = strength, | |
737375a2 AS |
326 | .crypter = crypter, |
327 | .key = chunk_alloc(key_len), | |
328 | .value = chunk_alloc(out_len), | |
329 | .max_requests = max_requests, | |
330 | .reseed_counter = 1, | |
331 | .ref = 1, | |
332 | ); | |
333 | ||
334 | memset(this->key.ptr, 0x00, key_len); | |
335 | memset(this->value.ptr, 0x00, out_len); | |
336 | ||
337 | seed = chunk_alloc(seed_len); | |
338 | DBG2(DBG_LIB, "DRBG requests %u bytes of entropy", seed_len); | |
339 | ||
11e9d2b8 | 340 | if (!entropy->get_bytes(entropy, seed.len, seed.ptr)) |
737375a2 AS |
341 | { |
342 | chunk_free(&seed); | |
343 | destroy(this); | |
344 | return NULL; | |
345 | } | |
346 | memxor(seed.ptr, personalization_str.ptr, personalization_str.len); | |
347 | DBG4(DBG_LIB, "seed: %B", &seed); | |
348 | ||
349 | success = update(this, seed); | |
350 | chunk_clear(&seed); | |
351 | ||
352 | if (!success) | |
353 | { | |
354 | destroy(this); | |
355 | return NULL; | |
356 | } | |
357 | ||
11e9d2b8 AS |
358 | /* ownership of entropy source is transferred to DRBG */ |
359 | this->entropy = entropy; | |
360 | ||
737375a2 AS |
361 | return &this->public; |
362 | } |