source: mainline/uspace/lib/crypto/crypto.c@ 064e0fd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 064e0fd was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 10.6 KB
Line 
1/*
2 * Copyright (c) 2015 Jan Kolarik
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @file crypto.c
30 *
31 * Cryptographic functions library.
32 */
33
34#include <str.h>
35#include <macros.h>
36#include <errno.h>
37#include <byteorder.h>
38#include "crypto.h"
39
40/** Hash function procedure definition. */
41typedef void (*hash_fnc_t)(uint32_t *, uint32_t *);
42
43/** Length of HMAC block. */
44#define HMAC_BLOCK_LENGTH 64
45
46/** Ceiling for uint32_t. */
47#define ceil_uint32(val) \
48 (((val) - (uint32_t) (val)) > 0 ? \
49 (uint32_t) ((val) + 1) : (uint32_t) (val))
50
51/** Floor for uint32_t. */
52#define floor_uint32(val) \
53 (((val) - (uint32_t) (val)) < 0 ? \
54 (uint32_t) ((val) - 1) : (uint32_t) (val))
55
56/** Pick value at specified index from array or zero if out of bounds. */
57#define get_at(input, size, i) \
58 ((i) < (size) ? (input[i]) : 0)
59
60/** Init values used in SHA1 and MD5 functions. */
61static const uint32_t hash_init[] = {
62 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
63};
64
65/** Shift amount array for MD5 algorithm. */
66static const uint32_t md5_shift[] = {
67 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
68 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
69 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
70 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
71};
72
73/** Substitution box for MD5 algorithm. */
74static const uint32_t md5_sbox[] = {
75 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
76 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
77 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
78 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
79 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
80 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
81 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
82 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
83 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
84 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
85 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
86 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
87 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
88 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
89 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
90 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
91};
92
93/** Working procedure of MD5 cryptographic hash function.
94 *
95 * @param h Working array with interim hash parts values.
96 * @param sched_arr Input array with scheduled values from input string.
97 *
98 */
99static void md5_proc(uint32_t *h, uint32_t *sched_arr)
100{
101 uint32_t f, g, temp;
102 uint32_t w[HASH_MD5 / 4];
103
104 memcpy(w, h, (HASH_MD5 / 4) * sizeof(uint32_t));
105
106 for (size_t k = 0; k < 64; k++) {
107 if (k < 16) {
108 f = (w[1] & w[2]) | (~w[1] & w[3]);
109 g = k;
110 } else if ((k >= 16) && (k < 32)) {
111 f = (w[1] & w[3]) | (w[2] & ~w[3]);
112 g = (5 * k + 1) % 16;
113 } else if ((k >= 32) && (k < 48)) {
114 f = w[1] ^ w[2] ^ w[3];
115 g = (3 * k + 5) % 16;
116 } else {
117 f = w[2] ^ (w[1] | ~w[3]);
118 g = 7 * k % 16;
119 }
120
121 temp = w[3];
122 w[3] = w[2];
123 w[2] = w[1];
124 w[1] += rotl_uint32(w[0] + f + md5_sbox[k] +
125 uint32_t_byteorder_swap(sched_arr[g]),
126 md5_shift[k]);
127 w[0] = temp;
128 }
129
130 for (uint8_t k = 0; k < HASH_MD5 / 4; k++)
131 h[k] += w[k];
132}
133
134/** Working procedure of SHA-1 cryptographic hash function.
135 *
136 * @param h Working array with interim hash parts values.
137 * @param sched_arr Input array with scheduled values from input string.
138 *
139 */
140static void sha1_proc(uint32_t *h, uint32_t *sched_arr)
141{
142 uint32_t f, cf, temp;
143 uint32_t w[HASH_SHA1 / 4];
144
145 for (size_t k = 16; k < 80; k++) {
146 sched_arr[k] = rotl_uint32(
147 sched_arr[k-3] ^
148 sched_arr[k-8] ^
149 sched_arr[k-14] ^
150 sched_arr[k-16],
151 1);
152 }
153
154 memcpy(w, h, (HASH_SHA1 / 4) * sizeof(uint32_t));
155
156 for (size_t k = 0; k < 80; k++) {
157 if (k < 20) {
158 f = (w[1] & w[2]) | (~w[1] & w[3]);
159 cf = 0x5A827999;
160 } else if ((k >= 20) && (k < 40)) {
161 f = w[1] ^ w[2] ^ w[3];
162 cf = 0x6ed9eba1;
163 } else if ((k >= 40) && (k < 60)) {
164 f = (w[1] & w[2]) | (w[1] & w[3]) | (w[2] & w[3]);
165 cf = 0x8f1bbcdc;
166 } else {
167 f = w[1] ^ w[2] ^ w[3];
168 cf = 0xca62c1d6;
169 }
170
171 temp = rotl_uint32(w[0], 5) + f + w[4] + cf + sched_arr[k];
172
173 w[4] = w[3];
174 w[3] = w[2];
175 w[2] = rotl_uint32(w[1], 30);
176 w[1] = w[0];
177 w[0] = temp;
178 }
179
180 for (uint8_t k = 0; k < HASH_SHA1 / 4; k++)
181 h[k] += w[k];
182}
183
184/** Create hash based on selected algorithm.
185 *
186 * @param input Input message byte sequence.
187 * @param input_size Size of message sequence.
188 * @param output Result hash byte sequence.
189 * @param hash_sel Hash function selector.
190 *
191 * @return EINVAL when input not specified,
192 * ENOMEM when pointer for output hash result
193 * is not allocated, otherwise EOK.
194 *
195 */
196errno_t create_hash(uint8_t *input, size_t input_size, uint8_t *output,
197 hash_func_t hash_sel)
198{
199 if (!input)
200 return EINVAL;
201
202 if (!output)
203 return ENOMEM;
204
205 hash_fnc_t hash_func = (hash_sel == HASH_MD5) ? md5_proc : sha1_proc;
206
207 /* Prepare scheduled input. */
208 uint8_t work_input[input_size + 1];
209 memcpy(work_input, input, input_size);
210 work_input[input_size] = 0x80;
211
212 // FIXME: double?
213 size_t blocks = ceil_uint32((((double) input_size + 1) / 4 + 2) / 16);
214 uint32_t work_arr[blocks * 16];
215 for (size_t i = 0; i < blocks; i++) {
216 for (size_t j = 0; j < 16; j++) {
217 work_arr[i*16 + j] =
218 (get_at(work_input, input_size + 1, i * 64 + j * 4) << 24) |
219 (get_at(work_input, input_size + 1, i * 64 + j * 4 + 1) << 16) |
220 (get_at(work_input, input_size + 1, i * 64 + j * 4 + 2) << 8) |
221 get_at(work_input, input_size + 1, i * 64 + j * 4 + 3);
222 }
223 }
224
225 uint64_t bits_size = (uint64_t) (input_size * 8);
226 if (hash_sel == HASH_MD5)
227 bits_size = uint64_t_byteorder_swap(bits_size);
228
229 work_arr[(blocks - 1) * 16 + 14] = bits_size >> 32;
230 work_arr[(blocks - 1) * 16 + 15] = bits_size & 0xffffffff;
231
232 /* Hash computation. */
233 uint32_t h[hash_sel / 4];
234 memcpy(h, hash_init, (hash_sel / 4) * sizeof(uint32_t));
235 uint32_t sched_arr[80];
236 for (size_t i = 0; i < blocks; i++) {
237 for (size_t k = 0; k < 16; k++)
238 sched_arr[k] = work_arr[i * 16 + k];
239
240 hash_func(h, sched_arr);
241 }
242
243 /* Copy hash parts into final result. */
244 for (size_t i = 0; i < hash_sel / 4; i++) {
245 if (hash_sel == HASH_SHA1)
246 h[i] = uint32_t_byteorder_swap(h[i]);
247
248 memcpy(output + i * sizeof(uint32_t), &h[i], sizeof(uint32_t));
249 }
250
251 return EOK;
252}
253
254/** Hash-based message authentication code.
255 *
256 * @param key Cryptographic key sequence.
257 * @param key_size Size of key sequence.
258 * @param msg Message sequence.
259 * @param msg_size Size of message sequence.
260 * @param hash Output parameter for result hash.
261 * @param hash_sel Hash function selector.
262 *
263 * @return EINVAL when key or message not specified,
264 * ENOMEM when pointer for output hash result
265 * is not allocated, otherwise EOK.
266 *
267 */
268errno_t hmac(uint8_t *key, size_t key_size, uint8_t *msg, size_t msg_size,
269 uint8_t *hash, hash_func_t hash_sel)
270{
271 if ((!key) || (!msg))
272 return EINVAL;
273
274 if (!hash)
275 return ENOMEM;
276
277 uint8_t work_key[HMAC_BLOCK_LENGTH];
278 uint8_t o_key_pad[HMAC_BLOCK_LENGTH];
279 uint8_t i_key_pad[HMAC_BLOCK_LENGTH];
280 uint8_t temp_hash[hash_sel];
281 memset(work_key, 0, HMAC_BLOCK_LENGTH);
282
283 if(key_size > HMAC_BLOCK_LENGTH)
284 create_hash(key, key_size, work_key, hash_sel);
285 else
286 memcpy(work_key, key, key_size);
287
288 for (size_t i = 0; i < HMAC_BLOCK_LENGTH; i++) {
289 o_key_pad[i] = work_key[i] ^ 0x5c;
290 i_key_pad[i] = work_key[i] ^ 0x36;
291 }
292
293 uint8_t temp_work[HMAC_BLOCK_LENGTH + max(msg_size, hash_sel)];
294 memcpy(temp_work, i_key_pad, HMAC_BLOCK_LENGTH);
295 memcpy(temp_work + HMAC_BLOCK_LENGTH, msg, msg_size);
296
297 create_hash(temp_work, HMAC_BLOCK_LENGTH + msg_size, temp_hash,
298 hash_sel);
299
300 memcpy(temp_work, o_key_pad, HMAC_BLOCK_LENGTH);
301 memcpy(temp_work + HMAC_BLOCK_LENGTH, temp_hash, hash_sel);
302
303 create_hash(temp_work, HMAC_BLOCK_LENGTH + hash_sel, hash, hash_sel);
304
305 return EOK;
306}
307
308/** Password-Based Key Derivation Function 2.
309 *
310 * As defined in RFC 2898, using HMAC-SHA1 with 4096 iterations
311 * and 32 bytes key result used for WPA/WPA2.
312 *
313 * @param pass Password sequence.
314 * @param pass_size Password sequence length.
315 * @param salt Salt sequence to be used with password.
316 * @param salt_size Salt sequence length.
317 * @param hash Output parameter for result hash (32 byte value).
318 *
319 * @return EINVAL when pass or salt not specified,
320 * ENOMEM when pointer for output hash result
321 * is not allocated, otherwise EOK.
322 *
323 */
324errno_t pbkdf2(uint8_t *pass, size_t pass_size, uint8_t *salt, size_t salt_size,
325 uint8_t *hash)
326{
327 if ((!pass) || (!salt))
328 return EINVAL;
329
330 if (!hash)
331 return ENOMEM;
332
333 uint8_t work_salt[salt_size + 4];
334 memcpy(work_salt, salt, salt_size);
335 uint8_t work_hmac[HASH_SHA1];
336 uint8_t temp_hmac[HASH_SHA1];
337 uint8_t xor_hmac[HASH_SHA1];
338 uint8_t temp_hash[HASH_SHA1 * 2];
339
340 for (size_t i = 0; i < 2; i++) {
341 uint32_t be_i = host2uint32_t_be(i + 1);
342
343 memcpy(work_salt + salt_size, &be_i, 4);
344 hmac(pass, pass_size, work_salt, salt_size + 4,
345 work_hmac, HASH_SHA1);
346 memcpy(xor_hmac, work_hmac, HASH_SHA1);
347
348 for (size_t k = 1; k < 4096; k++) {
349 memcpy(temp_hmac, work_hmac, HASH_SHA1);
350 hmac(pass, pass_size, temp_hmac, HASH_SHA1,
351 work_hmac, HASH_SHA1);
352
353 for (size_t t = 0; t < HASH_SHA1; t++)
354 xor_hmac[t] ^= work_hmac[t];
355 }
356
357 memcpy(temp_hash + i * HASH_SHA1, xor_hmac, HASH_SHA1);
358 }
359
360 memcpy(hash, temp_hash, PBKDF2_KEY_LENGTH);
361
362 return EOK;
363}
Note: See TracBrowser for help on using the repository browser.