1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021 The FreeBSD Foundation 5 * 6 * This software was developed by Ararat River Consulting, LLC under 7 * sponsorship from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/endian.h> 32 #include <crypto/chacha20_poly1305.h> 33 #include <opencrypto/xform_enc.h> 34 35 static const uint8_t zeroes[POLY1305_BLOCK_LEN]; 36 37 void 38 chacha20_poly1305_encrypt(uint8_t *dst, const uint8_t *src, 39 const size_t src_len, const uint8_t *aad, const size_t aad_len, 40 const uint8_t *nonce, const size_t nonce_len, const uint8_t *key) 41 { 42 const struct enc_xform *exf; 43 void *ctx; 44 size_t resid, todo; 45 uint64_t lengths[2]; 46 47 exf = &enc_xform_chacha20_poly1305; 48 ctx = __builtin_alloca(exf->ctxsize); 49 exf->setkey(ctx, key, CHACHA20_POLY1305_KEY); 50 exf->reinit(ctx, nonce, nonce_len); 51 52 exf->update(ctx, aad, aad_len); 53 if (aad_len % POLY1305_BLOCK_LEN != 0) 54 exf->update(ctx, zeroes, 55 POLY1305_BLOCK_LEN - aad_len % POLY1305_BLOCK_LEN); 56 57 resid = src_len; 58 todo = rounddown2(resid, CHACHA20_NATIVE_BLOCK_LEN); 59 if (todo > 0) { 60 exf->encrypt_multi(ctx, src, dst, todo); 61 exf->update(ctx, dst, todo); 62 src += todo; 63 dst += todo; 64 resid -= todo; 65 } 66 if (resid > 0) { 67 exf->encrypt_last(ctx, src, dst, resid); 68 exf->update(ctx, dst, resid); 69 dst += resid; 70 if (resid % POLY1305_BLOCK_LEN != 0) 71 exf->update(ctx, zeroes, 72 POLY1305_BLOCK_LEN - resid % POLY1305_BLOCK_LEN); 73 } 74 75 lengths[0] = htole64(aad_len); 76 lengths[1] = htole64(src_len); 77 exf->update(ctx, lengths, sizeof(lengths)); 78 exf->final(dst, ctx); 79 80 explicit_bzero(ctx, exf->ctxsize); 81 explicit_bzero(lengths, sizeof(lengths)); 82 } 83 84 bool 85 chacha20_poly1305_decrypt(uint8_t *dst, const uint8_t *src, 86 const size_t src_len, const uint8_t *aad, const size_t aad_len, 87 const uint8_t *nonce, const size_t nonce_len, const uint8_t *key) 88 { 89 const struct enc_xform *exf; 90 void *ctx; 91 size_t resid, todo; 92 union { 93 uint64_t lengths[2]; 94 char tag[POLY1305_HASH_LEN]; 95 } u; 96 bool result; 97 98 if (src_len < POLY1305_HASH_LEN) 99 return (false); 100 resid = src_len - POLY1305_HASH_LEN; 101 102 exf = &enc_xform_chacha20_poly1305; 103 ctx = __builtin_alloca(exf->ctxsize); 104 exf->setkey(ctx, key, CHACHA20_POLY1305_KEY); 105 exf->reinit(ctx, nonce, nonce_len); 106 107 exf->update(ctx, aad, aad_len); 108 if (aad_len % POLY1305_BLOCK_LEN != 0) 109 exf->update(ctx, zeroes, 110 POLY1305_BLOCK_LEN - aad_len % POLY1305_BLOCK_LEN); 111 exf->update(ctx, src, resid); 112 if (resid % POLY1305_BLOCK_LEN != 0) 113 exf->update(ctx, zeroes, 114 POLY1305_BLOCK_LEN - resid % POLY1305_BLOCK_LEN); 115 116 u.lengths[0] = htole64(aad_len); 117 u.lengths[1] = htole64(resid); 118 exf->update(ctx, u.lengths, sizeof(u.lengths)); 119 exf->final(u.tag, ctx); 120 result = (timingsafe_bcmp(u.tag, src + resid, POLY1305_HASH_LEN) == 0); 121 if (!result) 122 goto out; 123 124 todo = rounddown2(resid, CHACHA20_NATIVE_BLOCK_LEN); 125 if (todo > 0) { 126 exf->decrypt_multi(ctx, src, dst, todo); 127 src += todo; 128 dst += todo; 129 resid -= todo; 130 } 131 if (resid > 0) 132 exf->decrypt_last(ctx, src, dst, resid); 133 134 out: 135 explicit_bzero(ctx, exf->ctxsize); 136 explicit_bzero(&u, sizeof(u)); 137 return (result); 138 } 139 140 void 141 xchacha20_poly1305_encrypt(uint8_t *dst, const uint8_t *src, 142 const size_t src_len, const uint8_t *aad, const size_t aad_len, 143 const uint8_t *nonce, const uint8_t *key) 144 { 145 const struct enc_xform *exf; 146 void *ctx; 147 size_t resid, todo; 148 uint64_t lengths[2]; 149 150 exf = &enc_xform_xchacha20_poly1305; 151 ctx = __builtin_alloca(exf->ctxsize); 152 exf->setkey(ctx, key, XCHACHA20_POLY1305_KEY); 153 exf->reinit(ctx, nonce, XCHACHA20_POLY1305_IV_LEN); 154 155 exf->update(ctx, aad, aad_len); 156 if (aad_len % POLY1305_BLOCK_LEN != 0) 157 exf->update(ctx, zeroes, 158 POLY1305_BLOCK_LEN - aad_len % POLY1305_BLOCK_LEN); 159 160 resid = src_len; 161 todo = rounddown2(resid, CHACHA20_NATIVE_BLOCK_LEN); 162 if (todo > 0) { 163 exf->encrypt_multi(ctx, src, dst, todo); 164 exf->update(ctx, dst, todo); 165 src += todo; 166 dst += todo; 167 resid -= todo; 168 } 169 if (resid > 0) { 170 exf->encrypt_last(ctx, src, dst, resid); 171 exf->update(ctx, dst, resid); 172 dst += resid; 173 if (resid % POLY1305_BLOCK_LEN != 0) 174 exf->update(ctx, zeroes, 175 POLY1305_BLOCK_LEN - resid % POLY1305_BLOCK_LEN); 176 } 177 178 lengths[0] = htole64(aad_len); 179 lengths[1] = htole64(src_len); 180 exf->update(ctx, lengths, sizeof(lengths)); 181 exf->final(dst, ctx); 182 183 explicit_bzero(ctx, exf->ctxsize); 184 explicit_bzero(lengths, sizeof(lengths)); 185 } 186 187 bool 188 xchacha20_poly1305_decrypt(uint8_t *dst, const uint8_t *src, 189 const size_t src_len, const uint8_t *aad, const size_t aad_len, 190 const uint8_t *nonce, const uint8_t *key) 191 { 192 const struct enc_xform *exf; 193 void *ctx; 194 size_t resid, todo; 195 union { 196 uint64_t lengths[2]; 197 char tag[POLY1305_HASH_LEN]; 198 } u; 199 bool result; 200 201 if (src_len < POLY1305_HASH_LEN) 202 return (false); 203 resid = src_len - POLY1305_HASH_LEN; 204 205 exf = &enc_xform_xchacha20_poly1305; 206 ctx = __builtin_alloca(exf->ctxsize); 207 exf->setkey(ctx, key, XCHACHA20_POLY1305_KEY); 208 exf->reinit(ctx, nonce, XCHACHA20_POLY1305_IV_LEN); 209 210 exf->update(ctx, aad, aad_len); 211 if (aad_len % POLY1305_BLOCK_LEN != 0) 212 exf->update(ctx, zeroes, 213 POLY1305_BLOCK_LEN - aad_len % POLY1305_BLOCK_LEN); 214 exf->update(ctx, src, resid); 215 if (resid % POLY1305_BLOCK_LEN != 0) 216 exf->update(ctx, zeroes, 217 POLY1305_BLOCK_LEN - resid % POLY1305_BLOCK_LEN); 218 219 u.lengths[0] = htole64(aad_len); 220 u.lengths[1] = htole64(resid); 221 exf->update(ctx, u.lengths, sizeof(u.lengths)); 222 exf->final(u.tag, ctx); 223 result = (timingsafe_bcmp(u.tag, src + resid, POLY1305_HASH_LEN) == 0); 224 if (!result) 225 goto out; 226 227 todo = rounddown2(resid, CHACHA20_NATIVE_BLOCK_LEN); 228 if (todo > 0) { 229 exf->decrypt_multi(ctx, src, dst, todo); 230 src += todo; 231 dst += todo; 232 resid -= todo; 233 } 234 if (resid > 0) 235 exf->decrypt_last(ctx, src, dst, resid); 236 237 out: 238 explicit_bzero(ctx, exf->ctxsize); 239 explicit_bzero(&u, sizeof(u)); 240 return (result); 241 } 242