X-Git-Url: https://tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Fchacha-poly1305%2Fchachapoly.c;fp=src%2Fchacha-poly1305%2Fchachapoly.c;h=9a6620ce45bef2baa64be45a419bcb850aa9d7c6;hb=63df2139835bb532125562c73ec3efbc437a3634;hp=0000000000000000000000000000000000000000;hpb=9ce00234190baec0eaa608a5093bb3823487ef0c;p=tinc diff --git a/src/chacha-poly1305/chachapoly.c b/src/chacha-poly1305/chachapoly.c new file mode 100644 index 00000000..9a6620ce --- /dev/null +++ b/src/chacha-poly1305/chachapoly.c @@ -0,0 +1,182 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Grigori Goronzy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include "chachapoly.h" + +/** + * Constant-time memory compare. This should help to protect against + * side-channel attacks. + * + * \param av input 1 + * \param bv input 2 + * \param n bytes to compare + * \return 0 if inputs are equal + */ +static int memcmp_eq(const void *av, const void *bv, int n) { + const unsigned char *a = (const unsigned char *) av; + const unsigned char *b = (const unsigned char *) bv; + unsigned char res = 0; + int i; + + for(i = 0; i < n; i++) { + res |= *a ^ *b; + a++; + b++; + } + + return res; +} + +/** + * Poly1305 tag generation. This concatenates a string according to the rules + * outlined in RFC 7539 and calculates the tag. + * + * \param poly_key 32 byte secret one-time key for poly1305 + * \param ad associated data + * \param ad_len associated data length in bytes + * \param ct ciphertext + * \param ct_len ciphertext length in bytes + * \param tag pointer to 16 bytes for tag storage + */ +static void poly1305_get_tag(unsigned char *poly_key, const void *ad, + int ad_len, const void *ct, int ct_len, unsigned char *tag) { + struct poly1305_context poly; + unsigned left_over; + uint64_t len; + unsigned char pad[16]; + + poly1305_init(&poly, poly_key); + memset(&pad, 0, sizeof(pad)); + + /* associated data and padding */ + poly1305_update(&poly, ad, ad_len); + left_over = ad_len % 16; + + if(left_over) { + poly1305_update(&poly, pad, 16 - left_over); + } + + /* payload and padding */ + poly1305_update(&poly, ct, ct_len); + left_over = ct_len % 16; + + if(left_over) { + poly1305_update(&poly, pad, 16 - left_over); + } + + /* lengths */ + len = ad_len; + poly1305_update(&poly, (unsigned char *)&len, 8); + len = ct_len; + poly1305_update(&poly, (unsigned char *)&len, 8); + + poly1305_finish(&poly, tag); +} + +int chachapoly_init(struct chachapoly_ctx *ctx, const void *key, int key_len) { + assert(key_len == 128 || key_len == 256); + + memset(ctx, 0, sizeof(*ctx)); + chacha_keysetup(&ctx->cha_ctx, key, key_len); + return CHACHAPOLY_OK; +} + +int chachapoly_crypt(struct chachapoly_ctx *ctx, const void *nonce, + const void *ad, int ad_len, void *input, int input_len, + void *output, void *tag, int tag_len, int encrypt) { + unsigned char poly_key[CHACHA_BLOCKLEN]; + unsigned char calc_tag[POLY1305_TAGLEN]; + const unsigned char one[4] = { 1, 0, 0, 0 }; + + /* initialize keystream and generate poly1305 key */ + memset(poly_key, 0, sizeof(poly_key)); + chacha_ivsetup(&ctx->cha_ctx, nonce, NULL); + chacha_encrypt_bytes(&ctx->cha_ctx, poly_key, poly_key, sizeof(poly_key)); + + /* check tag if decrypting */ + if(encrypt == 0 && tag_len) { + poly1305_get_tag(poly_key, ad, ad_len, input, input_len, calc_tag); + + if(memcmp_eq(calc_tag, tag, tag_len) != 0) { + return CHACHAPOLY_INVALID_MAC; + } + } + + /* crypt data */ + chacha_ivsetup(&ctx->cha_ctx, nonce, one); + chacha_encrypt_bytes(&ctx->cha_ctx, (unsigned char *)input, + (unsigned char *)output, input_len); + + /* add tag if encrypting */ + if(encrypt && tag_len) { + poly1305_get_tag(poly_key, ad, ad_len, output, input_len, calc_tag); + memcpy(tag, calc_tag, tag_len); + } + + return CHACHAPOLY_OK; +} + +int chachapoly_crypt_short(struct chachapoly_ctx *ctx, const void *nonce, + const void *ad, int ad_len, void *input, int input_len, + void *output, void *tag, int tag_len, int encrypt) { + unsigned char keystream[CHACHA_BLOCKLEN]; + unsigned char calc_tag[POLY1305_TAGLEN]; + int i; + + assert(input_len <= 32); + + /* initialize keystream and generate poly1305 key */ + memset(keystream, 0, sizeof(keystream)); + chacha_ivsetup(&ctx->cha_ctx, nonce, NULL); + chacha_encrypt_bytes(&ctx->cha_ctx, keystream, keystream, + sizeof(keystream)); + + /* check tag if decrypting */ + if(encrypt == 0 && tag_len) { + poly1305_get_tag(keystream, ad, ad_len, input, input_len, calc_tag); + + if(memcmp_eq(calc_tag, tag, tag_len) != 0) { + return CHACHAPOLY_INVALID_MAC; + } + } + + /* crypt data */ + for(i = 0; i < input_len; i++) { + ((unsigned char *)output)[i] = + ((unsigned char *)input)[i] ^ keystream[32 + i]; + } + + /* add tag if encrypting */ + if(encrypt && tag_len) { + poly1305_get_tag(keystream, ad, ad_len, output, input_len, calc_tag); + memcpy(tag, calc_tag, tag_len); + } + + return CHACHAPOLY_OK; +}