Commit 856640b5 authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

Extend PBE code to support non default PKCS#5 v2.0 PRFs.

parent 76240b3a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -4,6 +4,9 @@

 Changes between 0.9.8b and 0.9.9  [xx XXX xxxx]

  *) Initial support for PKCS#5 v2.0 PRFs other than default SHA1 HMAC.
     [Steve Henson]

  *) Replace the algorithm specific calls to generate keys in "req" with the
     new API.
     [Steve Henson]
+12 −0
Original line number Diff line number Diff line
@@ -799,8 +799,20 @@ void PKCS5_PBE_add(void);

int EVP_PBE_CipherInit (ASN1_OBJECT *pbe_obj, const char *pass, int passlen,
	     ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de);

/* PBE type */

/* Can appear as the outermost AlgorithmIdentifier */
#define EVP_PBE_TYPE_OUTER	0x0
/* Is an PRF type OID */
#define EVP_PBE_TYPE_PRF	0x1

int EVP_PBE_alg_add_type(int pbe_type, int pbe_nid, int cipher_nid, int md_nid,
	     EVP_PBE_KEYGEN *keygen);
int EVP_PBE_alg_add(int nid, const EVP_CIPHER *cipher, const EVP_MD *md,
		    EVP_PBE_KEYGEN *keygen);
int EVP_PBE_find(int type, int pbe_nid,
			int *pcnid, int *pmnid, EVP_PBE_KEYGEN **pkeygen);
void EVP_PBE_cleanup(void);

#define ASN1_PKEY_ALIAS		0x1
+101 −37
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
 * project 1999.
 */
/* ====================================================================
 * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
 * Copyright (c) 1999-2006 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
@@ -67,24 +67,26 @@ static STACK *pbe_algs;

/* Setup a cipher context from a PBE algorithm */

