Commit 88e20b85 authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

Add support for ECDH KARI.

Add support for ECDH in enveloped data. The CMS ctrls for the EC ASN1
method decode/encode the appropriate parameters from the CMS ASN1 data
and send appropriate data to the EC public key method.
parent 25af7a5d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1204,6 +1204,7 @@ void ERR_load_EC_strings(void);
#define EC_R_INVALID_COMPRESSED_POINT			 110
#define EC_R_INVALID_COMPRESSION_BIT			 109
#define EC_R_INVALID_CURVE				 141
#define EC_R_INVALID_DIGEST				 151
#define EC_R_INVALID_DIGEST_TYPE			 138
#define EC_R_INVALID_ENCODING				 102
#define EC_R_INVALID_FIELD				 103
+377 −0
Original line number Diff line number Diff line
@@ -63,8 +63,12 @@
#ifndef OPENSSL_NO_CMS
#include <openssl/cms.h>
#endif
#include <openssl/asn1t.h>
#include "asn1_locl.h"

static int ecdh_cms_decrypt(CMS_RecipientInfo *ri);
static int ecdh_cms_encrypt(CMS_RecipientInfo *ri);

static int eckey_param2type(int *pptype, void **ppval, EC_KEY *ec_key)
	{
	const EC_GROUP  *group;
@@ -612,6 +616,17 @@ static int ec_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
			X509_ALGOR_set0(alg2, OBJ_nid2obj(snid), V_ASN1_UNDEF, 0);
			}
		return 1;

		case ASN1_PKEY_CTRL_CMS_ENVELOPE:
		if (arg1 == 1)
			return ecdh_cms_decrypt(arg2);
		else if (arg1 == 0)
			return ecdh_cms_encrypt(arg2);
		return -2;

		case ASN1_PKEY_CTRL_CMS_RI_TYPE:
		*(int *)arg2 = CMS_RECIPINFO_AGREE;
		return 1;
#endif

		case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
@@ -658,3 +673,365 @@ const EVP_PKEY_ASN1_METHOD eckey_asn1_meth =
	old_ec_priv_decode,
	old_ec_priv_encode
	};

#ifndef OPENSSL_NO_CMS

static int ecdh_cms_set_peerkey(EVP_PKEY_CTX *pctx,
				X509_ALGOR *alg, ASN1_BIT_STRING *pubkey)
	{
	ASN1_OBJECT *aoid;
	int atype;
	void *aval;
	int rv = 0;
	EVP_PKEY *pkpeer = NULL;
	EC_KEY *ecpeer = NULL;
	const unsigned char *p;
	int plen;
	X509_ALGOR_get0(&aoid, &atype, &aval, alg);
	if (OBJ_obj2nid(aoid) != NID_X9_62_id_ecPublicKey)
		goto err;
	/* If absent parameters get group from main key */
	if (atype == V_ASN1_UNDEF || atype == V_ASN1_NULL)
		{
		const EC_GROUP *grp;
		EVP_PKEY *pk;
		pk = EVP_PKEY_CTX_get0_pkey(pctx);
		if (!pk)
			goto err;
		grp = EC_KEY_get0_group(pk->pkey.ec);
		ecpeer = EC_KEY_new();
		if (!ecpeer)
			goto err;
		if (!EC_KEY_set_group(ecpeer, grp))
			goto err;
		}
	else
		{
		ecpeer = eckey_type2param(atype, aval);
		if (!ecpeer)
			goto err;
		}
	/* We have parameters now set public key */
	plen = ASN1_STRING_length(pubkey);
	p = ASN1_STRING_data(pubkey);
	if (!p || !plen)
		goto err;
	if (!o2i_ECPublicKey(&ecpeer, &p, plen))
		goto err;
	pkpeer = EVP_PKEY_new();
	if (!pkpeer)
		goto err;
	EVP_PKEY_set1_EC_KEY(pkpeer, ecpeer);
	if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0)
		rv = 1;
	err:
	if (ecpeer)
		EC_KEY_free(ecpeer);
	if (pkpeer)
		EVP_PKEY_free(pkpeer);
	return rv;
	}
