forked from RIOT-OS/RIOT
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
crypto: Add chacha20poly1305 AEAD algorithm
- Loading branch information
Showing
2 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/* | ||
* Copyright (C) 2008 D. J. Bernstein (dedicated to the public domain) | ||
* Copyright (C) 2015 René Kijewski <rene.kijewski@fu-berlin.de> | ||
* Copyright (C) 2018 Koen Zandberg | ||
* | ||
* This file is subject to the terms and conditions of the GNU Lesser | ||
* General Public License v2.1. See the file LICENSE in the top level | ||
* directory for more details. | ||
*/ | ||
|
||
/** | ||
* @ingroup sys_crypto_chacha20poly1305 | ||
* @{ | ||
* @file | ||
* @brief Implementation of the chacha20poly1305 aead cipher | ||
* | ||
* @author Koen Zandberg <koen@bergzand.net> | ||
* @see https://tools.ietf.org/html/rfc8439 | ||
* @} | ||
*/ | ||
|
||
#include <stdlib.h> | ||
#include <stdint.h> | ||
#include <string.h> | ||
|
||
#include "crypto/helper.h" | ||
#include "crypto/chacha20poly1305.h" | ||
#include "crypto/poly1305.h" | ||
|
||
/* Missing operations to convert numbers to little endian prevents this from | ||
* working on big endian systems */ | ||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ | ||
# error "This code is implementented in a way that it will only work for little-endian systems!" | ||
#endif | ||
|
||
/* Nothing to hide here, Literally "expand 32-byte k" */ | ||
static const uint32_t constant[] = {0x61707865, | ||
0x3320646e, | ||
0x79622d32, | ||
0x6b206574}; | ||
|
||
/* Padding to add to the poly1305 authentication tag */ | ||
static const uint8_t padding[15] = {0}; | ||
|
||
static uint32_t u8to32(const uint8_t *p) | ||
{ | ||
return | ||
((uint32_t)p[0] | | ||
((uint32_t)p[1] << 8) | | ||
((uint32_t)p[2] << 16) | | ||
((uint32_t)p[3] << 24)); | ||
} | ||
|
||
/* Single round */ | ||
void _r(uint32_t *a, uint32_t *b, uint32_t *d, unsigned c) | ||
{ | ||
*a += *b; | ||
uint32_t tmp = *a ^ *d; | ||
*d = (tmp << c) | (tmp >> (32 - c)); | ||
} | ||
|
||
void _add_initial(chacha20poly1305_ctx_t *ctx, const uint8_t *key, | ||
const uint8_t *nonce, uint32_t blk) | ||
{ | ||
for (unsigned i = 0; i < 4; i++) { | ||
ctx->state[i] += constant[i]; | ||
} | ||
for (unsigned i = 0; i < 8; i++) { | ||
ctx->state[i+4] += u8to32(key + 4*i); | ||
} | ||
ctx->state[12] += u8to32((uint8_t*)&blk); | ||
ctx->state[13] += u8to32(nonce); | ||
ctx->state[14] += u8to32(nonce+4); | ||
ctx->state[15] += u8to32(nonce+8); | ||
} | ||
|
||
void _keystream(chacha20poly1305_ctx_t *ctx, const uint8_t *key, | ||
const uint8_t *nonce, uint32_t blk) | ||
{ | ||
/* Initialize block state */ | ||
memset(ctx->state, 0, sizeof(ctx->state)); | ||
_add_initial(ctx, key, nonce, blk); | ||
|
||
/* perform rounds */ | ||
for (unsigned i = 0; i < 80; ++i) { | ||
uint32_t *a = &ctx->state[((i ) & 3) ]; | ||
uint32_t *b = &ctx->state[((i + ((i & 4) ? 1 : 0)) & 3) + (4 * 1)]; | ||
uint32_t *c = &ctx->state[((i + ((i & 4) ? 2 : 0)) & 3) + (4 * 2)]; | ||
uint32_t *d = &ctx->state[((i + ((i & 4) ? 3 : 0)) & 3) + (4 * 3)]; | ||
_r(a, b, d, 16); | ||
_r(c, d, b, 12); | ||
_r(a, b, d, 8); | ||
_r(c, d, b, 7); | ||
} | ||
/* add initial state */ | ||
_add_initial(ctx, key, nonce, blk); | ||
} | ||
|
||
void _xcrypt(chacha20poly1305_ctx_t *ctx, const uint8_t *key, | ||
const uint8_t *nonce, const uint8_t *in, uint8_t *out, size_t len) | ||
{ | ||
/* Number of full 64 byte blocks */ | ||
const size_t num_blocks = len >> 6; | ||
size_t pos = 0; | ||
/* xcrypt full blocks */ | ||
for (size_t i = 0; i < num_blocks; i++, pos += 64) { | ||
_keystream(ctx, key, nonce, i+1); | ||
for (size_t j = 0; j < 64; j++) { | ||
out[pos+j] = in[pos+j] ^ ((uint8_t*)ctx->state)[j]; | ||
} | ||
} | ||
/* xcrypt remaining bytes */ | ||
if (len - pos) { | ||
_keystream(ctx, key, nonce, num_blocks+1); | ||
for (size_t j = 0; j < len - pos; j++) { | ||
out[pos+j] = in[pos+j] ^ ((uint8_t*)ctx->state)[j]; | ||
} | ||
} | ||
} | ||
|
||
void _poly1305_padded(poly1305_ctx_t *pctx, const uint8_t *data, size_t len) | ||
{ | ||
poly1305_update(pctx, data, len); | ||
const size_t padlen = (16 - len) & 0xF; | ||
poly1305_update(pctx, padding, padlen); | ||
} | ||
|
||
/* Generate a poly1305 tag */ | ||
void _poly1305_gentag(uint8_t *mac, const uint8_t *key, const uint8_t *nonce, | ||
const uint8_t *cipher, size_t cipherlen, | ||
const uint8_t *aad, size_t aadlen) | ||
{ | ||
chacha20poly1305_ctx_t ctx; | ||
/* generate one time key */ | ||
_keystream(&ctx, key, nonce, 0); | ||
poly1305_init(&ctx.poly, (uint8_t*)ctx.state); | ||
/* Add aad */ | ||
_poly1305_padded(&ctx.poly, aad, aadlen); | ||
/* Add ciphertext */ | ||
_poly1305_padded(&ctx.poly, cipher, cipherlen); | ||
/* Add aad length */ | ||
const uint64_t lengths[2] = {aadlen, cipherlen}; | ||
poly1305_update(&ctx.poly, (uint8_t*)lengths, sizeof(lengths)); | ||
poly1305_finish(&ctx.poly, mac); | ||
crypto_secure_wipe(&ctx, sizeof(ctx)); | ||
} | ||
|
||
void chacha20poly1305_encrypt(uint8_t *cipher, const uint8_t *msg, | ||
size_t msglen, const uint8_t *aad, size_t aadlen, | ||
const uint8_t *key, const uint8_t *nonce) | ||
{ | ||
chacha20poly1305_ctx_t ctx; | ||
_xcrypt(&ctx, key, nonce, msg, cipher, msglen); | ||
crypto_secure_wipe(&ctx, sizeof(ctx)); | ||
/* Generate tag */ | ||
_poly1305_gentag(&cipher[msglen], key, nonce, | ||
cipher, msglen, aad, aadlen); | ||
/* Wipe structures */ | ||
} | ||
|
||
int chacha20poly1305_decrypt(const uint8_t *cipher, size_t cipherlen, | ||
uint8_t *msg, size_t *msglen, | ||
const uint8_t *aad, size_t aadlen, | ||
const uint8_t *key, const uint8_t *nonce) | ||
{ | ||
*msglen = cipherlen - CHACHA20POLY1305_TAG_BYTES; | ||
uint8_t mac[16]; | ||
_poly1305_gentag(mac, key, nonce, cipher, | ||
cipherlen - CHACHA20POLY1305_TAG_BYTES, aad, aadlen); | ||
if (crypto_equals(cipher+*msglen, mac, CHACHA20POLY1305_TAG_BYTES) == 0) { | ||
return 0; | ||
} | ||
chacha20poly1305_ctx_t ctx; | ||
/* Number of full blocks */ | ||
_xcrypt(&ctx, key, nonce, cipher, msg, *msglen); | ||
return 1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/* | ||
* Copyright (C) 2018 Koen Zandberg | ||
* | ||
* This file is subject to the terms and conditions of the GNU Lesser | ||
* General Public License v2.1. See the file LICENSE in the top level | ||
* directory for more details. | ||
*/ | ||
|
||
/** | ||
* @defgroup sys_crypto_chacha20poly1305 chacha20poly1305 AEAD cipher | ||
* @ingroup sys_crypto | ||
* @brief Provides RFC 8439 style chacha20poly1305 | ||
* | ||
* This module provides the chacha20poly1305 AEAD symmetric cipher following | ||
* [rfc 8439](https://tools.ietf.org/html/rfc8439). | ||
* | ||
* Nonces must be unique per message for a single key. They are allowed to be | ||
* predictable, e.g. a message counter and are allowed to be visible during | ||
* transmission. | ||
* @{ | ||
* | ||
* @file | ||
* @brief Chacha20poly1305 functions | ||
* | ||
* @author Koen Zandberg <koen@bergzand.net> | ||
*/ | ||
|
||
#ifndef CRYPTO_CHACHA20POLY1305_H | ||
#define CRYPTO_CHACHA20POLY1305_H | ||
|
||
#include "crypto/poly1305.h" | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
#define CHACHA20POLY1305_KEY_BYTES (32U) /**< Key length in bytes */ | ||
#define CHACHA20POLY1305_NONCE_BYTES (12U) /**< Nonce length in bytes */ | ||
#define CHACHA20POLY1305_TAG_BYTES (16U) /**< Tag length in bytes */ | ||
|
||
/** | ||
* @brief Chacha20poly1305 state struct | ||
*/ | ||
typedef union | ||
{ | ||
/* We need both the state matrix and the poly1305 state, but nearly not at | ||
* the same time. This works as long as the first 8 members of state | ||
* overlap fully or completely not with the first and second key parts | ||
* from the @ref poly1305_ctx_t struct */ | ||
uint32_t state[16]; /**< The current state of the key stream. */ | ||
poly1305_ctx_t poly; /**< Poly1305 state for the MAC */ | ||
} chacha20poly1305_ctx_t; | ||
|
||
/** | ||
* @brief Encrypt a plaintext to ciphertext and append a tag to protect the | ||
* ciphertext and additional data. | ||
* | ||
* It is allowed to have cipher == msg as long | ||
* as there is @ref CHACHA20POLY1305_TAG_BYTES space left to hold the | ||
* authentication tag | ||
* | ||
* | ||
* @param[out] cipher resulting ciphertext, is CHACHA20POLY1305_TAG_BYTES | ||
* longer than the message length | ||
* @param[in] msg message to encrypt | ||
* @param[in] msglen length in bytes of the message | ||
* @param[in] aad additional authenticated data to protect | ||
* @param[in] aadlen length of the additional authenticated data | ||
* @param[in] key key to encrypt with, must be | ||
* CHACHA20POLY1305_KEY_BYTES long | ||
* @param[in] nonce Nonce to use. Must be CHACHA20POLY1305_NONCE_BYTES | ||
* long | ||
*/ | ||
void chacha20poly1305_encrypt(uint8_t *cipher, const uint8_t *msg, | ||
size_t msglen, const uint8_t *aad, size_t aadlen, | ||
const uint8_t *key, const uint8_t *nonce); | ||
|
||
/** | ||
* @brief Verify the tag and decrypt a ciphertext to plaintext. | ||
* | ||
* It is allowed to have cipher == msg | ||
* | ||
* @param[in] cipher resulting ciphertext, is CHACHA20POLY1305_TAG_BYTES | ||
* longer than the message length | ||
* @param[in] cipherlen length of the ciphertext | ||
* @param[out] msg message to encrypt | ||
* @param[in] msglen resulting length in bytes of the message | ||
* @param[in] aad additional authenticated data to verify | ||
* @param[in] aadlen length of the additional authenticated data | ||
* @param[in] key key to decrypt with, must be | ||
* CHACHA20POLY1305_KEY_BYTES long | ||
* @param[in] nonce Nonce to use. Must be CHACHA20POLY1305_NONCE_BYTES | ||
* long | ||
*/ | ||
int chacha20poly1305_decrypt(const uint8_t *cipher, size_t cipherlen, | ||
uint8_t *msg, size_t *msglen, | ||
const uint8_t *aad, size_t aadlen, | ||
const uint8_t *key, const uint8_t *nonce); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
#endif /* CRYPTO_CHACHA20POLY1305_H */ | ||
/** @} */ |