/*
* Copyright 2006-2018 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <stdio.h>
#include "internal/cryptlib.h"
#include <openssl/x509.h>
#include <openssl/ec.h>
#include <openssl/rand.h>
#include "internal/asn1_int.h"
#include "internal/evp_int.h"
#include "ec_lcl.h"
#include "curve448/curve448_lcl.h"
#define X25519_BITS 253
#define X25519_SECURITY_BITS 128
#define ED25519_SIGSIZE 64
#define X448_BITS 448
#define ED448_BITS 456
#define X448_SECURITY_BITS 224
#define ED448_SIGSIZE 114
#define ISX448(id) ((id) == EVP_PKEY_X448)
#define IS25519(id) ((id) == EVP_PKEY_X25519 || (id) == EVP_PKEY_ED25519)
#define KEYLENID(id) (IS25519(id) ? X25519_KEYLEN \
: ((id) == EVP_PKEY_X448 ? X448_KEYLEN \
: ED448_KEYLEN))
#define KEYLEN(p) KEYLENID((p)->ameth->pkey_id)
typedef enum {
KEY_OP_PUBLIC,
KEY_OP_PRIVATE,
KEY_OP_KEYGEN
} ecx_key_op_t;
/* Setup EVP_PKEY using public, private or generation */
static int ecx_key_op(EVP_PKEY *pkey, int id, const X509_ALGOR *palg,
const unsigned char *p, int plen, ecx_key_op_t op)
{
ECX_KEY *key = NULL;
unsigned char *privkey, *pubkey;
if (op != KEY_OP_KEYGEN) {
if (palg != NULL) {
int ptype;
/* Algorithm parameters must be absent */
X509_ALGOR_get0(NULL, &ptype, NULL, palg);
if (ptype != V_ASN1_UNDEF) {
ECerr(EC_F_ECX_KEY_OP, EC_R_INVALID_ENCODING);
return 0;
}
}
if (p == NULL || plen != KEYLENID(id)) {
ECerr(EC_F_ECX_KEY_OP, EC_R_INVALID_ENCODING);
return 0;
}
}
key = OPENSSL_zalloc(sizeof(*key));
if (key == NULL) {
ECerr(EC_F_ECX_KEY_OP, ERR_R_MALLOC_FAILURE);
return 0;
}
pubkey = key->pubkey;
if (op == KEY_OP_PUBLIC) {
memcpy(pubkey, p, plen);
} else {
privkey = key->privkey = OPENSSL_secure_malloc(KEYLENID(id));
if (privkey == NULL) {
ECerr(EC_F_ECX_KEY_OP, ERR_R_MALLOC_FAILURE);
goto err;
}
if (op == KEY_OP_KEYGEN) {
if (RAND_priv_bytes(privkey, KEYLENID(id)) <= 0) {
OPENSSL_secure_free(privkey);
key->privkey = NULL;
goto err;
}
if (id == EVP_PKEY_X25519) {
privkey[0] &= 248;
privkey[X25519_KEYLEN - 1] &= 127;
privkey[X25519_KEYLEN - 1] |= 64;
} else if (id == EVP_PKEY_X448) {
privkey[0] &= 252;
privkey[X448_KEYLEN - 1] |= 128;
}
} else {
memcpy(privkey, p, KEYLENID(id));
}
switch (id) {
case EVP_PKEY_X25519:
X25519_public_from_private(pubkey, privkey);
break;
case EVP_PKEY_ED25519:
ED25519_public_from_private(pubkey, privkey);
break;
case EVP_PKEY_X448:
X448_public_from_private(pubkey, privkey);
break;
case EVP_PKEY_ED448:
/*
* TODO(3.0): We set the library context to NULL for now. This will
* need to change.
*/
ED448_public_from_private(NULL, pubkey, privkey);
break;
}
}
EVP_PKEY_assign(pkey, id, key);
return 1;
err:
OPENSSL_free(key);
return 0;
}
static int ecx_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
{
const ECX_KEY *ecxkey = pkey->pkey.ecx;
unsigned char *penc;
if (ecxkey == NULL) {
ECerr(EC_F_ECX_PUB_ENCODE, EC_R_INVALID_KEY);
return 0;
}
penc = OPENSSL_memdup(ecxkey->pubkey, KEYLEN(pkey));
if (penc == NULL) {
ECerr(EC_F_ECX_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
return 0;
}
if (!X509_PUBKEY_set0_param(pk, OBJ_nid2obj(pkey->ameth->pkey_id),
V_ASN1_UNDEF, NULL, penc, KEYLEN(pkey))) {
OPENSSL_free(penc);
ECerr(EC_F_ECX_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
return 0;
}
return 1;
}
static int ecx_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
{
const unsigned char *p;
int pklen;
X509_ALGOR *palg;
if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
return 0;
return ecx_key_op(pkey, pkey->ameth->pkey_id, palg, p, pklen,
KEY_OP_PUBLIC);