/* Set KDF parameters based on KDF NID */
static int ecdh_cms_set_kdf_param(EVP_PKEY_CTX *pctx, int eckdf_nid)
	{
	int kdf_nid, kdfmd_nid, cofactor;
	const EVP_MD *kdf_md;
	if (eckdf_nid == NID_undef)
		return 0;

	/* Lookup KDF type, cofactor mode and digest */
	if (!OBJ_find_sigid_algs(eckdf_nid, &kdfmd_nid, &kdf_nid))
		return 0;

	if (kdf_nid == NID_dh_std_kdf)
		cofactor = 0;
	else if (kdf_nid == NID_dh_cofactor_kdf)
		cofactor = 1;
	else return 0;

	if (EVP_PKEY_CTX_set_ecdh_cofactor_mode(pctx, cofactor) <= 0)
		return 0;

	if (EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_X9_62) <= 0)
		return 0;

	kdf_md = EVP_get_digestbynid(kdfmd_nid);
	if (!kdf_md)
		return 0;

	if (EVP_PKEY_CTX_set_ecdh_kdf_md(pctx, kdf_md) <= 0)
		return 0;
	return 1;
	}

/* Utilities to encode the ECC_CMS_SharedInfo structure used during key
 * derivation.
 */

typedef struct {
	X509_ALGOR *keyInfo;
	ASN1_OCTET_STRING *entityUInfo;
	ASN1_OCTET_STRING *suppPubInfo;
} ECC_CMS_SharedInfo;

ASN1_SEQUENCE(ECC_CMS_SharedInfo) = {
  ASN1_SIMPLE(ECC_CMS_SharedInfo, keyInfo, X509_ALGOR),
  ASN1_EXP_OPT(ECC_CMS_SharedInfo, entityUInfo, ASN1_OCTET_STRING, 0),
  ASN1_EXP_OPT(ECC_CMS_SharedInfo, suppPubInfo, ASN1_OCTET_STRING, 2),
} ASN1_SEQUENCE_END(ECC_CMS_SharedInfo)

static int ecdh_cms_set_ukm(EVP_PKEY_CTX *pctx, 
					X509_ALGOR *kekalg, 
					ASN1_OCTET_STRING *ukm,
					int keylen)
	{
	union {
		ECC_CMS_SharedInfo *pecsi;
		ASN1_VALUE *a;
	} intsi = {NULL};

	unsigned char *der = NULL;
	int plen;
	ASN1_OCTET_STRING oklen;
	unsigned char kl[4];
	ECC_CMS_SharedInfo ecsi;

	keylen <<= 3;
	kl[0] = (keylen >> 24) & 0xff;
	kl[1] = (keylen >> 16) & 0xff;
	kl[2] = (keylen >> 8) & 0xff;
	kl[3] = keylen & 0xff;
	oklen.length = 4;
	oklen.data = kl;
	oklen.type = V_ASN1_OCTET_STRING;
	oklen.flags = 0;
	ecsi.keyInfo = kekalg;
	ecsi.entityUInfo = ukm;
	ecsi.suppPubInfo = &oklen;
	intsi.pecsi = &ecsi;
	plen = ASN1_item_i2d(intsi.a, &der, ASN1_ITEM_rptr(ECC_CMS_SharedInfo));
	if (!der || !plen)
		goto err;
	if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, der, plen) <= 0)
		goto err;
	return 1;
	err:
	if (der)
		OPENSSL_free(der);
	return 0;
	}

