0d124b0a51
Only applies to algorithms that support it. Both raw private and public keys can be obtained for X25519, Ed25519, X448, Ed448. Raw private keys only can be obtained for HMAC, Poly1305 and SipHash Fixes #6259 Reviewed-by: Rich Salz <rsalz@openssl.org> Reviewed-by: Tim Hudson <tjh@openssl.org> (Merged from https://github.com/openssl/openssl/pull/6394)
830 lines
20 KiB
C
830 lines
20 KiB
C
/*
|
|
* Copyright 2006-2018 The OpenSSL Project Authors. All Rights Reserved.
|
|
*
|
|
* Licensed under the OpenSSL license (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"
|
|
|
|
#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:
|
|
ED448_public_from_private(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);
|
|
}
|
|
|
|
static int ecx_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
|
|
{
|
|
const ECX_KEY *akey = a->pkey.ecx;
|
|
const ECX_KEY *bkey = b->pkey.ecx;
|
|
|
|
if (akey == NULL || bkey == NULL)
|
|
return -2;
|
|
|
|
return CRYPTO_memcmp(akey->pubkey, bkey->pubkey, KEYLEN(a)) == 0;
|
|
}
|
|
|
|
static int ecx_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
|
|
{
|
|
const unsigned char *p;
|
|
int plen;
|
|
ASN1_OCTET_STRING *oct = NULL;
|
|
const X509_ALGOR *palg;
|
|
int rv;
|
|
|
|
if (!PKCS8_pkey_get0(NULL, &p, &plen, &palg, p8))
|
|
return 0;
|
|
|
|
oct = d2i_ASN1_OCTET_STRING(NULL, &p, plen);
|
|
if (oct == NULL) {
|
|
p = NULL;
|
|
plen = 0;
|
|
} else {
|
|
p = ASN1_STRING_get0_data(oct);
|
|
plen = ASN1_STRING_length(oct);
|
|
}
|
|
|
|
rv = ecx_key_op(pkey, pkey->ameth->pkey_id, palg, p, plen, KEY_OP_PRIVATE);
|
|
ASN1_OCTET_STRING_free(oct);
|
|
return rv;
|
|
}
|
|
|
|
static int ecx_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
|
|
{
|
|
const ECX_KEY *ecxkey = pkey->pkey.ecx;
|
|
ASN1_OCTET_STRING oct;
|
|
unsigned char *penc = NULL;
|
|
int penclen;
|
|
|
|
if (ecxkey == NULL || ecxkey->privkey == NULL) {
|
|
ECerr(EC_F_ECX_PRIV_ENCODE, EC_R_INVALID_PRIVATE_KEY);
|
|
return 0;
|
|
}
|
|
|
|
oct.data = ecxkey->privkey;
|
|
oct.length = KEYLEN(pkey);
|
|
oct.flags = 0;
|
|
|
|
penclen = i2d_ASN1_OCTET_STRING(&oct, &penc);
|
|
if (penclen < 0) {
|
|
ECerr(EC_F_ECX_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
|
|
return 0;
|
|
}
|
|
|
|
if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(pkey->ameth->pkey_id), 0,
|
|
V_ASN1_UNDEF, NULL, penc, penclen)) {
|
|
OPENSSL_clear_free(penc, penclen);
|
|
ECerr(EC_F_ECX_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int ecx_size(const EVP_PKEY *pkey)
|
|
{
|
|
return KEYLEN(pkey);
|
|
}
|
|
|
|
static int ecx_bits(const EVP_PKEY *pkey)
|
|
{
|
|
if (IS25519(pkey->ameth->pkey_id)) {
|
|
return X25519_BITS;
|
|
} else if(ISX448(pkey->ameth->pkey_id)) {
|
|
return X448_BITS;
|
|
} else {
|
|
return ED448_BITS;
|
|
}
|
|
}
|
|
|
|
static int ecx_security_bits(const EVP_PKEY *pkey)
|
|
{
|
|
if (IS25519(pkey->ameth->pkey_id)) {
|
|
return X25519_SECURITY_BITS;
|
|
} else {
|
|
return X448_SECURITY_BITS;
|
|
}
|
|
}
|
|
|
|
static void ecx_free(EVP_PKEY *pkey)
|
|
{
|
|
if (pkey->pkey.ecx != NULL)
|
|
OPENSSL_secure_clear_free(pkey->pkey.ecx->privkey, KEYLEN(pkey));
|
|
OPENSSL_free(pkey->pkey.ecx);
|
|
}
|
|
|
|
/* "parameters" are always equal */
|
|
static int ecx_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static int ecx_key_print(BIO *bp, const EVP_PKEY *pkey, int indent,
|
|
ASN1_PCTX *ctx, ecx_key_op_t op)
|
|
{
|
|
const ECX_KEY *ecxkey = pkey->pkey.ecx;
|
|
const char *nm = OBJ_nid2ln(pkey->ameth->pkey_id);
|
|
|
|
if (op == KEY_OP_PRIVATE) {
|
|
if (ecxkey == NULL || ecxkey->privkey == NULL) {
|
|
if (BIO_printf(bp, "%*s<INVALID PRIVATE KEY>\n", indent, "") <= 0)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
if (BIO_printf(bp, "%*s%s Private-Key:\n", indent, "", nm) <= 0)
|
|
return 0;
|
|
if (BIO_printf(bp, "%*spriv:\n", indent, "") <= 0)
|
|
return 0;
|
|
if (ASN1_buf_print(bp, ecxkey->privkey, KEYLEN(pkey),
|
|
indent + 4) == 0)
|
|
return 0;
|
|
} else {
|
|
if (ecxkey == NULL) {
|
|
if (BIO_printf(bp, "%*s<INVALID PUBLIC KEY>\n", indent, "") <= 0)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
if (BIO_printf(bp, "%*s%s Public-Key:\n", indent, "", nm) <= 0)
|
|
return 0;
|
|
}
|
|
if (BIO_printf(bp, "%*spub:\n", indent, "") <= 0)
|
|
return 0;
|
|
|
|
if (ASN1_buf_print(bp, ecxkey->pubkey, KEYLEN(pkey),
|
|
indent + 4) == 0)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int ecx_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
|
|
ASN1_PCTX *ctx)
|
|
{
|
|
return ecx_key_print(bp, pkey, indent, ctx, KEY_OP_PRIVATE);
|
|
}
|
|
|
|
static int ecx_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
|
|
ASN1_PCTX *ctx)
|
|
{
|
|
return ecx_key_print(bp, pkey, indent, ctx, KEY_OP_PUBLIC);
|
|
}
|
|
|
|
static int ecx_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
|
|
{
|
|
switch (op) {
|
|
|
|
case ASN1_PKEY_CTRL_SET1_TLS_ENCPT:
|
|
return ecx_key_op(pkey, pkey->ameth->pkey_id, NULL, arg2, arg1,
|
|
KEY_OP_PUBLIC);
|
|
|
|
case ASN1_PKEY_CTRL_GET1_TLS_ENCPT:
|
|
if (pkey->pkey.ecx != NULL) {
|
|
unsigned char **ppt = arg2;
|
|
|
|
*ppt = OPENSSL_memdup(pkey->pkey.ecx->pubkey, KEYLEN(pkey));
|
|
if (*ppt != NULL)
|
|
return KEYLEN(pkey);
|
|
}
|
|
return 0;
|
|
|
|
case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
|
|
*(int *)arg2 = NID_sha256;
|
|
return 2;
|
|
|
|
default:
|
|
return -2;
|
|
|
|
}
|
|
}
|
|
|
|
static int ecx_set_priv_key(EVP_PKEY *pkey, const unsigned char *priv,
|
|
size_t len)
|
|
{
|
|
return ecx_key_op(pkey, pkey->ameth->pkey_id, NULL, priv, len,
|
|
KEY_OP_PRIVATE);
|
|
}
|
|
|
|
static int ecx_set_pub_key(EVP_PKEY *pkey, const unsigned char *pub, size_t len)
|
|
{
|
|
return ecx_key_op(pkey, pkey->ameth->pkey_id, NULL, pub, len,
|
|
KEY_OP_PUBLIC);
|
|
}
|
|
|
|
static int ecx_get_priv_key(const EVP_PKEY *pkey, unsigned char *priv,
|
|
size_t *len)
|
|
{
|
|
const ECX_KEY *key = pkey->pkey.ecx;
|
|
|
|
if (priv == NULL) {
|
|
*len = KEYLENID(pkey->ameth->pkey_id);
|
|
return 1;
|
|
}
|
|
|
|
if (key == NULL
|
|
|| key->privkey == NULL
|
|
|| *len < (size_t)KEYLENID(pkey->ameth->pkey_id))
|
|
return 0;
|
|
|
|
*len = KEYLENID(pkey->ameth->pkey_id);
|
|
memcpy(priv, key->privkey, *len);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int ecx_get_pub_key(const EVP_PKEY *pkey, unsigned char *pub,
|
|
size_t *len)
|
|
{
|
|
const ECX_KEY *key = pkey->pkey.ecx;
|
|
|
|
if (pub == NULL) {
|
|
*len = KEYLENID(pkey->ameth->pkey_id);
|
|
return 1;
|
|
}
|
|
|
|
if (key == NULL
|
|
|| *len < (size_t)KEYLENID(pkey->ameth->pkey_id))
|
|
return 0;
|
|
|
|
*len = KEYLENID(pkey->ameth->pkey_id);
|
|
memcpy(pub, key->pubkey, *len);
|
|
|
|
return 1;
|
|
}
|
|
|
|
const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth = {
|
|
EVP_PKEY_X25519,
|
|
EVP_PKEY_X25519,
|
|
0,
|
|
"X25519",
|
|
"OpenSSL X25519 algorithm",
|
|
|
|
ecx_pub_decode,
|
|
ecx_pub_encode,
|
|
ecx_pub_cmp,
|
|
ecx_pub_print,
|
|
|
|
ecx_priv_decode,
|
|
ecx_priv_encode,
|
|
ecx_priv_print,
|
|
|
|
ecx_size,
|
|
ecx_bits,
|
|
ecx_security_bits,
|
|
|
|
0, 0, 0, 0,
|
|
ecx_cmp_parameters,
|
|
0, 0,
|
|
|
|
ecx_free,
|
|
ecx_ctrl,
|
|
NULL,
|
|
NULL,
|
|
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
|
|
ecx_set_priv_key,
|
|
ecx_set_pub_key,
|
|
ecx_get_priv_key,
|
|
ecx_get_pub_key,
|
|
};
|
|
|
|
const EVP_PKEY_ASN1_METHOD ecx448_asn1_meth = {
|
|
EVP_PKEY_X448,
|
|
EVP_PKEY_X448,
|
|
0,
|
|
"X448",
|
|
"OpenSSL X448 algorithm",
|
|
|
|
ecx_pub_decode,
|
|
ecx_pub_encode,
|
|
ecx_pub_cmp,
|
|
ecx_pub_print,
|
|
|
|
ecx_priv_decode,
|
|
ecx_priv_encode,
|
|
ecx_priv_print,
|
|
|
|
ecx_size,
|
|
ecx_bits,
|
|
ecx_security_bits,
|
|
|
|
0, 0, 0, 0,
|
|
ecx_cmp_parameters,
|
|
0, 0,
|
|
|
|
ecx_free,
|
|
ecx_ctrl,
|
|
NULL,
|
|
NULL,
|
|
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
|
|
ecx_set_priv_key,
|
|
ecx_set_pub_key,
|
|
ecx_get_priv_key,
|
|
ecx_get_pub_key,
|
|
};
|
|
|
|
static int ecd_size25519(const EVP_PKEY *pkey)
|
|
{
|
|
return ED25519_SIGSIZE;
|
|
}
|
|
|
|
static int ecd_size448(const EVP_PKEY *pkey)
|
|
{
|
|
return ED448_SIGSIZE;
|
|
}
|
|
|
|
static int ecd_item_verify(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
|
|
X509_ALGOR *sigalg, ASN1_BIT_STRING *str,
|
|
EVP_PKEY *pkey)
|
|
{
|
|
const ASN1_OBJECT *obj;
|
|
int ptype;
|
|
int nid;
|
|
|
|
/* Sanity check: make sure it is ED25519/ED448 with absent parameters */
|
|
X509_ALGOR_get0(&obj, &ptype, NULL, sigalg);
|
|
nid = OBJ_obj2nid(obj);
|
|
if ((nid != NID_ED25519 && nid != NID_ED448) || ptype != V_ASN1_UNDEF) {
|
|
ECerr(EC_F_ECD_ITEM_VERIFY, EC_R_INVALID_ENCODING);
|
|
return 0;
|
|
}
|
|
|
|
if (!EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey))
|
|
return 0;
|
|
|
|
return 2;
|
|
}
|
|
|
|
static int ecd_item_sign25519(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
|
|
X509_ALGOR *alg1, X509_ALGOR *alg2,
|
|
ASN1_BIT_STRING *str)
|
|
{
|
|
/* Set algorithms identifiers */
|
|
X509_ALGOR_set0(alg1, OBJ_nid2obj(NID_ED25519), V_ASN1_UNDEF, NULL);
|
|
if (alg2)
|
|
X509_ALGOR_set0(alg2, OBJ_nid2obj(NID_ED25519), V_ASN1_UNDEF, NULL);
|
|
/* Algorithm idetifiers set: carry on as normal */
|
|
return 3;
|
|
}
|
|
|
|
static int ecd_sig_info_set25519(X509_SIG_INFO *siginf, const X509_ALGOR *alg,
|
|
const ASN1_STRING *sig)
|
|
{
|
|
X509_SIG_INFO_set(siginf, NID_undef, NID_ED25519, X25519_SECURITY_BITS,
|
|
X509_SIG_INFO_TLS);
|
|
return 1;
|
|
}
|
|
|
|
static int ecd_item_sign448(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
|
|
X509_ALGOR *alg1, X509_ALGOR *alg2,
|
|
ASN1_BIT_STRING *str)
|
|
{
|
|
/* Set algorithm identifier */
|
|
X509_ALGOR_set0(alg1, OBJ_nid2obj(NID_ED448), V_ASN1_UNDEF, NULL);
|
|
if (alg2 != NULL)
|
|
X509_ALGOR_set0(alg2, OBJ_nid2obj(NID_ED448), V_ASN1_UNDEF, NULL);
|
|
/* Algorithm identifier set: carry on as normal */
|
|
return 3;
|
|
}
|
|
|
|
static int ecd_sig_info_set448(X509_SIG_INFO *siginf, const X509_ALGOR *alg,
|
|
const ASN1_STRING *sig)
|
|
{
|
|
X509_SIG_INFO_set(siginf, NID_undef, NID_ED448, X448_SECURITY_BITS,
|
|
X509_SIG_INFO_TLS);
|
|
return 1;
|
|
}
|
|
|
|
|
|
const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = {
|
|
EVP_PKEY_ED25519,
|
|
EVP_PKEY_ED25519,
|
|
0,
|
|
"ED25519",
|
|
"OpenSSL ED25519 algorithm",
|
|
|
|
ecx_pub_decode,
|
|
ecx_pub_encode,
|
|
ecx_pub_cmp,
|
|
ecx_pub_print,
|
|
|
|
ecx_priv_decode,
|
|
ecx_priv_encode,
|
|
ecx_priv_print,
|
|
|
|
ecd_size25519,
|
|
ecx_bits,
|
|
ecx_security_bits,
|
|
|
|
0, 0, 0, 0,
|
|
ecx_cmp_parameters,
|
|
0, 0,
|
|
|
|
ecx_free,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
ecd_item_verify,
|
|
ecd_item_sign25519,
|
|
ecd_sig_info_set25519,
|
|
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
|
|
ecx_set_priv_key,
|
|
ecx_set_pub_key,
|
|
ecx_get_priv_key,
|
|
ecx_get_pub_key,
|
|
};
|
|
|
|
const EVP_PKEY_ASN1_METHOD ed448_asn1_meth = {
|
|
EVP_PKEY_ED448,
|
|
EVP_PKEY_ED448,
|
|
0,
|
|
"ED448",
|
|
"OpenSSL ED448 algorithm",
|
|
|
|
ecx_pub_decode,
|
|
ecx_pub_encode,
|
|
ecx_pub_cmp,
|
|
ecx_pub_print,
|
|
|
|
ecx_priv_decode,
|
|
ecx_priv_encode,
|
|
ecx_priv_print,
|
|
|
|
ecd_size448,
|
|
ecx_bits,
|
|
ecx_security_bits,
|
|
|
|
0, 0, 0, 0,
|
|
ecx_cmp_parameters,
|
|
0, 0,
|
|
|
|
ecx_free,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
ecd_item_verify,
|
|
ecd_item_sign448,
|
|
ecd_sig_info_set448,
|
|
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
|
|
ecx_set_priv_key,
|
|
ecx_set_pub_key,
|
|
ecx_get_priv_key,
|
|
ecx_get_pub_key,
|
|
};
|
|
|
|
static int pkey_ecx_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
|
|
{
|
|
return ecx_key_op(pkey, ctx->pmeth->pkey_id, NULL, NULL, 0, KEY_OP_KEYGEN);
|
|
}
|
|
|
|
static int validate_ecx_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
|
|
size_t *keylen,
|
|
const unsigned char **privkey,
|
|
const unsigned char **pubkey)
|
|
{
|
|
const ECX_KEY *ecxkey, *peerkey;
|
|
|
|
if (ctx->pkey == NULL || ctx->peerkey == NULL) {
|
|
ECerr(EC_F_VALIDATE_ECX_DERIVE, EC_R_KEYS_NOT_SET);
|
|
return 0;
|
|
}
|
|
ecxkey = ctx->pkey->pkey.ecx;
|
|
peerkey = ctx->peerkey->pkey.ecx;
|
|
if (ecxkey == NULL || ecxkey->privkey == NULL) {
|
|
ECerr(EC_F_VALIDATE_ECX_DERIVE, EC_R_INVALID_PRIVATE_KEY);
|
|
return 0;
|
|
}
|
|
if (peerkey == NULL) {
|
|
ECerr(EC_F_VALIDATE_ECX_DERIVE, EC_R_INVALID_PEER_KEY);
|
|
return 0;
|
|
}
|
|
*privkey = ecxkey->privkey;
|
|
*pubkey = peerkey->pubkey;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_ecx_derive25519(EVP_PKEY_CTX *ctx, unsigned char *key,
|
|
size_t *keylen)
|
|
{
|
|
const unsigned char *privkey, *pubkey;
|
|
|
|
if (!validate_ecx_derive(ctx, key, keylen, &privkey, &pubkey)
|
|
|| (key != NULL
|
|
&& X25519(key, privkey, pubkey) == 0))
|
|
return 0;
|
|
*keylen = X25519_KEYLEN;
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_ecx_derive448(EVP_PKEY_CTX *ctx, unsigned char *key,
|
|
size_t *keylen)
|
|
{
|
|
const unsigned char *privkey, *pubkey;
|
|
|
|
if (!validate_ecx_derive(ctx, key, keylen, &privkey, &pubkey)
|
|
|| (key != NULL
|
|
&& X448(key, privkey, pubkey) == 0))
|
|
return 0;
|
|
*keylen = X448_KEYLEN;
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_ecx_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
|
|
{
|
|
/* Only need to handle peer key for derivation */
|
|
if (type == EVP_PKEY_CTRL_PEER_KEY)
|
|
return 1;
|
|
return -2;
|
|
}
|
|
|
|
const EVP_PKEY_METHOD ecx25519_pkey_meth = {
|
|
EVP_PKEY_X25519,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
pkey_ecx_keygen,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
pkey_ecx_derive25519,
|
|
pkey_ecx_ctrl,
|
|
0
|
|
};
|
|
|
|
const EVP_PKEY_METHOD ecx448_pkey_meth = {
|
|
EVP_PKEY_X448,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
pkey_ecx_keygen,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
pkey_ecx_derive448,
|
|
pkey_ecx_ctrl,
|
|
0
|
|
};
|
|
|
|
static int pkey_ecd_digestsign25519(EVP_MD_CTX *ctx, unsigned char *sig,
|
|
size_t *siglen, const unsigned char *tbs,
|
|
size_t tbslen)
|
|
{
|
|
const ECX_KEY *edkey = EVP_MD_CTX_pkey_ctx(ctx)->pkey->pkey.ecx;
|
|
|
|
if (sig == NULL) {
|
|
*siglen = ED25519_SIGSIZE;
|
|
return 1;
|
|
}
|
|
if (*siglen < ED25519_SIGSIZE) {
|
|
ECerr(EC_F_PKEY_ECD_DIGESTSIGN25519, EC_R_BUFFER_TOO_SMALL);
|
|
return 0;
|
|
}
|
|
|
|
if (ED25519_sign(sig, tbs, tbslen, edkey->pubkey, edkey->privkey) == 0)
|
|
return 0;
|
|
*siglen = ED25519_SIGSIZE;
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_ecd_digestsign448(EVP_MD_CTX *ctx, unsigned char *sig,
|
|
size_t *siglen, const unsigned char *tbs,
|
|
size_t tbslen)
|
|
{
|
|
const ECX_KEY *edkey = EVP_MD_CTX_pkey_ctx(ctx)->pkey->pkey.ecx;
|
|
|
|
if (sig == NULL) {
|
|
*siglen = ED448_SIGSIZE;
|
|
return 1;
|
|
}
|
|
if (*siglen < ED448_SIGSIZE) {
|
|
ECerr(EC_F_PKEY_ECD_DIGESTSIGN448, EC_R_BUFFER_TOO_SMALL);
|
|
return 0;
|
|
}
|
|
|
|
if (ED448_sign(sig, tbs, tbslen, edkey->pubkey, edkey->privkey, NULL,
|
|
0) == 0)
|
|
return 0;
|
|
*siglen = ED448_SIGSIZE;
|
|
return 1;
|
|
}
|
|
|
|
static int pkey_ecd_digestverify25519(EVP_MD_CTX *ctx, const unsigned char *sig,
|
|
size_t siglen, const unsigned char *tbs,
|
|
size_t tbslen)
|
|
{
|
|
const ECX_KEY *edkey = EVP_MD_CTX_pkey_ctx(ctx)->pkey->pkey.ecx;
|
|
|
|
if (siglen != ED25519_SIGSIZE)
|
|
return 0;
|
|
|
|
return ED25519_verify(tbs, tbslen, sig, edkey->pubkey);
|
|
}
|
|
|
|
static int pkey_ecd_digestverify448(EVP_MD_CTX *ctx, const unsigned char *sig,
|
|
size_t siglen, const unsigned char *tbs,
|
|
size_t tbslen)
|
|
{
|
|
const ECX_KEY *edkey = EVP_MD_CTX_pkey_ctx(ctx)->pkey->pkey.ecx;
|
|
|
|
if (siglen != ED448_SIGSIZE)
|
|
return 0;
|
|
|
|
return ED448_verify(tbs, tbslen, sig, edkey->pubkey, NULL, 0);
|
|
}
|
|
|
|
static int pkey_ecd_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
|
|
{
|
|
switch (type) {
|
|
case EVP_PKEY_CTRL_MD:
|
|
/* Only NULL allowed as digest */
|
|
if (p2 == NULL || (const EVP_MD *)p2 == EVP_md_null())
|
|
return 1;
|
|
ECerr(EC_F_PKEY_ECD_CTRL, EC_R_INVALID_DIGEST_TYPE);
|
|
return 0;
|
|
|
|
case EVP_PKEY_CTRL_DIGESTINIT:
|
|
return 1;
|
|
}
|
|
return -2;
|
|
}
|
|
|
|
const EVP_PKEY_METHOD ed25519_pkey_meth = {
|
|
EVP_PKEY_ED25519, EVP_PKEY_FLAG_SIGCTX_CUSTOM,
|
|
0, 0, 0, 0, 0, 0,
|
|
pkey_ecx_keygen,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
pkey_ecd_ctrl,
|
|
0,
|
|
pkey_ecd_digestsign25519,
|
|
pkey_ecd_digestverify25519
|
|
};
|
|
|
|
const EVP_PKEY_METHOD ed448_pkey_meth = {
|
|
EVP_PKEY_ED448, EVP_PKEY_FLAG_SIGCTX_CUSTOM,
|
|
0, 0, 0, 0, 0, 0,
|
|
pkey_ecx_keygen,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
pkey_ecd_ctrl,
|
|
0,
|
|
pkey_ecd_digestsign448,
|
|
pkey_ecd_digestverify448
|
|
};
|