Skip to content

Instantly share code, notes, and snippets.

@h-yamamo
Created February 22, 2018 14:16
Show Gist options
  • Save h-yamamo/29992a90bb7d92d8f6d524a06d11063f to your computer and use it in GitHub Desktop.
Save h-yamamo/29992a90bb7d92d8f6d524a06d11063f to your computer and use it in GitHub Desktop.
X25519 API and EVP_AEAD API implementation using Nettle library
/* Copyright (c) 2014, 2015, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
// "evp_aead_nettle.h" 2018-02-20 EVP_AEAD API and X25519 API wrapping nettle
// Origin: BoringSSL 3282 branch
// aead.h curve25519.h in include/openssl/
#ifndef EVP_AEAD_NETTLE_H
#define EVP_AEAD_NETTLE_H
#include <stdlib.h>
#include <string.h>
#include <nettle/gcm.h>
#include <nettle/chacha-poly1305.h>
#include <nettle/curve25519.h>
#if __GLIBC_PREREQ(2, 25)
# include <sys/random.h>
#else
# include <fcntl.h>
# include <unistd.h>
#endif
#if defined(__cplusplus)
extern "C" {
#endif
int evp_aead_nettle_func, evp_aead_nettle_error;
#define EVPerr(f, r) { evp_aead_nettle_func = f; evp_aead_nettle_error = r; }
// Function codes.
#define EVP_F_EVP_AEAD_CTX_INIT 10
#define EVP_F_EVP_AEAD_CTX_OPEN 11
#define EVP_F_EVP_AEAD_CTX_SEAL 12
#define EVP_F_AEAD_AES_GCM_INIT 13
#define EVP_F_AEAD_AES_GCM_OPEN 14
#define EVP_F_AEAD_AES_GCM_SEAL 15
#define EVP_F_AEAD_CHACHA20_POLY1305_INIT 16
#define EVP_F_AEAD_CHACHA20_POLY1305_OPEN 17
#define EVP_F_AEAD_CHACHA20_POLY1305_SEAL 18
#define ERR_LIB_EC 32
// Reason codes.
#define EVP_R_BAD_DECRYPT 40
#define EVP_R_BAD_KEY_LENGTH 41
#define EVP_R_BUFFER_TOO_SMALL 42
#define EVP_R_IV_TOO_LARGE 43
#define EVP_R_NO_OPERATION_SET 44
#define EVP_R_OUTPUT_ALIASES_INPUT 45
#define EVP_R_TAG_TOO_LARGE 46
#define EVP_R_TOO_LARGE 47
#define EVP_R_UNSUPPORTED_KEY_SIZE 48
#define ERR_R_FATAL 64
#define ERR_R_MALLOC_FAILURE (1|ERR_R_FATAL)
#define ERR_R_RAND_LIB (2|ERR_R_FATAL)
#define OPENSSL_EXPORT
#define ENGINE void
typedef struct evp_aead_st EVP_AEAD;
// Authenticated Encryption with Additional Data.
//
// AEAD couples confidentiality and integrity in a single primitive. AEAD
// algorithms take a key and then can seal and open individual messages. Each
// message has a unique, per-message nonce and, optionally, additional data
// which is authenticated but not included in the ciphertext.
//
// The |EVP_AEAD_CTX_init| function initialises an |EVP_AEAD_CTX| structure and
// performs any precomputation needed to use |aead| with |key|. The length of
// the key, |key_len|, is given in bytes.
//
// The |tag_len| argument contains the length of the tags, in bytes, and allows
// for the processing of truncated authenticators. A zero value indicates that
// the default tag length should be used and this is defined as
// |EVP_AEAD_DEFAULT_TAG_LENGTH| in order to make the code clear. Using
// truncated tags increases an attacker's chance of creating a valid forgery.
// Be aware that the attacker's chance may increase more than exponentially as
// would naively be expected.
//
// When no longer needed, the initialised |EVP_AEAD_CTX| structure must be
// passed to |EVP_AEAD_CTX_cleanup|, which will deallocate any memory used.
//
// With an |EVP_AEAD_CTX| in hand, one can seal and open messages. These
// operations are intended to meet the standard notions of privacy and
// authenticity for authenticated encryption. For formal definitions see
// Bellare and Namprempre, "Authenticated encryption: relations among notions
// and analysis of the generic composition paradigm," Lecture Notes in Computer
// Science B<1976> (2000), 531–545,
// http://www-cse.ucsd.edu/~mihir/papers/oem.html.
//
// When sealing messages, a nonce must be given. The length of the nonce is
// fixed by the AEAD in use and is returned by |EVP_AEAD_nonce_length|. *The
// nonce must be unique for all messages with the same key*. This is critically
// important - nonce reuse may completely undermine the security of the AEAD.
// Nonces may be predictable and public, so long as they are unique. Uniqueness
// may be achieved with a simple counter or, if large enough, may be generated
// randomly. The nonce must be passed into the "open" operation by the receiver
// so must either be implicit (e.g. a counter), or must be transmitted along
// with the sealed message.
//
// The "seal" and "open" operations are atomic - an entire message must be
// encrypted or decrypted in a single call. Large messages may have to be split
// up in order to accommodate this. When doing so, be mindful of the need not to
// repeat nonces and the possibility that an attacker could duplicate, reorder
// or drop message chunks. For example, using a single key for a given (large)
// message and sealing chunks with nonces counting from zero would be secure as
// long as the number of chunks was securely transmitted. (Otherwise an
// attacker could truncate the message by dropping chunks from the end.)
//
// The number of chunks could be transmitted by prefixing it to the plaintext,
// for example. This also assumes that no other message would ever use the same
// key otherwise the rule that nonces must be unique for a given key would be
// violated.
//
// The "seal" and "open" operations also permit additional data to be
// authenticated via the |ad| parameter. This data is not included in the
// ciphertext and must be identical for both the "seal" and "open" call. This
// permits implicit context to be authenticated but may be empty if not needed.
//
// The "seal" and "open" operations may work in-place if the |out| and |in|
// arguments are equal. Otherwise, if |out| and |in| alias, input data may be
// overwritten before it is read. This situation will cause an error.
//
// The "seal" and "open" operations return one on success and zero on error.
// AEAD algorithms.
// EVP_aead_aes_128_gcm is AES-128 in Galois Counter Mode.
OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_128_gcm(void);
// EVP_aead_aes_256_gcm is AES-256 in Galois Counter Mode.
OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_gcm(void);
// EVP_aead_chacha20_poly1305 is the AEAD built from ChaCha20 and
// Poly1305 as described in RFC 7539.
OPENSSL_EXPORT const EVP_AEAD *EVP_aead_chacha20_poly1305(void);
// Utility functions.
// EVP_AEAD_key_length returns the length, in bytes, of the keys used by
// |aead|.
OPENSSL_EXPORT size_t EVP_AEAD_key_length(const EVP_AEAD *aead);
// EVP_AEAD_nonce_length returns the length, in bytes, of the per-message nonce
// for |aead|.
OPENSSL_EXPORT size_t EVP_AEAD_nonce_length(const EVP_AEAD *aead);
// EVP_AEAD_max_overhead returns the maximum number of additional bytes added
// by the act of sealing data with |aead|.
OPENSSL_EXPORT size_t EVP_AEAD_max_overhead(const EVP_AEAD *aead);
// EVP_AEAD_max_tag_len returns the maximum tag length when using |aead|. This
// is the largest value that can be passed as |tag_len| to
// |EVP_AEAD_CTX_init|.
OPENSSL_EXPORT size_t EVP_AEAD_max_tag_len(const EVP_AEAD *aead);
// AEAD operations.
// An EVP_AEAD_CTX represents an AEAD algorithm configured with a specific key
// and message-independent IV.
typedef struct evp_aead_ctx_st {
const EVP_AEAD *aead;
// aead_state is an opaque pointer to whatever state the AEAD needs to
// maintain.
void *aead_state;
// tag_len may contain the actual length of the authentication tag if it is
// known at initialization time.
uint8_t tag_len;
} EVP_AEAD_CTX;
// EVP_AEAD_MAX_KEY_LENGTH contains the maximum key length used by
// any AEAD defined in this header.
#define EVP_AEAD_MAX_KEY_LENGTH 80
// EVP_AEAD_MAX_NONCE_LENGTH contains the maximum nonce length used by
// any AEAD defined in this header.
#define EVP_AEAD_MAX_NONCE_LENGTH 16
// EVP_AEAD_MAX_OVERHEAD contains the maximum overhead used by any AEAD
// defined in this header.
#define EVP_AEAD_MAX_OVERHEAD 64
// EVP_AEAD_DEFAULT_TAG_LENGTH is a magic value that can be passed to
// EVP_AEAD_CTX_init to indicate that the default tag length for an AEAD should
// be used.
#define EVP_AEAD_DEFAULT_TAG_LENGTH 0
// EVP_AEAD_CTX_init initializes |ctx| for the given AEAD algorithm. The |impl|
// argument is ignored and should be NULL. Authentication tags may be truncated
// by passing a size as |tag_len|. A |tag_len| of zero indicates the default
// tag length and this is defined as EVP_AEAD_DEFAULT_TAG_LENGTH for
// readability.
//
// Returns 1 on success. Otherwise returns 0 and pushes to the error stack. In
// the error case, you do not need to call |EVP_AEAD_CTX_cleanup|, but it's
// harmless to do so.
OPENSSL_EXPORT int EVP_AEAD_CTX_init(EVP_AEAD_CTX *ctx, const EVP_AEAD *aead,
const uint8_t *key, size_t key_len,
size_t tag_len, ENGINE *impl);
// EVP_AEAD_CTX_cleanup frees any data allocated by |ctx|. It is a no-op to
// call |EVP_AEAD_CTX_cleanup| on a |EVP_AEAD_CTX| that has been |memset| to
// all zeros.
OPENSSL_EXPORT void EVP_AEAD_CTX_cleanup(EVP_AEAD_CTX *ctx);
// EVP_AEAD_CTX_seal encrypts and authenticates |in_len| bytes from |in| and
// authenticates |ad_len| bytes from |ad| and writes the result to |out|. It
// returns one on success and zero otherwise.
//
// This function may be called concurrently with itself or any other seal/open
// function on the same |EVP_AEAD_CTX|.
//
// At most |max_out_len| bytes are written to |out| and, in order to ensure
// success, |max_out_len| should be |in_len| plus the result of
// |EVP_AEAD_max_overhead|. On successful return, |*out_len| is set to the
// actual number of bytes written.
//
// The length of |nonce|, |nonce_len|, must be equal to the result of
// |EVP_AEAD_nonce_length| for this AEAD.
//
// |EVP_AEAD_CTX_seal| never results in a partial output. If |max_out_len| is
// insufficient, zero will be returned. If any error occurs, |out| will be
// filled with zero bytes and |*out_len| set to zero.
//
// If |in| and |out| alias then |out| must be == |in|.
OPENSSL_EXPORT int EVP_AEAD_CTX_seal(const EVP_AEAD_CTX *ctx, uint8_t *out,
size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len);
// EVP_AEAD_CTX_open authenticates |in_len| bytes from |in| and |ad_len| bytes
// from |ad| and decrypts at most |in_len| bytes into |out|. It returns one on
// success and zero otherwise.
//
// This function may be called concurrently with itself or any other seal/open
// function on the same |EVP_AEAD_CTX|.
//
// At most |in_len| bytes are written to |out|. In order to ensure success,
// |max_out_len| should be at least |in_len|. On successful return, |*out_len|
// is set to the the actual number of bytes written.
//
// The length of |nonce|, |nonce_len|, must be equal to the result of
// |EVP_AEAD_nonce_length| for this AEAD.
//
// |EVP_AEAD_CTX_open| never results in a partial output. If |max_out_len| is
// insufficient, zero will be returned. If any error occurs, |out| will be
// filled with zero bytes and |*out_len| set to zero.
//
// If |in| and |out| alias then |out| must be == |in|.
OPENSSL_EXPORT int EVP_AEAD_CTX_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len);
///// Implementation /////
/* EVP_AEAD represents a specific AEAD algorithm. */
struct evp_aead_st {
uint8_t key_len;
uint8_t nonce_len;
uint8_t overhead;
uint8_t max_tag_len;
int (*init) (struct evp_aead_ctx_st *ctx,
const uint8_t *key, size_t key_len, size_t tag_len);
void (*cleanup) (struct evp_aead_ctx_st *ctx);
int (*seal) (const struct evp_aead_ctx_st *ctx,
uint8_t *out, size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len);
int (*open) (const struct evp_aead_ctx_st *ctx,
uint8_t *out, size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len);
};
// Utility functions.
size_t EVP_AEAD_key_length(const EVP_AEAD *aead)
{
return aead->key_len;
}
size_t EVP_AEAD_nonce_length(const EVP_AEAD *aead)
{
return aead->nonce_len;
}
size_t EVP_AEAD_max_overhead(const EVP_AEAD *aead)
{
return aead->overhead;
}
size_t EVP_AEAD_max_tag_len(const EVP_AEAD *aead)
{
return aead->max_tag_len;
}
int EVP_AEAD_CTX_init(EVP_AEAD_CTX *ctx, const EVP_AEAD *aead,
const uint8_t *key, size_t key_len,
size_t tag_len, ENGINE *impl)
{
if (aead == NULL || aead->init == NULL) {
EVPerr(EVP_F_EVP_AEAD_CTX_INIT, EVP_R_NO_OPERATION_SET);
return 0;
}
if (key_len != aead->key_len) {
EVPerr(EVP_F_EVP_AEAD_CTX_INIT, EVP_R_UNSUPPORTED_KEY_SIZE);
return 0;
}
if (tag_len > aead->max_tag_len) {
EVPerr(EVP_F_EVP_AEAD_CTX_INIT, EVP_R_TAG_TOO_LARGE);
return 0;
}
ctx->aead = aead;
return aead->init(ctx, key, key_len, tag_len);
}
void EVP_AEAD_CTX_cleanup(EVP_AEAD_CTX *ctx)
{
if (ctx->aead == NULL)
return;
ctx->aead->cleanup(ctx);
memset(ctx, 0, sizeof(*ctx));
}
/* check_alias returns 0 if |out| points within the buffer determined by |in|
* and |in_len| and 1 otherwise.
*
* When processing, there's only an issue if |out| points within in[:in_len]
* and isn't equal to |in|. If that's the case then writing the output will
* stomp input that hasn't been read yet.
*
* This function checks for that case. */
static int check_alias(const uint8_t *in, size_t in_len, const uint8_t *out)
{
if (out <= in)
return 1;
if (in + in_len <= out)
return 1;
return 0;
}
int EVP_AEAD_CTX_seal(const EVP_AEAD_CTX *ctx,
uint8_t *out, size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len)
{
size_t possible_out_len = in_len + ctx->aead->overhead;
if (possible_out_len < in_len /* overflow */) {
EVPerr(EVP_F_EVP_AEAD_CTX_SEAL, EVP_R_TOO_LARGE);
goto error;
}
if (!check_alias(in, in_len, out)) {
EVPerr(EVP_F_EVP_AEAD_CTX_SEAL, EVP_R_OUTPUT_ALIASES_INPUT);
goto error;
}
if (ctx->aead->seal(ctx, out, out_len, max_out_len,
nonce, nonce_len, in, in_len, ad, ad_len))
return 1;
error:
/* In the event of an error, clear the output buffer so that a caller
* that doesn't check the return value doesn't send raw data. */
memset(out, 0, max_out_len);
*out_len = 0;
return 0;
}
int EVP_AEAD_CTX_open(const EVP_AEAD_CTX *ctx,
uint8_t *out, size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len)
{
if (!check_alias(in, in_len, out)) {
EVPerr(EVP_F_EVP_AEAD_CTX_OPEN, EVP_R_OUTPUT_ALIASES_INPUT);
goto error;
}
if (ctx->aead->open(ctx, out, out_len, max_out_len,
nonce, nonce_len, in, in_len, ad, ad_len))
return 1;
error:
/* In the event of an error, clear the output buffer so that a caller
* that doesn't check the return value doesn't try and process bad
* data. */
memset(out, 0, max_out_len);
*out_len = 0;
return 0;
}
//// AES-GCM
static int
aead_aes_gcm_init(EVP_AEAD_CTX *ctx, const uint8_t *key, size_t key_len,
size_t tag_len)
{
if (key_len != AES128_KEY_SIZE && key_len != AES256_KEY_SIZE) {
EVPerr(EVP_F_AEAD_AES_GCM_INIT, EVP_R_BAD_KEY_LENGTH);
return 0; /* EVP_AEAD_CTX_init should catch this. */
}
if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH)
tag_len = GCM_DIGEST_SIZE;
if (tag_len > GCM_DIGEST_SIZE)
return 0; /* EVP_AEAD_CTX_init should catch this. */
if (key_len == AES128_KEY_SIZE) {
struct gcm_aes128_ctx *gcm_ctx = malloc(sizeof(struct gcm_aes128_ctx));
if (gcm_ctx == NULL) {
EVPerr(EVP_F_AEAD_AES_GCM_INIT, ERR_R_MALLOC_FAILURE);
return 0;
}
gcm_aes128_set_key(gcm_ctx, key);
ctx->aead_state = gcm_ctx;
}
else if (key_len == AES256_KEY_SIZE) {
struct gcm_aes256_ctx *gcm_ctx = malloc(sizeof(struct gcm_aes256_ctx));
if (gcm_ctx == NULL) {
EVPerr(EVP_F_AEAD_AES_GCM_INIT, ERR_R_MALLOC_FAILURE);
return 0;
}
gcm_aes256_set_key(gcm_ctx, key);
ctx->aead_state = gcm_ctx;
}
ctx->tag_len = tag_len;
return 1;
}
static void
aead_aes_gcm_cleanup(EVP_AEAD_CTX *ctx)
{
if (ctx == NULL)
return;
if (ctx->aead->key_len == AES128_KEY_SIZE) {
struct gcm_aes128_ctx *gcm_ctx = ctx->aead_state;
if (gcm_ctx) {
memset(gcm_ctx, 0, sizeof(struct gcm_aes128_ctx));
free(gcm_ctx);
}
}
else if (ctx->aead->key_len == AES256_KEY_SIZE) {
struct gcm_aes256_ctx *gcm_ctx = ctx->aead_state;
if (gcm_ctx) {
memset(gcm_ctx, 0, sizeof(struct gcm_aes256_ctx));
free(gcm_ctx);
}
}
}
static int
aead_aes_gcm_seal(const EVP_AEAD_CTX *ctx,
uint8_t *out, size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len)
{
if (max_out_len < in_len + ctx->tag_len) {
EVPerr(EVP_F_AEAD_AES_GCM_SEAL, EVP_R_BUFFER_TOO_SMALL);
return 0;
}
if (ctx->aead->key_len == AES128_KEY_SIZE) {
struct gcm_aes128_ctx *gcm_ctx = ctx->aead_state;
gcm_aes128_set_iv(gcm_ctx, nonce_len, nonce);
gcm_aes128_update(gcm_ctx, ad_len, ad);
gcm_aes128_encrypt(gcm_ctx, in_len, out, in);
gcm_aes128_digest(gcm_ctx, ctx->tag_len, out + in_len);
}
else if (ctx->aead->key_len == AES256_KEY_SIZE) {
struct gcm_aes256_ctx *gcm_ctx = ctx->aead_state;
gcm_aes256_set_iv(gcm_ctx, nonce_len, nonce);
gcm_aes256_update(gcm_ctx, ad_len, ad);
gcm_aes256_encrypt(gcm_ctx, in_len, out, in);
gcm_aes256_digest(gcm_ctx, ctx->tag_len, out + in_len);
}
else
return 0; // not reached
*out_len = in_len + ctx->tag_len;
return 1;
}
static int
aead_aes_gcm_open(const EVP_AEAD_CTX *ctx,
uint8_t *out, size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len)
{
uint8_t tag[GCM_DIGEST_SIZE];
size_t plaintext_len;
if (in_len < ctx->tag_len) {
EVPerr(EVP_F_AEAD_AES_GCM_OPEN, EVP_R_BAD_DECRYPT);
return 0;
}
plaintext_len = in_len - ctx->tag_len;
if (max_out_len < plaintext_len) {
EVPerr(EVP_F_AEAD_AES_GCM_OPEN, EVP_R_BUFFER_TOO_SMALL);
return 0;
}
if (ctx->aead->key_len == AES128_KEY_SIZE) {
struct gcm_aes128_ctx *gcm_ctx = ctx->aead_state;
gcm_aes128_set_iv(gcm_ctx, nonce_len, nonce);
gcm_aes128_update(gcm_ctx, ad_len, ad);
gcm_aes128_decrypt(gcm_ctx, plaintext_len, out, in);
gcm_aes128_digest(gcm_ctx, ctx->tag_len, tag);
}
else if (ctx->aead->key_len == AES256_KEY_SIZE) {
struct gcm_aes256_ctx *gcm_ctx = ctx->aead_state;
gcm_aes256_set_iv(gcm_ctx, nonce_len, nonce);
gcm_aes256_update(gcm_ctx, ad_len, ad);
gcm_aes256_decrypt(gcm_ctx, plaintext_len, out, in);
gcm_aes256_digest(gcm_ctx, ctx->tag_len, tag);
} else
return 0; // not reached
if (memcmp(tag, in + plaintext_len, ctx->tag_len) != 0) {
EVPerr(EVP_F_AEAD_AES_GCM_OPEN, EVP_R_BAD_DECRYPT);
memset(out, 0, plaintext_len);
return 0;
}
*out_len = plaintext_len;
return 1;
}
static const EVP_AEAD aead_aes_128_gcm = {
/* 16 */ AES128_KEY_SIZE, /* key len */
/* 12 */ GCM_IV_SIZE, /* nonce len */
/* 16 */ GCM_DIGEST_SIZE, /* overhead */
/* 16 */ GCM_DIGEST_SIZE, /* max tag length */
aead_aes_gcm_init,
aead_aes_gcm_cleanup,
aead_aes_gcm_seal,
aead_aes_gcm_open,
};
static const EVP_AEAD aead_aes_256_gcm = {
/* 32 */ AES256_KEY_SIZE, /* key len */
/* 12 */ GCM_IV_SIZE, /* nonce len */
/* 16 */ GCM_DIGEST_SIZE, /* overhead */
/* 16 */ GCM_DIGEST_SIZE, /* max tag length */
aead_aes_gcm_init,
aead_aes_gcm_cleanup,
aead_aes_gcm_seal,
aead_aes_gcm_open,
};
const EVP_AEAD *EVP_aead_aes_128_gcm()
{
return &aead_aes_128_gcm;
}
const EVP_AEAD *EVP_aead_aes_256_gcm()
{
return &aead_aes_256_gcm;
}
//// ChaCha20-Poly1305
static int
aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx,
const uint8_t *key, size_t key_len, size_t tag_len)
{
struct chacha_poly1305_ctx *c20_ctx;
if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH)
tag_len = CHACHA_POLY1305_DIGEST_SIZE;
if (tag_len > CHACHA_POLY1305_DIGEST_SIZE)
return 0; /* internal error - EVP_AEAD_CTX_init should catch this. */
if (key_len != CHACHA_POLY1305_KEY_SIZE)
return 0; /* internal error - EVP_AEAD_CTX_init should catch this. */
c20_ctx = malloc(sizeof(*c20_ctx));
if (c20_ctx == NULL) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_INIT, ERR_R_MALLOC_FAILURE);
return 0;
}
chacha_poly1305_set_key(c20_ctx, key);
ctx->aead_state = c20_ctx;
ctx->tag_len = tag_len;
return 1;
}
static void
aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx)
{
struct chacha_poly1305_ctx *c20_ctx;
if (ctx == NULL)
return;
c20_ctx = ctx->aead_state;
if (c20_ctx) {
memset(c20_ctx, 0, sizeof(*c20_ctx));
free(c20_ctx);
}
}
static int
aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx,
uint8_t *out, size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len)
{
struct chacha_poly1305_ctx *c20_ctx = ctx->aead_state;
const uint64_t in_len_64 = in_len;
/* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
* individual operations that work on more than 256GB at a time.
* |in_len_64| is needed because, on 32-bit platforms, size_t is only
* 32-bits and this produces a warning because it's always false.
* Casting to uint64_t inside the conditional is not sufficient to stop
* the warning. */
if (in_len_64 >= (1ull << 32) * 64 - 64) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_TOO_LARGE);
return 0;
}
if (in_len + CHACHA_POLY1305_DIGEST_SIZE < in_len) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_TOO_LARGE);
return 0;
}
if (max_out_len < in_len + CHACHA_POLY1305_DIGEST_SIZE) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_BUFFER_TOO_SMALL);
return 0;
}
if (nonce_len != CHACHA_POLY1305_NONCE_SIZE) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_IV_TOO_LARGE);
return 0;
}
chacha_poly1305_set_nonce(c20_ctx, nonce);
chacha_poly1305_update(c20_ctx, ad_len, ad);
chacha_poly1305_encrypt(c20_ctx, in_len, out, in);
chacha_poly1305_digest(c20_ctx, ctx->tag_len, out + in_len);
*out_len = in_len + ctx->tag_len;
return 1;
}
static int
aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx,
uint8_t *out, size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *in, size_t in_len,
const uint8_t *ad, size_t ad_len)
{
struct chacha_poly1305_ctx *c20_ctx = ctx->aead_state;
uint8_t tag[CHACHA_POLY1305_DIGEST_SIZE];
const uint64_t in_len_64 = in_len;
size_t plaintext_len;
if (in_len < CHACHA_POLY1305_DIGEST_SIZE) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT);
return 0;
}
/* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
* individual operations that work on more than 256GB at a time.
* |in_len_64| is needed because, on 32-bit platforms, size_t is only
* 32-bits and this produces a warning because it's always false.
* Casting to uint64_t inside the conditional is not sufficient to stop
* the warning. */
if (in_len_64 >= (1ull << 32) * 64 - 64) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_TOO_LARGE);
return 0;
}
if (nonce_len != CHACHA_POLY1305_NONCE_SIZE) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_IV_TOO_LARGE);
return 0;
}
plaintext_len = in_len - ctx->tag_len;
if (max_out_len < plaintext_len) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BUFFER_TOO_SMALL);
return 0;
}
chacha_poly1305_set_nonce(c20_ctx, nonce);
chacha_poly1305_update(c20_ctx, ad_len, ad);
chacha_poly1305_decrypt(c20_ctx, plaintext_len, out, in);
chacha_poly1305_digest(c20_ctx, ctx->tag_len, tag);
if (memcmp(tag, in + plaintext_len, ctx->tag_len) != 0) {
EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT);
memset(out, 0, plaintext_len);
return 0;
}
*out_len = plaintext_len;
return 1;
}
static const EVP_AEAD aead_chacha20_poly1305 = {
/* 32 */ CHACHA_POLY1305_KEY_SIZE, /* key len */
/* 12 */ CHACHA_POLY1305_NONCE_SIZE, /* nonce len */
/* 16 */ CHACHA_POLY1305_DIGEST_SIZE, /* overhead */
/* 16 */ CHACHA_POLY1305_DIGEST_SIZE, /* max tag length */
aead_chacha20_poly1305_init,
aead_chacha20_poly1305_cleanup,
aead_chacha20_poly1305_seal,
aead_chacha20_poly1305_open,
};
const EVP_AEAD *EVP_aead_chacha20_poly1305()
{
return &aead_chacha20_poly1305;
}
///// Key exchange /////
# if ! __GLIBC_PREREQ(2, 25)
static int getrandom(void *buf, size_t buflen, unsigned int flags)
{
int f, n;
if (flags)
return -1;
if (buflen > 64)
return -1;
f = open("/dev/urandom", O_RDONLY);
if (f < 0)
return -1;
n = read(f, buf, buflen);
close(f);
return n;
}
# endif
// X25519.
//
// X25519 is the Diffie-Hellman primitive built from curve25519. It is
// sometimes referred to as “curve25519”, but “X25519” is a more precise name.
// See http://cr.yp.to/ecdh.html and https://tools.ietf.org/html/rfc7748.
#define X25519_PRIVATE_KEY_LEN 32
#define X25519_PUBLIC_VALUE_LEN 32
#define X25519_SHARED_KEY_LEN 32
// X25519_keypair sets |out_public_value| and |out_private_key| to a freshly
// generated, public–private key pair.
OPENSSL_EXPORT void X25519_keypair(uint8_t out_public_value[32],
uint8_t out_private_key[32]);
// X25519 writes a shared key to |out_shared_key| that is calculated from the
// given private key and the peer's public value. It returns one on success and
// zero on error.
//
// Don't use the shared key directly, rather use a KDF and also include the two
// public values as inputs.
OPENSSL_EXPORT int X25519(uint8_t out_shared_key[32],
const uint8_t private_key[32],
const uint8_t peer_public_value[32]);
// X25519_public_from_private calculates a Diffie-Hellman public value from the
// given private key and writes it to |out_public_value|.
OPENSSL_EXPORT void X25519_public_from_private(uint8_t out_public_value[32],
const uint8_t private_key[32]);
///// Implementation /////
void X25519_keypair(uint8_t out_public_value[32], uint8_t out_private_key[32]) {
int r = getrandom(out_private_key, X25519_PRIVATE_KEY_LEN, 0);
if (r < X25519_PRIVATE_KEY_LEN) {
EVPerr(ERR_LIB_EC, ERR_R_RAND_LIB);
memset(out_private_key, 0, 32);
return;
}
/* All X25519 implementations should decode scalars correctly (see
* https://tools.ietf.org/html/rfc7748#section-5). However, if an
* implementation doesn't then it might interoperate with random keys a
* fraction of the time because they'll, randomly, happen to be correctly
* formed.
*
* Thus we do the opposite of the masking here to make sure that our private
* keys are never correctly masked and so, hopefully, any incorrect
* implementations are deterministically broken.
*
* This does not affect security because, although we're throwing away
* entropy, a valid implementation of scalarmult should throw away the exact
* same bits anyway. */
out_private_key[0] |= 7;
out_private_key[31] &= 63;
out_private_key[31] |= 128;
X25519_public_from_private(out_public_value, out_private_key);
}
int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
const uint8_t peer_public_value[32]) {
static const uint8_t kZeros[32] = {0};
curve25519_mul(out_shared_key, private_key, peer_public_value);
/* The all-zero output results when the input is a point of small order. */
return memcmp(kZeros, out_shared_key, 32) != 0;
}
void X25519_public_from_private(uint8_t out_public_value[32],
const uint8_t private_key[32]) {
curve25519_mul_g(out_public_value, private_key);
}
#if defined(__cplusplus)
} // extern C
#endif
#endif // EVP_AEAD_NETTLE_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment