fips-prf: Fail when trying to use append mode on FIPS-PRF
[strongswan.git] / src / libstrongswan / plugins / fips_prf / fips_prf.c
1 /*
2 * Copyright (C) 2006 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "fips_prf.h"
17
18 #include <arpa/inet.h>
19
20 #include <utils/debug.h>
21
22 typedef struct private_fips_prf_t private_fips_prf_t;
23
24 /**
25 * Private data of a fips_prf_t object.
26 */
27 struct private_fips_prf_t {
28 /**
29 * Public fips_prf_t interface.
30 */
31 fips_prf_t public;
32
33 /**
34 * key of prf function, "b" long
35 */
36 u_int8_t *key;
37
38 /**
39 * size of "b" in bytes
40 */
41 size_t b;
42
43 /**
44 * Keyed SHA1 prf: It does not use SHA1Final operation
45 */
46 prf_t *keyed_prf;
47
48 /**
49 * G function, either SHA1 or DES
50 */
51 bool (*g)(private_fips_prf_t *this, chunk_t c, u_int8_t res[]);
52 };
53
54 /**
55 * sum = (a + b) mod 2 ^ (length * 8)
56 */
57 static void add_mod(size_t length, u_int8_t a[], u_int8_t b[], u_int8_t sum[])
58 {
59 int i, c = 0;
60
61 for(i = length - 1; i >= 0; i--)
62 {
63 u_int32_t tmp;
64
65 tmp = a[i] + b[i] + c;
66 sum[i] = 0xff & tmp;
67 c = tmp >> 8;
68 }
69 }
70
71 /**
72 * calculate "chunk mod 2^(length*8)" and save it into buffer
73 */
74 static void chunk_mod(size_t length, chunk_t chunk, u_int8_t buffer[])
75 {
76 if (chunk.len < length)
77 {
78 /* apply seed as least significant bits, others are zero */
79 memset(buffer, 0, length - chunk.len);
80 memcpy(buffer + length - chunk.len, chunk.ptr, chunk.len);
81 }
82 else
83 {
84 /* use least significant bytes from seed, as we use mod 2^b */
85 memcpy(buffer, chunk.ptr + chunk.len - length, length);
86 }
87 }
88
89 /**
90 * Implementation of prf_t.get_bytes.
91 *
92 * Test vector:
93 *
94 * key:
95 * 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
96 * 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
97 * 0xeb, 0x5a, 0x38, 0xb6
98 *
99 * seed:
100 * 0x00
101 *
102 * result:
103 * 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
104 * 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
105 * 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
106 * 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
107 * 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
108 */
109 METHOD(prf_t, get_bytes, bool,
110 private_fips_prf_t *this, chunk_t seed, u_int8_t w[])
111 {
112 int i;
113 u_int8_t xval[this->b];
114 u_int8_t xseed[this->b];
115 u_int8_t sum[this->b];
116 u_int8_t *xkey = this->key;
117 u_int8_t one[this->b];
118
119 if (!w)
120 {
121 /* append mode is not supported */
122 return FALSE;
123 }
124
125 memset(one, 0, this->b);
126 one[this->b - 1] = 0x01;
127
128 /* 3.1 */
129 chunk_mod(this->b, seed, xseed);
130
131 /* 3.2 */
132 for (i = 0; i < 2; i++) /* twice */
133 {
134 /* a. XVAL = (XKEY + XSEED j) mod 2^b */
135 add_mod(this->b, xkey, xseed, xval);
136 DBG3(DBG_LIB, "XVAL %b", xval, (u_int)this->b);
137 /* b. wi = G(t, XVAL ) */
138 this->g(this, chunk_create(xval, this->b), &w[i * this->b]);
139 DBG3(DBG_LIB, "w[%d] %b", i, &w[i * this->b], (u_int)this->b);
140 /* c. XKEY = (1 + XKEY + wi) mod 2b */
141 add_mod(this->b, xkey, &w[i * this->b], sum);
142 add_mod(this->b, sum, one, xkey);
143 DBG3(DBG_LIB, "XKEY %b", xkey, (u_int)this->b);
144 }
145
146 /* 3.3 done already, mod q not used */
147
148 return TRUE;
149 }
150
151 METHOD(prf_t, get_block_size, size_t,
152 private_fips_prf_t *this)
153 {
154 return 2 * this->b;
155 }
156 METHOD(prf_t, allocate_bytes, bool,
157 private_fips_prf_t *this, chunk_t seed, chunk_t *chunk)
158 {
159 *chunk = chunk_alloc(get_block_size(this));
160 return get_bytes(this, seed, chunk->ptr);
161 }
162
163 METHOD(prf_t, get_key_size, size_t,
164 private_fips_prf_t *this)
165 {
166 return this->b;
167 }
168
169 METHOD(prf_t, set_key, bool,
170 private_fips_prf_t *this, chunk_t key)
171 {
172 /* save key as "key mod 2^b" */
173 chunk_mod(this->b, key, this->key);
174 return TRUE;
175 }
176
177 /**
178 * Implementation of the G() function based on SHA1
179 */
180 static bool g_sha1(private_fips_prf_t *this, chunk_t c, u_int8_t res[])
181 {
182 u_int8_t buf[64];
183
184 if (c.len < sizeof(buf))
185 {
186 /* pad c with zeros */
187 memset(buf, 0, sizeof(buf));
188 memcpy(buf, c.ptr, c.len);
189 c.ptr = buf;
190 c.len = sizeof(buf);
191 }
192 else
193 {
194 /* not more than 512 bits can be G()-ed */
195 c.len = sizeof(buf);
196 }
197
198 /* use the keyed hasher, but use an empty key to use SHA1 IV */
199 if (!this->keyed_prf->set_key(this->keyed_prf, chunk_empty) ||
200 !this->keyed_prf->get_bytes(this->keyed_prf, c, res))
201 {
202 return FALSE;
203 }
204 return TRUE;
205 }
206
207 METHOD(prf_t, destroy, void,
208 private_fips_prf_t *this)
209 {
210 this->keyed_prf->destroy(this->keyed_prf);
211 free(this->key);
212 free(this);
213 }
214
215 /*
216 * Described in header.
217 */
218 fips_prf_t *fips_prf_create(pseudo_random_function_t algo)
219 {
220 private_fips_prf_t *this;
221
222 INIT(this,
223 .public = {
224 .prf_interface = {
225 .get_bytes = _get_bytes,
226 .allocate_bytes = _allocate_bytes,
227 .get_block_size = _get_block_size,
228 .get_key_size = _get_key_size,
229 .set_key = _set_key,
230 .destroy = _destroy,
231 },
232 },
233 );
234
235 switch (algo)
236 {
237 case PRF_FIPS_SHA1_160:
238 {
239 this->g = g_sha1;
240 this->b = 20;
241 this->keyed_prf = lib->crypto->create_prf(lib->crypto, PRF_KEYED_SHA1);
242 if (this->keyed_prf == NULL)
243 {
244 free(this);
245 return NULL;
246 }
247 break;
248 }
249 case PRF_FIPS_DES:
250 /* not implemented yet */
251 default:
252 free(this);
253 return NULL;
254 }
255 this->key = malloc(this->b);
256
257 return &this->public;
258 }