]>
Commit | Line | Data |
---|---|---|
5ba3f43e A |
1 | /* |
2 | * Copyright (c) 2017 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | #include <sys/param.h> | |
30 | #include <sys/systm.h> | |
31 | #include <sys/socket.h> | |
32 | #include <sys/queue.h> | |
33 | #include <sys/syslog.h> | |
34 | #include <sys/errno.h> | |
35 | #include <sys/mbuf.h> | |
36 | #include <sys/mcache.h> | |
37 | #include <mach/vm_param.h> | |
38 | #include <kern/locks.h> | |
39 | #include <string.h> | |
40 | #include <net/if.h> | |
41 | #include <net/route.h> | |
42 | #include <net/net_osdep.h> | |
43 | #include <netinet6/ipsec.h> | |
44 | #include <netinet6/esp.h> | |
45 | #include <netinet6/esp_chachapoly.h> | |
46 | #include <netkey/key.h> | |
47 | #include <netkey/keydb.h> | |
48 | #include <corecrypto/cc.h> | |
49 | #include <libkern/crypto/chacha20poly1305.h> | |
50 | ||
51 | #define ESP_CHACHAPOLY_SALT_LEN 4 | |
52 | #define ESP_CHACHAPOLY_KEY_LEN 32 | |
53 | #define ESP_CHACHAPOLY_NONCE_LEN 12 | |
54 | ||
55 | // The minimum alignment is documented in KALLOC_LOG2_MINALIGN | |
56 | // which isn't accessible from here. Current minimum is 8. | |
57 | _Static_assert(_Alignof(chacha20poly1305_ctx) <= 8, | |
58 | "Alignment guarantee is broken"); | |
59 | ||
60 | #if ((( 8 * (ESP_CHACHAPOLY_KEY_LEN + ESP_CHACHAPOLY_SALT_LEN)) != ESP_CHACHAPOLY_KEYBITS_WITH_SALT) || \ | |
61 | (ESP_CHACHAPOLY_KEY_LEN != CCCHACHA20_KEY_NBYTES) || \ | |
62 | (ESP_CHACHAPOLY_NONCE_LEN != CCCHACHA20POLY1305_NONCE_NBYTES)) | |
63 | #error "Invalid sizes" | |
64 | #endif | |
65 | ||
66 | extern lck_mtx_t *sadb_mutex; | |
67 | ||
68 | typedef struct _esp_chachapoly_ctx { | |
69 | chacha20poly1305_ctx ccp_ctx; | |
70 | uint8_t ccp_salt[ESP_CHACHAPOLY_SALT_LEN]; | |
71 | bool ccp_implicit_iv; | |
72 | } esp_chachapoly_ctx_s, *esp_chachapoly_ctx_t; | |
73 | ||
74 | ||
75 | #define ESP_ASSERT(_cond, _format, ...) \ | |
76 | do { \ | |
77 | if (!(_cond)) { \ | |
78 | panic("%s:%d " _format, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ | |
79 | } \ | |
80 | } while (0) | |
81 | ||
82 | #define ESP_CHECK_ARG(_arg) ESP_ASSERT(_arg != NULL, #_arg "is NULL") | |
83 | ||
84 | #define _esp_log(_level, _format, ...) \ | |
85 | log(_level, "%s:%d " _format, __FUNCTION__, __LINE__, ##__VA_ARGS__) | |
86 | #define esp_log_err(_format, ...) _esp_log(LOG_ERR, _format, ##__VA_ARGS__) | |
87 | ||
88 | #define _esp_packet_log(_level, _format, ...) \ | |
89 | ipseclog((_level, "%s:%d " _format, __FUNCTION__, __LINE__, ##__VA_ARGS__)) | |
90 | #define esp_packet_log_err(_format, ...) _esp_packet_log(LOG_ERR, _format, ##__VA_ARGS__) | |
91 | ||
92 | int | |
93 | esp_chachapoly_mature(struct secasvar *sav) | |
94 | { | |
95 | const struct esp_algorithm *algo; | |
96 | ||
97 | ESP_CHECK_ARG(sav); | |
98 | ||
99 | if ((sav->flags & SADB_X_EXT_OLD) != 0) { | |
100 | esp_log_err("ChaChaPoly is incompatible with SADB_X_EXT_OLD"); | |
101 | return 1; | |
102 | } | |
103 | if ((sav->flags & SADB_X_EXT_DERIV) != 0) { | |
104 | esp_log_err("ChaChaPoly is incompatible with SADB_X_EXT_DERIV"); | |
105 | return 1; | |
106 | } | |
107 | ||
108 | if (sav->alg_enc != SADB_X_EALG_CHACHA20POLY1305) { | |
109 | esp_log_err("ChaChaPoly unsupported algorithm %d", | |
110 | sav->alg_enc); | |
111 | return 1; | |
112 | } | |
113 | ||
114 | if (sav->key_enc == NULL) { | |
115 | esp_log_err("ChaChaPoly key is missing"); | |
116 | return 1; | |
117 | } | |
118 | ||
119 | algo = esp_algorithm_lookup(sav->alg_enc); | |
120 | if (algo == NULL) { | |
121 | esp_log_err("ChaChaPoly lookup failed for algorithm %d", | |
122 | sav->alg_enc); | |
123 | return 1; | |
124 | } | |
125 | ||
126 | if (sav->key_enc->sadb_key_bits != ESP_CHACHAPOLY_KEYBITS_WITH_SALT) { | |
127 | esp_log_err("ChaChaPoly invalid key length %d bits", | |
128 | sav->key_enc->sadb_key_bits); | |
129 | return 1; | |
130 | } | |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
135 | int | |
136 | esp_chachapoly_schedlen(__unused const struct esp_algorithm *algo) | |
137 | { | |
138 | return sizeof(esp_chachapoly_ctx_s); | |
139 | } | |
140 | ||
141 | int | |
142 | esp_chachapoly_schedule(__unused const struct esp_algorithm *algo, | |
143 | struct secasvar *sav) | |
144 | { | |
145 | esp_chachapoly_ctx_t esp_ccp_ctx; | |
146 | int rc = 0; | |
147 | ||
148 | ESP_CHECK_ARG(sav); | |
149 | if (sav->ivlen != ESP_CHACHAPOLY_IV_LEN) { | |
150 | esp_log_err("Invalid ivlen %u", sav->ivlen); | |
151 | return EINVAL; | |
152 | } | |
153 | if (_KEYLEN(sav->key_enc) != ESP_CHACHAPOLY_KEY_LEN + ESP_CHACHAPOLY_SALT_LEN) { | |
154 | esp_log_err("Invalid key len %u", _KEYLEN(sav->key_enc)); | |
155 | return EINVAL; | |
156 | } | |
157 | LCK_MTX_ASSERT(sadb_mutex, LCK_MTX_ASSERT_OWNED); | |
158 | ||
159 | esp_ccp_ctx = (esp_chachapoly_ctx_t)sav->sched; | |
160 | ||
161 | rc = chacha20poly1305_init(&esp_ccp_ctx->ccp_ctx, | |
162 | (const uint8_t *)_KEYBUF(sav->key_enc)); | |
163 | if (rc != 0) { | |
164 | esp_log_err("chacha20poly1305_init returned %d", rc); | |
165 | return rc; | |
166 | } | |
167 | ||
168 | memcpy(esp_ccp_ctx->ccp_salt, | |
169 | (const uint8_t *)_KEYBUF(sav->key_enc) + ESP_CHACHAPOLY_KEY_LEN, | |
170 | sizeof(esp_ccp_ctx->ccp_salt)); | |
171 | ||
172 | esp_ccp_ctx->ccp_implicit_iv = ((sav->flags & SADB_X_EXT_IIV) != 0); | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
177 | int | |
178 | esp_chachapoly_encrypt_finalize(struct secasvar *sav, | |
179 | unsigned char *tag, | |
180 | unsigned int tag_bytes) | |
181 | { | |
182 | esp_chachapoly_ctx_t esp_ccp_ctx; | |
183 | int rc = 0; | |
184 | ||
185 | ESP_CHECK_ARG(sav); | |
186 | ESP_CHECK_ARG(tag); | |
187 | if (tag_bytes != ESP_CHACHAPOLY_ICV_LEN) { | |
188 | esp_log_err("Invalid tag_bytes %u", tag_bytes); | |
189 | return EINVAL; | |
190 | } | |
191 | ||
192 | esp_ccp_ctx = (esp_chachapoly_ctx_t)sav->sched; | |
193 | rc = chacha20poly1305_finalize(&esp_ccp_ctx->ccp_ctx, tag); | |
194 | if (rc != 0) { | |
195 | esp_log_err("chacha20poly1305_finalize returned %d", rc); | |
196 | return rc; | |
197 | } | |
198 | return 0; | |
199 | } | |
200 | ||
201 | int | |
202 | esp_chachapoly_decrypt_finalize(struct secasvar *sav, | |
203 | unsigned char *tag, | |
204 | unsigned int tag_bytes) | |
205 | { | |
206 | esp_chachapoly_ctx_t esp_ccp_ctx; | |
207 | int rc = 0; | |
208 | ||
209 | ESP_CHECK_ARG(sav); | |
210 | ESP_CHECK_ARG(tag); | |
211 | if (tag_bytes != ESP_CHACHAPOLY_ICV_LEN) { | |
212 | esp_log_err("Invalid tag_bytes %u", tag_bytes); | |
213 | return EINVAL; | |
214 | } | |
215 | ||
216 | esp_ccp_ctx = (esp_chachapoly_ctx_t)sav->sched; | |
217 | rc = chacha20poly1305_verify(&esp_ccp_ctx->ccp_ctx, tag); | |
218 | if (rc != 0) { | |
219 | esp_log_err("chacha20poly1305_finalize returned %d", rc); | |
220 | return rc; | |
221 | } | |
222 | return 0; | |
223 | } | |
224 | ||
225 | int | |
226 | esp_chachapoly_encrypt(struct mbuf *m, // head of mbuf chain | |
227 | size_t off, // offset to ESP header | |
228 | __unused size_t plen, | |
229 | struct secasvar *sav, | |
230 | __unused const struct esp_algorithm *algo, | |
231 | int ivlen) | |
232 | { | |
233 | struct mbuf *s = m; // this mbuf | |
234 | int32_t soff = 0; // offset from the head of mbuf chain (m) to head of this mbuf (s) | |
235 | int32_t sn = 0; // offset from the head of this mbuf (s) to the body | |
236 | uint8_t *sp; // buffer of a given encryption round | |
237 | size_t len; // length of a given encryption round | |
238 | const int32_t ivoff = (int32_t)off + (int32_t)sizeof(struct newesp); // IV offset | |
239 | int32_t bodyoff; // body offset | |
240 | int rc = 0; // return code of corecrypto operations | |
241 | struct newesp esp_hdr; // ESP header for AAD | |
242 | _Static_assert(sizeof(esp_hdr) == 8, "Bad size"); | |
243 | uint8_t nonce[ESP_CHACHAPOLY_NONCE_LEN]; | |
244 | esp_chachapoly_ctx_t esp_ccp_ctx; | |
245 | ||
246 | ESP_CHECK_ARG(m); | |
247 | ESP_CHECK_ARG(sav); | |
248 | if (ivlen != ESP_CHACHAPOLY_IV_LEN) { | |
249 | m_freem(m); | |
250 | esp_log_err("Invalid ivlen %u", ivlen); | |
251 | return EINVAL; | |
252 | } | |
253 | if (sav->ivlen != ESP_CHACHAPOLY_IV_LEN) { | |
254 | m_freem(m); | |
255 | esp_log_err("Invalid sav->ivlen %u", sav->ivlen); | |
256 | return EINVAL; | |
257 | } | |
258 | ||
259 | esp_ccp_ctx = (esp_chachapoly_ctx_t)sav->sched; | |
260 | if (esp_ccp_ctx->ccp_implicit_iv) { | |
261 | bodyoff = ivoff; | |
262 | } else { | |
263 | bodyoff = ivoff + ivlen; | |
264 | } | |
265 | // check if total packet length is enough to contain ESP + IV | |
266 | if (m->m_pkthdr.len < bodyoff) { | |
267 | esp_log_err("Packet too short %d < %zu", m->m_pkthdr.len, bodyoff); | |
268 | m_freem(m); | |
269 | return EINVAL; | |
270 | } | |
271 | ||
272 | rc = chacha20poly1305_reset(&esp_ccp_ctx->ccp_ctx); | |
273 | if (rc != 0) { | |
274 | m_freem(m); | |
275 | esp_log_err("chacha20poly1305_reset failed %d", rc); | |
276 | return rc; | |
277 | } | |
278 | ||
279 | // RFC 7634 dictates that the 12 byte nonce must be | |
280 | // the 4 byte salt followed by the 8 byte IV. | |
281 | // The IV MUST be non-repeating but does not need to be unpredictable, | |
282 | // so we use 4 bytes of 0 followed by the 4 byte ESP sequence number. | |
283 | // this allows us to use implicit IV -- draft-mglt-ipsecme-implicit-iv | |
284 | memset(sav->iv, 0, 4); | |
285 | memcpy(sav->iv + 4, &sav->seq, sizeof(sav->seq)); | |
286 | _Static_assert(4 + sizeof(sav->seq) == ESP_CHACHAPOLY_IV_LEN, | |
287 | "Bad IV length"); | |
288 | memcpy(nonce, esp_ccp_ctx->ccp_salt, ESP_CHACHAPOLY_SALT_LEN); | |
289 | memcpy(nonce + ESP_CHACHAPOLY_SALT_LEN, sav->iv, ESP_CHACHAPOLY_IV_LEN); | |
290 | _Static_assert(ESP_CHACHAPOLY_SALT_LEN + ESP_CHACHAPOLY_IV_LEN == sizeof(nonce), | |
291 | "Bad nonce length"); | |
292 | ||
293 | rc = chacha20poly1305_setnonce(&esp_ccp_ctx->ccp_ctx, nonce); | |
294 | if (rc != 0) { | |
295 | m_freem(m); | |
296 | esp_log_err("chacha20poly1305_setnonce failed %d", rc); | |
297 | return rc; | |
298 | } | |
299 | ||
300 | if (!esp_ccp_ctx->ccp_implicit_iv) { | |
301 | m_copyback(m, ivoff, ivlen, sav->iv); | |
302 | } | |
303 | cc_clear(sizeof(nonce), nonce); | |
304 | ||
305 | // Set Additional Authentication Data (AAD) | |
306 | m_copydata(m, (int)off, sizeof(esp_hdr), (void *)&esp_hdr); | |
307 | ||
308 | rc = chacha20poly1305_aad(&esp_ccp_ctx->ccp_ctx, | |
309 | sizeof(esp_hdr), | |
310 | (void *)&esp_hdr); | |
311 | if (rc != 0) { | |
312 | m_freem(m); | |
313 | esp_log_err("chacha20poly1305_aad failed %d", rc); | |
314 | return rc; | |
315 | } | |
316 | ||
317 | // skip headers/IV | |
318 | while (s != NULL && soff < bodyoff) { | |
319 | if (soff + s->m_len > bodyoff) { | |
320 | sn = bodyoff - soff; | |
321 | break; | |
322 | } | |
323 | ||
324 | soff += s->m_len; | |
325 | s = s->m_next; | |
326 | } | |
327 | ||
328 | while (s != NULL && soff < m->m_pkthdr.len) { | |
329 | len = (size_t)(s->m_len - sn); | |
330 | if (len == 0) { | |
331 | // skip empty mbufs | |
332 | continue; | |
333 | } | |
334 | sp = mtod(s, uint8_t *) + sn; | |
335 | ||
336 | rc = chacha20poly1305_encrypt(&esp_ccp_ctx->ccp_ctx, | |
337 | len, sp, sp); | |
338 | if (rc != 0) { | |
339 | m_freem(m); | |
340 | esp_log_err("chacha20poly1305_encrypt failed %d", rc); | |
341 | return rc; | |
342 | } | |
343 | ||
344 | sn = 0; | |
345 | soff += s->m_len; | |
346 | s = s->m_next; | |
347 | } | |
348 | if (s == NULL && soff != m->m_pkthdr.len) { | |
349 | m_freem(m); | |
350 | esp_log_err("not enough mbufs %d %d", soff, m->m_pkthdr.len); | |
351 | return EFBIG; | |
352 | } | |
353 | return 0; | |
354 | } | |
355 | ||
356 | int | |
357 | esp_chachapoly_decrypt(struct mbuf *m, // head of mbuf chain | |
358 | size_t off, // offset to ESP header | |
359 | struct secasvar *sav, | |
360 | __unused const struct esp_algorithm *algo, | |
361 | int ivlen) | |
362 | { | |
363 | struct mbuf *s = m; // this mbuf | |
364 | int32_t soff = 0; // offset from the head of mbuf chain (m) to head of this mbuf (s) | |
365 | int32_t sn = 0; // offset from the head of this mbuf (s) to the body | |
366 | uint8_t *sp; // buffer of a given encryption round | |
367 | size_t len; // length of a given encryption round | |
368 | const int32_t ivoff = (int32_t)off + (int32_t)sizeof(struct newesp); // IV offset | |
369 | int32_t bodyoff; // body offset | |
370 | int rc = 0; // return code of corecrypto operations | |
371 | struct newesp esp_hdr; // ESP header for AAD | |
372 | _Static_assert(sizeof(esp_hdr) == 8, "Bad size"); | |
373 | uint8_t nonce[ESP_CHACHAPOLY_NONCE_LEN]; | |
374 | esp_chachapoly_ctx_t esp_ccp_ctx; | |
375 | ||
376 | ESP_CHECK_ARG(m); | |
377 | ESP_CHECK_ARG(sav); | |
378 | if (ivlen != ESP_CHACHAPOLY_IV_LEN) { | |
379 | m_freem(m); | |
380 | esp_log_err("Invalid ivlen %u", ivlen); | |
381 | return EINVAL; | |
382 | } | |
383 | if (sav->ivlen != ESP_CHACHAPOLY_IV_LEN) { | |
384 | m_freem(m); | |
385 | esp_log_err("Invalid sav->ivlen %u", sav->ivlen); | |
386 | return EINVAL; | |
387 | } | |
388 | ||
389 | esp_ccp_ctx = (esp_chachapoly_ctx_t)sav->sched; | |
390 | if (esp_ccp_ctx->ccp_implicit_iv) { | |
391 | bodyoff = ivoff; | |
392 | } else { | |
393 | bodyoff = ivoff + ivlen; | |
394 | } | |
395 | // check if total packet length is enough to contain ESP + IV | |
396 | if (m->m_pkthdr.len < bodyoff) { | |
397 | esp_packet_log_err("Packet too short %d < %zu", m->m_pkthdr.len, bodyoff); | |
398 | m_freem(m); | |
399 | return EINVAL; | |
400 | } | |
401 | ||
402 | rc = chacha20poly1305_reset(&esp_ccp_ctx->ccp_ctx); | |
403 | if (rc != 0) { | |
404 | m_freem(m); | |
405 | esp_log_err("chacha20poly1305_reset failed %d", rc); | |
406 | return rc; | |
407 | } | |
408 | ||
409 | m_copydata(m, (int)off, sizeof(esp_hdr), (void *)&esp_hdr); | |
410 | ||
411 | // RFC 7634 dictates that the 12 byte nonce must be | |
412 | // the 4 byte salt followed by the 8 byte IV. | |
413 | memcpy(nonce, esp_ccp_ctx->ccp_salt, ESP_CHACHAPOLY_SALT_LEN); | |
414 | if (esp_ccp_ctx->ccp_implicit_iv) { | |
415 | // IV is implicit (4 zero bytes followed by the ESP sequence number) | |
416 | memset(nonce + ESP_CHACHAPOLY_SALT_LEN, 0, 4); | |
417 | memcpy(nonce + ESP_CHACHAPOLY_SALT_LEN + 4, &esp_hdr.esp_seq, sizeof(esp_hdr.esp_seq)); | |
418 | _Static_assert(4 + sizeof(esp_hdr.esp_seq) == ESP_CHACHAPOLY_IV_LEN, "Bad IV length"); | |
419 | } else { | |
420 | // copy IV from packet | |
421 | m_copydata(m, ivoff, ESP_CHACHAPOLY_IV_LEN, nonce + ESP_CHACHAPOLY_SALT_LEN); | |
422 | } | |
423 | _Static_assert(ESP_CHACHAPOLY_SALT_LEN + ESP_CHACHAPOLY_IV_LEN == sizeof(nonce), | |
424 | "Bad nonce length"); | |
425 | ||
426 | rc = chacha20poly1305_setnonce(&esp_ccp_ctx->ccp_ctx, nonce); | |
427 | if (rc != 0) { | |
428 | m_freem(m); | |
429 | esp_log_err("chacha20poly1305_setnonce failed %d", rc); | |
430 | return rc; | |
431 | } | |
432 | cc_clear(sizeof(nonce), nonce); | |
433 | ||
434 | // Set Additional Authentication Data (AAD) | |
435 | rc = chacha20poly1305_aad(&esp_ccp_ctx->ccp_ctx, | |
436 | sizeof(esp_hdr), | |
437 | (void *)&esp_hdr); | |
438 | if (rc != 0) { | |
439 | m_freem(m); | |
440 | esp_log_err("chacha20poly1305_aad failed %d", rc); | |
441 | return rc; | |
442 | } | |
443 | ||
444 | // skip headers/IV | |
445 | while (s != NULL && soff < bodyoff) { | |
446 | if (soff + s->m_len > bodyoff) { | |
447 | sn = bodyoff - soff; | |
448 | break; | |
449 | } | |
450 | ||
451 | soff += s->m_len; | |
452 | s = s->m_next; | |
453 | } | |
454 | ||
455 | while (s != NULL && soff < m->m_pkthdr.len) { | |
456 | len = (size_t)(s->m_len - sn); | |
457 | if (len == 0) { | |
458 | // skip empty mbufs | |
459 | continue; | |
460 | } | |
461 | sp = mtod(s, uint8_t *) + sn; | |
462 | ||
463 | rc = chacha20poly1305_decrypt(&esp_ccp_ctx->ccp_ctx, | |
464 | len, sp, sp); | |
465 | if (rc != 0) { | |
466 | m_freem(m); | |
467 | esp_packet_log_err("chacha20poly1305_decrypt failed %d", rc); | |
468 | return rc; | |
469 | } | |
470 | ||
471 | sn = 0; | |
472 | soff += s->m_len; | |
473 | s = s->m_next; | |
474 | } | |
475 | if (s == NULL && soff != m->m_pkthdr.len) { | |
476 | m_freem(m); | |
477 | esp_packet_log_err("not enough mbufs %d %d", soff, m->m_pkthdr.len); | |
478 | return EFBIG; | |
479 | } | |
480 | return 0; | |
481 | } |