blob: 0aef72549e7dce3807a1842ee9b2f7e3a5a048a7 [file] [log] [blame]
/*
* cifra - embedded cryptography library
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to the
* public domain worldwide. This software is distributed without any
* warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication
* along with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include "chacha20poly1305.h"
#include "salsa20.h"
#include "poly1305.h"
#include "bitops.h"
#include "handy.h"
#define ENCRYPT 1
#define DECRYPT 0
#define SUCCESS 0
#define FAILURE 1
static int process(const uint8_t key[32],
const uint8_t nonce[12],
const uint8_t *header, size_t nheader,
const uint8_t *input, size_t nbytes,
uint8_t *output,
int mode,
uint8_t tag[16])
{
/* First, generate the Poly1305 key by running ChaCha20 with the
* given key and a zero counter. The first half of the
* 64-byte output is the key. */
uint8_t fullnonce[16] = { 0 };
memcpy(fullnonce + 4, nonce, 12);
uint8_t polykey[32] = { 0 };
cf_chacha20_ctx chacha;
cf_chacha20_init_custom(&chacha, key, 32, fullnonce, 4);
cf_chacha20_cipher(&chacha, polykey, polykey, sizeof polykey);
/* Now initialise Poly1305. */
cf_poly1305 poly;
cf_poly1305_init(&poly, polykey, polykey + 16);
mem_clean(polykey, sizeof polykey);
/* Discard next 32 bytes of chacha20 key stream. */
cf_chacha20_cipher(&chacha, polykey, polykey, sizeof polykey);
mem_clean(polykey, sizeof polykey);
/* The input to Poly1305 is:
* AAD || pad(AAD) || cipher || pad(cipher) || len_64(aad) || len_64(cipher) */
uint8_t padbuf[16] = { 0 };
#define PADLEN(x) (16 - ((x) & 0xf))
/* AAD || pad(AAD) */
cf_poly1305_update(&poly, header, nheader);
cf_poly1305_update(&poly, padbuf, PADLEN(nheader));
/* || cipher */
if (mode == ENCRYPT)
{
/* If we're encrypting, we compute the ciphertext
* before inputting it into the MAC. */
cf_chacha20_cipher(&chacha, input, output, nbytes);
cf_poly1305_update(&poly, output, nbytes);
} else {
/* Otherwise: decryption -- input the ciphertext.
* Delay actual decryption until we checked the MAC. */
cf_poly1305_update(&poly, input, nbytes);
}
/* || pad(cipher) */
cf_poly1305_update(&poly, padbuf, PADLEN(nbytes));
/* || len_64(aad) || len_64(cipher) */
write64_le(nheader, padbuf);
write64_le(nbytes, padbuf + 8);
cf_poly1305_update(&poly, padbuf, sizeof padbuf);
/* MAC computation is now complete. */
if (mode == ENCRYPT)
{
cf_poly1305_finish(&poly, tag);
mem_clean(&chacha, sizeof chacha);
return SUCCESS;
}
/* Decrypt mode: calculate tag, and check it.
* If it's correct, proceed with decryption. */
uint8_t checktag[16];
cf_poly1305_finish(&poly, checktag);
if (mem_eq(checktag, tag, sizeof checktag))
{
cf_chacha20_cipher(&chacha, input, output, nbytes);
mem_clean(&chacha, sizeof chacha);
mem_clean(checktag, sizeof checktag);
return SUCCESS;
} else {
mem_clean(output, nbytes);
mem_clean(&chacha, sizeof chacha);
mem_clean(checktag, sizeof checktag);
return FAILURE;
}
}
void cf_chacha20poly1305_encrypt(const uint8_t key[32],
const uint8_t nonce[12],
const uint8_t *header, size_t nheader,
const uint8_t *plaintext, size_t nbytes,
uint8_t *ciphertext,
uint8_t tag[16])
{
process(key,
nonce,
header, nheader,
plaintext, nbytes,
ciphertext,
ENCRYPT,
tag);
}
int cf_chacha20poly1305_decrypt(const uint8_t key[32],
const uint8_t nonce[12],
const uint8_t *header, size_t nheader,
const uint8_t *ciphertext, size_t nbytes,
const uint8_t tag[16],
uint8_t *plaintext)
{
uint8_t ourtag[16];
memcpy(ourtag, tag, sizeof ourtag);
return process(key,
nonce,
header, nheader,
ciphertext, nbytes,
plaintext,
DECRYPT,
ourtag);
}