/* * 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 #include "internal/cryptlib.h" #include #include #include #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\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\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); } 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, }; 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, }; 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, }; 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, }; 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 };