865 lines
18 KiB
C
865 lines
18 KiB
C
/* $OpenBSD: ecx_methods.c,v 1.5 2023/03/15 06:34:07 tb Exp $ */
|
|
/*
|
|
* Copyright (c) 2022 Joel Sing <jsing@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and 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.
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <openssl/curve25519.h>
|
|
#include <openssl/ec.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/x509.h>
|
|
|
|
#include "asn1_local.h"
|
|
#include "bytestring.h"
|
|
#include "curve25519_internal.h"
|
|
#include "evp_local.h"
|
|
|
|
/*
|
|
* EVP PKEY and PKEY ASN.1 methods Ed25519 and X25519.
|
|
*
|
|
* RFC 7748 - Elliptic Curves for Security.
|
|
* RFC 8032 - Edwards-Curve Digital Signature Algorithm (EdDSA).
|
|
*/
|
|
|
|
#define ED25519_BITS 253
|
|
#define ED25519_SECURITY_BITS 128
|
|
#define ED25519_SIG_SIZE 64
|
|
|
|
#define X25519_BITS 253
|
|
#define X25519_SECURITY_BITS 128
|
|
|
|
static int
|
|
ecx_key_len(int nid)
|
|
{
|
|
switch (nid) {
|
|
case NID_ED25519:
|
|
return ED25519_KEYLEN;
|
|
case NID_X25519:
|
|
return X25519_KEYLEN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct ecx_key_st *
|
|
ecx_key_new(int nid)
|
|
{
|
|
struct ecx_key_st *ecx_key;
|
|
int key_len;
|
|
|
|
if ((key_len = ecx_key_len(nid)) == 0)
|
|
return NULL;
|
|
|
|
if ((ecx_key = calloc(1, sizeof(*ecx_key))) == NULL)
|
|
return NULL;
|
|
|
|
ecx_key->nid = nid;
|
|
ecx_key->key_len = key_len;
|
|
|
|
return ecx_key;
|
|
}
|
|
|
|
static void
|
|
ecx_key_clear(struct ecx_key_st *ecx_key)
|
|
{
|
|
freezero(ecx_key->priv_key, ecx_key->priv_key_len);
|
|
ecx_key->priv_key = NULL;
|
|
ecx_key->priv_key_len = 0;
|
|
|
|
freezero(ecx_key->pub_key, ecx_key->pub_key_len);
|
|
ecx_key->pub_key = NULL;
|
|
ecx_key->pub_key_len = 0;
|
|
}
|
|
|
|
static void
|
|
ecx_key_free(struct ecx_key_st *ecx_key)
|
|
{
|
|
if (ecx_key == NULL)
|
|
return;
|
|
|
|
ecx_key_clear(ecx_key);
|
|
|
|
freezero(ecx_key, sizeof(*ecx_key));
|
|
}
|
|
|
|
static int
|
|
ecx_key_generate(struct ecx_key_st *ecx_key)
|
|
{
|
|
uint8_t *pub_key = NULL, *priv_key = NULL;
|
|
int ret = 0;
|
|
|
|
ecx_key_clear(ecx_key);
|
|
|
|
if ((pub_key = calloc(1, ecx_key->key_len)) == NULL)
|
|
goto err;
|
|
if ((priv_key = calloc(1, ecx_key->key_len)) == NULL)
|
|
goto err;
|
|
|
|
switch (ecx_key->nid) {
|
|
case NID_ED25519:
|
|
ED25519_keypair(pub_key, priv_key);
|
|
break;
|
|
case NID_X25519:
|
|
X25519_keypair(pub_key, priv_key);
|
|
break;
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
ecx_key->priv_key = priv_key;
|
|
ecx_key->priv_key_len = ecx_key->key_len;
|
|
priv_key = NULL;
|
|
|
|
ecx_key->pub_key = pub_key;
|
|
ecx_key->pub_key_len = ecx_key->key_len;
|
|
pub_key = NULL;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
freezero(pub_key, ecx_key->key_len);
|
|
freezero(priv_key, ecx_key->key_len);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ecx_key_set_priv(struct ecx_key_st *ecx_key, const uint8_t *priv_key,
|
|
size_t priv_key_len)
|
|
{
|
|
uint8_t *pub_key = NULL;
|
|
CBS cbs;
|
|
|
|
ecx_key_clear(ecx_key);
|
|
|
|
if (priv_key_len != ecx_key->key_len)
|
|
goto err;
|
|
|
|
if ((pub_key = calloc(1, ecx_key->key_len)) == NULL)
|
|
goto err;
|
|
|
|
switch (ecx_key->nid) {
|
|
case NID_ED25519:
|
|
ED25519_public_from_private(pub_key, priv_key);
|
|
break;
|
|
case NID_X25519:
|
|
X25519_public_from_private(pub_key, priv_key);
|
|
break;
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
CBS_init(&cbs, priv_key, priv_key_len);
|
|
if (!CBS_stow(&cbs, &ecx_key->priv_key, &ecx_key->priv_key_len))
|
|
goto err;
|
|
|
|
ecx_key->pub_key = pub_key;
|
|
ecx_key->pub_key_len = ecx_key->key_len;
|
|
pub_key = NULL;
|
|
|
|
err:
|
|
freezero(pub_key, ecx_key->key_len);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
ecx_key_set_pub(struct ecx_key_st *ecx_key, const uint8_t *pub_key,
|
|
size_t pub_key_len)
|
|
{
|
|
CBS cbs;
|
|
|
|
ecx_key_clear(ecx_key);
|
|
|
|
if (pub_key_len != ecx_key->key_len)
|
|
return 0;
|
|
|
|
CBS_init(&cbs, pub_key, pub_key_len);
|
|
if (!CBS_stow(&cbs, &ecx_key->pub_key, &ecx_key->pub_key_len))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
ecx_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *xpubkey)
|
|
{
|
|
struct ecx_key_st *ecx_key = NULL;
|
|
X509_ALGOR *algor;
|
|
int algor_type;
|
|
const uint8_t *param;
|
|
int param_len;
|
|
int ret = 0;
|
|
|
|
if (!X509_PUBKEY_get0_param(NULL, ¶m, ¶m_len, &algor, xpubkey))
|
|
goto err;
|
|
|
|
/* Ensure that parameters have not been specified in the encoding. */
|
|
if (algor != NULL) {
|
|
X509_ALGOR_get0(NULL, &algor_type, NULL, algor);
|
|
if (algor_type != V_ASN1_UNDEF) {
|
|
ECerror(EC_R_INVALID_ENCODING);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (param == NULL || param_len != ecx_key_len(pkey->ameth->pkey_id)) {
|
|
ECerror(EC_R_INVALID_ENCODING);
|
|
goto err;
|
|
}
|
|
|
|
if ((ecx_key = ecx_key_new(pkey->ameth->pkey_id)) == NULL)
|
|
goto err;
|
|
if (!ecx_key_set_pub(ecx_key, param, param_len))
|
|
goto err;
|
|
if (!EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx_key))
|
|
goto err;
|
|
ecx_key = NULL;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
ecx_key_free(ecx_key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ecx_pub_encode(X509_PUBKEY *xpubkey, const EVP_PKEY *pkey)
|
|
{
|
|
const struct ecx_key_st *ecx_key = pkey->pkey.ecx;
|
|
uint8_t *pub_key = NULL;
|
|
size_t pub_key_len = 0;
|
|
ASN1_OBJECT *aobj;
|
|
CBS cbs;
|
|
int ret = 0;
|
|
|
|
if (ecx_key == NULL) {
|
|
ECerror(EC_R_INVALID_KEY);
|
|
goto err;
|
|
}
|
|
|
|
if (ecx_key->pub_key_len != ecx_key->key_len)
|
|
goto err;
|
|
|
|
if ((aobj = OBJ_nid2obj(pkey->ameth->pkey_id)) == NULL)
|
|
goto err;
|
|
|
|
CBS_init(&cbs, ecx_key->pub_key, ecx_key->pub_key_len);
|
|
if (!CBS_stow(&cbs, &pub_key, &pub_key_len))
|
|
goto err;
|
|
|
|
if (!X509_PUBKEY_set0_param(xpubkey, aobj, V_ASN1_UNDEF, NULL,
|
|
pub_key, pub_key_len))
|
|
goto err;
|
|
|
|
pub_key = NULL;
|
|
pub_key_len = 0;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
free(pub_key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ecx_pub_cmp(const EVP_PKEY *pkey1, const EVP_PKEY *pkey2)
|
|
{
|
|
if (pkey1->pkey.ecx == NULL || pkey1->pkey.ecx->pub_key == NULL)
|
|
return -2;
|
|
if (pkey2->pkey.ecx == NULL || pkey2->pkey.ecx->pub_key == NULL)
|
|
return -2;
|
|
if (pkey1->pkey.ecx->pub_key_len != pkey2->pkey.ecx->pub_key_len)
|
|
return -2;
|
|
|
|
return timingsafe_memcmp(pkey1->pkey.ecx->pub_key, pkey2->pkey.ecx->pub_key,
|
|
pkey1->pkey.ecx->pub_key_len) == 0;
|
|
}
|
|
|
|
static int
|
|
ecx_pub_print(BIO *bio, const EVP_PKEY *pkey, int indent, ASN1_PCTX *ctx)
|
|
{
|
|
struct ecx_key_st *ecx_key = pkey->pkey.ecx;
|
|
const char *name;
|
|
|
|
if ((name = OBJ_nid2ln(pkey->ameth->pkey_id)) == NULL)
|
|
return 0;
|
|
|
|
if (ecx_key == NULL || ecx_key->pub_key == NULL)
|
|
return BIO_printf(bio, "%*s<INVALID PUBLIC KEY>\n",
|
|
indent, "") > 0;
|
|
|
|
if (BIO_printf(bio, "%*s%s Public-Key:\n", indent, "", name) <= 0)
|
|
return 0;
|
|
if (BIO_printf(bio, "%*spub:\n", indent, "") <= 0)
|
|
return 0;
|
|
if (ASN1_buf_print(bio, ecx_key->pub_key, ecx_key->pub_key_len,
|
|
indent + 4) == 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
ecx_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8pki)
|
|
{
|
|
struct ecx_key_st *ecx_key = NULL;
|
|
ASN1_OCTET_STRING *aos = NULL;
|
|
const X509_ALGOR *algor;
|
|
int algor_type;
|
|
const uint8_t *param;
|
|
int param_len;
|
|
int ret = 0;
|
|
|
|
if (!PKCS8_pkey_get0(NULL, ¶m, ¶m_len, &algor, p8pki))
|
|
goto err;
|
|
if ((aos = d2i_ASN1_OCTET_STRING(NULL, ¶m, param_len)) == NULL)
|
|
goto err;
|
|
|
|
/* Ensure that parameters have not been specified in the encoding. */
|
|
if (algor != NULL) {
|
|
X509_ALGOR_get0(NULL, &algor_type, NULL, algor);
|
|
if (algor_type != V_ASN1_UNDEF) {
|
|
ECerror(EC_R_INVALID_ENCODING);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (ASN1_STRING_get0_data(aos) == NULL ||
|
|
ASN1_STRING_length(aos) != ecx_key_len(pkey->ameth->pkey_id)) {
|
|
ECerror(EC_R_INVALID_ENCODING);
|
|
goto err;
|
|
}
|
|
|
|
if ((ecx_key = ecx_key_new(pkey->ameth->pkey_id)) == NULL)
|
|
goto err;
|
|
if (!ecx_key_set_priv(ecx_key, ASN1_STRING_get0_data(aos),
|
|
ASN1_STRING_length(aos)))
|
|
goto err;
|
|
if (!EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx_key))
|
|
goto err;
|
|
ecx_key = NULL;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
ASN1_OCTET_STRING_free(aos);
|
|
ecx_key_free(ecx_key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ecx_priv_encode(PKCS8_PRIV_KEY_INFO *p8pki, const EVP_PKEY *pkey)
|
|
{
|
|
struct ecx_key_st *ecx_key = pkey->pkey.ecx;
|
|
ASN1_OCTET_STRING *aos = NULL;
|
|
ASN1_OBJECT *aobj;
|
|
uint8_t *der = NULL;
|
|
int der_len = 0;
|
|
int ret = 0;
|
|
|
|
if (ecx_key == NULL || ecx_key->priv_key == NULL) {
|
|
ECerror(EC_R_INVALID_PRIVATE_KEY);
|
|
goto err;
|
|
}
|
|
|
|
if ((aobj = OBJ_nid2obj(pkey->ameth->pkey_id)) == NULL)
|
|
goto err;
|
|
|
|
if ((aos = ASN1_OCTET_STRING_new()) == NULL)
|
|
goto err;
|
|
if (!ASN1_OCTET_STRING_set(aos, ecx_key->priv_key,
|
|
ecx_key->priv_key_len))
|
|
goto err;
|
|
if ((der_len = i2d_ASN1_OCTET_STRING(aos, &der)) < 0)
|
|
goto err;
|
|
if (!PKCS8_pkey_set0(p8pki, aobj, 0, V_ASN1_UNDEF, NULL, der, der_len))
|
|
goto err;
|
|
|
|
der = NULL;
|
|
der_len = 0;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
freezero(der, der_len);
|
|
ASN1_OCTET_STRING_free(aos);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ecx_priv_print(BIO *bio, const EVP_PKEY *pkey, int indent, ASN1_PCTX *ctx)
|
|
{
|
|
struct ecx_key_st *ecx_key = pkey->pkey.ecx;
|
|
const char *name;
|
|
|
|
if ((name = OBJ_nid2ln(pkey->ameth->pkey_id)) == NULL)
|
|
return 0;
|
|
|
|
if (ecx_key == NULL || ecx_key->priv_key == NULL)
|
|
return BIO_printf(bio, "%*s<INVALID PRIVATE KEY>\n",
|
|
indent, "") > 0;
|
|
|
|
if (BIO_printf(bio, "%*s%s Private-Key:\n", indent, "", name) <= 0)
|
|
return 0;
|
|
if (BIO_printf(bio, "%*spriv:\n", indent, "") <= 0)
|
|
return 0;
|
|
if (ASN1_buf_print(bio, ecx_key->priv_key, ecx_key->priv_key_len,
|
|
indent + 4) == 0)
|
|
return 0;
|
|
if (BIO_printf(bio, "%*spub:\n", indent, "") <= 0)
|
|
return 0;
|
|
if (ASN1_buf_print(bio, ecx_key->pub_key, ecx_key->pub_key_len,
|
|
indent + 4) == 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
ecx_size(const EVP_PKEY *pkey)
|
|
{
|
|
return ecx_key_len(pkey->ameth->pkey_id);
|
|
}
|
|
|
|
static int
|
|
ecx_sig_size(const EVP_PKEY *pkey)
|
|
{
|
|
switch (pkey->ameth->pkey_id) {
|
|
case EVP_PKEY_ED25519:
|
|
return ED25519_SIG_SIZE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ecx_bits(const EVP_PKEY *pkey)
|
|
{
|
|
switch (pkey->ameth->pkey_id) {
|
|
case EVP_PKEY_ED25519:
|
|
return ED25519_BITS;
|
|
case EVP_PKEY_X25519:
|
|
return X25519_BITS;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ecx_security_bits(const EVP_PKEY *pkey)
|
|
{
|
|
switch (pkey->ameth->pkey_id) {
|
|
case EVP_PKEY_ED25519:
|
|
return ED25519_SECURITY_BITS;
|
|
case EVP_PKEY_X25519:
|
|
return X25519_SECURITY_BITS;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ecx_param_cmp(const EVP_PKEY *pkey1, const EVP_PKEY *pkey2)
|
|
{
|
|
/* No parameters, so always equivalent. */
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
ecx_free(EVP_PKEY *pkey)
|
|
{
|
|
struct ecx_key_st *ecx_key = pkey->pkey.ecx;
|
|
|
|
return ecx_key_free(ecx_key);
|
|
}
|
|
|
|
static int
|
|
ecx_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
|
|
{
|
|
/* Not supported. */
|
|
return -2;
|
|
}
|
|
|
|
static int
|
|
ecx_sign_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
|
|
{
|
|
switch (op) {
|
|
case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
|
|
/* PureEdDSA does its own hashing. */
|
|
*(int *)arg2 = NID_undef;
|
|
return 2;
|
|
}
|
|
return -2;
|
|
}
|
|
|
|
static int
|
|
ecx_set_priv_key(EVP_PKEY *pkey, const uint8_t *priv, size_t len)
|
|
{
|
|
struct ecx_key_st *ecx_key = NULL;
|
|
int ret = 0;
|
|
|
|
if (priv == NULL || len != ecx_key_len(pkey->ameth->pkey_id)) {
|
|
ECerror(EC_R_INVALID_ENCODING);
|
|
goto err;
|
|
}
|
|
|
|
if ((ecx_key = ecx_key_new(pkey->ameth->pkey_id)) == NULL)
|
|
goto err;
|
|
if (!ecx_key_set_priv(ecx_key, priv, len))
|
|
goto err;
|
|
if (!EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx_key))
|
|
goto err;
|
|
ecx_key = NULL;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
ecx_key_free(ecx_key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ecx_set_pub_key(EVP_PKEY *pkey, const uint8_t *pub, size_t len)
|
|
{
|
|
struct ecx_key_st *ecx_key = NULL;
|
|
int ret = 0;
|
|
|
|
if (pub == NULL || len != ecx_key_len(pkey->ameth->pkey_id)) {
|
|
ECerror(EC_R_INVALID_ENCODING);
|
|
goto err;
|
|
}
|
|
|
|
if ((ecx_key = ecx_key_new(pkey->ameth->pkey_id)) == NULL)
|
|
goto err;
|
|
if (!ecx_key_set_pub(ecx_key, pub, len))
|
|
goto err;
|
|
if (!EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx_key))
|
|
goto err;
|
|
ecx_key = NULL;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
ecx_key_free(ecx_key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ecx_get_priv_key(const EVP_PKEY *pkey, unsigned char *out_priv, size_t *out_len)
|
|
{
|
|
struct ecx_key_st *ecx_key = pkey->pkey.ecx;
|
|
CBS cbs;
|
|
|
|
if (out_priv == NULL) {
|
|
*out_len = ecx_key_len(pkey->ameth->pkey_id);
|
|
return 1;
|
|
}
|
|
|
|
if (ecx_key == NULL || ecx_key->priv_key == NULL)
|
|
return 0;
|
|
|
|
CBS_init(&cbs, ecx_key->priv_key, ecx_key->priv_key_len);
|
|
if (!CBS_write_bytes(&cbs, out_priv, *out_len, out_len))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
ecx_get_pub_key(const EVP_PKEY *pkey, unsigned char *out_pub, size_t *out_len)
|
|
{
|
|
struct ecx_key_st *ecx_key = pkey->pkey.ecx;
|
|
CBS cbs;
|
|
|
|
if (out_pub == NULL) {
|
|
*out_len = ecx_key_len(pkey->ameth->pkey_id);
|
|
return 1;
|
|
}
|
|
|
|
if (ecx_key == NULL || ecx_key->pub_key == NULL)
|
|
return 0;
|
|
|
|
CBS_init(&cbs, ecx_key->pub_key, ecx_key->pub_key_len);
|
|
if (!CBS_write_bytes(&cbs, out_pub, *out_len, out_len))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
pkey_ecx_keygen(EVP_PKEY_CTX *pkey_ctx, EVP_PKEY *pkey)
|
|
{
|
|
struct ecx_key_st *ecx_key = NULL;
|
|
int ret = 0;
|
|
|
|
if ((ecx_key = ecx_key_new(pkey_ctx->pmeth->pkey_id)) == NULL)
|
|
goto err;
|
|
if (!ecx_key_generate(ecx_key))
|
|
goto err;
|
|
if (!EVP_PKEY_assign(pkey, pkey_ctx->pmeth->pkey_id, ecx_key))
|
|
goto err;
|
|
ecx_key = NULL;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
ecx_key_free(ecx_key);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
pkey_ecx_derive(EVP_PKEY_CTX *pkey_ctx, unsigned char *out_key,
|
|
size_t *out_key_len)
|
|
{
|
|
struct ecx_key_st *ecx_key, *ecx_peer_key;
|
|
|
|
if (pkey_ctx->pkey == NULL || pkey_ctx->peerkey == NULL) {
|
|
ECerror(EC_R_KEYS_NOT_SET);
|
|
return 0;
|
|
}
|
|
|
|
if ((ecx_key = pkey_ctx->pkey->pkey.ecx) == NULL) {
|
|
ECerror(EC_R_INVALID_PRIVATE_KEY);
|
|
return 0;
|
|
}
|
|
if (ecx_key->priv_key == NULL) {
|
|
ECerror(EC_R_INVALID_PRIVATE_KEY);
|
|
return 0;
|
|
}
|
|
|
|
if ((ecx_peer_key = pkey_ctx->peerkey->pkey.ecx) == NULL) {
|
|
ECerror(EC_R_INVALID_PEER_KEY);
|
|
return 0;
|
|
}
|
|
|
|
if (out_key != NULL) {
|
|
if (!X25519(out_key, ecx_key->priv_key, ecx_peer_key->pub_key))
|
|
return 0;
|
|
}
|
|
|
|
*out_key_len = X25519_KEYLEN;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
pkey_ecx_ctrl(EVP_PKEY_CTX *pkey_ctx, int op, int arg1, void *arg2)
|
|
{
|
|
if (op == EVP_PKEY_CTRL_PEER_KEY)
|
|
return 1;
|
|
|
|
return -2;
|
|
}
|
|
|
|
static int
|
|
ecx_item_verify(EVP_MD_CTX *md_ctx, const ASN1_ITEM *it, void *asn,
|
|
X509_ALGOR *algor, ASN1_BIT_STRING *abs, EVP_PKEY *pkey)
|
|
{
|
|
const ASN1_OBJECT *aobj;
|
|
int nid, param_type;
|
|
|
|
X509_ALGOR_get0(&aobj, ¶m_type, NULL, algor);
|
|
|
|
nid = OBJ_obj2nid(aobj);
|
|
|
|
if (nid != NID_ED25519 || param_type != V_ASN1_UNDEF) {
|
|
ECerror(EC_R_INVALID_ENCODING);
|
|
return 0;
|
|
}
|
|
|
|
if (!EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, pkey))
|
|
return 0;
|
|
|
|
return 2;
|
|
}
|
|
|
|
static int
|
|
ecx_item_sign(EVP_MD_CTX *md_ctx, const ASN1_ITEM *it, void *asn,
|
|
X509_ALGOR *algor1, X509_ALGOR *algor2, ASN1_BIT_STRING *abs)
|
|
{
|
|
ASN1_OBJECT *aobj;
|
|
|
|
if ((aobj = OBJ_nid2obj(NID_ED25519)) == NULL)
|
|
return 0;
|
|
|
|
if (!X509_ALGOR_set0(algor1, aobj, V_ASN1_UNDEF, NULL))
|
|
return 0;
|
|
|
|
if (algor2 != NULL) {
|
|
if (!X509_ALGOR_set0(algor2, aobj, V_ASN1_UNDEF, NULL))
|
|
return 0;
|
|
}
|
|
|
|
/* Tell ASN1_item_sign_ctx() that identifiers are set and it needs to sign. */
|
|
return 3;
|
|
}
|
|
|
|
static int
|
|
pkey_ecx_digestsign(EVP_MD_CTX *md_ctx, unsigned char *out_sig,
|
|
size_t *out_sig_len, const unsigned char *message, size_t message_len)
|
|
{
|
|
struct ecx_key_st *ecx_key;
|
|
EVP_PKEY_CTX *pkey_ctx;
|
|
|
|
pkey_ctx = EVP_MD_CTX_pkey_ctx(md_ctx);
|
|
ecx_key = pkey_ctx->pkey->pkey.ecx;
|
|
|
|
if (out_sig == NULL) {
|
|
*out_sig_len = ecx_sig_size(pkey_ctx->pkey);
|
|
return 1;
|
|
}
|
|
if (*out_sig_len < ecx_sig_size(pkey_ctx->pkey)) {
|
|
ECerror(EC_R_BUFFER_TOO_SMALL);
|
|
return 0;
|
|
}
|
|
|
|
if (ecx_key == NULL)
|
|
return 0;
|
|
if (ecx_key->priv_key == NULL || ecx_key->pub_key == NULL)
|
|
return 0;
|
|
|
|
if (!ED25519_sign(out_sig, message, message_len, ecx_key->pub_key,
|
|
ecx_key->priv_key))
|
|
return 0;
|
|
|
|
*out_sig_len = ecx_sig_size(pkey_ctx->pkey);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
pkey_ecx_digestverify(EVP_MD_CTX *md_ctx, const unsigned char *sig,
|
|
size_t sig_len, const unsigned char *message, size_t message_len)
|
|
{
|
|
struct ecx_key_st *ecx_key;
|
|
EVP_PKEY_CTX *pkey_ctx;
|
|
|
|
pkey_ctx = EVP_MD_CTX_pkey_ctx(md_ctx);
|
|
ecx_key = pkey_ctx->pkey->pkey.ecx;
|
|
|
|
if (ecx_key == NULL || ecx_key->pub_key == NULL)
|
|
return 0;
|
|
if (sig_len != ecx_sig_size(pkey_ctx->pkey))
|
|
return 0;
|
|
|
|
return ED25519_verify(message, message_len, sig, ecx_key->pub_key);
|
|
}
|
|
|
|
static int
|
|
pkey_ecx_ed_ctrl(EVP_PKEY_CTX *pkey_ctx, int op, int arg1, void *arg2)
|
|
{
|
|
switch (op) {
|
|
case EVP_PKEY_CTRL_MD:
|
|
/* PureEdDSA does its own hashing. */
|
|
if (arg2 != NULL && (const EVP_MD *)arg2 != EVP_md_null()) {
|
|
ECerror(EC_R_INVALID_DIGEST_TYPE);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
|
|
case EVP_PKEY_CTRL_DIGESTINIT:
|
|
return 1;
|
|
}
|
|
return -2;
|
|
}
|
|
|
|
const EVP_PKEY_ASN1_METHOD x25519_asn1_meth = {
|
|
.pkey_id = EVP_PKEY_X25519,
|
|
.pkey_base_id = EVP_PKEY_X25519,
|
|
.pkey_flags = 0,
|
|
.pem_str = "X25519",
|
|
.info = "OpenSSL X25519 algorithm",
|
|
|
|
.pub_decode = ecx_pub_decode,
|
|
.pub_encode = ecx_pub_encode,
|
|
.pub_cmp = ecx_pub_cmp,
|
|
.pub_print = ecx_pub_print,
|
|
|
|
.priv_decode = ecx_priv_decode,
|
|
.priv_encode = ecx_priv_encode,
|
|
.priv_print = ecx_priv_print,
|
|
|
|
.pkey_size = ecx_size,
|
|
.pkey_bits = ecx_bits,
|
|
.pkey_security_bits = ecx_security_bits,
|
|
|
|
.param_cmp = ecx_param_cmp,
|
|
|
|
.pkey_free = ecx_free,
|
|
.pkey_ctrl = ecx_ctrl,
|
|
|
|
.set_priv_key = ecx_set_priv_key,
|
|
.set_pub_key = ecx_set_pub_key,
|
|
.get_priv_key = ecx_get_priv_key,
|
|
.get_pub_key = ecx_get_pub_key,
|
|
};
|
|
|
|
const EVP_PKEY_METHOD x25519_pkey_meth = {
|
|
.pkey_id = EVP_PKEY_X25519,
|
|
.keygen = pkey_ecx_keygen,
|
|
.derive = pkey_ecx_derive,
|
|
.ctrl = pkey_ecx_ctrl,
|
|
};
|
|
|
|
const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = {
|
|
.pkey_id = EVP_PKEY_ED25519,
|
|
.pkey_base_id = EVP_PKEY_ED25519,
|
|
.pkey_flags = 0,
|
|
.pem_str = "ED25519",
|
|
.info = "OpenSSL ED25519 algorithm",
|
|
|
|
.pub_decode = ecx_pub_decode,
|
|
.pub_encode = ecx_pub_encode,
|
|
.pub_cmp = ecx_pub_cmp,
|
|
.pub_print = ecx_pub_print,
|
|
|
|
.priv_decode = ecx_priv_decode,
|
|
.priv_encode = ecx_priv_encode,
|
|
.priv_print = ecx_priv_print,
|
|
|
|
.pkey_size = ecx_sig_size,
|
|
.pkey_bits = ecx_bits,
|
|
.pkey_security_bits = ecx_security_bits,
|
|
|
|
.param_cmp = ecx_param_cmp,
|
|
|
|
.pkey_free = ecx_free,
|
|
.pkey_ctrl = ecx_sign_ctrl,
|
|
|
|
.item_verify = ecx_item_verify,
|
|
.item_sign = ecx_item_sign,
|
|
|
|
.set_priv_key = ecx_set_priv_key,
|
|
.set_pub_key = ecx_set_pub_key,
|
|
.get_priv_key = ecx_get_priv_key,
|
|
.get_pub_key = ecx_get_pub_key,
|
|
};
|
|
|
|
const EVP_PKEY_METHOD ed25519_pkey_meth = {
|
|
.pkey_id = EVP_PKEY_ED25519,
|
|
.flags = EVP_PKEY_FLAG_SIGCTX_CUSTOM,
|
|
.keygen = pkey_ecx_keygen,
|
|
.ctrl = pkey_ecx_ed_ctrl,
|
|
.digestsign = pkey_ecx_digestsign,
|
|
.digestverify = pkey_ecx_digestverify,
|
|
};
|