static gchar* cipher_pbkdf2_nss_sha1(const gchar *passphrase, const gchar *salt, guint iter_count, guint out_len) { PK11SlotInfo *slot; SECAlgorithmID *algorithm = NULL; PK11SymKey *symkey = NULL; const SECItem *symkey_data = NULL; SECItem salt_item, passphrase_item; guchar *passphrase_buff, *salt_buff; gchar *ret; g_return_val_if_fail(passphrase != NULL, NULL); g_return_val_if_fail(iter_count > 0, NULL); g_return_val_if_fail(out_len > 0, NULL); NSS_NoDB_Init(NULL); slot = PK11_GetBestSlot(PK11_AlgtagToMechanism(SEC_OID_PKCS5_PBKDF2), NULL); if (slot == NULL) { purple_debug_error("cipher-test", "NSS: couldn't get slot: " "%d\n", PR_GetError()); return NULL; } salt_buff = (guchar*)g_strdup(salt ? salt : ""); salt_item.type = siBuffer; salt_item.data = salt_buff; salt_item.len = salt ? strlen(salt) : 0; algorithm = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_AES_256_CBC, SEC_OID_HMAC_SHA1, out_len, iter_count, &salt_item); if (algorithm == NULL) { purple_debug_error("cipher-test", "NSS: couldn't create " "algorithm ID: %d\n", PR_GetError()); PK11_FreeSlot(slot); g_free(salt_buff); return NULL; } passphrase_buff = (guchar*)g_strdup(passphrase); passphrase_item.type = siBuffer; passphrase_item.data = passphrase_buff; passphrase_item.len = strlen(passphrase); symkey = PK11_PBEKeyGen(slot, algorithm, &passphrase_item, PR_FALSE, NULL); if (symkey == NULL) { purple_debug_error("cipher-test", "NSS: Couldn't generate key: " "%d\n", PR_GetError()); SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); PK11_FreeSlot(slot); g_free(passphrase_buff); g_free(salt_buff); return NULL; } if (PK11_ExtractKeyValue(symkey) == SECSuccess) symkey_data = PK11_GetKeyData(symkey); if (symkey_data == NULL || symkey_data->data == NULL) { purple_debug_error("cipher-test", "NSS: Couldn't extract key " "value: %d\n", PR_GetError()); PK11_FreeSymKey(symkey); SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); PK11_FreeSlot(slot); g_free(passphrase_buff); g_free(salt_buff); return NULL; } if (symkey_data->len != out_len) { purple_debug_error("cipher-test", "NSS: Invalid key length: %d " "(should be %d)\n", symkey_data->len, out_len); PK11_FreeSymKey(symkey); SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); PK11_FreeSlot(slot); g_free(passphrase_buff); g_free(salt_buff); return NULL; } ret = purple_base16_encode(symkey_data->data, symkey_data->len); PK11_FreeSymKey(symkey); SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); PK11_FreeSlot(slot); g_free(passphrase_buff); g_free(salt_buff); return ret; }
/** * @brief Create a key from the given passphrase. By default, the PBKDF2 * algorithm is used to generate the key from the passphrase. It is expected * that the same pass phrase will generate the same key, regardless of the * backend crypto platform used. The key is cleaned up when the context * is cleaned, and may be reused with multiple encryption or decryption * operations. * @note If *key is NULL, a apr_crypto_key_t will be created from a pool. If * *key is not NULL, *key must point at a previously created structure. * @param key The key returned, see note. * @param ivSize The size of the initialisation vector will be returned, based * on whether an IV is relevant for this type of crypto. * @param pass The passphrase to use. * @param passLen The passphrase length in bytes * @param salt The salt to use. * @param saltLen The salt length in bytes * @param type 3DES_192, AES_128, AES_192, AES_256. * @param mode Electronic Code Book / Cipher Block Chaining. * @param doPad Pad if necessary. * @param iterations Iteration count * @param f The context to use. * @param p The pool to use. * @return Returns APR_ENOKEY if the pass phrase is missing or empty, or if a backend * error occurred while generating the key. APR_ENOCIPHER if the type or mode * is not supported by the particular backend. APR_EKEYTYPE if the key type is * not known. APR_EPADDING if padding was requested but is not supported. * APR_ENOTIMPL if not implemented. */ static apr_status_t crypto_passphrase(apr_crypto_key_t **k, apr_size_t *ivSize, const char *pass, apr_size_t passLen, const unsigned char * salt, apr_size_t saltLen, const apr_crypto_block_key_type_e type, const apr_crypto_block_key_mode_e mode, const int doPad, const int iterations, const apr_crypto_t *f, apr_pool_t *p) { apr_status_t rv = APR_SUCCESS; PK11SlotInfo * slot; SECItem passItem; SECItem saltItem; SECAlgorithmID *algid; void *wincx = NULL; /* what is wincx? */ apr_crypto_key_t *key = *k; if (!key) { *k = key = apr_array_push(f->keys); } if (!key) { return APR_ENOMEM; } key->f = f; key->provider = f->provider; /* decide on what cipher mechanism we will be using */ switch (type) { case (APR_KEY_3DES_192): if (APR_MODE_CBC == mode) { key->cipherOid = SEC_OID_DES_EDE3_CBC; } else if (APR_MODE_ECB == mode) { return APR_ENOCIPHER; /* No OID for CKM_DES3_ECB; */ } break; case (APR_KEY_AES_128): if (APR_MODE_CBC == mode) { key->cipherOid = SEC_OID_AES_128_CBC; } else { key->cipherOid = SEC_OID_AES_128_ECB; } break; case (APR_KEY_AES_192): if (APR_MODE_CBC == mode) { key->cipherOid = SEC_OID_AES_192_CBC; } else { key->cipherOid = SEC_OID_AES_192_ECB; } break; case (APR_KEY_AES_256): if (APR_MODE_CBC == mode) { key->cipherOid = SEC_OID_AES_256_CBC; } else { key->cipherOid = SEC_OID_AES_256_ECB; } break; default: /* unknown key type, give up */ return APR_EKEYTYPE; } /* AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD */ key->cipherMech = PK11_AlgtagToMechanism(key->cipherOid); if (key->cipherMech == CKM_INVALID_MECHANISM) { return APR_ENOCIPHER; } if (doPad) { CK_MECHANISM_TYPE paddedMech; paddedMech = PK11_GetPadMechanism(key->cipherMech); if (CKM_INVALID_MECHANISM == paddedMech || key->cipherMech == paddedMech) { return APR_EPADDING; } key->cipherMech = paddedMech; } /* Turn the raw passphrase and salt into SECItems */ passItem.data = (unsigned char*) pass; passItem.len = passLen; saltItem.data = (unsigned char*) salt; saltItem.len = saltLen; /* generate the key */ /* pbeAlg and cipherAlg are the same. NSS decides the keylength. */ algid = PK11_CreatePBEV2AlgorithmID(key->cipherOid, key->cipherOid, SEC_OID_HMAC_SHA1, 0, 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; } } key->ivSize = PK11_GetIVLength(key->cipherMech); if (ivSize) { *ivSize = key->ivSize; } return rv; }