Merge branch 'iv-gen'
[strongswan.git] / src / libstrongswan / plugins / openssl / openssl_gcm.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
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 <openssl/opensslv.h>
17
18 #if OPENSSL_VERSION_NUMBER >= 0x1000100fL
19
20 #include "openssl_gcm.h"
21
22 #include <openssl/evp.h>
23 #include <crypto/iv/iv_gen_seq.h>
24
25 /** as defined in RFC 4106 */
26 #define IV_LEN 8
27 #define SALT_LEN 4
28 #define NONCE_LEN (IV_LEN + SALT_LEN)
29
30 typedef struct private_aead_t private_aead_t;
31
32 /**
33 * Private data of aead_t
34 */
35 struct private_aead_t {
36
37 /**
38 * Public interface
39 */
40 aead_t public;
41
42 /**
43 * The encryption key
44 */
45 chunk_t key;
46
47 /**
48 * Salt value
49 */
50 char salt[SALT_LEN];
51
52 /**
53 * Size of the integrity check value
54 */
55 size_t icv_size;
56
57 /**
58 * IV generator
59 */
60 iv_gen_t *iv_gen;
61
62 /**
63 * The cipher to use
64 */
65 const EVP_CIPHER *cipher;
66 };
67
68 /**
69 * Do the actual en/decryption in an EVP context
70 */
71 static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv,
72 u_char *out, int enc)
73 {
74 EVP_CIPHER_CTX ctx;
75 u_char nonce[NONCE_LEN];
76 bool success = FALSE;
77 int len;
78
79 memcpy(nonce, this->salt, SALT_LEN);
80 memcpy(nonce + SALT_LEN, iv.ptr, IV_LEN);
81
82 EVP_CIPHER_CTX_init(&ctx);
83 EVP_CIPHER_CTX_set_padding(&ctx, 0);
84 if (!EVP_CipherInit_ex(&ctx, this->cipher, NULL, NULL, NULL, enc) ||
85 !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, NONCE_LEN, NULL) ||
86 !EVP_CipherInit_ex(&ctx, NULL, NULL, this->key.ptr, nonce, enc))
87 {
88 goto done;
89 }
90 if (!enc && !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, this->icv_size,
91 data.ptr + data.len))
92 { /* set ICV for verification on decryption */
93 goto done;
94 }
95 if (assoc.len && !EVP_CipherUpdate(&ctx, NULL, &len, assoc.ptr, assoc.len))
96 { /* set AAD if specified */
97 goto done;
98 }
99 if (!EVP_CipherUpdate(&ctx, out, &len, data.ptr, data.len) ||
100 !EVP_CipherFinal_ex(&ctx, out + len, &len))
101 { /* EVP_CipherFinal_ex fails if ICV is incorrect on decryption */
102 goto done;
103 }
104 if (enc && !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, this->icv_size,
105 out + data.len))
106 { /* copy back the ICV when encrypting */
107 goto done;
108 }
109 success = TRUE;
110
111 done:
112 EVP_CIPHER_CTX_cleanup(&ctx);
113 return success;
114 }
115
116 METHOD(aead_t, encrypt, bool,
117 private_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv,
118 chunk_t *encrypted)
119 {
120 u_char *out;
121
122 out = plain.ptr;
123 if (encrypted)
124 {
125 *encrypted = chunk_alloc(plain.len + this->icv_size);
126 out = encrypted->ptr;
127 }
128 return crypt(this, plain, assoc, iv, out, 1);
129 }
130
131 METHOD(aead_t, decrypt, bool,
132 private_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv,
133 chunk_t *plain)
134 {
135 u_char *out;
136
137 if (encrypted.len < this->icv_size)
138 {
139 return FALSE;
140 }
141 encrypted.len -= this->icv_size;
142
143 out = encrypted.ptr;
144 if (plain)
145 {
146 *plain = chunk_alloc(encrypted.len);
147 out = plain->ptr;
148 }
149 return crypt(this, encrypted, assoc, iv, out, 0);
150 }
151
152 METHOD(aead_t, get_block_size, size_t,
153 private_aead_t *this)
154 {
155 return this->cipher->block_size;
156 }
157
158 METHOD(aead_t, get_icv_size, size_t,
159 private_aead_t *this)
160 {
161 return this->icv_size;
162 }
163
164 METHOD(aead_t, get_iv_size, size_t,
165 private_aead_t *this)
166 {
167 return IV_LEN;
168 }
169
170 METHOD(aead_t, get_iv_gen, iv_gen_t*,
171 private_aead_t *this)
172 {
173 return this->iv_gen;
174 }
175
176 METHOD(aead_t, get_key_size, size_t,
177 private_aead_t *this)
178 {
179 return this->key.len + SALT_LEN;
180 }
181
182 METHOD(aead_t, set_key, bool,
183 private_aead_t *this, chunk_t key)
184 {
185 if (key.len != get_key_size(this))
186 {
187 return FALSE;
188 }
189 memcpy(this->salt, key.ptr + key.len - SALT_LEN, SALT_LEN);
190 memcpy(this->key.ptr, key.ptr, this->key.len);
191 return TRUE;
192 }
193
194 METHOD(aead_t, destroy, void,
195 private_aead_t *this)
196 {
197 chunk_clear(&this->key);
198 this->iv_gen->destroy(this->iv_gen);
199 free(this);
200 }
201
202 /*
203 * Described in header
204 */
205 aead_t *openssl_gcm_create(encryption_algorithm_t algo, size_t key_size)
206 {
207 private_aead_t *this;
208
209 INIT(this,
210 .public = {
211 .encrypt = _encrypt,
212 .decrypt = _decrypt,
213 .get_block_size = _get_block_size,
214 .get_icv_size = _get_icv_size,
215 .get_iv_size = _get_iv_size,
216 .get_iv_gen = _get_iv_gen,
217 .get_key_size = _get_key_size,
218 .set_key = _set_key,
219 .destroy = _destroy,
220 },
221 );
222
223 switch (algo)
224 {
225 case ENCR_AES_GCM_ICV8:
226 this->icv_size = 8;
227 break;
228 case ENCR_AES_GCM_ICV12:
229 this->icv_size = 12;
230 break;
231 case ENCR_AES_GCM_ICV16:
232 this->icv_size = 16;
233 break;
234 default:
235 free(this);
236 return NULL;
237 }
238
239 switch (algo)
240 {
241 case ENCR_AES_GCM_ICV8:
242 case ENCR_AES_GCM_ICV12:
243 case ENCR_AES_GCM_ICV16:
244 switch (key_size)
245 {
246 case 0:
247 key_size = 16;
248 /* FALL */
249 case 16:
250 this->cipher = EVP_get_cipherbyname("aes-128-gcm");
251 break;
252 case 24:
253 this->cipher = EVP_get_cipherbyname("aes-192-gcm");
254 break;
255 case 32:
256 this->cipher = EVP_get_cipherbyname("aes-256-gcm");
257 break;
258 default:
259 free(this);
260 return NULL;
261 }
262 break;
263 default:
264 free(this);
265 return NULL;
266 }
267
268 if (!this->cipher)
269 {
270 free(this);
271 return NULL;
272 }
273
274 this->key = chunk_alloc(key_size);
275 this->iv_gen = iv_gen_seq_create();
276
277 return &this->public;
278 }
279
280 #endif /* OPENSSL_VERSION_NUMBER */