Revision control

Copy as Markdown

Other Tools

/* mac-poly1305.c - Poly1305 based MACs
* Copyright (C) 2014 Jussi Kivilinna <jussi.kivilinna@iki.fi>
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser general Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "g10lib.h"
#include "mac-internal.h"
#include "poly1305-internal.h"
struct poly1305mac_context_s {
poly1305_context_t ctx;
gcry_cipher_hd_t hd;
struct {
unsigned int key_set:1;
unsigned int nonce_set:1;
unsigned int tag:1;
} marks;
byte tag[POLY1305_TAGLEN];
byte key[POLY1305_KEYLEN];
};
static gcry_err_code_t
poly1305mac_open (gcry_mac_hd_t h)
{
struct poly1305mac_context_s *mac_ctx;
int secure = (h->magic == CTX_MAC_MAGIC_SECURE);
unsigned int flags = (secure ? GCRY_CIPHER_SECURE : 0);
gcry_err_code_t err;
int cipher_algo;
if (secure)
mac_ctx = xtrycalloc_secure (1, sizeof(*mac_ctx));
else
mac_ctx = xtrycalloc (1, sizeof(*mac_ctx));
if (!mac_ctx)
return gpg_err_code_from_syserror ();
h->u.poly1305mac.ctx = mac_ctx;
switch (h->spec->algo)
{
default:
/* already checked. */
case GCRY_MAC_POLY1305:
/* plain Poly1305. */
cipher_algo = -1;
return 0;
case GCRY_MAC_POLY1305_AES:
cipher_algo = GCRY_CIPHER_AES;
break;
case GCRY_MAC_POLY1305_CAMELLIA:
cipher_algo = GCRY_CIPHER_CAMELLIA128;
break;
case GCRY_MAC_POLY1305_TWOFISH:
cipher_algo = GCRY_CIPHER_TWOFISH;
break;
case GCRY_MAC_POLY1305_SERPENT:
cipher_algo = GCRY_CIPHER_SERPENT128;
break;
case GCRY_MAC_POLY1305_SEED:
cipher_algo = GCRY_CIPHER_SEED;
break;
}
err = _gcry_cipher_open_internal (&mac_ctx->hd, cipher_algo,
GCRY_CIPHER_MODE_ECB, flags);
if (err)
goto err_free;
return 0;
err_free:
xfree(h->u.poly1305mac.ctx);
return err;
}
static void
poly1305mac_close (gcry_mac_hd_t h)
{
struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
if (h->spec->algo != GCRY_MAC_POLY1305)
_gcry_cipher_close (mac_ctx->hd);
xfree(mac_ctx);
}
static gcry_err_code_t
poly1305mac_prepare_key (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
{
struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
size_t block_keylen = keylen - 16;
/* Need at least 16 + 1 byte key. */
if (keylen <= 16)
return GPG_ERR_INV_KEYLEN;
/* For Poly1305-AES, first part of key is passed to Poly1305 as is. */
memcpy (mac_ctx->key, key + block_keylen, 16);
/* Remaining part is used as key for the block cipher. */
return _gcry_cipher_setkey (mac_ctx->hd, key, block_keylen);
}
static gcry_err_code_t
poly1305mac_setkey (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
{
struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
gcry_err_code_t err;
memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
mac_ctx->marks.key_set = 0;
mac_ctx->marks.nonce_set = 0;
mac_ctx->marks.tag = 0;
if (h->spec->algo != GCRY_MAC_POLY1305)
{
err = poly1305mac_prepare_key (h, key, keylen);
if (err)
return err;
/* Poly1305-AES/etc also need nonce. */
mac_ctx->marks.key_set = 1;
mac_ctx->marks.nonce_set = 0;
}
else
{
/* For plain Poly1305, key is the nonce and setup is complete now. */
if (keylen != POLY1305_KEYLEN)
return GPG_ERR_INV_KEYLEN;
memcpy (mac_ctx->key, key, keylen);
err = _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
if (err)
{
memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
return err;
}
mac_ctx->marks.key_set = 1;
mac_ctx->marks.nonce_set = 1;
}
return 0;
}
static gcry_err_code_t
poly1305mac_setiv (gcry_mac_hd_t h, const unsigned char *iv, size_t ivlen)
{
struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
gcry_err_code_t err;
if (h->spec->algo == GCRY_MAC_POLY1305)
return GPG_ERR_INV_ARG;
if (ivlen != 16)
return GPG_ERR_INV_ARG;
if (!mac_ctx->marks.key_set)
return 0;
memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
mac_ctx->marks.nonce_set = 0;
mac_ctx->marks.tag = 0;
/* Prepare second part of the poly1305 key. */
err = _gcry_cipher_encrypt (mac_ctx->hd, mac_ctx->key + 16, 16, iv, 16);
if (err)
return err;
err = _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
if (err)
return err;
mac_ctx->marks.nonce_set = 1;
return 0;
}
static gcry_err_code_t
poly1305mac_reset (gcry_mac_hd_t h)
{
struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
if (!mac_ctx->marks.key_set || !mac_ctx->marks.nonce_set)
return GPG_ERR_INV_STATE;
memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
mac_ctx->marks.key_set = 1;
mac_ctx->marks.nonce_set = 1;
mac_ctx->marks.tag = 0;
return _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
}
static gcry_err_code_t
poly1305mac_write (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
{
struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
if (!mac_ctx->marks.key_set || !mac_ctx->marks.nonce_set ||
mac_ctx->marks.tag)
return GPG_ERR_INV_STATE;
_gcry_poly1305_update (&mac_ctx->ctx, buf, buflen);
return 0;
}
static gcry_err_code_t
poly1305mac_read (gcry_mac_hd_t h, unsigned char *outbuf, size_t *outlen)
{
struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
if (!mac_ctx->marks.key_set || !mac_ctx->marks.nonce_set)
return GPG_ERR_INV_STATE;
if (!mac_ctx->marks.tag)
{
_gcry_poly1305_finish(&mac_ctx->ctx, mac_ctx->tag);
memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
mac_ctx->marks.tag = 1;
}
if (*outlen == 0)
return 0;
if (*outlen <= POLY1305_TAGLEN)
buf_cpy (outbuf, mac_ctx->tag, *outlen);
else
{
buf_cpy (outbuf, mac_ctx->tag, POLY1305_TAGLEN);
*outlen = POLY1305_TAGLEN;
}
return 0;
}
static gcry_err_code_t
poly1305mac_verify (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
{
struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
gcry_err_code_t err;
size_t outlen = 0;
/* Check and finalize tag. */
err = poly1305mac_read(h, NULL, &outlen);
if (err)
return err;
if (buflen > POLY1305_TAGLEN)
return GPG_ERR_INV_LENGTH;
return buf_eq_const (buf, mac_ctx->tag, buflen) ? 0 : GPG_ERR_CHECKSUM;
}
static unsigned int
poly1305mac_get_maclen (int algo)
{
(void)algo;
return POLY1305_TAGLEN;
}
static unsigned int
poly1305mac_get_keylen (int algo)
{
(void)algo;
return POLY1305_KEYLEN;
}
static gcry_mac_spec_ops_t poly1305mac_ops = {
poly1305mac_open,
poly1305mac_close,
poly1305mac_setkey,
poly1305mac_setiv,
poly1305mac_reset,
poly1305mac_write,
poly1305mac_read,
poly1305mac_verify,
poly1305mac_get_maclen,
poly1305mac_get_keylen,
NULL,
NULL,
};
gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac = {
GCRY_MAC_POLY1305, {0, 0}, "POLY1305",
&poly1305mac_ops
};
#if USE_AES
gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_aes = {
GCRY_MAC_POLY1305_AES, {0, 0}, "POLY1305_AES",
&poly1305mac_ops
};
#endif
#if USE_CAMELLIA
gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_camellia = {
GCRY_MAC_POLY1305_CAMELLIA, {0, 0}, "POLY1305_CAMELLIA",
&poly1305mac_ops
};
#endif
#if USE_TWOFISH
gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_twofish = {
GCRY_MAC_POLY1305_TWOFISH, {0, 0}, "POLY1305_TWOFISH",
&poly1305mac_ops
};
#endif
#if USE_SERPENT
gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_serpent = {
GCRY_MAC_POLY1305_SERPENT, {0, 0}, "POLY1305_SERPENT",
&poly1305mac_ops
};
#endif
#if USE_SEED
gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_seed = {
GCRY_MAC_POLY1305_SEED, {0, 0}, "POLY1305_SEED",
&poly1305mac_ops
};
#endif