static apr_status_t crypto_key()

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;
}