typedef struct {
typedef struct
	{
	int pbe_type;
	int pbe_nid;
const EVP_CIPHER *cipher;
const EVP_MD *md;
	int cipher_nid;
	int md_nid;
	EVP_PBE_KEYGEN *keygen;
	} EVP_PBE_CTL;

int EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen,
	     ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de)
	{
	const EVP_CIPHER *cipher;
	const EVP_MD *md;
	int cipher_nid, md_nid;
	EVP_PBE_KEYGEN *keygen;

	EVP_PBE_CTL *pbetmp, pbelu;
	int i;
	pbelu.pbe_nid = OBJ_obj2nid(pbe_obj);
	if (pbelu.pbe_nid != NID_undef) i = sk_find(pbe_algs, (char *)&pbelu);
	else i = -1;

	if (i == -1) {
	if (!EVP_PBE_find(EVP_PBE_TYPE_OUTER, OBJ_obj2nid(pbe_obj),
					&cipher_nid, &md_nid, &keygen))
		{
		char obj_tmp[80];
		EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_UNKNOWN_PBE_ALGORITHM);
		if (!pbe_obj) BUF_strlcpy (obj_tmp, "NULL", sizeof obj_tmp);
@@ -92,12 +94,24 @@ int EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen,
		ERR_add_error_data(2, "TYPE=", obj_tmp);
		return 0;
		}
	if(!pass) passlen = 0;
	else if (passlen == -1) passlen = strlen(pass);
	pbetmp = (EVP_PBE_CTL *)sk_value (pbe_algs, i);
	i = (*pbetmp->keygen)(ctx, pass, passlen, param, pbetmp->cipher,
						 pbetmp->md, en_de);
	if (!i) {

	if(!pass)
		passlen = 0;
	else if (passlen == -1)
		passlen = strlen(pass);

	if (cipher_nid == -1)
		cipher = NULL;
	else
		cipher = EVP_get_cipherbynid(cipher_nid);

	if (md_nid == -1)
		md = NULL;
	else
		md = EVP_get_digestbynid(md_nid);

	if (!keygen(ctx, pass, passlen, param, cipher, md, en_de))
		{
		EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_KEYGEN_FAILURE);
		return 0;
		}
@@ -108,28 +122,78 @@ static int pbe_cmp(const char * const *a, const char * const *b)
	{
	const EVP_PBE_CTL * const *pbe1 = (const EVP_PBE_CTL * const *) a,
			* const *pbe2 = (const EVP_PBE_CTL * const *)b;
	return ((*pbe1)->pbe_nid - (*pbe2)->pbe_nid);
	int ret = (*pbe1)->pbe_type - (*pbe2)->pbe_type;
	if (ret)
		return ret;
	else
		return (*pbe1)->pbe_nid - (*pbe2)->pbe_nid;
	}

/* Add a PBE algorithm */

int EVP_PBE_alg_add(int nid, const EVP_CIPHER *cipher, const EVP_MD *md,
int EVP_PBE_alg_add_type(int pbe_type, int pbe_nid, int cipher_nid, int md_nid,
	     EVP_PBE_KEYGEN *keygen)
	{
	EVP_PBE_CTL *pbe_tmp;
	if (!pbe_algs) pbe_algs = sk_new(pbe_cmp);
	if (!(pbe_tmp = (EVP_PBE_CTL*) OPENSSL_malloc (sizeof(EVP_PBE_CTL)))) {
	if (!pbe_algs)
		pbe_algs = sk_new(pbe_cmp);
	if (!(pbe_tmp = (EVP_PBE_CTL*) OPENSSL_malloc (sizeof(EVP_PBE_CTL))))
		{
		EVPerr(EVP_F_EVP_PBE_ALG_ADD,ERR_R_MALLOC_FAILURE);
		return 0;
		}
	pbe_tmp->pbe_nid = nid;
	pbe_tmp->cipher = cipher;
	pbe_tmp->md = md;
	pbe_tmp->pbe_type = pbe_type;
	pbe_tmp->pbe_nid = pbe_nid;
	pbe_tmp->cipher_nid = cipher_nid;
	pbe_tmp->md_nid = md_nid;
	pbe_tmp->keygen = keygen;


	sk_push (pbe_algs, (char *)pbe_tmp);
	return 1;
	}

int EVP_PBE_alg_add(int nid, const EVP_CIPHER *cipher, const EVP_MD *md,
	     EVP_PBE_KEYGEN *keygen)
	{
	int cipher_nid, md_nid;
	if (cipher)
		cipher_nid = EVP_CIPHER_type(cipher);
	else
		cipher_nid = -1;
	if (md)
		md_nid = EVP_MD_type(md);
	else
		md_nid = -1;

	return EVP_PBE_alg_add_type(EVP_PBE_TYPE_OUTER, nid,
					cipher_nid, md_nid, keygen);
	}

int EVP_PBE_find(int type, int pbe_nid,
			int *pcnid, int *pmnid, EVP_PBE_KEYGEN **pkeygen)
	{
	EVP_PBE_CTL *pbetmp, pbelu;
	int i;
	if (pbe_nid == NID_undef)
		return 0;
	pbelu.pbe_type = type;
	pbelu.pbe_nid = pbe_nid;
	i = sk_find(pbe_algs, (char *)&pbelu);
	if (i == -1)
		return 0;
	pbetmp = (EVP_PBE_CTL *)sk_value (pbe_algs, i);
	if (pcnid)
		*pcnid = pbetmp->cipher_nid;
	if (pmnid)
		*pmnid = pbetmp->md_nid;
	if (pkeygen)
		*pkeygen = pbetmp->keygen;
	return 1;
	}
		


void EVP_PBE_cleanup(void)
	{
	sk_pop_free(pbe_algs, OPENSSL_freeFunc);
+1 −0
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ EVP_PBE_alg_add(NID_pbeWithSHA1AndRC2_CBC, EVP_rc2_64_cbc(), EVP_sha1(),
#endif
#ifndef OPENSSL_NO_HMAC
EVP_PBE_alg_add(NID_pbes2, NULL, NULL, PKCS5_v2_PBE_keyivgen);
EVP_PBE_alg_add_type(EVP_PBE_TYPE_PRF, NID_hmacWithSHA1, -1, NID_sha1, 0);
#endif
}

+57 −23
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
 * project 1999.
 */
/* ====================================================================
 * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
 * Copyright (c) 1999-2006 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
@@ -71,28 +71,36 @@
#endif

/* This is an implementation of PKCS#5 v2.0 password based encryption key
 * derivation function PBKDF2 using the only currently defined function HMAC
 * with SHA1. Verified against test vectors posted by Peter Gutmann
 * derivation function PBKDF2.
 * SHA1 version verified against test vectors posted by Peter Gutmann
 * <pgut001@cs.auckland.ac.nz> to the PKCS-TNG <pkcs-tng@rsa.com> mailing list.
 */

int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen,
int PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
			   const unsigned char *salt, int saltlen, int iter,
			   const EVP_MD *digest,
			   int keylen, unsigned char *out)
	{
	unsigned char digtmp[SHA_DIGEST_LENGTH], *p, itmp[4];
	int cplen, j, k, tkeylen;
	unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4];
	int cplen, j, k, tkeylen, mdlen;
	unsigned long i = 1;
	HMAC_CTX hctx;

	mdlen = EVP_MD_size(digest);

	HMAC_CTX_init(&hctx);
	p = out;
	tkeylen = keylen;
	if(!pass) passlen = 0;
	else if(passlen == -1) passlen = strlen(pass);
	while(tkeylen) {
		if(tkeylen > SHA_DIGEST_LENGTH) cplen = SHA_DIGEST_LENGTH;
		else cplen = tkeylen;
	if(!pass)
		passlen = 0;
	else if(passlen == -1)
		passlen = strlen(pass);
	while(tkeylen)
		{
		if(tkeylen > mdlen)
			cplen = mdlen;
		else
			cplen = tkeylen;
		/* We are unlikely to ever use more than 256 blocks (5120 bits!)
		 * but just in case...
		 */
@@ -100,15 +108,17 @@ int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen,
		itmp[1] = (unsigned char)((i >> 16) & 0xff);
		itmp[2] = (unsigned char)((i >> 8) & 0xff);
		itmp[3] = (unsigned char)(i & 0xff);
		HMAC_Init_ex(&hctx, pass, passlen, EVP_sha1(), NULL);
		HMAC_Init_ex(&hctx, pass, passlen, digest, NULL);
		HMAC_Update(&hctx, salt, saltlen);
		HMAC_Update(&hctx, itmp, 4);
		HMAC_Final(&hctx, digtmp, NULL);
		memcpy(p, digtmp, cplen);
		for(j = 1; j < iter; j++) {
			HMAC(EVP_sha1(), pass, passlen,
				 digtmp, SHA_DIGEST_LENGTH, digtmp, NULL);
			for(k = 0; k < cplen; k++) p[k] ^= digtmp[k];
		for(j = 1; j < iter; j++)
			{
			HMAC(digest, pass, passlen,
				 digtmp, mdlen, digtmp, NULL);
			for(k = 0; k < cplen; k++)
				p[k] ^= digtmp[k];
			}
		tkeylen-= cplen;
		i++;
@@ -127,6 +137,14 @@ int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen,
	return 1;
	}

int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen,
			   const unsigned char *salt, int saltlen, int iter,
			   int keylen, unsigned char *out)
	{
	return PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, EVP_sha1(),
					keylen, out);
	}

#ifdef DO_TEST
main()
{
@@ -155,6 +173,8 @@ int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen,
	PBE2PARAM *pbe2 = NULL;
	const EVP_CIPHER *cipher;
	PBKDF2PARAM *kdf = NULL;
	const EVP_MD *prfmd;
	int prf_nid, hmac_md_nid;

	if (param == NULL || param->type != V_ASN1_SEQUENCE ||
	    param->value.sequence == NULL) {
@@ -226,7 +246,20 @@ int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen,
		goto err;
	}

	if(kdf->prf && (OBJ_obj2nid(kdf->prf->algorithm) != NID_hmacWithSHA1)) {
	if (kdf->prf)
		prf_nid = OBJ_obj2nid(kdf->prf->algorithm);
	else
		prf_nid = NID_hmacWithSHA1;

	if (!EVP_PBE_find(EVP_PBE_TYPE_PRF, prf_nid, NULL, &hmac_md_nid, 0))
		{
		EVPerr(EVP_F_PKCS5_V2_PBE_KEYIVGEN, EVP_R_UNSUPPORTED_PRF);
		goto err;
		}

	prfmd = EVP_get_digestbynid(hmac_md_nid);
	if (prfmd == NULL)
		{
		EVPerr(EVP_F_PKCS5_V2_PBE_KEYIVGEN, EVP_R_UNSUPPORTED_PRF);
		goto err;
		}
@@ -241,7 +274,8 @@ int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen,
	salt = kdf->salt->value.octet_string->data;
	saltlen = kdf->salt->value.octet_string->length;
	iter = ASN1_INTEGER_get(kdf->iter);
	PKCS5_PBKDF2_HMAC_SHA1(pass, passlen, salt, saltlen, iter, keylen, key);
	PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, prfmd,
								keylen, key);
	EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, en_de);
	OPENSSL_cleanse(key, keylen);
	PBKDF2PARAM_free(kdf);
Loading