static int ecdh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri)
	{
	int rv = 0;

	X509_ALGOR *alg, *kekalg = NULL;
	ASN1_OCTET_STRING *ukm;
	const unsigned char *p;
	int plen, keylen;
	const EVP_CIPHER *kekcipher;
	EVP_CIPHER_CTX *kekctx;

	if (!CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm))
		return 0;

	if (!ecdh_cms_set_kdf_param(pctx, OBJ_obj2nid(alg->algorithm)))
		{
		ECerr(EC_F_ECDH_CMS_SET_SHARED_INFO, EC_R_KDF_PARAMETER_ERROR);
		return 0;
		}

	if (alg->parameter->type != V_ASN1_SEQUENCE)
		return 0;

	p = alg->parameter->value.sequence->data;
	plen = alg->parameter->value.sequence->length;
	kekalg = d2i_X509_ALGOR(NULL, &p, plen);
	if (!kekalg)
		goto err;
	kekctx = CMS_RecipientInfo_kari_get0_ctx(ri);
	if (!kekctx)
		goto err;
	kekcipher = EVP_get_cipherbyobj(kekalg->algorithm);
	if (!kekcipher || EVP_CIPHER_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
		goto err;
	if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, NULL))
		goto err;

	keylen = EVP_CIPHER_CTX_key_length(kekctx);
	if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(pctx, keylen) <= 0)
		goto err;

	if (!ecdh_cms_set_ukm(pctx, kekalg, ukm, keylen))
		goto err;

	rv = 1;
	err:
	if (kekalg)
		X509_ALGOR_free(kekalg);
	return rv;
	}

static int ecdh_cms_decrypt(CMS_RecipientInfo *ri)
	{
	EVP_PKEY_CTX *pctx;
	pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
	if (!pctx)
		return 0;
	/* See if we need to set peer key */
	if (!EVP_PKEY_CTX_get0_peerkey(pctx))
		{
		X509_ALGOR *alg;
		ASN1_BIT_STRING *pubkey;
		if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &alg, &pubkey,
							NULL, NULL, NULL))
			return 0;
		if (!alg || !pubkey)
			return 0;
		if (!ecdh_cms_set_peerkey(pctx, alg, pubkey))
			{
			ECerr(EC_F_ECDH_CMS_DECRYPT, EC_R_PEER_KEY_ERROR);
			return 0;
			}
		}
	/* Set ECDH derivation parameters and initialise unwrap context */
	if (!ecdh_cms_set_shared_info(pctx, ri))
		{
		ECerr(EC_F_ECDH_CMS_DECRYPT, EC_R_SHARED_INFO_ERROR);
		return 0;
		}
	return 1;
	}

