diff options
Diffstat (limited to 'security/nss/lib/pkcs12')
-rw-r--r-- | security/nss/lib/pkcs12/Makefile | 77 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/config.mk | 44 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/manifest.mn | 58 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12.h | 181 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12creat.c | 251 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12d.c | 3328 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12dec.c | 693 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12e.c | 2282 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12exp.c | 1407 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12local.c | 1363 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12local.h | 88 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12plcy.c | 198 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12plcy.h | 57 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12t.h | 182 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/p12tmpl.c | 320 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/pkcs12.h | 67 | ||||
-rw-r--r-- | security/nss/lib/pkcs12/pkcs12t.h | 386 |
17 files changed, 10982 insertions, 0 deletions
diff --git a/security/nss/lib/pkcs12/Makefile b/security/nss/lib/pkcs12/Makefile new file mode 100644 index 000000000..5742208b5 --- /dev/null +++ b/security/nss/lib/pkcs12/Makefile @@ -0,0 +1,77 @@ +#! gmake +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include config.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + + diff --git a/security/nss/lib/pkcs12/config.mk b/security/nss/lib/pkcs12/config.mk new file mode 100644 index 000000000..3b647d954 --- /dev/null +++ b/security/nss/lib/pkcs12/config.mk @@ -0,0 +1,44 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + + +# +# Override TARGETS variable so that only static libraries +# are specifed as dependencies within rules.mk. +# + +TARGETS = $(LIBRARY) +SHARED_LIBRARY = +IMPORT_LIBRARY = +PROGRAM = + diff --git a/security/nss/lib/pkcs12/manifest.mn b/security/nss/lib/pkcs12/manifest.mn new file mode 100644 index 000000000..c8b4981d3 --- /dev/null +++ b/security/nss/lib/pkcs12/manifest.mn @@ -0,0 +1,58 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +CORE_DEPTH = ../../.. + +EXPORTS = \ + pkcs12t.h \ + pkcs12.h \ + p12plcy.h \ + p12.h \ + p12t.h \ + $(NULL) + +MODULE = security + +CSRCS = \ + p12local.c \ + p12creat.c \ + p12dec.c \ + p12plcy.c \ + p12tmpl.c \ + p12e.c \ + p12d.c \ + $(NULL) + +REQUIRES = security dbm + +LIBRARY_NAME = pkcs12 diff --git a/security/nss/lib/pkcs12/p12.h b/security/nss/lib/pkcs12/p12.h new file mode 100644 index 000000000..14a8dd385 --- /dev/null +++ b/security/nss/lib/pkcs12/p12.h @@ -0,0 +1,181 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + + +#ifndef _P12_H_ +#define _P12_H_ + +#include "secoid.h" +#include "key.h" +#include "secpkcs7.h" +#include "p12t.h" + +typedef int (PR_CALLBACK * PKCS12OpenFunction)(void *arg); +typedef int (PR_CALLBACK * PKCS12ReadFunction)(void *arg, + unsigned char *buffer, + unsigned int *lenRead, + unsigned int maxLen); +typedef int (PR_CALLBACK * PKCS12WriteFunction)(void *arg, + unsigned char *buffer, + unsigned int *bufLen, + unsigned int *lenWritten); +typedef int (PR_CALLBACK * PKCS12CloseFunction)(void *arg); +typedef SECStatus (PR_CALLBACK * PKCS12UnicodeConvertFunction)( + PRArenaPool *arena, + SECItem *dest, SECItem *src, + PRBool toUnicode, + PRBool swapBytes); +typedef void (PR_CALLBACK * SEC_PKCS12EncoderOutputCallback)( + void *arg, const char *buf, + unsigned long len); +typedef void (PR_CALLBACK * SEC_PKCS12DecoderOutputCallback)( + void *arg, const char *buf, + unsigned long len); +typedef SECItem * (PR_CALLBACK * SEC_PKCS12NicknameCollisionCallback)( + SECItem *old_nickname, + PRBool *cancel, + void *arg); + + + + +typedef SECStatus (PR_CALLBACK *digestOpenFn)(void *arg, PRBool readData); +typedef SECStatus (PR_CALLBACK *digestCloseFn)(void *arg, PRBool removeFile); +typedef int (PR_CALLBACK *digestIOFn)(void *arg, unsigned char *buf, + unsigned long len); + +typedef struct SEC_PKCS12ExportContextStr SEC_PKCS12ExportContext; +typedef struct SEC_PKCS12SafeInfoStr SEC_PKCS12SafeInfo; +typedef struct SEC_PKCS12DecoderContextStr SEC_PKCS12DecoderContext; + +struct sec_PKCS12PasswordModeInfo { + SECItem *password; + SECOidTag algorithm; +}; + +struct sec_PKCS12PublicKeyModeInfo { + CERTCertificate *cert; + CERTCertDBHandle *certDb; + SECOidTag algorithm; + int keySize; +}; + +SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt, + CERTCertDBHandle *certDb, + CERTCertificate *signer, + CERTCertificate **recipients, + SECOidTag algorithm, int keysize); + +extern SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag privAlg); + +extern SEC_PKCS12SafeInfo * +SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt); + +extern SECStatus +SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag integAlg); +extern SECStatus +SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt, + CERTCertificate *cert, CERTCertDBHandle *certDb, + SECOidTag algorithm, int keySize); + +extern SEC_PKCS12ExportContext * +SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg, + PK11SlotInfo *slot, void *wincx); + +extern SECStatus +SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, + SEC_PKCS12SafeInfo *safe, void *nestedDest, + CERTCertificate *cert, CERTCertDBHandle *certDb, + SECItem *keyId, PRBool includeCertChain); + +extern SECStatus +SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, + SEC_PKCS12SafeInfo *safe, + void *nestedDest, CERTCertificate *cert, + PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem, + SECItem *keyId, SECItem *nickName); + +extern SECStatus +SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, + CERTCertificate *cert, CERTCertDBHandle *certDb, + void *keySafe, void *keyNestedDest, + PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm); + +extern SECStatus +SEC_PKCS12AddDERCertAndEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, SECItem *derCert, + void *keySafe, void *keyNestedDest, + SECKEYEncryptedPrivateKeyInfo *epki, char *nickname); + +extern void * +SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt, + void *baseSafe, void *nestedDest); + +extern SECStatus +SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, + SEC_PKCS12EncoderOutputCallback output, void *outputarg); + +extern void +SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12exp); + +extern SEC_PKCS12DecoderContext * +SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx, + digestOpenFn dOpen, digestCloseFn dClose, + digestIOFn dRead, digestIOFn dWrite, void *dArg); + +extern SECStatus +SEC_PKCS12DecoderUpdate(SEC_PKCS12DecoderContext *p12dcx, unsigned char *data, + unsigned long len); + +extern void +SEC_PKCS12DecoderFinish(SEC_PKCS12DecoderContext *p12dcx); + +extern SECStatus +SEC_PKCS12DecoderVerify(SEC_PKCS12DecoderContext *p12dcx); + +extern SECStatus +SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx, + SEC_PKCS12NicknameCollisionCallback nicknameCb); + +extern SECStatus +SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx); + +CERTCertList * +SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx); + +#endif diff --git a/security/nss/lib/pkcs12/p12creat.c b/security/nss/lib/pkcs12/p12creat.c new file mode 100644 index 000000000..65678b299 --- /dev/null +++ b/security/nss/lib/pkcs12/p12creat.c @@ -0,0 +1,251 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "pkcs12.h" +#include "secitem.h" +#include "secport.h" +#include "secder.h" +#include "secoid.h" +#include "p12local.h" +#include "secerr.h" + + +/* allocate space for a PFX structure and set up initial + * arena pool. pfx structure is cleared and a pointer to + * the new structure is returned. + */ +SEC_PKCS12PFXItem * +sec_pkcs12_new_pfx(void) +{ + SEC_PKCS12PFXItem *pfx = NULL; + PRArenaPool *poolp = NULL; + + poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); /* XXX Different size? */ + if(poolp == NULL) + goto loser; + + pfx = (SEC_PKCS12PFXItem *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12PFXItem)); + if(pfx == NULL) + goto loser; + pfx->poolp = poolp; + + return pfx; + +loser: + PORT_FreeArena(poolp, PR_TRUE); + return NULL; +} + +/* allocate space for a PFX structure and set up initial + * arena pool. pfx structure is cleared and a pointer to + * the new structure is returned. + */ +SEC_PKCS12AuthenticatedSafe * +sec_pkcs12_new_asafe(PRArenaPool *poolp) +{ + SEC_PKCS12AuthenticatedSafe *asafe = NULL; + void *mark; + + mark = PORT_ArenaMark(poolp); + asafe = (SEC_PKCS12AuthenticatedSafe *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12AuthenticatedSafe)); + if(asafe == NULL) + goto loser; + asafe->poolp = poolp; + PORT_Memset(&asafe->old_baggage, 0, sizeof(SEC_PKCS7ContentInfo)); + + PORT_ArenaUnmark(poolp, mark); + return asafe; + +loser: + PORT_ArenaRelease(poolp, mark); + return NULL; +} + +/* create a safe contents structure with a list of + * length 0 with the first element being NULL + */ +SEC_PKCS12SafeContents * +sec_pkcs12_create_safe_contents(PRArenaPool *poolp) +{ + SEC_PKCS12SafeContents *safe; + void *mark; + + if(poolp == NULL) + return NULL; + + /* allocate structure */ + mark = PORT_ArenaMark(poolp); + safe = (SEC_PKCS12SafeContents *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12SafeContents)); + if(safe == NULL) + { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + + /* init list */ + safe->contents = (SEC_PKCS12SafeBag**)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12SafeBag *)); + if(safe->contents == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + safe->contents[0] = NULL; + safe->poolp = poolp; + safe->safe_size = 0; + PORT_ArenaUnmark(poolp, mark); + return safe; +} + +/* create a new external bag which is appended onto the list + * of bags in baggage. the bag is created in the same arena + * as baggage + */ +SEC_PKCS12BaggageItem * +sec_pkcs12_create_external_bag(SEC_PKCS12Baggage *luggage) +{ + void *dummy, *mark; + SEC_PKCS12BaggageItem *bag; + + if(luggage == NULL) { + return NULL; + } + + mark = PORT_ArenaMark(luggage->poolp); + + /* allocate space for null terminated bag list */ + if(luggage->bags == NULL) { + luggage->bags=(SEC_PKCS12BaggageItem**)PORT_ArenaZAlloc(luggage->poolp, + sizeof(SEC_PKCS12BaggageItem *)); + if(luggage->bags == NULL) { + goto loser; + } + luggage->luggage_size = 0; + } + + /* grow the list */ + dummy = PORT_ArenaGrow(luggage->poolp, luggage->bags, + sizeof(SEC_PKCS12BaggageItem *) * (luggage->luggage_size + 1), + sizeof(SEC_PKCS12BaggageItem *) * (luggage->luggage_size + 2)); + if(dummy == NULL) { + goto loser; + } + luggage->bags = (SEC_PKCS12BaggageItem**)dummy; + + luggage->bags[luggage->luggage_size] = + (SEC_PKCS12BaggageItem *)PORT_ArenaZAlloc(luggage->poolp, + sizeof(SEC_PKCS12BaggageItem)); + if(luggage->bags[luggage->luggage_size] == NULL) { + goto loser; + } + + /* create new bag and append it to the end */ + bag = luggage->bags[luggage->luggage_size]; + bag->espvks = (SEC_PKCS12ESPVKItem **)PORT_ArenaZAlloc( + luggage->poolp, + sizeof(SEC_PKCS12ESPVKItem *)); + bag->unencSecrets = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc( + luggage->poolp, + sizeof(SEC_PKCS12SafeBag *)); + if((bag->espvks == NULL) || (bag->unencSecrets == NULL)) { + goto loser; + } + + bag->poolp = luggage->poolp; + luggage->luggage_size++; + luggage->bags[luggage->luggage_size] = NULL; + bag->espvks[0] = NULL; + bag->unencSecrets[0] = NULL; + bag->nEspvks = bag->nSecrets = 0; + + PORT_ArenaUnmark(luggage->poolp, mark); + return bag; + +loser: + PORT_ArenaRelease(luggage->poolp, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; +} + +/* creates a baggage witha NULL terminated 0 length list */ +SEC_PKCS12Baggage * +sec_pkcs12_create_baggage(PRArenaPool *poolp) +{ + SEC_PKCS12Baggage *luggage; + void *mark; + + if(poolp == NULL) + return NULL; + + mark = PORT_ArenaMark(poolp); + + /* allocate bag */ + luggage = (SEC_PKCS12Baggage *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12Baggage)); + if(luggage == NULL) + { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + + /* init list */ + luggage->bags = (SEC_PKCS12BaggageItem **)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12BaggageItem *)); + if(luggage->bags == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + + luggage->bags[0] = NULL; + luggage->luggage_size = 0; + luggage->poolp = poolp; + + PORT_ArenaUnmark(poolp, mark); + return luggage; +} + +/* free pfx structure and associated items in the arena */ +void +SEC_PKCS12DestroyPFX(SEC_PKCS12PFXItem *pfx) +{ + if (pfx != NULL && pfx->poolp != NULL) + { + PORT_FreeArena(pfx->poolp, PR_TRUE); + } +} diff --git a/security/nss/lib/pkcs12/p12d.c b/security/nss/lib/pkcs12/p12d.c new file mode 100644 index 000000000..051428575 --- /dev/null +++ b/security/nss/lib/pkcs12/p12d.c @@ -0,0 +1,3328 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + + +#include "nssrenam.h" +#include "p12t.h" +#include "p12.h" +#include "plarena.h" +#include "secitem.h" +#include "secoid.h" +#include "seccomon.h" +#include "secport.h" +#include "cert.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "secerr.h" +#include "pk11func.h" +#include "p12plcy.h" +#include "p12local.h" +#include "alghmac.h" +#include "secder.h" +#include "secport.h" + +#include "certdb.h" + +#include "prcpucfg.h" + +typedef struct sec_PKCS12SafeContentsContextStr sec_PKCS12SafeContentsContext; + +/* Opaque structure for decoding SafeContents. These are used + * for each authenticated safe as well as any nested safe contents. + */ +struct sec_PKCS12SafeContentsContextStr { + /* the parent decoder context */ + SEC_PKCS12DecoderContext *p12dcx; + + /* memory arena to allocate space from */ + PRArenaPool *arena; + + /* decoder context and destination for decoding safe contents */ + SEC_ASN1DecoderContext *safeContentsDcx; + sec_PKCS12SafeContents safeContents; + + /* information for decoding safe bags within the safe contents. + * these variables are updated for each safe bag decoded. + */ + SEC_ASN1DecoderContext *currentSafeBagDcx; + sec_PKCS12SafeBag *currentSafeBag; + PRBool skipCurrentSafeBag; + + /* if the safe contents is nested, the parent is pointed to here. */ + sec_PKCS12SafeContentsContext *nestedCtx; +}; + +/* opaque decoder context structure. information for decoding a pkcs 12 + * PDU are stored here as well as decoding pointers for intermediary + * structures which are part of the PKCS 12 PDU. Upon a successful + * decode, the safe bags containing certificates and keys encountered. + */ +struct SEC_PKCS12DecoderContextStr { + PRArenaPool *arena; + PK11SlotInfo *slot; + void *wincx; + PRBool error; + int errorValue; + + /* password */ + SECItem *pwitem; + + /* used for decoding the PFX structure */ + SEC_ASN1DecoderContext *pfxDcx; + sec_PKCS12PFXItem pfx; + + /* safe bags found during decoding */ + sec_PKCS12SafeBag **safeBags; + unsigned int safeBagCount; + + /* state variables for decoding authenticated safes. */ + SEC_PKCS7DecoderContext *currentASafeP7Dcx; + SEC_PKCS5KeyAndPassword *currentASafeKeyPwd; + SEC_ASN1DecoderContext *aSafeDcx; + SEC_PKCS7DecoderContext *aSafeP7Dcx; + sec_PKCS12AuthenticatedSafe authSafe; + SEC_PKCS7ContentInfo *aSafeCinfo; + sec_PKCS12SafeContents safeContents; + + /* safe contents info */ + unsigned int safeContentsCnt; + sec_PKCS12SafeContentsContext **safeContentsList; + + /* HMAC info */ + sec_PKCS12MacData macData; + SEC_ASN1DecoderContext *hmacDcx; + + /* routines for reading back the data to be hmac'd */ + digestOpenFn dOpen; + digestCloseFn dClose; + digestIOFn dRead, dWrite; + void *dArg; + + /* helper functions */ + SECKEYGetPasswordKey pwfn; + void *pwfnarg; + PRBool swapUnicodeBytes; + + /* import information */ + PRBool bagsVerified; + + /* buffer management for the default callbacks implementation */ + void *buffer; /* storage area */ + PRInt32 filesize; /* actual data size */ + PRInt32 allocated; /* total buffer size allocated */ + PRInt32 currentpos; /* position counter */ +}; + + +/* make sure that the PFX version being decoded is a version + * which we support. + */ +static PRBool +sec_pkcs12_proper_version(sec_PKCS12PFXItem *pfx) +{ + /* if no version, assume it is not supported */ + if(pfx->version.len == 0) { + return PR_FALSE; + } + + if(DER_GetInteger(&pfx->version) > SEC_PKCS12_VERSION) { + return PR_FALSE; + } + + return PR_TRUE; +} + +/* retrieve the key for decrypting the safe contents */ +static PK11SymKey * +sec_pkcs12_decoder_get_decrypt_key(void *arg, SECAlgorithmID *algid) +{ + SEC_PKCS5KeyAndPassword *keyPwd = + (SEC_PKCS5KeyAndPassword *)arg; + + if(!keyPwd) { + return NULL; + } + + /* if no slot specified, use the internal key slot */ + if(!keyPwd->slot) { + keyPwd->slot = PK11_GetInternalKeySlot(); + } + + /* retrieve the key */ + if(!keyPwd->key) { + keyPwd->key = PK11_PBEKeyGen(keyPwd->slot, algid, + keyPwd->pwitem, PR_FALSE, keyPwd->wincx); + } + + return (PK11SymKey *)keyPwd; +} + +/* XXX this needs to be modified to handle enveloped data. most + * likely, it should mirror the routines for SMIME in that regard. + */ +static PRBool +sec_pkcs12_decoder_decryption_allowed(SECAlgorithmID *algid, + PK11SymKey *bulkkey) +{ + PRBool decryptionAllowed = SEC_PKCS12DecryptionAllowed(algid); + + if(!decryptionAllowed) { + return PR_FALSE; + } + + return PR_TRUE; +} + +/* when we encounter a new safe bag during the decoding, we need + * to allocate space for the bag to be decoded to and set the + * state variables appropriately. all of the safe bags are allocated + * in a buffer in the outer SEC_PKCS12DecoderContext, however, + * a pointer to the safeBag is also used in the sec_PKCS12SafeContentsContext + * for the current bag. + */ +static SECStatus +sec_pkcs12_decoder_init_new_safe_bag(sec_PKCS12SafeContentsContext + *safeContentsCtx) +{ + void *mark = NULL; + SEC_PKCS12DecoderContext *p12dcx; + + /* make sure that the structures are defined, and there has + * not been an error in the decoding + */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error) { + return SECFailure; + } + + p12dcx = safeContentsCtx->p12dcx; + mark = PORT_ArenaMark(p12dcx->arena); + + /* allocate a new safe bag, if bags already exist, grow the + * list of bags, otherwise allocate a new list. the list is + * NULL terminated. + */ + if(p12dcx->safeBagCount) { + p12dcx->safeBags = + (sec_PKCS12SafeBag**)PORT_ArenaGrow(p12dcx->arena,p12dcx->safeBags, + (p12dcx->safeBagCount + 1) * sizeof(sec_PKCS12SafeBag *), + (p12dcx->safeBagCount + 2) * sizeof(sec_PKCS12SafeBag *)); + } else { + p12dcx->safeBags = (sec_PKCS12SafeBag**)PORT_ArenaZAlloc(p12dcx->arena, + 2 * sizeof(sec_PKCS12SafeBag *)); + } + if(!p12dcx->safeBags) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* append the bag to the end of the list and update the reference + * in the safeContentsCtx. + */ + p12dcx->safeBags[p12dcx->safeBagCount] = + (sec_PKCS12SafeBag*)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeBag)); + safeContentsCtx->currentSafeBag = p12dcx->safeBags[p12dcx->safeBagCount]; + p12dcx->safeBags[++p12dcx->safeBagCount] = NULL; + if(!safeContentsCtx->currentSafeBag) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + safeContentsCtx->currentSafeBag->slot = safeContentsCtx->p12dcx->slot; + safeContentsCtx->currentSafeBag->pwitem = safeContentsCtx->p12dcx->pwitem; + safeContentsCtx->currentSafeBag->swapUnicodeBytes = + safeContentsCtx->p12dcx->swapUnicodeBytes; + safeContentsCtx->currentSafeBag->arena = safeContentsCtx->p12dcx->arena; + + PORT_ArenaUnmark(p12dcx->arena, mark); + return SECSuccess; + +loser: + + /* if an error occurred, release the memory and set the error flag + * the only possible errors triggered by this function are memory + * related. + */ + if(mark) { + PORT_ArenaRelease(p12dcx->arena, mark); + } + + p12dcx->error = PR_TRUE; + return SECFailure; +} + +/* A wrapper for updating the ASN1 context in which a safeBag is + * being decoded. This function is called as a callback from + * secasn1d when decoding SafeContents structures. + */ +static void +sec_pkcs12_decoder_safe_bag_update(void *arg, const char *data, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext *)arg; + SEC_PKCS12DecoderContext *p12dcx; + SECStatus rv; + + /* make sure that we are not skipping the current safeBag, + * and that there are no errors. If so, just return rather + * than continuing to process. + */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error + || safeContentsCtx->skipCurrentSafeBag) { + return; + } + p12dcx = safeContentsCtx->p12dcx; + + rv = SEC_ASN1DecoderUpdate(safeContentsCtx->currentSafeBagDcx, data, len); + if(rv != SECSuccess) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + return; + +loser: + /* set the error, and finish the decoder context. because there + * is not a way of returning an error message, it may be worth + * while to do a check higher up and finish any decoding contexts + * that are still open. + */ + p12dcx->error = PR_TRUE; + SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagDcx); + safeContentsCtx->currentSafeBagDcx = NULL; + return; +} + +/* forward declarations of functions that are used when decoding + * safeContents bags which are nested and when decoding the + * authenticatedSafes. + */ +static SECStatus +sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext + *safeContentsCtx); +static SECStatus +sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext + *safeContentsCtx); +static void +sec_pkcs12_decoder_safe_bag_update(void *arg, const char *data, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind); + +/* notify function for decoding safeBags. This function is + * used to filter safeBag types which are not supported, + * initiate the decoding of nested safe contents, and decode + * safeBags in general. this function is set when the decoder + * context for the safeBag is first created. + */ +static void +sec_pkcs12_decoder_safe_bag_notify(void *arg, PRBool before, + void *dest, int real_depth) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext *)arg; + SEC_PKCS12DecoderContext *p12dcx; + sec_PKCS12SafeBag *bag; + PRBool after; + + /* if an error is encountered, return */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx || + safeContentsCtx->p12dcx->error) { + return; + } + p12dcx = safeContentsCtx->p12dcx; + + /* to make things more readable */ + if(before) + after = PR_FALSE; + else + after = PR_TRUE; + + /* have we determined the safeBagType yet? */ + bag = safeContentsCtx->currentSafeBag; + if(bag->bagTypeTag == NULL) { + if(after && (dest == &(bag->safeBagType))) { + bag->bagTypeTag = SECOID_FindOID(&(bag->safeBagType)); + if(bag->bagTypeTag == NULL) { + p12dcx->error = PR_TRUE; + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + } + } + return; + } + + /* process the safeBag depending on it's type. those + * which we do not support, are ignored. we start a decoding + * context for a nested safeContents. + */ + switch(bag->bagTypeTag->offset) { + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + break; + case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: + /* if we are just starting to decode the safeContents, initialize + * a new safeContentsCtx to process it. + */ + if(before && (dest == &(bag->safeBagContent))) { + sec_pkcs12_decoder_begin_nested_safe_contents(safeContentsCtx); + } else if(after && (dest == &(bag->safeBagContent))) { + /* clean up the nested decoding */ + sec_pkcs12_decoder_finish_nested_safe_contents(safeContentsCtx); + } + break; + case SEC_OID_PKCS12_V1_CRL_BAG_ID: + case SEC_OID_PKCS12_V1_SECRET_BAG_ID: + default: + /* skip any safe bag types we don't understand or handle */ + safeContentsCtx->skipCurrentSafeBag = PR_TRUE; + break; + } + + return; +} + +/* notify function for decoding safe contents. each entry in the + * safe contents is a safeBag which needs to be allocated and + * the decoding context initialized at the beginning and then + * the context needs to be closed and finished at the end. + * + * this function is set when the safeContents decode context is + * initialized. + */ +static void +sec_pkcs12_decoder_safe_contents_notify(void *arg, PRBool before, + void *dest, int real_depth) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext*)arg; + SEC_PKCS12DecoderContext *p12dcx; + SECStatus rv; + + /* if there is an error we don't want to continue processing, + * just return and keep going. + */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error) { + return; + } + p12dcx = safeContentsCtx->p12dcx; + + /* if we are done with the current safeBag, then we need to + * finish the context and set the state variables appropriately. + */ + if(!before) { + SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsDcx); + SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagDcx); + safeContentsCtx->currentSafeBagDcx = NULL; + safeContentsCtx->skipCurrentSafeBag = PR_FALSE; + } else { + /* we are starting a new safe bag. we need to allocate space + * for the bag and initialize the decoding context. + */ + rv = sec_pkcs12_decoder_init_new_safe_bag(safeContentsCtx); + if(rv != SECSuccess) { + goto loser; + } + + /* set up the decoder context */ + safeContentsCtx->currentSafeBagDcx = SEC_ASN1DecoderStart(p12dcx->arena, + safeContentsCtx->currentSafeBag, + sec_PKCS12SafeBagTemplate); + if(!safeContentsCtx->currentSafeBagDcx) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* set the notify and filter procs so that the safe bag + * data gets sent to the proper location when decoding. + */ + SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->currentSafeBagDcx, + sec_pkcs12_decoder_safe_bag_notify, + safeContentsCtx); + SEC_ASN1DecoderSetFilterProc(safeContentsCtx->safeContentsDcx, + sec_pkcs12_decoder_safe_bag_update, + safeContentsCtx, PR_TRUE); + } + + return; + +loser: + /* in the event of an error, we want to close the decoding + * context and clear the filter and notify procedures. + */ + p12dcx->error = PR_TRUE; + + if(safeContentsCtx->currentSafeBagDcx) { + SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagDcx); + safeContentsCtx->currentSafeBagDcx = NULL; + } + + SEC_ASN1DecoderClearNotifyProc(safeContentsCtx->safeContentsDcx); + SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsDcx); + + return; +} + +/* initialize the safeContents for decoding. this routine + * is used for authenticatedSafes as well as nested safeContents. + */ +static sec_PKCS12SafeContentsContext * +sec_pkcs12_decoder_safe_contents_init_decode(SEC_PKCS12DecoderContext *p12dcx, + PRBool nestedSafe) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = NULL; + const SEC_ASN1Template *theTemplate; + + if(!p12dcx || p12dcx->error) { + return NULL; + } + + /* allocate a new safeContents list or grow the existing list and + * append the new safeContents onto the end. + */ + if(!p12dcx->safeContentsCnt) { + p12dcx->safeContentsList = + (sec_PKCS12SafeContentsContext**)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeContentsContext *)); + } else { + p12dcx->safeContentsList = + (sec_PKCS12SafeContentsContext **) PORT_ArenaGrow(p12dcx->arena, + p12dcx->safeContentsList, + (p12dcx->safeContentsCnt * + sizeof(sec_PKCS12SafeContentsContext *)), + (1 + p12dcx->safeContentsCnt * + sizeof(sec_PKCS12SafeContentsContext *))); + } + if(!p12dcx->safeContentsList) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + p12dcx->safeContentsList[p12dcx->safeContentsCnt] = + (sec_PKCS12SafeContentsContext*)PORT_ArenaZAlloc( + p12dcx->arena, + sizeof(sec_PKCS12SafeContentsContext)); + p12dcx->safeContentsList[p12dcx->safeContentsCnt+1] = NULL; + if(!p12dcx->safeContentsList[p12dcx->safeContentsCnt]) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* set up the state variables */ + safeContentsCtx = p12dcx->safeContentsList[p12dcx->safeContentsCnt]; + p12dcx->safeContentsCnt++; + safeContentsCtx->p12dcx = p12dcx; + safeContentsCtx->arena = p12dcx->arena; + + /* begin the decoding -- the template is based on whether we are + * decoding a nested safeContents or not. + */ + if(nestedSafe == PR_TRUE) { + theTemplate = sec_PKCS12NestedSafeContentsDecodeTemplate; + } else { + theTemplate = sec_PKCS12SafeContentsDecodeTemplate; + } + + /* start the decoder context */ + safeContentsCtx->safeContentsDcx = SEC_ASN1DecoderStart(p12dcx->arena, + &safeContentsCtx->safeContents, + theTemplate); + + if(!safeContentsCtx->safeContentsDcx) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* set the safeContents notify procedure to look for + * and start the decode of safeBags. + */ + SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->safeContentsDcx, + sec_pkcs12_decoder_safe_contents_notify, + safeContentsCtx); + + return safeContentsCtx; + +loser: + /* in the case of an error, we want to finish the decoder + * context and set the error flag. + */ + if(safeContentsCtx && safeContentsCtx->safeContentsDcx) { + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsDcx); + safeContentsCtx->safeContentsDcx = NULL; + } + + p12dcx->error = PR_TRUE; + + return NULL; +} + +/* wrapper for updating safeContents. this is set as the filter of + * safeBag when there is a nested safeContents. + */ +static void +sec_pkcs12_decoder_nested_safe_contents_update(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext *)arg; + SEC_PKCS12DecoderContext *p12dcx; + SECStatus rv; + + /* check for an error */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error) { + return; + } + + /* no need to update if no data sent in */ + if(!len || !buf) { + return; + } + + /* update the decoding context */ + p12dcx = safeContentsCtx->p12dcx; + rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsDcx, buf, len); + if(rv != SECSuccess) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + return; + +loser: + /* handle any errors. If a decoding context is open, close it. */ + p12dcx->error = PR_TRUE; + if(safeContentsCtx->safeContentsDcx) { + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsDcx); + safeContentsCtx->safeContentsDcx = NULL; + } +} + +/* whenever a new safeContentsSafeBag is encountered, we need + * to init a safeContentsContext. + */ +static SECStatus +sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext + *safeContentsCtx) +{ + /* check for an error */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx || + safeContentsCtx->p12dcx->error) { + return SECFailure; + } + + safeContentsCtx->nestedCtx = sec_pkcs12_decoder_safe_contents_init_decode( + safeContentsCtx->p12dcx, + PR_TRUE); + if(!safeContentsCtx->nestedCtx) { + return SECFailure; + } + + /* set up new filter proc */ + SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->nestedCtx->safeContentsDcx, + sec_pkcs12_decoder_safe_contents_notify, + safeContentsCtx->nestedCtx); + SEC_ASN1DecoderSetFilterProc(safeContentsCtx->currentSafeBagDcx, + sec_pkcs12_decoder_nested_safe_contents_update, + safeContentsCtx->nestedCtx, PR_TRUE); + + return SECSuccess; +} + +/* when the safeContents is done decoding, we need to reset the + * proper filter and notify procs and close the decoding context + */ +static SECStatus +sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext + *safeContentsCtx) +{ + /* check for error */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx || + safeContentsCtx->p12dcx->error) { + return SECFailure; + } + + /* clean up */ + SEC_ASN1DecoderClearFilterProc(safeContentsCtx->currentSafeBagDcx); + SEC_ASN1DecoderClearNotifyProc(safeContentsCtx->nestedCtx->safeContentsDcx); + SEC_ASN1DecoderFinish(safeContentsCtx->nestedCtx->safeContentsDcx); + safeContentsCtx->nestedCtx->safeContentsDcx = NULL; + safeContentsCtx->nestedCtx = NULL; + + return SECSuccess; +} + +/* wrapper for updating safeContents. This is used when decoding + * the nested safeContents and any authenticatedSafes. + */ +static void +sec_pkcs12_decoder_safe_contents_callback(void *arg, const char *buf, + unsigned long len) +{ + SECStatus rv; + sec_PKCS12SafeContentsContext *safeContentsCtx = + (sec_PKCS12SafeContentsContext *)arg; + SEC_PKCS12DecoderContext *p12dcx; + + /* check for error */ + if(!safeContentsCtx || !safeContentsCtx->p12dcx + || safeContentsCtx->p12dcx->error) { + return; + } + p12dcx = safeContentsCtx->p12dcx; + + /* update the decoder */ + rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsDcx, buf, len); + if(rv != SECSuccess) { + /* if we fail while trying to decode a 'safe', it's probably because + * we didn't have the correct password. */ + PORT_SetError(SEC_ERROR_BAD_PASSWORD); + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + goto loser; + } + + return; + +loser: + /* set the error and finish the context */ + p12dcx->error = PR_TRUE; + if(safeContentsCtx->safeContentsDcx) { + SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsDcx); + safeContentsCtx->safeContentsDcx = NULL; + } + + return; +} + +/* this is a wrapper for the ASN1 decoder to call SEC_PKCS7DecoderUpdate + */ +static void +sec_pkcs12_decoder_wrap_p7_update(void *arg, const char *data, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + SEC_PKCS7DecoderContext *p7dcx = (SEC_PKCS7DecoderContext *)arg; + + SEC_PKCS7DecoderUpdate(p7dcx, data, len); +} + +/* notify function for decoding aSafes. at the beginning, + * of an authenticatedSafe, we start a decode of a safeContents. + * at the end, we clean up the safeContents decoder context and + * reset state variables + */ +static void +sec_pkcs12_decoder_asafes_notify(void *arg, PRBool before, void *dest, + int real_depth) +{ + SEC_PKCS12DecoderContext *p12dcx; + sec_PKCS12SafeContentsContext *safeContentsCtx; + + /* make sure no error occurred. */ + p12dcx = (SEC_PKCS12DecoderContext *)arg; + if(!p12dcx || p12dcx->error) { + return; + } + + if(before) { + + /* init a new safeContentsContext */ + safeContentsCtx = sec_pkcs12_decoder_safe_contents_init_decode(p12dcx, + PR_FALSE); + if(!safeContentsCtx) { + goto loser; + } + + /* set up password and encryption key information */ + p12dcx->currentASafeKeyPwd = + (SEC_PKCS5KeyAndPassword*)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(SEC_PKCS5KeyAndPassword)); + if(!p12dcx->currentASafeKeyPwd) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + p12dcx->currentASafeKeyPwd->pwitem = p12dcx->pwitem; + p12dcx->currentASafeKeyPwd->slot = p12dcx->slot; + p12dcx->currentASafeKeyPwd->wincx = p12dcx->wincx; + + /* initiate the PKCS7ContentInfo decode */ + p12dcx->currentASafeP7Dcx = SEC_PKCS7DecoderStart( + sec_pkcs12_decoder_safe_contents_callback, + safeContentsCtx, + p12dcx->pwfn, p12dcx->pwfnarg, + sec_pkcs12_decoder_get_decrypt_key, + p12dcx->currentASafeKeyPwd, + sec_pkcs12_decoder_decryption_allowed); + if(!p12dcx->currentASafeP7Dcx) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + SEC_ASN1DecoderSetFilterProc(p12dcx->aSafeDcx, + sec_pkcs12_decoder_wrap_p7_update, + p12dcx->currentASafeP7Dcx, PR_TRUE); + } + + if(!before) { + /* if one is being decoded, finish the decode */ + if(p12dcx->currentASafeP7Dcx != NULL) { + if(!SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx)) { + p12dcx->currentASafeP7Dcx = NULL; + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + p12dcx->currentASafeP7Dcx = NULL; + } + p12dcx->currentASafeP7Dcx = NULL; + if(p12dcx->currentASafeKeyPwd->key != NULL) { + p12dcx->currentASafeKeyPwd->key = NULL; + } + } + + + return; + +loser: + /* set the error flag */ + p12dcx->error = PR_TRUE; + return; +} + +/* wrapper for updating asafes decoding context. this function + * writes data being decoded to disk, so that a mac can be computed + * later. + */ +static void +sec_pkcs12_decoder_asafes_callback(void *arg, const char *buf, + unsigned long len) +{ + SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *)arg; + SECStatus rv; + + if(!p12dcx || p12dcx->error) { + return; + } + + /* update the context */ + rv = SEC_ASN1DecoderUpdate(p12dcx->aSafeDcx, buf, len); + if(rv != SECSuccess) { + p12dcx->error = (PRBool)SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* if we are writing to a file, write out the new information */ + if(p12dcx->dWrite) { + unsigned long writeLen = (*p12dcx->dWrite)(p12dcx->dArg, + (unsigned char *)buf, len); + if(writeLen != len) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + } + + return; + +loser: + /* set the error flag */ + p12dcx->error = PR_TRUE; + SEC_ASN1DecoderFinish(p12dcx->aSafeDcx); + p12dcx->aSafeDcx = NULL; + + return; +} + +/* start the decode of an authenticatedSafe contentInfo. + */ +static SECStatus +sec_pkcs12_decode_start_asafes_cinfo(SEC_PKCS12DecoderContext *p12dcx) +{ + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + /* start the decode context */ + p12dcx->aSafeDcx = SEC_ASN1DecoderStart(p12dcx->arena, + &p12dcx->authSafe, + sec_PKCS12AuthenticatedSafeTemplate); + if(!p12dcx->aSafeDcx) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* set the notify function */ + SEC_ASN1DecoderSetNotifyProc(p12dcx->aSafeDcx, + sec_pkcs12_decoder_asafes_notify, p12dcx); + + /* begin the authSafe decoder context */ + p12dcx->aSafeP7Dcx = SEC_PKCS7DecoderStart( + sec_pkcs12_decoder_asafes_callback, p12dcx, + p12dcx->pwfn, p12dcx->pwfnarg, NULL, NULL, NULL); + if(!p12dcx->aSafeP7Dcx) { + p12dcx->errorValue = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* open the temp file for writing, if the filter functions were set */ + if(p12dcx->dOpen && (*p12dcx->dOpen)(p12dcx->dArg, PR_FALSE) + != SECSuccess) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + + return SECSuccess; + +loser: + p12dcx->error = PR_TRUE; + + if(p12dcx->aSafeDcx) { + SEC_ASN1DecoderFinish(p12dcx->aSafeDcx); + p12dcx->aSafeDcx = NULL; + } + + if(p12dcx->aSafeP7Dcx) { + SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx); + p12dcx->aSafeP7Dcx = NULL; + } + + return SECFailure; +} + +/* wrapper for updating the safeContents. this function is used as + * a filter for the pfx when decoding the authenticated safes + */ +static void +sec_pkcs12_decode_asafes_cinfo_update(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + SEC_PKCS12DecoderContext *p12dcx; + SECStatus rv; + + p12dcx = (SEC_PKCS12DecoderContext*)arg; + if(!p12dcx || p12dcx->error) { + return; + } + + /* update the safeContents decoder */ + rv = SEC_PKCS7DecoderUpdate(p12dcx->aSafeP7Dcx, buf, len); + if(rv != SECSuccess) { + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + goto loser; + } + + return; + +loser: + + /* did we find an error? if so, close the context and set the + * error flag. + */ + SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx); + p12dcx->aSafeP7Dcx = NULL; + p12dcx->error = PR_TRUE; +} + +/* notify procedure used while decoding the pfx. When we encounter + * the authSafes, we want to trigger the decoding of authSafes as well + * as when we encounter the macData, trigger the decoding of it. we do + * this because we we are streaming the decoder and not decoding in place. + * the pfx which is the destination, only has the version decoded into it. + */ +static void +sec_pkcs12_decoder_pfx_notify_proc(void *arg, PRBool before, void *dest, + int real_depth) +{ + SECStatus rv; + SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext*)arg; + + /* if an error occurrs, clear the notifyProc and the filterProc + * and continue. + */ + if(p12dcx->error) { + SEC_ASN1DecoderClearNotifyProc(p12dcx->pfxDcx); + SEC_ASN1DecoderClearFilterProc(p12dcx->pfxDcx); + return; + } + + if(before && (dest == &p12dcx->pfx.encodedAuthSafe)) { + + /* we want to make sure this is a version we support */ + if(!sec_pkcs12_proper_version(&p12dcx->pfx)) { + p12dcx->errorValue = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION; + goto loser; + } + + /* start the decode of the aSafes cinfo... */ + rv = sec_pkcs12_decode_start_asafes_cinfo(p12dcx); + if(rv != SECSuccess) { + goto loser; + } + + /* set the filter proc to update the authenticated safes. */ + SEC_ASN1DecoderSetFilterProc(p12dcx->pfxDcx, + sec_pkcs12_decode_asafes_cinfo_update, + p12dcx, PR_TRUE); + } + + if(!before && (dest == &p12dcx->pfx.encodedAuthSafe)) { + + /* we are done decoding the authenticatedSafes, so we need to + * finish the decoderContext and clear the filter proc + * and close the hmac callback, if present + */ + p12dcx->aSafeCinfo = SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx); + p12dcx->aSafeP7Dcx = NULL; + if(!p12dcx->aSafeCinfo) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + SEC_ASN1DecoderClearFilterProc(p12dcx->pfxDcx); + if(p12dcx->dClose && ((*p12dcx->dClose)(p12dcx->dArg, PR_FALSE) + != SECSuccess)) { + p12dcx->errorValue = PORT_GetError(); + goto loser; + } + + } + + return; + +loser: + p12dcx->error = PR_TRUE; +} + +/* default implementations of the open/close/read/write functions for + SEC_PKCS12DecoderStart +*/ + +#define DEFAULT_TEMP_SIZE 4096 + +static SECStatus +p12u_DigestOpen(void *arg, PRBool readData) +{ + SEC_PKCS12DecoderContext* p12cxt = arg; + + p12cxt->currentpos = 0; + + if (PR_FALSE == readData) { + /* allocate an initial buffer */ + p12cxt->filesize = 0; + p12cxt->allocated = DEFAULT_TEMP_SIZE; + p12cxt->buffer = PORT_Alloc(DEFAULT_TEMP_SIZE); + PR_ASSERT(p12cxt->buffer); + } + else + { + PR_ASSERT(p12cxt->buffer); + if (!p12cxt->buffer) { + return SECFailure; /* no data to read */ + } + } + + return SECSuccess; +} + +static SECStatus +p12u_DigestClose(void *arg, PRBool removeFile) +{ + SEC_PKCS12DecoderContext* p12cxt = arg; + + PR_ASSERT(p12cxt); + if (!p12cxt) { + return SECFailure; + } + p12cxt->currentpos = 0; + + if (PR_TRUE == removeFile) { + PR_ASSERT(p12cxt->buffer); + if (!p12cxt->buffer) { + return SECFailure; + } + if (p12cxt->buffer) { + PORT_Free(p12cxt->buffer); + p12cxt->buffer = NULL; + p12cxt->allocated = 0; + p12cxt->filesize = 0; + } + } + + return SECSuccess; +} + +static int +p12u_DigestRead(void *arg, unsigned char *buf, unsigned long len) +{ + int toread = len; + SEC_PKCS12DecoderContext* p12cxt = arg; + + if(!buf || len == 0) { + return -1; + } + + if (!p12cxt->buffer || ((p12cxt->filesize-p12cxt->currentpos)<len) ) { + /* trying to read past the end of the buffer */ + toread = p12cxt->filesize-p12cxt->currentpos; + } + memcpy(buf, (void*)((char*)p12cxt->buffer+p12cxt->currentpos), toread); + p12cxt->currentpos += toread; + return toread; +} + +static int +p12u_DigestWrite(void *arg, unsigned char *buf, unsigned long len) +{ + SEC_PKCS12DecoderContext* p12cxt = arg; + + if(!buf || len == 0) { + return -1; + } + + if (p12cxt->currentpos+len > p12cxt->filesize) { + p12cxt->filesize = p12cxt->currentpos + len; + } + else { + p12cxt->filesize += len; + } + if (p12cxt->filesize > p12cxt->allocated) { + void* newbuffer; + size_t newsize = p12cxt->filesize + DEFAULT_TEMP_SIZE; + newbuffer = PORT_Realloc(p12cxt->buffer, newsize); + if (NULL == newbuffer) { + return -1; /* can't extend the buffer */ + } + p12cxt->buffer = newbuffer; + p12cxt->allocated = newsize; + } + PR_ASSERT(p12cxt->buffer); + memcpy((void*)((char*)p12cxt->buffer+p12cxt->currentpos), buf, len); + p12cxt->currentpos += len; + return len; +} + +/* SEC_PKCS12DecoderStart + * Creates a decoder context for decoding a PKCS 12 PDU objct. + * This function sets up the initial decoding context for the + * PFX and sets the needed state variables. + * + * pwitem - the password for the hMac and any encoded safes. + * this should be changed to take a callback which retrieves + * the password. it may be possible for different safes to + * have different passwords. also, the password is already + * in unicode. it should probably be converted down below via + * a unicode conversion callback. + * slot - the slot to import the dataa into should multiple slots + * be supported based on key type and cert type? + * dOpen, dClose, dRead, dWrite - digest routines for writing data + * to a file so it could be read back and the hmack recomputed + * and verified. doesn't seem to be away for both encoding + * and decoding to be single pass, thus the need for these + * routines. + * dArg - the argument for dOpen, etc. + * + * if NULL == dOpen == dClose == dRead == dWrite == dArg, then default + * implementations using a memory buffer are used + * + * This function returns the decoder context, if it was successful. + * Otherwise, null is returned. + */ +SEC_PKCS12DecoderContext * +SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx, + digestOpenFn dOpen, digestCloseFn dClose, + digestIOFn dRead, digestIOFn dWrite, void *dArg) +{ + SEC_PKCS12DecoderContext *p12dcx; + PRArenaPool *arena; + + arena = PORT_NewArena(2048); /* different size? */ + if(!arena) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* allocate the decoder context and set the state variables */ + p12dcx = (SEC_PKCS12DecoderContext*)PORT_ArenaZAlloc(arena, sizeof(SEC_PKCS12DecoderContext)); + if(!p12dcx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + if (!dOpen && !dClose && !dRead && !dWrite && !dArg) { + /* use default implementations */ + dOpen = p12u_DigestOpen; + dClose = p12u_DigestClose; + dRead = p12u_DigestRead; + dWrite = p12u_DigestWrite; + dArg = (void*)p12dcx; + } + + p12dcx->arena = arena; + p12dcx->pwitem = pwitem; + p12dcx->slot = (slot ? slot : PK11_GetInternalKeySlot()); + p12dcx->wincx = wincx; +#ifdef IS_LITTLE_ENDIAN + p12dcx->swapUnicodeBytes = PR_TRUE; +#else + p12dcx->swapUnicodeBytes = PR_FALSE; +#endif + p12dcx->errorValue = 0; + p12dcx->error = PR_FALSE; + + /* a slot is *required */ + if(!slot) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* start the decoding of the PFX and set the notify proc + * for the PFX item. + */ + p12dcx->pfxDcx = SEC_ASN1DecoderStart(p12dcx->arena, &p12dcx->pfx, + sec_PKCS12PFXItemTemplate); + if(!p12dcx->pfxDcx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + SEC_ASN1DecoderSetNotifyProc(p12dcx->pfxDcx, + sec_pkcs12_decoder_pfx_notify_proc, + p12dcx); + + /* set up digest functions */ + p12dcx->dOpen = dOpen; + p12dcx->dWrite = dWrite; + p12dcx->dClose = dClose; + p12dcx->dRead = dRead; + p12dcx->dArg = dArg; + + return p12dcx; + +loser: + PORT_FreeArena(arena, PR_TRUE); + return NULL; +} + +/* SEC_PKCS12DecoderUpdate + * Streaming update sending more data to the decoder. If + * an error occurs, SECFailure is returned. + * + * p12dcx - the decoder context + * data, len - the data buffer and length of data to send to + * the update functions. + */ +SECStatus +SEC_PKCS12DecoderUpdate(SEC_PKCS12DecoderContext *p12dcx, + unsigned char *data, unsigned long len) +{ + SECStatus rv; + + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + /* update the PFX decoder context */ + rv = SEC_ASN1DecoderUpdate(p12dcx->pfxDcx, (const char *)data, len); + if(rv != SECSuccess) { + p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + goto loser; + } + + return SECSuccess; + +loser: + + p12dcx->error = PR_TRUE; + return SECFailure; +} + +/* IN_BUF_LEN should be larger than SHA1_LENGTH */ +#define IN_BUF_LEN 80 + +/* verify the hmac by reading the data from the temporary file + * using the routines specified when the decodingContext was + * created and return SECSuccess if the hmac matches. + */ +static SECStatus +sec_pkcs12_decoder_verify_mac(SEC_PKCS12DecoderContext *p12dcx) +{ + SECStatus rv = SECFailure; + SECItem hmacRes; + unsigned char buf[IN_BUF_LEN]; + unsigned int bufLen; + int iteration; + PK11Context *pk11cx = NULL; + SECItem ignore = {0}; + PK11SymKey *symKey; + SECItem *params; + SECOidTag algtag; + CK_MECHANISM_TYPE integrityMech; + + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + /* generate hmac key */ + if(p12dcx->macData.iter.data) { + iteration = (int)DER_GetInteger(&p12dcx->macData.iter); + } else { + iteration = 1; + } + + params = PK11_CreatePBEParams(&p12dcx->macData.macSalt, p12dcx->pwitem, + iteration); + + algtag = SECOID_GetAlgorithmTag(&p12dcx->macData.safeMac.digestAlgorithm); + switch (algtag) { + case SEC_OID_SHA1: + integrityMech = CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN; break; + case SEC_OID_MD5: + integrityMech = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN; break; + case SEC_OID_MD2: + integrityMech = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN; break; + default: + goto loser; + } + + symKey = PK11_KeyGen(NULL, integrityMech, params, 20, NULL); + PK11_DestroyPBEParams(params); + if (!symKey) goto loser; + /* init hmac */ + pk11cx = PK11_CreateContextBySymKey(sec_pkcs12_algtag_to_mech(algtag), + CKA_SIGN, symKey, &ignore); + if(!pk11cx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + rv = PK11_DigestBegin(pk11cx); + + /* try to open the data for readback */ + if(p12dcx->dOpen && ((*p12dcx->dOpen)(p12dcx->dArg, PR_TRUE) + != SECSuccess)) { + goto loser; + } + + /* read the data back IN_BUF_LEN bytes at a time and recompute + * the hmac. if fewer bytes are read than are requested, it is + * assumed that the end of file has been reached. if bytesRead + * is returned as -1, then an error occured reading from the + * file. + */ + while(1) { + int bytesRead = (*p12dcx->dRead)(p12dcx->dArg, buf, IN_BUF_LEN); + if(bytesRead == -1) { + goto loser; + } + + rv = PK11_DigestOp(pk11cx, buf, bytesRead); + if(bytesRead < IN_BUF_LEN) { + break; + } + } + + /* finish the hmac context */ + rv = PK11_DigestFinal(pk11cx, buf, &bufLen, IN_BUF_LEN); + + hmacRes.data = buf; + hmacRes.len = bufLen; + + /* is the hmac computed the same as the hmac which was decoded? */ + rv = SECSuccess; + if(SECITEM_CompareItem(&hmacRes, &p12dcx->macData.safeMac.digest) + != SECEqual) { + PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC); + rv = SECFailure; + } + +loser: + /* close the file and remove it */ + if(p12dcx->dClose) { + (*p12dcx->dClose)(p12dcx->dArg, PR_TRUE); + } + + if(pk11cx) { + PK11_DestroyContext(pk11cx, PR_TRUE); + } + + return rv; +} + +/* SEC_PKCS12DecoderVerify + * Verify the macData or the signature of the decoded PKCS 12 PDU. + * If the signature or the macData do not match, SECFailure is + * returned. + * + * p12dcx - the decoder context + */ +SECStatus +SEC_PKCS12DecoderVerify(SEC_PKCS12DecoderContext *p12dcx) +{ + SECStatus rv = SECSuccess; + + /* make sure that no errors have occured... */ + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + /* check the signature or the mac depending on the type of + * integrity used. + */ + if(p12dcx->pfx.encodedMacData.len) { + rv = SEC_ASN1DecodeItem(p12dcx->arena, &p12dcx->macData, + sec_PKCS12MacDataTemplate, + &p12dcx->pfx.encodedMacData); + if(rv == SECSuccess) { + return sec_pkcs12_decoder_verify_mac(p12dcx); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + if(SEC_PKCS7VerifySignature(p12dcx->aSafeCinfo, certUsageEmailSigner, + PR_FALSE)) { + return SECSuccess; + } else { + PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC); + } + } + + return SECFailure; +} + +/* SEC_PKCS12DecoderFinish + * Free any open ASN1 or PKCS7 decoder contexts and then + * free the arena pool which everything should be allocated + * from. This function should be called upon completion of + * decoding and installing of a pfx pdu. This should be + * called even if an error occurs. + * + * p12dcx - the decoder context + */ +void +SEC_PKCS12DecoderFinish(SEC_PKCS12DecoderContext *p12dcx) +{ + if(!p12dcx) { + return; + } + + if(p12dcx->pfxDcx) { + SEC_ASN1DecoderFinish(p12dcx->pfxDcx); + p12dcx->pfxDcx = NULL; + } + + if(p12dcx->aSafeDcx) { + SEC_ASN1DecoderFinish(p12dcx->aSafeDcx); + p12dcx->aSafeDcx = NULL; + } + + if(p12dcx->currentASafeP7Dcx) { + SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx); + p12dcx->currentASafeP7Dcx = NULL; + } + + if(p12dcx->aSafeP7Dcx) { + SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx); + } + + if(p12dcx->hmacDcx) { + SEC_ASN1DecoderFinish(p12dcx->hmacDcx); + p12dcx->hmacDcx = NULL; + } + + if(p12dcx->arena) { + PORT_FreeArena(p12dcx->arena, PR_TRUE); + } +} + +static SECStatus +sec_pkcs12_decoder_set_attribute_value(sec_PKCS12SafeBag *bag, + SECOidTag attributeType, + SECItem *attrValue) +{ + int i = 0; + SECOidData *oid; + + if(!bag || !attrValue) { + return SECFailure; + } + + oid = SECOID_FindOIDByTag(attributeType); + if(!oid) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + if(!bag->attribs) { + bag->attribs = (sec_PKCS12Attribute**)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute *) * 2); + } else { + while(bag->attribs[i]) i++; + bag->attribs = (sec_PKCS12Attribute **)PORT_ArenaGrow(bag->arena, + bag->attribs, + (i + 1) * sizeof(sec_PKCS12Attribute *), + (i + 2) * sizeof(sec_PKCS12Attribute *)); + } + + if(!bag->attribs) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + bag->attribs[i] = (sec_PKCS12Attribute*)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute)); + if(!bag->attribs) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + bag->attribs[i]->attrValue = (SECItem**)PORT_ArenaZAlloc(bag->arena, + sizeof(SECItem *) * 2); + if(!bag->attribs[i]->attrValue) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + bag->attribs[i+1] = NULL; + bag->attribs[i]->attrValue[0] = attrValue; + bag->attribs[i]->attrValue[1] = NULL; + + if(SECITEM_CopyItem(bag->arena, &bag->attribs[i]->attrType, &oid->oid) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + return SECSuccess; +} + +static SECItem * +sec_pkcs12_get_attribute_value(sec_PKCS12SafeBag *bag, + SECOidTag attributeType) +{ + int i = 0; + + if(!bag->attribs) { + return NULL; + } + + while(bag->attribs[i] != NULL) { + if(SECOID_FindOIDTag(&bag->attribs[i]->attrType) + == attributeType) { + return bag->attribs[i]->attrValue[0]; + } + i++; + } + + return NULL; +} + +/* For now, this function will merely remove any ":" + * in the nickname which the PK11 functions may have + * placed there. This will keep dual certs from appearing + * twice under "Your" certificates when imported onto smart + * cards. Once with the name "Slot:Cert" and another with + * the nickname "Slot:Slot:Cert" + */ +static void +sec_pkcs12_sanitize_nickname(PK11SlotInfo *slot, SECItem *nick) +{ + char *nickname; + char *delimit; + int delimitlen; + + nickname = (char*)nick->data; /*Mac breaks without this type cast*/ + if ((delimit = PORT_Strchr(nickname, ':')) != NULL) { + char *slotName; + int slotNameLen; + + slotNameLen = delimit-nickname; + slotName = PORT_NewArray(char, (slotNameLen+1)); + PORT_Assert(slotName); + if (slotName == NULL) { + /* What else can we do?*/ + return; + } + PORT_Memcpy(slotName, nickname, slotNameLen); + slotName[slotNameLen] = '\0'; + if (PORT_Strcmp(PK11_GetTokenName(slot), slotName) == 0) { + delimitlen = PORT_Strlen(delimit+1); + PORT_Memmove(nickname, delimit+1, delimitlen+1); + nick->len = delimitlen; + } + PORT_Free(slotName); + } + +} + +static SECItem * +sec_pkcs12_get_nickname(sec_PKCS12SafeBag *bag) +{ + SECItem *src, *dest; + + if(!bag) { + bag->problem = PR_TRUE; + bag->error = SEC_ERROR_NO_MEMORY; + return NULL; + } + + src = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME); + if(!src) { + return NULL; + } + + dest = (SECItem*)PORT_ZAlloc(sizeof(SECItem)); + if(!dest) { + goto loser; + } + if(!sec_pkcs12_convert_item_to_unicode(NULL, dest, src, PR_FALSE, + PR_FALSE, PR_FALSE)) { + goto loser; + } + + sec_pkcs12_sanitize_nickname(bag->slot, dest); + + return dest; + +loser: + if(dest) { + SECITEM_ZfreeItem(dest, PR_TRUE); + } + + bag->problem = PR_TRUE; + bag->error = PORT_GetError(); + return NULL; +} + +static SECStatus +sec_pkcs12_set_nickname(sec_PKCS12SafeBag *bag, SECItem *name) +{ + int i = 0; + sec_PKCS12Attribute *attr = NULL; + SECOidData *oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_FRIENDLY_NAME); + + if(!bag || !bag->arena || !name) { + return SECFailure; + } + + if(!bag->attribs) { + if(!oid) { + goto loser; + } + + bag->attribs = (sec_PKCS12Attribute**)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute *)*2); + if(!bag->attribs) { + goto loser; + } + bag->attribs[0] = (sec_PKCS12Attribute*)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute)); + if(!bag->attribs[0]) { + goto loser; + } + bag->attribs[1] = NULL; + + attr = bag->attribs[0]; + if(SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid) + != SECSuccess) { + goto loser; + } + } else { + while(bag->attribs[i]) { + if(SECOID_FindOIDTag(&bag->attribs[i]->attrType) + == SEC_OID_PKCS9_FRIENDLY_NAME) { + attr = bag->attribs[i]; + goto have_attrib; + + } + i++; + } + if(!attr) { + bag->attribs = (sec_PKCS12Attribute **)PORT_ArenaGrow(bag->arena, + bag->attribs, + (i+1) * sizeof(sec_PKCS12Attribute *), + (i+2) * sizeof(sec_PKCS12Attribute *)); + if(!bag->attribs) { + goto loser; + } + bag->attribs[i] = + (sec_PKCS12Attribute *)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12Attribute)); + if(!bag->attribs[i]) { + goto loser; + } + bag->attribs[i+1] = NULL; + attr = bag->attribs[i]; + if(SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid) + != SECSuccess) { + goto loser; + } + } + } +have_attrib: + PORT_Assert(attr); + if(!attr->attrValue) { + attr->attrValue = (SECItem **)PORT_ArenaZAlloc(bag->arena, + sizeof(SECItem *) * 2); + if(!attr->attrValue) { + goto loser; + } + attr->attrValue[0] = (SECItem*)PORT_ArenaZAlloc(bag->arena, + sizeof(SECItem)); + if(!attr->attrValue[0]) { + goto loser; + } + attr->attrValue[1] = NULL; + } + + name->len = PORT_Strlen((char *)name->data); + if(!sec_pkcs12_convert_item_to_unicode(bag->arena, attr->attrValue[0], + name, PR_FALSE, PR_FALSE, PR_TRUE)) { + goto loser; + } + + return SECSuccess; + +loser: + bag->problem = PR_TRUE; + bag->error = SEC_ERROR_NO_MEMORY; + return SECFailure; +} + +static SECStatus +sec_pkcs12_get_key_info(sec_PKCS12SafeBag *key) +{ + int i = 0; + SECKEYPrivateKeyInfo *pki = NULL; + + if(!key) { + return SECFailure; + } + + /* if the bag does *not* contain an unencrypted PrivateKeyInfo + * then we cannot convert the attributes. We are propagating + * attributes within the PrivateKeyInfo to the SafeBag level. + */ + if(SECOID_FindOIDTag(&(key->safeBagType)) != + SEC_OID_PKCS12_V1_KEY_BAG_ID) { + return SECSuccess; + } + + pki = key->safeBagContent.pkcs8KeyBag; + + if(!pki || !pki->attributes) { + return SECSuccess; + } + + while(pki->attributes[i]) { + SECItem *attrValue = NULL; + + if(SECOID_FindOIDTag(&pki->attributes[i]->attrType) == + SEC_OID_PKCS9_LOCAL_KEY_ID) { + attrValue = sec_pkcs12_get_attribute_value(key, + SEC_OID_PKCS9_LOCAL_KEY_ID); + if(!attrValue) { + if(sec_pkcs12_decoder_set_attribute_value(key, + SEC_OID_PKCS9_LOCAL_KEY_ID, + pki->attributes[i]->attrValue[0]) + != SECSuccess) { + key->problem = PR_TRUE; + key->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + } + } + + if(SECOID_FindOIDTag(&pki->attributes[i]->attrType) == + SEC_OID_PKCS9_FRIENDLY_NAME) { + attrValue = sec_pkcs12_get_attribute_value(key, + SEC_OID_PKCS9_FRIENDLY_NAME); + if(!attrValue) { + if(sec_pkcs12_decoder_set_attribute_value(key, + SEC_OID_PKCS9_FRIENDLY_NAME, + pki->attributes[i]->attrValue[0]) + != SECSuccess) { + key->problem = PR_TRUE; + key->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + } + } + + i++; + } + + return SECSuccess; +} + +/* retrieve the nickname for the certificate bag. first look + * in the cert bag, otherwise get it from the key. + */ +static SECItem * +sec_pkcs12_get_nickname_for_cert(sec_PKCS12SafeBag *cert, + sec_PKCS12SafeBag *key, + void *wincx) +{ + SECItem *nickname; + + if(!cert) { + return NULL; + } + + nickname = sec_pkcs12_get_nickname(cert); + if(nickname) { + return nickname; + } + + if(key) { + nickname = sec_pkcs12_get_nickname(key); + + if(nickname && sec_pkcs12_set_nickname(cert, nickname) + != SECSuccess) { + cert->error = SEC_ERROR_NO_MEMORY; + cert->problem = PR_TRUE; + if(nickname) { + SECITEM_ZfreeItem(nickname, PR_TRUE); + } + return NULL; + } + } + + return nickname; +} + +/* set the nickname for the certificate */ +static SECStatus +sec_pkcs12_set_nickname_for_cert(sec_PKCS12SafeBag *cert, + sec_PKCS12SafeBag *key, + SECItem *nickname, + void *wincx) +{ + if(!nickname || !cert) { + return SECFailure; + } + + if(sec_pkcs12_set_nickname(cert, nickname) != SECSuccess) { + cert->error = SEC_ERROR_NO_MEMORY; + cert->problem = PR_TRUE; + return SECFailure; + } + + if(key) { + if(sec_pkcs12_set_nickname(key, nickname) != SECSuccess) { + cert->error = SEC_ERROR_NO_MEMORY; + cert->problem = PR_TRUE; + return SECFailure; + } + } + + return SECSuccess; +} + +/* retrieve the DER cert from the cert bag */ +static SECItem * +sec_pkcs12_get_der_cert(sec_PKCS12SafeBag *cert) +{ + if(!cert) { + return NULL; + } + + if(SECOID_FindOIDTag(&cert->safeBagType) != SEC_OID_PKCS12_V1_CERT_BAG_ID) { + return NULL; + } + + /* only support X509 certs not SDSI */ + if(SECOID_FindOIDTag(&cert->safeBagContent.certBag->bagID) + != SEC_OID_PKCS9_X509_CERT) { + return NULL; + } + + return SECITEM_DupItem(&(cert->safeBagContent.certBag->value.x509Cert)); +} + +struct certNickInfo { + PRArenaPool *arena; + unsigned int nNicks; + SECItem **nickList; + unsigned int error; +}; + +/* callback for traversing certificates to gather the nicknames + * used in a particular traversal. for instance, when using + * CERT_TraversePermCertsForSubject, gather the nicknames and + * store them in the certNickInfo for a particular DN. + * + * this handles the case where multiple nicknames are allowed + * for the same dn, which is not currently allowed, but may be + * in the future. + */ +static SECStatus +gatherNicknames(CERTCertificate *cert, void *arg) +{ + struct certNickInfo *nickArg = (struct certNickInfo *)arg; + SECItem tempNick; + unsigned int i; + + if(!cert || !nickArg || nickArg->error) { + return SECFailure; + } + + if(!cert->nickname) { + return SECSuccess; + } + + tempNick.data = (unsigned char *)cert->nickname; + tempNick.len = PORT_Strlen(cert->nickname) + 1; + + /* do we already have the nickname in the list? */ + if(nickArg->nNicks > 0) { + + /* nicknames have been encountered, but there is no list -- bad */ + if(!nickArg->nickList) { + nickArg->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + + for(i = 0; i < nickArg->nNicks; i++) { + if(SECITEM_CompareItem(nickArg->nickList[i], &tempNick) + == SECEqual) { + return SECSuccess; + } + } + } + + /* add the nickname to the list */ + if(nickArg->nNicks == 0) { + nickArg->nickList = (SECItem **)PORT_ArenaZAlloc(nickArg->arena, + 2 * sizeof(SECItem *)); + } else { + nickArg->nickList = (SECItem **)PORT_ArenaGrow(nickArg->arena, + nickArg->nickList, + (nickArg->nNicks + 1) * sizeof(SECItem *), + (nickArg->nNicks + 2) * sizeof(SECItem *)); + } + if(!nickArg->nickList) { + nickArg->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + + nickArg->nickList[nickArg->nNicks] = + (SECItem *)PORT_ArenaZAlloc(nickArg->arena, sizeof(SECItem)); + if(!nickArg->nickList[nickArg->nNicks]) { + nickArg->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + + + if(SECITEM_CopyItem(nickArg->arena, nickArg->nickList[nickArg->nNicks], + &tempNick) != SECSuccess) { + nickArg->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + + nickArg->nNicks++; + + return SECSuccess; +} + +/* traverses the certs in the data base or in the token for the + * DN to see if any certs currently have a nickname set. + * If so, return it. + */ +static SECItem * +sec_pkcs12_get_existing_nick_for_dn(sec_PKCS12SafeBag *cert, void *wincx) +{ + struct certNickInfo *nickArg = NULL; + SECItem *derCert, *returnDn = NULL; + PRArenaPool *arena = NULL; + CERTCertificate *tempCert; + + if(!cert) { + return NULL; + } + + derCert = sec_pkcs12_get_der_cert(cert); + if(!derCert) { + return NULL; + } + + tempCert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if(!tempCert) { + returnDn = NULL; + goto loser; + } + + arena = PORT_NewArena(1024); + if(!arena) { + returnDn = NULL; + goto loser; + } + nickArg = (struct certNickInfo *)PORT_ArenaZAlloc(arena, + sizeof(struct certNickInfo)); + if(!nickArg) { + returnDn = NULL; + goto loser; + } + nickArg->error = 0; + nickArg->nNicks = 0; + nickArg->nickList = NULL; + nickArg->arena = arena; + + /* if the token is local, first traverse the cert database + * then traverse the token. + */ + if(PK11_TraverseCertsForSubjectInSlot(tempCert, cert->slot, gatherNicknames, + (void *)nickArg) != SECSuccess) { + returnDn = NULL; + goto loser; + } + + if(nickArg->error) { + /* XXX do we want to set the error? */ + returnDn = NULL; + goto loser; + } + + if(nickArg->nNicks == 0) { + returnDn = NULL; + goto loser; + } + + /* set it to the first name, for now. handle multiple names? */ + returnDn = SECITEM_DupItem(nickArg->nickList[0]); + +loser: + if(arena) { + PORT_FreeArena(arena, PR_TRUE); + } + + if(tempCert) { + CERT_DestroyCertificate(tempCert); + } + + if(derCert) { + SECITEM_FreeItem(derCert, PR_TRUE); + } + + return (returnDn); +} + +/* counts certificates found for a given traversal function */ +static SECStatus +countCertificate(CERTCertificate *cert, void *arg) +{ + unsigned int *nCerts = (unsigned int *)arg; + + if(!cert || !arg) { + return SECFailure; + } + + (*nCerts)++; + return SECSuccess; +} + +static PRBool +sec_pkcs12_certs_for_nickname_exist(SECItem *nickname, PK11SlotInfo *slot) +{ + unsigned int nCerts = 0; + + if(!nickname || !slot) { + return PR_TRUE; + } + + /* we want to check the local database first if we are importing to it */ + PK11_TraverseCertsForNicknameInSlot(nickname, slot, countCertificate, + (void *)&nCerts); + if(nCerts) return PR_TRUE; + + return PR_FALSE; +} + +/* validate cert nickname such that there is a one-to-one relation + * between nicknames and dn's. we want to enforce the case that the + * nickname is non-NULL and that there is only one nickname per DN. + * + * if there is a problem with a nickname or the nickname is not present, + * the user will be prompted for it. + */ +static void +sec_pkcs12_validate_cert_nickname(sec_PKCS12SafeBag *cert, + sec_PKCS12SafeBag *key, + SEC_PKCS12NicknameCollisionCallback nicknameCb, + void *wincx) +{ + SECItem *certNickname, *existingDNNick; + PRBool setNickname = PR_FALSE, cancel = PR_FALSE; + SECItem *newNickname = NULL; + + if(!cert || !cert->hasKey) { + return; + } + + if(!nicknameCb) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + return; + } + + if(cert->hasKey && !key) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + return; + } + + certNickname = sec_pkcs12_get_nickname_for_cert(cert, key, wincx); + existingDNNick = sec_pkcs12_get_existing_nick_for_dn(cert, wincx); + + /* nickname is already used w/ this dn, so it is safe to return */ + if(certNickname && existingDNNick && + SECITEM_CompareItem(certNickname, existingDNNick) == SECEqual) { + goto loser; + } + + /* nickname not set in pkcs 12 bags, but a nick is already used for + * this dn. set the nicks in the p12 bags and finish. + */ + if(existingDNNick) { + if(sec_pkcs12_set_nickname_for_cert(cert, key, existingDNNick, wincx) + != SECSuccess) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + } + goto loser; + } + + /* at this point, we have a certificate for which the DN is not located + * on the token. the nickname specified may or may not be NULL. if it + * is not null, we need to make sure that there are no other certificates + * with this nickname in the token for it to be valid. this imposes a + * one to one relationship between DN and nickname. + * + * if the nickname is null, we need the user to enter a nickname for + * the certificate. + * + * once we have a nickname, we make sure that the nickname is unique + * for the DN. if it is not, the user is reprompted to enter a new + * nickname. + * + * in order to exit this loop, the nickname entered is either unique + * or the user hits cancel and the certificate is not imported. + */ + setNickname = PR_FALSE; + while(1) { + if(certNickname && certNickname->data) { + /* we will use the nickname so long as no other certs have the + * same nickname. and the nickname is not NULL. + */ + if(!sec_pkcs12_certs_for_nickname_exist(certNickname, cert->slot)) { + if(setNickname) { + if(sec_pkcs12_set_nickname_for_cert(cert, key, certNickname, + wincx) != SECSuccess) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + } + } + goto loser; + } + } + + setNickname = PR_FALSE; + newNickname = (*nicknameCb)(certNickname, &cancel, wincx); + if(cancel) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_USER_CANCELLED; + goto loser; + } + + if(!newNickname) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + goto loser; + } + + /* at this point we have a new nickname, if we have an existing + * certNickname, we need to free it and assign the new nickname + * to it to avoid a memory leak. happy? + */ + if(certNickname) { + SECITEM_ZfreeItem(certNickname, PR_TRUE); + certNickname = NULL; + } + + certNickname = newNickname; + setNickname = PR_TRUE; + /* go back and recheck the new nickname */ + } + +loser: + if(certNickname) { + SECITEM_ZfreeItem(certNickname, PR_TRUE); + } + + if(existingDNNick) { + SECITEM_ZfreeItem(existingDNNick, PR_TRUE); + } +} + +static void +sec_pkcs12_validate_cert(sec_PKCS12SafeBag *cert, + sec_PKCS12SafeBag *key, + SEC_PKCS12NicknameCollisionCallback nicknameCb, + void *wincx) +{ + CERTCertificate *leafCert, *testCert; + + if(!cert) { + return; + } + + cert->validated = PR_TRUE; + + if(!nicknameCb) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + cert->noInstall = PR_TRUE; + return; + } + + if(!cert->safeBagContent.certBag) { + cert->noInstall = PR_TRUE; + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE; + return; + } + + cert->noInstall = PR_FALSE; + cert->removeExisting = PR_FALSE; + cert->problem = PR_FALSE; + cert->error = 0; + + leafCert = CERT_DecodeDERCertificate( + &cert->safeBagContent.certBag->value.x509Cert, PR_FALSE, NULL); + if(!leafCert) { + cert->noInstall = PR_TRUE; + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + return; + } + + testCert = PK11_FindCertFromDERCert(cert->slot, leafCert, wincx); + CERT_DestroyCertificate(leafCert); + /* if we can't find the certificate through the PKCS11 interface, + * we should check the cert database directly, if we are + * importing to an internal slot. + */ + if(!testCert && PK11_IsInternal(cert->slot)) { + testCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), + &cert->safeBagContent.certBag->value.x509Cert); + } + + if(testCert) { + if(!testCert->nickname) { + cert->removeExisting = PR_TRUE; + } + CERT_DestroyCertificate(testCert); + if(cert->noInstall && !cert->removeExisting) { + return; + } + } + + sec_pkcs12_validate_cert_nickname(cert, key, nicknameCb, wincx); +} + +static void +sec_pkcs12_validate_key_by_cert(sec_PKCS12SafeBag *cert, sec_PKCS12SafeBag *key, + void *wincx) +{ + CERTCertificate *leafCert; + SECKEYPrivateKey *privk; + + if(!key) { + return; + } + + key->validated = PR_TRUE; + + if(!cert) { + key->problem = PR_TRUE; + key->noInstall = PR_TRUE; + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY; + return; + } + + leafCert = CERT_DecodeDERCertificate( + &(cert->safeBagContent.certBag->value.x509Cert), PR_FALSE, NULL); + if(!leafCert) { + key->problem = PR_TRUE; + key->noInstall = PR_TRUE; + key->error = SEC_ERROR_NO_MEMORY; + return; + } + + privk = PK11_FindPrivateKeyFromCert(key->slot, leafCert, wincx); + if(!privk) { + privk = PK11_FindKeyByDERCert(key->slot, leafCert, wincx); + } + + if(privk) { + SECKEY_DestroyPrivateKey(privk); + key->noInstall = PR_TRUE; + } + + CERT_DestroyCertificate(leafCert); +} + +static SECStatus +sec_pkcs12_remove_existing_cert(sec_PKCS12SafeBag *cert, + void *wincx) +{ + SECItem *derCert = NULL; + CERTCertificate *tempCert = NULL; + CK_OBJECT_HANDLE certObj; + PRBool removed = PR_FALSE; + + if(!cert) { + return SECFailure; + } + + PORT_Assert(cert->removeExisting); + + cert->removeExisting = PR_FALSE; + derCert = &cert->safeBagContent.certBag->value.x509Cert; + tempCert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if(!tempCert) { + return SECFailure; + } + + certObj = PK11_FindCertInSlot(cert->slot, tempCert, wincx); + CERT_DestroyCertificate(tempCert); + tempCert = NULL; + + if(certObj != CK_INVALID_HANDLE) { + PK11_DestroyObject(cert->slot, certObj); + removed = PR_TRUE; + } else if(PK11_IsInternal(cert->slot)) { + tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), derCert); + if(tempCert) { + if(SEC_DeletePermCertificate(tempCert) == SECSuccess) { + removed = PR_TRUE; + } + CERT_DestroyCertificate(tempCert); + tempCert = NULL; + } + } + + if(!removed) { + cert->problem = PR_TRUE; + cert->error = SEC_ERROR_NO_MEMORY; + cert->noInstall = PR_TRUE; + } + + if(tempCert) { + CERT_DestroyCertificate(tempCert); + } + + return ((removed) ? SECSuccess : SECFailure); +} + +static SECStatus +sec_pkcs12_add_cert(sec_PKCS12SafeBag *cert, PRBool keyExists, void *wincx) +{ + SECItem *derCert, *nickName; + char *nickData = NULL; + SECStatus rv; + + if(!cert) { + return SECFailure; + } + + if(cert->problem || cert->noInstall || cert->installed) { + return SECSuccess; + } + + derCert = &cert->safeBagContent.certBag->value.x509Cert; + if(cert->removeExisting) { + if(sec_pkcs12_remove_existing_cert(cert, wincx) + != SECSuccess) { + return SECFailure; + } + cert->removeExisting = PR_FALSE; + } + + PORT_Assert(!cert->problem && !cert->removeExisting && !cert->noInstall); + + nickName = sec_pkcs12_get_nickname(cert); + if(nickName) { + nickData = (char *)nickName->data; + } + + if(keyExists) { + CERTCertificate *newCert; + + newCert = CERT_DecodeDERCertificate( derCert, PR_FALSE, NULL); + if(!newCert) { + if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE); + cert->error = SEC_ERROR_NO_MEMORY; + cert->problem = PR_TRUE; + return SECFailure; + } + + rv = PK11_ImportCertForKeyToSlot(cert->slot, newCert, nickData, + PR_TRUE, wincx); + CERT_DestroyCertificate(newCert); + } else { + SECItem *certList[2]; + certList[0] = derCert; + certList[1] = NULL; + rv = CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageUserCertImport, + 1, certList, NULL, PR_TRUE, PR_FALSE, nickData); + } + + cert->installed = PR_TRUE; + if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE); + return rv; +} + +static SECStatus +sec_pkcs12_add_key(sec_PKCS12SafeBag *key, SECItem *publicValue, + KeyType keyType, unsigned int keyUsage, void *wincx) +{ + SECStatus rv; + SECItem *nickName; + + if(!key) { + return SECFailure; + } + + if(key->removeExisting) { + key->problem = PR_TRUE; + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY; + return SECFailure; + } + + if(key->problem || key->noInstall) { + return SECSuccess; + } + + nickName = sec_pkcs12_get_nickname(key); + + switch(SECOID_FindOIDTag(&key->safeBagType)) + { + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + rv = PK11_ImportPrivateKeyInfo(key->slot, + key->safeBagContent.pkcs8KeyBag, + nickName, publicValue, PR_TRUE, PR_TRUE, + keyUsage, wincx); + break; + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + rv = PK11_ImportEncryptedPrivateKeyInfo(key->slot, + key->safeBagContent.pkcs8ShroudedKeyBag, + key->pwitem, nickName, publicValue, + PR_TRUE, PR_TRUE, keyType, keyUsage, + wincx); + break; + default: + key->error = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION; + key->problem = PR_TRUE; + if(nickName) { + SECITEM_ZfreeItem(nickName, PR_TRUE); + } + return SECFailure; + } + + key->installed = PR_TRUE; + + if(nickName) { + SECITEM_ZfreeItem(nickName, PR_TRUE); + } + + if(rv != SECSuccess) { + key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY; + key->problem = PR_TRUE; + } else { + key->installed = PR_TRUE; + } + + return rv; +} + +static SECStatus +sec_pkcs12_add_item_to_bag_list(sec_PKCS12SafeBag ***bagList, + sec_PKCS12SafeBag *bag) +{ + int i = 0; + + if(!bagList || !bag) { + return SECFailure; + } + + if(!(*bagList)) { + (*bagList) = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(bag->arena, + sizeof(sec_PKCS12SafeBag *) * 2); + } else { + while((*bagList)[i]) i++; + (*bagList) = (sec_PKCS12SafeBag **)PORT_ArenaGrow(bag->arena, *bagList, + sizeof(sec_PKCS12SafeBag *) * (i + 1), + sizeof(sec_PKCS12SafeBag *) * (i + 2)); + } + + if(!(*bagList)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + (*bagList)[i] = bag; + (*bagList)[i+1] = NULL; + + return SECSuccess; +} + +static sec_PKCS12SafeBag ** +sec_pkcs12_find_certs_for_key(sec_PKCS12SafeBag **safeBags, sec_PKCS12SafeBag *key ) +{ + sec_PKCS12SafeBag **certList = NULL; + SECItem *keyId; + int i; + + if(!safeBags || !safeBags[0]) { + return NULL; + } + + keyId = sec_pkcs12_get_attribute_value(key, SEC_OID_PKCS9_LOCAL_KEY_ID); + if(!keyId) { + return NULL; + } + + i = 0; + certList = NULL; + while(safeBags[i]) { + if(SECOID_FindOIDTag(&(safeBags[i]->safeBagType)) + == SEC_OID_PKCS12_V1_CERT_BAG_ID) { + SECItem *certKeyId = sec_pkcs12_get_attribute_value(safeBags[i], + SEC_OID_PKCS9_LOCAL_KEY_ID); + + if(certKeyId && (SECITEM_CompareItem(certKeyId, keyId) + == SECEqual)) { + if(sec_pkcs12_add_item_to_bag_list(&certList, safeBags[i]) + != SECSuccess) { + return NULL; + } + } + } + i++; + } + + return certList; +} + +CERTCertList * +SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx) +{ + CERTCertList *certList = NULL; + sec_PKCS12SafeBag **safeBags = p12dcx->safeBags; + int i; + + if (!p12dcx || !p12dcx->safeBags || !p12dcx->safeBags[0]) { + return NULL; + } + + safeBags = p12dcx->safeBags; + i = 0; + certList = CERT_NewCertList(); + + if (certList == NULL) { + return NULL; + } + + while(safeBags[i]) { + if (SECOID_FindOIDTag(&(safeBags[i]->safeBagType)) + == SEC_OID_PKCS12_V1_CERT_BAG_ID) { + SECItem *derCert = sec_pkcs12_get_der_cert(safeBags[i]) ; + CERTCertificate *tempCert = NULL; + + if (derCert == NULL) continue; + tempCert=CERT_DecodeDERCertificate(derCert, PR_TRUE, NULL); + + if (tempCert) { + CERT_AddCertToListTail(certList,tempCert); + } + SECITEM_FreeItem(derCert,PR_TRUE); + } + i++; + } + + return certList; +} +static sec_PKCS12SafeBag ** +sec_pkcs12_get_key_bags(sec_PKCS12SafeBag **safeBags) +{ + int i; + sec_PKCS12SafeBag **keyList = NULL; + SECOidTag bagType; + + if(!safeBags || !safeBags[0]) { + return NULL; + } + + i = 0; + while(safeBags[i]) { + bagType = SECOID_FindOIDTag(&(safeBags[i]->safeBagType)); + switch(bagType) { + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + if(sec_pkcs12_add_item_to_bag_list(&keyList, safeBags[i]) + != SECSuccess) { + return NULL; + } + break; + default: + break; + } + i++; + } + + return keyList; +} + +static SECStatus +sec_pkcs12_validate_bags(sec_PKCS12SafeBag **safeBags, + SEC_PKCS12NicknameCollisionCallback nicknameCb, + void *wincx) +{ + sec_PKCS12SafeBag **keyList; + int i; + + if(!safeBags || !nicknameCb) { + return SECFailure; + } + + if(!safeBags[0]) { + return SECSuccess; + } + + keyList = sec_pkcs12_get_key_bags(safeBags); + if(keyList) { + i = 0; + + while(keyList[i]) { + sec_PKCS12SafeBag **certList = sec_pkcs12_find_certs_for_key( + safeBags, keyList[i]); + if(certList) { + int j = 0; + + if(SECOID_FindOIDTag(&(keyList[i]->safeBagType)) == + SEC_OID_PKCS12_V1_KEY_BAG_ID) { + /* if it is an unencrypted private key then make sure + * the attributes are propageted to the appropriate + * level + */ + if(sec_pkcs12_get_key_info(keyList[i]) != SECSuccess) { + keyList[i]->problem = PR_TRUE; + keyList[i]->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + } + + sec_pkcs12_validate_key_by_cert(certList[0], keyList[i], wincx); + while(certList[j]) { + certList[j]->hasKey = PR_TRUE; + if(keyList[i]->problem) { + certList[j]->problem = PR_TRUE; + certList[j]->error = keyList[i]->error; + } else { + sec_pkcs12_validate_cert(certList[j], keyList[i], + nicknameCb, wincx); + if(certList[j]->problem) { + keyList[i]->problem = certList[j]->problem; + keyList[i]->error = certList[j]->error; + } + } + j++; + } + } + + i++; + } + } + + i = 0; + while(safeBags[i]) { + if(!safeBags[i]->validated) { + SECOidTag bagType = SECOID_FindOIDTag(&safeBags[i]->safeBagType); + + switch(bagType) { + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + sec_pkcs12_validate_cert(safeBags[i], NULL, nicknameCb, + wincx); + break; + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + safeBags[i]->noInstall = PR_TRUE; + safeBags[i]->problem = PR_TRUE; + safeBags[i]->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY; + break; + default: + safeBags[i]->noInstall = PR_TRUE; + } + } + i++; + } + + return SECSuccess; +} + +SECStatus +SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx, + SEC_PKCS12NicknameCollisionCallback nicknameCb) +{ + SECStatus rv; + int i, noInstallCnt, probCnt, bagCnt, errorVal = 0; + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + rv = sec_pkcs12_validate_bags(p12dcx->safeBags, nicknameCb, p12dcx->wincx); + if(rv == SECSuccess) { + p12dcx->bagsVerified = PR_TRUE; + } + + noInstallCnt = probCnt = bagCnt = 0; + i = 0; + while(p12dcx->safeBags[i]) { + bagCnt++; + if(p12dcx->safeBags[i]->noInstall) noInstallCnt++; + if(p12dcx->safeBags[i]->problem) { + probCnt++; + errorVal = p12dcx->safeBags[i]->error; + } + i++; + } + + if(bagCnt == noInstallCnt) { + PORT_SetError(SEC_ERROR_PKCS12_DUPLICATE_DATA); + return SECFailure; + } + + if(probCnt) { + PORT_SetError(errorVal); + return SECFailure; + } + + return rv; +} + +static SECItem * +sec_pkcs12_get_public_value_and_type(sec_PKCS12SafeBag *certBag, + KeyType *type, unsigned int *usage) +{ + SECKEYPublicKey *pubKey = NULL; + CERTCertificate *cert = NULL; + SECItem *pubValue; + + *type = nullKey; + *usage = 0; + + if(!certBag) { + return NULL; + } + + cert = CERT_DecodeDERCertificate( + &certBag->safeBagContent.certBag->value.x509Cert, PR_FALSE, NULL); + if(!cert) { + return NULL; + } + + *usage = cert->keyUsage; + pubKey = CERT_ExtractPublicKey(cert); + CERT_DestroyCertificate(cert); + if(!pubKey) { + return NULL; + } + + *type = pubKey->keyType; + switch(pubKey->keyType) { + case dsaKey: + pubValue = SECITEM_DupItem(&pubKey->u.dsa.publicValue); + break; + case dhKey: + pubValue = SECITEM_DupItem(&pubKey->u.dh.publicValue); + break; + case rsaKey: + pubValue = SECITEM_DupItem(&pubKey->u.rsa.modulus); + break; + default: + pubValue = NULL; + } + + SECKEY_DestroyPublicKey(pubKey); + + return pubValue; +} + +static SECStatus +sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, + void *wincx) +{ + sec_PKCS12SafeBag **keyList, **certList; + int i; + + if(!safeBags) { + return SECFailure; + } + + if(!safeBags[0]) { + return SECSuccess; + } + + keyList = sec_pkcs12_get_key_bags(safeBags); + if(keyList) { + i = 0; + + while(keyList[i]) { + SECStatus rv; + SECItem *publicValue = NULL; + KeyType keyType; + unsigned int keyUsage; + + if(keyList[i]->problem) { + goto next_key_bag; + } + + certList = sec_pkcs12_find_certs_for_key(safeBags, + keyList[i]); + if(certList) { + publicValue = sec_pkcs12_get_public_value_and_type(certList[0], + &keyType, &keyUsage); + } + rv = sec_pkcs12_add_key(keyList[i], publicValue, keyType, keyUsage, + wincx); + if(publicValue) { + SECITEM_FreeItem(publicValue, PR_TRUE); + } + if(rv != SECSuccess) { + PORT_SetError(keyList[i]->error); + return SECFailure; + } + + if(certList) { + int j = 0; + + while(certList[j]) { + SECStatus certRv; + + if(rv != SECSuccess) { + certList[j]->problem = keyList[i]->problem; + certList[j]->error = keyList[i]->error; + certList[j]->noInstall = PR_TRUE; + goto next_cert_bag; + } + + certRv = sec_pkcs12_add_cert(certList[j], + certList[j]->hasKey, wincx); + if(certRv != SECSuccess) { + keyList[i]->problem = certList[j]->problem; + keyList[i]->error = certList[j]->error; + PORT_SetError(certList[j]->error); + return SECFailure; + } +next_cert_bag: + j++; + } + } + +next_key_bag: + i++; + } + } + + i = 0; + while(safeBags[i]) { + if(!safeBags[i]->installed) { + SECStatus rv; + SECOidTag bagType = SECOID_FindOIDTag(&(safeBags[i]->safeBagType)); + + switch(bagType) { + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + rv = sec_pkcs12_add_cert(safeBags[i], safeBags[i]->hasKey, + wincx); + if(rv != SECSuccess) { + PORT_SetError(safeBags[i]->error); + return SECFailure; + } + break; + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + default: + break; + } + } + i++; + } + + return SECSuccess; +} + +SECStatus +SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx) +{ + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + if(!p12dcx->bagsVerified) { + return SECFailure; + } + + return sec_pkcs12_install_bags(p12dcx->safeBags, p12dcx->wincx); +} + +static SECStatus +sec_pkcs12_decoder_append_bag_to_context(SEC_PKCS12DecoderContext *p12dcx, + sec_PKCS12SafeBag *bag) +{ + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + if(!p12dcx->safeBagCount) { + p12dcx->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeBag *) * 2); + } else { + p12dcx->safeBags = + (sec_PKCS12SafeBag **)PORT_ArenaGrow(p12dcx->arena, p12dcx->safeBags, + (p12dcx->safeBagCount + 1) * sizeof(sec_PKCS12SafeBag *), + (p12dcx->safeBagCount + 2) * sizeof(sec_PKCS12SafeBag *)); + } + + if(!p12dcx->safeBags) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + p12dcx->safeBags[p12dcx->safeBagCount] = bag; + p12dcx->safeBags[p12dcx->safeBagCount+1] = NULL; + p12dcx->safeBagCount++; + + return SECSuccess; +} + +static sec_PKCS12SafeBag * +sec_pkcs12_decoder_convert_old_key(SEC_PKCS12DecoderContext *p12dcx, + void *key, PRBool isEspvk) +{ + sec_PKCS12SafeBag *keyBag; + SECOidData *oid; + SECOidTag keyTag; + SECItem *keyID, *nickName, *newNickName; + + if(!p12dcx || p12dcx->error || !key) { + return NULL; + } + + newNickName =(SECItem *)PORT_ArenaZAlloc(p12dcx->arena, sizeof(SECItem)); + keyBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeBag)); + if(!keyBag || !newNickName) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + keyBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes; + keyBag->slot = p12dcx->slot; + keyBag->arena = p12dcx->arena; + keyBag->pwitem = p12dcx->pwitem; + keyBag->oldBagType = PR_TRUE; + + keyTag = (isEspvk) ? SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID : + SEC_OID_PKCS12_V1_KEY_BAG_ID; + oid = SECOID_FindOIDByTag(keyTag); + if(!oid) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + if(SECITEM_CopyItem(p12dcx->arena, &keyBag->safeBagType, &oid->oid) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + if(isEspvk) { + SEC_PKCS12ESPVKItem *espvk = (SEC_PKCS12ESPVKItem *)key; + keyBag->safeBagContent.pkcs8ShroudedKeyBag = + espvk->espvkCipherText.pkcs8KeyShroud; + nickName = &(espvk->espvkData.uniNickName); + if(!espvk->espvkData.assocCerts || !espvk->espvkData.assocCerts[0]) { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + return NULL; + } + keyID = &espvk->espvkData.assocCerts[0]->digest; + } else { + SEC_PKCS12PrivateKey *pk = (SEC_PKCS12PrivateKey *)key; + keyBag->safeBagContent.pkcs8KeyBag = &pk->pkcs8data; + nickName= &(pk->pvkData.uniNickName); + if(!pk->pvkData.assocCerts || !pk->pvkData.assocCerts[0]) { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + return NULL; + } + keyID = &pk->pvkData.assocCerts[0]->digest; + } + + if(nickName->len) { + if(nickName->len >= 2) { + if(nickName->data[0] && nickName->data[1]) { + if(!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName, + nickName, PR_FALSE, PR_FALSE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + nickName = newNickName; + } else if(nickName->data[0] && !nickName->data[1]) { + unsigned int j = 0; + unsigned char t; + for(j = 0; j < nickName->len; j+=2) { + t = nickName->data[j+1]; + nickName->data[j+1] = nickName->data[j]; + nickName->data[j] = t; + } + } + } else { + if(!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName, + nickName, PR_FALSE, PR_FALSE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + nickName = newNickName; + } + } + + if(sec_pkcs12_decoder_set_attribute_value(keyBag, + SEC_OID_PKCS9_FRIENDLY_NAME, + nickName) != SECSuccess) { + return NULL; + } + + if(sec_pkcs12_decoder_set_attribute_value(keyBag,SEC_OID_PKCS9_LOCAL_KEY_ID, + keyID) != SECSuccess) { + return NULL; + } + + return keyBag; +} + +static sec_PKCS12SafeBag * +sec_pkcs12_decoder_create_cert(SEC_PKCS12DecoderContext *p12dcx, + SECItem *derCert) +{ + sec_PKCS12SafeBag *certBag; + SECOidData *oid; + SGNDigestInfo *digest; + SECItem *keyId; + SECStatus rv; + + if(!p12dcx || p12dcx->error || !derCert) { + return NULL; + } + + keyId = (SECItem *)PORT_ArenaZAlloc(p12dcx->arena, sizeof(SECItem)); + if(!keyId) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + digest = sec_pkcs12_compute_thumbprint(derCert); + if(!digest) { + return NULL; + } + + rv = SECITEM_CopyItem(p12dcx->arena, keyId, &digest->digest); + SGN_DestroyDigestInfo(digest); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + oid = SECOID_FindOIDByTag(SEC_OID_PKCS12_V1_CERT_BAG_ID); + certBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12SafeBag)); + if(!certBag || !oid || (SECITEM_CopyItem(p12dcx->arena, + &certBag->safeBagType, &oid->oid) != SECSuccess)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + certBag->slot = p12dcx->slot; + certBag->pwitem = p12dcx->pwitem; + certBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes; + certBag->arena = p12dcx->arena; + + oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_X509_CERT); + certBag->safeBagContent.certBag = + (sec_PKCS12CertBag *)PORT_ArenaZAlloc(p12dcx->arena, + sizeof(sec_PKCS12CertBag)); + if(!certBag->safeBagContent.certBag || !oid || + (SECITEM_CopyItem(p12dcx->arena, + &certBag->safeBagContent.certBag->bagID, + &oid->oid) != SECSuccess)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + if(SECITEM_CopyItem(p12dcx->arena, + &(certBag->safeBagContent.certBag->value.x509Cert), + derCert) != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + if(sec_pkcs12_decoder_set_attribute_value(certBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + return NULL; + } + + return certBag; +} + +static sec_PKCS12SafeBag ** +sec_pkcs12_decoder_convert_old_cert(SEC_PKCS12DecoderContext *p12dcx, + SEC_PKCS12CertAndCRL *oldCert) +{ + sec_PKCS12SafeBag **certList; + SECItem **derCertList; + int i, j; + + if(!p12dcx || p12dcx->error || !oldCert) { + return NULL; + } + + derCertList = SEC_PKCS7GetCertificateList(&oldCert->value.x509->certOrCRL); + if(!derCertList) { + return NULL; + } + + i = 0; + while(derCertList[i]) i++; + + certList = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(p12dcx->arena, + (i + 1) * sizeof(sec_PKCS12SafeBag *)); + if(!certList) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + for(j = 0; j < i; j++) { + certList[j] = sec_pkcs12_decoder_create_cert(p12dcx, derCertList[j]); + if(!certList[j]) { + return NULL; + } + } + + return certList; +} + +static SECStatus +sec_pkcs12_decoder_convert_old_key_and_certs(SEC_PKCS12DecoderContext *p12dcx, + void *oldKey, PRBool isEspvk, + SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + sec_PKCS12SafeBag *key, **certList; + SEC_PKCS12CertAndCRL *oldCert; + SEC_PKCS12PVKSupportingData *pvkData; + int i; + SECItem *keyName; + + if(!p12dcx || !oldKey) { + return SECFailure; + } + + if(isEspvk) { + pvkData = &((SEC_PKCS12ESPVKItem *)(oldKey))->espvkData; + } else { + pvkData = &((SEC_PKCS12PrivateKey *)(oldKey))->pvkData; + } + + if(!pvkData->assocCerts || !pvkData->assocCerts[0]) { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + return SECFailure; + } + + oldCert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage, + SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID, NULL, + pvkData->assocCerts[0]); + if(!oldCert) { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + return SECFailure; + } + + key = sec_pkcs12_decoder_convert_old_key(p12dcx,oldKey, isEspvk); + certList = sec_pkcs12_decoder_convert_old_cert(p12dcx, oldCert); + if(!key || !certList) { + return SECFailure; + } + + if(sec_pkcs12_decoder_append_bag_to_context(p12dcx, key) != SECSuccess) { + return SECFailure; + } + + keyName = sec_pkcs12_get_nickname(key); + if(!keyName) { + return SECFailure; + } + + i = 0; + while(certList[i]) { + if(sec_pkcs12_decoder_append_bag_to_context(p12dcx, certList[i]) + != SECSuccess) { + return SECFailure; + } + i++; + } + + certList = sec_pkcs12_find_certs_for_key(p12dcx->safeBags, key); + if(!certList) { + return SECFailure; + } + + i = 0; + while(certList[i] != 0) { + if(sec_pkcs12_set_nickname(certList[i], keyName) != SECSuccess) { + return SECFailure; + } + i++; + } + + return SECSuccess; +} + +static SECStatus +sec_pkcs12_decoder_convert_old_safe_to_bags(SEC_PKCS12DecoderContext *p12dcx, + SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + SECStatus rv; + + if(!p12dcx || p12dcx->error) { + return SECFailure; + } + + if(safe && safe->contents) { + int i = 0; + while(safe->contents[i] != NULL) { + if(SECOID_FindOIDTag(&safe->contents[i]->safeBagType) + == SEC_OID_PKCS12_KEY_BAG_ID) { + int j = 0; + SEC_PKCS12PrivateKeyBag *privBag = + safe->contents[i]->safeContent.keyBag; + + while(privBag->privateKeys[j] != NULL) { + SEC_PKCS12PrivateKey *pk = privBag->privateKeys[j]; + rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx,pk, + PR_FALSE, safe, baggage); + if(rv != SECSuccess) { + goto loser; + } + j++; + } + } + i++; + } + } + + if(baggage && baggage->bags) { + int i = 0; + while(baggage->bags[i] != NULL) { + SEC_PKCS12BaggageItem *bag = baggage->bags[i]; + int j = 0; + + if(!bag->espvks) { + i++; + continue; + } + + while(bag->espvks[j] != NULL) { + SEC_PKCS12ESPVKItem *espvk = bag->espvks[j]; + rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx, espvk, + PR_TRUE, safe, baggage); + if(rv != SECSuccess) { + goto loser; + } + j++; + } + i++; + } + } + + return SECSuccess; + +loser: + return SECFailure; +} + +SEC_PKCS12DecoderContext * +sec_PKCS12ConvertOldSafeToNew(PRArenaPool *arena, PK11SlotInfo *slot, + PRBool swapUnicode, SECItem *pwitem, + void *wincx, SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + SEC_PKCS12DecoderContext *p12dcx; + + if(!arena || !slot || !pwitem) { + return NULL; + } + + if(!safe && !baggage) { + return NULL; + } + + p12dcx = (SEC_PKCS12DecoderContext *)PORT_ArenaZAlloc(arena, + sizeof(SEC_PKCS12DecoderContext)); + if(!p12dcx) { + return NULL; + } + + p12dcx->arena = arena; + p12dcx->slot = slot; + p12dcx->wincx = wincx; + p12dcx->error = PR_FALSE; + p12dcx->swapUnicodeBytes = swapUnicode; + p12dcx->pwitem = pwitem; + + if(sec_pkcs12_decoder_convert_old_safe_to_bags(p12dcx, safe, baggage) + != SECSuccess) { + p12dcx->error = PR_TRUE; + return NULL; + } + + return p12dcx; +} diff --git a/security/nss/lib/pkcs12/p12dec.c b/security/nss/lib/pkcs12/p12dec.c new file mode 100644 index 000000000..abce05885 --- /dev/null +++ b/security/nss/lib/pkcs12/p12dec.c @@ -0,0 +1,693 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#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 occured 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 occured. + */ +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); + safe_contents->swapUnicode = pfx->swapUnicode; + if(safe_contents == NULL) { + rv = SECFailure; + } else { + 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; +} diff --git a/security/nss/lib/pkcs12/p12e.c b/security/nss/lib/pkcs12/p12e.c new file mode 100644 index 000000000..9f9a92cea --- /dev/null +++ b/security/nss/lib/pkcs12/p12e.c @@ -0,0 +1,2282 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "nssrenam.h" +#include "p12t.h" +#include "p12.h" +#include "plarena.h" +#include "secitem.h" +#include "secoid.h" +#include "seccomon.h" +#include "secport.h" +#include "cert.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "secerr.h" +#include "pk11func.h" +#include "p12plcy.h" +#include "p12local.h" +#include "alghmac.h" +#include "prcpucfg.h" + +/********************************* + * Structures used in exporting the PKCS 12 blob + *********************************/ + +/* A SafeInfo is used for each ContentInfo which makes up the + * sequence of safes in the AuthenticatedSafe portion of the + * PFX structure. + */ +struct SEC_PKCS12SafeInfoStr { + PRArenaPool *arena; + + /* information for setting up password encryption */ + SECItem pwitem; + SECOidTag algorithm; + PK11SymKey *encryptionKey; + + /* how many items have been stored in this safe, + * we will skip any safe which does not contain any + * items + */ + unsigned int itemCount; + + /* the content info for the safe */ + SEC_PKCS7ContentInfo *cinfo; + + sec_PKCS12SafeContents *safe; +}; + +/* An opaque structure which contains information needed for exporting + * certificates and keys through PKCS 12. + */ +struct SEC_PKCS12ExportContextStr { + PRArenaPool *arena; + PK11SlotInfo *slot; + void *wincx; + + /* integrity information */ + PRBool integrityEnabled; + PRBool pwdIntegrity; + union { + struct sec_PKCS12PasswordModeInfo pwdInfo; + struct sec_PKCS12PublicKeyModeInfo pubkeyInfo; + } integrityInfo; + + /* helper functions */ + /* retrieve the password call back */ + SECKEYGetPasswordKey pwfn; + void *pwfnarg; + + /* safe contents bags */ + SEC_PKCS12SafeInfo **safeInfos; + unsigned int safeInfoCount; + + /* the sequence of safes */ + sec_PKCS12AuthenticatedSafe authSafe; + + /* information needing deletion */ + CERTCertificate **certList; +}; + +/* structures for passing information to encoder callbacks when processing + * data through the ASN1 engine. + */ +struct sec_pkcs12_encoder_output { + SEC_PKCS12EncoderOutputCallback outputfn; + void *outputarg; +}; + +struct sec_pkcs12_hmac_and_output_info { + void *arg; + struct sec_pkcs12_encoder_output output; +}; + +/* An encoder context which is used for the actual encoding + * portion of PKCS 12. + */ +typedef struct sec_PKCS12EncoderContextStr { + PRArenaPool *arena; + SEC_PKCS12ExportContext *p12exp; + PK11SymKey *encryptionKey; + + /* encoder information - this is set up based on whether + * password based or public key pased privacy is being used + */ + SEC_ASN1EncoderContext *ecx; + union { + struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo; + struct sec_pkcs12_encoder_output encOutput; + } output; + + /* structures for encoding of PFX and MAC */ + sec_PKCS12PFXItem pfx; + sec_PKCS12MacData mac; + + /* authenticated safe encoding tracking information */ + SEC_PKCS7ContentInfo *aSafeCinfo; + SEC_PKCS7EncoderContext *aSafeP7Ecx; + SEC_ASN1EncoderContext *aSafeEcx; + unsigned int currentSafe; + + /* hmac context */ + PK11Context *hmacCx; +} sec_PKCS12EncoderContext; + + +/********************************* + * Export setup routines + *********************************/ + +/* SEC_PKCS12CreateExportContext + * Creates an export context and sets the unicode and password retrieval + * callbacks. This is the first call which must be made when exporting + * a PKCS 12 blob. + * + * pwfn, pwfnarg - password retrieval callback and argument. these are + * required for password-authentication mode. + */ +SEC_PKCS12ExportContext * +SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg, + PK11SlotInfo *slot, void *wincx) +{ + PRArenaPool *arena = NULL; + SEC_PKCS12ExportContext *p12ctxt = NULL; + + /* allocate the arena and create the context */ + arena = PORT_NewArena(4096); + if(!arena) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena, + sizeof(SEC_PKCS12ExportContext)); + if(!p12ctxt) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* password callback for key retrieval */ + p12ctxt->pwfn = pwfn; + p12ctxt->pwfnarg = pwfnarg; + + p12ctxt->integrityEnabled = PR_FALSE; + p12ctxt->arena = arena; + p12ctxt->wincx = wincx; + p12ctxt->slot = (slot) ? slot : PK11_GetInternalSlot(); + + return p12ctxt; + +loser: + if(arena) { + PORT_FreeArena(arena, PR_TRUE); + } + + return NULL; +} + +/* + * Adding integrity mode + */ + +/* SEC_PKCS12AddPasswordIntegrity + * Add password integrity to the exported data. If an integrity method + * has already been set, then return an error. + * + * p12ctxt - the export context + * pwitem - the password for integrity mode + * integAlg - the integrity algorithm to use for authentication. + */ +SECStatus +SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag integAlg) +{ + if(!p12ctxt || p12ctxt->integrityEnabled) { + return SECFailure; + } + + /* set up integrity information */ + p12ctxt->pwdIntegrity = PR_TRUE; + p12ctxt->integrityInfo.pwdInfo.password = + (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); + if(!p12ctxt->integrityInfo.pwdInfo.password) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + if(SECITEM_CopyItem(p12ctxt->arena, + p12ctxt->integrityInfo.pwdInfo.password, pwitem) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg; + p12ctxt->integrityEnabled = PR_TRUE; + + return SECSuccess; +} + +/* SEC_PKCS12AddPublicKeyIntegrity + * Add public key integrity to the exported data. If an integrity method + * has already been set, then return an error. The certificate must be + * allowed to be used as a signing cert. + * + * p12ctxt - the export context + * cert - signer certificate + * certDb - the certificate database + * algorithm - signing algorithm + * keySize - size of the signing key (?) + */ +SECStatus +SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt, + CERTCertificate *cert, CERTCertDBHandle *certDb, + SECOidTag algorithm, int keySize) +{ + if(!p12ctxt) { + return SECFailure; + } + + p12ctxt->integrityInfo.pubkeyInfo.cert = cert; + p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb; + p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm; + p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize; + p12ctxt->integrityEnabled = PR_TRUE; + + return SECSuccess; +} + + +/* + * Adding safes - encrypted (password/public key) or unencrypted + * Each of the safe creation routines return an opaque pointer which + * are later passed into the routines for exporting certificates and + * keys. + */ + +/* append the newly created safeInfo to list of safeInfos in the export + * context. + */ +static SECStatus +sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *info) +{ + void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL; + + if(!p12ctxt || !info) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + /* if no safeInfos have been set, create the list, otherwise expand it. */ + if(!p12ctxt->safeInfoCount) { + p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->arena, + 2 * sizeof(SEC_PKCS12SafeInfo *)); + dummy1 = p12ctxt->safeInfos; + p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, + 2 * sizeof(SECItem *)); + dummy2 = p12ctxt->authSafe.encodedSafes; + } else { + dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos, + (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12SafeInfo *), + (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12SafeInfo *)); + p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1; + dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes, + (p12ctxt->authSafe.safeCount + 1) * sizeof(SECItem *), + (p12ctxt->authSafe.safeCount + 2) * sizeof(SECItem *)); + p12ctxt->authSafe.encodedSafes = (SECItem**)dummy2; + } + if(!dummy1 || !dummy2) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* append the new safeInfo and null terminate the list */ + p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info; + p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL; + p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] = + (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); + if(!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL; + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(p12ctxt->arena, mark); + return SECFailure; +} + +/* SEC_PKCS12CreatePasswordPrivSafe + * Create a password privacy safe to store exported information in. + * + * p12ctxt - export context + * pwitem - password for encryption + * privAlg - pbe algorithm through which encryption is done. + */ +SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag privAlg) +{ + SEC_PKCS12SafeInfo *safeInfo = NULL; + void *mark = NULL; + PK11SlotInfo *slot; + SECAlgorithmID *algId; + SECItem uniPwitem = {siBuffer, NULL, 0}; + + if(!p12ctxt) { + return NULL; + } + + /* allocate the safe info */ + mark = PORT_ArenaMark(p12ctxt->arena); + safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SEC_PKCS12SafeInfo)); + if(!safeInfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; + } + + safeInfo->itemCount = 0; + + /* create the encrypted safe */ + safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn, + p12ctxt->pwfnarg); + if(!safeInfo->cinfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + safeInfo->arena = p12ctxt->arena; + + /* convert the password to unicode */ + if(!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem, + PR_TRUE, PR_TRUE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* generate the encryption key */ + slot = p12ctxt->slot; + if(!slot) { + slot = PK11_GetInternalKeySlot(); + if(!slot) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + } + + algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo); + safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem, + PR_FALSE, p12ctxt->wincx); + if(!safeInfo->encryptionKey) { + goto loser; + } + + safeInfo->arena = p12ctxt->arena; + safeInfo->safe = NULL; + if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { + goto loser; + } + + if(uniPwitem.data) { + SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); + } + PORT_ArenaUnmark(p12ctxt->arena, mark); + return safeInfo; + +loser: + if(safeInfo->cinfo) { + SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); + } + + if(uniPwitem.data) { + SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); + } + + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/* SEC_PKCS12CreateUnencryptedSafe + * Creates an unencrypted safe within the export context. + * + * p12ctxt - the export context + */ +SEC_PKCS12SafeInfo * +SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt) +{ + SEC_PKCS12SafeInfo *safeInfo = NULL; + void *mark = NULL; + + if(!p12ctxt) { + return NULL; + } + + /* create the safe info */ + mark = PORT_ArenaMark(p12ctxt->arena); + safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SEC_PKCS12SafeInfo)); + if(!safeInfo) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + safeInfo->itemCount = 0; + + /* create the safe content */ + safeInfo->cinfo = SEC_PKCS7CreateData(); + if(!safeInfo->cinfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return safeInfo; + +loser: + if(safeInfo->cinfo) { + SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); + } + + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/* SEC_PKCS12CreatePubKeyEncryptedSafe + * Creates a safe which is protected by public key encryption. + * + * p12ctxt - the export context + * certDb - the certificate database + * signer - the signer's certificate + * recipients - the list of recipient certificates. + * algorithm - the encryption algorithm to use + * keysize - the algorithms key size (?) + */ +SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt, + CERTCertDBHandle *certDb, + CERTCertificate *signer, + CERTCertificate **recipients, + SECOidTag algorithm, int keysize) +{ + SEC_PKCS12SafeInfo *safeInfo = NULL; + void *mark = NULL; + + if(!p12ctxt || !signer || !recipients || !(*recipients)) { + return NULL; + } + + /* allocate the safeInfo */ + mark = PORT_ArenaMark(p12ctxt->arena); + safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SEC_PKCS12SafeInfo)); + if(!safeInfo) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + safeInfo->itemCount = 0; + safeInfo->arena = p12ctxt->arena; + + /* create the enveloped content info using certUsageEmailSigner currently. + * XXX We need to eventually use something other than certUsageEmailSigner + */ + safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner, + certDb, algorithm, keysize, + p12ctxt->pwfn, p12ctxt->pwfnarg); + if(!safeInfo->cinfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* add recipients */ + if(recipients) { + unsigned int i = 0; + while(recipients[i] != NULL) { + SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i], + certUsageEmailRecipient, certDb); + if(rv != SECSuccess) { + goto loser; + } + i++; + } + } + + if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return safeInfo; + +loser: + if(safeInfo->cinfo) { + SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); + safeInfo->cinfo = NULL; + } + + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/********************************* + * Routines to handle the exporting of the keys and certificates + *********************************/ + +/* creates a safe contents which safeBags will be appended to */ +sec_PKCS12SafeContents * +sec_PKCS12CreateSafeContents(PRArenaPool *arena) +{ + sec_PKCS12SafeContents *safeContents; + + if(arena == NULL) { + return NULL; + } + + /* create the safe contents */ + safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena, + sizeof(sec_PKCS12SafeContents)); + if(!safeContents) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* set up the internal contents info */ + safeContents->safeBags = NULL; + safeContents->arena = arena; + safeContents->bagCount = 0; + + return safeContents; + +loser: + return NULL; +} + +/* appends a safe bag to a safeContents using the specified arena. + */ +SECStatus +sec_pkcs12_append_bag_to_safe_contents(PRArenaPool *arena, + sec_PKCS12SafeContents *safeContents, + sec_PKCS12SafeBag *safeBag) +{ + void *mark = NULL, *dummy = NULL; + + if(!arena || !safeBag || !safeContents) { + return SECFailure; + } + + mark = PORT_ArenaMark(arena); + if(!mark) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + /* allocate space for the list, or reallocate to increase space */ + if(!safeContents->safeBags) { + safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena, + (2 * sizeof(sec_PKCS12SafeBag *))); + dummy = safeContents->safeBags; + safeContents->bagCount = 0; + } else { + dummy = PORT_ArenaGrow(arena, safeContents->safeBags, + (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag *), + (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag *)); + safeContents->safeBags = (sec_PKCS12SafeBag **)dummy; + } + + if(!dummy) { + PORT_ArenaRelease(arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + /* append the bag at the end and null terminate the list */ + safeContents->safeBags[safeContents->bagCount++] = safeBag; + safeContents->safeBags[safeContents->bagCount] = NULL; + + PORT_ArenaUnmark(arena, mark); + + return SECSuccess; +} + +/* appends a safeBag to a specific safeInfo. + */ +SECStatus +sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt, + SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag) +{ + sec_PKCS12SafeContents *dest; + SECStatus rv = SECFailure; + + if(!p12ctxt || !safeBag || !safeInfo) { + return SECFailure; + } + + if(!safeInfo->safe) { + safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena); + if(!safeInfo->safe) { + return SECFailure; + } + } + + dest = safeInfo->safe; + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag); + if(rv == SECSuccess) { + safeInfo->itemCount++; + } + + return rv; +} + +/* Creates a safeBag of the specified type, and if bagData is specified, + * the contents are set. The contents could be set later by the calling + * routine. + */ +sec_PKCS12SafeBag * +sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType, + void *bagData) +{ + sec_PKCS12SafeBag *safeBag; + PRBool setName = PR_TRUE; + void *mark = NULL; + SECStatus rv = SECSuccess; + SECOidData *oidData = NULL; + + if(!p12ctxt) { + return NULL; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + if(!mark) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(sec_PKCS12SafeBag)); + if(!safeBag) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* set the bags content based upon bag type */ + switch(bagType) { + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + safeBag->safeBagContent.pkcs8KeyBag = + (SECKEYPrivateKeyInfo *)bagData; + break; + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData; + break; + case SEC_OID_PKCS12_V1_CRL_BAG_ID: + safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData; + break; + case SEC_OID_PKCS12_V1_SECRET_BAG_ID: + safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData; + break; + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + safeBag->safeBagContent.pkcs8ShroudedKeyBag = + (SECKEYEncryptedPrivateKeyInfo *)bagData; + break; + case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: + safeBag->safeBagContent.safeContents = + (sec_PKCS12SafeContents *)bagData; + setName = PR_FALSE; + break; + default: + goto loser; + } + + oidData = SECOID_FindOIDByTag(bagType); + if(oidData) { + rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->oid); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + } else { + goto loser; + } + + safeBag->arena = p12ctxt->arena; + PORT_ArenaUnmark(p12ctxt->arena, mark); + + return safeBag; + +loser: + if(mark) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } + + return NULL; +} + +/* Creates a new certificate bag and returns a pointer to it. If an error + * occurs NULL is returned. + */ +sec_PKCS12CertBag * +sec_PKCS12NewCertBag(PRArenaPool *arena, SECOidTag certType) +{ + sec_PKCS12CertBag *certBag = NULL; + SECOidData *bagType = NULL; + SECStatus rv; + void *mark = NULL; + + if(!arena) { + return NULL; + } + + mark = PORT_ArenaMark(arena); + certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena, + sizeof(sec_PKCS12CertBag)); + if(!certBag) { + PORT_ArenaRelease(arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + bagType = SECOID_FindOIDByTag(certType); + if(!bagType) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_ArenaUnmark(arena, mark); + return certBag; + +loser: + PORT_ArenaRelease(arena, mark); + return NULL; +} + +/* Creates a new CRL bag and returns a pointer to it. If an error + * occurs NULL is returned. + */ +sec_PKCS12CRLBag * +sec_PKCS12NewCRLBag(PRArenaPool *arena, SECOidTag crlType) +{ + sec_PKCS12CRLBag *crlBag = NULL; + SECOidData *bagType = NULL; + SECStatus rv; + void *mark = NULL; + + if(!arena) { + return NULL; + } + + mark = PORT_ArenaMark(arena); + crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena, + sizeof(sec_PKCS12CRLBag)); + if(!crlBag) { + PORT_ArenaRelease(arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + bagType = SECOID_FindOIDByTag(crlType); + if(!bagType) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_ArenaUnmark(arena, mark); + return crlBag; + +loser: + PORT_ArenaRelease(arena, mark); + return NULL; +} + +/* sec_PKCS12AddAttributeToBag + * adds an attribute to a safeBag. currently, the only attributes supported + * are those which are specified within PKCS 12. + * + * p12ctxt - the export context + * safeBag - the safeBag to which attributes are appended + * attrType - the attribute type + * attrData - the attribute data + */ +SECStatus +sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt, + sec_PKCS12SafeBag *safeBag, SECOidTag attrType, + SECItem *attrData) +{ + sec_PKCS12Attribute *attribute; + void *mark = NULL, *dummy = NULL; + SECOidData *oiddata = NULL; + SECItem unicodeName = { siBuffer, NULL, 0}; + void *src = NULL; + unsigned int nItems = 0; + SECStatus rv; + + if(!safeBag || !p12ctxt) { + return SECFailure; + } + + mark = PORT_ArenaMark(safeBag->arena); + + /* allocate the attribute */ + attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena, + sizeof(sec_PKCS12Attribute)); + if(!attribute) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* set up the attribute */ + oiddata = SECOID_FindOIDByTag(attrType); + if(!oiddata) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) != + SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + nItems = 1; + switch(attrType) { + case SEC_OID_PKCS9_LOCAL_KEY_ID: + { + src = attrData; + break; + } + case SEC_OID_PKCS9_FRIENDLY_NAME: + { + if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, + &unicodeName, attrData, PR_FALSE, + PR_FALSE, PR_TRUE)) { + goto loser; + } + src = &unicodeName; + break; + } + default: + goto loser; + } + + /* append the attribute to the attribute value list */ + attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, + ((nItems + 1) * sizeof(SECItem *))); + if(!attribute->attrValue) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* XXX this will need to be changed if attributes requiring more than + * one element are ever used. + */ + attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SECItem)); + if(!attribute->attrValue[0]) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + attribute->attrValue[1] = NULL; + + rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0], + (SECItem*)src); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* append the attribute to the safeBag attributes */ + if(safeBag->nAttribs) { + dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs, + ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)), + ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *))); + safeBag->attribs = (sec_PKCS12Attribute **)dummy; + } else { + safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->arena, + 2 * sizeof(sec_PKCS12Attribute *)); + dummy = safeBag->attribs; + } + if(!dummy) { + goto loser; + } + + safeBag->attribs[safeBag->nAttribs] = attribute; + safeBag->attribs[++safeBag->nAttribs] = NULL; + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + if(mark) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } + + return SECFailure; +} + +/* SEC_PKCS12AddCert + * Adds a certificate to the data being exported. + * + * p12ctxt - the export context + * safe - the safeInfo to which the certificate is placed + * nestedDest - if the cert is to be placed within a nested safeContents then, + * this value is to be specified with the destination + * cert - the cert to export + * certDb - the certificate database handle + * keyId - a unique identifier to associate a certificate/key pair + * includeCertChain - PR_TRUE if the certificate chain is to be included. + */ +SECStatus +SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, + void *nestedDest, CERTCertificate *cert, + CERTCertDBHandle *certDb, SECItem *keyId, + PRBool includeCertChain) +{ + sec_PKCS12CertBag *certBag; + sec_PKCS12SafeBag *safeBag; + void *mark; + SECStatus rv; + SECItem nick = {siBuffer, NULL,0}; + + if(!p12ctxt || !cert) { + return SECFailure; + } + mark = PORT_ArenaMark(p12ctxt->arena); + + /* allocate the cert bag */ + certBag = sec_PKCS12NewCertBag(p12ctxt->arena, + SEC_OID_PKCS9_X509_CERT); + if(!certBag) { + goto loser; + } + + if(SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert, + &cert->derCert) != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* if the cert chain is to be included, we should only be exporting + * the cert from our internal database. + */ + if(includeCertChain) { + CERTCertificateList *certList = CERT_CertChainFromCert(cert, + certUsageSSLClient, + PR_TRUE); + unsigned int count = 0; + if(!certList) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* add cert chain */ + for(count = 0; count < (unsigned int)certList->len; count++) { + if(SECITEM_CompareItem(&certList->certs[count], &cert->derCert) + != SECEqual) { + CERTCertificate *tempCert; + + /* decode the certificate */ + /* XXX + * This was rather silly. The chain is constructed above + * by finding all of the CERTCertificate's in the database. + * Then the chain is put into a CERTCertificateList, which only + * contains the DER. Finally, the DER was decoded, and the + * decoded cert was sent recursively back to this function. + * Beyond being inefficent, this causes data loss (specifically, + * the nickname). Instead, for 3.4, we'll do a lookup by the + * DER, which should return the cached entry. + */ + tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), + &certList->certs[count]); + if(!tempCert) { + CERT_DestroyCertificateList(certList); + goto loser; + } + + /* add the certificate */ + if(SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert, + certDb, NULL, PR_FALSE) != SECSuccess) { + CERT_DestroyCertificate(tempCert); + CERT_DestroyCertificateList(certList); + goto loser; + } + CERT_DestroyCertificate(tempCert); + } + } + CERT_DestroyCertificateList(certList); + } + + /* if the certificate has a nickname, we will set the friendly name + * to that. + */ + if(cert->nickname) { + if (cert->slot && !PK11_IsInternal(cert->slot)) { + /* + * The cert is coming off of an external token, + * let's strip the token name from the nickname + * and only add what comes after the colon as the + * nickname. -javi + */ + char *delimit; + + delimit = PORT_Strchr(cert->nickname,':'); + if (delimit == NULL) { + nick.data = (unsigned char *)cert->nickname; + nick.len = PORT_Strlen(cert->nickname); + } else { + delimit++; + nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena, + delimit); + nick.len = PORT_Strlen(delimit); + } + } else { + nick.data = (unsigned char *)cert->nickname; + nick.len = PORT_Strlen(cert->nickname); + } + } + + safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID, + certBag); + if(!safeBag) { + goto loser; + } + + /* add the friendly name and keyId attributes, if necessary */ + if(nick.data) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, + SEC_OID_PKCS9_FRIENDLY_NAME, &nick) + != SECSuccess) { + goto loser; + } + } + + if(keyId) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + goto loser; + } + } + + /* append the cert safeBag */ + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + safeBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag); + } + + if(rv != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + if(mark) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } + + return SECFailure; +} + +/* SEC_PKCS12AddEncryptedKey + * Extracts the key associated with a particular certificate and exports + * it. + * + * p12ctxt - the export context + * safe - the safeInfo to place the key in + * nestedDest - the nested safeContents to place a key + * cert - the certificate which the key belongs to + * shroudKey - encrypt the private key for export. This value should + * always be true. lower level code will not allow the export + * of unencrypted private keys. + * algorithm - the algorithm with which to encrypt the private key + * pwitem - the password to encrypted the private key with + * keyId - the keyID attribute + * nickName - the nickname attribute + */ +static SECStatus +SEC_PKCS12AddEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, + SECKEYEncryptedPrivateKeyInfo *epki, SEC_PKCS12SafeInfo *safe, + void *nestedDest, SECItem *keyId, SECItem *nickName) +{ + void *mark; + void *keyItem; + SECOidTag keyType; + SECStatus rv = SECFailure; + sec_PKCS12SafeBag *returnBag; + + if(!p12ctxt || !safe || !epki) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + keyItem = PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if(!keyItem) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, + (SECKEYEncryptedPrivateKeyInfo *)keyItem, + epki); + keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID; + + if(rv != SECSuccess) { + goto loser; + } + + /* create the safe bag and set any attributes */ + returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem); + if(!returnBag) { + rv = SECFailure; + goto loser; + } + + if(nickName) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, + SEC_OID_PKCS9_FRIENDLY_NAME, nickName) + != SECSuccess) { + goto loser; + } + } + + if(keyId) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + goto loser; + } + } + + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + returnBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag); + } + +loser: + + if (rv != SECSuccess) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } else { + PORT_ArenaUnmark(p12ctxt->arena, mark); + } + + return rv; +} + +/* SEC_PKCS12AddKeyForCert + * Extracts the key associated with a particular certificate and exports + * it. + * + * p12ctxt - the export context + * safe - the safeInfo to place the key in + * nestedDest - the nested safeContents to place a key + * cert - the certificate which the key belongs to + * shroudKey - encrypt the private key for export. This value should + * always be true. lower level code will not allow the export + * of unencrypted private keys. + * algorithm - the algorithm with which to encrypt the private key + * pwitem - the password to encrypt the private key with + * keyId - the keyID attribute + * nickName - the nickname attribute + */ +SECStatus +SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, + void *nestedDest, CERTCertificate *cert, + PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem, + SECItem *keyId, SECItem *nickName) +{ + void *mark; + void *keyItem; + SECOidTag keyType; + SECStatus rv = SECFailure; + SECItem nickname = {siBuffer,NULL,0}, uniPwitem = {siBuffer, NULL, 0}; + sec_PKCS12SafeBag *returnBag; + + if(!p12ctxt || !cert || !safe) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + /* retrieve the key based upon the type that it is and + * specify the type of safeBag to store the key in + */ + if(!shroudKey) { + + /* extract the key unencrypted. this will most likely go away */ + SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert, + p12ctxt->wincx); + if(!pki) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + return SECFailure; + } + keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo)); + if(!keyItem) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena, + (SECKEYPrivateKeyInfo *)keyItem, pki); + keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID; + SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); + } else { + + /* extract the key encrypted */ + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + PK11SlotInfo *slot = p12ctxt->slot; + + if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, &uniPwitem, + pwitem, PR_TRUE, PR_TRUE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* we want to make sure to take the key out of the key slot */ + if(PK11_IsInternal(p12ctxt->slot)) { + slot = PK11_GetInternalKeySlot(); + } + + epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, + &uniPwitem, cert, 1, + p12ctxt->wincx); + if(PK11_IsInternal(p12ctxt->slot)) { + PK11_FreeSlot(slot); + } + + keyItem = PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if(!keyItem) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(!epki) { + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + return SECFailure; + } + rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, + (SECKEYEncryptedPrivateKeyInfo *)keyItem, + epki); + keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID; + SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); + } + + if(rv != SECSuccess) { + goto loser; + } + + /* if no nickname specified, let's see if the certificate has a + * nickname. + */ + if(!nickName) { + if(cert->nickname) { + nickname.data = (unsigned char *)cert->nickname; + nickname.len = PORT_Strlen(cert->nickname); + nickName = &nickname; + } + } + + /* create the safe bag and set any attributes */ + returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem); + if(!returnBag) { + rv = SECFailure; + goto loser; + } + + if(nickName) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, + SEC_OID_PKCS9_FRIENDLY_NAME, nickName) + != SECSuccess) { + goto loser; + } + } + + if(keyId) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + goto loser; + } + } + + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + returnBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag); + } + +loser: + + if (rv != SECSuccess) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } else { + PORT_ArenaUnmark(p12ctxt->arena, mark); + } + + return rv; +} + +/* SEC_PKCS12AddCertAndEncryptedKey + * Add a certificate and key pair to be exported. + * + * p12ctxt - the export context + * certSafe - the safeInfo where the cert is stored + * certNestedDest - the nested safeContents to store the cert + * keySafe - the safeInfo where the key is stored + * keyNestedDest - the nested safeContents to store the key + * shroudKey - extract the private key encrypted? + * pwitem - the password with which the key is encrypted + * algorithm - the algorithm with which the key is encrypted + */ +SECStatus +SEC_PKCS12AddDERCertAndEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, + SECItem *derCert, void *keySafe, + void *keyNestedDest, SECKEYEncryptedPrivateKeyInfo *epki, + char *nickname) +{ + SECStatus rv = SECFailure; + SGNDigestInfo *digest = NULL; + void *mark = NULL; + CERTCertificate *cert; + SECItem nick = {siBuffer, NULL,0}, *nickPtr = NULL; + + if(!p12ctxt || !certSafe || !keySafe || !derCert) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if(!cert) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + cert->nickname = nickname; + + /* generate the thumbprint of the cert to use as a keyId */ + digest = sec_pkcs12_compute_thumbprint(&cert->derCert); + if(!digest) { + CERT_DestroyCertificate(cert); + return SECFailure; + } + + /* add the certificate */ + rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, + certNestedDest, cert, NULL, + &digest->digest, PR_FALSE); + if(rv != SECSuccess) { + goto loser; + } + + if(nickname) { + nick.data = (unsigned char *)nickname; + nick.len = PORT_Strlen(nickname); + nickPtr = &nick; + } else { + nickPtr = NULL; + } + + /* add the key */ + rv = SEC_PKCS12AddEncryptedKey(p12ctxt, epki, (SEC_PKCS12SafeInfo*)keySafe, + keyNestedDest, &digest->digest, nickPtr ); + if(rv != SECSuccess) { + goto loser; + } + + SGN_DestroyDigestInfo(digest); + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + SGN_DestroyDigestInfo(digest); + CERT_DestroyCertificate(cert); + PORT_ArenaRelease(p12ctxt->arena, mark); + + return SECFailure; +} + +/* SEC_PKCS12AddCertAndKey + * Add a certificate and key pair to be exported. + * + * p12ctxt - the export context + * certSafe - the safeInfo where the cert is stored + * certNestedDest - the nested safeContents to store the cert + * keySafe - the safeInfo where the key is stored + * keyNestedDest - the nested safeContents to store the key + * shroudKey - extract the private key encrypted? + * pwitem - the password with which the key is encrypted + * algorithm - the algorithm with which the key is encrypted + */ +SECStatus +SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, + CERTCertificate *cert, CERTCertDBHandle *certDb, + void *keySafe, void *keyNestedDest, + PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm) +{ + SECStatus rv = SECFailure; + SGNDigestInfo *digest = NULL; + void *mark = NULL; + + if(!p12ctxt || !certSafe || !keySafe || !cert) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + /* generate the thumbprint of the cert to use as a keyId */ + digest = sec_pkcs12_compute_thumbprint(&cert->derCert); + if(!digest) { + PORT_ArenaRelease(p12ctxt->arena, mark); + return SECFailure; + } + + /* add the certificate */ + rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, + (SEC_PKCS12SafeInfo*)certNestedDest, cert, certDb, + &digest->digest, PR_TRUE); + if(rv != SECSuccess) { + goto loser; + } + + /* add the key */ + rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo*)keySafe, + keyNestedDest, cert, + shroudKey, algorithm, pwitem, + &digest->digest, NULL ); + if(rv != SECSuccess) { + goto loser; + } + + SGN_DestroyDigestInfo(digest); + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + SGN_DestroyDigestInfo(digest); + PORT_ArenaRelease(p12ctxt->arena, mark); + + return SECFailure; +} + +/* SEC_PKCS12CreateNestedSafeContents + * Allows nesting of safe contents to be implemented. No limit imposed on + * depth. + * + * p12ctxt - the export context + * baseSafe - the base safeInfo + * nestedDest - a parent safeContents (?) + */ +void * +SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt, + void *baseSafe, void *nestedDest) +{ + sec_PKCS12SafeContents *newSafe; + sec_PKCS12SafeBag *safeContentsBag; + void *mark; + SECStatus rv; + + if(!p12ctxt || !baseSafe) { + return NULL; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena); + if(!newSafe) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* create the safeContents safeBag */ + safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt, + SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID, + newSafe); + if(!safeContentsBag) { + goto loser; + } + + /* append the safeContents to the appropriate area */ + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + safeContentsBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo*)baseSafe, + safeContentsBag); + } + if(rv != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return newSafe; + +loser: + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/********************************* + * Encoding routines + *********************************/ + +/* set up the encoder context based on information in the export context + * and return the newly allocated enocoder context. A return of NULL + * indicates an error occurred. + */ +sec_PKCS12EncoderContext * +sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp) +{ + sec_PKCS12EncoderContext *p12enc = NULL; + unsigned int i, nonEmptyCnt; + SECStatus rv; + SECItem ignore = {0}; + void *mark; + + if(!p12exp || !p12exp->safeInfos) { + return NULL; + } + + /* check for any empty safes and skip them */ + i = nonEmptyCnt = 0; + while(p12exp->safeInfos[i]) { + if(p12exp->safeInfos[i]->itemCount) { + nonEmptyCnt++; + } + i++; + } + if(nonEmptyCnt == 0) { + return NULL; + } + p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL; + + /* allocate the encoder context */ + mark = PORT_ArenaMark(p12exp->arena); + p12enc = (sec_PKCS12EncoderContext*)PORT_ArenaZAlloc(p12exp->arena, + sizeof(sec_PKCS12EncoderContext)); + if(!p12enc) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + p12enc->arena = p12exp->arena; + p12enc->p12exp = p12exp; + + /* set up the PFX version and information */ + PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem)); + if(!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version), + SEC_PKCS12_VERSION) ) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* set up the authenticated safe content info based on the + * type of integrity being used. this should be changed to + * enforce integrity mode, but will not be implemented until + * it is confirmed that integrity must be in place + */ + if(p12exp->integrityEnabled && !p12exp->pwdIntegrity) { + SECStatus rv; + + /* create public key integrity mode */ + p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData( + p12exp->integrityInfo.pubkeyInfo.cert, + certUsageEmailSigner, + p12exp->integrityInfo.pubkeyInfo.certDb, + p12exp->integrityInfo.pubkeyInfo.algorithm, + NULL, + p12exp->pwfn, + p12exp->pwfnarg); + if(!p12enc->aSafeCinfo) { + goto loser; + } + if(SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo,NULL) != SECSuccess) { + goto loser; + } + rv = SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo); + PORT_Assert(rv == SECSuccess); + } else { + p12enc->aSafeCinfo = SEC_PKCS7CreateData(); + + /* init password pased integrity mode */ + if(p12exp->integrityEnabled) { + SECItem pwd = {siBuffer,NULL, 0}; + SECItem *salt = sec_pkcs12_generate_salt(); + PK11SymKey *symKey; + SECItem *params; + CK_MECHANISM_TYPE integrityMech; + + /* zero out macData and set values */ + PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData)); + + if(!salt) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* generate HMAC key */ + if(!sec_pkcs12_convert_item_to_unicode(NULL, &pwd, + p12exp->integrityInfo.pwdInfo.password, PR_TRUE, + PR_TRUE, PR_TRUE)) { + goto loser; + } + + params = PK11_CreatePBEParams(salt, &pwd, 1); + SECITEM_ZfreeItem(salt, PR_TRUE); + SECITEM_ZfreeItem(&pwd, PR_FALSE); + + switch (p12exp->integrityInfo.pwdInfo.algorithm) { + case SEC_OID_SHA1: + integrityMech = CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN; break; + case SEC_OID_MD5: + integrityMech = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN; break; + case SEC_OID_MD2: + integrityMech = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN; break; + default: + goto loser; + } + + symKey = PK11_KeyGen(NULL, integrityMech, params, 20, NULL); + PK11_DestroyPBEParams(params); + if(!symKey) { + goto loser; + } + + /* initialize hmac */ + p12enc->hmacCx = PK11_CreateContextBySymKey( + sec_pkcs12_algtag_to_mech(p12exp->integrityInfo.pwdInfo.algorithm), + CKA_SIGN, symKey, &ignore); + if(!p12enc->hmacCx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + rv = PK11_DigestBegin(p12enc->hmacCx); + if (rv != SECSuccess) + goto loser; + } + } + + if(!p12enc->aSafeCinfo) { + goto loser; + } + + PORT_ArenaUnmark(p12exp->arena, mark); + + return p12enc; + +loser: + if(p12enc) { + if(p12enc->aSafeCinfo) { + SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo); + } + if(p12enc->hmacCx) { + PK11_DestroyContext(p12enc->hmacCx, PR_TRUE); + } + } + if (p12exp->arena != NULL) + PORT_ArenaRelease(p12exp->arena, mark); + + return NULL; +} + +/* callback wrapper to allow the ASN1 engine to call the PKCS 12 + * output routines. + */ +static void +sec_pkcs12_encoder_out(void *arg, const char *buf, unsigned long len, + int depth, SEC_ASN1EncodingPart data_kind) +{ + struct sec_pkcs12_encoder_output *output; + + output = (struct sec_pkcs12_encoder_output*)arg; + (* output->outputfn)(output->outputarg, buf, len); +} + +/* callback wrapper to wrap SEC_PKCS7EncoderUpdate for ASN1 encoder + */ +static void +sec_pkcs12_wrap_pkcs7_encoder_update(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + SEC_PKCS7EncoderContext *ecx; + if(!buf || !len) { + return; + } + + ecx = (SEC_PKCS7EncoderContext*)arg; + SEC_PKCS7EncoderUpdate(ecx, buf, len); +} + +/* callback wrapper to wrap SEC_ASN1EncoderUpdate for PKCS 7 encoding + */ +static void +sec_pkcs12_wrap_asn1_update_for_p7_update(void *arg, const char *buf, + unsigned long len) +{ + if(!buf && !len) return; + + SEC_ASN1EncoderUpdate((SEC_ASN1EncoderContext*)arg, buf, len); +} + +/* callback wrapper which updates the HMAC and passes on bytes to the + * appropriate output function. + */ +static void +sec_pkcs12_asafe_update_hmac_and_encode_bits(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + sec_PKCS12EncoderContext *p12ecx; + + p12ecx = (sec_PKCS12EncoderContext*)arg; + PK11_DigestOp(p12ecx->hmacCx, (unsigned char *)buf, len); + sec_pkcs12_wrap_pkcs7_encoder_update(p12ecx->aSafeP7Ecx, buf, len, + depth, data_kind); +} + +/* this function encodes content infos which are part of the + * sequence of content infos labeled AuthenticatedSafes + */ +static SECStatus +sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx) +{ + SECStatus rv = SECSuccess; + SEC_PKCS5KeyAndPassword keyPwd; + SEC_PKCS7EncoderContext *p7ecx; + SEC_PKCS7ContentInfo *cinfo; + SEC_ASN1EncoderContext *ecx = NULL; + + void *arg = NULL; + + if(p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) { + SEC_PKCS12SafeInfo *safeInfo; + SECOidTag cinfoType; + + safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe]; + + /* skip empty safes */ + if(safeInfo->itemCount == 0) { + return SECSuccess; + } + + cinfo = safeInfo->cinfo; + cinfoType = SEC_PKCS7ContentType(cinfo); + + /* determine the safe type and set the appropriate argument */ + switch(cinfoType) { + case SEC_OID_PKCS7_DATA: + arg = NULL; + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + keyPwd.pwitem = &safeInfo->pwitem; + keyPwd.key = safeInfo->encryptionKey; + arg = &keyPwd; + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + arg = NULL; + break; + default: + return SECFailure; + + } + + /* start the PKCS7 encoder */ + p7ecx = SEC_PKCS7EncoderStart(cinfo, + sec_pkcs12_wrap_asn1_update_for_p7_update, + p12ecx->aSafeEcx, (PK11SymKey *)arg); + if(!p7ecx) { + goto loser; + } + + /* encode safe contents */ + ecx = SEC_ASN1EncoderStart(safeInfo->safe, sec_PKCS12SafeContentsTemplate, + sec_pkcs12_wrap_pkcs7_encoder_update, p7ecx); + if(!ecx) { + goto loser; + } + rv = SEC_ASN1EncoderUpdate(ecx, NULL, 0); + SEC_ASN1EncoderFinish(ecx); + ecx = NULL; + if(rv != SECSuccess) { + goto loser; + } + + + /* finish up safe content info */ + rv = SEC_PKCS7EncoderFinish(p7ecx, p12ecx->p12exp->pwfn, + p12ecx->p12exp->pwfnarg); + } + + return SECSuccess; + +loser: + if(p7ecx) { + SEC_PKCS7EncoderFinish(p7ecx, p12ecx->p12exp->pwfn, + p12ecx->p12exp->pwfnarg); + } + + if(ecx) { + SEC_ASN1EncoderFinish(ecx); + } + + return SECFailure; +} + +/* finish the HMAC and encode the macData so that it can be + * encoded. + */ +static SECStatus +sec_pkcs12_update_mac(sec_PKCS12EncoderContext *p12ecx) +{ + SECItem hmac = { siBuffer, NULL, 0 }; + SECStatus rv; + SGNDigestInfo *di = NULL; + void *dummy; + + if(!p12ecx) { + return SECFailure; + } + + /* make sure we are using password integrity mode */ + if(!p12ecx->p12exp->integrityEnabled) { + return SECSuccess; + } + + if(!p12ecx->p12exp->pwdIntegrity) { + return SECSuccess; + } + + /* finish the hmac */ + hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH); + if(!hmac.data) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + rv = PK11_DigestFinal(p12ecx->hmacCx, hmac.data, &hmac.len, SHA1_LENGTH); + + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* create the digest info */ + di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm, + hmac.data, hmac.len); + if(!di) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + + rv = SGN_CopyDigestInfo(p12ecx->arena, &p12ecx->mac.safeMac, di); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* encode the mac data */ + dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData, + &p12ecx->mac, sec_PKCS12MacDataTemplate); + if(!dummy) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + } + +loser: + if(di) { + SGN_DestroyDigestInfo(di); + } + if(hmac.data) { + SECITEM_ZfreeItem(&hmac, PR_FALSE); + } + PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE); + p12ecx->hmacCx = NULL; + + return rv; +} + +/* wraps the ASN1 encoder update for PKCS 7 encoder */ +static void +sec_pkcs12_wrap_asn1_encoder_update(void *arg, const char *buf, + unsigned long len) +{ + SEC_ASN1EncoderContext *cx; + + cx = (SEC_ASN1EncoderContext*)arg; + SEC_ASN1EncoderUpdate(cx, buf, len); +} + +/* pfx notify function for ASN1 encoder. we want to stop encoding, once we reach + * the authenticated safe. at that point, the encoder will be updated via streaming + * as the authenticated safe is encoded. + */ +static void +sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_depth) +{ + sec_PKCS12EncoderContext *p12ecx; + + if(!before) { + return; + } + + /* look for authenticated safe */ + p12ecx = (sec_PKCS12EncoderContext*)arg; + if(dest != &p12ecx->pfx.encodedAuthSafe) { + return; + } + + SEC_ASN1EncoderSetTakeFromBuf(p12ecx->ecx); + SEC_ASN1EncoderSetStreaming(p12ecx->ecx); + SEC_ASN1EncoderClearNotifyProc(p12ecx->ecx); +} + +/* SEC_PKCS12Encode + * Encodes the PFX item and returns it to the output function, via + * callback. the output function must be capable of multiple updates. + * + * p12exp - the export context + * output - the output function callback, will be called more than once, + * must be able to accept streaming data. + * outputarg - argument for the output callback. + */ +SECStatus +SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, + SEC_PKCS12EncoderOutputCallback output, void *outputarg) +{ + sec_PKCS12EncoderContext *p12enc; + struct sec_pkcs12_encoder_output outInfo; + SECStatus rv; + + if(!p12exp || !output) { + return SECFailure; + } + + /* get the encoder context */ + p12enc = sec_pkcs12_encoder_start_context(p12exp); + if(!p12enc) { + return SECFailure; + } + + outInfo.outputfn = output; + outInfo.outputarg = outputarg; + + /* set up PFX encoder. Set it for streaming */ + p12enc->ecx = SEC_ASN1EncoderStart(&p12enc->pfx, sec_PKCS12PFXItemTemplate, + sec_pkcs12_encoder_out, + &outInfo); + if(!p12enc->ecx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + SEC_ASN1EncoderSetStreaming(p12enc->ecx); + SEC_ASN1EncoderSetNotifyProc(p12enc->ecx, sec_pkcs12_encoder_pfx_notify, p12enc); + rv = SEC_ASN1EncoderUpdate(p12enc->ecx, NULL, 0); + if(rv != SECSuccess) { + rv = SECFailure; + goto loser; + } + + /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */ + p12enc->aSafeP7Ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo, + sec_pkcs12_wrap_asn1_encoder_update, + p12enc->ecx, NULL); + if(!p12enc->aSafeP7Ecx) { + rv = SECFailure; + goto loser; + } + + /* encode asafe */ + if(p12enc->p12exp->integrityEnabled && p12enc->p12exp->pwdIntegrity) { + p12enc->aSafeEcx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe, + sec_PKCS12AuthenticatedSafeTemplate, + sec_pkcs12_asafe_update_hmac_and_encode_bits, + p12enc); + } else { + p12enc->aSafeEcx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe, + sec_PKCS12AuthenticatedSafeTemplate, + sec_pkcs12_wrap_pkcs7_encoder_update, + p12enc->aSafeP7Ecx); + } + if(!p12enc->aSafeEcx) { + rv = SECFailure; + goto loser; + } + SEC_ASN1EncoderSetStreaming(p12enc->aSafeEcx); + SEC_ASN1EncoderSetTakeFromBuf(p12enc->aSafeEcx); + + /* encode each of the safes */ + while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) { + sec_pkcs12_encoder_asafe_process(p12enc); + p12enc->currentSafe++; + } + SEC_ASN1EncoderClearTakeFromBuf(p12enc->aSafeEcx); + SEC_ASN1EncoderClearStreaming(p12enc->aSafeEcx); + SEC_ASN1EncoderUpdate(p12enc->aSafeEcx, NULL, 0); + SEC_ASN1EncoderFinish(p12enc->aSafeEcx); + + /* finish the encoding of the authenticated safes */ + rv = SEC_PKCS7EncoderFinish(p12enc->aSafeP7Ecx, p12exp->pwfn, + p12exp->pwfnarg); + if(rv != SECSuccess) { + goto loser; + } + + SEC_ASN1EncoderClearTakeFromBuf(p12enc->ecx); + SEC_ASN1EncoderClearStreaming(p12enc->ecx); + + /* update the mac, if necessary */ + rv = sec_pkcs12_update_mac(p12enc); + if(rv != SECSuccess) { + goto loser; + } + + /* finish encoding the pfx */ + rv = SEC_ASN1EncoderUpdate(p12enc->ecx, NULL, 0); + + SEC_ASN1EncoderFinish(p12enc->ecx); + +loser: + return rv; +} + +void +SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx) +{ + int i = 0; + + if(!p12ecx) { + return; + } + + if(p12ecx->safeInfos) { + i = 0; + while(p12ecx->safeInfos[i] != NULL) { + if(p12ecx->safeInfos[i]->encryptionKey) { + PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey); + } + if(p12ecx->safeInfos[i]->cinfo) { + SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo); + } + i++; + } + } + + PORT_FreeArena(p12ecx->arena, PR_TRUE); +} + + +/********************************* + * All-in-one routines for exporting certificates + *********************************/ +struct inPlaceEncodeInfo { + PRBool error; + SECItem outItem; +}; + +static void +sec_pkcs12_in_place_encoder_output(void *arg, const char *buf, unsigned long len) +{ + struct inPlaceEncodeInfo *outInfo = (struct inPlaceEncodeInfo*)arg; + + if(!outInfo || !len || outInfo->error) { + return; + } + + if(!outInfo->outItem.data) { + outInfo->outItem.data = (unsigned char*)PORT_ZAlloc(len); + outInfo->outItem.len = 0; + } else { + if(!PORT_Realloc(&(outInfo->outItem.data), (outInfo->outItem.len + len))) { + SECITEM_ZfreeItem(&(outInfo->outItem), PR_FALSE); + outInfo->outItem.data = NULL; + PORT_SetError(SEC_ERROR_NO_MEMORY); + outInfo->error = PR_TRUE; + return; + } + } + + PORT_Memcpy(&(outInfo->outItem.data[outInfo->outItem.len]), buf, len); + outInfo->outItem.len += len; + + return; +} + +/* + * SEC_PKCS12ExportCertifcateAndKeyUsingPassword + * Exports a certificate/key pair using password-based encryption and + * authentication. + * + * pwfn, pwfnarg - password function and argument for the key database + * cert - the certificate to export + * certDb - certificate database + * pwitem - the password to use + * shroudKey - encrypt the key externally, + * keyShroudAlg - encryption algorithm for key + * encryptionAlg - the algorithm with which data is encrypted + * integrityAlg - the algorithm for integrity + */ +SECItem * +SEC_PKCS12ExportCertificateAndKeyUsingPassword( + SECKEYGetPasswordKey pwfn, void *pwfnarg, + CERTCertificate *cert, PK11SlotInfo *slot, + CERTCertDBHandle *certDb, SECItem *pwitem, + PRBool shroudKey, SECOidTag shroudAlg, + PRBool encryptCert, SECOidTag certEncAlg, + SECOidTag integrityAlg, void *wincx) +{ + struct inPlaceEncodeInfo outInfo; + SEC_PKCS12ExportContext *p12ecx = NULL; + SEC_PKCS12SafeInfo *keySafe, *certSafe; + SECItem *returnItem = NULL; + + if(!cert || !pwitem || !slot) { + return NULL; + } + + outInfo.error = PR_FALSE; + outInfo.outItem.data = NULL; + outInfo.outItem.len = 0; + + p12ecx = SEC_PKCS12CreateExportContext(pwfn, pwfnarg, slot, wincx); + if(!p12ecx) { + return NULL; + } + + /* set up cert safe */ + if(encryptCert) { + certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certEncAlg); + } else { + certSafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx); + } + if(!certSafe) { + goto loser; + } + + /* set up key safe */ + if(shroudKey) { + keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx); + } else { + keySafe = certSafe; + } + if(!keySafe) { + goto loser; + } + + /* add integrity mode */ + if(SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, integrityAlg) + != SECSuccess) { + goto loser; + } + + /* add cert and key pair */ + if(SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert, certDb, + keySafe, NULL, shroudKey, pwitem, shroudAlg) + != SECSuccess) { + goto loser; + } + + /* encode the puppy */ + if(SEC_PKCS12Encode(p12ecx, sec_pkcs12_in_place_encoder_output, &outInfo) + != SECSuccess) { + goto loser; + } + if(outInfo.error) { + goto loser; + } + + SEC_PKCS12DestroyExportContext(p12ecx); + + returnItem = SECITEM_DupItem(&outInfo.outItem); + SECITEM_ZfreeItem(&outInfo.outItem, PR_FALSE); + + return returnItem; + +loser: + if(outInfo.outItem.data) { + SECITEM_ZfreeItem(&(outInfo.outItem), PR_TRUE); + } + + if(p12ecx) { + SEC_PKCS12DestroyExportContext(p12ecx); + } + + return NULL; +} + + diff --git a/security/nss/lib/pkcs12/p12exp.c b/security/nss/lib/pkcs12/p12exp.c new file mode 100644 index 000000000..242d98288 --- /dev/null +++ b/security/nss/lib/pkcs12/p12exp.c @@ -0,0 +1,1407 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "plarena.h" +#include "secitem.h" +#include "secoid.h" +#include "seccomon.h" +#include "secport.h" +#include "cert.h" +#include "pkcs12.h" +#include "p12local.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "secerr.h" +#include "p12plcy.h" + +/* release the memory taken up by the list of nicknames */ +static void +sec_pkcs12_destroy_nickname_list(SECItem **nicknames) +{ + int i = 0; + + if(nicknames == NULL) { + return; + } + + while(nicknames[i] != NULL) { + SECITEM_FreeItem(nicknames[i], PR_FALSE); + i++; + } + + PORT_Free(nicknames); +} + +/* release the memory taken up by the list of certificates */ +static void +sec_pkcs12_destroy_certificate_list(CERTCertificate **ref_certs) +{ + int i = 0; + + if(ref_certs == NULL) { + return; + } + + while(ref_certs[i] != NULL) { + CERT_DestroyCertificate(ref_certs[i]); + i++; + } +} + +static void +sec_pkcs12_destroy_cinfos_for_cert_bags(SEC_PKCS12CertAndCRLBag *certBag) +{ + int j = 0; + j = 0; + while(certBag->certAndCRLs[j] != NULL) { + SECOidTag certType = SECOID_FindOIDTag(&certBag->certAndCRLs[j]->BagID); + if(certType == SEC_OID_PKCS12_X509_CERT_CRL_BAG) { + SEC_PKCS12X509CertCRL *x509; + x509 = certBag->certAndCRLs[j]->value.x509; + SEC_PKCS7DestroyContentInfo(&x509->certOrCRL); + } + j++; + } +} + +/* destroy all content infos since they were not allocated in common + * pool + */ +static void +sec_pkcs12_destroy_cert_content_infos(SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + int i, j; + + if((safe != NULL) && (safe->contents != NULL)) { + i = 0; + while(safe->contents[i] != NULL) { + SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType); + if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) { + SEC_PKCS12CertAndCRLBag *certBag; + certBag = safe->contents[i]->safeContent.certAndCRLBag; + sec_pkcs12_destroy_cinfos_for_cert_bags(certBag); + } + i++; + } + } + + if((baggage != NULL) && (baggage->bags != NULL)) { + i = 0; + while(baggage->bags[i] != NULL) { + if(baggage->bags[i]->unencSecrets != NULL) { + j = 0; + while(baggage->bags[i]->unencSecrets[j] != NULL) { + SECOidTag bagType; + bagType = SECOID_FindOIDTag(&baggage->bags[i]->unencSecrets[j]->safeBagType); + if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) { + SEC_PKCS12CertAndCRLBag *certBag; + certBag = baggage->bags[i]->unencSecrets[j]->safeContent.certAndCRLBag; + sec_pkcs12_destroy_cinfos_for_cert_bags(certBag); + } + j++; + } + } + i++; + } + } +} + +/* convert the nickname list from a NULL termincated Char list + * to a NULL terminated SECItem list + */ +static SECItem ** +sec_pkcs12_convert_nickname_list(char **nicknames) +{ + SECItem **nicks; + int i, j; + PRBool error = PR_FALSE; + + if(nicknames == NULL) { + return NULL; + } + + i = j = 0; + while(nicknames[i] != NULL) { + i++; + } + + /* allocate the space and copy the data */ + nicks = (SECItem **)PORT_ZAlloc(sizeof(SECItem *) * (i + 1)); + if(nicks != NULL) { + for(j = 0; ((j < i) && (error == PR_FALSE)); j++) { + nicks[j] = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(nicks[j] != NULL) { + nicks[j]->data = + (unsigned char *)PORT_ZAlloc(PORT_Strlen(nicknames[j])+1); + if(nicks[j]->data != NULL) { + nicks[j]->len = PORT_Strlen(nicknames[j]); + PORT_Memcpy(nicks[j]->data, nicknames[j], nicks[j]->len); + nicks[j]->data[nicks[j]->len] = 0; + } else { + error = PR_TRUE; + } + } else { + error = PR_TRUE; + } + } + } + + if(error == PR_TRUE) { + for(i = 0; i < j; i++) { + SECITEM_FreeItem(nicks[i], PR_TRUE); + } + PORT_Free(nicks); + nicks = NULL; + } + + return nicks; +} + +/* package the certificate add_cert into PKCS12 structures, + * retrieve the certificate chain for the cert and return + * the packaged contents. + * poolp -- common memory pool; + * add_cert -- certificate to package up + * nickname for the certificate + * a return of NULL indicates an error + */ +static SEC_PKCS12CertAndCRL * +sec_pkcs12_get_cert(PRArenaPool *poolp, + CERTCertificate *add_cert, + SECItem *nickname) +{ + SEC_PKCS12CertAndCRL *cert; + SEC_PKCS7ContentInfo *cinfo; + SGNDigestInfo *t_di; + void *mark; + SECStatus rv; + + if((poolp == NULL) || (add_cert == NULL) || (nickname == NULL)) { + return NULL; + } + mark = PORT_ArenaMark(poolp); + + cert = sec_pkcs12_new_cert_crl(poolp, SEC_OID_PKCS12_X509_CERT_CRL_BAG); + if(cert != NULL) { + + /* copy the nickname */ + rv = SECITEM_CopyItem(poolp, &cert->nickname, nickname); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + cert = NULL; + } else { + + /* package the certificate and cert chain into a NULL signer + * PKCS 7 SignedData content Info and prepare it for encoding + * since we cannot use DER_ANY_TEMPLATE + */ + cinfo = SEC_PKCS7CreateCertsOnly(add_cert, PR_TRUE, NULL); + rv = SEC_PKCS7PrepareForEncode(cinfo, NULL, NULL, NULL); + + /* thumbprint the certificate */ + if((cinfo != NULL) && (rv == SECSuccess)) + { + PORT_Memcpy(&cert->value.x509->certOrCRL, cinfo, sizeof(*cinfo)); + t_di = sec_pkcs12_compute_thumbprint(&add_cert->derCert); + if(t_di != NULL) + { + /* test */ + rv = SGN_CopyDigestInfo(poolp, &cert->value.x509->thumbprint, + t_di); + if(rv != SECSuccess) { + cert = NULL; + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + SGN_DestroyDigestInfo(t_di); + } + else + cert = NULL; + } + } + } + + if (cert == NULL) { + PORT_ArenaRelease(poolp, mark); + } else { + PORT_ArenaUnmark(poolp, mark); + } + + return cert; +} + +/* package the private key associated with the certificate and + * return the appropriate PKCS 12 structure + * poolp common memory pool + * nickname key nickname + * cert -- cert to look up + * wincx -- window handle + * an error is indicated by a return of NULL + */ +static SEC_PKCS12PrivateKey * +sec_pkcs12_get_private_key(PRArenaPool *poolp, + SECItem *nickname, + CERTCertificate *cert, + void *wincx) +{ + SECKEYPrivateKeyInfo *pki; + SEC_PKCS12PrivateKey *pk; + SECStatus rv; + void *mark; + + if((poolp == NULL) || (nickname == NULL)) { + return NULL; + } + + mark = PORT_ArenaMark(poolp); + + /* retrieve key from the data base */ + pki = PK11_ExportPrivateKeyInfo(nickname, cert, wincx); + if(pki == NULL) { + PORT_ArenaRelease(poolp, mark); + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + return NULL; + } + + pk = (SEC_PKCS12PrivateKey *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12PrivateKey)); + if(pk != NULL) { + rv = sec_pkcs12_init_pvk_data(poolp, &pk->pvkData); + + if(rv == SECSuccess) { + /* copy the key into poolp memory space */ + rv = SECKEY_CopyPrivateKeyInfo(poolp, &pk->pkcs8data, pki); + if(rv == SECSuccess) { + rv = SECITEM_CopyItem(poolp, &pk->pvkData.nickname, nickname); + } + } + + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + pk = NULL; + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + /* destroy private key, zeroing out data */ + SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); + if (pk == NULL) { + PORT_ArenaRelease(poolp, mark); + } else { + PORT_ArenaUnmark(poolp, mark); + } + + return pk; +} + +/* get a shrouded key item associated with a certificate + * return the appropriate PKCS 12 structure + * poolp common memory pool + * nickname key nickname + * cert -- cert to look up + * wincx -- window handle + * an error is indicated by a return of NULL + */ +static SEC_PKCS12ESPVKItem * +sec_pkcs12_get_shrouded_key(PRArenaPool *poolp, + SECItem *nickname, + CERTCertificate *cert, + SECOidTag algorithm, + SECItem *pwitem, + PKCS12UnicodeConvertFunction unicodeFn, + void *wincx) +{ + SECKEYEncryptedPrivateKeyInfo *epki; + SEC_PKCS12ESPVKItem *pk; + void *mark; + SECStatus rv; + PK11SlotInfo *slot = NULL; + PRBool swapUnicodeBytes = PR_FALSE; + +#ifdef IS_LITTLE_ENDIAN + swapUnicodeBytes = PR_TRUE; +#endif + + if((poolp == NULL) || (nickname == NULL)) + return NULL; + + mark = PORT_ArenaMark(poolp); + + /* use internal key slot */ + slot = PK11_GetInternalKeySlot(); + + /* retrieve encrypted prviate key */ + epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, pwitem, + nickname, cert, 1, 0, NULL); + PK11_FreeSlot(slot); + if(epki == NULL) { + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + + /* create a private key and store the data into the poolp memory space */ + pk = sec_pkcs12_create_espvk(poolp, SEC_OID_PKCS12_PKCS8_KEY_SHROUDING); + if(pk != NULL) { + rv = sec_pkcs12_init_pvk_data(poolp, &pk->espvkData); + rv = SECITEM_CopyItem(poolp, &pk->espvkData.nickname, nickname); + pk->espvkCipherText.pkcs8KeyShroud = + (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(poolp, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if((pk->espvkCipherText.pkcs8KeyShroud != NULL) && (rv == SECSuccess)) { + rv = SECKEY_CopyEncryptedPrivateKeyInfo(poolp, + pk->espvkCipherText.pkcs8KeyShroud, epki); + if(rv == SECSuccess) { + rv = (*unicodeFn)(poolp, &pk->espvkData.uniNickName, nickname, + PR_TRUE, swapUnicodeBytes); + } + } + + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + pk = NULL; + } + } + + SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); + if(pk == NULL) { + PORT_ArenaRelease(poolp, mark); + } else { + PORT_ArenaUnmark(poolp, mark); + } + + return pk; +} + +/* add a thumbprint to a private key associated certs list + * pvk is the area where the list is stored + * thumb is the thumbprint to copy + * a return of SECFailure indicates an error + */ +static SECStatus +sec_pkcs12_add_thumbprint(SEC_PKCS12PVKSupportingData *pvk, + SGNDigestInfo *thumb) +{ + SGNDigestInfo **thumb_list = NULL; + int nthumbs, size; + void *mark, *dummy; + SECStatus rv = SECFailure; + + if((pvk == NULL) || (thumb == NULL)) { + return SECFailure; + } + + mark = PORT_ArenaMark(pvk->poolp); + + thumb_list = pvk->assocCerts; + nthumbs = pvk->nThumbs; + + /* allocate list space needed -- either growing or allocating + * list must be NULL terminated + */ + size = sizeof(SGNDigestInfo *); + dummy = PORT_ArenaGrow(pvk->poolp, thumb_list, (size * (nthumbs + 1)), + (size * (nthumbs + 2))); + thumb_list = dummy; + if(dummy != NULL) { + thumb_list[nthumbs] = (SGNDigestInfo *)PORT_ArenaZAlloc(pvk->poolp, + sizeof(SGNDigestInfo)); + if(thumb_list[nthumbs] != NULL) { + SGN_CopyDigestInfo(pvk->poolp, thumb_list[nthumbs], thumb); + nthumbs += 1; + thumb_list[nthumbs] = 0; + } else { + dummy = NULL; + } + } + + if(dummy == NULL) { + PORT_ArenaRelease(pvk->poolp, mark); + return SECFailure; + } + + pvk->assocCerts = thumb_list; + pvk->nThumbs = nthumbs; + + PORT_ArenaUnmark(pvk->poolp, mark); + return SECSuccess; +} + +/* search the list of shrouded keys in the baggage for the desired + * name. return a pointer to the item. a return of NULL indicates + * that no match was present or that an error occurred. + */ +static SEC_PKCS12ESPVKItem * +sec_pkcs12_get_espvk_by_name(SEC_PKCS12Baggage *luggage, + SECItem *name) +{ + PRBool found = PR_FALSE; + SEC_PKCS12ESPVKItem *espvk = NULL; + int i, j; + SECComparison rv = SECEqual; + SECItem *t_name; + SEC_PKCS12BaggageItem *bag; + + if((luggage == NULL) || (name == NULL)) { + return NULL; + } + + i = 0; + while((found == PR_FALSE) && (i < luggage->luggage_size)) { + j = 0; + bag = luggage->bags[i]; + while((found == PR_FALSE) && (j < bag->nEspvks)) { + espvk = bag->espvks[j]; + if(espvk->poolp == NULL) { + espvk->poolp = luggage->poolp; + } + t_name = SECITEM_DupItem(&espvk->espvkData.nickname); + if(t_name != NULL) { + rv = SECITEM_CompareItem(name, t_name); + if(rv == SECEqual) { + found = PR_TRUE; + } + SECITEM_FreeItem(t_name, PR_TRUE); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + j++; + } + i++; + } + + if(found != PR_TRUE) { + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME); + return NULL; + } + + return espvk; +} + +/* locates a certificate and copies the thumbprint to the + * appropriate private key + */ +static SECStatus +sec_pkcs12_propagate_thumbprints(SECItem **nicknames, + CERTCertificate **ref_certs, + SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + SEC_PKCS12CertAndCRL *cert; + SEC_PKCS12PrivateKey *key; + SEC_PKCS12ESPVKItem *espvk; + int i; + PRBool error = PR_FALSE; + SECStatus rv = SECFailure; + + if((nicknames == NULL) || (safe == NULL)) { + return SECFailure; + } + + i = 0; + while((nicknames[i] != NULL) && (error == PR_FALSE)) { + /* process all certs */ + cert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage, + SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID, + nicknames[i], NULL); + if(cert != NULL) { + /* locate key and copy thumbprint */ + key = (SEC_PKCS12PrivateKey *)sec_pkcs12_find_object(safe, baggage, + SEC_OID_PKCS12_KEY_BAG_ID, + nicknames[i], NULL); + if(key != NULL) { + key->pvkData.poolp = key->poolp; + rv = sec_pkcs12_add_thumbprint(&key->pvkData, + &cert->value.x509->thumbprint); + if(rv == SECFailure) + error = PR_TRUE; /* XXX Set error? */ + } + + /* look in the baggage as well...*/ + if((baggage != NULL) && (error == PR_FALSE)) { + espvk = sec_pkcs12_get_espvk_by_name(baggage, nicknames[i]); + if(espvk != NULL) { + espvk->espvkData.poolp = espvk->poolp; + rv = sec_pkcs12_add_thumbprint(&espvk->espvkData, + &cert->value.x509->thumbprint); + if(rv == SECFailure) + error = PR_TRUE; /* XXX Set error? */ + } + } + } + i++; + } + + if(error == PR_TRUE) { + return SECFailure; + } + + return SECSuccess; +} + +/* append a safe bag to the end of the safe contents list */ +SECStatus +sec_pkcs12_append_safe_bag(SEC_PKCS12SafeContents *safe, + SEC_PKCS12SafeBag *bag) +{ + int size; + void *mark = NULL, *dummy = NULL; + + if((bag == NULL) || (safe == NULL)) + return SECFailure; + + mark = PORT_ArenaMark(safe->poolp); + + size = (safe->safe_size * sizeof(SEC_PKCS12SafeBag *)); + + if(safe->safe_size > 0) { + dummy = (SEC_PKCS12SafeBag **)PORT_ArenaGrow(safe->poolp, + safe->contents, + size, + (size + sizeof(SEC_PKCS12SafeBag *))); + safe->contents = dummy; + } else { + safe->contents = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc(safe->poolp, + (2 * sizeof(SEC_PKCS12SafeBag *))); + dummy = safe->contents; + } + + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + safe->contents[safe->safe_size] = bag; + safe->safe_size++; + safe->contents[safe->safe_size] = NULL; + + PORT_ArenaUnmark(safe->poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(safe->poolp, mark); + return SECFailure; +} + +/* append a certificate onto the end of a cert bag */ +static SECStatus +sec_pkcs12_append_cert_to_bag(PRArenaPool *arena, + SEC_PKCS12SafeBag *safebag, + CERTCertificate *cert, + SECItem *nickname) +{ + int size; + void *dummy = NULL, *mark = NULL; + SEC_PKCS12CertAndCRL *p12cert; + SEC_PKCS12CertAndCRLBag *bag; + + if((arena == NULL) || (safebag == NULL) || + (cert == NULL) || (nickname == NULL)) { + return SECFailure; + } + + bag = safebag->safeContent.certAndCRLBag; + if(bag == NULL) { + return SECFailure; + } + + mark = PORT_ArenaMark(arena); + + p12cert = sec_pkcs12_get_cert(arena, cert, nickname); + if(p12cert == NULL) { + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; + } + + size = bag->bag_size * sizeof(SEC_PKCS12CertAndCRL *); + if(bag->bag_size > 0) { + dummy = (SEC_PKCS12CertAndCRL **)PORT_ArenaGrow(bag->poolp, + bag->certAndCRLs, size, size + sizeof(SEC_PKCS12CertAndCRL *)); + bag->certAndCRLs = dummy; + } else { + bag->certAndCRLs = (SEC_PKCS12CertAndCRL **)PORT_ArenaZAlloc(bag->poolp, + (2 * sizeof(SEC_PKCS12CertAndCRL *))); + dummy = bag->certAndCRLs; + } + + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + bag->certAndCRLs[bag->bag_size] = p12cert; + bag->bag_size++; + bag->certAndCRLs[bag->bag_size] = NULL; + + PORT_ArenaUnmark(bag->poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; +} + +/* append a key onto the end of a list of keys in a key bag */ +SECStatus +sec_pkcs12_append_key_to_bag(SEC_PKCS12SafeBag *safebag, + SEC_PKCS12PrivateKey *pk) +{ + void *mark, *dummy; + SEC_PKCS12PrivateKeyBag *bag; + int size; + + if((safebag == NULL) || (pk == NULL)) + return SECFailure; + + bag = safebag->safeContent.keyBag; + if(bag == NULL) { + return SECFailure; + } + + mark = PORT_ArenaMark(bag->poolp); + + size = (bag->bag_size * sizeof(SEC_PKCS12PrivateKey *)); + + if(bag->bag_size > 0) { + dummy = (SEC_PKCS12PrivateKey **)PORT_ArenaGrow(bag->poolp, + bag->privateKeys, + size, + size + sizeof(SEC_PKCS12PrivateKey *)); + bag->privateKeys = dummy; + } else { + bag->privateKeys = (SEC_PKCS12PrivateKey **)PORT_ArenaZAlloc(bag->poolp, + (2 * sizeof(SEC_PKCS12PrivateKey *))); + dummy = bag->privateKeys; + } + + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + bag->privateKeys[bag->bag_size] = pk; + bag->bag_size++; + bag->privateKeys[bag->bag_size] = NULL; + + PORT_ArenaUnmark(bag->poolp, mark); + return SECSuccess; + +loser: + /* XXX Free memory? */ + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; +} + +/* append a safe bag to the baggage area */ +static SECStatus +sec_pkcs12_append_unshrouded_bag(SEC_PKCS12BaggageItem *bag, + SEC_PKCS12SafeBag *u_bag) +{ + int size; + void *mark = NULL, *dummy = NULL; + + if((bag == NULL) || (u_bag == NULL)) + return SECFailure; + + mark = PORT_ArenaMark(bag->poolp); + + /* dump things into the first bag */ + size = (bag->nSecrets + 1) * sizeof(SEC_PKCS12SafeBag *); + dummy = PORT_ArenaGrow(bag->poolp, + bag->unencSecrets, size, + size + sizeof(SEC_PKCS12SafeBag *)); + bag->unencSecrets = dummy; + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + bag->unencSecrets[bag->nSecrets] = u_bag; + bag->nSecrets++; + bag->unencSecrets[bag->nSecrets] = NULL; + + PORT_ArenaUnmark(bag->poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; +} + +/* gather up all certificates and keys and package them up + * in the safe, baggage, or both. + * nicknames is the list of nicknames and corresponding certs in ref_certs + * ref_certs a null terminated list of certificates + * rSafe, rBaggage -- return areas for safe and baggage + * shroud_keys -- store keys externally + * pwitem -- password for computing integrity mac and encrypting contents + * wincx -- window handle + * + * if a failure occurs, an error is set and SECFailure returned. + */ +static SECStatus +sec_pkcs12_package_certs_and_keys(SECItem **nicknames, + CERTCertificate **ref_certs, + PRBool unencryptedCerts, + SEC_PKCS12SafeContents **rSafe, + SEC_PKCS12Baggage **rBaggage, + PRBool shroud_keys, + SECOidTag shroud_alg, + SECItem *pwitem, + PKCS12UnicodeConvertFunction unicodeFn, + void *wincx) +{ + PRArenaPool *permArena; + SEC_PKCS12SafeContents *safe = NULL; + SEC_PKCS12Baggage *baggage = NULL; + + SECStatus rv = SECFailure; + PRBool problem = PR_FALSE; + + SEC_PKCS12ESPVKItem *espvk = NULL; + SEC_PKCS12PrivateKey *pk = NULL; + CERTCertificate *add_cert = NULL; + SEC_PKCS12SafeBag *certbag = NULL, *keybag = NULL; + SEC_PKCS12BaggageItem *external_bag = NULL; + int ncerts = 0, nkeys = 0; + int i; + + if((nicknames == NULL) || (rSafe == NULL) || (rBaggage == NULL)) { + return SECFailure; + } + + *rBaggage = baggage; + *rSafe = safe; + + permArena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(permArena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + /* allocate structures */ + safe = sec_pkcs12_create_safe_contents(permArena); + if(safe == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + + certbag = sec_pkcs12_create_safe_bag(permArena, + SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID); + if(certbag == NULL) { + rv = SECFailure; + goto loser; + } + + if(shroud_keys != PR_TRUE) { + keybag = sec_pkcs12_create_safe_bag(permArena, + SEC_OID_PKCS12_KEY_BAG_ID); + if(keybag == NULL) { + rv = SECFailure; + goto loser; + } + } + + if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) { + baggage = sec_pkcs12_create_baggage(permArena); + if(baggage == NULL) { + rv = SECFailure; + goto loser; + } + external_bag = sec_pkcs12_create_external_bag(baggage); + } + + /* package keys and certs */ + i = 0; + while((nicknames[i] != NULL) && (problem == PR_FALSE)) { + if(ref_certs[i] != NULL) { + /* append cert to bag o certs */ + rv = sec_pkcs12_append_cert_to_bag(permArena, certbag, + ref_certs[i], + nicknames[i]); + if(rv == SECFailure) { + problem = PR_FALSE; + } else { + ncerts++; + } + + if(rv == SECSuccess) { + /* package up them keys */ + if(shroud_keys == PR_TRUE) { + espvk = sec_pkcs12_get_shrouded_key(permArena, + nicknames[i], + ref_certs[i], + shroud_alg, + pwitem, unicodeFn, + wincx); + if(espvk != NULL) { + rv = sec_pkcs12_append_shrouded_key(external_bag, espvk); + SECITEM_CopyItem(permArena, &espvk->derCert, + &ref_certs[i]->derCert); + } else { + rv = SECFailure; + } + } else { + pk = sec_pkcs12_get_private_key(permArena, nicknames[i], + ref_certs[i], wincx); + if(pk != NULL) { + rv = sec_pkcs12_append_key_to_bag(keybag, pk); + SECITEM_CopyItem(permArena, &espvk->derCert, + &ref_certs[i]->derCert); + } else { + rv = SECFailure; + } + } + + if(rv == SECFailure) { + problem = PR_TRUE; + } else { + nkeys++; + } + } + } else { + /* handle only keys here ? */ + problem = PR_TRUE; + } + i++; + } + + /* let success fall through */ +loser: + if(problem == PR_FALSE) { + /* if we have certs, we want to append the cert bag to the + * appropriate area + */ + if(ncerts > 0) { + if(unencryptedCerts != PR_TRUE) { + rv = sec_pkcs12_append_safe_bag(safe, certbag); + } else { + rv = sec_pkcs12_append_unshrouded_bag(external_bag, certbag); + } + } else { + rv = SECSuccess; + } + + /* append key bag, if they are stored in safe contents */ + if((rv == SECSuccess) && (shroud_keys == PR_FALSE) && (nkeys > 0)) { + rv = sec_pkcs12_append_safe_bag(safe, keybag); + } + } else { + rv = SECFailure; + } + + /* if baggage not used, NULLify it */ + if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) { + if(((unencryptedCerts == PR_TRUE) && (ncerts == 0)) && + ((shroud_keys == PR_TRUE) && (nkeys == 0))) + baggage = NULL; + } else { + baggage = NULL; + } + + if((problem == PR_TRUE) || (rv == SECFailure)) { + PORT_FreeArena(permArena, PR_TRUE); + rv = SECFailure; + baggage = NULL; + safe = NULL; + } + + *rBaggage = baggage; + *rSafe = safe; + + return rv; +} + +/* DER encode the safe contents and return a SECItem. if an error + * occurs, NULL is returned. + */ +static SECItem * +sec_pkcs12_encode_safe_contents(SEC_PKCS12SafeContents *safe) +{ + SECItem *dsafe = NULL, *tsafe; + void *dummy = NULL; + PRArenaPool *arena; + + if(safe == NULL) { + return NULL; + } + +/* rv = sec_pkcs12_prepare_for_der_code_safe(safe, PR_TRUE); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + }*/ + + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + tsafe = (SECItem *)PORT_ArenaZAlloc(arena, sizeof(SECItem)); + if(tsafe != NULL) { + dummy = SEC_ASN1EncodeItem(arena, tsafe, safe, + SEC_PKCS12SafeContentsTemplate); + if(dummy != NULL) { + dsafe = SECITEM_DupItem(tsafe); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena(arena, PR_TRUE); + + return dsafe; +} + +/* prepare the authenicated safe for encoding and encode it. + * baggage is copied to the appropriate area, safe is encoded and + * encrypted. the version and transport mode are set on the asafe. + * the whole ball of wax is then der encoded and packaged up into + * data content info + * safe -- container of certs and keys, is encrypted. + * baggage -- container of certs and keys, keys assumed to be encrypted by + * another method, certs are in the clear + * algorithm -- algorithm by which to encrypt safe + * pwitem -- password for encryption + * wincx - window handle + * + * return of NULL is an error condition. + */ +static SEC_PKCS7ContentInfo * +sec_pkcs12_get_auth_safe(SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage, + SECOidTag algorithm, + SECItem *pwitem, + PKCS12UnicodeConvertFunction unicodeFn, + void *wincx) +{ + SECItem *src = NULL, *dest = NULL, *psalt = NULL; + PRArenaPool *poolp; + SEC_PKCS12AuthenticatedSafe *asafe; + SEC_PKCS7ContentInfo *safe_cinfo = NULL; + SEC_PKCS7ContentInfo *asafe_cinfo = NULL; + void *dummy; + SECStatus rv = SECSuccess; + PRBool swapUnicodeBytes = PR_FALSE; + +#ifdef IS_LITTLE_ENDIAN + swapUnicodeBytes = PR_TRUE; +#endif + + if(((safe != NULL) && (pwitem == NULL)) && (baggage == NULL)) + return NULL; + + poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(poolp == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* prepare authenticated safe for encode */ + asafe = sec_pkcs12_new_asafe(poolp); + if(asafe != NULL) { + + /* set version */ + dummy = SEC_ASN1EncodeInteger(asafe->poolp, &asafe->version, + SEC_PKCS12_PFX_VERSION); + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + + /* generate the privacy salt used to create virtual pwd */ + psalt = sec_pkcs12_generate_salt(); + if(psalt != NULL) { + rv = SECITEM_CopyItem(asafe->poolp, &asafe->privacySalt, + psalt); + if(rv == SECSuccess) { + asafe->privacySalt.len *= 8; + } + else { + SECITEM_ZfreeItem(psalt, PR_TRUE); + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + } + + if((psalt == NULL) || (rv == SECFailure)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + + /* package up safe contents */ + if(safe != NULL) + { + safe_cinfo = SEC_PKCS7CreateEncryptedData(algorithm, NULL, wincx); + if((safe_cinfo != NULL) && (safe->safe_size > 0)) { + /* encode the safe and encrypt the contents of the + * content info + */ + src = sec_pkcs12_encode_safe_contents(safe); + + if(src != NULL) { + rv = SEC_PKCS7SetContent(safe_cinfo, (char *)src->data, src->len); + SECITEM_ZfreeItem(src, PR_TRUE); + if(rv == SECSuccess) { + SECItem *vpwd; + vpwd = sec_pkcs12_create_virtual_password(pwitem, psalt, + unicodeFn, swapUnicodeBytes); + if(vpwd != NULL) { + rv = SEC_PKCS7EncryptContents(NULL, safe_cinfo, + vpwd, wincx); + SECITEM_ZfreeItem(vpwd, PR_TRUE); + } else { + rv = SECFailure; + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + rv = SECFailure; + } + } else if(safe->safe_size > 0) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } else { + /* case where there is NULL content in the safe contents */ + rv = SEC_PKCS7SetContent(safe_cinfo, NULL, 0); + if(rv != SECFailure) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } + + if(rv != SECSuccess) { + SEC_PKCS7DestroyContentInfo(safe_cinfo); + safe_cinfo = NULL; + goto loser; + } + + asafe->safe = safe_cinfo; + /* + PORT_Memcpy(&asafe->safe, safe_cinfo, sizeof(*safe_cinfo)); + */ + } + + /* copy the baggage to the authenticated safe baggage if present */ + if(baggage != NULL) { + PORT_Memcpy(&asafe->baggage, baggage, sizeof(*baggage)); + } + + /* encode authenticated safe and store it in a Data content info */ + dest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem)); + if(dest != NULL) { + dummy = SEC_ASN1EncodeItem(poolp, dest, asafe, + SEC_PKCS12AuthenticatedSafeTemplate); + if(dummy != NULL) { + asafe_cinfo = SEC_PKCS7CreateData(); + if(asafe_cinfo != NULL) { + rv = SEC_PKCS7SetContent(asafe_cinfo, + (char *)dest->data, + dest->len); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + SEC_PKCS7DestroyContentInfo(asafe_cinfo); + asafe_cinfo = NULL; + } + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + } + } + } + +loser: + PORT_FreeArena(poolp, PR_TRUE); + if(safe_cinfo != NULL) { + SEC_PKCS7DestroyContentInfo(safe_cinfo); + } + if(psalt != NULL) { + SECITEM_ZfreeItem(psalt, PR_TRUE); + } + + if(rv == SECFailure) { + return NULL; + } + + return asafe_cinfo; +} + +/* generates the PFX and computes the mac on the authenticated safe + * NULL implies an error + */ +static SEC_PKCS12PFXItem * +sec_pkcs12_get_pfx(SEC_PKCS7ContentInfo *cinfo, + PRBool do_mac, + SECItem *pwitem, PKCS12UnicodeConvertFunction unicodeFn) +{ + SECItem *dest = NULL, *mac = NULL, *salt = NULL, *key = NULL; + SEC_PKCS12PFXItem *pfx; + SECStatus rv = SECFailure; + SGNDigestInfo *di; + SECItem *vpwd; + PRBool swapUnicodeBytes = PR_FALSE; + +#ifdef IS_LITTLE_ENDIAN + swapUnicodeBytes = PR_TRUE; +#endif + + if((cinfo == NULL) || ((do_mac == PR_TRUE) && (pwitem == NULL))) { + return NULL; + } + + /* allocate new pfx structure */ + pfx = sec_pkcs12_new_pfx(); + if(pfx == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + PORT_Memcpy(&pfx->authSafe, cinfo, sizeof(*cinfo)); + if(do_mac == PR_TRUE) { + + /* salt for computing mac */ + salt = sec_pkcs12_generate_salt(); + if(salt != NULL) { + rv = SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, salt); + pfx->macData.macSalt.len *= 8; + + vpwd = sec_pkcs12_create_virtual_password(pwitem, salt, + unicodeFn, swapUnicodeBytes); + if(vpwd == NULL) { + rv = SECFailure; + key = NULL; + } else { + key = sec_pkcs12_generate_key_from_password(SEC_OID_SHA1, + salt, vpwd); + SECITEM_ZfreeItem(vpwd, PR_TRUE); + } + + if((key != NULL) && (rv == SECSuccess)) { + dest = SEC_PKCS7GetContent(cinfo); + if(dest != NULL) { + + /* compute mac on data -- for password integrity mode */ + mac = sec_pkcs12_generate_mac(key, dest, PR_FALSE); + if(mac != NULL) { + di = SGN_CreateDigestInfo(SEC_OID_SHA1, + mac->data, mac->len); + if(di != NULL) { + rv = SGN_CopyDigestInfo(pfx->poolp, + &pfx->macData.safeMac, di); + SGN_DestroyDigestInfo(di); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + SECITEM_ZfreeItem(mac, PR_TRUE); + } + } else { + rv = SECFailure; + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + } + + if(key != NULL) { + SECITEM_ZfreeItem(key, PR_TRUE); + } + SECITEM_ZfreeItem(salt, PR_TRUE); + } + } + + if(rv == SECFailure) { + SEC_PKCS12DestroyPFX(pfx); + pfx = NULL; + } + + return pfx; +} + +/* der encode the pfx */ +static SECItem * +sec_pkcs12_encode_pfx(SEC_PKCS12PFXItem *pfx) +{ + SECItem *dest; + void *dummy; + + if(pfx == NULL) { + return NULL; + } + + dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(dest == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + dummy = SEC_ASN1EncodeItem(NULL, dest, pfx, SEC_PKCS12PFXItemTemplate); + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + SECITEM_ZfreeItem(dest, PR_TRUE); + dest = NULL; + } + + return dest; +} + +SECItem * +SEC_PKCS12GetPFX(char **nicknames, + CERTCertificate **ref_certs, + PRBool shroud_keys, + SEC_PKCS5GetPBEPassword pbef, + void *pbearg, + PKCS12UnicodeConvertFunction unicodeFn, + void *wincx) +{ + SECItem **nicks = NULL; + SEC_PKCS12PFXItem *pfx = NULL; + SEC_PKCS12Baggage *baggage = NULL; + SEC_PKCS12SafeContents *safe = NULL; + SEC_PKCS7ContentInfo *cinfo = NULL; + SECStatus rv = SECFailure; + SECItem *dest = NULL, *pwitem = NULL; + PRBool problem = PR_FALSE; + PRBool unencryptedCerts; + SECOidTag shroud_alg, safe_alg; + + /* how should we encrypt certs ? */ + unencryptedCerts = !SEC_PKCS12IsEncryptionAllowed(); + if(!unencryptedCerts) { + safe_alg = SEC_PKCS12GetPreferredEncryptionAlgorithm(); + if(safe_alg == SEC_OID_UNKNOWN) { + safe_alg = SEC_PKCS12GetStrongestAllowedAlgorithm(); + } + if(safe_alg == SEC_OID_UNKNOWN) { + unencryptedCerts = PR_TRUE; + /* for export where no encryption is allowed, we still need + * to encrypt the NULL contents per the spec. encrypted info + * is known plaintext, so it shouldn't be a problem. + */ + safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; + } + } else { + /* for export where no encryption is allowed, we still need + * to encrypt the NULL contents per the spec. encrypted info + * is known plaintext, so it shouldn't be a problem. + */ + safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; + } + + /* keys are always stored with triple DES */ + shroud_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC; + + /* check for FIPS, if so, do not encrypt certs */ + if(PK11_IsFIPS() && !unencryptedCerts) { + unencryptedCerts = PR_TRUE; + } + + if((nicknames == NULL) || (pbef == NULL) || (ref_certs == NULL)) { + problem = PR_TRUE; + goto loser; + } + + + /* get password */ + pwitem = (*pbef)(pbearg); + if(pwitem == NULL) { + problem = PR_TRUE; + goto loser; + } + nicks = sec_pkcs12_convert_nickname_list(nicknames); + + /* get safe and baggage */ + rv = sec_pkcs12_package_certs_and_keys(nicks, ref_certs, unencryptedCerts, + &safe, &baggage, shroud_keys, + shroud_alg, pwitem, unicodeFn, wincx); + if(rv == SECFailure) { + problem = PR_TRUE; + } + + if((safe != NULL) && (problem == PR_FALSE)) { + /* copy thumbprints */ + rv = sec_pkcs12_propagate_thumbprints(nicks, ref_certs, safe, baggage); + + /* package everything up into AuthenticatedSafe */ + cinfo = sec_pkcs12_get_auth_safe(safe, baggage, + safe_alg, pwitem, unicodeFn, wincx); + + sec_pkcs12_destroy_cert_content_infos(safe, baggage); + + /* get the pfx and mac it */ + if(cinfo != NULL) { + pfx = sec_pkcs12_get_pfx(cinfo, PR_TRUE, pwitem, unicodeFn); + if(pfx != NULL) { + dest = sec_pkcs12_encode_pfx(pfx); + SEC_PKCS12DestroyPFX(pfx); + } + SEC_PKCS7DestroyContentInfo(cinfo); + } + + if(safe != NULL) { + PORT_FreeArena(safe->poolp, PR_TRUE); + } + } else { + if(safe != NULL) { + PORT_FreeArena(safe->poolp, PR_TRUE); + } + } + +loser: + if(nicks != NULL) { + sec_pkcs12_destroy_nickname_list(nicks); + } + + if(ref_certs != NULL) { + sec_pkcs12_destroy_certificate_list(ref_certs); + } + + if(pwitem != NULL) { + SECITEM_ZfreeItem(pwitem, PR_TRUE); + } + + if(problem == PR_TRUE) { + dest = NULL; + } + + return dest; +} diff --git a/security/nss/lib/pkcs12/p12local.c b/security/nss/lib/pkcs12/p12local.c new file mode 100644 index 000000000..ec7e4a76a --- /dev/null +++ b/security/nss/lib/pkcs12/p12local.c @@ -0,0 +1,1363 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "nssrenam.h" +#include "pkcs12.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "seccomon.h" +#include "secoid.h" +#include "sechash.h" +#include "secitem.h" +#include "secerr.h" +#include "pk11func.h" +#include "p12local.h" +#include "alghmac.h" +#include "p12.h" + +#define SALT_LENGTH 16 + +SEC_ASN1_MKSUB(SECKEY_PrivateKeyInfoTemplate) +SEC_ASN1_MKSUB(sgn_DigestInfoTemplate) + +CK_MECHANISM_TYPE +sec_pkcs12_algtag_to_mech(SECOidTag algtag) +{ + switch (algtag) { + case SEC_OID_MD2: + return CKM_MD2_HMAC; + case SEC_OID_MD5: + return CKM_MD5_HMAC; + case SEC_OID_SHA1: + return CKM_SHA_1_HMAC; + default: + break; + } + return CKM_INVALID_MECHANISM; +} + +/* helper functions */ +/* returns proper bag type template based upon object type tag */ +const SEC_ASN1Template * +sec_pkcs12_choose_bag_type_old(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12SafeBag *safebag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + safebag = (SEC_PKCS12SafeBag*)src_or_dest; + + oiddata = safebag->safeBagTypeTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&safebag->safeBagType); + safebag->safeBagTypeTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate); + break; + case SEC_OID_PKCS12_KEY_BAG_ID: + theTemplate = SEC_PointerToPKCS12KeyBagTemplate; + break; + case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID: + theTemplate = SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD; + break; + case SEC_OID_PKCS12_SECRET_BAG_ID: + theTemplate = SEC_PointerToPKCS12SecretBagTemplate; + break; + } + return theTemplate; +} + +const SEC_ASN1Template * +sec_pkcs12_choose_bag_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12SafeBag *safebag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + safebag = (SEC_PKCS12SafeBag*)src_or_dest; + + oiddata = safebag->safeBagTypeTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&safebag->safeBagType); + safebag->safeBagTypeTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_ASN1_GET(SEC_AnyTemplate); + break; + case SEC_OID_PKCS12_KEY_BAG_ID: + theTemplate = SEC_PKCS12PrivateKeyBagTemplate; + break; + case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID: + theTemplate = SEC_PKCS12CertAndCRLBagTemplate; + break; + case SEC_OID_PKCS12_SECRET_BAG_ID: + theTemplate = SEC_PKCS12SecretBagTemplate; + break; + } + return theTemplate; +} + +/* returns proper cert crl template based upon type tag */ +const SEC_ASN1Template * +sec_pkcs12_choose_cert_crl_type_old(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12CertAndCRL *certbag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + certbag = (SEC_PKCS12CertAndCRL*)src_or_dest; + oiddata = certbag->BagTypeTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&certbag->BagID); + certbag->BagTypeTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate); + break; + case SEC_OID_PKCS12_X509_CERT_CRL_BAG: + theTemplate = SEC_PointerToPKCS12X509CertCRLTemplate_OLD; + break; + case SEC_OID_PKCS12_SDSI_CERT_BAG: + theTemplate = SEC_PointerToPKCS12SDSICertTemplate; + break; + } + return theTemplate; +} + +const SEC_ASN1Template * +sec_pkcs12_choose_cert_crl_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12CertAndCRL *certbag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + certbag = (SEC_PKCS12CertAndCRL*)src_or_dest; + oiddata = certbag->BagTypeTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&certbag->BagID); + certbag->BagTypeTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate); + break; + case SEC_OID_PKCS12_X509_CERT_CRL_BAG: + theTemplate = SEC_PointerToPKCS12X509CertCRLTemplate; + break; + case SEC_OID_PKCS12_SDSI_CERT_BAG: + theTemplate = SEC_PointerToPKCS12SDSICertTemplate; + break; + } + return theTemplate; +} + +/* returns appropriate shroud template based on object type tag */ +const SEC_ASN1Template * +sec_pkcs12_choose_shroud_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12ESPVKItem *espvk; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + espvk = (SEC_PKCS12ESPVKItem*)src_or_dest; + oiddata = espvk->espvkTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&espvk->espvkOID); + espvk->espvkTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate); + break; + case SEC_OID_PKCS12_PKCS8_KEY_SHROUDING: + theTemplate = + SEC_ASN1_GET(SECKEY_PointerToEncryptedPrivateKeyInfoTemplate); + break; + } + return theTemplate; +} + +/* generate SALT placing it into the character array passed in. + * it is assumed that salt_dest is an array of appropriate size + * XXX We might want to generate our own random context + */ +SECItem * +sec_pkcs12_generate_salt(void) +{ + SECItem *salt; + + salt = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(salt == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + salt->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * + SALT_LENGTH); + salt->len = SALT_LENGTH; + if(salt->data == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + SECITEM_ZfreeItem(salt, PR_TRUE); + return NULL; + } + + PK11_GenerateRandom(salt->data, salt->len); + + return salt; +} + +/* generate KEYS -- as per PKCS12 section 7. + * only used for MAC + */ +SECItem * +sec_pkcs12_generate_key_from_password(SECOidTag algorithm, + SECItem *salt, + SECItem *password) +{ + unsigned char *pre_hash=NULL; + unsigned char *hash_dest=NULL; + SECStatus res; + PRArenaPool *poolp; + SECItem *key = NULL; + int key_len = 0; + + if((salt == NULL) || (password == NULL)) { + return NULL; + } + + poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if(poolp == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + pre_hash = (unsigned char *)PORT_ArenaZAlloc(poolp, sizeof(char) * + (salt->len+password->len)); + if(pre_hash == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + hash_dest = (unsigned char *)PORT_ArenaZAlloc(poolp, + sizeof(unsigned char) * SHA1_LENGTH); + if(hash_dest == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_Memcpy(pre_hash, salt->data, salt->len); + /* handle password of 0 length case */ + if(password->len > 0) { + PORT_Memcpy(&(pre_hash[salt->len]), password->data, password->len); + } + + res = PK11_HashBuf(SEC_OID_SHA1, hash_dest, pre_hash, + (salt->len+password->len)); + if(res == SECFailure) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + switch(algorithm) { + case SEC_OID_SHA1: + if(key_len == 0) + key_len = 16; + key = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(key == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + key->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) + * key_len); + if(key->data == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + key->len = key_len; + PORT_Memcpy(key->data, &hash_dest[SHA1_LENGTH-key->len], key->len); + break; + default: + goto loser; + break; + } + + PORT_FreeArena(poolp, PR_TRUE); + return key; + +loser: + PORT_FreeArena(poolp, PR_TRUE); + if(key != NULL) { + SECITEM_ZfreeItem(key, PR_TRUE); + } + return NULL; +} + +/* MAC is generated per PKCS 12 section 6. It is expected that key, msg + * and mac_dest are pre allocated, non-NULL arrays. msg_len is passed in + * because it is not known how long the message actually is. String + * manipulation routines will not necessarily work because msg may have + * imbedded NULLs + */ +static SECItem * +sec_pkcs12_generate_old_mac(SECItem *key, + SECItem *msg) +{ + SECStatus res; + PRArenaPool *temparena = NULL; + unsigned char *hash_dest=NULL, *hash_src1=NULL, *hash_src2 = NULL; + int i; + SECItem *mac = NULL; + + if((key == NULL) || (msg == NULL)) + goto loser; + + /* allocate return item */ + mac = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(mac == NULL) + return NULL; + mac->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) + * SHA1_LENGTH); + mac->len = SHA1_LENGTH; + if(mac->data == NULL) + goto loser; + + /* allocate temporary items */ + temparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if(temparena == NULL) + goto loser; + + hash_src1 = (unsigned char *)PORT_ArenaZAlloc(temparena, + sizeof(unsigned char) * (16+msg->len)); + if(hash_src1 == NULL) + goto loser; + + hash_src2 = (unsigned char *)PORT_ArenaZAlloc(temparena, + sizeof(unsigned char) * (SHA1_LENGTH+16)); + if(hash_src2 == NULL) + goto loser; + + hash_dest = (unsigned char *)PORT_ArenaZAlloc(temparena, + sizeof(unsigned char) * SHA1_LENGTH); + if(hash_dest == NULL) + goto loser; + + /* perform mac'ing as per PKCS 12 */ + + /* first round of hashing */ + for(i = 0; i < 16; i++) + hash_src1[i] = key->data[i] ^ 0x36; + PORT_Memcpy(&(hash_src1[16]), msg->data, msg->len); + res = PK11_HashBuf(SEC_OID_SHA1, hash_dest, hash_src1, (16+msg->len)); + if(res == SECFailure) + goto loser; + + /* second round of hashing */ + for(i = 0; i < 16; i++) + hash_src2[i] = key->data[i] ^ 0x5c; + PORT_Memcpy(&(hash_src2[16]), hash_dest, SHA1_LENGTH); + res = PK11_HashBuf(SEC_OID_SHA1, mac->data, hash_src2, SHA1_LENGTH+16); + if(res == SECFailure) + goto loser; + + PORT_FreeArena(temparena, PR_TRUE); + return mac; + +loser: + if(temparena != NULL) + PORT_FreeArena(temparena, PR_TRUE); + if(mac != NULL) + SECITEM_ZfreeItem(mac, PR_TRUE); + return NULL; +} + +/* MAC is generated per PKCS 12 section 6. It is expected that key, msg + * and mac_dest are pre allocated, non-NULL arrays. msg_len is passed in + * because it is not known how long the message actually is. String + * manipulation routines will not necessarily work because msg may have + * imbedded NULLs + */ +SECItem * +sec_pkcs12_generate_mac(SECItem *key, + SECItem *msg, + PRBool old_method) +{ + SECStatus res = SECFailure; + SECItem *mac = NULL; + PK11Context *pk11cx = NULL; + SECItem ignore = {0}; + + if((key == NULL) || (msg == NULL)) { + return NULL; + } + + if(old_method == PR_TRUE) { + return sec_pkcs12_generate_old_mac(key, msg); + } + + /* allocate return item */ + mac = SECITEM_AllocItem(NULL, NULL, SHA1_LENGTH); + if (mac == NULL) { + return NULL; + } + + pk11cx = PK11_CreateContextByRawKey(NULL, CKM_SHA_1_HMAC, PK11_OriginDerive, + CKA_SIGN, key, &ignore, NULL); + if (pk11cx == NULL) { + goto loser; + } + + res = PK11_DigestBegin(pk11cx); + if (res == SECFailure) { + goto loser; + } + + res = PK11_DigestOp(pk11cx, msg->data, msg->len); + if (res == SECFailure) { + goto loser; + } + + res = PK11_DigestFinal(pk11cx, mac->data, &mac->len, SHA1_LENGTH); + if (res == SECFailure) { + goto loser; + } + + PK11_DestroyContext(pk11cx, PR_TRUE); + pk11cx = NULL; + +loser: + + if(res != SECSuccess) { + SECITEM_ZfreeItem(mac, PR_TRUE); + mac = NULL; + if (pk11cx) { + PK11_DestroyContext(pk11cx, PR_TRUE); + } + } + + return mac; +} + +/* compute the thumbprint of the DER cert and create a digest info + * to store it in and return the digest info. + * a return of NULL indicates an error. + */ +SGNDigestInfo * +sec_pkcs12_compute_thumbprint(SECItem *der_cert) +{ + SGNDigestInfo *thumb = NULL; + SECItem digest; + PRArenaPool *temparena = NULL; + SECStatus rv = SECFailure; + + if(der_cert == NULL) + return NULL; + + temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(temparena == NULL) { + return NULL; + } + + digest.data = (unsigned char *)PORT_ArenaZAlloc(temparena, + sizeof(unsigned char) * + SHA1_LENGTH); + /* digest data and create digest info */ + if(digest.data != NULL) { + digest.len = SHA1_LENGTH; + rv = PK11_HashBuf(SEC_OID_SHA1, digest.data, der_cert->data, + der_cert->len); + if(rv == SECSuccess) { + thumb = SGN_CreateDigestInfo(SEC_OID_SHA1, + digest.data, + digest.len); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena(temparena, PR_TRUE); + + return thumb; +} + +/* create a virtual password per PKCS 12, the password is converted + * to unicode, the salt is prepended to it, and then the whole thing + * is returned */ +SECItem * +sec_pkcs12_create_virtual_password(SECItem *password, SECItem *salt, + PRBool swap) +{ + SECItem uniPwd = {siBuffer, NULL,0}, *retPwd = NULL; + + if((password == NULL) || (salt == NULL)) { + return NULL; + } + + if(password->len == 0) { + uniPwd.data = (unsigned char*)PORT_ZAlloc(2); + uniPwd.len = 2; + if(!uniPwd.data) { + return NULL; + } + } else { + uniPwd.data = (unsigned char*)PORT_ZAlloc(password->len * 3); + uniPwd.len = password->len * 3; + if(!PORT_UCS2_ASCIIConversion(PR_TRUE, password->data, password->len, + uniPwd.data, uniPwd.len, &uniPwd.len, swap)) { + SECITEM_ZfreeItem(&uniPwd, PR_FALSE); + return NULL; + } + } + + retPwd = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(retPwd == NULL) { + goto loser; + } + + /* allocate space and copy proper data */ + retPwd->len = uniPwd.len + salt->len; + retPwd->data = (unsigned char *)PORT_Alloc(retPwd->len); + if(retPwd->data == NULL) { + PORT_Free(retPwd); + goto loser; + } + + PORT_Memcpy(retPwd->data, salt->data, salt->len); + PORT_Memcpy((retPwd->data + salt->len), uniPwd.data, uniPwd.len); + + SECITEM_ZfreeItem(&uniPwd, PR_FALSE); + + return retPwd; + +loser: + PORT_SetError(SEC_ERROR_NO_MEMORY); + SECITEM_ZfreeItem(&uniPwd, PR_FALSE); + return NULL; +} + +/* appends a shrouded key to a key bag. this is used for exporting + * to store externally wrapped keys. it is used when importing to convert + * old items to new + */ +SECStatus +sec_pkcs12_append_shrouded_key(SEC_PKCS12BaggageItem *bag, + SEC_PKCS12ESPVKItem *espvk) +{ + int size; + void *mark = NULL, *dummy = NULL; + + if((bag == NULL) || (espvk == NULL)) + return SECFailure; + + mark = PORT_ArenaMark(bag->poolp); + + /* grow the list */ + size = (bag->nEspvks + 1) * sizeof(SEC_PKCS12ESPVKItem *); + dummy = (SEC_PKCS12ESPVKItem **)PORT_ArenaGrow(bag->poolp, + bag->espvks, size, + size + sizeof(SEC_PKCS12ESPVKItem *)); + bag->espvks = (SEC_PKCS12ESPVKItem**)dummy; + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + bag->espvks[bag->nEspvks] = espvk; + bag->nEspvks++; + bag->espvks[bag->nEspvks] = NULL; + + PORT_ArenaUnmark(bag->poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; +} + +/* search a certificate list for a nickname, a thumbprint, or both + * within a certificate bag. if the certificate could not be + * found or an error occurs, NULL is returned; + */ +static SEC_PKCS12CertAndCRL * +sec_pkcs12_find_cert_in_certbag(SEC_PKCS12CertAndCRLBag *certbag, + SECItem *nickname, SGNDigestInfo *thumbprint) +{ + PRBool search_both = PR_FALSE, search_nickname = PR_FALSE; + int i, j; + + if((certbag == NULL) || ((nickname == NULL) && (thumbprint == NULL))) { + return NULL; + } + + if(thumbprint && nickname) { + search_both = PR_TRUE; + } + + if(nickname) { + search_nickname = PR_TRUE; + } + +search_again: + i = 0; + while(certbag->certAndCRLs[i] != NULL) { + SEC_PKCS12CertAndCRL *cert = certbag->certAndCRLs[i]; + + if(SECOID_FindOIDTag(&cert->BagID) == SEC_OID_PKCS12_X509_CERT_CRL_BAG) { + + /* check nicknames */ + if(search_nickname) { + if(SECITEM_CompareItem(nickname, &cert->nickname) == SECEqual) { + return cert; + } + } else { + /* check thumbprints */ + SECItem **derCertList; + + /* get pointer to certificate list, does not need to + * be freed since it is within the arena which will + * be freed later. + */ + derCertList = SEC_PKCS7GetCertificateList(&cert->value.x509->certOrCRL); + j = 0; + if(derCertList != NULL) { + while(derCertList[j] != NULL) { + SECComparison eq; + SGNDigestInfo *di; + di = sec_pkcs12_compute_thumbprint(derCertList[j]); + if(di) { + eq = SGN_CompareDigestInfo(thumbprint, di); + SGN_DestroyDigestInfo(di); + if(eq == SECEqual) { + /* copy the derCert for later reference */ + cert->value.x509->derLeafCert = derCertList[j]; + return cert; + } + } else { + /* an error occurred */ + return NULL; + } + j++; + } + } + } + } + + i++; + } + + if(search_both) { + search_both = PR_FALSE; + search_nickname = PR_FALSE; + goto search_again; + } + + return NULL; +} + +/* search a key list for a nickname, a thumbprint, or both + * within a key bag. if the key could not be + * found or an error occurs, NULL is returned; + */ +static SEC_PKCS12PrivateKey * +sec_pkcs12_find_key_in_keybag(SEC_PKCS12PrivateKeyBag *keybag, + SECItem *nickname, SGNDigestInfo *thumbprint) +{ + PRBool search_both = PR_FALSE, search_nickname = PR_FALSE; + int i, j; + + if((keybag == NULL) || ((nickname == NULL) && (thumbprint == NULL))) { + return NULL; + } + + if(keybag->privateKeys == NULL) { + return NULL; + } + + if(thumbprint && nickname) { + search_both = PR_TRUE; + } + + if(nickname) { + search_nickname = PR_TRUE; + } + +search_again: + i = 0; + while(keybag->privateKeys[i] != NULL) { + SEC_PKCS12PrivateKey *key = keybag->privateKeys[i]; + + /* check nicknames */ + if(search_nickname) { + if(SECITEM_CompareItem(nickname, &key->pvkData.nickname) == SECEqual) { + return key; + } + } else { + /* check digests */ + SGNDigestInfo **assocCerts = key->pvkData.assocCerts; + if((assocCerts == NULL) || (assocCerts[0] == NULL)) { + return NULL; + } + + j = 0; + while(assocCerts[j] != NULL) { + SECComparison eq; + eq = SGN_CompareDigestInfo(thumbprint, assocCerts[j]); + if(eq == SECEqual) { + return key; + } + j++; + } + } + i++; + } + + if(search_both) { + search_both = PR_FALSE; + search_nickname = PR_FALSE; + goto search_again; + } + + return NULL; +} + +/* seach the safe first then try the baggage bag + * safe and bag contain certs and keys to search + * objType is the object type to look for + * bagType is the type of bag that was found by sec_pkcs12_find_object + * index is the entity in safe->safeContents or bag->unencSecrets which + * is being searched + * nickname and thumbprint are the search criteria + * + * a return of null indicates no match + */ +static void * +sec_pkcs12_try_find(SEC_PKCS12SafeContents *safe, + SEC_PKCS12BaggageItem *bag, + SECOidTag objType, SECOidTag bagType, int index, + SECItem *nickname, SGNDigestInfo *thumbprint) +{ + PRBool searchSafe; + int i = index; + + if((safe == NULL) && (bag == NULL)) { + return NULL; + } + + searchSafe = (safe == NULL ? PR_FALSE : PR_TRUE); + switch(objType) { + case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID: + if(objType == bagType) { + SEC_PKCS12CertAndCRLBag *certBag; + + if(searchSafe) { + certBag = safe->contents[i]->safeContent.certAndCRLBag; + } else { + certBag = bag->unencSecrets[i]->safeContent.certAndCRLBag; + } + return sec_pkcs12_find_cert_in_certbag(certBag, nickname, + thumbprint); + } + break; + case SEC_OID_PKCS12_KEY_BAG_ID: + if(objType == bagType) { + SEC_PKCS12PrivateKeyBag *keyBag; + + if(searchSafe) { + keyBag = safe->contents[i]->safeContent.keyBag; + } else { + keyBag = bag->unencSecrets[i]->safeContent.keyBag; + } + return sec_pkcs12_find_key_in_keybag(keyBag, nickname, + thumbprint); + } + break; + default: + break; + } + + return NULL; +} + +/* searches both the baggage and the safe areas looking for + * object of specified type matching either the nickname or the + * thumbprint specified. + * + * safe and baggage store certs and keys + * objType is the OID for the bag type to be searched: + * SEC_OID_PKCS12_KEY_BAG_ID, or + * SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID + * nickname and thumbprint are the search criteria + * + * if no match found, NULL returned and error set + */ +void * +sec_pkcs12_find_object(SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage, + SECOidTag objType, + SECItem *nickname, + SGNDigestInfo *thumbprint) +{ + int i, j; + void *retItem; + + if(((safe == NULL) && (thumbprint == NULL)) || + ((nickname == NULL) && (thumbprint == NULL))) { + return NULL; + } + + i = 0; + if((safe != NULL) && (safe->contents != NULL)) { + while(safe->contents[i] != NULL) { + SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType); + retItem = sec_pkcs12_try_find(safe, NULL, objType, bagType, i, + nickname, thumbprint); + if(retItem != NULL) { + return retItem; + } + i++; + } + } + + if((baggage != NULL) && (baggage->bags != NULL)) { + i = 0; + while(baggage->bags[i] != NULL) { + SEC_PKCS12BaggageItem *xbag = baggage->bags[i]; + j = 0; + if(xbag->unencSecrets != NULL) { + while(xbag->unencSecrets[j] != NULL) { + SECOidTag bagType; + bagType = SECOID_FindOIDTag(&xbag->unencSecrets[j]->safeBagType); + retItem = sec_pkcs12_try_find(NULL, xbag, objType, bagType, + j, nickname, thumbprint); + if(retItem != NULL) { + return retItem; + } + j++; + } + } + i++; + } + } + + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME); + return NULL; +} + +/* this function converts a password to unicode and encures that the + * required double 0 byte be placed at the end of the string + */ +PRBool +sec_pkcs12_convert_item_to_unicode(PRArenaPool *arena, SECItem *dest, + SECItem *src, PRBool zeroTerm, + PRBool asciiConvert, PRBool toUnicode) +{ + PRBool success = PR_FALSE; + if(!src || !dest) { + return PR_FALSE; + } + + dest->len = src->len * 3 + 2; + if(arena) { + dest->data = (unsigned char*)PORT_ArenaZAlloc(arena, dest->len); + } else { + dest->data = (unsigned char*)PORT_ZAlloc(dest->len); + } + + if(!dest->data) { + dest->len = 0; + return PR_FALSE; + } + + if(!asciiConvert) { + success = PORT_UCS2_UTF8Conversion(toUnicode, src->data, src->len, dest->data, + dest->len, &dest->len); + } else { +#ifndef IS_LITTLE_ENDIAN + PRBool swapUnicode = PR_FALSE; +#else + PRBool swapUnicode = PR_TRUE; +#endif + success = PORT_UCS2_ASCIIConversion(toUnicode, src->data, src->len, dest->data, + dest->len, &dest->len, swapUnicode); + } + + if(!success) { + if(!arena) { + PORT_Free(dest->data); + dest->data = NULL; + dest->len = 0; + } + return PR_FALSE; + } + + if((dest->data[dest->len-1] || dest->data[dest->len-2]) && zeroTerm) { + if(dest->len + 2 > 3 * src->len) { + if(arena) { + dest->data = (unsigned char*)PORT_ArenaGrow(arena, + dest->data, dest->len, + dest->len + 2); + } else { + dest->data = (unsigned char*)PORT_Realloc(dest->data, + dest->len + 2); + } + + if(!dest->data) { + return PR_FALSE; + } + } + dest->len += 2; + dest->data[dest->len-1] = dest->data[dest->len-2] = 0; + } + + return PR_TRUE; +} + +/* pkcs 12 templates */ +static const SEC_ASN1TemplateChooserPtr sec_pkcs12_shroud_chooser = + sec_pkcs12_choose_shroud_type; + +const SEC_ASN1Template SEC_PKCS12CodedSafeBagTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) }, + { SEC_ASN1_ANY, offsetof(SEC_PKCS12SafeBag, derSafeContent) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12CodedCertBagTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) }, + { SEC_ASN1_ANY, offsetof(SEC_PKCS12CertAndCRL, derValue) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12CodedCertAndCRLBagTemplate[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs), + SEC_PKCS12CodedCertBagTemplate }, +}; + +const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12ESPVKItem) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12ESPVKItem, espvkOID) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12ESPVKItem, espvkData), + SEC_PKCS12PVKSupportingDataTemplate_OLD }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_DYNAMIC | 0, offsetof(SEC_PKCS12ESPVKItem, espvkCipherText), + &sec_pkcs12_shroud_chooser }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12ESPVKItem) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12ESPVKItem, espvkOID) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12ESPVKItem, espvkData), + SEC_PKCS12PVKSupportingDataTemplate }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_DYNAMIC | 0, offsetof(SEC_PKCS12ESPVKItem, espvkCipherText), + &sec_pkcs12_shroud_chooser }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PVKAdditionalDataTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKAdditionalData) }, + { SEC_ASN1_OBJECT_ID, + offsetof(SEC_PKCS12PVKAdditionalData, pvkAdditionalType) }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12PVKAdditionalData, pvkAdditionalContent) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKSupportingData) }, + { SEC_ASN1_SET_OF | SEC_ASN1_XTRN , + offsetof(SEC_PKCS12PVKSupportingData, assocCerts), + SEC_ASN1_SUB(sgn_DigestInfoTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, + offsetof(SEC_PKCS12PVKSupportingData, regenerable) }, + { SEC_ASN1_PRINTABLE_STRING, + offsetof(SEC_PKCS12PVKSupportingData, nickname) }, + { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, + offsetof(SEC_PKCS12PVKSupportingData, pvkAdditionalDER) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKSupportingData) }, + { SEC_ASN1_SET_OF | SEC_ASN1_XTRN , + offsetof(SEC_PKCS12PVKSupportingData, assocCerts), + SEC_ASN1_SUB(sgn_DigestInfoTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, + offsetof(SEC_PKCS12PVKSupportingData, regenerable) }, + { SEC_ASN1_BMP_STRING, + offsetof(SEC_PKCS12PVKSupportingData, uniNickName) }, + { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, + offsetof(SEC_PKCS12PVKSupportingData, pvkAdditionalDER) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12BaggageItemTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12BaggageItem) }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, espvks), + SEC_PKCS12ESPVKItemTemplate }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, unencSecrets), + SEC_PKCS12SafeBagTemplate }, + /*{ SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, unencSecrets), + SEC_PKCS12CodedSafeBagTemplate }, */ + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12BaggageTemplate[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12Baggage, bags), + SEC_PKCS12BaggageItemTemplate }, +}; + +const SEC_ASN1Template SEC_PKCS12BaggageTemplate_OLD[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12Baggage_OLD, espvks), + SEC_PKCS12ESPVKItemTemplate_OLD }, +}; + +static const SEC_ASN1TemplateChooserPtr sec_pkcs12_bag_chooser = + sec_pkcs12_choose_bag_type; + +static const SEC_ASN1TemplateChooserPtr sec_pkcs12_bag_chooser_old = + sec_pkcs12_choose_bag_type_old; + +const SEC_ASN1Template SEC_PKCS12SafeBagTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12SafeBag, safeContent), + &sec_pkcs12_bag_chooser_old }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SafeBagTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_POINTER, + offsetof(SEC_PKCS12SafeBag, safeContent), + &sec_pkcs12_bag_chooser }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BMP_STRING, + offsetof(SEC_PKCS12SafeBag, uniSafeBagName) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate_OLD[] = +{ + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS12SafeContents, contents), + SEC_PKCS12SafeBagTemplate_OLD } +}; + +const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate[] = +{ + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS12SafeContents, contents), + SEC_PKCS12SafeBagTemplate } /* here */ +}; + +const SEC_ASN1Template SEC_PKCS12PrivateKeyTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PrivateKey) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12PrivateKey, pvkData), + SEC_PKCS12PVKSupportingDataTemplate }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(SEC_PKCS12PrivateKey, pkcs8data), + SEC_ASN1_SUB(SECKEY_PrivateKeyInfoTemplate) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PrivateKeyBagTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PrivateKeyBag) }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12PrivateKeyBag, privateKeys), + SEC_PKCS12PrivateKeyTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, certOrCRL), + sec_PKCS7ContentInfoTemplate }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN , + offsetof(SEC_PKCS12X509CertCRL, thumbprint), + SEC_ASN1_SUB(sgn_DigestInfoTemplate) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, certOrCRL), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SDSICertTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) }, + { SEC_ASN1_IA5_STRING, offsetof(SEC_PKCS12SDSICert, value) }, + { 0 } +}; + +static const SEC_ASN1TemplateChooserPtr sec_pkcs12_cert_crl_chooser_old = + sec_pkcs12_choose_cert_crl_type_old; + +static const SEC_ASN1TemplateChooserPtr sec_pkcs12_cert_crl_chooser = + sec_pkcs12_choose_cert_crl_type; + +const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) }, + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_EXPLICIT | + SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | 0, + offsetof(SEC_PKCS12CertAndCRL, value), + &sec_pkcs12_cert_crl_chooser_old }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12CertAndCRL, value), + &sec_pkcs12_cert_crl_chooser }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs), + SEC_PKCS12CertAndCRLTemplate }, +}; + +const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRLBag) }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs), + SEC_PKCS12CertAndCRLTemplate_OLD }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SecretAdditionalTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SecretAdditional) }, + { SEC_ASN1_OBJECT_ID, + offsetof(SEC_PKCS12SecretAdditional, secretAdditionalType) }, + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_EXPLICIT, + offsetof(SEC_PKCS12SecretAdditional, secretAdditionalContent) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SecretTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12Secret) }, + { SEC_ASN1_BMP_STRING, offsetof(SEC_PKCS12Secret, uniSecretName) }, + { SEC_ASN1_ANY, offsetof(SEC_PKCS12Secret, value) }, + { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL, + offsetof(SEC_PKCS12Secret, secretAdditional), + SEC_PKCS12SecretAdditionalTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SecretItemTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12Secret) }, + { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12SecretItem, secret), SEC_PKCS12SecretTemplate }, + { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS12SecretItem, subFolder), SEC_PKCS12SafeBagTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SecretBagTemplate[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12SecretBag, secrets), + SEC_PKCS12SecretItemTemplate }, +}; + +const SEC_ASN1Template SEC_PKCS12MacDataTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN , offsetof(SEC_PKCS12MacData, safeMac), + SEC_ASN1_SUB(sgn_DigestInfoTemplate) }, + { SEC_ASN1_BIT_STRING, offsetof(SEC_PKCS12MacData, macSalt) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PFXItemTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) }, + { SEC_ASN1_OPTIONAL | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12PFXItem, macData), SEC_PKCS12MacDataTemplate }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS12PFXItem, authSafe), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PFXItemTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) }, + { SEC_ASN1_OPTIONAL | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(SEC_PKCS12PFXItem, old_safeMac), + SEC_ASN1_SUB(sgn_DigestInfoTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BIT_STRING, + offsetof(SEC_PKCS12PFXItem, old_macSalt) }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS12PFXItem, authSafe), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12AuthenticatedSafe) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(SEC_PKCS12AuthenticatedSafe, version) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_OBJECT_ID, + offsetof(SEC_PKCS12AuthenticatedSafe, transportMode) }, + { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL, + offsetof(SEC_PKCS12AuthenticatedSafe, privacySalt) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SET_OF, + offsetof(SEC_PKCS12AuthenticatedSafe, baggage.bags), + SEC_PKCS12BaggageItemTemplate }, + { SEC_ASN1_POINTER, + offsetof(SEC_PKCS12AuthenticatedSafe, safe), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12AuthenticatedSafe) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(SEC_PKCS12AuthenticatedSafe, version) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(SEC_PKCS12AuthenticatedSafe, transportMode) }, + { SEC_ASN1_BIT_STRING, + offsetof(SEC_PKCS12AuthenticatedSafe, privacySalt) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12AuthenticatedSafe, old_baggage), + SEC_PKCS12BaggageTemplate_OLD }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS12AuthenticatedSafe, old_safe), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12KeyBagTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12PrivateKeyBagTemplate } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12CertAndCRLBagTemplate_OLD } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12CertAndCRLBagTemplate } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12SecretBagTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12SecretBagTemplate } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate_OLD[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12X509CertCRLTemplate_OLD } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12X509CertCRLTemplate } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12SDSICertTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12SDSICertTemplate } +}; + + diff --git a/security/nss/lib/pkcs12/p12local.h b/security/nss/lib/pkcs12/p12local.h new file mode 100644 index 000000000..e0333e2c3 --- /dev/null +++ b/security/nss/lib/pkcs12/p12local.h @@ -0,0 +1,88 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + + +#ifndef _P12LOCAL_H_ +#define _P12LOCAL_H_ + +#include "plarena.h" +#include "secoidt.h" +#include "secasn1.h" +#include "secder.h" +#include "certt.h" +#include "secpkcs7.h" +#include "pkcs12.h" +#include "p12.h" + +/* helper functions */ +extern const SEC_ASN1Template * +sec_pkcs12_choose_bag_type(void *src_or_dest, PRBool encoding); +extern const SEC_ASN1Template * +sec_pkcs12_choose_cert_crl_type(void *src_or_dest, PRBool encoding); +extern const SEC_ASN1Template * +sec_pkcs12_choose_shroud_type(void *src_or_dest, PRBool encoding); +extern SECItem *sec_pkcs12_generate_salt(void); +extern SECItem *sec_pkcs12_generate_key_from_password(SECOidTag algorithm, + SECItem *salt, SECItem *password); +extern SECItem *sec_pkcs12_generate_mac(SECItem *key, SECItem *msg, + PRBool old_method); +extern SGNDigestInfo *sec_pkcs12_compute_thumbprint(SECItem *der_cert); +extern SECItem *sec_pkcs12_create_virtual_password(SECItem *password, + SECItem *salt, PRBool swapUnicodeBytes); +extern SECStatus sec_pkcs12_append_shrouded_key(SEC_PKCS12BaggageItem *bag, + SEC_PKCS12ESPVKItem *espvk); +extern void *sec_pkcs12_find_object(SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage, SECOidTag objType, + SECItem *nickname, SGNDigestInfo *thumbprint); +extern PRBool sec_pkcs12_convert_item_to_unicode(PRArenaPool *arena, SECItem *dest, + SECItem *src, PRBool zeroTerm, + PRBool asciiConvert, PRBool toUnicode); +extern CK_MECHANISM_TYPE sec_pkcs12_algtag_to_mech(SECOidTag algtag); + +/* create functions */ +extern SEC_PKCS12PFXItem *sec_pkcs12_new_pfx(void); +extern SEC_PKCS12SafeContents *sec_pkcs12_create_safe_contents( + PRArenaPool *poolp); +extern SEC_PKCS12Baggage *sec_pkcs12_create_baggage(PRArenaPool *poolp); +extern SEC_PKCS12BaggageItem *sec_pkcs12_create_external_bag(SEC_PKCS12Baggage *luggage); +extern void SEC_PKCS12DestroyPFX(SEC_PKCS12PFXItem *pfx); +extern SEC_PKCS12AuthenticatedSafe *sec_pkcs12_new_asafe(PRArenaPool *poolp); + +/* conversion from old to new */ +extern SEC_PKCS12DecoderContext * +sec_PKCS12ConvertOldSafeToNew(PRArenaPool *arena, PK11SlotInfo *slot, + PRBool swapUnicode, SECItem *pwitem, + void *wincx, SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage); + +#endif diff --git a/security/nss/lib/pkcs12/p12plcy.c b/security/nss/lib/pkcs12/p12plcy.c new file mode 100644 index 000000000..a62be5fe7 --- /dev/null +++ b/security/nss/lib/pkcs12/p12plcy.c @@ -0,0 +1,198 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + + +#include "p12plcy.h" +#include "secoid.h" +#include "secport.h" +#include "secpkcs5.h" + +#define PKCS12_NULL 0x0000 + +typedef struct pkcs12SuiteMapStr { + SECOidTag algTag; + unsigned int keyLengthBits; /* in bits */ + unsigned long suite; + PRBool allowed; + PRBool preferred; +} pkcs12SuiteMap; + +static pkcs12SuiteMap pkcs12SuiteMaps[] = { + { SEC_OID_RC4, 40, PKCS12_RC4_40, PR_FALSE, PR_FALSE}, + { SEC_OID_RC4, 128, PKCS12_RC4_128, PR_FALSE, PR_FALSE}, + { SEC_OID_RC2_CBC, 40, PKCS12_RC2_CBC_40, PR_FALSE, PR_TRUE}, + { SEC_OID_RC2_CBC, 128, PKCS12_RC2_CBC_128, PR_FALSE, PR_FALSE}, + { SEC_OID_DES_CBC, 64, PKCS12_DES_56, PR_FALSE, PR_FALSE}, + { SEC_OID_DES_EDE3_CBC, 192, PKCS12_DES_EDE3_168, PR_FALSE, PR_FALSE}, + { SEC_OID_UNKNOWN, 0, PKCS12_NULL, PR_FALSE, PR_FALSE}, + { SEC_OID_UNKNOWN, 0, 0L, PR_FALSE, PR_FALSE} +}; + +/* determine if algid is an algorithm which is allowed */ +PRBool +SEC_PKCS12DecryptionAllowed(SECAlgorithmID *algid) +{ + unsigned int keyLengthBits; + SECOidTag algId; + int i; + + algId = SEC_PKCS5GetCryptoAlgorithm(algid); + if(algId == SEC_OID_UNKNOWN) { + return PR_FALSE; + } + + keyLengthBits = (unsigned int)(SEC_PKCS5GetKeyLength(algid) * 8); + + i = 0; + while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) { + if((pkcs12SuiteMaps[i].algTag == algId) && + (pkcs12SuiteMaps[i].keyLengthBits == keyLengthBits)) { + + return pkcs12SuiteMaps[i].allowed; + } + i++; + } + + return PR_FALSE; +} + +/* is any encryption allowed? */ +PRBool +SEC_PKCS12IsEncryptionAllowed(void) +{ + int i; + + i = 0; + while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) { + if(pkcs12SuiteMaps[i].allowed == PR_TRUE) { + return PR_TRUE; + } + i++; + } + + return PR_FALSE; +} + +/* get the preferred algorithm. + */ +SECOidTag +SEC_PKCS12GetPreferredEncryptionAlgorithm(void) +{ + int i; + + i = 0; + while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) { + if((pkcs12SuiteMaps[i].preferred == PR_TRUE) && + (pkcs12SuiteMaps[i].allowed == PR_TRUE)) { + return SEC_PKCS5GetPBEAlgorithm(pkcs12SuiteMaps[i].algTag, + pkcs12SuiteMaps[i].keyLengthBits); + } + i++; + } + + return SEC_OID_UNKNOWN; +} + +/* return the strongest algorithm allowed */ +SECOidTag +SEC_PKCS12GetStrongestAllowedAlgorithm(void) +{ + int i, keyLengthBits = 0; + SECOidTag algorithm = SEC_OID_UNKNOWN; + + i = 0; + while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) { + if((pkcs12SuiteMaps[i].allowed == PR_TRUE) && + (pkcs12SuiteMaps[i].keyLengthBits > (unsigned int)keyLengthBits) && + (pkcs12SuiteMaps[i].algTag != SEC_OID_RC4)) { + algorithm = pkcs12SuiteMaps[i].algTag; + keyLengthBits = pkcs12SuiteMaps[i].keyLengthBits; + } + i++; + } + + if(algorithm == SEC_OID_UNKNOWN) { + return SEC_OID_UNKNOWN; + } + + return SEC_PKCS5GetPBEAlgorithm(algorithm, keyLengthBits); +} + +SECStatus +SEC_PKCS12EnableCipher(long which, int on) +{ + int i; + + i = 0; + while(pkcs12SuiteMaps[i].suite != 0L) { + if(pkcs12SuiteMaps[i].suite == (unsigned long)which) { + if(on) { + pkcs12SuiteMaps[i].allowed = PR_TRUE; + } else { + pkcs12SuiteMaps[i].allowed = PR_FALSE; + } + return SECSuccess; + } + i++; + } + + return SECFailure; +} + +SECStatus +SEC_PKCS12SetPreferredCipher(long which, int on) +{ + int i; + PRBool turnedOff = PR_FALSE; + PRBool turnedOn = PR_FALSE; + + i = 0; + while(pkcs12SuiteMaps[i].suite != 0L) { + if(pkcs12SuiteMaps[i].preferred == PR_TRUE) { + pkcs12SuiteMaps[i].preferred = PR_FALSE; + turnedOff = PR_TRUE; + } + if(pkcs12SuiteMaps[i].suite == (unsigned long)which) { + pkcs12SuiteMaps[i].preferred = PR_TRUE; + turnedOn = PR_TRUE; + } + i++; + } + + if((turnedOn) && (turnedOff)) { + return SECSuccess; + } + + return SECFailure; +} + diff --git a/security/nss/lib/pkcs12/p12plcy.h b/security/nss/lib/pkcs12/p12plcy.h new file mode 100644 index 000000000..1a3a9ee2f --- /dev/null +++ b/security/nss/lib/pkcs12/p12plcy.h @@ -0,0 +1,57 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +#ifndef _P12PLCY_H_ +#define _P12PLCY_H_ + +#include "secoid.h" +#include "ciferfam.h" + +/* for the algid specified, can we decrypt it ? */ +extern PRBool SEC_PKCS12DecryptionAllowed(SECAlgorithmID *algid); + +/* is encryption allowed? */ +extern PRBool SEC_PKCS12IsEncryptionAllowed(void); + +/* get the preferred encryption algorithm */ +extern SECOidTag SEC_PKCS12GetPreferredEncryptionAlgorithm(void); + +/* get the stronget crypto allowed (based on order in the table */ +extern SECOidTag SEC_PKCS12GetStrongestAllowedAlgorithm(void); + +/* enable a cipher for encryption/decryption */ +extern SECStatus SEC_PKCS12EnableCipher(long which, int on); + +/* return the preferred cipher for encryption */ +extern SECStatus SEC_PKCS12SetPreferredCipher(long which, int on); + +#endif diff --git a/security/nss/lib/pkcs12/p12t.h b/security/nss/lib/pkcs12/p12t.h new file mode 100644 index 000000000..6b9d3da1b --- /dev/null +++ b/security/nss/lib/pkcs12/p12t.h @@ -0,0 +1,182 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef _P12T_H_ +#define _P12T_H_ + +#include "secoid.h" +#include "key.h" +#include "pkcs11.h" +#include "secpkcs7.h" +#include "secdig.h" /* for SGNDigestInfo */ + +#define SEC_PKCS12_VERSION 3 + +/* structure declarations */ +typedef struct sec_PKCS12PFXItemStr sec_PKCS12PFXItem; +typedef struct sec_PKCS12MacDataStr sec_PKCS12MacData; +typedef struct sec_PKCS12AuthenticatedSafeStr sec_PKCS12AuthenticatedSafe; +typedef struct sec_PKCS12SafeContentsStr sec_PKCS12SafeContents; +typedef struct sec_PKCS12SafeBagStr sec_PKCS12SafeBag; +typedef struct sec_PKCS12PKCS8ShroudedKeyBagStr sec_PKCS12PKCS8ShroudedKeyBag; +typedef struct sec_PKCS12CertBagStr sec_PKCS12CertBag; +typedef struct sec_PKCS12CRLBagStr sec_PKCS12CRLBag; +typedef struct sec_PKCS12SecretBag sec_PKCS12SecretBag; +typedef struct sec_PKCS12AttributeStr sec_PKCS12Attribute; + +struct sec_PKCS12CertBagStr { + /* what type of cert is stored? */ + SECItem bagID; + + /* certificate information */ + union { + SECItem x509Cert; + SECItem SDSICert; + } value; +}; + +struct sec_PKCS12CRLBagStr { + /* what type of cert is stored? */ + SECItem bagID; + + /* certificate information */ + union { + SECItem x509CRL; + } value; +}; + +struct sec_PKCS12SecretBag { + /* what type of secret? */ + SECItem secretType; + + /* secret information. ssshhhh be vewy vewy quiet. */ + SECItem secretContent; +}; + +struct sec_PKCS12AttributeStr { + SECItem attrType; + SECItem **attrValue; +}; + +struct sec_PKCS12SafeBagStr { + + /* What type of bag are we using? */ + SECItem safeBagType; + + /* Dependent upon the type of bag being used. */ + union { + SECKEYPrivateKeyInfo *pkcs8KeyBag; + SECKEYEncryptedPrivateKeyInfo *pkcs8ShroudedKeyBag; + sec_PKCS12CertBag *certBag; + sec_PKCS12CRLBag *crlBag; + sec_PKCS12SecretBag *secretBag; + sec_PKCS12SafeContents *safeContents; + } safeBagContent; + + sec_PKCS12Attribute **attribs; + + /* used locally */ + SECOidData *bagTypeTag; + PRArenaPool *arena; + unsigned int nAttribs; + + /* used for validation/importing */ + PRBool problem, noInstall, validated, hasKey, removeExisting, installed; + int error; + + PRBool swapUnicodeBytes; + PK11SlotInfo *slot; + SECItem *pwitem; + PRBool oldBagType; +}; + +struct sec_PKCS12SafeContentsStr { + sec_PKCS12SafeBag **safeBags; + SECItem **encodedSafeBags; + + /* used locally */ + PRArenaPool *arena; + unsigned int bagCount; +}; + +struct sec_PKCS12MacDataStr { + SGNDigestInfo safeMac; + SECItem macSalt; + SECItem iter; +}; + +struct sec_PKCS12PFXItemStr { + + SECItem version; + + /* Content type will either be Data (password integrity mode) + * or signedData (public-key integrity mode) + */ + SEC_PKCS7ContentInfo *authSafe; + SECItem encodedAuthSafe; + + /* Only present in password integrity mode */ + sec_PKCS12MacData macData; + SECItem encodedMacData; +}; + +struct sec_PKCS12AuthenticatedSafeStr { + /* Content type will either be encryptedData (password privacy mode) + * or envelopedData (public-key privacy mode) + */ + SEC_PKCS7ContentInfo **safes; + SECItem **encodedSafes; + + /* used locally */ + unsigned int safeCount; + SECItem dummySafe; +}; + +extern const SEC_ASN1Template sec_PKCS12PFXItemTemplate[]; +extern const SEC_ASN1Template sec_PKCS12MacDataTemplate[]; +extern const SEC_ASN1Template sec_PKCS12AuthenticatedSafeTemplate[]; +extern const SEC_ASN1Template sec_PKCS12SafeContentsTemplate[]; +extern const SEC_ASN1Template sec_PKCS12SafeContentsDecodeTemplate[]; +extern const SEC_ASN1Template sec_PKCS12NestedSafeContentsDecodeTemplate[]; +extern const SEC_ASN1Template sec_PKCS12CertBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12CRLBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12SecretBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToCertBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToCRLBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToSecretBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToSafeContentsTemplate[]; +extern const SEC_ASN1Template sec_PKCS12AttributeTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToContentInfoTemplate[]; +extern const SEC_ASN1Template sec_PKCS12SafeBagTemplate[]; + +#endif diff --git a/security/nss/lib/pkcs12/p12tmpl.c b/security/nss/lib/pkcs12/p12tmpl.c new file mode 100644 index 000000000..e58816386 --- /dev/null +++ b/security/nss/lib/pkcs12/p12tmpl.c @@ -0,0 +1,320 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "plarena.h" +#include "secitem.h" +#include "secoid.h" +#include "seccomon.h" +#include "secport.h" +#include "cert.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "p12t.h" + +SEC_ASN1_MKSUB(SEC_AnyTemplate) +SEC_ASN1_MKSUB(sgn_DigestInfoTemplate) + +static const SEC_ASN1Template * +sec_pkcs12_choose_safe_bag_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + sec_PKCS12SafeBag *safeBag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + safeBag = (sec_PKCS12SafeBag*)src_or_dest; + + oiddata = SECOID_FindOID(&safeBag->safeBagType); + if(oiddata == NULL) { + return SEC_ASN1_GET(SEC_AnyTemplate); + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_ASN1_GET(SEC_AnyTemplate); + break; + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + theTemplate = SEC_ASN1_GET(SECKEY_PointerToPrivateKeyInfoTemplate); + break; + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + theTemplate = sec_PKCS12PointerToCertBagTemplate; + break; + case SEC_OID_PKCS12_V1_CRL_BAG_ID: + theTemplate = sec_PKCS12PointerToCRLBagTemplate; + break; + case SEC_OID_PKCS12_V1_SECRET_BAG_ID: + theTemplate = sec_PKCS12PointerToSecretBagTemplate; + break; + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + theTemplate = + SEC_ASN1_GET(SECKEY_PointerToEncryptedPrivateKeyInfoTemplate); + break; + case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: + if(encoding) { + theTemplate = sec_PKCS12PointerToSafeContentsTemplate; + } else { + theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate); + } + break; + } + return theTemplate; +} + +static const SEC_ASN1Template * +sec_pkcs12_choose_crl_bag_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + sec_PKCS12CRLBag *crlbag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + crlbag = (sec_PKCS12CRLBag*)src_or_dest; + + oiddata = SECOID_FindOID(&crlbag->bagID); + if(oiddata == NULL) { + return SEC_ASN1_GET(SEC_AnyTemplate); + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_ASN1_GET(SEC_AnyTemplate); + break; + case SEC_OID_PKCS9_X509_CRL: + theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate); + break; + } + return theTemplate; +} + +static const SEC_ASN1Template * +sec_pkcs12_choose_cert_bag_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + sec_PKCS12CertBag *certbag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + certbag = (sec_PKCS12CertBag*)src_or_dest; + + oiddata = SECOID_FindOID(&certbag->bagID); + if(oiddata == NULL) { + return SEC_ASN1_GET(SEC_AnyTemplate); + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_ASN1_GET(SEC_AnyTemplate); + break; + case SEC_OID_PKCS9_X509_CERT: + theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate); + break; + case SEC_OID_PKCS9_SDSI_CERT: + theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate); + break; + } + return theTemplate; +} + +static const SEC_ASN1Template * +sec_pkcs12_choose_attr_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + sec_PKCS12Attribute *attr; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + attr = (sec_PKCS12Attribute*)src_or_dest; + + oiddata = SECOID_FindOID(&attr->attrType); + if(oiddata == NULL) { + return SEC_ASN1_GET(SEC_AnyTemplate); + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_ASN1_GET(SEC_AnyTemplate); + break; + case SEC_OID_PKCS9_FRIENDLY_NAME: + theTemplate = SEC_ASN1_GET(SEC_BMPStringTemplate); + break; + case SEC_OID_PKCS9_LOCAL_KEY_ID: + theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate); + break; + case SEC_OID_PKCS12_KEY_USAGE: + theTemplate = SEC_ASN1_GET(SEC_BitStringTemplate); + break; + } + + return theTemplate; +} + + +const SEC_ASN1Template sec_PKCS12PointerToContentInfoTemplate[] = { + { SEC_ASN1_POINTER | SEC_ASN1_MAY_STREAM, 0, sec_PKCS7ContentInfoTemplate } +}; + +static const SEC_ASN1TemplateChooserPtr sec_pkcs12_crl_bag_chooser = + sec_pkcs12_choose_crl_bag_type; + +static const SEC_ASN1TemplateChooserPtr sec_pkcs12_cert_bag_chooser = + sec_pkcs12_choose_cert_bag_type; + +static const SEC_ASN1TemplateChooserPtr sec_pkcs12_safe_bag_chooser = + sec_pkcs12_choose_safe_bag_type; + +static const SEC_ASN1TemplateChooserPtr sec_pkcs12_attr_chooser = + sec_pkcs12_choose_attr_type; + +const SEC_ASN1Template sec_PKCS12PointerToCertBagTemplate[] = { + { SEC_ASN1_POINTER, 0, sec_PKCS12CertBagTemplate } +}; + +const SEC_ASN1Template sec_PKCS12PointerToCRLBagTemplate[] = { + { SEC_ASN1_POINTER, 0, sec_PKCS12CRLBagTemplate } +}; + +const SEC_ASN1Template sec_PKCS12PointerToSecretBagTemplate[] = { + { SEC_ASN1_POINTER, 0, sec_PKCS12SecretBagTemplate } +}; + +const SEC_ASN1Template sec_PKCS12PointerToSafeContentsTemplate[] = { + { SEC_ASN1_POINTER, 0, sec_PKCS12SafeContentsTemplate } +}; + +const SEC_ASN1Template sec_PKCS12PFXItemTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, 0, NULL, + sizeof(sec_PKCS12PFXItem) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(sec_PKCS12PFXItem, version) }, + { SEC_ASN1_ANY | SEC_ASN1_MAY_STREAM, + offsetof(sec_PKCS12PFXItem, encodedAuthSafe) }, + { SEC_ASN1_ANY | SEC_ASN1_MAY_STREAM, + offsetof(sec_PKCS12PFXItem, encodedMacData) }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12MacDataTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12MacData) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN , offsetof(sec_PKCS12MacData, safeMac), + SEC_ASN1_SUB(sgn_DigestInfoTemplate) }, + { SEC_ASN1_OCTET_STRING, offsetof(sec_PKCS12MacData, macSalt) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, offsetof(sec_PKCS12MacData, iter) }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12AuthenticatedSafeTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM | SEC_ASN1_XTRN , + offsetof(sec_PKCS12AuthenticatedSafe, encodedSafes), + SEC_ASN1_SUB(SEC_AnyTemplate) } +}; + +const SEC_ASN1Template sec_PKCS12SafeBagTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, 0, NULL, + sizeof(sec_PKCS12SafeBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12SafeBag, safeBagType) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_MAY_STREAM | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(sec_PKCS12SafeBag, safeBagContent), + &sec_pkcs12_safe_bag_chooser }, + { SEC_ASN1_SET_OF | SEC_ASN1_OPTIONAL, offsetof(sec_PKCS12SafeBag, attribs), + sec_PKCS12AttributeTemplate }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12SafeContentsTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM, + offsetof(sec_PKCS12SafeContents, safeBags), + sec_PKCS12SafeBagTemplate } +}; + +const SEC_ASN1Template sec_PKCS12SequenceOfAnyTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM | SEC_ASN1_XTRN , 0, + SEC_ASN1_SUB(SEC_AnyTemplate) } +}; + +const SEC_ASN1Template sec_PKCS12NestedSafeContentsDecodeTemplate[] = { + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0, + offsetof(sec_PKCS12SafeContents, encodedSafeBags), + sec_PKCS12SequenceOfAnyTemplate } +}; + +const SEC_ASN1Template sec_PKCS12SafeContentsDecodeTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM | SEC_ASN1_XTRN , + offsetof(sec_PKCS12SafeContents, encodedSafeBags), + SEC_ASN1_SUB(SEC_AnyTemplate) } +}; + +const SEC_ASN1Template sec_PKCS12CRLBagTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12CRLBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12CRLBag, bagID) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_POINTER, + offsetof(sec_PKCS12CRLBag, value), &sec_pkcs12_crl_bag_chooser }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12CertBagTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12CertBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12CertBag, bagID) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(sec_PKCS12CertBag, value), &sec_pkcs12_cert_bag_chooser }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12SecretBagTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12SecretBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12SecretBag, secretType) }, + { SEC_ASN1_ANY, offsetof(sec_PKCS12SecretBag, secretContent) }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12AttributeTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12Attribute) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12Attribute, attrType) }, + { SEC_ASN1_SET_OF | SEC_ASN1_DYNAMIC, + offsetof(sec_PKCS12Attribute, attrValue), + &sec_pkcs12_attr_chooser }, + { 0 } +}; diff --git a/security/nss/lib/pkcs12/pkcs12.h b/security/nss/lib/pkcs12/pkcs12.h new file mode 100644 index 000000000..dd9d99594 --- /dev/null +++ b/security/nss/lib/pkcs12/pkcs12.h @@ -0,0 +1,67 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + + +#ifndef _PKCS12_H_ +#define _PKCS12_H_ + +#include "pkcs12t.h" +#include "p12.h" + +typedef SECItem * (* SEC_PKCS12GetPassword)(void *arg); + +/* Decode functions */ +/* Import a PFX item. + * der_pfx is the der-encoded pfx item to import. + * pbef, and pbefarg are used to retrieve passwords for the HMAC, + * and any passwords needed for passing to PKCS5 encryption + * routines. + * algorithm is the algorithm by which private keys are stored in + * the key database. this could be a specific algorithm or could + * be based on a global setting. + * slot is the slot to where the certificates will be placed. if NULL, + * the internal key slot is used. + * If the process is successful, a SECSuccess is returned, otherwise + * a failure occurred. + */ +SECStatus +SEC_PKCS12PutPFX(SECItem *der_pfx, SECItem *pwitem, + SEC_PKCS12NicknameCollisionCallback ncCall, + PK11SlotInfo *slot, void *wincx); + +/* check the first two bytes of a file to make sure that it matches + * the desired header for a PKCS 12 file + */ +PRBool SEC_PKCS12ValidData(char *buf, int bufLen, long int totalLength); + +#endif diff --git a/security/nss/lib/pkcs12/pkcs12t.h b/security/nss/lib/pkcs12/pkcs12t.h new file mode 100644 index 000000000..53c36c3f7 --- /dev/null +++ b/security/nss/lib/pkcs12/pkcs12t.h @@ -0,0 +1,386 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef _PKCS12T_H_ +#define _PKCS12T_H_ + +#include "seccomon.h" +#include "secoid.h" +#include "cert.h" +#include "key.h" +#include "plarena.h" +#include "secpkcs7.h" +#include "secdig.h" /* for SGNDigestInfo */ + +/* PKCS12 Structures */ +typedef struct SEC_PKCS12PFXItemStr SEC_PKCS12PFXItem; +typedef struct SEC_PKCS12MacDataStr SEC_PKCS12MacData; +typedef struct SEC_PKCS12AuthenticatedSafeStr SEC_PKCS12AuthenticatedSafe; +typedef struct SEC_PKCS12BaggageItemStr SEC_PKCS12BaggageItem; +typedef struct SEC_PKCS12BaggageStr SEC_PKCS12Baggage; +typedef struct SEC_PKCS12Baggage_OLDStr SEC_PKCS12Baggage_OLD; +typedef struct SEC_PKCS12ESPVKItemStr SEC_PKCS12ESPVKItem; +typedef struct SEC_PKCS12PVKSupportingDataStr SEC_PKCS12PVKSupportingData; +typedef struct SEC_PKCS12PVKAdditionalDataStr SEC_PKCS12PVKAdditionalData; +typedef struct SEC_PKCS12SafeContentsStr SEC_PKCS12SafeContents; +typedef struct SEC_PKCS12SafeBagStr SEC_PKCS12SafeBag; +typedef struct SEC_PKCS12PrivateKeyStr SEC_PKCS12PrivateKey; +typedef struct SEC_PKCS12PrivateKeyBagStr SEC_PKCS12PrivateKeyBag; +typedef struct SEC_PKCS12CertAndCRLBagStr SEC_PKCS12CertAndCRLBag; +typedef struct SEC_PKCS12CertAndCRLStr SEC_PKCS12CertAndCRL; +typedef struct SEC_PKCS12X509CertCRLStr SEC_PKCS12X509CertCRL; +typedef struct SEC_PKCS12SDSICertStr SEC_PKCS12SDSICert; +typedef struct SEC_PKCS12SecretStr SEC_PKCS12Secret; +typedef struct SEC_PKCS12SecretAdditionalStr SEC_PKCS12SecretAdditional; +typedef struct SEC_PKCS12SecretItemStr SEC_PKCS12SecretItem; +typedef struct SEC_PKCS12SecretBagStr SEC_PKCS12SecretBag; + +typedef SECItem *(* SEC_PKCS12PasswordFunc)(SECItem *args); + +/* PKCS12 types */ + +/* stores shrouded keys */ +struct SEC_PKCS12BaggageStr +{ + PRArenaPool *poolp; + SEC_PKCS12BaggageItem **bags; + + int luggage_size; /* used locally */ +}; + +/* additional data to be associated with keys. currently there + * is nothing defined to be stored here. allows future expansion. + */ +struct SEC_PKCS12PVKAdditionalDataStr +{ + PRArenaPool *poolp; + SECOidData *pvkAdditionalTypeTag; /* used locally */ + SECItem pvkAdditionalType; + SECItem pvkAdditionalContent; +}; + +/* cert and other supporting data for private keys. used + * for both shrouded and non-shrouded keys. + */ +struct SEC_PKCS12PVKSupportingDataStr +{ + PRArenaPool *poolp; + SGNDigestInfo **assocCerts; + SECItem regenerable; + SECItem nickname; + SEC_PKCS12PVKAdditionalData pvkAdditional; + SECItem pvkAdditionalDER; + + SECItem uniNickName; + /* used locally */ + int nThumbs; +}; + +/* shrouded key structure. supports only pkcs8 shrouding + * currently. + */ +struct SEC_PKCS12ESPVKItemStr +{ + PRArenaPool *poolp; /* used locally */ + SECOidData *espvkTag; /* used locally */ + SECItem espvkOID; + SEC_PKCS12PVKSupportingData espvkData; + union + { + SECKEYEncryptedPrivateKeyInfo *pkcs8KeyShroud; + } espvkCipherText; + + PRBool duplicate; /* used locally */ + PRBool problem_cert; /* used locally */ + PRBool single_cert; /* used locally */ + int nCerts; /* used locally */ + SECItem derCert; /* used locally */ +}; + +/* generic bag store for the safe. safeBagType identifies + * the type of bag stored. + */ +struct SEC_PKCS12SafeBagStr +{ + PRArenaPool *poolp; + SECOidData *safeBagTypeTag; /* used locally */ + SECItem safeBagType; + union + { + SEC_PKCS12PrivateKeyBag *keyBag; + SEC_PKCS12CertAndCRLBag *certAndCRLBag; + SEC_PKCS12SecretBag *secretBag; + } safeContent; + + SECItem derSafeContent; + SECItem safeBagName; + + SECItem uniSafeBagName; +}; + +/* stores private keys and certificates in a list. each safebag + * has an ID identifying the type of content stored. + */ +struct SEC_PKCS12SafeContentsStr +{ + PRArenaPool *poolp; + SEC_PKCS12SafeBag **contents; + + /* used for tracking purposes */ + int safe_size; + PRBool old; + PRBool swapUnicode; + PRBool possibleSwapUnicode; +}; + +/* private key structure which holds encrypted private key and + * supporting data including nickname and certificate thumbprint. + */ +struct SEC_PKCS12PrivateKeyStr +{ + PRArenaPool *poolp; + SEC_PKCS12PVKSupportingData pvkData; + SECKEYPrivateKeyInfo pkcs8data; /* borrowed from PKCS 8 */ + + PRBool duplicate; /* used locally */ + PRBool problem_cert;/* used locally */ + PRBool single_cert; /* used locally */ + int nCerts; /* used locally */ + SECItem derCert; /* used locally */ +}; + +/* private key bag, holds a (null terminated) list of private key + * structures. + */ +struct SEC_PKCS12PrivateKeyBagStr +{ + PRArenaPool *poolp; + SEC_PKCS12PrivateKey **privateKeys; + + int bag_size; /* used locally */ +}; + +/* container to hold certificates. currently supports x509 + * and sdsi certificates + */ +struct SEC_PKCS12CertAndCRLStr +{ + PRArenaPool *poolp; + SECOidData *BagTypeTag; /* used locally */ + SECItem BagID; + union + { + SEC_PKCS12X509CertCRL *x509; + SEC_PKCS12SDSICert *sdsi; + } value; + + SECItem derValue; + SECItem nickname; /* used locally */ + PRBool duplicate; /* used locally */ +}; + +/* x509 certificate structure. typically holds the der encoding + * of the x509 certificate. thumbprint contains a digest of the + * certificate + */ +struct SEC_PKCS12X509CertCRLStr +{ + PRArenaPool *poolp; + SEC_PKCS7ContentInfo certOrCRL; + SGNDigestInfo thumbprint; + + SECItem *derLeafCert; /* used locally */ +}; + +/* sdsi certificate structure. typically holds the der encoding + * of the sdsi certificate. thumbprint contains a digest of the + * certificate + */ +struct SEC_PKCS12SDSICertStr +{ + PRArenaPool *poolp; + SECItem value; + SGNDigestInfo thumbprint; +}; + +/* contains a null terminated list of certs and crls */ +struct SEC_PKCS12CertAndCRLBagStr +{ + PRArenaPool *poolp; + SEC_PKCS12CertAndCRL **certAndCRLs; + + int bag_size; /* used locally */ +}; + +/* additional secret information. currently no information + * stored in this structure. + */ +struct SEC_PKCS12SecretAdditionalStr +{ + PRArenaPool *poolp; + SECOidData *secretTypeTag; /* used locally */ + SECItem secretAdditionalType; + SECItem secretAdditionalContent; +}; + +/* secrets container. this will be used to contain currently + * unspecified secrets. (it's a secret) + */ +struct SEC_PKCS12SecretStr +{ + PRArenaPool *poolp; + SECItem secretName; + SECItem value; + SEC_PKCS12SecretAdditional secretAdditional; + + SECItem uniSecretName; +}; + +struct SEC_PKCS12SecretItemStr +{ + PRArenaPool *poolp; + SEC_PKCS12Secret secret; + SEC_PKCS12SafeBag subFolder; +}; + +/* a bag of secrets. holds a null terminated list of secrets. + */ +struct SEC_PKCS12SecretBagStr +{ + PRArenaPool *poolp; + SEC_PKCS12SecretItem **secrets; + + int bag_size; /* used locally */ +}; + +struct SEC_PKCS12MacDataStr +{ + SGNDigestInfo safeMac; + SECItem macSalt; +}; + +/* outer transfer unit */ +struct SEC_PKCS12PFXItemStr +{ + PRArenaPool *poolp; + SEC_PKCS12MacData macData; + SEC_PKCS7ContentInfo authSafe; + + /* for compatibility with beta */ + PRBool old; + SGNDigestInfo old_safeMac; + SECItem old_macSalt; + + /* compatibility between platforms for unicode swapping */ + PRBool swapUnicode; +}; + +struct SEC_PKCS12BaggageItemStr { + PRArenaPool *poolp; + SEC_PKCS12ESPVKItem **espvks; + SEC_PKCS12SafeBag **unencSecrets; + + int nEspvks; + int nSecrets; +}; + +/* stores shrouded keys */ +struct SEC_PKCS12Baggage_OLDStr +{ + PRArenaPool *poolp; + SEC_PKCS12ESPVKItem **espvks; + + int luggage_size; /* used locally */ +}; + +/* authenticated safe, stores certs, keys, and shrouded keys */ +struct SEC_PKCS12AuthenticatedSafeStr +{ + PRArenaPool *poolp; + SECItem version; + SECOidData *transportTypeTag; /* local not part of encoding*/ + SECItem transportMode; + SECItem privacySalt; + SEC_PKCS12Baggage baggage; + SEC_PKCS7ContentInfo *safe; + + /* used for beta compatibility */ + PRBool old; + PRBool emptySafe; + SEC_PKCS12Baggage_OLD old_baggage; + SEC_PKCS7ContentInfo old_safe; + PRBool swapUnicode; +}; +#define SEC_PKCS12_PFX_VERSION 1 /* what we create */ + + + +/* PKCS 12 Templates */ +extern const SEC_ASN1Template SEC_PKCS12PFXItemTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12BaggageTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12PFXItemTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12MacDataTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12BaggageTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PVKAdditionalTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SafeBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PrivateKeyTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PrivateKeyBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SDSICertTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SecretBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SecretTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SecretItemTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SecretAdditionalTemplate[]; +extern const SEC_ASN1Template SGN_DigestInfoTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12KeyBagTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12SecretBagTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12SDSICertTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CodedSafeBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CodedCertBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CodedCertAndCRLBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate_OLD[]; +#endif |