From dc1ce3bc64845d16314af1f11acf5384e4ae9b34 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Tue, 30 Jul 2013 18:05:08 +0100 Subject: [PATCH] Add KDF for DH. Add X9.42 DH KDF. Move sharedinfo generation code to CMS library as the same structure is used by DH and ECDH. Move ASN1_OBJECT typedef to ossl_typ.h so it can be picked up by dh headers without the need to use ASN1. --- crypto/asn1/asn1.h | 4 +- crypto/cms/cms.h | 3 + crypto/cms/cms_asn1.c | 43 +++++++++ crypto/dh/Makefile | 4 +- crypto/dh/dh.h | 7 ++ crypto/dh/dh_kdf.c | 197 ++++++++++++++++++++++++++++++++++++++++++ crypto/ec/ec_ameth.c | 78 ++++------------- crypto/ossl_typ.h | 2 + 8 files changed, 274 insertions(+), 64 deletions(-) create mode 100644 crypto/dh/dh_kdf.c diff --git a/crypto/asn1/asn1.h b/crypto/asn1/asn1.h index accc1e946b..8bc24d3225 100644 --- a/crypto/asn1/asn1.h +++ b/crypto/asn1/asn1.h @@ -208,14 +208,14 @@ typedef struct asn1_const_ctx_st #define ASN1_OBJECT_FLAG_CRITICAL 0x02 /* critical x509v3 object id */ #define ASN1_OBJECT_FLAG_DYNAMIC_STRINGS 0x04 /* internal use */ #define ASN1_OBJECT_FLAG_DYNAMIC_DATA 0x08 /* internal use */ -typedef struct asn1_object_st +struct asn1_object_st { const char *sn,*ln; int nid; int length; const unsigned char *data; /* data remains const after init */ int flags; /* Should we free this one */ - } ASN1_OBJECT; + }; #define ASN1_STRING_FLAG_BITS_LEFT 0x08 /* Set if 0x07 has bits left value */ /* This indicates that the ASN1_STRING is not a real value but just a place diff --git a/crypto/cms/cms.h b/crypto/cms/cms.h index f644cbfbc4..8b1d29f0c1 100644 --- a/crypto/cms/cms.h +++ b/crypto/cms/cms.h @@ -364,6 +364,9 @@ EVP_CIPHER_CTX *CMS_RecipientInfo_kari_get0_ctx(CMS_RecipientInfo *ri); int CMS_RecipientInfo_kari_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri, CMS_RecipientEncryptedKey *rek); +int CMS_SharedInfo_encode(unsigned char **pder, X509_ALGOR *kekalg, + ASN1_OCTET_STRING *ukm, int keylen); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. diff --git a/crypto/cms/cms_asn1.c b/crypto/cms/cms_asn1.c index 7d79db221a..83ae2cc0f3 100644 --- a/crypto/cms/cms_asn1.c +++ b/crypto/cms/cms_asn1.c @@ -423,3 +423,46 @@ ASN1_SEQUENCE(CMS_Receipt) = { ASN1_SIMPLE(CMS_Receipt, originatorSignatureValue, ASN1_OCTET_STRING) } ASN1_SEQUENCE_END(CMS_Receipt) +/* Utilities to encode the CMS_SharedInfo structure used during key + * derivation. + */ + +typedef struct { + X509_ALGOR *keyInfo; + ASN1_OCTET_STRING *entityUInfo; + ASN1_OCTET_STRING *suppPubInfo; +} CMS_SharedInfo; + +ASN1_SEQUENCE(CMS_SharedInfo) = { + ASN1_SIMPLE(CMS_SharedInfo, keyInfo, X509_ALGOR), + ASN1_EXP_OPT(CMS_SharedInfo, entityUInfo, ASN1_OCTET_STRING, 0), + ASN1_EXP_OPT(CMS_SharedInfo, suppPubInfo, ASN1_OCTET_STRING, 2), +} ASN1_SEQUENCE_END(CMS_SharedInfo) + +int CMS_SharedInfo_encode(unsigned char **pder, X509_ALGOR *kekalg, + ASN1_OCTET_STRING *ukm, int keylen) + { + union { + CMS_SharedInfo *pecsi; + ASN1_VALUE *a; + } intsi = {NULL}; + + ASN1_OCTET_STRING oklen; + unsigned char kl[4]; + 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; + return ASN1_item_i2d(intsi.a, pder, ASN1_ITEM_rptr(CMS_SharedInfo)); + } diff --git a/crypto/dh/Makefile b/crypto/dh/Makefile index 9dd08d617b..ca01f373b4 100644 --- a/crypto/dh/Makefile +++ b/crypto/dh/Makefile @@ -18,9 +18,9 @@ APPS= LIB=$(TOP)/libcrypto.a LIBSRC= dh_asn1.c dh_gen.c dh_key.c dh_lib.c dh_check.c dh_err.c dh_depr.c \ - dh_ameth.c dh_pmeth.c dh_prn.c dh_rfc5114.c + dh_ameth.c dh_pmeth.c dh_prn.c dh_rfc5114.c dh_kdf.c LIBOBJ= dh_asn1.o dh_gen.o dh_key.o dh_lib.o dh_check.o dh_err.o dh_depr.o \ - dh_ameth.o dh_pmeth.o dh_prn.o dh_rfc5114.o + dh_ameth.o dh_pmeth.o dh_prn.o dh_rfc5114.o dh_kdf.o SRC= $(LIBSRC) diff --git a/crypto/dh/dh.h b/crypto/dh/dh.h index 53f9f26566..a86c64db01 100644 --- a/crypto/dh/dh.h +++ b/crypto/dh/dh.h @@ -239,6 +239,13 @@ DH *DH_get_1024_160(void); DH *DH_get_2048_224(void); DH *DH_get_2048_256(void); +/* RFC2631 KDF */ +int DH_KDF_X9_42(unsigned char *out, size_t outlen, + const unsigned char *Z, size_t Zlen, + ASN1_OBJECT *key_oid, + const unsigned char *ukm, size_t ukmlen, + const EVP_MD *md); + #define EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, len) \ EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN, \ EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN, len, NULL) diff --git a/crypto/dh/dh_kdf.c b/crypto/dh/dh_kdf.c new file mode 100644 index 0000000000..dbdd0b9ba6 --- /dev/null +++ b/crypto/dh/dh_kdf.c @@ -0,0 +1,197 @@ +/* crypto/dh/dh_kdf.c */ +/* + * Written by Stephen Henson for the OpenSSL project. + */ +/* ==================================================================== + * Copyright (c) 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include +#include +#include +#include +#include + + +/* Key derivation from X9.42/RFC2631 */ + +#define DH_KDF_MAX (1L << 30) + +/* Skip past an ASN1 structure: for OBJECT skip content octets too */ + +static int skip_asn1(unsigned char **pp, long *plen, int exptag) + { + const unsigned char *q = *pp; + int i, tag, xclass; + long tmplen; + i = ASN1_get_object(&q, &tmplen, &tag, &xclass, *plen); + if (i & 0x80) + return 0; + if (tag != exptag || xclass != V_ASN1_UNIVERSAL) + return 0; + if (tag == V_ASN1_OBJECT) + q += tmplen; + *plen -= q - *pp; + *pp = (unsigned char *)q; + return 1; + } + +/* Encode the DH shared info structure, return an offset to the counter + * value so we can update the structure without reencoding it. + */ + + +static int dh_sharedinfo_encode(unsigned char **pder, unsigned char **pctr, + ASN1_OBJECT *key_oid, size_t outlen, + const unsigned char *ukm, size_t ukmlen) + { + unsigned char *p; + int derlen; + long tlen; + /* "magic" value to check offset is sane */ + static unsigned char ctr[4] = {0xF3, 0x17, 0x22, 0x53}; + X509_ALGOR atmp; + ASN1_OCTET_STRING ctr_oct, ukm_oct, *pukm_oct; + ASN1_TYPE ctr_atype; + if (ukmlen > DH_KDF_MAX || outlen > DH_KDF_MAX) + return 0; + ctr_oct.data = ctr; + ctr_oct.length = 4; + ctr_oct.flags = 0; + ctr_oct.type = V_ASN1_OCTET_STRING; + ctr_atype.type = V_ASN1_OCTET_STRING; + ctr_atype.value.octet_string = &ctr_oct; + atmp.algorithm = key_oid; + atmp.parameter = &ctr_atype; + if (ukm) + { + ukm_oct.type = V_ASN1_OCTET_STRING; + ukm_oct.flags = 0; + ukm_oct.data = (unsigned char *)ukm; + ukm_oct.length = ukmlen; + pukm_oct = &ukm_oct; + } + else + pukm_oct = NULL; + derlen = CMS_SharedInfo_encode(pder, &atmp, pukm_oct, outlen); + if (derlen <= 0) + return 0; + p = *pder; + tlen = derlen; + if (!skip_asn1(&p, &tlen, V_ASN1_SEQUENCE)) + return 0; + if (!skip_asn1(&p, &tlen, V_ASN1_SEQUENCE)) + return 0; + if (!skip_asn1(&p, &tlen, V_ASN1_OBJECT)) + return 0; + if (!skip_asn1(&p, &tlen, V_ASN1_OCTET_STRING)) + return 0; + if (memcmp(p, ctr, 4)) + return 0; + *pctr = p; + return derlen; + } + +int DH_KDF_X9_42(unsigned char *out, size_t outlen, + const unsigned char *Z, size_t Zlen, + ASN1_OBJECT *key_oid, + const unsigned char *ukm, size_t ukmlen, + const EVP_MD *md) + { + EVP_MD_CTX mctx; + int rv = 0; + unsigned int i; + size_t mdlen; + unsigned char *der = NULL, *ctr; + int derlen; + if (Zlen > DH_KDF_MAX) + return 0; + mdlen = EVP_MD_size(md); + EVP_MD_CTX_init(&mctx); + derlen = dh_sharedinfo_encode(&der, &ctr, key_oid, outlen, + ukm, ukmlen); + if (derlen == 0) + goto err; + for (i = 1;;i++) + { + unsigned char mtmp[EVP_MAX_MD_SIZE]; + EVP_DigestInit_ex(&mctx, md, NULL); + if (!EVP_DigestUpdate(&mctx, Z, Zlen)) + goto err; + ctr[3] = i & 0xFF; + ctr[2] = (i >> 8) & 0xFF; + ctr[1] = (i >> 16) & 0xFF; + ctr[0] = (i >> 24) & 0xFF; + if (!EVP_DigestUpdate(&mctx, der, derlen)) + goto err; + if (outlen >= mdlen) + { + if (!EVP_DigestFinal(&mctx, out, NULL)) + goto err; + outlen -= mdlen; + if (outlen == 0) + break; + out += mdlen; + } + else + { + if (!EVP_DigestFinal(&mctx, mtmp, NULL)) + goto err; + memcpy(out, mtmp, outlen); + OPENSSL_cleanse(mtmp, mdlen); + break; + } + } + rv = 1; + err: + if (der) + OPENSSL_free(der); + EVP_MD_CTX_cleanup(&mctx); + return rv; + } + diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c index d757fd61ef..f024f90497 100644 --- a/crypto/ec/ec_ameth.c +++ b/crypto/ec/ec_ameth.c @@ -764,63 +764,6 @@ static int ecdh_cms_set_kdf_param(EVP_PKEY_CTX *pctx, int eckdf_nid) 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; @@ -828,6 +771,7 @@ static int ecdh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) X509_ALGOR *alg, *kekalg = NULL; ASN1_OCTET_STRING *ukm; const unsigned char *p; + unsigned char *der = NULL; int plen, keylen; const EVP_CIPHER *kekcipher; EVP_CIPHER_CTX *kekctx; @@ -864,13 +808,21 @@ static int ecdh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(pctx, keylen) <= 0) goto err; - if (!ecdh_cms_set_ukm(pctx, kekalg, ukm, keylen)) + plen = CMS_SharedInfo_encode(&der, kekalg, ukm, keylen); + + if (!plen) goto err; + if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, der, plen) <= 0) + goto err; + der = NULL; + rv = 1; err: if (kekalg) X509_ALGOR_free(kekalg); + if (der) + OPENSSL_free(der); return rv; } @@ -1019,13 +971,19 @@ static int ecdh_cms_encrypt(CMS_RecipientInfo *ri) if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(pctx, keylen) <= 0) goto err; - if (!ecdh_cms_set_ukm(pctx, wrap_alg, ukm, keylen)) + + penclen = CMS_SharedInfo_encode(&penc, wrap_alg, ukm, keylen); + + if (!penclen) goto err; + if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, penc, penclen) <= 0) + goto err; + penc = NULL; + /* 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; diff --git a/crypto/ossl_typ.h b/crypto/ossl_typ.h index 9e6995d37f..e78a0347db 100644 --- a/crypto/ossl_typ.h +++ b/crypto/ossl_typ.h @@ -96,6 +96,8 @@ typedef int ASN1_BOOLEAN; typedef int ASN1_NULL; #endif +typedef struct asn1_object_st ASN1_OBJECT; + typedef struct ASN1_ITEM_st ASN1_ITEM; typedef struct asn1_pctx_st ASN1_PCTX; typedef struct asn1_sctx_st ASN1_SCTX; -- 2.40.0