1 /* 2 * Copyright (c) 2018 Thomas Pornin <pornin@bolet.org> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 #include "inner.h" 26 27 /* 28 * CCM initialisation. This does everything except setting the vtable, 29 * which depends on whether this is a context for encrypting or for 30 * decrypting. 31 */ 32 static void 33 gen_ccm_init(br_sslrec_ccm_context *cc, 34 const br_block_ctrcbc_class *bc_impl, 35 const void *key, size_t key_len, 36 const void *iv, size_t tag_len) 37 { 38 cc->seq = 0; 39 bc_impl->init(&cc->bc.vtable, key, key_len); 40 memcpy(cc->iv, iv, sizeof cc->iv); 41 cc->tag_len = tag_len; 42 } 43 44 static void 45 in_ccm_init(br_sslrec_ccm_context *cc, 46 const br_block_ctrcbc_class *bc_impl, 47 const void *key, size_t key_len, 48 const void *iv, size_t tag_len) 49 { 50 cc->vtable.in = &br_sslrec_in_ccm_vtable; 51 gen_ccm_init(cc, bc_impl, key, key_len, iv, tag_len); 52 } 53 54 static int 55 ccm_check_length(const br_sslrec_ccm_context *cc, size_t rlen) 56 { 57 /* 58 * CCM overhead is 8 bytes for nonce_explicit, and the tag 59 * (normally 8 or 16 bytes, depending on cipher suite). 60 */ 61 size_t over; 62 63 over = 8 + cc->tag_len; 64 return rlen >= over && rlen <= (16384 + over); 65 } 66 67 static unsigned char * 68 ccm_decrypt(br_sslrec_ccm_context *cc, 69 int record_type, unsigned version, void *data, size_t *data_len) 70 { 71 br_ccm_context zc; 72 unsigned char *buf; 73 unsigned char nonce[12], header[13]; 74 size_t len; 75 76 buf = (unsigned char *)data + 8; 77 len = *data_len - (8 + cc->tag_len); 78 79 /* 80 * Make nonce (implicit + explicit parts). 81 */ 82 memcpy(nonce, cc->iv, sizeof cc->iv); 83 memcpy(nonce + 4, data, 8); 84 85 /* 86 * Assemble synthetic header for the AAD. 87 */ 88 br_enc64be(header, cc->seq ++); 89 header[8] = (unsigned char)record_type; 90 br_enc16be(header + 9, version); 91 br_enc16be(header + 11, len); 92 93 /* 94 * Perform CCM decryption. 95 */ 96 br_ccm_init(&zc, &cc->bc.vtable); 97 br_ccm_reset(&zc, nonce, sizeof nonce, sizeof header, len, cc->tag_len); 98 br_ccm_aad_inject(&zc, header, sizeof header); 99 br_ccm_flip(&zc); 100 br_ccm_run(&zc, 0, buf, len); 101 if (!br_ccm_check_tag(&zc, buf + len)) { 102 return NULL; 103 } 104 *data_len = len; 105 return buf; 106 } 107 108 /* see bearssl_ssl.h */ 109 const br_sslrec_in_ccm_class br_sslrec_in_ccm_vtable = { 110 { 111 sizeof(br_sslrec_ccm_context), 112 (int (*)(const br_sslrec_in_class *const *, size_t)) 113 &ccm_check_length, 114 (unsigned char *(*)(const br_sslrec_in_class **, 115 int, unsigned, void *, size_t *)) 116 &ccm_decrypt 117 }, 118 (void (*)(const br_sslrec_in_ccm_class **, 119 const br_block_ctrcbc_class *, const void *, size_t, 120 const void *, size_t)) 121 &in_ccm_init 122 }; 123 124 static void 125 out_ccm_init(br_sslrec_ccm_context *cc, 126 const br_block_ctrcbc_class *bc_impl, 127 const void *key, size_t key_len, 128 const void *iv, size_t tag_len) 129 { 130 cc->vtable.out = &br_sslrec_out_ccm_vtable; 131 gen_ccm_init(cc, bc_impl, key, key_len, iv, tag_len); 132 } 133 134 static void 135 ccm_max_plaintext(const br_sslrec_ccm_context *cc, 136 size_t *start, size_t *end) 137 { 138 size_t len; 139 140 *start += 8; 141 len = *end - *start - cc->tag_len; 142 if (len > 16384) { 143 len = 16384; 144 } 145 *end = *start + len; 146 } 147 148 static unsigned char * 149 ccm_encrypt(br_sslrec_ccm_context *cc, 150 int record_type, unsigned version, void *data, size_t *data_len) 151 { 152 br_ccm_context zc; 153 unsigned char *buf; 154 unsigned char nonce[12], header[13]; 155 size_t len; 156 157 buf = (unsigned char *)data; 158 len = *data_len; 159 160 /* 161 * Make nonce; the explicit part is an encoding of the sequence 162 * number. 163 */ 164 memcpy(nonce, cc->iv, sizeof cc->iv); 165 br_enc64be(nonce + 4, cc->seq); 166 167 /* 168 * Assemble synthetic header for the AAD. 169 */ 170 br_enc64be(header, cc->seq ++); 171 header[8] = (unsigned char)record_type; 172 br_enc16be(header + 9, version); 173 br_enc16be(header + 11, len); 174 175 /* 176 * Perform CCM encryption. 177 */ 178 br_ccm_init(&zc, &cc->bc.vtable); 179 br_ccm_reset(&zc, nonce, sizeof nonce, sizeof header, len, cc->tag_len); 180 br_ccm_aad_inject(&zc, header, sizeof header); 181 br_ccm_flip(&zc); 182 br_ccm_run(&zc, 1, buf, len); 183 br_ccm_get_tag(&zc, buf + len); 184 185 /* 186 * Assemble header and adjust pointer/length. 187 */ 188 len += 8 + cc->tag_len; 189 buf -= 13; 190 memcpy(buf + 5, nonce + 4, 8); 191 buf[0] = (unsigned char)record_type; 192 br_enc16be(buf + 1, version); 193 br_enc16be(buf + 3, len); 194 *data_len = len + 5; 195 return buf; 196 } 197 198 /* see bearssl_ssl.h */ 199 const br_sslrec_out_ccm_class br_sslrec_out_ccm_vtable = { 200 { 201 sizeof(br_sslrec_ccm_context), 202 (void (*)(const br_sslrec_out_class *const *, 203 size_t *, size_t *)) 204 &ccm_max_plaintext, 205 (unsigned char *(*)(const br_sslrec_out_class **, 206 int, unsigned, void *, size_t *)) 207 &ccm_encrypt 208 }, 209 (void (*)(const br_sslrec_out_ccm_class **, 210 const br_block_ctrcbc_class *, const void *, size_t, 211 const void *, size_t)) 212 &out_ccm_init 213 }; 214