summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcvs2hg <devnull@localhost>2001-07-11 23:58:03 +0000
committercvs2hg <devnull@localhost>2001-07-11 23:58:03 +0000
commit9b09365045637779e78b6bc89af721928f6e768e (patch)
tree16a8c88bd91c4f759abdec26dd13fdfd281ba785
parent1d2fc658c9eeed67ed768fdb3924b176b9d05b22 (diff)
downloadnss-hg-9b09365045637779e78b6bc89af721928f6e768e.tar.gz
fixup commit for tag 'NSS_3_3_BETA3'NSS_3_3_BETA3
-rw-r--r--security/nss/lib/ckfw/object.c1044
-rw-r--r--security/nss/lib/pk11wrap/pk11kea.c224
-rw-r--r--security/nss/lib/pkcs12/p12d.c3230
-rw-r--r--security/nss/lib/softoken/keydb.c2482
-rw-r--r--security/nss/lib/softoken/private.h78
-rw-r--r--security/nss/lib/util/secerr.h187
6 files changed, 7245 insertions, 0 deletions
diff --git a/security/nss/lib/ckfw/object.c b/security/nss/lib/ckfw/object.c
new file mode 100644
index 000000000..ec8dfd6d5
--- /dev/null
+++ b/security/nss/lib/ckfw/object.c
@@ -0,0 +1,1044 @@
+/*
+ * 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.
+ */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * object.c
+ *
+ * This file implements the NSSCKFWObject type and methods.
+ */
+
+#ifndef CK_T
+#include "ck.h"
+#endif /* CK_T */
+
+/*
+ * NSSCKFWObject
+ *
+ * -- create/destroy --
+ * nssCKFWObject_Create
+ * nssCKFWObject_Finalize
+ * nssCKFWObject_Destroy
+ *
+ * -- public accessors --
+ * NSSCKFWObject_GetMDObject
+ * NSSCKFWObject_GetArena
+ * NSSCKFWObject_IsTokenObject
+ * NSSCKFWObject_GetAttributeCount
+ * NSSCKFWObject_GetAttributeTypes
+ * NSSCKFWObject_GetAttributeSize
+ * NSSCKFWObject_GetAttribute
+ * NSSCKFWObject_SetAttribute
+ * NSSCKFWObject_GetObjectSize
+ *
+ * -- implement public accessors --
+ * nssCKFWObject_GetMDObject
+ * nssCKFWObject_GetArena
+ *
+ * -- private accessors --
+ * nssCKFWObject_SetHandle
+ * nssCKFWObject_GetHandle
+ *
+ * -- module fronts --
+ * nssCKFWObject_IsTokenObject
+ * nssCKFWObject_GetAttributeCount
+ * nssCKFWObject_GetAttributeTypes
+ * nssCKFWObject_GetAttributeSize
+ * nssCKFWObject_GetAttribute
+ * nssCKFWObject_SetAttribute
+ * nssCKFWObject_GetObjectSize
+ */
+
+struct NSSCKFWObjectStr {
+ NSSCKFWMutex *mutex; /* merely to serialise the MDObject calls */
+ NSSArena *arena;
+ NSSCKMDObject *mdObject;
+ NSSCKMDSession *mdSession;
+ NSSCKFWSession *fwSession;
+ NSSCKMDToken *mdToken;
+ NSSCKFWToken *fwToken;
+ NSSCKMDInstance *mdInstance;
+ NSSCKFWInstance *fwInstance;
+ CK_OBJECT_HANDLE hObject;
+};
+
+#ifdef DEBUG
+/*
+ * But first, the pointer-tracking stuff.
+ *
+ * NOTE: the pointer-tracking support in NSS/base currently relies
+ * upon NSPR's CallOnce support. That, however, relies upon NSPR's
+ * locking, which is tied into the runtime. We need a pointer-tracker
+ * implementation that uses the locks supplied through C_Initialize.
+ * That support, however, can be filled in later. So for now, I'll
+ * just do this routines as no-ops.
+ */
+
+static CK_RV
+object_add_pointer
+(
+ const NSSCKFWObject *fwObject
+)
+{
+ return CKR_OK;
+}
+
+static CK_RV
+object_remove_pointer
+(
+ const NSSCKFWObject *fwObject
+)
+{
+ return CKR_OK;
+}
+
+NSS_IMPLEMENT CK_RV
+nssCKFWObject_verifyPointer
+(
+ const NSSCKFWObject *fwObject
+)
+{
+ return CKR_OK;
+}
+
+#endif /* DEBUG */
+
+
+/*
+ * nssCKFWObject_Create
+ *
+ */
+NSS_IMPLEMENT NSSCKFWObject *
+nssCKFWObject_Create
+(
+ NSSArena *arena,
+ NSSCKMDObject *mdObject,
+ NSSCKFWSession *fwSession,
+ NSSCKFWToken *fwToken,
+ NSSCKFWInstance *fwInstance,
+ CK_RV *pError
+)
+{
+ NSSCKFWObject *fwObject;
+ nssCKFWHash *mdObjectHash;
+
+#ifdef NSSDEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (NSSCKFWObject *)NULL;
+ }
+
+ if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
+ *pError = CKR_ARGUMENTS_BAD;
+ return (NSSCKFWObject *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ mdObjectHash = nssCKFWToken_GetMDObjectHash(fwToken);
+ if( (nssCKFWHash *)NULL == mdObjectHash ) {
+ *pError = CKR_GENERAL_ERROR;
+ return (NSSCKFWObject *)NULL;
+ }
+
+ if( nssCKFWHash_Exists(mdObjectHash, mdObject) ) {
+ fwObject = nssCKFWHash_Lookup(mdObjectHash, mdObject);
+ return fwObject;
+ }
+
+ fwObject = nss_ZNEW(arena, NSSCKFWObject);
+ if( (NSSCKFWObject *)NULL == fwObject ) {
+ *pError = CKR_HOST_MEMORY;
+ return (NSSCKFWObject *)NULL;
+ }
+
+ fwObject->arena = arena;
+ fwObject->mdObject = mdObject;
+ fwObject->fwSession = fwSession;
+
+ if( (NSSCKFWSession *)NULL != fwSession ) {
+ fwObject->mdSession = nssCKFWSession_GetMDSession(fwSession);
+ }
+
+ fwObject->fwToken = fwToken;
+
+ if( (NSSCKFWToken *)NULL != fwToken ) {
+ fwObject->mdToken = nssCKFWToken_GetMDToken(fwToken);
+ }
+
+ fwObject->fwInstance = fwInstance;
+ fwObject->mdInstance = nssCKFWInstance_GetMDInstance(fwInstance);
+ fwObject->mutex = nssCKFWInstance_CreateMutex(fwInstance, arena, pError);
+ if( (NSSCKFWMutex *)NULL == fwObject->mutex ) {
+ if( CKR_OK == *pError ) {
+ *pError = CKR_GENERAL_ERROR;
+ }
+ return (NSSCKFWObject *)NULL;
+ }
+
+ *pError = nssCKFWHash_Add(mdObjectHash, mdObject, fwObject);
+ if( CKR_OK != *pError ) {
+ nss_ZFreeIf(fwObject);
+ return (NSSCKFWObject *)NULL;
+ }
+
+#ifdef DEBUG
+ *pError = object_add_pointer(fwObject);
+ if( CKR_OK != *pError ) {
+ nssCKFWHash_Remove(mdObjectHash, mdObject);
+ nss_ZFreeIf(fwObject);
+ return (NSSCKFWObject *)NULL;
+ }
+#endif /* DEBUG */
+
+ *pError = CKR_OK;
+ return fwObject;
+}
+
+/*
+ * nssCKFWObject_Finalize
+ *
+ */
+NSS_IMPLEMENT void
+nssCKFWObject_Finalize
+(
+ NSSCKFWObject *fwObject
+)
+{
+ nssCKFWHash *mdObjectHash;
+
+#ifdef NSSDEBUG
+ if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
+ return;
+ }
+#endif /* NSSDEBUG */
+
+ (void)nssCKFWMutex_Destroy(fwObject->mutex);
+
+ if( (void *)NULL != (void *)fwObject->mdObject->Finalize ) {
+ fwObject->mdObject->Finalize(fwObject->mdObject, fwObject,
+ fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
+ fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance);
+ }
+
+ mdObjectHash = nssCKFWToken_GetMDObjectHash(fwObject->fwToken);
+ if( (nssCKFWHash *)NULL != mdObjectHash ) {
+ nssCKFWHash_Remove(mdObjectHash, fwObject->mdObject);
+ }
+
+ nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject);
+ nss_ZFreeIf(fwObject);
+
+#ifdef DEBUG
+ (void)object_remove_pointer(fwObject);
+#endif /* DEBUG */
+
+ return;
+}
+
+/*
+ * nssCKFWObject_Destroy
+ *
+ */
+NSS_IMPLEMENT void
+nssCKFWObject_Destroy
+(
+ NSSCKFWObject *fwObject
+)
+{
+ nssCKFWHash *mdObjectHash;
+
+#ifdef NSSDEBUG
+ if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
+ return;
+ }
+#endif /* NSSDEBUG */
+
+ (void)nssCKFWMutex_Destroy(fwObject->mutex);
+
+ if( (void *)NULL != (void *)fwObject->mdObject->Destroy ) {
+ fwObject->mdObject->Destroy(fwObject->mdObject, fwObject,
+ fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
+ fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance);
+ }
+
+ mdObjectHash = nssCKFWToken_GetMDObjectHash(fwObject->fwToken);
+ if( (nssCKFWHash *)NULL != mdObjectHash ) {
+ nssCKFWHash_Remove(mdObjectHash, fwObject->mdObject);
+ }
+
+ nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject);
+ nss_ZFreeIf(fwObject);
+
+#ifdef DEBUG
+ (void)object_remove_pointer(fwObject);
+#endif /* DEBUG */
+
+ return;
+}
+
+/*
+ * nssCKFWObject_GetMDObject
+ *
+ */
+NSS_IMPLEMENT NSSCKMDObject *
+nssCKFWObject_GetMDObject
+(
+ NSSCKFWObject *fwObject
+)
+{
+#ifdef NSSDEBUG
+ if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
+ return (NSSCKMDObject *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ return fwObject->mdObject;
+}
+
+/*
+ * nssCKFWObject_GetArena
+ *
+ */
+NSS_IMPLEMENT NSSArena *
+nssCKFWObject_GetArena
+(
+ NSSCKFWObject *fwObject,
+ CK_RV *pError
+)
+{
+#ifdef NSSDEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (NSSArena *)NULL;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (NSSArena *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ return fwObject->arena;
+}
+
+/*
+ * nssCKFWObject_SetHandle
+ *
+ */
+NSS_IMPLEMENT CK_RV
+nssCKFWObject_SetHandle
+(
+ NSSCKFWObject *fwObject,
+ CK_OBJECT_HANDLE hObject
+)
+{
+#ifdef NSSDEBUG
+ CK_RV error = CKR_OK;
+#endif /* NSSDEBUG */
+
+#ifdef NSSDEBUG
+ error = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != error ) {
+ return error;
+ }
+#endif /* NSSDEBUG */
+
+ if( (CK_OBJECT_HANDLE)0 != fwObject->hObject ) {
+ return CKR_GENERAL_ERROR;
+ }
+
+ fwObject->hObject = hObject;
+
+ return CKR_OK;
+}
+
+/*
+ * nssCKFWObject_GetHandle
+ *
+ */
+NSS_IMPLEMENT CK_OBJECT_HANDLE
+nssCKFWObject_GetHandle
+(
+ NSSCKFWObject *fwObject
+)
+{
+#ifdef NSSDEBUG
+ if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
+ return (CK_OBJECT_HANDLE)0;
+ }
+#endif /* NSSDEBUG */
+
+ return fwObject->hObject;
+}
+
+/*
+ * nssCKFWObject_IsTokenObject
+ *
+ */
+NSS_IMPLEMENT CK_BBOOL
+nssCKFWObject_IsTokenObject
+(
+ NSSCKFWObject *fwObject
+)
+{
+ CK_BBOOL b = CK_FALSE;
+
+#ifdef NSSDEBUG
+ if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
+ return CK_FALSE;
+ }
+#endif /* NSSDEBUG */
+
+ if( CKR_OK != nssCKFWMutex_Lock(fwObject->mutex) ) {
+ return CK_FALSE;
+ }
+
+ if( (void *)NULL == (void *)fwObject->mdObject->IsTokenObject ) {
+ NSSItem item;
+ NSSItem *pItem;
+ CK_RV rv = CKR_OK;
+
+ item.data = (void *)&b;
+ item.size = sizeof(b);
+
+ pItem = nssCKFWObject_GetAttribute(fwObject, CKA_TOKEN, &item,
+ (NSSArena *)NULL, &rv);
+ if( (NSSItem *)NULL == pItem ) {
+ /* Error of some type */
+ b = CK_FALSE;
+ goto done;
+ }
+
+ goto done;
+ }
+
+ b = fwObject->mdObject->IsTokenObject(fwObject->mdObject, fwObject,
+ fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
+ fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance);
+
+ done:
+ (void)nssCKFWMutex_Unlock(fwObject->mutex);
+ return b;
+}
+
+/*
+ * nssCKFWObject_GetAttributeCount
+ *
+ */
+NSS_IMPLEMENT CK_ULONG
+nssCKFWObject_GetAttributeCount
+(
+ NSSCKFWObject *fwObject,
+ CK_RV *pError
+)
+{
+ CK_ULONG rv;
+
+#ifdef NSSDEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (CK_ULONG)0;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (CK_ULONG)0;
+ }
+#endif /* NSSDEBUG */
+
+ if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeCount ) {
+ *pError = CKR_GENERAL_ERROR;
+ return (CK_ULONG)0;
+ }
+
+ *pError = nssCKFWMutex_Lock(fwObject->mutex);
+ if( CKR_OK != *pError ) {
+ return (CK_ULONG)0;
+ }
+
+ rv = fwObject->mdObject->GetAttributeCount(fwObject->mdObject, fwObject,
+ fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
+ fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
+ pError);
+
+ (void)nssCKFWMutex_Unlock(fwObject->mutex);
+ return rv;
+}
+
+/*
+ * nssCKFWObject_GetAttributeTypes
+ *
+ */
+NSS_IMPLEMENT CK_RV
+nssCKFWObject_GetAttributeTypes
+(
+ NSSCKFWObject *fwObject,
+ CK_ATTRIBUTE_TYPE_PTR typeArray,
+ CK_ULONG ulCount
+)
+{
+ CK_RV error = CKR_OK;
+
+#ifdef NSSDEBUG
+ error = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != error ) {
+ return error;
+ }
+
+ if( (CK_ATTRIBUTE_TYPE_PTR)NULL == typeArray ) {
+ return CKR_ARGUMENTS_BAD;
+ }
+#endif /* NSSDEBUG */
+
+ if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeTypes ) {
+ return CKR_GENERAL_ERROR;
+ }
+
+ error = nssCKFWMutex_Lock(fwObject->mutex);
+ if( CKR_OK != error ) {
+ return error;
+ }
+
+ error = fwObject->mdObject->GetAttributeTypes(fwObject->mdObject, fwObject,
+ fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
+ fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
+ typeArray, ulCount);
+
+ (void)nssCKFWMutex_Unlock(fwObject->mutex);
+ return error;
+}
+
+/*
+ * nssCKFWObject_GetAttributeSize
+ *
+ */
+NSS_IMPLEMENT CK_ULONG
+nssCKFWObject_GetAttributeSize
+(
+ NSSCKFWObject *fwObject,
+ CK_ATTRIBUTE_TYPE attribute,
+ CK_RV *pError
+)
+{
+ CK_ULONG rv;
+
+#ifdef NSSDEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (CK_ULONG)0;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (CK_ULONG)0;
+ }
+#endif /* NSSDEBUG */
+
+ if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeSize ) {
+ *pError = CKR_GENERAL_ERROR;
+ return (CK_ULONG )0;
+ }
+
+ *pError = nssCKFWMutex_Lock(fwObject->mutex);
+ if( CKR_OK != *pError ) {
+ return (CK_ULONG)0;
+ }
+
+ rv = fwObject->mdObject->GetAttributeSize(fwObject->mdObject, fwObject,
+ fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
+ fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
+ attribute, pError);
+
+ (void)nssCKFWMutex_Unlock(fwObject->mutex);
+ return rv;
+}
+
+/*
+ * nssCKFWObject_GetAttribute
+ *
+ * Usual NSS allocation rules:
+ * If itemOpt is not NULL, it will be returned; otherwise an NSSItem
+ * will be allocated. If itemOpt is not NULL but itemOpt->data is,
+ * the buffer will be allocated; otherwise, the buffer will be used.
+ * Any allocations will come from the optional arena, if one is
+ * specified.
+ */
+NSS_IMPLEMENT NSSItem *
+nssCKFWObject_GetAttribute
+(
+ NSSCKFWObject *fwObject,
+ CK_ATTRIBUTE_TYPE attribute,
+ NSSItem *itemOpt,
+ NSSArena *arenaOpt,
+ CK_RV *pError
+)
+{
+ NSSItem *rv = (NSSItem *)NULL;
+ NSSItem *mdItem;
+
+#ifdef NSSDEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (NSSItem *)NULL;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (NSSItem *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeSize ) {
+ *pError = CKR_GENERAL_ERROR;
+ return (NSSItem *)NULL;
+ }
+
+ *pError = nssCKFWMutex_Lock(fwObject->mutex);
+ if( CKR_OK != *pError ) {
+ return (NSSItem *)NULL;
+ }
+
+ mdItem = fwObject->mdObject->GetAttribute(fwObject->mdObject, fwObject,
+ fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
+ fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
+ attribute, pError);
+
+ if( (NSSItem *)NULL == mdItem ) {
+ if( CKR_OK == *pError ) {
+ *pError = CKR_GENERAL_ERROR;
+ }
+
+ goto done;
+ }
+
+ if( (NSSItem *)NULL == itemOpt ) {
+ rv = nss_ZNEW(arenaOpt, NSSItem);
+ if( (NSSItem *)NULL == rv ) {
+ *pError = CKR_HOST_MEMORY;
+ goto done;
+ }
+ } else {
+ rv = itemOpt;
+ }
+
+ if( (void *)NULL == rv->data ) {
+ rv->size = mdItem->size;
+ rv->data = nss_ZAlloc(arenaOpt, rv->size);
+ if( (void *)NULL == rv->data ) {
+ *pError = CKR_HOST_MEMORY;
+ if( (NSSItem *)NULL == itemOpt ) {
+ nss_ZFreeIf(rv);
+ }
+ rv = (NSSItem *)NULL;
+ goto done;
+ }
+ } else {
+ if( rv->size >= mdItem->size ) {
+ rv->size = mdItem->size;
+ } else {
+ *pError = CKR_BUFFER_TOO_SMALL;
+ /* Should we set rv->size to mdItem->size? */
+ /* rv can't have been allocated */
+ rv = (NSSItem *)NULL;
+ goto done;
+ }
+ }
+
+ (void)nsslibc_memcpy(rv->data, mdItem->data, rv->size);
+
+ done:
+ (void)nssCKFWMutex_Unlock(fwObject->mutex);
+ return rv;
+}
+
+/*
+ * nssCKFWObject_SetAttribute
+ *
+ */
+NSS_IMPLEMENT CK_RV
+nssCKFWObject_SetAttribute
+(
+ NSSCKFWObject *fwObject,
+ CK_ATTRIBUTE_TYPE attribute,
+ NSSItem *value
+)
+{
+ CK_RV error = CKR_OK;
+
+#ifdef NSSDEBUG
+ error = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != error ) {
+ return error;
+ }
+#endif /* NSSDEBUG */
+
+ if( CKA_TOKEN == attribute ) {
+ /*
+ * We're changing from a session object to a token object or
+ * vice-versa.
+ */
+
+ CK_ATTRIBUTE a;
+ NSSCKFWObject *newFwObject;
+ NSSCKFWObject swab;
+
+ a.type = CKA_TOKEN;
+ a.pValue = value->data;
+ a.ulValueLen = value->size;
+
+ newFwObject = nssCKFWSession_CopyObject(fwObject->fwSession, fwObject,
+ &a, 1, &error);
+ if( (NSSCKFWObject *)NULL == newFwObject ) {
+ if( CKR_OK == error ) {
+ error = CKR_GENERAL_ERROR;
+ }
+ return error;
+ }
+
+ /*
+ * Actually, I bet the locking is worse than this.. this part of
+ * the code could probably use some scrutiny and reworking.
+ */
+ error = nssCKFWMutex_Lock(fwObject->mutex);
+ if( CKR_OK != error ) {
+ nssCKFWObject_Destroy(newFwObject);
+ return error;
+ }
+
+ error = nssCKFWMutex_Lock(newFwObject->mutex);
+ if( CKR_OK != error ) {
+ nssCKFWMutex_Unlock(fwObject->mutex);
+ nssCKFWObject_Destroy(newFwObject);
+ return error;
+ }
+
+ /*
+ * Now, we have our new object, but it has a new fwObject pointer,
+ * while we have to keep the existing one. So quick swap the contents.
+ */
+ swab = *fwObject;
+ *fwObject = *newFwObject;
+ *newFwObject = swab;
+
+ /* But keep the mutexes the same */
+ swab.mutex = fwObject->mutex;
+ fwObject->mutex = newFwObject->mutex;
+ newFwObject->mutex = swab.mutex;
+
+ (void)nssCKFWMutex_Unlock(newFwObject->mutex);
+ (void)nssCKFWMutex_Unlock(fwObject->mutex);
+
+ /*
+ * Either remove or add this to the list of session objects
+ */
+
+ if( CK_FALSE == *(CK_BBOOL *)value->data ) {
+ /*
+ * New one is a session object, except since we "stole" the fwObject, it's
+ * not in the list. Add it.
+ */
+ nssCKFWSession_RegisterSessionObject(fwObject->fwSession, fwObject);
+ } else {
+ /*
+ * New one is a token object, except since we "stole" the fwObject, it's
+ * in the list. Remove it.
+ */
+ nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject);
+ }
+
+ /*
+ * Now delete the old object. Remember the names have changed.
+ */
+ nssCKFWObject_Destroy(newFwObject);
+
+ return CKR_OK;
+ } else {
+ /*
+ * An "ordinary" change.
+ */
+ if( (void *)NULL == (void *)fwObject->mdObject->SetAttribute ) {
+ /* We could fake it with copying, like above.. later */
+ return CKR_ATTRIBUTE_READ_ONLY;
+ }
+
+ error = nssCKFWMutex_Lock(fwObject->mutex);
+ if( CKR_OK != error ) {
+ return error;
+ }
+
+ error = fwObject->mdObject->SetAttribute(fwObject->mdObject, fwObject,
+ fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
+ fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
+ attribute, value);
+
+ (void)nssCKFWMutex_Unlock(fwObject->mutex);
+
+ return error;
+ }
+}
+
+/*
+ * nssCKFWObject_GetObjectSize
+ *
+ */
+NSS_IMPLEMENT CK_ULONG
+nssCKFWObject_GetObjectSize
+(
+ NSSCKFWObject *fwObject,
+ CK_RV *pError
+)
+{
+ CK_ULONG rv;
+
+#ifdef NSSDEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (CK_ULONG)0;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (CK_ULONG)0;
+ }
+#endif /* NSSDEBUG */
+
+ if( (void *)NULL == (void *)fwObject->mdObject->GetObjectSize ) {
+ *pError = CKR_INFORMATION_SENSITIVE;
+ return (CK_ULONG)0;
+ }
+
+ *pError = nssCKFWMutex_Lock(fwObject->mutex);
+ if( CKR_OK != *pError ) {
+ return (CK_ULONG)0;
+ }
+
+ rv = fwObject->mdObject->GetObjectSize(fwObject->mdObject, fwObject,
+ fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
+ fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
+ pError);
+
+ (void)nssCKFWMutex_Unlock(fwObject->mutex);
+ return rv;
+}
+
+/*
+ * NSSCKFWObject_GetMDObject
+ *
+ */
+NSS_IMPLEMENT NSSCKMDObject *
+NSSCKFWObject_GetMDObject
+(
+ NSSCKFWObject *fwObject
+)
+{
+#ifdef DEBUG
+ if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
+ return (NSSCKMDObject *)NULL;
+ }
+#endif /* DEBUG */
+
+ return nssCKFWObject_GetMDObject(fwObject);
+}
+
+/*
+ * NSSCKFWObject_GetArena
+ *
+ */
+NSS_IMPLEMENT NSSArena *
+NSSCKFWObject_GetArena
+(
+ NSSCKFWObject *fwObject,
+ CK_RV *pError
+)
+{
+#ifdef DEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (NSSArena *)NULL;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (NSSArena *)NULL;
+ }
+#endif /* DEBUG */
+
+ return nssCKFWObject_GetArena(fwObject, pError);
+}
+
+/*
+ * NSSCKFWObject_IsTokenObject
+ *
+ */
+NSS_IMPLEMENT CK_BBOOL
+NSSCKFWObject_IsTokenObject
+(
+ NSSCKFWObject *fwObject
+)
+{
+#ifdef DEBUG
+ if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
+ return CK_FALSE;
+ }
+#endif /* DEBUG */
+
+ return nssCKFWObject_IsTokenObject(fwObject);
+}
+
+/*
+ * NSSCKFWObject_GetAttributeCount
+ *
+ */
+NSS_IMPLEMENT CK_ULONG
+NSSCKFWObject_GetAttributeCount
+(
+ NSSCKFWObject *fwObject,
+ CK_RV *pError
+)
+{
+#ifdef DEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (CK_ULONG)0;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (CK_ULONG)0;
+ }
+#endif /* DEBUG */
+
+ return nssCKFWObject_GetAttributeCount(fwObject, pError);
+}
+
+/*
+ * NSSCKFWObject_GetAttributeTypes
+ *
+ */
+NSS_IMPLEMENT CK_RV
+NSSCKFWObject_GetAttributeTypes
+(
+ NSSCKFWObject *fwObject,
+ CK_ATTRIBUTE_TYPE_PTR typeArray,
+ CK_ULONG ulCount
+)
+{
+ CK_RV error = CKR_OK;
+
+#ifdef DEBUG
+ error = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != error ) {
+ return error;
+ }
+
+ if( (CK_ATTRIBUTE_TYPE_PTR)NULL == typeArray ) {
+ return CKR_ARGUMENTS_BAD;
+ }
+#endif /* DEBUG */
+
+ return nssCKFWObject_GetAttributeTypes(fwObject, typeArray, ulCount);
+}
+
+/*
+ * NSSCKFWObject_GetAttributeSize
+ *
+ */
+NSS_IMPLEMENT CK_ULONG
+NSSCKFWObject_GetAttributeSize
+(
+ NSSCKFWObject *fwObject,
+ CK_ATTRIBUTE_TYPE attribute,
+ CK_RV *pError
+)
+{
+#ifdef DEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (CK_ULONG)0;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (CK_ULONG)0;
+ }
+#endif /* DEBUG */
+
+ return nssCKFWObject_GetAttributeSize(fwObject, attribute, pError);
+}
+
+/*
+ * NSSCKFWObject_GetAttribute
+ *
+ */
+NSS_IMPLEMENT NSSItem *
+NSSCKFWObject_GetAttribute
+(
+ NSSCKFWObject *fwObject,
+ CK_ATTRIBUTE_TYPE attribute,
+ NSSItem *itemOpt,
+ NSSArena *arenaOpt,
+ CK_RV *pError
+)
+{
+#ifdef DEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (NSSItem *)NULL;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (NSSItem *)NULL;
+ }
+#endif /* DEBUG */
+
+ return nssCKFWObject_GetAttribute(fwObject, attribute, itemOpt, arenaOpt, pError);
+}
+
+/*
+ * NSSCKFWObject_GetObjectSize
+ *
+ */
+NSS_IMPLEMENT CK_ULONG
+NSSCKFWObject_GetObjectSize
+(
+ NSSCKFWObject *fwObject,
+ CK_RV *pError
+)
+{
+#ifdef DEBUG
+ if( (CK_RV *)NULL == pError ) {
+ return (CK_ULONG)0;
+ }
+
+ *pError = nssCKFWObject_verifyPointer(fwObject);
+ if( CKR_OK != *pError ) {
+ return (CK_ULONG)0;
+ }
+#endif /* DEBUG */
+
+ return nssCKFWObject_GetObjectSize(fwObject, pError);
+}
diff --git a/security/nss/lib/pk11wrap/pk11kea.c b/security/nss/lib/pk11wrap/pk11kea.c
new file mode 100644
index 000000000..6006f6032
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11kea.c
@@ -0,0 +1,224 @@
+/*
+ * 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.
+ */
+/*
+ * This file implements the Symkey wrapper and the PKCS context
+ * Interfaces.
+ */
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "nssilock.h"
+#include "secmodi.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "key.h"
+#include "secasn1.h"
+#include "sechash.h"
+#include "cert.h"
+#include "secerr.h"
+
+/*
+ * find an RSA public key on a card
+ */
+static CK_OBJECT_HANDLE
+pk11_FindRSAPubKey(PK11SlotInfo *slot)
+{
+ CK_KEY_TYPE key_type = CKK_RSA;
+ CK_OBJECT_CLASS class_type = CKO_PUBLIC_KEY;
+ CK_ATTRIBUTE theTemplate[2];
+ int template_count = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ CK_ATTRIBUTE *attrs = theTemplate;
+
+ PK11_SETATTRS(attrs,CKA_CLASS,&class_type,sizeof(class_type)); attrs++;
+ PK11_SETATTRS(attrs,CKA_KEY_TYPE,&key_type,sizeof(key_type)); attrs++;
+ template_count = attrs - theTemplate;
+ PR_ASSERT(template_count <= sizeof(theTemplate)/sizeof(CK_ATTRIBUTE));
+
+ return pk11_FindObjectByTemplate(slot,theTemplate,template_count);
+}
+
+SECKEYPublicKey *PK11_ExtractPublicKey(PK11SlotInfo *slot, KeyType keyType,
+ CK_OBJECT_HANDLE id);
+
+PK11SymKey *
+pk11_KeyExchange(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey)
+{
+ PK11SymKey *newSymKey = NULL;
+ SECStatus rv;
+ /* performance improvement can go here --- use a generated key to as a
+ * per startup wrapping key. If it exists, use it, otherwise do a full
+ * key exchange. */
+
+ /* find a common Key Exchange algorithm */
+ /* RSA */
+ if (PK11_DoesMechanism(symKey->slot, CKM_RSA_PKCS) &&
+ PK11_DoesMechanism(slot,CKM_RSA_PKCS)) {
+ CK_OBJECT_HANDLE pubKeyHandle = CK_INVALID_KEY;
+ CK_OBJECT_HANDLE privKeyHandle = CK_INVALID_KEY;
+ SECKEYPublicKey *pubKey = NULL;
+ SECKEYPrivateKey *privKey = NULL;
+ SECItem wrapData;
+
+ wrapData.data = NULL;
+
+ /* find RSA Public Key on target */
+ pubKeyHandle = pk11_FindRSAPubKey(slot);
+ if (pubKeyHandle != CK_INVALID_KEY) {
+ privKeyHandle = PK11_MatchItem(slot,pubKeyHandle,CKO_PRIVATE_KEY);
+ }
+
+ /* if no key exits, generate a key pair */
+ if (privKeyHandle == CK_INVALID_KEY) {
+ unsigned int keyLength = PK11_GetKeyLength(symKey);
+ PK11RSAGenParams rsaParams;
+
+ rsaParams.keySizeInBits =
+ ((keyLength == 0) || (keyLength > 16)) ? 512 : 256;
+ rsaParams.pe = 0x10001;
+ privKey = PK11_GenerateKeyPair(slot,CKM_RSA_PKCS_KEY_PAIR_GEN,
+ &rsaParams, &pubKey,PR_FALSE,PR_TRUE,symKey->cx);
+ } else {
+ /* if key's exist, build SECKEY data structures for them */
+ privKey = PK11_MakePrivKey(slot,nullKey, PR_TRUE, privKeyHandle,
+ symKey->cx);
+ if (privKey != NULL) {
+ pubKey = PK11_ExtractPublicKey(slot, rsaKey, pubKeyHandle);
+ if (pubKey && pubKey->pkcs11Slot) {
+ PK11_FreeSlot(pubKey->pkcs11Slot);
+ pubKey->pkcs11Slot = NULL;
+ pubKey->pkcs11ID = CK_INVALID_KEY;
+ }
+ }
+ }
+ if (privKey == NULL) goto rsa_failed;
+ if (pubKey == NULL) goto rsa_failed;
+
+ wrapData.len = SECKEY_PublicKeyStrength(pubKey);
+ if (!wrapData.len) goto rsa_failed;
+ wrapData.data = PORT_Alloc(wrapData.len);
+ if (wrapData.data == NULL) goto rsa_failed;
+
+ /* now wrap the keys in and out */
+ rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, pubKey, symKey, &wrapData);
+ if (rv == SECSuccess) {
+ newSymKey = PK11_PubUnwrapSymKey(privKey,&wrapData,type,operation,
+ symKey->size);
+ }
+rsa_failed:
+ if (wrapData.data != NULL) PORT_Free(wrapData.data);
+ if (privKey != NULL) SECKEY_DestroyPrivateKey(privKey);
+ if (pubKey != NULL) SECKEY_DestroyPublicKey(pubKey);
+
+ return newSymKey;
+ }
+ /* KEA */
+ if (PK11_DoesMechanism(symKey->slot, CKM_KEA_KEY_DERIVE) &&
+ PK11_DoesMechanism(slot,CKM_KEA_KEY_DERIVE)) {
+ CERTCertificate *certSource = NULL;
+ CERTCertificate *certTarget = NULL;
+ SECKEYPublicKey *pubKeySource = NULL;
+ SECKEYPublicKey *pubKeyTarget = NULL;
+ SECKEYPrivateKey *privKeySource = NULL;
+ SECKEYPrivateKey *privKeyTarget = NULL;
+ PK11SymKey *tekSource = NULL;
+ PK11SymKey *tekTarget = NULL;
+ SECItem Ra,wrap;
+
+ /* can only exchange skipjack keys */
+ if (type != CKM_SKIPJACK_CBC64) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ goto kea_failed;
+ }
+
+ /* find a pair of certs we can use */
+ rv = PK11_GetKEAMatchedCerts(symKey->slot,slot,&certSource,&certTarget);
+ if (rv != SECSuccess) goto kea_failed;
+
+ /* get all the key pairs */
+ pubKeyTarget = CERT_ExtractPublicKey(certSource);
+ pubKeySource = CERT_ExtractPublicKey(certTarget);
+ privKeySource =
+ PK11_FindKeyByDERCert(symKey->slot,certSource,symKey->cx);
+ privKeyTarget =
+ PK11_FindKeyByDERCert(slot,certTarget,symKey->cx);
+
+ if ((pubKeySource == NULL) || (pubKeyTarget == NULL) ||
+ (privKeySource == NULL) || (privKeyTarget == NULL)) goto kea_failed;
+
+ /* generate the wrapping TEK's */
+ Ra.data = (unsigned char*)PORT_Alloc(128 /* FORTEZZA RA MAGIC */);
+ Ra.len = 128;
+ if (Ra.data == NULL) goto kea_failed;
+
+ tekSource = PK11_PubDerive(privKeySource,pubKeyTarget,PR_TRUE,&Ra,NULL,
+ CKM_SKIPJACK_WRAP, CKM_KEA_KEY_DERIVE,CKA_WRAP,0,symKey->cx);
+ tekTarget = PK11_PubDerive(privKeyTarget,pubKeySource,PR_FALSE,&Ra,NULL,
+ CKM_SKIPJACK_WRAP, CKM_KEA_KEY_DERIVE,CKA_WRAP,0,symKey->cx);
+ PORT_Free(Ra.data);
+
+ if ((tekSource == NULL) || (tekTarget == NULL)) { goto kea_failed; }
+
+ /* wrap the key out of Source into target */
+ wrap.data = (unsigned char*)PORT_Alloc(12); /* MAGIC SKIPJACK LEN */
+ wrap.len = 12;
+
+ /* paranoia to prevent infinite recursion on bugs */
+ PORT_Assert(tekSource->slot == symKey->slot);
+ if (tekSource->slot != symKey->slot) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ goto kea_failed;
+ }
+
+ rv = PK11_WrapSymKey(CKM_SKIPJACK_WRAP,NULL,tekSource,symKey,&wrap);
+ if (rv == SECSuccess) {
+ newSymKey = PK11_UnwrapSymKey(tekTarget, CKM_SKIPJACK_WRAP, NULL,
+ &wrap, type, operation, symKey->size);
+ }
+ PORT_Free(wrap.data);
+kea_failed:
+ if (certSource == NULL) CERT_DestroyCertificate(certSource);
+ if (certTarget == NULL) CERT_DestroyCertificate(certTarget);
+ if (pubKeySource == NULL) SECKEY_DestroyPublicKey(pubKeySource);
+ if (pubKeyTarget == NULL) SECKEY_DestroyPublicKey(pubKeyTarget);
+ if (privKeySource == NULL) SECKEY_DestroyPrivateKey(privKeySource);
+ if (privKeyTarget == NULL) SECKEY_DestroyPrivateKey(privKeyTarget);
+ if (tekSource == NULL) PK11_FreeSymKey(tekSource);
+ if (tekTarget == NULL) PK11_FreeSymKey(tekTarget);
+ return newSymKey;
+ }
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return NULL;
+}
+
diff --git a/security/nss/lib/pkcs12/p12d.c b/security/nss/lib/pkcs12/p12d.c
new file mode 100644
index 000000000..9abae13c8
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12d.c
@@ -0,0 +1,3230 @@
+/*
+ * 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;
+};
+
+
+/* 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;
+}
+
+/* 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.
+ *
+ * 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;
+ }
+ 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;
+ PBEBitGenContext *pbeCtxt = NULL;
+ SECItem *hmacKey = NULL, hmacRes;
+ unsigned char buf[IN_BUF_LEN];
+ unsigned int bufLen;
+ int iteration;
+ PK11Context *pk11cx;
+ SECOidTag algtag;
+ SECItem ignore = {0};
+
+ if(!p12dcx || p12dcx->error) {
+ return SECFailure;
+ }
+
+ /* generate hmac key */
+ if(p12dcx->macData.iter.data) {
+ iteration = (int)DER_GetInteger(&p12dcx->macData.iter);
+ } else {
+ iteration = 1;
+ }
+ pbeCtxt = PBE_CreateContext(SECOID_GetAlgorithmTag(
+ &p12dcx->macData.safeMac.digestAlgorithm),
+ pbeBitGenIntegrityKey, p12dcx->pwitem,
+ &p12dcx->macData.macSalt, 160, iteration);
+ if(!pbeCtxt) {
+ return SECFailure;
+ }
+ hmacKey = PBE_GenerateBits(pbeCtxt);
+ PBE_DestroyContext(pbeCtxt);
+ pbeCtxt = NULL;
+ if(!hmacKey) {
+ return SECFailure;
+ }
+
+ /* init hmac */
+ algtag = SECOID_GetAlgorithmTag(&p12dcx->macData.safeMac.digestAlgorithm);
+ pk11cx = PK11_CreateContextByRawKey(NULL,
+ sec_pkcs12_algtag_to_mech(algtag),
+ PK11_OriginDerive, CKA_SIGN,
+ hmacKey, &ignore, NULL);
+ SECITEM_ZfreeItem(hmacKey, PR_TRUE);
+ hmacKey = NULL;
+ 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);
+ }
+
+ if(hmacKey) {
+ SECITEM_ZfreeItem(hmacKey, 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)
+{
+ void *freedCtxt = NULL;
+
+ 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_NewTempCertificate(CERT_GetDefaultCertDB(), derCert, NULL,
+ PR_FALSE, PR_TRUE);
+ 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_IsInternal(cert->slot)) {
+ if(CERT_TraversePermCertsForSubject(CERT_GetDefaultCertDB(),
+ &tempCert->derSubject, gatherNicknames,
+ nickArg) != SECSuccess) {
+ returnDn = NULL;
+ goto loser;
+ }
+ }
+
+ 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 */
+ if(PK11_IsInternal(slot)) {
+ CERT_TraversePermCertsForNickname(CERT_GetDefaultCertDB(),
+ (char *)nickname->data,
+ countCertificate, (void *)&nCerts);
+ }
+
+ 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_NewTempCertificate(CERT_GetDefaultCertDB(),
+ &cert->safeBagContent.certBag->value.x509Cert,
+ NULL, PR_FALSE, PR_TRUE);
+ 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;
+ } else {
+ cert->noInstall = 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_NewTempCertificate(CERT_GetDefaultCertDB(),
+ &(cert->safeBagContent.certBag->value.x509Cert),
+ NULL, PR_FALSE, PR_TRUE);
+ 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;
+ PK11SlotInfo *slot = NULL;
+ PRBool removed = PR_FALSE;
+
+ if(!cert) {
+ return SECFailure;
+ }
+
+ PORT_Assert(cert->removeExisting);
+
+ cert->removeExisting = PR_FALSE;
+ derCert = &cert->safeBagContent.certBag->value.x509Cert;
+ tempCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert,
+ NULL, PR_FALSE, PR_TRUE);
+ if(!tempCert) {
+ return SECFailure;
+ }
+
+ certObj = PK11_FindCertInSlot(cert->slot, tempCert, wincx);
+ CERT_DestroyCertificate(tempCert);
+ tempCert = NULL;
+
+ if(certObj != CK_INVALID_KEY) {
+ 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_NewTempCertificate(CERT_GetDefaultCertDB(),
+ derCert, NULL, PR_FALSE, PR_TRUE);
+ 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_NewTempCertificate(CERT_GetDefaultCertDB(),
+ derCert, NULL, PR_FALSE, PR_TRUE);
+
+ 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_NewTempCertificate(CERT_GetDefaultCertDB(),
+ &certBag->safeBagContent.certBag->value.x509Cert,
+ NULL, PR_FALSE, PR_FALSE);
+ 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/softoken/keydb.c b/security/nss/lib/softoken/keydb.c
new file mode 100644
index 000000000..90da85ac0
--- /dev/null
+++ b/security/nss/lib/softoken/keydb.c
@@ -0,0 +1,2482 @@
+/*
+ * 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.
+ *
+ * Private Key Database code
+ *
+ * $Id$
+ */
+
+#include "keylow.h"
+#include "keydbt.h"
+#include "seccomon.h"
+#include "sechash.h"
+#include "secder.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "blapi.h"
+#include "secitem.h"
+#include "cert.h"
+#include "mcom_db.h"
+#include "secpkcs5.h"
+#include "secerr.h"
+
+#include "private.h"
+
+/*
+ * Record keys for keydb
+ */
+#define SALT_STRING "global-salt"
+#define VERSION_STRING "Version"
+#define KEYDB_PW_CHECK_STRING "password-check"
+#define KEYDB_PW_CHECK_LEN 14
+#define KEYDB_FAKE_PW_CHECK_STRING "fake-password-check"
+#define KEYDB_FAKE_PW_CHECK_LEN 19
+
+/* Size of the global salt for key database */
+#define SALT_LENGTH 16
+
+/* ASN1 Templates for new decoder/encoder */
+/*
+ * Attribute value for PKCS8 entries (static?)
+ */
+const SEC_ASN1Template SECKEY_AttributeTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SECKEYAttribute) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SECKEYAttribute, attrType) },
+ { SEC_ASN1_SET_OF, offsetof(SECKEYAttribute, attrValue),
+ SEC_AnyTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_SetOfAttributeTemplate[] = {
+ { SEC_ASN1_SET_OF, 0, SECKEY_AttributeTemplate },
+};
+
+const SEC_ASN1Template SECKEY_PrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SECKEYPrivateKeyInfo) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SECKEYPrivateKeyInfo,version) },
+ { SEC_ASN1_INLINE,
+ offsetof(SECKEYPrivateKeyInfo,algorithm),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SECKEYPrivateKeyInfo,privateKey) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SECKEYPrivateKeyInfo,attributes),
+ SECKEY_SetOfAttributeTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_PointerToPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SECKEY_PrivateKeyInfoTemplate }
+};
+
+const SEC_ASN1Template SECKEY_EncryptedPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SECKEYEncryptedPrivateKeyInfo) },
+ { SEC_ASN1_INLINE,
+ offsetof(SECKEYEncryptedPrivateKeyInfo,algorithm),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SECKEYEncryptedPrivateKeyInfo,encryptedData) },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_PointerToEncryptedPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SECKEY_EncryptedPrivateKeyInfoTemplate }
+};
+
+/* ====== Default key databse encryption algorithm ====== */
+
+static SECOidTag defaultKeyDBAlg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
+
+/*
+ * Default algorithm for encrypting data in the key database
+ */
+SECOidTag
+SECKEY_GetDefaultKeyDBAlg(void)
+{
+ return(defaultKeyDBAlg);
+}
+
+void
+SECKEY_SetDefaultKeyDBAlg(SECOidTag alg)
+{
+ defaultKeyDBAlg = alg;
+
+ return;
+}
+
+static void
+sec_destroy_dbkey(SECKEYDBKey *dbkey)
+{
+ if ( dbkey && dbkey->arena ) {
+ PORT_FreeArena(dbkey->arena, PR_FALSE);
+ }
+}
+
+static void
+free_dbt(DBT *dbt)
+{
+ if ( dbt ) {
+ PORT_Free(dbt->data);
+ PORT_Free(dbt);
+ }
+
+ return;
+}
+
+/*
+ * format of key database entries for version 3 of database:
+ * byte offset field
+ * ----------- -----
+ * 0 version
+ * 1 salt-len
+ * 2 nn-len
+ * 3.. salt-data
+ * ... nickname
+ * ... encrypted-key-data
+ */
+static DBT *
+encode_dbkey(SECKEYDBKey *dbkey)
+{
+ DBT *bufitem = NULL;
+ unsigned char *buf;
+ int nnlen;
+ char *nn;
+
+ bufitem = (DBT *)PORT_ZAlloc(sizeof(DBT));
+ if ( bufitem == NULL ) {
+ goto loser;
+ }
+
+ if ( dbkey->nickname ) {
+ nn = dbkey->nickname;
+ nnlen = PORT_Strlen(nn) + 1;
+ } else {
+ nn = "";
+ nnlen = 1;
+ }
+
+ /* compute the length of the record */
+ /* 1 + 1 + 1 == version number header + salt length + nn len */
+ bufitem->size = dbkey->salt.len + nnlen + dbkey->derPK.len + 1 + 1 + 1;
+
+ bufitem->data = (void *)PORT_ZAlloc(bufitem->size);
+ if ( bufitem->data == NULL ) {
+ goto loser;
+ }
+
+ buf = (unsigned char *)bufitem->data;
+
+ /* set version number */
+ buf[0] = PRIVATE_KEY_DB_FILE_VERSION;
+
+ /* set length of salt */
+ PORT_Assert(dbkey->salt.len < 256);
+ buf[1] = dbkey->salt.len;
+
+ /* set length of nickname */
+ PORT_Assert(nnlen < 256);
+ buf[2] = nnlen;
+
+ /* copy salt */
+ PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len);
+
+ /* copy nickname */
+ PORT_Memcpy(&buf[3 + dbkey->salt.len], nn, nnlen);
+
+ /* copy encrypted key */
+ PORT_Memcpy(&buf[3 + dbkey->salt.len + nnlen], dbkey->derPK.data,
+ dbkey->derPK.len);
+
+ return(bufitem);
+
+loser:
+ if ( bufitem ) {
+ free_dbt(bufitem);
+ }
+
+ return(NULL);
+}
+
+static SECKEYDBKey *
+decode_dbkey(DBT *bufitem, int expectedVersion)
+{
+ SECKEYDBKey *dbkey;
+ PLArenaPool *arena = NULL;
+ unsigned char *buf;
+ int version;
+ int keyoff;
+ int nnlen;
+ int saltoff;
+
+ buf = (unsigned char *)bufitem->data;
+
+ version = buf[0];
+
+ if ( version != expectedVersion ) {
+ goto loser;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ dbkey = (SECKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYDBKey));
+ if ( dbkey == NULL ) {
+ goto loser;
+ }
+
+ dbkey->arena = arena;
+ dbkey->salt.data = NULL;
+ dbkey->derPK.data = NULL;
+
+ dbkey->salt.len = buf[1];
+ dbkey->salt.data = (unsigned char *)PORT_ArenaZAlloc(arena, dbkey->salt.len);
+ if ( dbkey->salt.data == NULL ) {
+ goto loser;
+ }
+
+ saltoff = 2;
+ keyoff = 2 + dbkey->salt.len;
+
+ if ( expectedVersion == PRIVATE_KEY_DB_FILE_VERSION ) {
+ nnlen = buf[2];
+ if ( nnlen ) {
+ dbkey->nickname = (char *)PORT_ArenaZAlloc(arena, nnlen + 1);
+ if ( dbkey->nickname ) {
+ PORT_Memcpy(dbkey->nickname, &buf[keyoff+1], nnlen);
+ }
+ }
+ keyoff += ( nnlen + 1 );
+ saltoff = 3;
+ }
+
+ PORT_Memcpy(dbkey->salt.data, &buf[saltoff], dbkey->salt.len);
+
+ dbkey->derPK.len = bufitem->size - keyoff;
+ dbkey->derPK.data = (unsigned char *)PORT_ArenaZAlloc(arena,dbkey->derPK.len);
+ if ( dbkey->derPK.data == NULL ) {
+ goto loser;
+ }
+
+ PORT_Memcpy(dbkey->derPK.data, &buf[keyoff], dbkey->derPK.len);
+
+ return(dbkey);
+
+loser:
+
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+static SECKEYDBKey *
+get_dbkey(SECKEYKeyDBHandle *handle, DBT *index)
+{
+ SECKEYDBKey *dbkey;
+ DBT entry;
+ int ret;
+
+ /* get it from the database */
+ ret = (* handle->db->get)(handle->db, index, &entry, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return NULL;
+ }
+
+ /* set up dbkey struct */
+
+ dbkey = decode_dbkey(&entry, PRIVATE_KEY_DB_FILE_VERSION);
+
+ return(dbkey);
+}
+
+static SECStatus
+put_dbkey(SECKEYKeyDBHandle *handle, DBT *index, SECKEYDBKey *dbkey, PRBool update)
+{
+ DBT *keydata = NULL;
+ int status;
+
+ keydata = encode_dbkey(dbkey);
+ if ( keydata == NULL ) {
+ goto loser;
+ }
+
+ /* put it in the database */
+ if ( update ) {
+ status = (* handle->db->put)(handle->db, index, keydata, 0);
+ } else {
+ status = (* handle->db->put)(handle->db, index, keydata,
+ R_NOOVERWRITE);
+ }
+
+ if ( status ) {
+ goto loser;
+ }
+
+ /* sync the database */
+ status = (* handle->db->sync)(handle->db, 0);
+ if ( status ) {
+ goto loser;
+ }
+
+ free_dbt(keydata);
+ return(SECSuccess);
+
+loser:
+ if ( keydata ) {
+ free_dbt(keydata);
+ }
+
+ return(SECFailure);
+}
+
+SECStatus
+SECKEY_TraverseKeys(SECKEYKeyDBHandle *handle,
+ SECStatus (* keyfunc)(DBT *k, DBT *d, void *pdata),
+ void *udata )
+{
+ DBT data;
+ DBT key;
+ SECStatus status;
+ int ret;
+
+ if (handle == NULL) {
+ return(SECFailure);
+ }
+
+ ret = (* handle->db->seq)(handle->db, &key, &data, R_FIRST);
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ /* skip version record */
+ if ( data.size > 1 ) {
+ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
+ if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
+ continue;
+ }
+ }
+
+ /* skip password check */
+ if ( key.size == KEYDB_PW_CHECK_LEN ) {
+ if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING,
+ KEYDB_PW_CHECK_LEN) == 0 ) {
+ continue;
+ }
+ }
+
+ status = (* keyfunc)(&key, &data, udata);
+ if (status != SECSuccess) {
+ return(status);
+ }
+ }
+ } while ( (* handle->db->seq)(handle->db, &key, &data, R_NEXT) == 0 );
+
+ return(SECSuccess);
+}
+
+typedef struct keyNode {
+ struct keyNode *next;
+ DBT key;
+} keyNode;
+
+typedef struct {
+ PLArenaPool *arena;
+ keyNode *head;
+} keyList;
+
+static SECStatus
+sec_add_key_to_list(DBT *key, DBT *data, void *arg)
+{
+ keyList *keylist;
+ keyNode *node;
+ void *keydata;
+
+ keylist = (keyList *)arg;
+
+ /* allocate the node struct */
+ node = (keyNode*)PORT_ArenaZAlloc(keylist->arena, sizeof(keyNode));
+ if ( node == NULL ) {
+ return(SECFailure);
+ }
+
+ /* allocate room for key data */
+ keydata = PORT_ArenaZAlloc(keylist->arena, key->size);
+ if ( keydata == NULL ) {
+ return(SECFailure);
+ }
+
+ /* link node into list */
+ node->next = keylist->head;
+ keylist->head = node;
+
+ /* copy key into node */
+ PORT_Memcpy(keydata, key->data, key->size);
+ node->key.size = key->size;
+ node->key.data = keydata;
+
+ return(SECSuccess);
+}
+
+static SECItem *
+decodeKeyDBGlobalSalt(DBT *saltData)
+{
+ SECItem *saltitem;
+
+ saltitem = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if ( saltitem == NULL ) {
+ return(NULL);
+ }
+
+ saltitem->data = (unsigned char *)PORT_ZAlloc(saltData->size);
+ if ( saltitem->data == NULL ) {
+ PORT_Free(saltitem);
+ return(NULL);
+ }
+
+ saltitem->len = saltData->size;
+ PORT_Memcpy(saltitem->data, saltData->data, saltitem->len);
+
+ return(saltitem);
+}
+
+static SECItem *
+GetKeyDBGlobalSalt(SECKEYKeyDBHandle *handle)
+{
+ DBT saltKey;
+ DBT saltData;
+ int ret;
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ ret = (* handle->db->get)(handle->db, &saltKey, &saltData, 0);
+ if ( ret ) {
+ return(NULL);
+ }
+
+ return(decodeKeyDBGlobalSalt(&saltData));
+}
+
+static SECStatus
+makeGlobalVersion(SECKEYKeyDBHandle *handle)
+{
+ unsigned char version;
+ DBT versionData;
+ DBT versionKey;
+ int status;
+
+ version = PRIVATE_KEY_DB_FILE_VERSION;
+ versionData.data = &version;
+ versionData.size = 1;
+ versionKey.data = VERSION_STRING;
+ versionKey.size = sizeof(VERSION_STRING)-1;
+
+ /* put version string into the database now */
+ status = (* handle->db->put)(handle->db, &versionKey, &versionData, 0);
+ if ( status ) {
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+
+static SECStatus
+makeGlobalSalt(SECKEYKeyDBHandle *handle)
+{
+ DBT saltKey;
+ DBT saltData;
+ unsigned char saltbuf[16];
+ int status;
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ saltData.data = (void *)saltbuf;
+ saltData.size = sizeof(saltbuf);
+ RNG_GenerateGlobalRandomBytes(saltbuf, sizeof(saltbuf));
+
+ /* put global salt into the database now */
+ status = (* handle->db->put)( handle->db, &saltKey, &saltData, 0);
+ if ( status ) {
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+static char *
+keyDBFilenameCallback(void *arg, int dbVersion)
+{
+ return(PORT_Strdup((char *)arg));
+}
+
+SECKEYKeyDBHandle *
+SECKEY_OpenKeyDBFilename(char *dbname, PRBool readOnly)
+{
+ return(SECKEY_OpenKeyDB(readOnly, keyDBFilenameCallback,
+ (void *)dbname));
+}
+
+SECKEYKeyDBHandle *
+SECKEY_OpenKeyDB(PRBool readOnly, SECKEYDBNameFunc namecb, void *cbarg)
+{
+ SECKEYKeyDBHandle *handle;
+ DBT versionKey;
+ DBT versionData;
+ int ret;
+ SECStatus rv;
+ int openflags;
+ char *dbname = NULL;
+ PRBool updated = PR_FALSE;
+
+ handle = (SECKEYKeyDBHandle *)PORT_ZAlloc (sizeof(SECKEYKeyDBHandle));
+ if (handle == NULL) {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ versionKey.data = VERSION_STRING;
+ versionKey.size = sizeof(VERSION_STRING)-1;
+
+ if ( readOnly ) {
+ openflags = O_RDONLY;
+ } else {
+ openflags = O_RDWR;
+ }
+
+ dbname = (*namecb)(cbarg, PRIVATE_KEY_DB_FILE_VERSION);
+ if ( dbname == NULL ) {
+ goto loser;
+ }
+
+ handle->db = dbopen( dbname, openflags, 0600, DB_HASH, 0 );
+
+ /* check for correct version number */
+ if (handle->db != NULL) {
+ /* lookup version string in database */
+ ret = (* handle->db->get)( handle->db, &versionKey, &versionData, 0 );
+
+ /* error accessing the database */
+ if ( ret < 0 ) {
+ goto loser;
+ }
+
+ if ( ret == 1 ) {
+ /* no version number record, reset the database */
+ (* handle->db->close)( handle->db );
+ handle->db = NULL;
+
+ goto newdb;
+
+ }
+
+ handle->version = *( (unsigned char *)versionData.data);
+
+ if (handle->version != PRIVATE_KEY_DB_FILE_VERSION ) {
+ /* bogus version number record, reset the database */
+ (* handle->db->close)( handle->db );
+ handle->db = NULL;
+
+ goto newdb;
+ }
+
+ }
+
+newdb:
+
+ /* if first open fails, try to create a new DB */
+ if ( handle->db == NULL ) {
+ if ( readOnly ) {
+ goto loser;
+ }
+
+ handle->db = dbopen( dbname,
+ O_RDWR | O_CREAT | O_TRUNC, 0600, DB_HASH, 0 );
+
+ PORT_Free( dbname );
+ dbname = NULL;
+
+ /* if create fails then we lose */
+ if ( handle->db == NULL ) {
+ goto loser;
+ }
+
+ rv = makeGlobalVersion(handle);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /*
+ * try to update from v2 db
+ */
+ dbname = (*namecb)(cbarg, 2);
+ if ( dbname != NULL ) {
+ handle->updatedb = dbopen( dbname, O_RDONLY, 0600, DB_HASH, 0 );
+
+ if ( handle->updatedb ) {
+
+
+ /*
+ * Try to update the db using a null password. If the db
+ * doesn't have a password, then this will work. If it does
+ * have a password, then this will fail and we will do the
+ * update later
+ */
+ rv = SECKEY_UpdateKeyDBPass1(handle);
+ if ( rv == SECSuccess ) {
+ updated = PR_TRUE;
+ }
+ }
+
+ PORT_Free( dbname );
+ dbname = NULL;
+ }
+
+ /* we are using the old salt if we updated from an old db */
+ if ( ! updated ) {
+ rv = makeGlobalSalt(handle);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ /* sync the database */
+ ret = (* handle->db->sync)(handle->db, 0);
+ if ( ret ) {
+ goto loser;
+ }
+ }
+
+ handle->global_salt = GetKeyDBGlobalSalt(handle);
+ if ( dbname )
+ PORT_Free( dbname );
+ return handle;
+
+loser:
+
+ if ( dbname )
+ PORT_Free( dbname );
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+
+ if ( handle->db ) {
+ (* handle->db->close)(handle->db);
+ }
+ if ( handle->updatedb ) {
+ (* handle->updatedb->close)(handle->updatedb);
+ }
+ PORT_Free(handle);
+ return NULL;
+}
+
+/*
+ * Close the database
+ */
+void
+SECKEY_CloseKeyDB(SECKEYKeyDBHandle *handle)
+{
+ if (handle != NULL) {
+ if (handle == SECKEY_GetDefaultKeyDB()) {
+ SECKEY_SetDefaultKeyDB(NULL);
+ }
+ if (handle->db != NULL) {
+ (* handle->db->close)(handle->db);
+ }
+ PORT_Free(handle);
+ }
+}
+
+/* Get the key database version */
+int
+SECKEY_GetKeyDBVersion(SECKEYKeyDBHandle *handle)
+{
+ PORT_Assert(handle != NULL);
+
+ return handle->version;
+}
+
+/*
+ * Allow use of default key database, so that apps (such as mozilla) do
+ * not have to pass the handle all over the place.
+ */
+
+static SECKEYKeyDBHandle *sec_default_key_db = NULL;
+
+void
+SECKEY_SetDefaultKeyDB(SECKEYKeyDBHandle *handle)
+{
+ sec_default_key_db = handle;
+}
+
+SECKEYKeyDBHandle *
+SECKEY_GetDefaultKeyDB(void)
+{
+ return sec_default_key_db;
+}
+
+/*
+ * Delete a private key that was stored in the database
+ */
+SECStatus
+SECKEY_DeleteKey(SECKEYKeyDBHandle *handle, SECItem *pubkey)
+{
+ DBT namekey;
+ int ret;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ /* set up db key and data */
+ namekey.data = pubkey->data;
+ namekey.size = pubkey->len;
+
+ /* delete it from the database */
+ ret = (* handle->db->del)(handle->db, &namekey, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ /* sync the database */
+ ret = (* handle->db->sync)(handle->db, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+/*
+ * Store a key in the database, indexed by its public key modulus.(value!)
+ */
+SECStatus
+SECKEY_StoreKeyByPublicKey(SECKEYKeyDBHandle *handle,
+ SECKEYLowPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SECKEYGetPasswordKey f, void *arg)
+{
+ return SECKEY_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData, nickname,
+ f, arg, SECKEY_GetDefaultKeyDBAlg());
+}
+
+/* see if the public key for this cert is in the database filed
+ * by modulus
+ */
+SECStatus
+SECKEY_KeyForCertExists(SECKEYKeyDBHandle *handle, CERTCertificate *cert)
+{
+ SECKEYPublicKey *pubkey = NULL;
+ DBT namekey;
+ DBT dummy;
+ int status;
+
+ /* get cert's public key */
+ pubkey = CERT_ExtractPublicKey(cert);
+ if ( pubkey == NULL ) {
+ return SECFailure;
+ }
+
+ /* TNH - make key from SECKEYPublicKey */
+ switch (pubkey->keyType) {
+ case rsaKey:
+ namekey.data = pubkey->u.rsa.modulus.data;
+ namekey.size = pubkey->u.rsa.modulus.len;
+ break;
+ case dsaKey:
+ namekey.data = pubkey->u.dsa.publicValue.data;
+ namekey.size = pubkey->u.dsa.publicValue.len;
+ break;
+ case dhKey:
+ namekey.data = pubkey->u.dh.publicValue.data;
+ namekey.size = pubkey->u.dh.publicValue.len;
+ break;
+ default:
+ /* XXX We don't do Fortezza or DH yet. */
+ return SECFailure;
+ }
+
+ status = (* handle->db->get)(handle->db, &namekey, &dummy, 0);
+ SECKEY_DestroyPublicKey(pubkey);
+ if ( status ) {
+ /* TNH - should this really set an error? */
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * find the private key for a cert
+ */
+SECKEYLowPrivateKey *
+SECKEY_FindKeyByCert(SECKEYKeyDBHandle *handle, CERTCertificate *cert,
+ SECKEYGetPasswordKey f, void *arg)
+{
+ SECKEYPublicKey *pubkey = NULL;
+ SECItem *keyItem;
+ SECKEYLowPrivateKey *privKey = NULL;
+
+ /* get cert's public key */
+ pubkey = CERT_ExtractPublicKey(cert);
+ if ( !pubkey ) {
+ goto loser;
+ }
+
+ /* TNH - make record key from SECKEYPublicKey (again) */
+ switch (pubkey->keyType) {
+ case rsaKey:
+ keyItem = &pubkey->u.rsa.modulus;
+ break;
+ case dsaKey:
+ keyItem = &pubkey->u.dsa.publicValue;
+ break;
+ case dhKey:
+ keyItem = &pubkey->u.dh.publicValue;
+ break;
+ /* fortezza an NULL keys are not stored in the data base */
+ case fortezzaKey:
+ case nullKey:
+ goto loser;
+ }
+
+ privKey = SECKEY_FindKeyByPublicKey(handle, keyItem, f, arg);
+
+ /* success falls through */
+loser:
+ SECKEY_DestroyPublicKey(pubkey);
+ return(privKey);
+}
+
+/*
+ * check to see if the user has a password
+ */
+SECStatus
+SECKEY_HasKeyDBPassword(SECKEYKeyDBHandle *handle)
+{
+ DBT checkkey, checkdata;
+ int ret;
+
+ if (handle == NULL) {
+ return(SECFailure);
+ }
+
+ checkkey.data = KEYDB_PW_CHECK_STRING;
+ checkkey.size = KEYDB_PW_CHECK_LEN;
+
+ ret = (* handle->db->get)(handle->db, &checkkey, &checkdata, 0 );
+ if ( ret ) {
+ /* see if this was an updated DB first */
+ checkkey.data = KEYDB_FAKE_PW_CHECK_STRING;
+ checkkey.size = KEYDB_FAKE_PW_CHECK_LEN;
+ ret = (* handle->db->get)(handle->db, &checkkey, &checkdata, 0 );
+ if ( ret ) {
+ return(SECFailure);
+ }
+ }
+
+ return(SECSuccess);
+}
+
+/*
+ * Set up the password checker in the key database.
+ * This is done by encrypting a known plaintext with the user's key.
+ */
+SECStatus
+SECKEY_SetKeyDBPassword(SECKEYKeyDBHandle *handle, SECItem *pwitem)
+{
+ return SECKEY_SetKeyDBPasswordAlg(handle, pwitem,
+ SECKEY_GetDefaultKeyDBAlg());
+}
+
+/*
+ * Re-encrypt the entire key database with a new password.
+ * NOTE: This really should create a new database rather than doing it
+ * in place in the original
+ */
+SECStatus
+SECKEY_ChangeKeyDBPassword(SECKEYKeyDBHandle *handle,
+ SECItem *oldpwitem, SECItem *newpwitem)
+{
+ return SECKEY_ChangeKeyDBPasswordAlg(handle, oldpwitem, newpwitem,
+ SECKEY_GetDefaultKeyDBAlg());
+}
+
+static SECStatus
+HashPassword(unsigned char *hashresult, char *pw, SECItem *salt)
+{
+ SHA1Context *cx;
+ unsigned int outlen;
+ cx = SHA1_NewContext();
+ if ( cx == NULL ) {
+ return(SECFailure);
+ }
+
+ SHA1_Begin(cx);
+ if ( ( salt != NULL ) && ( salt->data != NULL ) ) {
+ SHA1_Update(cx, salt->data, salt->len);
+ }
+
+ SHA1_Update(cx, (unsigned char *)pw, PORT_Strlen(pw));
+ SHA1_End(cx, hashresult, &outlen, SHA1_LENGTH);
+
+ SHA1_DestroyContext(cx, PR_TRUE);
+
+ return(SECSuccess);
+}
+
+SECItem *
+SECKEY_HashPassword(char *pw, SECItem *salt)
+{
+ SECItem *pwitem;
+ SECStatus rv;
+
+ pwitem = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if ( pwitem == NULL ) {
+ return(NULL);
+ }
+ pwitem->len = SHA1_LENGTH;
+ pwitem->data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH);
+ if ( pwitem->data == NULL ) {
+ PORT_Free(pwitem);
+ return(NULL);
+ }
+ if ( pw ) {
+ rv = HashPassword(pwitem->data, pw, salt);
+ if ( rv != SECSuccess ) {
+ SECITEM_ZfreeItem(pwitem, PR_TRUE);
+ return(NULL);
+ }
+ }
+
+ return(pwitem);
+}
+
+/* Derive the actual password value for the database from a pw string */
+SECItem *
+SECKEY_DeriveKeyDBPassword(SECKEYKeyDBHandle *keydb, char *pw)
+{
+ PORT_Assert(keydb != NULL);
+ PORT_Assert(pw != NULL);
+ if (keydb == NULL || pw == NULL) return(NULL);
+
+ return SECKEY_HashPassword(pw, keydb->global_salt);
+}
+
+#if 0
+/* Appears obsolete - TNH */
+/* get the algorithm with which a private key
+ * is encrypted.
+ */
+SECOidTag
+seckey_get_private_key_algorithm(SECKEYKeyDBHandle *keydb, DBT *index)
+{
+ SECKEYDBKey *dbkey = NULL;
+ SECOidTag algorithm = SEC_OID_UNKNOWN;
+ SECKEYEncryptedPrivateKeyInfo *epki = NULL;
+ PLArenaPool *poolp = NULL;
+ SECStatus rv;
+
+ poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if(poolp == NULL)
+ return (SECOidTag)SECFailure; /* TNH - this is bad */
+
+ dbkey = get_dbkey(keydb, index);
+ if(dbkey == NULL)
+ return (SECOidTag)SECFailure;
+
+ epki = (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(poolp,
+ sizeof(SECKEYEncryptedPrivateKeyInfo));
+ if(epki == NULL)
+ goto loser;
+ rv = SEC_ASN1DecodeItem(poolp, epki,
+ SECKEY_EncryptedPrivateKeyInfoTemplate, &dbkey->derPK);
+ if(rv == SECFailure)
+ goto loser;
+
+ algorithm = SECOID_GetAlgorithmTag(&epki->algorithm);
+
+ /* let success fall through */
+loser:
+ if(poolp != NULL)
+ PORT_FreeArena(poolp, PR_TRUE);\
+ if(dbkey != NULL)
+ sec_destroy_dbkey(dbkey);
+
+ return algorithm;
+}
+#endif
+
+/*
+ * Derive an RC4 key from a password key and a salt. This
+ * was the method to used to encrypt keys in the version 2?
+ * database
+ */
+SECItem *
+seckey_create_rc4_key(SECItem *pwitem, SECItem *salt)
+{
+ MD5Context *md5 = NULL;
+ unsigned int part;
+ SECStatus rv = SECFailure;
+ SECItem *key = NULL;
+
+ key = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(key != NULL)
+ {
+ key->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) *
+ MD5_LENGTH);
+ key->len = MD5_LENGTH;
+ if(key->data != NULL)
+ {
+ md5 = MD5_NewContext();
+ if ( md5 != NULL )
+ {
+ MD5_Begin(md5);
+ MD5_Update(md5, salt->data, salt->len);
+ MD5_Update(md5, pwitem->data, pwitem->len);
+ MD5_End(md5, key->data, &part, MD5_LENGTH);
+ MD5_DestroyContext(md5, PR_TRUE);
+ rv = SECSuccess;
+ }
+ }
+
+ if(rv != SECSuccess)
+ {
+ SECITEM_FreeItem(key, PR_TRUE);
+ key = NULL;
+ }
+ }
+
+ return key;
+}
+
+SECItem *
+seckey_create_rc4_salt(void)
+{
+ SECItem *salt = NULL;
+ SECStatus rv = SECFailure;
+
+ salt = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(salt == NULL)
+ return NULL;
+
+ salt->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) *
+ SALT_LENGTH);
+ if(salt->data != NULL)
+ {
+ salt->len = SALT_LENGTH;
+ RNG_GenerateGlobalRandomBytes(salt->data, salt->len);
+ rv = SECSuccess;
+ }
+
+ if(rv == SECFailure)
+ {
+ SECITEM_FreeItem(salt, PR_TRUE);
+ salt = NULL;
+ }
+
+ return salt;
+}
+
+SECItem *
+seckey_rc4_cipher(SECItem *key, SECItem *src, PRBool encrypt)
+{
+ SECItem *dest = NULL;
+ RC4Context *ctxt = NULL;
+ SECStatus rv = SECFailure;
+
+ if((key == NULL) || (src == NULL))
+ return NULL;
+
+ dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(dest == NULL)
+ return NULL;
+
+ dest->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) *
+ (src->len + 64)); /* TNH - padding? */
+ if(dest->data != NULL)
+ {
+ ctxt = RC4_CreateContext(key->data, key->len);
+ if(ctxt != NULL)
+ {
+ if(encrypt == PR_TRUE)
+ rv = RC4_Encrypt(ctxt, dest->data, &dest->len,
+ src->len + 64, src->data, src->len);
+ else
+ rv = RC4_Decrypt(ctxt, dest->data, &dest->len,
+ src->len + 64, src->data, src->len);
+ RC4_DestroyContext(ctxt, PR_TRUE);
+ }
+ }
+
+ if(rv == SECFailure)
+ if(dest != NULL)
+ {
+ SECITEM_FreeItem(dest, PR_TRUE);
+ dest = NULL;
+ }
+
+ return dest;
+}
+
+/* TNH - keydb is unused */
+/* TNH - the pwitem should be the derived key for RC4 */
+SECKEYEncryptedPrivateKeyInfo *
+seckey_encrypt_private_key(
+ SECKEYLowPrivateKey *pk, SECItem *pwitem, SECKEYKeyDBHandle *keydb,
+ SECOidTag algorithm)
+{
+ SECKEYEncryptedPrivateKeyInfo *epki = NULL;
+ SECKEYPrivateKeyInfo *pki = NULL;
+ SECStatus rv = SECFailure;
+ SECAlgorithmID *algid = NULL;
+ PLArenaPool *temparena = NULL, *permarena = NULL;
+ SECItem *key = NULL, *salt = NULL, *der_item = NULL;
+ SECItem *dummy = NULL, *dest = NULL;
+
+ permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if(permarena == NULL)
+ return NULL;
+
+ temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if(temparena == NULL)
+ goto loser;
+
+ /* allocate structures */
+ epki = (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(permarena,
+ sizeof(SECKEYEncryptedPrivateKeyInfo));
+ pki = (SECKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena,
+ sizeof(SECKEYPrivateKeyInfo));
+ der_item = (SECItem *)PORT_ArenaZAlloc(temparena, sizeof(SECItem));
+ if((epki == NULL) || (pki == NULL) || (der_item == NULL))
+ goto loser;
+
+ epki->arena = permarena;
+
+ /* setup private key info */
+ dummy = SEC_ASN1EncodeInteger(temparena, &(pki->version),
+ SEC_PRIVATE_KEY_INFO_VERSION);
+ if(dummy == NULL)
+ goto loser;
+
+ /* Encode the key, and set the algorithm (with params) */
+ switch (pk->keyType) {
+ case rsaKey:
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ SECKEY_RSAPrivateKeyTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_PKCS1_RSA_ENCRYPTION, 0);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ break;
+ case dsaKey:
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ SECKEY_DSAPrivateKeyTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ dummy = SEC_ASN1EncodeItem(temparena, NULL, &pk->u.dsa.params,
+ SECKEY_PQGParamsTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_ANSIX9_DSA_SIGNATURE, dummy);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ break;
+ case dhKey:
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ SECKEY_DHPrivateKeyTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_X942_DIFFIE_HELMAN_KEY, dummy);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+ break;
+ default:
+ /* We don't support DH or Fortezza private keys yet */
+ PORT_Assert(PR_FALSE);
+ break;
+ }
+
+ /* setup encrypted private key info */
+ dummy = SEC_ASN1EncodeItem(temparena, der_item, pki,
+ SECKEY_PrivateKeyInfoTemplate);
+ if(dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECFailure; /* assume failure */
+ switch(algorithm)
+ {
+ case SEC_OID_RC4:
+ salt = seckey_create_rc4_salt();
+ if(salt != NULL)
+ {
+ key = seckey_create_rc4_key(pwitem, salt);
+ if(key != NULL)
+ {
+ dest = seckey_rc4_cipher(key, der_item, PR_TRUE);
+ if(dest != NULL)
+ {
+ rv = SECITEM_CopyItem(permarena, &epki->encryptedData,
+ dest);
+ if(rv == SECSuccess)
+ rv = SECOID_SetAlgorithmID(permarena,
+ &epki->algorithm, SEC_OID_RC4, salt);
+ }
+ }
+ }
+ if(dest != NULL)
+ SECITEM_FreeItem(dest, PR_TRUE);
+ if(key != NULL)
+ SECITEM_ZfreeItem(key, PR_TRUE);
+ break;
+ default:
+ algid = SEC_PKCS5CreateAlgorithmID(algorithm, NULL, 1);
+ if(algid != NULL)
+ {
+ dest = SEC_PKCS5CipherData(algid, pwitem,
+ der_item, PR_TRUE, NULL);
+ if(dest != NULL)
+ {
+ rv = SECITEM_CopyItem(permarena, &epki->encryptedData,
+ dest);
+ if(rv == SECSuccess)
+ rv = SECOID_CopyAlgorithmID(permarena,
+ &epki->algorithm, algid);
+ }
+ }
+ if(dest != NULL)
+ SECITEM_FreeItem(dest, PR_TRUE);
+ if(algid != NULL)
+ SECOID_DestroyAlgorithmID(algid, PR_TRUE);
+ break;
+ }
+
+ /* let success fall through */
+loser:
+
+ if(rv == SECFailure)
+ {
+ PORT_FreeArena(permarena, PR_TRUE);
+ epki = NULL;
+ }
+
+ if(temparena != NULL)
+ PORT_FreeArena(temparena, PR_TRUE);
+
+ if(salt != NULL)
+ SECITEM_FreeItem(salt, PR_TRUE);
+
+ return epki;
+}
+
+SECStatus
+seckey_put_private_key(SECKEYKeyDBHandle *keydb, DBT *index, SECItem *pwitem,
+ SECKEYLowPrivateKey *pk, char *nickname, PRBool update,
+ SECOidTag algorithm)
+{
+ SECKEYDBKey *dbkey = NULL;
+ SECKEYEncryptedPrivateKeyInfo *epki = NULL;
+ PLArenaPool *temparena = NULL, *permarena = NULL;
+ SECItem *dummy = NULL;
+ SECItem *salt = NULL;
+ SECStatus rv = SECFailure;
+
+ if((keydb == NULL) || (index == NULL) || (pwitem == NULL) ||
+ (pk == NULL))
+ return SECFailure;
+
+ permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if(permarena == NULL)
+ return SECFailure;
+
+ dbkey = (SECKEYDBKey *)PORT_ArenaZAlloc(permarena, sizeof(SECKEYDBKey));
+ if(dbkey == NULL)
+ goto loser;
+ dbkey->arena = permarena;
+ dbkey->nickname = nickname;
+
+ /* TNH - for RC4, the salt should be created here */
+
+ epki = seckey_encrypt_private_key(pk, pwitem, keydb, algorithm);
+ if(epki == NULL)
+ goto loser;
+ temparena = epki->arena;
+
+ /* extract salt for db key */
+ switch(algorithm)
+ {
+ case SEC_OID_RC4:
+ rv = SECITEM_CopyItem(permarena, &(dbkey->salt),
+ &(epki->algorithm.parameters));
+ epki->algorithm.parameters.len = 0;
+ epki->algorithm.parameters.data = NULL;
+ break;
+ default:
+ /* TNH - this should not be necessary */
+ salt = SEC_PKCS5GetSalt(&epki->algorithm);
+ if(salt != NULL)
+ {
+ rv = SECITEM_CopyItem(permarena, &(dbkey->salt), salt);
+ SECITEM_ZfreeItem(salt, PR_TRUE);
+ }
+ break;
+ }
+
+ dummy = SEC_ASN1EncodeItem(permarena, &(dbkey->derPK), epki,
+ SECKEY_EncryptedPrivateKeyInfoTemplate);
+ if(dummy == NULL)
+ rv = SECFailure;
+ else
+ rv = put_dbkey(keydb, index, dbkey, update);
+
+ /* let success fall through */
+loser:
+ if(rv != SECSuccess)
+ if(permarena != NULL)
+ PORT_FreeArena(permarena, PR_TRUE);
+ if(temparena != NULL)
+ PORT_FreeArena(temparena, PR_TRUE);
+
+ return rv;
+}
+
+/*
+ * Store a key in the database, indexed by its public key modulus.
+ * Note that the nickname is optional. It was only used by keyutil.
+ */
+SECStatus
+SECKEY_StoreKeyByPublicKeyAlg(SECKEYKeyDBHandle *handle,
+ SECKEYLowPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SECKEYGetPasswordKey f, void *arg,
+ SECOidTag algorithm)
+{
+ DBT namekey;
+ SECItem *pwitem = NULL;
+ SECStatus rv;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ /* set up db key and data */
+ namekey.data = pubKeyData->data;
+ namekey.size = pubKeyData->len;
+
+ pwitem = (*f )(arg, handle);
+ if ( pwitem == NULL ) {
+ return(SECFailure);
+ }
+
+ /* encrypt the private key */
+ rv = seckey_put_private_key(handle, &namekey, pwitem, privkey, nickname,
+ PR_FALSE, algorithm);
+ SECITEM_ZfreeItem(pwitem, PR_TRUE);
+
+ return(rv);
+}
+
+SECKEYLowPrivateKey *
+seckey_decrypt_private_key(SECKEYEncryptedPrivateKeyInfo *epki,
+ SECItem *pwitem)
+{
+ SECKEYLowPrivateKey *pk = NULL;
+ SECKEYPrivateKeyInfo *pki = NULL;
+ SECStatus rv = SECFailure;
+ SECOidTag algorithm;
+ PLArenaPool *temparena = NULL, *permarena = NULL;
+ SECItem *salt = NULL, *dest = NULL, *key = NULL;
+
+ if((epki == NULL) || (pwitem == NULL))
+ goto loser;
+
+ temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if((temparena == NULL) || (permarena == NULL))
+ goto loser;
+
+ /* allocate temporary items */
+ pki = (SECKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena,
+ sizeof(SECKEYPrivateKeyInfo));
+
+ /* allocate permanent arena items */
+ pk = (SECKEYLowPrivateKey *)PORT_ArenaZAlloc(permarena,
+ sizeof(SECKEYLowPrivateKey));
+
+ if((pk == NULL) || (pki == NULL))
+ goto loser;
+
+ pk->arena = permarena;
+
+ algorithm = SECOID_GetAlgorithmTag(&(epki->algorithm));
+ switch(algorithm)
+ {
+ case SEC_OID_RC4:
+ salt = SECITEM_DupItem(&epki->algorithm.parameters);
+ if(salt != NULL)
+ {
+ key = seckey_create_rc4_key(pwitem, salt);
+ if(key != NULL)
+ {
+ dest = seckey_rc4_cipher(key, &epki->encryptedData,
+ PR_FALSE);
+ }
+ }
+ if(salt != NULL)
+ SECITEM_ZfreeItem(salt, PR_TRUE);
+ if(key != NULL)
+ SECITEM_ZfreeItem(key, PR_TRUE);
+ break;
+ default:
+ /* we depend on the fact that if this key was encoded with
+ * DES, that the pw was also encoded with DES, so we don't have
+ * to do the update here, the password code will handle it. */
+ dest = SEC_PKCS5CipherData(&epki->algorithm, pwitem,
+ &epki->encryptedData, PR_FALSE, NULL);
+ break;
+ }
+
+ if(dest != NULL)
+ {
+ rv = SEC_ASN1DecodeItem(temparena, pki,
+ SECKEY_PrivateKeyInfoTemplate, dest);
+ if(rv == SECSuccess)
+ {
+ switch(SECOID_GetAlgorithmTag(&pki->algorithm)) {
+ case SEC_OID_X500_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ pk->keyType = rsaKey;
+ rv = SEC_ASN1DecodeItem(permarena, pk,
+ SECKEY_RSAPrivateKeyTemplate,
+ &pki->privateKey);
+ break;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ pk->keyType = dsaKey;
+ rv = SEC_ASN1DecodeItem(permarena, pk,
+ SECKEY_DSAPrivateKeyTemplate,
+ &pki->privateKey);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = SEC_ASN1DecodeItem(permarena, &pk->u.dsa.params,
+ SECKEY_PQGParamsTemplate,
+ &pki->algorithm.parameters);
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ pk->keyType = dhKey;
+ rv = SEC_ASN1DecodeItem(permarena, pk,
+ SECKEY_DHPrivateKeyTemplate,
+ &pki->privateKey);
+ break;
+ default:
+ rv = SECFailure;
+ break;
+ }
+ }
+ else if(PORT_GetError() == SEC_ERROR_BAD_DER)
+ {
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ goto loser;
+ }
+ }
+
+ /* let success fall through */
+loser:
+ if(temparena != NULL)
+ PORT_FreeArena(temparena, PR_TRUE);
+ if(dest != NULL)
+ SECITEM_ZfreeItem(dest, PR_TRUE);
+
+ if(rv != SECSuccess)
+ {
+ if(permarena != NULL)
+ PORT_FreeArena(permarena, PR_TRUE);
+ pk = NULL;
+ }
+
+ return pk;
+}
+
+static SECKEYLowPrivateKey *
+seckey_decode_encrypted_private_key(SECKEYDBKey *dbkey, SECItem *pwitem)
+{
+ SECKEYLowPrivateKey *pk = NULL;
+ SECKEYEncryptedPrivateKeyInfo *epki;
+ PLArenaPool *temparena = NULL;
+ SECStatus rv;
+ SECOidTag algorithm;
+
+ if( ( dbkey == NULL ) || ( pwitem == NULL ) ) {
+ return NULL;
+ }
+
+ temparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if(temparena == NULL) {
+ return NULL;
+ }
+
+ epki = (SECKEYEncryptedPrivateKeyInfo *)
+ PORT_ArenaZAlloc(temparena, sizeof(SECKEYEncryptedPrivateKeyInfo));
+
+ if(epki == NULL) {
+ goto loser;
+ }
+
+ rv = SEC_ASN1DecodeItem(temparena, epki,
+ SECKEY_EncryptedPrivateKeyInfoTemplate,
+ &(dbkey->derPK));
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+
+ algorithm = SECOID_GetAlgorithmTag(&(epki->algorithm));
+ switch(algorithm)
+ {
+ case SEC_OID_RC4:
+ /* TNH - this code should derive the actual RC4 key from salt and
+ pwitem */
+ rv = SECITEM_CopyItem(temparena, &(epki->algorithm.parameters),
+ &(dbkey->salt));
+ break;
+ default:
+ break;
+ }
+
+ pk = seckey_decrypt_private_key(epki, pwitem);
+
+ /* let success fall through */
+loser:
+
+ PORT_FreeArena(temparena, PR_TRUE);
+ return pk;
+}
+
+SECKEYLowPrivateKey *
+seckey_get_private_key(SECKEYKeyDBHandle *keydb, DBT *index, char **nickname,
+ SECItem *pwitem)
+{
+ SECKEYDBKey *dbkey = NULL;
+ SECKEYLowPrivateKey *pk = NULL;
+
+ if( ( keydb == NULL ) || ( index == NULL ) || ( pwitem == NULL ) ) {
+ return NULL;
+ }
+
+ dbkey = get_dbkey(keydb, index);
+ if(dbkey == NULL) {
+ goto loser;
+ }
+
+ if ( nickname ) {
+ if ( dbkey->nickname && ( dbkey->nickname[0] != 0 ) ) {
+ *nickname = PORT_Strdup(dbkey->nickname);
+ } else {
+ *nickname = NULL;
+ }
+ }
+
+ pk = seckey_decode_encrypted_private_key(dbkey, pwitem);
+
+ /* let success fall through */
+loser:
+
+ if ( dbkey != NULL ) {
+ sec_destroy_dbkey(dbkey);
+ }
+
+ return pk;
+}
+
+/*
+ * used by pkcs11 to import keys into it's object format... In the future
+ * we really need a better way to tie in...
+ */
+SECKEYLowPrivateKey *
+SECKEY_DecryptKey(DBT *key, SECItem *pwitem,
+ SECKEYKeyDBHandle *handle) {
+ return seckey_get_private_key(handle,key,NULL,pwitem);
+}
+
+/*
+ * Find a key in the database, indexed by its public key modulus
+ * This is used to find keys that have been stored before their
+ * certificate arrives. Once the certificate arrives the key
+ * is looked up by the public modulus in the certificate, and the
+ * re-stored by its nickname.
+ */
+SECKEYLowPrivateKey *
+SECKEY_FindKeyByPublicKey(SECKEYKeyDBHandle *handle, SECItem *modulus,
+ SECKEYGetPasswordKey f, void *arg)
+{
+ DBT namekey;
+ SECKEYLowPrivateKey *pk = NULL;
+ SECItem *pwitem = NULL;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return NULL;
+ }
+
+ /* set up db key */
+ namekey.data = modulus->data;
+ namekey.size = modulus->len;
+
+ pwitem = (*f )(arg, handle);
+ if ( pwitem == NULL ) {
+ return(NULL);
+ }
+
+ pk = seckey_get_private_key(handle, &namekey, NULL, pwitem);
+ SECITEM_ZfreeItem(pwitem, PR_TRUE);
+
+ /* no need to free dbkey, since its on the stack, and the data it
+ * points to is owned by the database
+ */
+ return(pk);
+}
+
+/* ===== ENCODING ROUTINES ===== */
+
+static SECStatus
+encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg,
+ SECItem *encCheck)
+{
+ SECOidData *oidData;
+ SECStatus rv;
+
+ oidData = SECOID_FindOIDByTag(alg);
+ if ( oidData == NULL ) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ entry->len = 1 + oidData->oid.len + encCheck->len;
+ if ( arena ) {
+ entry->data = (unsigned char *)PORT_ArenaAlloc(arena, entry->len);
+ } else {
+ entry->data = (unsigned char *)PORT_Alloc(entry->len);
+ }
+
+ if ( entry->data == NULL ) {
+ goto loser;
+ }
+
+ /* first length of oid */
+ entry->data[0] = (unsigned char)oidData->oid.len;
+ /* next oid itself */
+ PORT_Memcpy(&entry->data[1], oidData->oid.data, oidData->oid.len);
+ /* finally the encrypted check string */
+ PORT_Memcpy(&entry->data[1+oidData->oid.len], encCheck->data,
+ encCheck->len);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Set up the password checker in the key database.
+ * This is done by encrypting a known plaintext with the user's key.
+ */
+SECStatus
+SECKEY_SetKeyDBPasswordAlg(SECKEYKeyDBHandle *handle,
+ SECItem *pwitem, SECOidTag algorithm)
+{
+ DBT checkkey;
+ SECAlgorithmID *algid = NULL;
+ SECStatus rv = SECFailure;
+ SECKEYDBKey *dbkey = NULL;
+ PLArenaPool *arena;
+ SECItem *key = NULL, *salt = NULL;
+ SECItem *dest = NULL, test_key;
+
+ if (handle == NULL) {
+ return(SECFailure);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ dbkey = (SECKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYDBKey));
+ if ( dbkey == NULL ) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ dbkey->arena = arena;
+
+ /* encrypt key */
+ checkkey.data = test_key.data = (unsigned char *)KEYDB_PW_CHECK_STRING;
+ checkkey.size = test_key.len = KEYDB_PW_CHECK_LEN;
+
+ salt = seckey_create_rc4_salt();
+ if(salt == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ switch(algorithm)
+ {
+ case SEC_OID_RC4:
+ key = seckey_create_rc4_key(pwitem, salt);
+ if(key != NULL)
+ {
+ dest = seckey_rc4_cipher(key, &test_key, PR_TRUE);
+ SECITEM_FreeItem(key, PR_TRUE);
+ }
+ break;
+ default:
+ algid = SEC_PKCS5CreateAlgorithmID(algorithm, salt, 1);
+ if(algid != NULL)
+ dest = SEC_PKCS5CipherData(algid, pwitem, &test_key,
+ PR_TRUE, NULL);
+ break;
+ }
+
+ if(dest != NULL)
+ {
+ rv = SECITEM_CopyItem(arena, &dbkey->salt, salt);
+ if(rv == SECFailure)
+ goto loser;
+
+ rv = encodePWCheckEntry(arena, &dbkey->derPK, algorithm, dest);
+
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = put_dbkey(handle, &checkkey, dbkey, PR_TRUE);
+ } else {
+ rv = SECFailure;
+ }
+
+ /* let success fall through */
+loser:
+ if ( arena != NULL ) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+
+ if ( dest != NULL ) {
+ SECITEM_ZfreeItem(dest, PR_TRUE);
+ }
+
+ if ( salt != NULL ) {
+ SECITEM_ZfreeItem(salt, PR_TRUE);
+ }
+
+ return(rv);
+}
+
+static PRBool
+seckey_HasAServerKey(DB *db)
+{
+ DBT checkKey;
+ DBT checkData;
+ DBT key;
+ DBT data;
+ int ret;
+ PRBool found = PR_FALSE;
+
+ ret = (* db->seq)(db, &key, &data, R_FIRST);
+ if ( ret ) {
+ return PR_FALSE;
+ }
+
+ do {
+ /* skip version record */
+ if ( data.size > 1 ) {
+ /* skip salt */
+ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
+ if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
+ continue;
+ }
+ }
+ /* skip pw check entry */
+ if ( key.size == KEYDB_PW_CHECK_LEN ) {
+ if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING,
+ KEYDB_PW_CHECK_LEN) == 0 ) {
+ continue;
+ }
+ }
+
+ /* keys stored by nickname will have 0 as the last byte of the
+ * db key. Other keys must be stored by modulus. We will not
+ * update those because they are left over from a keygen that
+ * never resulted in a cert.
+ */
+ if ( ((unsigned char *)key.data)[key.size-1] != 0 ) {
+ continue;
+ }
+
+ if (PORT_Strcmp(key.data,"Server-Key") == 0) {
+ found = PR_TRUE;
+ break;
+ }
+
+ }
+ } while ( (* db->seq)(db, &key, &data, R_NEXT) == 0 );
+
+ return found;
+}
+
+static SECStatus
+seckey_CheckKeyDB1Password(SECKEYKeyDBHandle *handle, SECItem *pwitem)
+{
+ SECStatus rv = SECFailure;
+ keyList keylist;
+ keyNode *node = NULL;
+ SECKEYLowPrivateKey *privkey = NULL;
+
+
+ /*
+ * first find a key
+ */
+
+ /* traverse the database, collecting the keys of all records */
+ keylist.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( keylist.arena == NULL )
+ {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(SECFailure);
+ }
+ keylist.head = NULL;
+
+ /* TNH - TraverseKeys should not be public, since it exposes
+ the underlying DBT data type. */
+ rv = SECKEY_TraverseKeys(handle, sec_add_key_to_list, (void *)&keylist);
+ if ( rv != SECSuccess )
+ goto done;
+
+ /* just get the first key from the list */
+ node = keylist.head;
+
+ /* no private keys, accept any password */
+ if (node == NULL) {
+ rv = SECSuccess;
+ goto done;
+ }
+ privkey = seckey_get_private_key(handle, &node->key, NULL, pwitem);
+ if (privkey == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* if we can decrypt the private key, then we had the correct password */
+ rv = SECSuccess;
+ SECKEY_LowDestroyPrivateKey(privkey);
+
+done:
+
+ /* free the arena */
+ if ( keylist.arena ) {
+ PORT_FreeArena(keylist.arena, PR_FALSE);
+ }
+
+ return(rv);
+}
+
+/*
+ * check to see if the user has typed the right password
+ */
+SECStatus
+SECKEY_CheckKeyDBPassword(SECKEYKeyDBHandle *handle, SECItem *pwitem)
+{
+ DBT checkkey;
+ DBT checkdata;
+ SECAlgorithmID *algid = NULL;
+ SECStatus rv = SECFailure;
+ SECKEYDBKey *dbkey = NULL;
+ SECItem *key = NULL;
+ SECItem *dest = NULL;
+ SECOidTag algorithm;
+ SECItem oid;
+ SECItem encstring;
+ PRBool update = PR_FALSE;
+ int ret;
+
+ if (handle == NULL) {
+ goto loser;
+ }
+
+ checkkey.data = KEYDB_PW_CHECK_STRING;
+ checkkey.size = KEYDB_PW_CHECK_LEN;
+
+ dbkey = get_dbkey(handle, &checkkey);
+
+ if ( dbkey == NULL ) {
+ checkkey.data = KEYDB_FAKE_PW_CHECK_STRING;
+ checkkey.size = KEYDB_FAKE_PW_CHECK_LEN;
+ ret = (* handle->db->get)(handle->db, &checkkey,
+ &checkdata, 0 );
+ if (ret) {
+ goto loser;
+ }
+ /* if we have the fake PW_CHECK, then try to decode the key
+ * rather than the pwcheck item.
+ */
+ rv = seckey_CheckKeyDB1Password(handle,pwitem);
+ if (rv == SECSuccess) {
+ /* OK we have enough to complete our conversion */
+ SECKEY_UpdateKeyDBPass2(handle,pwitem);
+ }
+ return rv;
+ }
+
+ /* build the oid item */
+ oid.len = dbkey->derPK.data[0];
+ oid.data = &dbkey->derPK.data[1];
+
+ /* make sure entry is the correct length
+ * since we are probably using a block cipher, the block will be
+ * padded, so we may get a bit more than the exact size we need.
+ */
+ if ( dbkey->derPK.len < (KEYDB_PW_CHECK_LEN + 1 + oid.len ) ) {
+ goto loser;
+ }
+
+ /* find the algorithm tag */
+ algorithm = SECOID_FindOIDTag(&oid);
+
+ /* make a secitem of the encrypted check string */
+ encstring.len = dbkey->derPK.len - ( oid.len + 1 );
+ encstring.data = &dbkey->derPK.data[oid.len+1];
+
+ switch(algorithm)
+ {
+ case SEC_OID_RC4:
+ key = seckey_create_rc4_key(pwitem, &dbkey->salt);
+ if(key != NULL) {
+ dest = seckey_rc4_cipher(key, &encstring, PR_FALSE);
+ SECITEM_FreeItem(key, PR_TRUE);
+ }
+ break;
+ default:
+ algid = SEC_PKCS5CreateAlgorithmID(algorithm,
+ &dbkey->salt, 1);
+ if(algid != NULL) {
+ /* Decrypt - this function implements a workaround for
+ * a previous coding error. It will decrypt values using
+ * DES rather than 3DES, if the initial try at 3DES
+ * decryption fails. In this case, the update flag is
+ * set to TRUE. This indication is used later to force
+ * an update of the database to "real" 3DES encryption.
+ */
+ dest = SEC_PKCS5CipherData(algid, pwitem,
+ &encstring, PR_FALSE, &update);
+ SECOID_DestroyAlgorithmID(algid, PR_TRUE);
+ }
+ break;
+ }
+
+ if(dest == NULL) {
+ goto loser;
+ }
+
+ if ((dest->len == KEYDB_PW_CHECK_LEN) &&
+ (PORT_Memcmp(dest->data,
+ KEYDB_PW_CHECK_STRING, KEYDB_PW_CHECK_LEN) == 0)) {
+ rv = SECSuccess;
+ /* we succeeded */
+ if ( algorithm == SEC_OID_RC4 ) {
+ /* partially updated database */
+ SECKEY_UpdateKeyDBPass2(handle, pwitem);
+ }
+ /* Force an update of the password to remove the incorrect DES
+ * encryption (see the note above)
+ */
+ if (update &&
+ (algorithm == SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC)) {
+ /* data base was encoded with DES not triple des, fix it */
+ SECKEY_UpdateKeyDBPass2(handle,pwitem);
+ }
+ }
+
+loser:
+ sec_destroy_dbkey(dbkey);
+ if(dest != NULL) {
+ SECITEM_ZfreeItem(dest, PR_TRUE);
+ }
+
+ return(rv);
+}
+
+/*
+ * Change the database password and/or algorithm. This internal
+ * routine does not check the old password. That must be done by
+ * the caller.
+ */
+static SECStatus
+ChangeKeyDBPasswordAlg(SECKEYKeyDBHandle *handle,
+ SECItem *oldpwitem, SECItem *newpwitem,
+ SECOidTag new_algorithm)
+{
+ SECStatus rv;
+ keyList keylist;
+ keyNode *node = NULL;
+ SECKEYLowPrivateKey *privkey = NULL;
+ char *nickname;
+ DBT newkey;
+ int ret;
+
+ /* traverse the database, collecting the keys of all records */
+ keylist.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( keylist.arena == NULL )
+ {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(SECFailure);
+ }
+ keylist.head = NULL;
+
+ /* TNH - TraverseKeys should not be public, since it exposes
+ the underlying DBT data type. */
+ rv = SECKEY_TraverseKeys(handle, sec_add_key_to_list, (void *)&keylist);
+ if ( rv != SECSuccess )
+ goto loser;
+
+ /* walk the list, re-encrypting each entry */
+ node = keylist.head;
+ while ( node != NULL )
+ {
+ privkey = seckey_get_private_key(handle, &node->key, &nickname,
+ oldpwitem);
+
+ if (privkey == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* delete the old record */
+ ret = (* handle->db->del)(handle->db, &node->key, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* get the public key, which we use as the database index */
+
+ switch (privkey->keyType) {
+ case rsaKey:
+ newkey.data = privkey->u.rsa.modulus.data;
+ newkey.size = privkey->u.rsa.modulus.len;
+ break;
+ case dsaKey:
+ newkey.data = privkey->u.dsa.publicValue.data;
+ newkey.size = privkey->u.dsa.publicValue.len;
+ break;
+ case dhKey:
+ newkey.data = privkey->u.dh.publicValue.data;
+ newkey.size = privkey->u.dh.publicValue.len;
+ break;
+ default:
+ /* XXX We don't do Fortezza. */
+ return SECFailure;
+ }
+
+ rv = seckey_put_private_key(handle, &newkey, newpwitem, privkey,
+ nickname, PR_TRUE, new_algorithm);
+
+ if ( rv != SECSuccess )
+ {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* next node */
+ node = node->next;
+ }
+
+ rv = SECKEY_SetKeyDBPasswordAlg(handle, newpwitem, new_algorithm);
+
+loser:
+
+ /* free the arena */
+ if ( keylist.arena ) {
+ PORT_FreeArena(keylist.arena, PR_FALSE);
+ }
+
+ return(rv);
+}
+
+/*
+ * Re-encrypt the entire key database with a new password.
+ * NOTE: The really should create a new database rather than doing it
+ * in place in the original
+ */
+SECStatus
+SECKEY_ChangeKeyDBPasswordAlg(SECKEYKeyDBHandle *handle,
+ SECItem *oldpwitem, SECItem *newpwitem,
+ SECOidTag new_algorithm)
+{
+ SECStatus rv;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECKEY_CheckKeyDBPassword(handle, oldpwitem);
+ if ( rv != SECSuccess ) {
+ return(SECFailure); /* return rv? */
+ }
+
+ rv = ChangeKeyDBPasswordAlg(handle, oldpwitem, newpwitem, new_algorithm);
+
+loser:
+ return(rv);
+}
+
+/*
+ * Second pass of updating the key db. This time we have a password.
+ */
+SECStatus
+SECKEY_UpdateKeyDBPass2(SECKEYKeyDBHandle *handle, SECItem *pwitem)
+{
+ SECStatus rv;
+
+ rv = ChangeKeyDBPasswordAlg(handle, pwitem, pwitem,
+ SECKEY_GetDefaultKeyDBAlg());
+
+ return(rv);
+}
+
+/*
+ * currently updates key database from v2 to v3
+ */
+SECStatus
+SECKEY_UpdateKeyDBPass1(SECKEYKeyDBHandle *handle)
+{
+ SECStatus rv;
+ DBT versionKey;
+ DBT versionData;
+ DBT checkKey;
+ DBT checkData;
+ DBT saltKey;
+ DBT saltData;
+ DBT key;
+ DBT data;
+ SECItem *rc4key = NULL;
+ SECKEYDBKey *dbkey = NULL;
+ SECItem *oldSalt = NULL;
+ int ret;
+ SECItem checkitem;
+
+ if ( handle->updatedb == NULL ) {
+ return(SECSuccess);
+ }
+
+ /*
+ * check the version record
+ */
+ versionKey.data = VERSION_STRING;
+ versionKey.size = sizeof(VERSION_STRING)-1;
+
+ ret = (* handle->updatedb->get)(handle->updatedb, &versionKey,
+ &versionData, 0 );
+
+ if (ret) {
+ /* no version record, so old db never used */
+ goto done;
+ }
+
+ if ( ( versionData.size != 1 ) ||
+ ( *((unsigned char *)versionData.data) != 2 ) ) {
+ /* corrupt or wrong version number so don't update */
+ goto done;
+ }
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ ret = (* handle->updatedb->get)(handle->updatedb, &saltKey, &saltData, 0);
+ if ( ret ) {
+ /* no salt in old db, so it is corrupted */
+ goto done;
+ }
+
+ oldSalt = decodeKeyDBGlobalSalt(&saltData);
+ if ( oldSalt == NULL ) {
+ /* bad salt in old db, so it is corrupted */
+ goto done;
+ }
+
+ /*
+ * look for a pw check entry
+ */
+ checkKey.data = KEYDB_PW_CHECK_STRING;
+ checkKey.size = KEYDB_PW_CHECK_LEN;
+
+ ret = (* handle->updatedb->get)(handle->updatedb, &checkKey,
+ &checkData, 0 );
+ if (ret) {
+ /*
+ * if we have a key, but no KEYDB_PW_CHECK_STRING, then this must
+ * be an old server database, and it does have a password associated
+ * with it. Put a fake entry in so we can identify this db when we do
+ * get the password for it.
+ */
+ if (seckey_HasAServerKey(handle->updatedb)) {
+ DBT fcheckKey;
+ DBT fcheckData;
+
+ /*
+ * include a fake string
+ */
+ fcheckKey.data = KEYDB_FAKE_PW_CHECK_STRING;
+ fcheckKey.size = KEYDB_FAKE_PW_CHECK_LEN;
+ fcheckData.data = "1";
+ fcheckData.size = 1;
+ /* put global salt into the new database now */
+ ret = (* handle->db->put)( handle->db, &saltKey, &saltData, 0);
+ if ( ret ) {
+ goto done;
+ }
+ ret = (* handle->db->put)( handle->db, &fcheckKey, &fcheckData, 0);
+ if ( ret ) {
+ goto done;
+ }
+ } else {
+ goto done;
+ }
+ } else {
+ /* put global salt into the new database now */
+ ret = (* handle->db->put)( handle->db, &saltKey, &saltData, 0);
+ if ( ret ) {
+ goto done;
+ }
+
+ dbkey = decode_dbkey(&checkData, 2);
+ if ( dbkey == NULL ) {
+ goto done;
+ }
+ checkitem = dbkey->derPK;
+ dbkey->derPK.data = NULL;
+
+ /* format the new pw check entry */
+ rv = encodePWCheckEntry(NULL, &dbkey->derPK, SEC_OID_RC4, &checkitem);
+ if ( rv != SECSuccess ) {
+ goto done;
+ }
+
+ rv = put_dbkey(handle, &checkKey, dbkey, PR_TRUE);
+ if ( rv != SECSuccess ) {
+ goto done;
+ }
+
+ /* free the dbkey */
+ sec_destroy_dbkey(dbkey);
+ dbkey = NULL;
+ }
+
+
+ /* now traverse the database */
+ ret = (* handle->updatedb->seq)(handle->updatedb, &key, &data, R_FIRST);
+ if ( ret ) {
+ goto done;
+ }
+
+ do {
+ /* skip version record */
+ if ( data.size > 1 ) {
+ /* skip salt */
+ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
+ if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
+ continue;
+ }
+ }
+ /* skip pw check entry */
+ if ( key.size == checkKey.size ) {
+ if ( PORT_Memcmp(key.data, checkKey.data, key.size) == 0 ) {
+ continue;
+ }
+ }
+
+ /* keys stored by nickname will have 0 as the last byte of the
+ * db key. Other keys must be stored by modulus. We will not
+ * update those because they are left over from a keygen that
+ * never resulted in a cert.
+ */
+ if ( ((unsigned char *)key.data)[key.size-1] != 0 ) {
+ continue;
+ }
+
+ dbkey = decode_dbkey(&data, 2);
+ if ( dbkey == NULL ) {
+ continue;
+ }
+
+ /* This puts the key into the new database with the same
+ * index (nickname) that it had before. The second pass
+ * of the update will have the password. It will decrypt
+ * and re-encrypt the entries using a new algorithm.
+ */
+ dbkey->nickname = (char *)key.data;
+ rv = put_dbkey(handle, &key, dbkey, PR_FALSE);
+ dbkey->nickname = NULL;
+
+ sec_destroy_dbkey(dbkey);
+ }
+ } while ( (* handle->updatedb->seq)(handle->updatedb, &key, &data,
+ R_NEXT) == 0 );
+
+ dbkey = NULL;
+
+done:
+ /* sync the database */
+ ret = (* handle->db->sync)(handle->db, 0);
+
+ (* handle->updatedb->close)(handle->updatedb);
+ handle->updatedb = NULL;
+
+ if ( rc4key ) {
+ SECITEM_FreeItem(rc4key, PR_TRUE);
+ }
+
+ if ( oldSalt ) {
+ SECITEM_FreeItem(oldSalt, PR_TRUE);
+ }
+
+ if ( dbkey ) {
+ sec_destroy_dbkey(dbkey);
+ }
+
+ return(SECSuccess);
+}
+
+/*
+ * Clear out all the keys in the existing database
+ */
+SECStatus
+SECKEY_ResetKeyDB(SECKEYKeyDBHandle *handle)
+{
+ SECStatus rv;
+ DBT key;
+ DBT data;
+ int ret;
+ int errors = 0;
+
+ if ( handle->db == NULL ) {
+ return(SECSuccess);
+ }
+
+
+ /* now traverse the database */
+ ret = (* handle->db->seq)(handle->db, &key, &data, R_FIRST);
+ if ( ret ) {
+ goto done;
+ }
+
+ do {
+ /* delete each entry */
+ ret = (* handle->db->del)(handle->db, &key, 0);
+ if ( ret ) errors++;
+
+ } while ( (* handle->db->seq)(handle->db, &key, &data,
+ R_NEXT) == 0 );
+ rv = makeGlobalVersion(handle);
+ if ( rv != SECSuccess ) {
+ errors++;
+ goto done;
+ }
+
+ rv = makeGlobalSalt(handle);
+ if ( rv != SECSuccess ) {
+ errors++;
+ goto done;
+ }
+
+ if (handle->global_salt) {
+ SECITEM_FreeItem(handle->global_salt,PR_TRUE);
+ }
+ handle->global_salt = GetKeyDBGlobalSalt(handle);
+
+done:
+ /* sync the database */
+ ret = (* handle->db->sync)(handle->db, 0);
+
+ return (errors == 0 ? SECSuccess : SECFailure);
+}
+
+/* These functions simply return the address of the above-declared templates.
+** This is necessary for Windows DLLs. Sigh.
+*/
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PrivateKeyInfoTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PointerToPrivateKeyInfoTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_EncryptedPrivateKeyInfoTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PointerToEncryptedPrivateKeyInfoTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_DSAPublicKeyTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_RSAPublicKeyTemplate)
+
diff --git a/security/nss/lib/softoken/private.h b/security/nss/lib/softoken/private.h
new file mode 100644
index 000000000..b90ceaaea
--- /dev/null
+++ b/security/nss/lib/softoken/private.h
@@ -0,0 +1,78 @@
+/*
+ * private.h - Private data structures for the software token library
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef _PRIVATE_H_
+#define _PRIVATE_H_
+
+#include "nspr.h"
+#include "seccomon.h"
+#include "mcom_db.h"
+
+/*
+ * Handle structure for open key databases
+ */
+struct SECKEYKeyDBHandleStr {
+ DB *db;
+ DB *updatedb; /* used when updating an old version */
+ SECItem *global_salt; /* password hashing salt for this db */
+ int version; /* version of the database */
+};
+
+/*
+** Typedef for callback for traversing key database.
+** "key" is the key used to index the data in the database (nickname)
+** "data" is the key data
+** "pdata" is the user's data
+*/
+typedef SECStatus (* SECKEYTraverseKeysFunc)(DBT *key, DBT *data, void *pdata);
+
+
+SEC_BEGIN_PROTOS
+
+/*
+** Traverse the entire key database, and pass the nicknames and keys to a
+** user supplied function.
+** "f" is the user function to call for each key
+** "udata" is the user's data, which is passed through to "f"
+*/
+extern SECStatus SECKEY_TraverseKeys(SECKEYKeyDBHandle *handle,
+ SECKEYTraverseKeysFunc f,
+ void *udata);
+
+SEC_END_PROTOS
+
+#endif /* _PRIVATE_H_ */
diff --git a/security/nss/lib/util/secerr.h b/security/nss/lib/util/secerr.h
new file mode 100644
index 000000000..8b152f8e4
--- /dev/null
+++ b/security/nss/lib/util/secerr.h
@@ -0,0 +1,187 @@
+/*
+ * 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 __SEC_ERR_H_
+#define __SEC_ERR_H_
+
+
+#define SEC_ERROR_BASE (-0x2000)
+#define SEC_ERROR_LIMIT (SEC_ERROR_BASE + 1000)
+
+#define IS_SEC_ERROR(code) \
+ (((code) >= SEC_ERROR_BASE) && ((code) < SEC_ERROR_LIMIT))
+
+#ifndef NO_SECURITY_ERROR_ENUM
+typedef enum {
+SEC_ERROR_IO = SEC_ERROR_BASE + 0,
+SEC_ERROR_LIBRARY_FAILURE = SEC_ERROR_BASE + 1,
+SEC_ERROR_BAD_DATA = SEC_ERROR_BASE + 2,
+SEC_ERROR_OUTPUT_LEN = SEC_ERROR_BASE + 3,
+SEC_ERROR_INPUT_LEN = SEC_ERROR_BASE + 4,
+SEC_ERROR_INVALID_ARGS = SEC_ERROR_BASE + 5,
+SEC_ERROR_INVALID_ALGORITHM = SEC_ERROR_BASE + 6,
+SEC_ERROR_INVALID_AVA = SEC_ERROR_BASE + 7,
+SEC_ERROR_INVALID_TIME = SEC_ERROR_BASE + 8,
+SEC_ERROR_BAD_DER = SEC_ERROR_BASE + 9,
+SEC_ERROR_BAD_SIGNATURE = SEC_ERROR_BASE + 10,
+SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11,
+SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12,
+SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13,
+SEC_ERROR_BAD_KEY = SEC_ERROR_BASE + 14,
+SEC_ERROR_BAD_PASSWORD = SEC_ERROR_BASE + 15,
+SEC_ERROR_RETRY_PASSWORD = SEC_ERROR_BASE + 16,
+SEC_ERROR_NO_NODELOCK = SEC_ERROR_BASE + 17,
+SEC_ERROR_BAD_DATABASE = SEC_ERROR_BASE + 18,
+SEC_ERROR_NO_MEMORY = SEC_ERROR_BASE + 19,
+SEC_ERROR_UNTRUSTED_ISSUER = SEC_ERROR_BASE + 20,
+SEC_ERROR_UNTRUSTED_CERT = SEC_ERROR_BASE + 21,
+SEC_ERROR_DUPLICATE_CERT = (SEC_ERROR_BASE + 22),
+SEC_ERROR_DUPLICATE_CERT_NAME = (SEC_ERROR_BASE + 23),
+SEC_ERROR_ADDING_CERT = (SEC_ERROR_BASE + 24),
+SEC_ERROR_FILING_KEY = (SEC_ERROR_BASE + 25),
+SEC_ERROR_NO_KEY = (SEC_ERROR_BASE + 26),
+SEC_ERROR_CERT_VALID = (SEC_ERROR_BASE + 27),
+SEC_ERROR_CERT_NOT_VALID = (SEC_ERROR_BASE + 28),
+SEC_ERROR_CERT_NO_RESPONSE = (SEC_ERROR_BASE + 29),
+SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = (SEC_ERROR_BASE + 30),
+SEC_ERROR_CRL_EXPIRED = (SEC_ERROR_BASE + 31),
+SEC_ERROR_CRL_BAD_SIGNATURE = (SEC_ERROR_BASE + 32),
+SEC_ERROR_CRL_INVALID = (SEC_ERROR_BASE + 33),
+SEC_ERROR_EXTENSION_VALUE_INVALID = (SEC_ERROR_BASE + 34),
+SEC_ERROR_EXTENSION_NOT_FOUND = (SEC_ERROR_BASE + 35),
+SEC_ERROR_CA_CERT_INVALID = (SEC_ERROR_BASE + 36),
+SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID = (SEC_ERROR_BASE + 37),
+SEC_ERROR_CERT_USAGES_INVALID = (SEC_ERROR_BASE + 38),
+SEC_INTERNAL_ONLY = (SEC_ERROR_BASE + 39),
+SEC_ERROR_INVALID_KEY = (SEC_ERROR_BASE + 40),
+SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION = (SEC_ERROR_BASE + 41),
+SEC_ERROR_OLD_CRL = (SEC_ERROR_BASE + 42),
+SEC_ERROR_NO_EMAIL_CERT = (SEC_ERROR_BASE + 43),
+SEC_ERROR_NO_RECIPIENT_CERTS_QUERY = (SEC_ERROR_BASE + 44),
+SEC_ERROR_NOT_A_RECIPIENT = (SEC_ERROR_BASE + 45),
+SEC_ERROR_PKCS7_KEYALG_MISMATCH = (SEC_ERROR_BASE + 46),
+SEC_ERROR_PKCS7_BAD_SIGNATURE = (SEC_ERROR_BASE + 47),
+SEC_ERROR_UNSUPPORTED_KEYALG = (SEC_ERROR_BASE + 48),
+SEC_ERROR_DECRYPTION_DISALLOWED = (SEC_ERROR_BASE + 49),
+/* Fortezza Alerts */
+XP_SEC_FORTEZZA_BAD_CARD = (SEC_ERROR_BASE + 50),
+XP_SEC_FORTEZZA_NO_CARD = (SEC_ERROR_BASE + 51),
+XP_SEC_FORTEZZA_NONE_SELECTED = (SEC_ERROR_BASE + 52),
+XP_SEC_FORTEZZA_MORE_INFO = (SEC_ERROR_BASE + 53),
+XP_SEC_FORTEZZA_PERSON_NOT_FOUND = (SEC_ERROR_BASE + 54),
+XP_SEC_FORTEZZA_NO_MORE_INFO = (SEC_ERROR_BASE + 55),
+XP_SEC_FORTEZZA_BAD_PIN = (SEC_ERROR_BASE + 56),
+XP_SEC_FORTEZZA_PERSON_ERROR = (SEC_ERROR_BASE + 57),
+SEC_ERROR_NO_KRL = (SEC_ERROR_BASE + 58),
+SEC_ERROR_KRL_EXPIRED = (SEC_ERROR_BASE + 59),
+SEC_ERROR_KRL_BAD_SIGNATURE = (SEC_ERROR_BASE + 60),
+SEC_ERROR_REVOKED_KEY = (SEC_ERROR_BASE + 61),
+SEC_ERROR_KRL_INVALID = (SEC_ERROR_BASE + 62),
+SEC_ERROR_NEED_RANDOM = (SEC_ERROR_BASE + 63),
+SEC_ERROR_NO_MODULE = (SEC_ERROR_BASE + 64),
+SEC_ERROR_NO_TOKEN = (SEC_ERROR_BASE + 65),
+SEC_ERROR_READ_ONLY = (SEC_ERROR_BASE + 66),
+SEC_ERROR_NO_SLOT_SELECTED = (SEC_ERROR_BASE + 67),
+SEC_ERROR_CERT_NICKNAME_COLLISION = (SEC_ERROR_BASE + 68),
+SEC_ERROR_KEY_NICKNAME_COLLISION = (SEC_ERROR_BASE + 69),
+SEC_ERROR_SAFE_NOT_CREATED = (SEC_ERROR_BASE + 70),
+SEC_ERROR_BAGGAGE_NOT_CREATED = (SEC_ERROR_BASE + 71),
+XP_JAVA_REMOVE_PRINCIPAL_ERROR = (SEC_ERROR_BASE + 72),
+XP_JAVA_DELETE_PRIVILEGE_ERROR = (SEC_ERROR_BASE + 73),
+XP_JAVA_CERT_NOT_EXISTS_ERROR = (SEC_ERROR_BASE + 74),
+SEC_ERROR_BAD_EXPORT_ALGORITHM = (SEC_ERROR_BASE + 75),
+SEC_ERROR_EXPORTING_CERTIFICATES = (SEC_ERROR_BASE + 76),
+SEC_ERROR_IMPORTING_CERTIFICATES = (SEC_ERROR_BASE + 77),
+SEC_ERROR_PKCS12_DECODING_PFX = (SEC_ERROR_BASE + 78),
+SEC_ERROR_PKCS12_INVALID_MAC = (SEC_ERROR_BASE + 79),
+SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM = (SEC_ERROR_BASE + 80),
+SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE = (SEC_ERROR_BASE + 81),
+SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE = (SEC_ERROR_BASE + 82),
+SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM = (SEC_ERROR_BASE + 83),
+SEC_ERROR_PKCS12_UNSUPPORTED_VERSION = (SEC_ERROR_BASE + 84),
+SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT = (SEC_ERROR_BASE + 85),
+SEC_ERROR_PKCS12_CERT_COLLISION = (SEC_ERROR_BASE + 86),
+SEC_ERROR_USER_CANCELLED = (SEC_ERROR_BASE + 87),
+SEC_ERROR_PKCS12_DUPLICATE_DATA = (SEC_ERROR_BASE + 88),
+SEC_ERROR_MESSAGE_SEND_ABORTED = (SEC_ERROR_BASE + 89),
+SEC_ERROR_INADEQUATE_KEY_USAGE = (SEC_ERROR_BASE + 90),
+SEC_ERROR_INADEQUATE_CERT_TYPE = (SEC_ERROR_BASE + 91),
+SEC_ERROR_CERT_ADDR_MISMATCH = (SEC_ERROR_BASE + 92),
+SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY = (SEC_ERROR_BASE + 93),
+SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN = (SEC_ERROR_BASE + 94),
+SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME = (SEC_ERROR_BASE + 95),
+SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY = (SEC_ERROR_BASE + 96),
+SEC_ERROR_PKCS12_UNABLE_TO_WRITE = (SEC_ERROR_BASE + 97),
+SEC_ERROR_PKCS12_UNABLE_TO_READ = (SEC_ERROR_BASE + 98),
+SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED = (SEC_ERROR_BASE + 99),
+SEC_ERROR_KEYGEN_FAIL = (SEC_ERROR_BASE + 100),
+SEC_ERROR_INVALID_PASSWORD = (SEC_ERROR_BASE + 101),
+SEC_ERROR_RETRY_OLD_PASSWORD = (SEC_ERROR_BASE + 102),
+SEC_ERROR_BAD_NICKNAME = (SEC_ERROR_BASE + 103),
+SEC_ERROR_NOT_FORTEZZA_ISSUER = (SEC_ERROR_BASE + 104),
+/* UNUSED (SEC_ERROR_BASE + 105) */
+SEC_ERROR_JS_INVALID_MODULE_NAME = (SEC_ERROR_BASE + 106),
+SEC_ERROR_JS_INVALID_DLL = (SEC_ERROR_BASE + 107),
+SEC_ERROR_JS_ADD_MOD_FAILURE = (SEC_ERROR_BASE + 108),
+SEC_ERROR_JS_DEL_MOD_FAILURE = (SEC_ERROR_BASE + 109),
+SEC_ERROR_OLD_KRL = (SEC_ERROR_BASE + 110),
+SEC_ERROR_CKL_CONFLICT = (SEC_ERROR_BASE + 111),
+SEC_ERROR_CERT_NOT_IN_NAME_SPACE = (SEC_ERROR_BASE + 112),
+SEC_ERROR_KRL_NOT_YET_VALID = (SEC_ERROR_BASE + 113),
+SEC_ERROR_CRL_NOT_YET_VALID = (SEC_ERROR_BASE + 114),
+SEC_ERROR_UNKNOWN_CERT = (SEC_ERROR_BASE + 115),
+SEC_ERROR_UNKNOWN_SIGNER = (SEC_ERROR_BASE + 116),
+SEC_ERROR_CERT_BAD_ACCESS_LOCATION = (SEC_ERROR_BASE + 117),
+SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE = (SEC_ERROR_BASE + 118),
+SEC_ERROR_OCSP_BAD_HTTP_RESPONSE = (SEC_ERROR_BASE + 119),
+SEC_ERROR_OCSP_MALFORMED_REQUEST = (SEC_ERROR_BASE + 120),
+SEC_ERROR_OCSP_SERVER_ERROR = (SEC_ERROR_BASE + 121),
+SEC_ERROR_OCSP_TRY_SERVER_LATER = (SEC_ERROR_BASE + 122),
+SEC_ERROR_OCSP_REQUEST_NEEDS_SIG = (SEC_ERROR_BASE + 123),
+SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST = (SEC_ERROR_BASE + 124),
+SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS = (SEC_ERROR_BASE + 125),
+SEC_ERROR_OCSP_UNKNOWN_CERT = (SEC_ERROR_BASE + 126),
+SEC_ERROR_OCSP_NOT_ENABLED = (SEC_ERROR_BASE + 127),
+SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER = (SEC_ERROR_BASE + 128),
+SEC_ERROR_OCSP_MALFORMED_RESPONSE = (SEC_ERROR_BASE + 129),
+SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE = (SEC_ERROR_BASE + 130),
+SEC_ERROR_OCSP_FUTURE_RESPONSE = (SEC_ERROR_BASE + 131),
+SEC_ERROR_OCSP_OLD_RESPONSE = (SEC_ERROR_BASE + 132),
+/* smime stuff */
+SEC_ERROR_DIGEST_NOT_FOUND = (SEC_ERROR_BASE + 133),
+SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE = (SEC_ERROR_BASE + 134)
+
+} SECErrorCodes;
+#endif /* NO_SECURITY_ERROR_ENUM */
+
+#endif /* __SEC_ERR_H_ */