Annotation of embedaddon/strongswan/src/libstrongswan/plugins/chapoly/chapoly_aead.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2015 Martin Willi
3: * Copyright (C) 2015 revosec AG
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 "chapoly_aead.h"
17: #include "chapoly_drv.h"
18:
19: #include <crypto/iv/iv_gen_seq.h>
20:
21: /* maximum plain message size */
22: #define P_MAX 247877906880
23:
24: typedef struct private_chapoly_aead_t private_chapoly_aead_t;
25:
26: /**
27: * Private data of an chapoly_aead_t object.
28: */
29: struct private_chapoly_aead_t {
30:
31: /**
32: * Public chapoly_aead_t interface.
33: */
34: chapoly_aead_t public;
35:
36: /**
37: * IV generator.
38: */
39: iv_gen_t *iv_gen;
40:
41: /**
42: * Driver backend
43: */
44: chapoly_drv_t *drv;
45: };
46:
47: /**
48: * Include a partial block to ICV by padding it with zero bytes
49: */
50: static bool poly_update_padded(private_chapoly_aead_t *this,
51: u_char *in, size_t len)
52: {
53: u_char b[POLY_BLOCK_SIZE];
54:
55: memset(b, 0, sizeof(b));
56: memcpy(b, in, len);
57:
58: return this->drv->poly(this->drv, b, 1);
59: }
60:
61: /**
62: * Include associated data with padding to ICV
63: */
64: static bool poly_head(private_chapoly_aead_t *this, u_char *assoc, size_t len)
65: {
66: u_int blocks, rem;
67:
68: blocks = len / POLY_BLOCK_SIZE;
69: rem = len % POLY_BLOCK_SIZE;
70: if (!this->drv->poly(this->drv, assoc, blocks))
71: {
72: return FALSE;
73: }
74: if (rem)
75: {
76: return poly_update_padded(this, assoc + blocks * POLY_BLOCK_SIZE, rem);
77: }
78: return TRUE;
79: }
80:
81: /**
82: * Include length fields to ICV
83: */
84: static bool poly_tail(private_chapoly_aead_t *this, size_t alen, size_t clen)
85: {
86: struct {
87: uint64_t alen;
88: uint64_t clen;
89: } b;
90:
91: b.alen = htole64(alen);
92: b.clen = htole64(clen);
93:
94: return this->drv->poly(this->drv, (u_char*)&b, 1);
95: }
96:
97: /**
98: * Perform ChaCha20 encryption inline and generate an ICV tag
99: */
100: static bool do_encrypt(private_chapoly_aead_t *this, size_t len, u_char *data,
101: u_char *iv, size_t alen, u_char *assoc, u_char *icv)
102: {
103: u_int blocks, rem, prem;
104:
105: if (!this->drv->init(this->drv, iv) ||
106: !poly_head(this, assoc, alen))
107: {
108: return FALSE;
109: }
110: blocks = len / CHACHA_BLOCK_SIZE;
111: if (!this->drv->encrypt(this->drv, data, blocks))
112: {
113: return FALSE;
114: }
115: rem = len % CHACHA_BLOCK_SIZE;
116: if (rem)
117: {
118: u_char stream[CHACHA_BLOCK_SIZE];
119:
120: data += blocks * CHACHA_BLOCK_SIZE;
121: if (!this->drv->chacha(this->drv, stream))
122: {
123: return FALSE;
124: }
125: memxor(data, stream, rem);
126:
127: blocks = rem / POLY_BLOCK_SIZE;
128: if (!this->drv->poly(this->drv, data, blocks))
129: {
130: return FALSE;
131: }
132: prem = rem % POLY_BLOCK_SIZE;
133: if (prem)
134: {
135: poly_update_padded(this, data + blocks * POLY_BLOCK_SIZE, prem);
136: }
137: }
138: return poly_tail(this, alen, len) &&
139: this->drv->finish(this->drv, icv);
140: }
141:
142: /**
143: * Perform ChaCha20 decryption inline and generate an ICV tag
144: */
145: static bool do_decrypt(private_chapoly_aead_t *this, size_t len, u_char *data,
146: u_char *iv, size_t alen, u_char *assoc, u_char *icv)
147: {
148: u_int blocks, rem, prem;
149:
150: if (!this->drv->init(this->drv, iv) ||
151: !poly_head(this, assoc, alen))
152: {
153: return FALSE;
154: }
155: blocks = len / CHACHA_BLOCK_SIZE;
156: if (!this->drv->decrypt(this->drv, data, blocks))
157: {
158: return FALSE;
159: }
160: rem = len % CHACHA_BLOCK_SIZE;
161: if (rem)
162: {
163: u_char stream[CHACHA_BLOCK_SIZE];
164:
165: data += blocks * CHACHA_BLOCK_SIZE;
166:
167: blocks = rem / POLY_BLOCK_SIZE;
168: if (!this->drv->poly(this->drv, data, blocks))
169: {
170: return FALSE;
171: }
172: prem = rem % POLY_BLOCK_SIZE;
173: if (prem)
174: {
175: poly_update_padded(this, data + blocks * POLY_BLOCK_SIZE, prem);
176: }
177: if (!this->drv->chacha(this->drv, stream))
178: {
179: return FALSE;
180: }
181: memxor(data, stream, rem);
182: }
183: return poly_tail(this, alen, len) &&
184: this->drv->finish(this->drv, icv);
185: }
186:
187: METHOD(aead_t, encrypt, bool,
188: private_chapoly_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv,
189: chunk_t *encr)
190: {
191: u_char *out;
192:
193: if (sizeof(plain.len) > sizeof(uint32_t) && plain.len > P_MAX)
194: {
195: return FALSE;
196: }
197: if (iv.len != CHACHA_IV_SIZE)
198: {
199: return FALSE;
200: }
201: out = plain.ptr;
202: if (encr)
203: {
204: *encr = chunk_alloc(plain.len + POLY_ICV_SIZE);
205: out = encr->ptr;
206: memcpy(out, plain.ptr, plain.len);
207: }
208: do_encrypt(this, plain.len, out, iv.ptr, assoc.len, assoc.ptr,
209: out + plain.len);
210: return TRUE;
211: }
212:
213: METHOD(aead_t, decrypt, bool,
214: private_chapoly_aead_t *this, chunk_t encr, chunk_t assoc, chunk_t iv,
215: chunk_t *plain)
216: {
217: u_char *out, icv[POLY_ICV_SIZE];
218: if (iv.len != CHACHA_IV_SIZE || encr.len < POLY_ICV_SIZE)
219: {
220: return FALSE;
221: }
222: encr.len -= POLY_ICV_SIZE;
223: if (sizeof(encr.len) > sizeof(uint32_t) && encr.len > P_MAX)
224: {
225: return FALSE;
226: }
227: out = encr.ptr;
228: if (plain)
229: {
230: *plain = chunk_alloc(encr.len);
231: out = plain->ptr;
232: memcpy(out, encr.ptr, encr.len);
233: }
234: do_decrypt(this, encr.len, out, iv.ptr, assoc.len, assoc.ptr, icv);
235: return memeq_const(icv, encr.ptr + encr.len, POLY_ICV_SIZE);
236: }
237:
238: METHOD(aead_t, get_block_size, size_t,
239: private_chapoly_aead_t *this)
240: {
241: return 1;
242: }
243:
244: METHOD(aead_t, get_icv_size, size_t,
245: private_chapoly_aead_t *this)
246: {
247: return POLY_ICV_SIZE;
248: }
249:
250: METHOD(aead_t, get_iv_size, size_t,
251: private_chapoly_aead_t *this)
252: {
253: return CHACHA_IV_SIZE;
254: }
255:
256: METHOD(aead_t, get_iv_gen, iv_gen_t*,
257: private_chapoly_aead_t *this)
258: {
259: return this->iv_gen;
260: }
261:
262: METHOD(aead_t, get_key_size, size_t,
263: private_chapoly_aead_t *this)
264: {
265: return CHACHA_KEY_SIZE + CHACHA_SALT_SIZE;
266: }
267:
268: METHOD(aead_t, set_key, bool,
269: private_chapoly_aead_t *this, chunk_t key)
270: {
271: if (key.len != CHACHA_KEY_SIZE + CHACHA_SALT_SIZE)
272: {
273: return FALSE;
274: }
275: return this->drv->set_key(this->drv, "expand 32-byte k",
276: key.ptr, key.ptr + CHACHA_KEY_SIZE);
277: }
278:
279: METHOD(aead_t, destroy, void,
280: private_chapoly_aead_t *this)
281: {
282: this->drv->destroy(this->drv);
283: this->iv_gen->destroy(this->iv_gen);
284: free(this);
285: }
286:
287: /**
288: * See header
289: */
290: chapoly_aead_t *chapoly_aead_create(encryption_algorithm_t algo,
291: size_t key_size, size_t salt_size)
292: {
293: private_chapoly_aead_t *this;
294: chapoly_drv_t *drv;
295:
296: if (algo != ENCR_CHACHA20_POLY1305)
297: {
298: return NULL;
299: }
300: if (key_size && key_size != CHACHA_KEY_SIZE)
301: {
302: return NULL;
303: }
304: if (salt_size && salt_size != CHACHA_SALT_SIZE)
305: {
306: return NULL;
307: }
308: drv = chapoly_drv_probe();
309: if (!drv)
310: {
311: return NULL;
312: }
313:
314: INIT(this,
315: .public = {
316: .aead = {
317: .encrypt = _encrypt,
318: .decrypt = _decrypt,
319: .get_block_size = _get_block_size,
320: .get_icv_size = _get_icv_size,
321: .get_iv_size = _get_iv_size,
322: .get_iv_gen = _get_iv_gen,
323: .get_key_size = _get_key_size,
324: .set_key = _set_key,
325: .destroy = _destroy,
326: },
327: },
328: .iv_gen = iv_gen_seq_create(),
329: .drv = drv,
330: );
331:
332: return &this->public;
333: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>