char * crypto_encrypt (const char *cipher, const guint8 *data, gsize data_len, const char *iv, gsize iv_len, const char *key, gsize key_len, gsize *out_len, GError **error) { SECStatus ret; CK_MECHANISM_TYPE cipher_mech = CKM_DES3_CBC_PAD; PK11SlotInfo *slot = NULL; SECItem key_item = { .data = (unsigned char *) key, .len = key_len }; SECItem iv_item = { .data = (unsigned char *) iv, .len = iv_len }; PK11SymKey *sym_key = NULL; SECItem *sec_param = NULL; PK11Context *ctx = NULL; unsigned char *output, *padded_buf; gsize output_len; int encrypted_len = 0, i; gboolean success = FALSE; gsize padded_buf_len, pad_len; if (!crypto_init (error)) return NULL; if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) cipher_mech = CKM_DES3_CBC_PAD; else if (!strcmp (cipher, CIPHER_AES_CBC)) cipher_mech = CKM_AES_CBC_PAD; else { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_UNKNOWN_CIPHER, _("Private key cipher '%s' was unknown."), cipher); return NULL; } /* If data->len % ivlen == 0, then we add another complete block * onto the end so that the decrypter knows there's padding. */ pad_len = iv_len - (data_len % iv_len); output_len = padded_buf_len = data_len + pad_len; padded_buf = g_malloc0 (padded_buf_len); memcpy (padded_buf, data, data_len); for (i = 0; i < pad_len; i++) padded_buf[data_len + i] = (guint8) (pad_len & 0xFF); output = g_malloc0 (output_len); slot = PK11_GetBestSlot (cipher_mech, NULL); if (!slot) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_FAILED, _("Failed to initialize the encryption cipher slot.")); goto out; } sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); if (!sym_key) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to set symmetric key for encryption.")); goto out; } sec_param = PK11_ParamFromIV (cipher_mech, &iv_item); if (!sec_param) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to set IV for encryption.")); goto out; } ctx = PK11_CreateContextBySymKey (cipher_mech, CKA_ENCRYPT, sym_key, sec_param); if (!ctx) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to initialize the encryption context.")); goto out; } ret = PK11_CipherOp (ctx, output, &encrypted_len, output_len, padded_buf, padded_buf_len); if (ret != SECSuccess) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to encrypt: %d."), PORT_GetError ()); goto out; } if (encrypted_len != output_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Unexpected amount of data after encrypting.")); goto out; } *out_len = encrypted_len; success = TRUE; out: if (ctx) PK11_DestroyContext (ctx, PR_TRUE); if (sym_key) PK11_FreeSymKey (sym_key); if (sec_param) SECITEM_FreeItem (sec_param, PR_TRUE); if (slot) PK11_FreeSlot (slot); memset (padded_buf, 0, padded_buf_len); g_free (padded_buf); if (!success) { memset (output, 0, output_len); g_free (output); output = NULL; } return (char *) output; } NMCryptoFileFormat crypto_verify_cert (const unsigned char *data, gsize len, GError **error) { CERTCertificate *cert; if (!crypto_init (error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; /* Try DER/PEM first */ cert = CERT_DecodeCertFromPackage ((char *) data, len); if (!cert) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode certificate: %d"), PORT_GetError()); return NM_CRYPTO_FILE_FORMAT_UNKNOWN; } CERT_DestroyCertificate (cert); return NM_CRYPTO_FILE_FORMAT_X509; } gboolean crypto_verify_pkcs12 (const guint8 *data, gsize data_len, const char *password, GError **error) { SEC_PKCS12DecoderContext *p12ctx = NULL; SECItem pw = { 0 }; PK11SlotInfo *slot = NULL; SECStatus s; gunichar2 *ucs2_password; glong ucs2_chars = 0; #ifndef WORDS_BIGENDIAN guint16 *p; #endif /* WORDS_BIGENDIAN */ if (error) g_return_val_if_fail (*error == NULL, FALSE); if (!crypto_init (error)) return FALSE; /* PKCS#12 passwords are apparently UCS2 BIG ENDIAN, and NSS doesn't do * any conversions for us. */ if (password && *password) { if (!g_utf8_validate (password, -1, NULL)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_PASSWORD, _("Password must be UTF-8")); return FALSE; } ucs2_password = g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL); /* Can't fail if g_utf8_validate() succeeded */ g_return_val_if_fail (ucs2_password != NULL && ucs2_chars != 0, FALSE); ucs2_chars *= 2; /* convert # UCS2 characters -> bytes */ pw.data = PORT_ZAlloc(ucs2_chars + 2); memcpy (pw.data, ucs2_password, ucs2_chars); pw.len = ucs2_chars + 2; /* include terminating NULL */ memset (ucs2_password, 0, ucs2_chars); g_free (ucs2_password); #ifndef WORDS_BIGENDIAN for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++) *p = GUINT16_SWAP_LE_BE (*p); #endif /* WORDS_BIGENDIAN */ } else { /* NULL password */ pw.data = NULL; pw.len = 0; } slot = PK11_GetInternalKeySlot(); p12ctx = SEC_PKCS12DecoderStart (&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL); if (!p12ctx) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_FAILED, _("Couldn't initialize PKCS#12 decoder: %d"), PORT_GetError()); goto error; } s = SEC_PKCS12DecoderUpdate (p12ctx, (guint8 *)data, data_len); if (s != SECSuccess) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode PKCS#12 file: %d"), PORT_GetError()); goto error; } s = SEC_PKCS12DecoderVerify (p12ctx); if (s != SECSuccess) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Couldn't verify PKCS#12 file: %d"), PORT_GetError()); goto error; } SEC_PKCS12DecoderFinish (p12ctx); SECITEM_ZfreeItem (&pw, PR_FALSE); return TRUE; error: if (p12ctx) SEC_PKCS12DecoderFinish (p12ctx); if (slot) PK11_FreeSlot(slot); SECITEM_ZfreeItem (&pw, PR_FALSE); return FALSE; }
nsresult nsPKCS12Blob::ImportFromFileHelper(nsILocalFile *file, nsPKCS12Blob::ImportMode aImportMode, nsPKCS12Blob::RetryReason &aWantRetry) { nsNSSShutDownPreventionLock locker; nsresult rv; SECStatus srv = SECSuccess; SEC_PKCS12DecoderContext *dcx = NULL; SECItem unicodePw; PK11SlotInfo *slot=nsnull; nsXPIDLString tokenName; unicodePw.data = NULL; aWantRetry = rr_do_not_retry; if (aImportMode == im_try_zero_length_secitem) { unicodePw.len = 0; } else { // get file password (unicode) rv = getPKCS12FilePassword(&unicodePw); if (NS_FAILED(rv)) goto finish; if (unicodePw.data == NULL) { handleError(PIP_PKCS12_USER_CANCELED); return NS_OK; } } mToken->GetTokenName(getter_Copies(tokenName)); { NS_ConvertUTF16toUTF8 tokenNameCString(tokenName); slot = PK11_FindSlotByName(tokenNameCString.get()); } if (!slot) { srv = SECFailure; goto finish; } // initialize the decoder dcx = SEC_PKCS12DecoderStart(&unicodePw, slot, NULL, digest_open, digest_close, digest_read, digest_write, this); if (!dcx) { srv = SECFailure; goto finish; } // read input file and feed it to the decoder rv = inputToDecoder(dcx, file); if (NS_FAILED(rv)) { if (NS_ERROR_ABORT == rv) { // inputToDecoder indicated a NSS error srv = SECFailure; } goto finish; } // verify the blob srv = SEC_PKCS12DecoderVerify(dcx); if (srv) goto finish; // validate bags srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision); if (srv) goto finish; // import cert and key srv = SEC_PKCS12DecoderImportBags(dcx); if (srv) goto finish; // Later - check to see if this should become default email cert handleError(PIP_PKCS12_RESTORE_OK); finish: // If srv != SECSuccess, NSS probably set a specific error code. // We should use that error code instead of inventing a new one // for every error possible. if (srv != SECSuccess) { if (SEC_ERROR_BAD_PASSWORD == PORT_GetError()) { if (unicodePw.len == sizeof(PRUnichar)) { // no password chars available, // unicodeToItem allocated space for the trailing zero character only. aWantRetry = rr_auto_retry_empty_password_flavors; } else { aWantRetry = rr_bad_password; handleError(PIP_PKCS12_NSS_ERROR); } } else { handleError(PIP_PKCS12_NSS_ERROR); } } else if (NS_FAILED(rv)) { handleError(PIP_PKCS12_RESTORE_FAILED); } if (slot) PK11_FreeSlot(slot); // finish the decoder if (dcx) SEC_PKCS12DecoderFinish(dcx); SECITEM_ZfreeItem(&unicodePw, false); return NS_OK; }
static gboolean import_from_file_helper (EPKCS12 *pkcs12, PK11SlotInfo *slot, const gchar *path, gboolean *aWantRetry, GError **error) { /*nsNSSShutDownPreventionLock locker; */ gboolean rv; SECStatus srv = SECSuccess; SEC_PKCS12DecoderContext *dcx = NULL; SECItem passwd; GError *err = NULL; *aWantRetry = FALSE; passwd.data = NULL; rv = prompt_for_password ( _("PKCS12 File Password"), _("Enter password for PKCS12 file:"), &passwd); if (!rv) goto finish; if (passwd.data == NULL) { handle_error (PKCS12_USER_CANCELED); return TRUE; } /* initialize the decoder */ dcx = SEC_PKCS12DecoderStart ( &passwd, slot, /* we specify NULL for all the * funcs + data so it'll use the * default pk11wrap functions */ NULL, NULL, NULL, NULL, NULL, NULL); if (!dcx) { srv = SECFailure; goto finish; } /* read input file and feed it to the decoder */ rv = input_to_decoder (dcx, path, &err); if (!rv) { #ifdef notyet /* XXX we need this to check the gerror */ if (NS_ERROR_ABORT == rv) { /* inputToDecoder indicated a NSS error */ srv = SECFailure; } #else srv = SECFailure; #endif goto finish; } /* verify the blob */ srv = SEC_PKCS12DecoderVerify (dcx); if (srv) goto finish; /* validate bags */ srv = SEC_PKCS12DecoderValidateBags (dcx, nickname_collision); if (srv) goto finish; /* import cert and key */ srv = SEC_PKCS12DecoderImportBags (dcx); if (srv) goto finish; /* Later - check to see if this should become default email cert */ handle_error (PKCS12_RESTORE_OK); finish: /* If srv != SECSuccess, NSS probably set a specific error code. * We should use that error code instead of inventing a new one * for every error possible. */ if (srv != SECSuccess) { if (SEC_ERROR_BAD_PASSWORD == PORT_GetError ()) { *aWantRetry = TRUE; } handle_error (PKCS12_NSS_ERROR); } else if (!rv) { handle_error (PKCS12_RESTORE_FAILED); } /* finish the decoder */ if (dcx) SEC_PKCS12DecoderFinish (dcx); return TRUE; }
// Based on nsPKCS12Blob::ImportFromFileHelper. int nsPKCS12Blob_ImportHelper(const char* pkcs12_data, size_t pkcs12_len, const base::string16& password, bool is_extractable, bool try_zero_length_secitem, PK11SlotInfo *slot, net::CertificateList* imported_certs) { DCHECK(pkcs12_data); DCHECK(slot); int import_result = net::ERR_PKCS12_IMPORT_FAILED; SECStatus srv = SECSuccess; SEC_PKCS12DecoderContext *dcx = NULL; SECItem unicodePw; SECItem attribute_value; CK_BBOOL attribute_data = CK_FALSE; const SEC_PKCS12DecoderItem* decoder_item = NULL; unicodePw.type = siBuffer; unicodePw.len = 0; unicodePw.data = NULL; if (!try_zero_length_secitem) { unicodeToItem(password.c_str(), &unicodePw); } // Initialize the decoder dcx = SEC_PKCS12DecoderStart(&unicodePw, slot, // wincx NULL, // dOpen, dClose, dRead, dWrite, dArg: NULL // specifies default impl using memory buffer. NULL, NULL, NULL, NULL, NULL); if (!dcx) { srv = SECFailure; goto finish; } // feed input to the decoder srv = SEC_PKCS12DecoderUpdate(dcx, (unsigned char*)pkcs12_data, pkcs12_len); if (srv) goto finish; // verify the blob srv = SEC_PKCS12DecoderVerify(dcx); if (srv) goto finish; // validate bags srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision); if (srv) goto finish; // import certificate and key srv = SEC_PKCS12DecoderImportBags(dcx); if (srv) goto finish; attribute_value.data = &attribute_data; attribute_value.len = sizeof(attribute_data); srv = SEC_PKCS12DecoderIterateInit(dcx); if (srv) goto finish; if (imported_certs) imported_certs->clear(); // Collect the list of decoded certificates, and mark private keys // non-extractable if needed. while (SEC_PKCS12DecoderIterateNext(dcx, &decoder_item) == SECSuccess) { if (decoder_item->type != SEC_OID_PKCS12_V1_CERT_BAG_ID) continue; CERTCertificate* cert = PK11_FindCertFromDERCertItem( slot, decoder_item->der, NULL); // wincx if (!cert) { LOG(ERROR) << "Could not grab a handle to the certificate in the slot " << "from the corresponding PKCS#12 DER certificate."; continue; } // Add the cert to the list if (imported_certs) { // Empty list of intermediates. net::X509Certificate::OSCertHandles intermediates; imported_certs->push_back( net::X509Certificate::CreateFromHandle(cert, intermediates)); } // Once we have determined that the imported certificate has an // associated private key too, only then can we mark the key as // non-extractable. if (!decoder_item->hasKey) { CERT_DestroyCertificate(cert); continue; } // Iterate through all the imported PKCS12 items and mark any accompanying // private keys as non-extractable. if (!is_extractable) { SECKEYPrivateKey* privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL); // wincx if (privKey) { // Mark the private key as non-extractable. srv = PK11_WriteRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &attribute_value); SECKEY_DestroyPrivateKey(privKey); if (srv) { LOG(ERROR) << "Could not set CKA_EXTRACTABLE attribute on private " << "key."; CERT_DestroyCertificate(cert); break; } } } CERT_DestroyCertificate(cert); if (srv) goto finish; } import_result = net::OK; finish: // If srv != SECSuccess, NSS probably set a specific error code. // We should use that error code instead of inventing a new one // for every error possible. if (srv != SECSuccess) { int error = PORT_GetError(); LOG(ERROR) << "PKCS#12 import failed with error " << error; switch (error) { case SEC_ERROR_BAD_PASSWORD: case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT: import_result = net::ERR_PKCS12_IMPORT_BAD_PASSWORD; break; case SEC_ERROR_PKCS12_INVALID_MAC: import_result = net::ERR_PKCS12_IMPORT_INVALID_MAC; break; case SEC_ERROR_BAD_DER: case SEC_ERROR_PKCS12_DECODING_PFX: case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE: import_result = net::ERR_PKCS12_IMPORT_INVALID_FILE; break; case SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM: case SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE: case SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM: case SEC_ERROR_PKCS12_UNSUPPORTED_VERSION: import_result = net::ERR_PKCS12_IMPORT_UNSUPPORTED; break; default: import_result = net::ERR_PKCS12_IMPORT_FAILED; break; } } // Finish the decoder if (dcx) SEC_PKCS12DecoderFinish(dcx); SECITEM_ZfreeItem(&unicodePw, PR_FALSE); return import_result; }
UtlBoolean SmimeBody::nssSmimeDecrypt(const char* derPkcs12, int derPkcs12Length, const char* pkcs12Password, UtlBoolean dataIsInBase64Format, const char* dataToDecrypt, int dataToDecryptLength, UtlString& decryptedData) { UtlBoolean decryptSucceeded = FALSE; decryptedData.remove(0); #ifdef ENABLE_NSS_SMIME Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SmimeBody::nssSmimeDecrypt not implemented"); ////// BEGIN WARNING: THIS CODE HAS NOT BEEN TESTED AT ALL /////// // allocate a temporaty slot in the database PK11SlotInfo *slot = PK11_GetInternalKeySlot(); PRBool swapUnicode = PR_FALSE; SEC_PKCS12DecoderContext *p12Decoder = NULL; // Need to put the pkcs12 password into a SECItem SECItem passwordItem; passwordItem.data = (unsigned char*) pkcs12Password; passwordItem.len = strlen(pkcs12Password); SECItem uniPasswordItem; uniPasswordItem.data = NULL; uniPasswordItem.len = 0; #ifdef IS_LITTLE_ENDIAN swapUnicode = PR_TRUE; #endif // Allocate a temporary internal slot slot = PK11_GetInternalSlot(); if(slot == NULL) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "unable to use slot in NSS dataqbase for S/MIME decryption"); } else { // Do UNICODE conversion of password based upon the platform // (not sure this is even neccessary in our application). I do not // know how we would get unicode passwords if(0) //P12U_UnicodeConversion(NULL, &uniPasswordItem, passwordItem, PR_TRUE, // swapUnicode) != SECSuccess) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "NSS Unicode conversion failed for PKCS12 object for S/MIME decryption"); } else { // Initialze the decoder for the PKCS12 container for the private key p12Decoder = SEC_PKCS12DecoderStart(&passwordItem, slot, NULL, NULL, NULL, NULL, NULL, NULL); if(!p12Decoder) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "failed to initialize PKCS12 decoder to extract private key for S/MIME decryption"); } else { // Add the PKCS12 data to the decoder if(SEC_PKCS12DecoderUpdate(p12Decoder, (unsigned char *) derPkcs12, derPkcs12Length) != SECSuccess || // Validate the decoded PKCS12 SEC_PKCS12DecoderVerify(p12Decoder) != SECSuccess) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "unable to decrypt PKCS12 for S/MIME decryption. Perhaps invalid PKCS12 or PKCS12 password"); } else { // Import the private key and certificate from the // decoded PKCS12 into the database if(SEC_PKCS12DecoderImportBags(p12Decoder) != SECSuccess) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "failed to import private key and certificate into NSS database"); } else { // Put the S/MIME data in a SECItem SECItem dataToDecodeItem; dataToDecodeItem.data = (unsigned char *) dataToDecrypt; dataToDecodeItem.len = dataToDecryptLength; if(dataIsInBase64Format) { // TODO: // Use some NSS util. to convert base64 to binary Os::Logger::instance().log(FAC_SIP, PRI_ERR, "NSS decrypt of base64 S/MIME message not implemented"); } else { // Decode the S/MIME blob NSSCMSMessage *cmsMessage = NSS_CMSMessage_CreateFromDER(&dataToDecodeItem, nssOutToUtlString, &decryptedData, NULL, NULL, NULL, NULL); if(cmsMessage && decryptedData.length() > 0) { decryptSucceeded = TRUE; } // TODO: // Remove the temporary private key from the // database using the slot handle } } } } } } // Clean up if(p12Decoder) { SEC_PKCS12DecoderFinish(p12Decoder); } if(uniPasswordItem.data) { SECITEM_ZfreeItem(&uniPasswordItem, PR_FALSE); } ////// END WARNING ///// #else Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SmimeBody::nssSmimeDecrypt invoked with ENABLE_NSS_SMIME not defined"); #endif return(decryptSucceeded); }