]>
Commit | Line | Data |
---|---|---|
818dc865 TB |
1 | /* |
2 | * Copyright (C) 2020 Tobias Brunner | |
818dc865 | 3 | * Copyright (C) 2014 Martin Willi |
19ef2aec TB |
4 | * |
5 | * Copyright (C) secunet Security Networks AG | |
818dc865 TB |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
16 | */ | |
17 | ||
18 | #include "tls_aead.h" | |
19 | ||
20 | #include <bio/bio_writer.h> | |
21 | ||
22 | typedef struct private_tls_aead_t private_tls_aead_t; | |
23 | ||
24 | /** | |
25 | * Private data of an tls_aead_t object. | |
26 | */ | |
27 | struct private_tls_aead_t { | |
28 | ||
29 | /** | |
30 | * Public tls_aead_t interface. | |
31 | */ | |
32 | tls_aead_t public; | |
33 | ||
34 | /** | |
35 | * AEAD transform. | |
36 | */ | |
37 | aead_t *aead; | |
38 | ||
39 | /** | |
40 | * IV derived from key material. | |
41 | */ | |
42 | chunk_t iv; | |
43 | ||
44 | /** | |
45 | * Size of the salt that's internally used by the AEAD implementation. | |
46 | */ | |
47 | size_t salt; | |
48 | }; | |
49 | ||
50 | /** | |
51 | * Additional data for AEAD (record header) | |
52 | */ | |
53 | typedef struct __attribute__((__packed__)) { | |
54 | uint8_t type; | |
55 | uint16_t version; | |
56 | uint16_t length; | |
57 | } sigheader_t; | |
58 | ||
59 | /** | |
60 | * Generate the IV from the given sequence number. | |
61 | */ | |
62 | static bool generate_iv(private_tls_aead_t *this, uint64_t seq, chunk_t iv) | |
63 | { | |
64 | if (iv.len < sizeof(uint64_t) || | |
65 | iv.len < this->iv.len) | |
66 | { | |
67 | return FALSE; | |
68 | } | |
69 | memset(iv.ptr, 0, iv.len); | |
70 | htoun64(iv.ptr + iv.len - sizeof(uint64_t), seq); | |
71 | memxor(iv.ptr + iv.len - this->iv.len, this->iv.ptr, this->iv.len); | |
72 | return TRUE; | |
73 | } | |
74 | ||
75 | METHOD(tls_aead_t, encrypt, bool, | |
76 | private_tls_aead_t *this, tls_version_t version, tls_content_type_t *type, | |
77 | uint64_t seq, chunk_t *data) | |
78 | { | |
79 | bio_writer_t *writer; | |
80 | chunk_t assoc, encrypted, iv, padding, plain; | |
81 | uint8_t icvlen; | |
82 | sigheader_t hdr; | |
83 | ||
84 | iv = chunk_alloca(this->aead->get_iv_size(this->aead)); | |
85 | if (!generate_iv(this, seq, iv)) | |
86 | { | |
87 | return FALSE; | |
88 | } | |
89 | ||
90 | /* no padding for now */ | |
91 | padding = chunk_empty; | |
92 | icvlen = this->aead->get_icv_size(this->aead); | |
93 | ||
94 | writer = bio_writer_create(data->len + 1 + padding.len + icvlen); | |
95 | writer->write_data(writer, *data); | |
96 | writer->write_uint8(writer, *type); | |
97 | writer->write_data(writer, padding); | |
98 | writer->skip(writer, icvlen); | |
99 | encrypted = writer->extract_buf(writer); | |
100 | writer->destroy(writer); | |
101 | ||
102 | plain = encrypted; | |
103 | plain.len -= icvlen; | |
104 | ||
105 | hdr.type = TLS_APPLICATION_DATA; | |
106 | htoun16(&hdr.version, TLS_1_2); | |
107 | htoun16(&hdr.length, encrypted.len); | |
108 | ||
109 | assoc = chunk_from_thing(hdr); | |
110 | if (!this->aead->encrypt(this->aead, plain, assoc, iv, NULL)) | |
111 | { | |
112 | chunk_free(&encrypted); | |
113 | return FALSE; | |
114 | } | |
115 | chunk_free(data); | |
116 | *type = TLS_APPLICATION_DATA; | |
117 | *data = encrypted; | |
118 | return TRUE; | |
119 | } | |
120 | ||
121 | METHOD(tls_aead_t, decrypt, bool, | |
122 | private_tls_aead_t *this, tls_version_t version, tls_content_type_t *type, | |
123 | uint64_t seq, chunk_t *data) | |
124 | { | |
125 | chunk_t assoc, iv; | |
126 | uint8_t icvlen; | |
127 | sigheader_t hdr; | |
128 | ||
129 | iv = chunk_alloca(this->aead->get_iv_size(this->aead)); | |
130 | if (!generate_iv(this, seq, iv)) | |
131 | { | |
132 | return FALSE; | |
133 | } | |
134 | ||
135 | icvlen = this->aead->get_icv_size(this->aead); | |
136 | if (data->len < icvlen) | |
137 | { | |
138 | return FALSE; | |
139 | } | |
140 | ||
141 | hdr.type = TLS_APPLICATION_DATA; | |
142 | htoun16(&hdr.version, TLS_1_2); | |
143 | htoun16(&hdr.length, data->len); | |
144 | ||
145 | assoc = chunk_from_thing(hdr); | |
146 | if (!this->aead->decrypt(this->aead, *data, assoc, iv, NULL)) | |
147 | { | |
148 | return FALSE; | |
149 | } | |
150 | data->len -= icvlen; | |
151 | ||
152 | while (data->len && !data->ptr[data->len-1]) | |
153 | { /* ignore any padding */ | |
154 | data->len--; | |
155 | } | |
156 | if (data->len < 1) | |
157 | { | |
158 | return FALSE; | |
159 | } | |
160 | *type = data->ptr[data->len-1]; | |
161 | data->len--; | |
162 | return TRUE; | |
163 | } | |
164 | ||
165 | METHOD(tls_aead_t, get_mac_key_size, size_t, | |
166 | private_tls_aead_t *this) | |
167 | { | |
168 | return 0; | |
169 | } | |
170 | ||
171 | METHOD(tls_aead_t, get_encr_key_size, size_t, | |
172 | private_tls_aead_t *this) | |
173 | { | |
174 | /* our AEAD implementations add the salt length here, so subtract it */ | |
175 | return this->aead->get_key_size(this->aead) - this->salt; | |
176 | } | |
177 | ||
178 | METHOD(tls_aead_t, get_iv_size, size_t, | |
179 | private_tls_aead_t *this) | |
180 | { | |
181 | /* analogous to the change above, we add the salt length here */ | |
182 | return this->aead->get_iv_size(this->aead) + this->salt; | |
183 | } | |
184 | ||
185 | METHOD(tls_aead_t, set_keys, bool, | |
186 | private_tls_aead_t *this, chunk_t mac, chunk_t encr, chunk_t iv) | |
187 | { | |
188 | chunk_t key, salt; | |
189 | bool success; | |
190 | ||
191 | if (mac.len || iv.len < this->salt) | |
192 | { | |
193 | return FALSE; | |
194 | } | |
195 | ||
196 | /* we have to recombine the keys as our AEAD implementations expect the | |
197 | * salt as part of the key */ | |
198 | chunk_clear(&this->iv); | |
199 | chunk_split(iv, "ma", this->salt, &salt, iv.len - this->salt, &this->iv); | |
200 | key = chunk_cata("cc", encr, salt); | |
201 | success = this->aead->set_key(this->aead, key); | |
202 | memwipe(key.ptr, key.len); | |
203 | return success; | |
204 | } | |
205 | ||
206 | METHOD(tls_aead_t, destroy, void, | |
207 | private_tls_aead_t *this) | |
208 | { | |
209 | this->aead->destroy(this->aead); | |
210 | chunk_clear(&this->iv); | |
211 | free(this); | |
212 | } | |
213 | ||
214 | /* | |
215 | * Described in header | |
216 | */ | |
217 | tls_aead_t *tls_aead_create_seq(encryption_algorithm_t encr, size_t encr_size) | |
218 | { | |
219 | private_tls_aead_t *this; | |
220 | size_t salt; | |
221 | ||
222 | switch (encr) | |
223 | { | |
224 | case ENCR_AES_GCM_ICV16: | |
225 | case ENCR_CHACHA20_POLY1305: | |
226 | salt = 4; | |
227 | break; | |
228 | case ENCR_AES_CCM_ICV8: | |
229 | case ENCR_AES_CCM_ICV16: | |
230 | salt = 3; | |
231 | break; | |
232 | default: | |
233 | return NULL; | |
234 | } | |
235 | ||
236 | INIT(this, | |
237 | .public = { | |
238 | .encrypt = _encrypt, | |
239 | .decrypt = _decrypt, | |
240 | .get_mac_key_size = _get_mac_key_size, | |
241 | .get_encr_key_size = _get_encr_key_size, | |
242 | .get_iv_size = _get_iv_size, | |
243 | .set_keys = _set_keys, | |
244 | .destroy = _destroy, | |
245 | }, | |
246 | .aead = lib->crypto->create_aead(lib->crypto, encr, encr_size, salt), | |
247 | .salt = salt, | |
248 | ); | |
249 | ||
250 | if (!this->aead) | |
251 | { | |
252 | free(this); | |
253 | return NULL; | |
254 | } | |
255 | ||
256 | if (this->aead->get_block_size(this->aead) != 1) | |
257 | { /* TLS does not define any padding scheme for AEAD */ | |
258 | destroy(this); | |
259 | return NULL; | |
260 | } | |
261 | ||
262 | return &this->public; | |
263 | } |