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