Files
libsodium/src/libsodium/crypto_secretstream/xchacha20poly1305/secretstream_xchacha20poly1305.c
T

238 lines
9.0 KiB
C

#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include "core.h"
#include "crypto_aead_chacha20poly1305.h"
#include "crypto_aead_xchacha20poly1305.h"
#include "crypto_core_hchacha20.h"
#include "crypto_onetimeauth_poly1305.h"
#include "crypto_secretstream_xchacha20poly1305.h"
#include "randombytes.h"
#include "utils.h"
#include "private/common.h"
#define crypto_secretstream_xchacha20poly1305_COUNTERBYTES 4U
#define crypto_secretstream_xchacha20poly1305_INONCEBYTES 8U
static const unsigned char _pad0[16] = { 0 };
void
crypto_secretstream_xchacha20poly1305_keygen
(unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES])
{
randombytes_buf(k, crypto_secretstream_xchacha20poly1305_KEYBYTES);
}
int
crypto_secretstream_xchacha20poly1305_init_push
(crypto_secretstream_xchacha20poly1305_state *state,
unsigned char out[crypto_secretstream_xchacha20poly1305_INITBYTES],
const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES])
{
COMPILER_ASSERT(crypto_secretstream_xchacha20poly1305_INITBYTES ==
crypto_core_hchacha20_INPUTBYTES + crypto_secretstream_xchacha20poly1305_INONCEBYTES);
randombytes_buf(out, crypto_secretstream_xchacha20poly1305_INITBYTES);
crypto_core_hchacha20(state->k, out, k, NULL);
memcpy(state->nonce, out + crypto_core_hchacha20_INPUTBYTES,
crypto_secretstream_xchacha20poly1305_INONCEBYTES);
memset(state->nonce + crypto_secretstream_xchacha20poly1305_INONCEBYTES, 0,
crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
memset(state->_pad, 0, sizeof state->_pad);
return 0;
}
int
crypto_secretstream_xchacha20poly1305_init_pull
(crypto_secretstream_xchacha20poly1305_state *state,
const unsigned char in[crypto_secretstream_xchacha20poly1305_INITBYTES],
const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES])
{
crypto_core_hchacha20(state->k, in, k, NULL);
memcpy(state->nonce, in + crypto_core_hchacha20_INPUTBYTES,
crypto_secretstream_xchacha20poly1305_INONCEBYTES);
memset(state->nonce + crypto_secretstream_xchacha20poly1305_INONCEBYTES, 0,
crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
memset(state->_pad, 0, sizeof state->_pad);
return 0;
}
void
crypto_secretstream_xchacha20poly1305_rekey
(crypto_secretstream_xchacha20poly1305_state *state)
{
unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
crypto_secretstream_xchacha20poly1305_INONCEBYTES];
crypto_stream_chacha20_ietf(new_key_and_inonce, sizeof new_key_and_inonce,
state->nonce, state->k);
memcpy(state->k, new_key_and_inonce, sizeof state->k);
memcpy(state->nonce, new_key_and_inonce + sizeof state->k,
crypto_secretstream_xchacha20poly1305_INONCEBYTES);
memset(state->nonce + crypto_secretstream_xchacha20poly1305_INONCEBYTES, 0,
crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
}
int
crypto_secretstream_xchacha20poly1305_push
(crypto_secretstream_xchacha20poly1305_state *state,
unsigned char *out, unsigned long long *outlen_p,
const unsigned char *m, unsigned long long mlen,
const unsigned char *ad, unsigned long long adlen, unsigned char tag)
{
crypto_onetimeauth_poly1305_state poly1305_state;
unsigned char block[64U];
unsigned char slen[8U];
unsigned char *c;
unsigned char *mac;
unsigned int i;
if (outlen_p != NULL) {
*outlen_p = 0U;
}
if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGESBYTES_MAX) {
sodium_misuse();
}
crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
crypto_onetimeauth_poly1305_init(&poly1305_state, block);
sodium_memzero(block, sizeof block);
crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
(0x10 - adlen) & 0xf);
memset(block, 0, sizeof block);
block[0] = tag;
crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
state->nonce, 1U, state->k);
crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
out[0] = block[0];
c = out + (sizeof tag);
crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
crypto_onetimeauth_poly1305_update
(&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
STORE64_LE(slen, (uint64_t) adlen);
crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
STORE64_LE(slen, (sizeof block) + mlen);
crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
mac = c + mlen;
crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
sodium_memzero(&poly1305_state, sizeof poly1305_state);
for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
state->nonce[i] ^= mac[i];
}
sodium_increment(&state->nonce[crypto_secretstream_xchacha20poly1305_INONCEBYTES],
crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
sodium_is_zero(&state->nonce[crypto_secretstream_xchacha20poly1305_INONCEBYTES],
crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
crypto_secretstream_xchacha20poly1305_rekey(state);
}
if (outlen_p != NULL) {
*outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
}
return 0;
}
int
crypto_secretstream_xchacha20poly1305_pull
(crypto_secretstream_xchacha20poly1305_state *state,
unsigned char *m, unsigned long long *mlen_p, unsigned char *tag_p,
const unsigned char *in, unsigned long long inlen,
const unsigned char *ad, unsigned long long adlen)
{
crypto_onetimeauth_poly1305_state poly1305_state;
unsigned char block[64U];
unsigned char slen[8U];
unsigned char mac[crypto_onetimeauth_poly1305_BYTES];
const unsigned char *c;
const unsigned char *stored_mac;
unsigned long long mlen;
unsigned int i;
unsigned char tag;
if (mlen_p != NULL) {
*mlen_p = 0U;
}
if (tag_p != NULL) {
*tag_p = 0xff;
}
if (inlen < crypto_secretstream_xchacha20poly1305_ABYTES) {
return -1;
}
mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGESBYTES_MAX) {
sodium_misuse();
}
crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
crypto_onetimeauth_poly1305_init(&poly1305_state, block);
sodium_memzero(block, sizeof block);
crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
(0x10 - adlen) & 0xf);
memset(block, 0, sizeof block);
block[0] = in[0];
crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
state->nonce, 1U, state->k);
tag = block[0];
block[0] = in[0];
crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
c = in + (sizeof tag);
crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
crypto_onetimeauth_poly1305_update
(&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
STORE64_LE(slen, (uint64_t) adlen);
crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
STORE64_LE(slen, (sizeof block) + mlen);
crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
sodium_memzero(&poly1305_state, sizeof poly1305_state);
stored_mac = c + mlen;
if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
sodium_memzero(mac, sizeof mac);
return -1;
}
crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
state->nonce[i] ^= mac[i];
}
sodium_increment(&state->nonce[crypto_secretstream_xchacha20poly1305_INONCEBYTES],
crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
sodium_is_zero(&state->nonce[crypto_secretstream_xchacha20poly1305_INONCEBYTES],
crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
crypto_secretstream_xchacha20poly1305_rekey(state);
}
if (mlen_p != NULL) {
*mlen_p = mlen;
}
if (tag_p != NULL) {
*tag_p = tag;
}
return 0;
}
size_t
crypto_secretstream_xchacha20poly1305_statebytes(void)
{
return sizeof(crypto_secretstream_xchacha20poly1305_state);
}