in crypto/apr_crypto_nss.c [550:823]
static apr_status_t crypto_key(apr_crypto_key_t **k,
const apr_crypto_key_rec_t *rec, const apr_crypto_t *f, apr_pool_t *p)
{
apr_status_t rv = APR_SUCCESS;
PK11SlotInfo *slot, *tslot;
PK11SymKey *tkey;
SECItem secretItem;
SECItem wrappedItem;
SECItem *secParam;
PK11Context *ctx;
SECStatus s;
SECItem passItem;
SECItem saltItem;
SECAlgorithmID *algid;
void *wincx = NULL; /* what is wincx? */
apr_crypto_key_t *key;
int blockSize;
int remainder;
key = *k;
if (!key) {
*k = key = apr_pcalloc(p, sizeof *key);
if (!key) {
return APR_ENOMEM;
}
apr_pool_cleanup_register(p, key, crypto_key_cleanup,
apr_pool_cleanup_null);
}
key->pool = p;
key->f = f;
key->provider = f->provider;
key->rec = rec;
switch (rec->ktype) {
case APR_CRYPTO_KTYPE_PASSPHRASE: {
/* decide on what cipher mechanism we will be using */
rv = crypto_cipher_mechanism(key, rec->type, rec->mode, rec->pad);
if (APR_SUCCESS != rv) {
return rv;
}
/* Turn the raw passphrase and salt into SECItems */
passItem.data = (unsigned char*) rec->k.passphrase.pass;
passItem.len = rec->k.passphrase.passLen;
saltItem.data = (unsigned char*) rec->k.passphrase.salt;
saltItem.len = rec->k.passphrase.saltLen;
/* generate the key */
/* pbeAlg and cipherAlg are the same. */
algid = PK11_CreatePBEV2AlgorithmID(key->cipherOid, key->cipherOid,
SEC_OID_HMAC_SHA1, key->keyLength,
rec->k.passphrase.iterations, &saltItem);
if (algid) {
slot = PK11_GetBestSlot(key->cipherMech, wincx);
if (slot) {
key->symKey = PK11_PBEKeyGen(slot, algid, &passItem, PR_FALSE,
wincx);
PK11_FreeSlot(slot);
}
SECOID_DestroyAlgorithmID(algid, PR_TRUE);
}
/* sanity check? */
if (!key->symKey) {
PRErrorCode perr = PORT_GetError();
if (perr) {
f->result->rc = perr;
f->result->msg = PR_ErrorToName(perr);
rv = APR_ENOKEY;
}
}
break;
}
case APR_CRYPTO_KTYPE_SECRET: {
/* decide on what cipher mechanism we will be using */
rv = crypto_cipher_mechanism(key, rec->type, rec->mode, rec->pad);
if (APR_SUCCESS != rv) {
return rv;
}
/*
* NSS is by default in FIPS mode, which disallows the use of unencrypted
* symmetrical keys. As per http://permalink.gmane.org/gmane.comp.mozilla.crypto/7947
* we do the following:
*
* 1. Generate a (temporary) symmetric key in NSS.
* 2. Use that symmetric key to encrypt your symmetric key as data.
* 3. Unwrap your wrapped symmetric key, using the symmetric key
* you generated in Step 1 as the unwrapping key.
*
* http://permalink.gmane.org/gmane.comp.mozilla.crypto/7947
*/
/* generate the key */
slot = PK11_GetBestSlot(key->cipherMech, NULL);
if (slot) {
unsigned char data[BUFFER_SIZE];
/* sanity check - key correct size? */
if (rec->k.secret.secretLen != key->keyLength) {
PK11_FreeSlot(slot);
return APR_EKEYLENGTH;
}
tslot = PK11_GetBestSlot(CKM_AES_ECB, NULL);
if (tslot) {
/* generate a temporary wrapping key */
tkey = PK11_KeyGen(tslot, CKM_AES_ECB, 0, PK11_GetBestKeyLength(tslot, CKM_AES_ECB), 0);
/* prepare the key to wrap */
secretItem.data = (unsigned char *) rec->k.secret.secret;
secretItem.len = rec->k.secret.secretLen;
/* ensure our key matches the blocksize */
secParam = PK11_GenerateNewParam(CKM_AES_ECB, tkey);
blockSize = PK11_GetBlockSize(CKM_AES_ECB, secParam);
remainder = rec->k.secret.secretLen % blockSize;
if (remainder) {
secretItem.data =
apr_pcalloc(p, rec->k.secret.secretLen + remainder);
apr_crypto_clear(p, secretItem.data,
rec->k.secret.secretLen);
memcpy(secretItem.data, rec->k.secret.secret,
rec->k.secret.secretLen);
secretItem.len += remainder;
}
/* prepare a space for the wrapped key */
wrappedItem.data = data;
/* wrap the key */
ctx = PK11_CreateContextBySymKey(CKM_AES_ECB, CKA_ENCRYPT, tkey,
secParam);
if (ctx) {
s = PK11_CipherOp(ctx, wrappedItem.data,
(int *) (&wrappedItem.len), BUFFER_SIZE,
secretItem.data, secretItem.len);
if (s == SECSuccess) {
/* unwrap the key again */
key->symKey = PK11_UnwrapSymKeyWithFlags(tkey,
CKM_AES_ECB, NULL, &wrappedItem,
key->cipherMech, CKA_ENCRYPT,
rec->k.secret.secretLen, 0);
}
PK11_DestroyContext(ctx, PR_TRUE);
}
/* clean up */
SECITEM_FreeItem(secParam, PR_TRUE);
PK11_FreeSymKey(tkey);
PK11_FreeSlot(tslot);
}
PK11_FreeSlot(slot);
}
/* sanity check? */
if (!key->symKey) {
PRErrorCode perr = PORT_GetError();
if (perr) {
f->result->rc = perr;
f->result->msg = PR_ErrorToName(perr);
rv = APR_ENOKEY;
}
}
break;
}
case APR_CRYPTO_KTYPE_HASH: {
switch (rec->k.hash.digest) {
case APR_CRYPTO_DIGEST_MD5:
key->hashAlg = SEC_OID_MD5;
break;
case APR_CRYPTO_DIGEST_SHA1:
key->hashAlg = SEC_OID_SHA1;
break;
case APR_CRYPTO_DIGEST_SHA224:
key->hashAlg = SEC_OID_SHA224;
break;
case APR_CRYPTO_DIGEST_SHA256:
key->hashAlg = SEC_OID_SHA256;
break;
case APR_CRYPTO_DIGEST_SHA384:
key->hashAlg = SEC_OID_SHA384;
break;
case APR_CRYPTO_DIGEST_SHA512:
key->hashAlg = SEC_OID_SHA512;
break;
default:
return APR_ENODIGEST;
}
break;
}
case APR_CRYPTO_KTYPE_HMAC: {
switch (rec->k.hmac.digest) {
case APR_CRYPTO_DIGEST_MD5:
key->hashMech = CKM_MD5_HMAC;
break;
case APR_CRYPTO_DIGEST_SHA1:
key->hashMech = CKM_SHA_1_HMAC;
break;
case APR_CRYPTO_DIGEST_SHA224:
key->hashMech = CKM_SHA224_HMAC;
break;
case APR_CRYPTO_DIGEST_SHA256:
key->hashMech = CKM_SHA256_HMAC;
break;
case APR_CRYPTO_DIGEST_SHA384:
key->hashMech = CKM_SHA384_HMAC;
break;
case APR_CRYPTO_DIGEST_SHA512:
key->hashMech = CKM_SHA512_HMAC;
break;
default:
return APR_ENODIGEST;
}
/* generate the key */
slot = PK11_GetBestSlot(key->hashMech, NULL);
if (slot) {
/* prepare the key to wrap */
secretItem.data = (unsigned char *) rec->k.hmac.secret;
secretItem.len = rec->k.hmac.secretLen;
key->symKey = PK11_ImportSymKey(slot, key->hashMech, PK11_OriginDerive,
CKA_SIGN, &secretItem, NULL);
/* sanity check? */
if (!key->symKey) {
PRErrorCode perr = PORT_GetError();
if (perr) {
f->result->rc = perr;
f->result->msg = PR_ErrorToName(perr);
rv = APR_ENOKEY;
}
}
PK11_FreeSlot(slot);
}
break;
}
case APR_CRYPTO_KTYPE_CMAC: {
return APR_ENOTIMPL;
}
default: {
return APR_ENOKEY;
}
}
return rv;
}