static int ecdh_cms_encrypt(CMS_RecipientInfo *ri)
	{
	EVP_PKEY_CTX *pctx;
	EVP_PKEY *pkey;
	EVP_CIPHER_CTX *ctx;
	int keylen;
	X509_ALGOR *talg, *wrap_alg = NULL;
	ASN1_OBJECT *aoid;
	ASN1_BIT_STRING *pubkey;
	ASN1_STRING *wrap_str;
	ASN1_OCTET_STRING *ukm;
	unsigned char *penc = NULL;
	int penclen;
	int rv = 0;
	int ecdh_nid, kdf_type, kdf_nid, wrap_nid;
	const EVP_MD *kdf_md;
	pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
	if (!pctx)
		return 0;
	/* Get ephemeral key */
	pkey = EVP_PKEY_CTX_get0_pkey(pctx);
	if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &talg, &pubkey,
							NULL, NULL, NULL))
		goto err;
	X509_ALGOR_get0(&aoid, NULL, NULL, talg);
	/* Is everything uninitialised? */
	if (aoid == OBJ_nid2obj(NID_undef))
		{

		EC_KEY *eckey = pkey->pkey.ec;
		/* Set the key */
		unsigned char *p;

		penclen = i2o_ECPublicKey(eckey, NULL);
		if (penclen <= 0)
			goto err;
		penc = OPENSSL_malloc(penclen);
		if (!penc)
			goto err;
		p = penc;
		penclen = i2o_ECPublicKey(eckey, &p);
		if (penclen <= 0)
			goto err;
		ASN1_STRING_set0(pubkey, penc, penclen);
		pubkey->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07);
		pubkey->flags|=ASN1_STRING_FLAG_BITS_LEFT;

		penc = NULL;
		X509_ALGOR_set0(talg, OBJ_nid2obj(NID_X9_62_id_ecPublicKey),
							V_ASN1_UNDEF, NULL);
		}

	/* See if custom paraneters set */
	kdf_type = EVP_PKEY_CTX_get_ecdh_kdf_type(pctx);
	if (kdf_type <= 0)
		goto err;
	if (!EVP_PKEY_CTX_get_ecdh_kdf_md(pctx, &kdf_md))
		goto err;
	ecdh_nid = EVP_PKEY_CTX_get_ecdh_cofactor_mode(pctx);
	if (ecdh_nid < 0)
		goto err;
	else if (ecdh_nid == 0)
		ecdh_nid = NID_dh_std_kdf;
	else if (ecdh_nid == 1)
		ecdh_nid = NID_dh_cofactor_kdf;

	if (kdf_type == EVP_PKEY_ECDH_KDF_NONE)
		{
		kdf_type = EVP_PKEY_ECDH_KDF_X9_62;
		if (EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, kdf_type) <= 0)
			goto err;
		}
	else
		/* Uknown KDF */
		goto err;
	if (kdf_md == NULL)
		{
		/* Fixme later for better MD */
		kdf_md = EVP_sha1();
		if (EVP_PKEY_CTX_set_ecdh_kdf_md(pctx, kdf_md) <= 0)
			goto err;
		}

	if (!CMS_RecipientInfo_kari_get0_alg(ri, &talg, &ukm))
		goto err;

	/* Lookup NID for KDF+cofactor+digest */

	if (!OBJ_find_sigid_by_algs(&kdf_nid, EVP_MD_type(kdf_md), ecdh_nid))
		goto err;
	/* Get wrap NID */
	ctx = CMS_RecipientInfo_kari_get0_ctx(ri);
	wrap_nid = EVP_CIPHER_CTX_type(ctx);
	keylen = EVP_CIPHER_CTX_key_length(ctx);

	/* Package wrap algorithm in an AlgorithmIdentifier */

	wrap_alg = X509_ALGOR_new();
	if (!wrap_alg)
		goto err;
	X509_ALGOR_set0(wrap_alg, OBJ_nid2obj(wrap_nid), V_ASN1_UNDEF, NULL);

	if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(pctx, keylen) <= 0)
		goto err;
	if (!ecdh_cms_set_ukm(pctx, wrap_alg, ukm, keylen))
		goto err;

	/* Now need to wrap encoding of wrap AlgorithmIdentifier into
	 * parameter of another AlgorithmIdentifier.
	 */
	penc = NULL;
	penclen = i2d_X509_ALGOR(wrap_alg, &penc);
	if (!penc || !penclen)
		goto err;
	wrap_str = ASN1_STRING_new();
	if (!wrap_str)
		goto err;
	ASN1_STRING_set0(wrap_str, penc, penclen);
	penc = NULL;
	X509_ALGOR_set0(talg, OBJ_nid2obj(kdf_nid), V_ASN1_SEQUENCE, wrap_str);

	rv = 1;

	err:
	if (penc)
		OPENSSL_free(penc);
	if (wrap_alg)
		X509_ALGOR_free(wrap_alg);
	return rv;
	}

