aesni: Implement a AES-NI based CTR crypter using the key schedule
[strongswan.git] / src / libstrongswan / plugins / aesni / aesni_ctr.c
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 "aesni_ctr.h"
17 #include "aesni_key.h"
18
19 #include <tmmintrin.h>
20
21 typedef struct private_aesni_ctr_t private_aesni_ctr_t;
22
23 /**
24 * CTR en/decryption method type
25 */
26 typedef void (*aesni_ctr_fn_t)(private_aesni_ctr_t*, size_t, u_char*, u_char*);
27
28 /**
29 * Private data of an aesni_ctr_t object.
30 */
31 struct private_aesni_ctr_t {
32
33 /**
34 * Public aesni_ctr_t interface.
35 */
36 aesni_ctr_t public;
37
38 /**
39 * Key size
40 */
41 u_int key_size;
42
43 /**
44 * Key schedule
45 */
46 aesni_key_t *key;
47
48 /**
49 * Encryption method
50 */
51 aesni_ctr_fn_t crypt;
52
53 /**
54 * Counter state
55 */
56 struct {
57 char nonce[4];
58 char iv[8];
59 u_int32_t counter;
60 } __attribute__((packed, aligned(sizeof(__m128i)))) state;
61 };
62
63 /**
64 * Generic CTR encryption
65 */
66 static void encrypt_ctr(private_aesni_ctr_t *this,
67 size_t len, u_char *in, u_char *out)
68 {
69 __m128i state, t, d, b, swap, one, *bi, *bo;
70 u_int i, round, blocks, rem;
71
72 one = _mm_set_epi32(0, 0, 0, 1);
73 swap = _mm_setr_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
74 state = _mm_load_si128((__m128i*)&this->state);
75 blocks = len / AES_BLOCK_SIZE;
76 rem = len % AES_BLOCK_SIZE;
77 bi = (__m128i*)in;
78 bo = (__m128i*)out;
79
80 for (i = 0; i < blocks; i++)
81 {
82 d = _mm_loadu_si128(bi + i);
83 t = _mm_xor_si128(state, this->key->schedule[0]);
84 for (round = 1; round < this->key->rounds; round++)
85 {
86 t = _mm_aesenc_si128(t, this->key->schedule[round]);
87 }
88 t = _mm_aesenclast_si128(t, this->key->schedule[this->key->rounds]);
89 t = _mm_xor_si128(t, d);
90 _mm_storeu_si128(bo + i, t);
91
92 /* big endian increment */
93 t = _mm_shuffle_epi8(state, swap);
94 t = _mm_add_epi64(t, one);
95 state = _mm_shuffle_epi8(t, swap);
96 }
97
98 if (rem)
99 {
100 memset(&b, 0, sizeof(b));
101 memcpy(&b, bi + blocks, rem);
102
103 d = _mm_loadu_si128(&b);
104 t = _mm_xor_si128(state, this->key->schedule[0]);
105 for (round = 1; round < this->key->rounds; round++)
106 {
107 t = _mm_aesenc_si128(t, this->key->schedule[round]);
108 }
109 t = _mm_aesenclast_si128(t, this->key->schedule[this->key->rounds]);
110 t = _mm_xor_si128(t, d);
111 _mm_storeu_si128(&b, t);
112
113 memcpy(bo + blocks, &b, rem);
114 }
115 }
116
117 METHOD(crypter_t, crypt, bool,
118 private_aesni_ctr_t *this, chunk_t in, chunk_t iv, chunk_t *out)
119 {
120 u_char *buf;
121
122 if (!this->key || iv.len != sizeof(this->state.iv))
123 {
124 return FALSE;
125 }
126 memcpy(this->state.iv, iv.ptr, sizeof(this->state.iv));
127 this->state.counter = htonl(1);
128
129 buf = in.ptr;
130 if (out)
131 {
132 *out = chunk_alloc(in.len);
133 buf = out->ptr;
134 }
135 this->crypt(this, in.len, in.ptr, buf);
136 return TRUE;
137 }
138
139 METHOD(crypter_t, get_block_size, size_t,
140 private_aesni_ctr_t *this)
141 {
142 return 1;
143 }
144
145 METHOD(crypter_t, get_iv_size, size_t,
146 private_aesni_ctr_t *this)
147 {
148 return sizeof(this->state.iv);
149 }
150
151 METHOD(crypter_t, get_key_size, size_t,
152 private_aesni_ctr_t *this)
153 {
154 return this->key_size + sizeof(this->state.nonce);
155 }
156
157 METHOD(crypter_t, set_key, bool,
158 private_aesni_ctr_t *this, chunk_t key)
159 {
160 if (key.len != get_key_size(this))
161 {
162 return FALSE;
163 }
164
165 memcpy(this->state.nonce, key.ptr + key.len - sizeof(this->state.nonce),
166 sizeof(this->state.nonce));
167 key.len -= sizeof(this->state.nonce);
168
169 DESTROY_IF(this->key);
170 this->key = aesni_key_create(TRUE, key);
171
172 return this->key;
173 }
174
175 METHOD(crypter_t, destroy, void,
176 private_aesni_ctr_t *this)
177 {
178 DESTROY_IF(this->key);
179 free(this);
180 }
181
182 /**
183 * See header
184 */
185 aesni_ctr_t *aesni_ctr_create(encryption_algorithm_t algo, size_t key_size)
186 {
187 private_aesni_ctr_t *this;
188
189 if (algo != ENCR_AES_CTR)
190 {
191 return NULL;
192 }
193 switch (key_size)
194 {
195 case 0:
196 key_size = 16;
197 break;
198 case 16:
199 case 24:
200 case 32:
201 break;
202 default:
203 return NULL;
204 }
205
206 INIT(this,
207 .public = {
208 .crypter = {
209 .encrypt = _crypt,
210 .decrypt = _crypt,
211 .get_block_size = _get_block_size,
212 .get_iv_size = _get_iv_size,
213 .get_key_size = _get_key_size,
214 .set_key = _set_key,
215 .destroy = _destroy,
216 },
217 },
218 .key_size = key_size,
219 .crypt = encrypt_ctr,
220 );
221
222 return &this->public;
223 }