Index: nss_pkcs12/p12dec.c |
=================================================================== |
--- nss_pkcs12/p12dec.c (revision 26000) |
+++ nss_pkcs12/p12dec.c (working copy) |
@@ -1,664 +0,0 @@ |
-/* This Source Code Form is subject to the terms of the Mozilla Public |
- * License, v. 2.0. If a copy of the MPL was not distributed with this |
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
- |
-#include "pkcs12.h" |
-#include "plarena.h" |
-#include "secpkcs7.h" |
-#include "p12local.h" |
-#include "secoid.h" |
-#include "secitem.h" |
-#include "secport.h" |
-#include "secasn1.h" |
-#include "secder.h" |
-#include "secerr.h" |
-#include "cert.h" |
-#include "certdb.h" |
-#include "p12plcy.h" |
-#include "p12.h" |
-#include "secpkcs5.h" |
- |
-/* PFX extraction and validation routines */ |
- |
-/* decode the DER encoded PFX item. if unable to decode, check to see if it |
- * is an older PFX item. If that fails, assume the file was not a valid |
- * pfx file. |
- * the returned pfx structure should be destroyed using SEC_PKCS12DestroyPFX |
- */ |
-static SEC_PKCS12PFXItem * |
-sec_pkcs12_decode_pfx(SECItem *der_pfx) |
-{ |
- SEC_PKCS12PFXItem *pfx; |
- SECStatus rv; |
- |
- if(der_pfx == NULL) { |
- return NULL; |
- } |
- |
- /* allocate the space for a new PFX item */ |
- pfx = sec_pkcs12_new_pfx(); |
- if(pfx == NULL) { |
- return NULL; |
- } |
- |
- rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate, |
- der_pfx); |
- |
- /* if a failure occurred, check for older version... |
- * we also get rid of the old pfx structure, because we don't |
- * know where it failed and what data in may contain |
- */ |
- if(rv != SECSuccess) { |
- SEC_PKCS12DestroyPFX(pfx); |
- pfx = sec_pkcs12_new_pfx(); |
- if(pfx == NULL) { |
- return NULL; |
- } |
- rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate_OLD, |
- der_pfx); |
- if(rv != SECSuccess) { |
- PORT_SetError(SEC_ERROR_PKCS12_DECODING_PFX); |
- PORT_FreeArena(pfx->poolp, PR_TRUE); |
- return NULL; |
- } |
- pfx->old = PR_TRUE; |
- SGN_CopyDigestInfo(pfx->poolp, &pfx->macData.safeMac, &pfx->old_safeMac); |
- SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, &pfx->old_macSalt); |
- } else { |
- pfx->old = PR_FALSE; |
- } |
- |
- /* convert bit string from bits to bytes */ |
- pfx->macData.macSalt.len /= 8; |
- |
- return pfx; |
-} |
- |
-/* validate the integrity MAC used in the PFX. The MAC is generated |
- * per the PKCS 12 document. If the MAC is incorrect, it is most likely |
- * due to an invalid password. |
- * pwitem is the integrity password |
- * pfx is the decoded pfx item |
- */ |
-static PRBool |
-sec_pkcs12_check_pfx_mac(SEC_PKCS12PFXItem *pfx, |
- SECItem *pwitem) |
-{ |
- SECItem *key = NULL, *mac = NULL, *data = NULL; |
- SECItem *vpwd = NULL; |
- SECOidTag algorithm; |
- PRBool ret = PR_FALSE; |
- |
- if(pfx == NULL) { |
- return PR_FALSE; |
- } |
- |
- algorithm = SECOID_GetAlgorithmTag(&pfx->macData.safeMac.digestAlgorithm); |
- switch(algorithm) { |
- /* only SHA1 hashing supported as a MACing algorithm */ |
- case SEC_OID_SHA1: |
- if(pfx->old == PR_FALSE) { |
- pfx->swapUnicode = PR_FALSE; |
- } |
- |
-recheckUnicodePassword: |
- vpwd = sec_pkcs12_create_virtual_password(pwitem, |
- &pfx->macData.macSalt, |
- pfx->swapUnicode); |
- if(vpwd == NULL) { |
- return PR_FALSE; |
- } |
- |
- key = sec_pkcs12_generate_key_from_password(algorithm, |
- &pfx->macData.macSalt, |
- (pfx->old ? pwitem : vpwd)); |
- /* free vpwd only for newer PFX */ |
- if(vpwd) { |
- SECITEM_ZfreeItem(vpwd, PR_TRUE); |
- } |
- if(key == NULL) { |
- return PR_FALSE; |
- } |
- |
- data = SEC_PKCS7GetContent(&pfx->authSafe); |
- if(data == NULL) { |
- break; |
- } |
- |
- /* check MAC */ |
- mac = sec_pkcs12_generate_mac(key, data, pfx->old); |
- ret = PR_TRUE; |
- if(mac) { |
- SECItem *safeMac = &pfx->macData.safeMac.digest; |
- if(SECITEM_CompareItem(mac, safeMac) != SECEqual) { |
- |
- /* if we encounter an invalid mac, lets invert the |
- * password in case of unicode changes |
- */ |
- if(((!pfx->old) && pfx->swapUnicode) || (pfx->old)){ |
- PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC); |
- ret = PR_FALSE; |
- } else { |
- SECITEM_ZfreeItem(mac, PR_TRUE); |
- pfx->swapUnicode = PR_TRUE; |
- goto recheckUnicodePassword; |
- } |
- } |
- SECITEM_ZfreeItem(mac, PR_TRUE); |
- } else { |
- ret = PR_FALSE; |
- } |
- break; |
- default: |
- PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM); |
- ret = PR_FALSE; |
- break; |
- } |
- |
- /* let success fall through */ |
- if(key != NULL) |
- SECITEM_ZfreeItem(key, PR_TRUE); |
- |
- return ret; |
-} |
- |
-/* check the validity of the pfx structure. we currently only support |
- * password integrity mode, so we check the MAC. |
- */ |
-static PRBool |
-sec_pkcs12_validate_pfx(SEC_PKCS12PFXItem *pfx, |
- SECItem *pwitem) |
-{ |
- SECOidTag contentType; |
- |
- contentType = SEC_PKCS7ContentType(&pfx->authSafe); |
- switch(contentType) |
- { |
- case SEC_OID_PKCS7_DATA: |
- return sec_pkcs12_check_pfx_mac(pfx, pwitem); |
- break; |
- case SEC_OID_PKCS7_SIGNED_DATA: |
- default: |
- PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE); |
- break; |
- } |
- |
- return PR_FALSE; |
-} |
- |
-/* decode and return the valid PFX. if the PFX item is not valid, |
- * NULL is returned. |
- */ |
-static SEC_PKCS12PFXItem * |
-sec_pkcs12_get_pfx(SECItem *pfx_data, |
- SECItem *pwitem) |
-{ |
- SEC_PKCS12PFXItem *pfx; |
- PRBool valid_pfx; |
- |
- if((pfx_data == NULL) || (pwitem == NULL)) { |
- return NULL; |
- } |
- |
- pfx = sec_pkcs12_decode_pfx(pfx_data); |
- if(pfx == NULL) { |
- return NULL; |
- } |
- |
- valid_pfx = sec_pkcs12_validate_pfx(pfx, pwitem); |
- if(valid_pfx != PR_TRUE) { |
- SEC_PKCS12DestroyPFX(pfx); |
- pfx = NULL; |
- } |
- |
- return pfx; |
-} |
- |
-/* authenticated safe decoding, validation, and access routines |
- */ |
- |
-/* convert dogbert beta 3 authenticated safe structure to a post |
- * beta three structure, so that we don't have to change more routines. |
- */ |
-static SECStatus |
-sec_pkcs12_convert_old_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe) |
-{ |
- SEC_PKCS12Baggage *baggage; |
- SEC_PKCS12BaggageItem *bag; |
- SECStatus rv = SECSuccess; |
- |
- if(asafe->old_baggage.espvks == NULL) { |
- /* XXX should the ASN1 engine produce a single NULL element list |
- * rather than setting the pointer to NULL? |
- * There is no need to return an error -- assume that the list |
- * was empty. |
- */ |
- return SECSuccess; |
- } |
- |
- baggage = sec_pkcs12_create_baggage(asafe->poolp); |
- if(!baggage) { |
- return SECFailure; |
- } |
- bag = sec_pkcs12_create_external_bag(baggage); |
- if(!bag) { |
- return SECFailure; |
- } |
- |
- PORT_Memcpy(&asafe->baggage, baggage, sizeof(SEC_PKCS12Baggage)); |
- |
- /* if there are shrouded keys, append them to the bag */ |
- rv = SECSuccess; |
- if(asafe->old_baggage.espvks[0] != NULL) { |
- int nEspvk = 0; |
- rv = SECSuccess; |
- while((asafe->old_baggage.espvks[nEspvk] != NULL) && |
- (rv == SECSuccess)) { |
- rv = sec_pkcs12_append_shrouded_key(bag, |
- asafe->old_baggage.espvks[nEspvk]); |
- nEspvk++; |
- } |
- } |
- |
- return rv; |
-} |
- |
-/* decodes the authenticated safe item. a return of NULL indicates |
- * an error. however, the error will have occurred either in memory |
- * allocation or in decoding the authenticated safe. |
- * |
- * if an old PFX item has been found, we want to convert the |
- * old authenticated safe to the new one. |
- */ |
-static SEC_PKCS12AuthenticatedSafe * |
-sec_pkcs12_decode_authenticated_safe(SEC_PKCS12PFXItem *pfx) |
-{ |
- SECItem *der_asafe = NULL; |
- SEC_PKCS12AuthenticatedSafe *asafe = NULL; |
- SECStatus rv; |
- |
- if(pfx == NULL) { |
- return NULL; |
- } |
- |
- der_asafe = SEC_PKCS7GetContent(&pfx->authSafe); |
- if(der_asafe == NULL) { |
- /* XXX set error ? */ |
- goto loser; |
- } |
- |
- asafe = sec_pkcs12_new_asafe(pfx->poolp); |
- if(asafe == NULL) { |
- goto loser; |
- } |
- |
- if(pfx->old == PR_FALSE) { |
- rv = SEC_ASN1DecodeItem(pfx->poolp, asafe, |
- SEC_PKCS12AuthenticatedSafeTemplate, |
- der_asafe); |
- asafe->old = PR_FALSE; |
- asafe->swapUnicode = pfx->swapUnicode; |
- } else { |
- /* handle beta exported files */ |
- rv = SEC_ASN1DecodeItem(pfx->poolp, asafe, |
- SEC_PKCS12AuthenticatedSafeTemplate_OLD, |
- der_asafe); |
- asafe->safe = &(asafe->old_safe); |
- rv = sec_pkcs12_convert_old_auth_safe(asafe); |
- asafe->old = PR_TRUE; |
- } |
- |
- if(rv != SECSuccess) { |
- goto loser; |
- } |
- |
- asafe->poolp = pfx->poolp; |
- |
- return asafe; |
- |
-loser: |
- return NULL; |
-} |
- |
-/* validates the safe within the authenticated safe item. |
- * in order to be valid: |
- * 1. the privacy salt must be present |
- * 2. the encryption algorithm must be supported (including |
- * export policy) |
- * PR_FALSE indicates an error, PR_TRUE indicates a valid safe |
- */ |
-static PRBool |
-sec_pkcs12_validate_encrypted_safe(SEC_PKCS12AuthenticatedSafe *asafe) |
-{ |
- PRBool valid = PR_FALSE; |
- SECAlgorithmID *algid; |
- |
- if(asafe == NULL) { |
- return PR_FALSE; |
- } |
- |
- /* if mode is password privacy, then privacySalt is assumed |
- * to be non-zero. |
- */ |
- if(asafe->privacySalt.len != 0) { |
- valid = PR_TRUE; |
- asafe->privacySalt.len /= 8; |
- } else { |
- PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); |
- return PR_FALSE; |
- } |
- |
- /* until spec changes, content will have between 2 and 8 bytes depending |
- * upon the algorithm used if certs are unencrypted... |
- * also want to support case where content is empty -- which we produce |
- */ |
- if(SEC_PKCS7IsContentEmpty(asafe->safe, 8) == PR_TRUE) { |
- asafe->emptySafe = PR_TRUE; |
- return PR_TRUE; |
- } |
- |
- asafe->emptySafe = PR_FALSE; |
- |
- /* make sure that a pbe algorithm is being used */ |
- algid = SEC_PKCS7GetEncryptionAlgorithm(asafe->safe); |
- if(algid != NULL) { |
- if(SEC_PKCS5IsAlgorithmPBEAlg(algid)) { |
- valid = SEC_PKCS12DecryptionAllowed(algid); |
- |
- if(valid == PR_FALSE) { |
- PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM); |
- } |
- } else { |
- PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM); |
- valid = PR_FALSE; |
- } |
- } else { |
- valid = PR_FALSE; |
- PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM); |
- } |
- |
- return valid; |
-} |
- |
-/* validates authenticates safe: |
- * 1. checks that the version is supported |
- * 2. checks that only password privacy mode is used (currently) |
- * 3. further, makes sure safe has appropriate policies per above function |
- * PR_FALSE indicates failure. |
- */ |
-static PRBool |
-sec_pkcs12_validate_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe) |
-{ |
- PRBool valid = PR_TRUE; |
- SECOidTag safe_type; |
- int version; |
- |
- if(asafe == NULL) { |
- return PR_FALSE; |
- } |
- |
- /* check version, since it is default it may not be present. |
- * therefore, assume ok |
- */ |
- if((asafe->version.len > 0) && (asafe->old == PR_FALSE)) { |
- version = DER_GetInteger(&asafe->version); |
- if(version > SEC_PKCS12_PFX_VERSION) { |
- PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_VERSION); |
- return PR_FALSE; |
- } |
- } |
- |
- /* validate password mode is being used */ |
- safe_type = SEC_PKCS7ContentType(asafe->safe); |
- switch(safe_type) |
- { |
- case SEC_OID_PKCS7_ENCRYPTED_DATA: |
- valid = sec_pkcs12_validate_encrypted_safe(asafe); |
- break; |
- case SEC_OID_PKCS7_ENVELOPED_DATA: |
- default: |
- PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE); |
- valid = PR_FALSE; |
- break; |
- } |
- |
- return valid; |
-} |
- |
-/* retrieves the authenticated safe item from the PFX item |
- * before returning the authenticated safe, the validity of the |
- * authenticated safe is checked and if valid, returned. |
- * a return of NULL indicates that an error occurred. |
- */ |
-static SEC_PKCS12AuthenticatedSafe * |
-sec_pkcs12_get_auth_safe(SEC_PKCS12PFXItem *pfx) |
-{ |
- SEC_PKCS12AuthenticatedSafe *asafe; |
- PRBool valid_safe; |
- |
- if(pfx == NULL) { |
- return NULL; |
- } |
- |
- asafe = sec_pkcs12_decode_authenticated_safe(pfx); |
- if(asafe == NULL) { |
- return NULL; |
- } |
- |
- valid_safe = sec_pkcs12_validate_auth_safe(asafe); |
- if(valid_safe != PR_TRUE) { |
- asafe = NULL; |
- } else if(asafe) { |
- asafe->baggage.poolp = asafe->poolp; |
- } |
- |
- return asafe; |
-} |
- |
-/* decrypts the authenticated safe. |
- * a return of anything but SECSuccess indicates an error. the |
- * password is not known to be valid until the call to the |
- * function sec_pkcs12_get_safe_contents. If decoding the safe |
- * fails, it is assumed the password was incorrect and the error |
- * is set then. any failure here is assumed to be due to |
- * internal problems in SEC_PKCS7DecryptContents or below. |
- */ |
-static SECStatus |
-sec_pkcs12_decrypt_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe, |
- SECItem *pwitem, |
- void *wincx) |
-{ |
- SECStatus rv = SECFailure; |
- SECItem *vpwd = NULL; |
- |
- if((asafe == NULL) || (pwitem == NULL)) { |
- return SECFailure; |
- } |
- |
- if(asafe->old == PR_FALSE) { |
- vpwd = sec_pkcs12_create_virtual_password(pwitem, &asafe->privacySalt, |
- asafe->swapUnicode); |
- if(vpwd == NULL) { |
- return SECFailure; |
- } |
- } |
- |
- rv = SEC_PKCS7DecryptContents(asafe->poolp, asafe->safe, |
- (asafe->old ? pwitem : vpwd), wincx); |
- |
- if(asafe->old == PR_FALSE) { |
- SECITEM_ZfreeItem(vpwd, PR_TRUE); |
- } |
- |
- return rv; |
-} |
- |
-/* extract the safe from the authenticated safe. |
- * if we are unable to decode the safe, then it is likely that the |
- * safe has not been decrypted or the password used to decrypt |
- * the safe was invalid. we assume that the password was invalid and |
- * set an error accordingly. |
- * a return of NULL indicates that an error occurred. |
- */ |
-static SEC_PKCS12SafeContents * |
-sec_pkcs12_get_safe_contents(SEC_PKCS12AuthenticatedSafe *asafe) |
-{ |
- SECItem *src = NULL; |
- SEC_PKCS12SafeContents *safe = NULL; |
- SECStatus rv = SECFailure; |
- |
- if(asafe == NULL) { |
- return NULL; |
- } |
- |
- safe = (SEC_PKCS12SafeContents *)PORT_ArenaZAlloc(asafe->poolp, |
- sizeof(SEC_PKCS12SafeContents)); |
- if(safe == NULL) { |
- return NULL; |
- } |
- safe->poolp = asafe->poolp; |
- safe->old = asafe->old; |
- safe->swapUnicode = asafe->swapUnicode; |
- |
- src = SEC_PKCS7GetContent(asafe->safe); |
- if(src != NULL) { |
- const SEC_ASN1Template *theTemplate; |
- if(asafe->old != PR_TRUE) { |
- theTemplate = SEC_PKCS12SafeContentsTemplate; |
- } else { |
- theTemplate = SEC_PKCS12SafeContentsTemplate_OLD; |
- } |
- |
- rv = SEC_ASN1DecodeItem(asafe->poolp, safe, theTemplate, src); |
- |
- /* if we could not decode the item, password was probably invalid */ |
- if(rv != SECSuccess) { |
- safe = NULL; |
- PORT_SetError(SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT); |
- } |
- } else { |
- PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); |
- rv = SECFailure; |
- } |
- |
- return safe; |
-} |
- |
-/* import PFX item |
- * der_pfx is the der encoded pfx structure |
- * pbef and pbearg are the integrity/encryption password call back |
- * ncCall is the nickname collision calllback |
- * slot is the destination token |
- * wincx window handler |
- * |
- * on error, error code set and SECFailure returned |
- */ |
-SECStatus |
-SEC_PKCS12PutPFX(SECItem *der_pfx, SECItem *pwitem, |
- SEC_PKCS12NicknameCollisionCallback ncCall, |
- PK11SlotInfo *slot, |
- void *wincx) |
-{ |
- SEC_PKCS12PFXItem *pfx; |
- SEC_PKCS12AuthenticatedSafe *asafe; |
- SEC_PKCS12SafeContents *safe_contents = NULL; |
- SECStatus rv; |
- |
- if(!der_pfx || !pwitem || !slot) { |
- return SECFailure; |
- } |
- |
- /* decode and validate each section */ |
- rv = SECFailure; |
- |
- pfx = sec_pkcs12_get_pfx(der_pfx, pwitem); |
- if(pfx != NULL) { |
- asafe = sec_pkcs12_get_auth_safe(pfx); |
- if(asafe != NULL) { |
- |
- /* decrypt safe -- only if not empty */ |
- if(asafe->emptySafe != PR_TRUE) { |
- rv = sec_pkcs12_decrypt_auth_safe(asafe, pwitem, wincx); |
- if(rv == SECSuccess) { |
- safe_contents = sec_pkcs12_get_safe_contents(asafe); |
- if(safe_contents == NULL) { |
- rv = SECFailure; |
- } |
- } |
- } else { |
- safe_contents = sec_pkcs12_create_safe_contents(asafe->poolp); |
- if(safe_contents == NULL) { |
- rv = SECFailure; |
- } else { |
- safe_contents->swapUnicode = pfx->swapUnicode; |
- rv = SECSuccess; |
- } |
- } |
- |
- /* get safe contents and begin import */ |
- if(rv == SECSuccess) { |
- SEC_PKCS12DecoderContext *p12dcx; |
- |
- p12dcx = sec_PKCS12ConvertOldSafeToNew(pfx->poolp, slot, |
- pfx->swapUnicode, |
- pwitem, wincx, safe_contents, |
- &asafe->baggage); |
- if(!p12dcx) { |
- rv = SECFailure; |
- goto loser; |
- } |
- |
- if(SEC_PKCS12DecoderValidateBags(p12dcx, ncCall) |
- != SECSuccess) { |
- rv = SECFailure; |
- goto loser; |
- } |
- |
- rv = SEC_PKCS12DecoderImportBags(p12dcx); |
- } |
- |
- } |
- } |
- |
-loser: |
- |
- if(pfx) { |
- SEC_PKCS12DestroyPFX(pfx); |
- } |
- |
- return rv; |
-} |
- |
-PRBool |
-SEC_PKCS12ValidData(char *buf, int bufLen, long int totalLength) |
-{ |
- int lengthLength; |
- |
- PRBool valid = PR_FALSE; |
- |
- if(buf == NULL) { |
- return PR_FALSE; |
- } |
- |
- /* check for constructed sequence identifier tag */ |
- if(*buf == (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) { |
- totalLength--; /* header byte taken care of */ |
- buf++; |
- |
- lengthLength = (long int)SEC_ASN1LengthLength(totalLength - 1); |
- if(totalLength > 0x7f) { |
- lengthLength--; |
- *buf &= 0x7f; /* remove bit 8 indicator */ |
- if((*buf - (char)lengthLength) == 0) { |
- valid = PR_TRUE; |
- } |
- } else { |
- lengthLength--; |
- if((*buf - (char)lengthLength) == 0) { |
- valid = PR_TRUE; |
- } |
- } |
- } |
- |
- return valid; |
-} |