#endif
+7 −1
Original line number Diff line number Diff line
/* crypto/ec/ec_err.c */
/* ====================================================================
 * Copyright (c) 1999-2011 The OpenSSL Project.  All rights reserved.
 * Copyright (c) 1999-2013 The OpenSSL Project.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
@@ -76,6 +76,8 @@ static ERR_STRING_DATA EC_str_functs[]=
{ERR_FUNC(EC_F_D2I_ECPKPARAMETERS),	"d2i_ECPKParameters"},
{ERR_FUNC(EC_F_D2I_ECPRIVATEKEY),	"d2i_ECPrivateKey"},
{ERR_FUNC(EC_F_DO_EC_KEY_PRINT),	"DO_EC_KEY_PRINT"},
{ERR_FUNC(EC_F_ECDH_CMS_DECRYPT),	"ECDH_CMS_DECRYPT"},
{ERR_FUNC(EC_F_ECDH_CMS_SET_SHARED_INFO),	"ECDH_CMS_SET_SHARED_INFO"},
{ERR_FUNC(EC_F_ECKEY_PARAM2TYPE),	"ECKEY_PARAM2TYPE"},
{ERR_FUNC(EC_F_ECKEY_PARAM_DECODE),	"ECKEY_PARAM_DECODE"},
{ERR_FUNC(EC_F_ECKEY_PRIV_DECODE),	"ECKEY_PRIV_DECODE"},
@@ -229,6 +231,7 @@ static ERR_STRING_DATA EC_str_reasons[]=
{ERR_REASON(EC_R_INVALID_COMPRESSED_POINT),"invalid compressed point"},
{ERR_REASON(EC_R_INVALID_COMPRESSION_BIT),"invalid compression bit"},
{ERR_REASON(EC_R_INVALID_CURVE)          ,"invalid curve"},
{ERR_REASON(EC_R_INVALID_DIGEST)         ,"invalid digest"},
{ERR_REASON(EC_R_INVALID_DIGEST_TYPE)    ,"invalid digest type"},
{ERR_REASON(EC_R_INVALID_ENCODING)       ,"invalid encoding"},
{ERR_REASON(EC_R_INVALID_FIELD)          ,"invalid field"},
@@ -237,6 +240,7 @@ static ERR_STRING_DATA EC_str_reasons[]=
{ERR_REASON(EC_R_INVALID_PENTANOMIAL_BASIS),"invalid pentanomial basis"},
{ERR_REASON(EC_R_INVALID_PRIVATE_KEY)    ,"invalid private key"},
{ERR_REASON(EC_R_INVALID_TRINOMIAL_BASIS),"invalid trinomial basis"},
{ERR_REASON(EC_R_KDF_PARAMETER_ERROR)    ,"kdf parameter error"},
{ERR_REASON(EC_R_KEYS_NOT_SET)           ,"keys not set"},
{ERR_REASON(EC_R_MISSING_PARAMETERS)     ,"missing parameters"},
{ERR_REASON(EC_R_MISSING_PRIVATE_KEY)    ,"missing private key"},
@@ -247,9 +251,11 @@ static ERR_STRING_DATA EC_str_reasons[]=
{ERR_REASON(EC_R_NO_FIELD_MOD)           ,"no field mod"},
{ERR_REASON(EC_R_NO_PARAMETERS_SET)      ,"no parameters set"},
{ERR_REASON(EC_R_PASSED_NULL_PARAMETER)  ,"passed null parameter"},
{ERR_REASON(EC_R_PEER_KEY_ERROR)         ,"peer key error"},
{ERR_REASON(EC_R_PKPARAMETERS2GROUP_FAILURE),"pkparameters2group failure"},
{ERR_REASON(EC_R_POINT_AT_INFINITY)      ,"point at infinity"},
{ERR_REASON(EC_R_POINT_IS_NOT_ON_CURVE)  ,"point is not on curve"},
{ERR_REASON(EC_R_SHARED_INFO_ERROR)      ,"shared info error"},
{ERR_REASON(EC_R_SLOT_FULL)              ,"slot full"},
{ERR_REASON(EC_R_UNDEFINED_GENERATOR)    ,"undefined generator"},
{ERR_REASON(EC_R_UNDEFINED_ORDER)        ,"undefined order"},
+17 −1
Original line number Diff line number Diff line
@@ -319,7 +319,7 @@ static int pkey_ec_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
		case EVP_PKEY_CTRL_EC_ECDH_COFACTOR:
		if (p1 == -2)
			{
			if (dctx->co_key)
			if (dctx->cofactor_mode != -1)
				return dctx->cofactor_mode;
			else
				{
@@ -459,6 +459,22 @@ static int pkey_ec_ctrl_str(EVP_PKEY_CTX *ctx,
			return -2;
		return EVP_PKEY_CTX_set_ec_param_enc(ctx, param_enc);
		}
	else if (!strcmp(type, "ecdh_kdf_md"))
		{
		const EVP_MD *md;
		if (!(md = EVP_get_digestbyname(value)))
			{
			ECerr(EC_F_PKEY_EC_CTRL_STR, EC_R_INVALID_DIGEST);
			return 0;
			}
		return EVP_PKEY_CTX_set_ecdh_kdf_md(ctx, md);
		}
	else if (!strcmp(type, "ecdh_cofactor_mode"))
		{
		int co_mode;
		co_mode = atoi(value);
		return EVP_PKEY_CTX_set_ecdh_cofactor_mode(ctx, co_mode);
		}
			
	return -2;
	}