diff options
author | relyea%netscape.com <devnull@localhost> | 2000-03-31 19:16:26 +0000 |
---|---|---|
committer | relyea%netscape.com <devnull@localhost> | 2000-03-31 19:16:26 +0000 |
commit | 222a52dab759085f56dcb6588b69a6a937d82aa2 (patch) | |
tree | 8cc2b9251b3777cc1b8a9fe5b615d02b0aadece9 /security/nss | |
parent | dc6d4e64be376d12f325301e7e3c5cd048338e9a (diff) | |
download | nss-hg-222a52dab759085f56dcb6588b69a6a937d82aa2.tar.gz |
Initial NSS Open Source Checkin
Diffstat (limited to 'security/nss')
66 files changed, 50361 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/Makefile b/security/nss/lib/pk11wrap/Makefile new file mode 100644 index 000000000..774e68762 --- /dev/null +++ b/security/nss/lib/pk11wrap/Makefile @@ -0,0 +1,77 @@ +#! gmake +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +-include config.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + +export:: private_export + + diff --git a/security/nss/lib/pk11wrap/config.mk b/security/nss/lib/pk11wrap/config.mk new file mode 100644 index 000000000..a73a1086e --- /dev/null +++ b/security/nss/lib/pk11wrap/config.mk @@ -0,0 +1,44 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +# +# Override TARGETS variable so that only static libraries +# are specifed as dependencies within rules.mk. +# + +TARGETS = $(LIBRARY) +SHARED_LIBRARY = +IMPORT_LIBRARY = +PURE_LIBRARY = +PROGRAM = + diff --git a/security/nss/lib/pk11wrap/manifest.mn b/security/nss/lib/pk11wrap/manifest.mn new file mode 100644 index 000000000..b558c6f0d --- /dev/null +++ b/security/nss/lib/pk11wrap/manifest.mn @@ -0,0 +1,62 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# +CORE_DEPTH = ../../.. + +EXPORTS = \ + secmod.h \ + secmodt.h \ + pk11func.h \ + $(NULL) + +PRIVATE_EXPORTS = \ + secmodi.h \ + secmodti.h \ + $(NULL) + +MODULE = security + +CSRCS = \ + pk11cert.c \ + pk11err.c \ + pk11load.c \ + pk11slot.c \ + pk11db.c \ + pk11list.c \ + pk11skey.c \ + pk11kea.c \ + pk11util.c \ + $(NULL) + +REQUIRES = security dbm + +LIBRARY_NAME = pk11wrap diff --git a/security/nss/lib/pk11wrap/pk11cert.c b/security/nss/lib/pk11wrap/pk11cert.c new file mode 100644 index 000000000..da77fc09b --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11cert.c @@ -0,0 +1,2256 @@ +/* + * 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 "prlock.h" +#include "secmodi.h" +#include "pkcs11.h" +#include "pk11func.h" +#include "cert.h" +#include "secitem.h" +#include "key.h" +#include "hasht.h" +#include "secoid.h" +#include "pkcs7t.h" + +#include "certdb.h" +#include "secerr.h" +#include "sslerr.h" + +#define PK11_SEARCH_CHUNKSIZE 10 + +CK_OBJECT_HANDLE +pk11_FindPubKeyByAnyCert(CERTCertificate *cert, PK11SlotInfo **slot, void *wincx); + +/* + * build a cert nickname based on the token name and the label of the + * certificate If the label in NULL, build a label based on the ID. + */ +static int toHex(int x) { return (x < 10) ? (x+'0') : (x+'a'-10); } +#define MAX_CERT_ID 4 +#define DEFAULT_STRING "Cert ID " +static char * +pk11_buildNickname(PK11SlotInfo *slot,CK_ATTRIBUTE *cert_label, + CK_ATTRIBUTE *key_label, CK_ATTRIBUTE *cert_id) +{ + int prefixLen = PORT_Strlen(slot->token_name); + int suffixLen = 0; + char *suffix = NULL; + char buildNew[sizeof(DEFAULT_STRING)+MAX_CERT_ID*2]; + char *next,*nickname; + + if (slot->isInternal) { + return NULL; + } + + if ((cert_label) && (cert_label->pValue)) { + suffixLen = cert_label->ulValueLen; + suffix = (char*)cert_label->pValue; + } else if (key_label && (key_label->pValue)) { + suffixLen = key_label->ulValueLen; + suffix = (char*)key_label->pValue; + } else if ((cert_id) && cert_id->pValue) { + int i,first = cert_id->ulValueLen - MAX_CERT_ID; + int offset = sizeof(DEFAULT_STRING); + char *idValue = (char *)cert_id->pValue; + + PORT_Memcpy(buildNew,DEFAULT_STRING,sizeof(DEFAULT_STRING)-1); + next = buildNew + offset; + if (first < 0) first = 0; + for (i=first; i < (int) cert_id->ulValueLen; i++) { + *next++ = toHex((idValue[i] >> 4) & 0xf); + *next++ = toHex(idValue[i] & 0xf); + } + *next++ = 0; + suffix = buildNew; + suffixLen = PORT_Strlen(buildNew); + } else { + PORT_SetError( SEC_ERROR_LIBRARY_FAILURE ); + return NULL; + } + + next = nickname = (char *)PORT_Alloc(prefixLen+1+suffixLen+1); + if (nickname == NULL) return NULL; + + PORT_Memcpy(next,slot->token_name,prefixLen); + next += prefixLen; + *next++ = ':'; + PORT_Memcpy(next,suffix,suffixLen); + next += suffixLen; + *next++ = 0; + return nickname; +} + +/* + * return the object handle that matches the template + */ +CK_OBJECT_HANDLE +pk11_FindObjectByTemplate(PK11SlotInfo *slot,CK_ATTRIBUTE *theTemplate,int tsize) +{ + CK_OBJECT_HANDLE object; + CK_RV crv; + CK_ULONG objectCount; + + /* + * issue the find + */ + PK11_EnterSlotMonitor(slot); + crv=PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, theTemplate, tsize); + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + return CK_INVALID_KEY; + } + + crv=PK11_GETTAB(slot)->C_FindObjects(slot->session,&object,1,&objectCount); + PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session); + PK11_ExitSlotMonitor(slot); + if ((crv != CKR_OK) || (objectCount < 1)) { + /* shouldn't use SSL_ERROR... here */ + PORT_SetError( crv != CKR_OK ? PK11_MapError(crv) : + SSL_ERROR_NO_CERTIFICATE); + return CK_INVALID_KEY; + } + + /* blow up if the PKCS #11 module returns us and invalid object handle */ + PORT_Assert(object != CK_INVALID_KEY); + return object; +} + +/* + * return all the object handles that matches the template + */ +CK_OBJECT_HANDLE * +pk11_FindObjectsByTemplate(PK11SlotInfo *slot, + CK_ATTRIBUTE *findTemplate,int findCount,int *object_count) { + CK_OBJECT_HANDLE *objID = NULL; + CK_ULONG returned_count = 0; + CK_RV crv; + + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, findTemplate, + findCount); + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + *object_count = -1; + return NULL; + } + + + /* + * collect all the Matching Objects + */ + do { + CK_OBJECT_HANDLE *oldObjID = objID; + + if (objID == NULL) { + objID = (CK_OBJECT_HANDLE *) PORT_Alloc(sizeof(CK_OBJECT_HANDLE)* + (*object_count+ PK11_SEARCH_CHUNKSIZE)); + } else { + objID = (CK_OBJECT_HANDLE *) PORT_Realloc(objID, + sizeof(CK_OBJECT_HANDLE)*(*object_count+PK11_SEARCH_CHUNKSIZE)); + } + + if (objID == NULL) { + if (oldObjID) PORT_Free(oldObjID); + break; + } + crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, + &objID[*object_count],PK11_SEARCH_CHUNKSIZE,&returned_count); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + PORT_Free(objID); + objID = NULL; + break; + } + *object_count += returned_count; + } while (returned_count == PK11_SEARCH_CHUNKSIZE); + + PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session); + PK11_ExitSlotMonitor(slot); + + if (objID && (*object_count == 0)) { + PORT_Free(objID); + return NULL; + } + if (objID == NULL) *object_count = -1; + return objID; +} +/* + * given a PKCS #11 object, match it's peer based on the KeyID. searchID + * is typically a privateKey or a certificate while the peer is the opposite + */ +CK_OBJECT_HANDLE +PK11_MatchItem(PK11SlotInfo *slot, CK_OBJECT_HANDLE searchID, + CK_OBJECT_CLASS matchclass) +{ + CK_ATTRIBUTE theTemplate[] = { + { CKA_ID, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + CK_ATTRIBUTE *keyclass = &theTemplate[1]; + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + /* if you change the array, change the variable below as well */ + CK_OBJECT_HANDLE peerID; + CK_OBJECT_HANDLE parent; + PRArenaPool *arena; + CK_RV crv; + + /* now we need to create space for the public key */ + arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) return CK_INVALID_KEY; + + crv = PK11_GetAttributes(arena,slot,searchID,theTemplate,tsize); + if (crv != CKR_OK) { + PORT_FreeArena(arena,PR_FALSE); + PORT_SetError( PK11_MapError(crv) ); + return CK_INVALID_KEY; + } + + /* + * issue the find + */ + parent = *(CK_OBJECT_CLASS *)(keyclass->pValue); + *(CK_OBJECT_CLASS *)(keyclass->pValue) = matchclass; + + peerID = pk11_FindObjectByTemplate(slot,theTemplate,tsize); + PORT_FreeArena(arena,PR_FALSE); + + return peerID; +} + +PRBool +PK11_IsUserCert(PK11SlotInfo *slot, CERTCertificate *cert, + CK_OBJECT_HANDLE certID) +{ + CK_OBJECT_CLASS theClass; + + if (slot == NULL) return PR_FALSE; + if (cert == NULL) return PR_FALSE; + + theClass = CKO_PRIVATE_KEY; + if (!PK11_IsLoggedIn(slot,NULL) && PK11_NeedLogin(slot)) { + theClass = CKO_PUBLIC_KEY; + } + if (PK11_MatchItem(slot, certID , theClass) != CK_INVALID_KEY) { + return PR_TRUE; + } + + if (theClass == CKO_PUBLIC_KEY) { + SECKEYPublicKey *pubKey= CERT_ExtractPublicKey(cert); + CK_ATTRIBUTE theTemplate; + + if (pubKey == NULL) { + return PR_FALSE; + } + + PK11_SETATTRS(&theTemplate,0,NULL,0); + switch (pubKey->keyType) { + case rsaKey: + PK11_SETATTRS(&theTemplate,CKA_MODULUS, pubKey->u.rsa.modulus.data, + pubKey->u.rsa.modulus.len); + break; + case dsaKey: + PK11_SETATTRS(&theTemplate,CKA_VALUE, pubKey->u.dsa.publicValue.data, + pubKey->u.dsa.publicValue.len); + case dhKey: + PK11_SETATTRS(&theTemplate,CKA_VALUE, pubKey->u.dh.publicValue.data, + pubKey->u.dh.publicValue.len); + break; + } + + if (theTemplate.ulValueLen == 0) { + SECKEY_DestroyPublicKey(pubKey); + return PR_FALSE; + } + pk11_SignedToUnsigned(&theTemplate); + if (pk11_FindObjectByTemplate(slot,&theTemplate,1) != CK_INVALID_KEY) { + SECKEY_DestroyPublicKey(pubKey); + return PR_TRUE; + } + SECKEY_DestroyPublicKey(pubKey); + } + return PR_FALSE; +} + +/* + * Check out if a cert has ID of zero. This is a magic ID that tells + * NSS that this cert may be an automagically trusted cert. + * The Cert has to be self signed as well. That check is done elsewhere. + * + */ +PRBool +pk11_isID0(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID) +{ + CK_ATTRIBUTE keyID = {CKA_ID, NULL, 0}; + PRBool isZero = PR_FALSE; + int i; + CK_RV crv; + + + crv = PK11_GetAttributes(NULL,slot,certID,&keyID,1); + if (crv != CKR_OK) { + return isZero; + } + + if (keyID.ulValueLen != 0) { + char *value = (char *)keyID.pValue; + isZero = PR_TRUE; /* ID exists, may be zero */ + for (i=0; i < (int) keyID.ulValueLen; i++) { + if (value[i] != 0) { + isZero = PR_FALSE; /* nope */ + break; + } + } + } + PORT_Free(keyID.pValue); + return isZero; + +} + +CERTCertificate +*pk11_fastCert(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID, + CK_ATTRIBUTE *privateLabel, char **nickptr) +{ + CK_ATTRIBUTE certTemp[] = { + { CKA_ID, NULL, 0 }, + { CKA_VALUE, NULL, 0 }, + { CKA_LABEL, NULL, 0 } + }; + CK_ATTRIBUTE *id = &certTemp[0]; + CK_ATTRIBUTE *certDER = &certTemp[1]; + CK_ATTRIBUTE *label = &certTemp[2]; + SECItem derCert; + int csize = sizeof(certTemp)/sizeof(certTemp[0]); + PRArenaPool *arena; + char *nickname; + CERTCertificate *cert; + CK_RV crv; + + arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) return NULL; + /* + * grab the der encoding + */ + crv = PK11_GetAttributes(arena,slot,certID,certTemp,csize); + if (crv != CKR_OK) { + PORT_FreeArena(arena,PR_FALSE); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + + /* + * build a certificate out of it + */ + derCert.data = (unsigned char*)certDER->pValue; + derCert.len = certDER->ulValueLen; + + /* figure out the nickname.... */ + nickname = pk11_buildNickname(slot,label,privateLabel,id); + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, nickname, + PR_FALSE, PR_TRUE); + if (nickptr) { + *nickptr = nickname; + } else { + if (nickname) PORT_Free(nickname); + } + PORT_FreeArena(arena,PR_FALSE); + return cert; +} + +/* + * Build an CERTCertificate structure from a PKCS#11 object ID.... certID + * Must be a CertObject. This code does not explicitly checks that. + */ +CERTCertificate * +PK11_MakeCertFromHandle(PK11SlotInfo *slot,CK_OBJECT_HANDLE certID, + CK_ATTRIBUTE *privateLabel) +{ + char * nickname = NULL; + CERTCertificate *cert = NULL; + CERTCertTrust *trust; + PRBool isFortezzaRootCA = PR_FALSE; + PRBool swapNickname = PR_FALSE; + + cert = pk11_fastCert(slot,certID,privateLabel, &nickname); + if (cert == NULL) goto loser; + + if (nickname) { + if (cert->nickname != NULL) { + cert->dbnickname = cert->nickname; + } + cert->nickname = PORT_ArenaStrdup(cert->arena,nickname); + PORT_Free(nickname); + nickname = NULL; + swapNickname = PR_TRUE; + } + + /* remember where this cert came from.... If we have just looked + * it up from the database and it already has a slot, don't add a new + * one. */ + if (cert->slot == NULL) { + cert->slot = PK11_ReferenceSlot(slot); + cert->pkcs11ID = certID; + cert->ownSlot = PR_TRUE; + } + + if (cert->trust == NULL) { + unsigned int type; + + trust = + (CERTCertTrust*)PORT_ArenaAlloc(cert->arena, sizeof(CERTCertTrust)); + if (trust == NULL) goto loser; + + PORT_Memset(trust,0, sizeof(CERTCertTrust)); + cert->trust = trust; + /* build some cert trust flags */ + if (CERT_IsCACert(cert, &type)) { + unsigned int trustflags = CERTDB_VALID_CA; + + /* Allow PKCS #11 modules to give us trusted CA's. We only accept + * valid CA's which are self-signed here. They must have an object + * ID of '0'. */ + if (pk11_isID0(slot,certID) && + SECITEM_CompareItem(&cert->derSubject,&cert->derIssuer) + == SECEqual) { + trustflags |= CERTDB_TRUSTED_CA; + /* is the slot a fortezza card? allow the user or + * admin to turn on objectSigning, but don't turn + * full trust on explicitly */ + if (PK11_DoesMechanism(slot,CKM_KEA_KEY_DERIVE)) { + trust->objectSigningFlags |= CERTDB_VALID_CA; + isFortezzaRootCA = PR_TRUE; + } + } + if ((type & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) { + trust->sslFlags |= trustflags; + } + if ((type & NS_CERT_TYPE_EMAIL_CA) == NS_CERT_TYPE_EMAIL_CA) { + trust->emailFlags |= trustflags; + } + if ((type & NS_CERT_TYPE_OBJECT_SIGNING_CA) + == NS_CERT_TYPE_OBJECT_SIGNING_CA) { + trust->objectSigningFlags |= trustflags; + } + } + } else { + trust = cert->trust; + } + + if (PK11_IsUserCert(slot,cert,certID)) { + trust->sslFlags |= CERTDB_USER; + trust->emailFlags |= CERTDB_USER; + /* trust->objectSigningFlags |= CERTDB_USER; */ + } + + + /* if fortezza, write the root cert to the DB */ + if ((isFortezzaRootCA) && (!cert->isperm)) { + char *name = NULL; + if (swapNickname) { + nickname = cert->nickname; + cert->nickname = cert->dbnickname; + } + if (cert->nickname) { + name = PORT_Strdup(cert->nickname); + } + if (name == NULL) name = CERT_MakeCANickname(cert); + CERT_AddTempCertToPerm(cert,name,cert->trust); + if (name) PORT_Free(name); + if (swapNickname) { + if (cert->nickname != NULL) { + cert->dbnickname = cert->nickname; + } + cert->nickname = PORT_ArenaStrdup(cert->arena,nickname); + } + + } + + return cert; + +loser: + if (nickname) PORT_Free(nickname); + if (cert) CERT_DestroyCertificate(cert); + return NULL; +} + + +/* + * Build get a certificate from a private key + */ +CERTCertificate * +PK11_GetCertFromPrivateKey(SECKEYPrivateKey *privKey) +{ + PK11SlotInfo *slot = privKey->pkcs11Slot; + CK_OBJECT_HANDLE certID = + PK11_MatchItem(slot,privKey->pkcs11ID,CKO_CERTIFICATE); + SECStatus rv; + CERTCertificate *cert; + + if (certID == CK_INVALID_KEY) { + /* couldn't find it on the card, look in our data base */ + SECItem derSubject; + + rv = PK11_ReadAttribute(slot, privKey->pkcs11ID, CKA_SUBJECT, NULL, + &derSubject); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_NO_CERTIFICATE); + return NULL; + } + + cert = CERT_FindCertByName(CERT_GetDefaultCertDB(),&derSubject); + PORT_Free(derSubject.data); + return cert; + } + cert = PK11_MakeCertFromHandle(slot,certID,NULL); + return (cert); + +} + +/* + * destroy a private key if there are no matching certs. + * this function also frees the privKey structure. + */ +SECStatus +PK11_DeleteTokenPrivateKey(SECKEYPrivateKey *privKey) +{ + CERTCertificate *cert=PK11_GetCertFromPrivateKey(privKey); + + /* found a cert matching the private key?. */ + if (cert != NULL) { + /* yes, don't delete the key */ + CERT_DestroyCertificate(cert); + SECKEY_DestroyPrivateKey(privKey); + return SECWouldBlock; + } + /* now, then it's safe for the key to go away */ + PK11_DestroyTokenObject(privKey->pkcs11Slot,privKey->pkcs11ID); + SECKEY_DestroyPrivateKey(privKey); + return SECSuccess; +} + + +/* + * delete a cert and it's private key (if no other certs are pointing to the + * private key. + */ +SECStatus +PK11_DeleteTokenCertAndKey(CERTCertificate *cert,void *wincx) +{ + SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert,wincx); + CK_OBJECT_HANDLE pubKey; + PK11SlotInfo *slot = NULL; + + pubKey = pk11_FindPubKeyByAnyCert(cert, &slot, wincx); + if (privKey) { + PK11_DestroyTokenObject(cert->slot,cert->pkcs11ID); + PK11_DeleteTokenPrivateKey(privKey); + } + if ((pubKey != CK_INVALID_KEY) && (slot != NULL)) { + PK11_DestroyTokenObject(slot,pubKey); + PK11_FreeSlot(slot); + } + return SECSuccess; +} + +/* + * count the number of objects that match the template. + */ +int +PK11_NumberObjectsFor(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate, + int templateCount) +{ + CK_OBJECT_HANDLE objID[PK11_SEARCH_CHUNKSIZE]; + int object_count = 0; + CK_ULONG returned_count = 0; + CK_RV crv; + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, + findTemplate, templateCount); + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + return 0; + } + + /* + * collect all the Matching Objects + */ + do { + crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, + objID,PK11_SEARCH_CHUNKSIZE,&returned_count); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + break; + } + object_count += returned_count; + } while (returned_count == PK11_SEARCH_CHUNKSIZE); + + PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session); + PK11_ExitSlotMonitor(slot); + return object_count; +} + +/* + * cert callback structure + */ +typedef struct pk11DoCertCallbackStr { + SECStatus(* callback)(PK11SlotInfo *slot, CERTCertificate*, void *); + SECStatus(* noslotcallback)(CERTCertificate*, void *); + void *callbackArg; +} pk11DoCertCallback; + +/* + * callback to map object handles to certificate structures. + */ +SECStatus +pk11_DoCerts(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID, void *arg) +{ + CERTCertificate *cert; + pk11DoCertCallback *certcb = (pk11DoCertCallback *) arg; + + cert = PK11_MakeCertFromHandle(slot, certID, NULL); + + if (cert == NULL) { + return SECFailure; + } + + if (certcb ) { + if (certcb->callback) { + (*certcb->callback)(slot, cert, certcb->callbackArg); + } + if (certcb->noslotcallback) { + (*certcb->noslotcallback)(cert, certcb->callbackArg); + } + } + + CERT_DestroyCertificate(cert); + + return SECSuccess; +} + + +/* + * key call back structure. + */ +typedef struct pk11KeyCallbackStr { + SECStatus (* callback)(SECKEYPrivateKey *,void *); + void *callbackArg; + void *wincx; +} pk11KeyCallback; + +/* + * callback to map Object Handles to Private Keys; + */ +SECStatus +pk11_DoKeys(PK11SlotInfo *slot, CK_OBJECT_HANDLE keyHandle, void *arg) +{ + SECStatus rv = SECSuccess; + SECKEYPrivateKey *privKey; + pk11KeyCallback *keycb = (pk11KeyCallback *) arg; + + privKey = PK11_MakePrivKey(slot,nullKey,PR_TRUE,keyHandle,keycb->wincx); + + if (privKey == NULL) { + return SECFailure; + } + + if (keycb && (keycb->callback)) { + rv = (*keycb->callback)(privKey,keycb->callbackArg); + } + + SECKEY_DestroyPrivateKey(privKey); + return rv; +} + + +/* Traverse slots callback */ +typedef struct pk11TraverseSlotStr { + SECStatus (*callback)(PK11SlotInfo *,CK_OBJECT_HANDLE, void *); + void *callbackArg; + CK_ATTRIBUTE *findTemplate; + int templateCount; +} pk11TraverseSlotCert; + +/* + * Extract all the certs on a card from a slot. + */ +SECStatus +PK11_TraverseSlot(PK11SlotInfo *slot, void *arg) +{ + int i; + CK_OBJECT_HANDLE *objID = NULL; + int object_count = 0; + CK_ULONG returned_count = 0; + pk11TraverseSlotCert *slotcb = (pk11TraverseSlotCert *) arg; + + objID = pk11_FindObjectsByTemplate(slot,slotcb->findTemplate, + slotcb->templateCount,&object_count); + + /*Actually this isn't a failure... there just were no objs to be found*/ + if (object_count == 0) { + return SECSuccess; + } + + if (objID == NULL) { + return SECFailure; + } + + for (i=0; i < object_count; i++) { + (*slotcb->callback)(slot,objID[i],slotcb->callbackArg); + } + PORT_Free(objID); + return SECSuccess; +} + +typedef struct pk11CertCallbackStr { + SECStatus(* callback)(CERTCertificate*,SECItem *,void *); + void *callbackArg; +} pk11CertCallback; + +static SECStatus +pk11_SaveCert(PK11SlotInfo *slot, CERTCertificate *cert, void *arg) +{ + pk11CertCallback *certcb = (pk11CertCallback *)arg; + SECStatus rv = SECSuccess; + + if (slot->cert_count == slot->array_size) return CKR_OK; + + slot->cert_array[slot->cert_count] = CERT_DupCertificate(cert); + if (slot->cert_array[slot->cert_count] == NULL) { + return SECFailure; + } + /* now the slot has a hold of the cert, free the slot's element in the + * cert.. */ + if (cert->ownSlot && (slot == cert->slot)) { + PK11_FreeSlot(cert->slot); + cert->ownSlot = PR_FALSE; + } + slot->cert_count++; + + if (certcb->callback) { + rv = (*certcb->callback)(cert, NULL, certcb->callbackArg); + } + return rv; +} + + +/* free the slots */ +void +PK11_FreeSlotCerts(PK11SlotInfo *slot) +{ + int i; + + if (slot->cert_array) { + for (i=0; i < slot->cert_count; i++) { + /* if we point the cert on our array, the cert doesn't have a + * reference to use (otherwise you would never be able to free + * a slot :) */ + if ((slot->cert_array[i]->slot == slot) && + (!slot->cert_array[i]->ownSlot)) { + slot->cert_array[i]->slot = NULL; + } + CERT_DestroyCertificate(slot->cert_array[i]); + } + PORT_Free(slot->cert_array); + slot->cert_array = NULL; + slot->cert_count = 0; + } + return; +} + +/* + * Update PQG parameters for all the certs on a slot. + */ +static SECStatus +pk11_UpdateSlotPQG(PK11SlotInfo *slot) +{ + int i, tag; + CERTCertificate * cert; + SECOidData *oid; + SECStatus rv1 = SECSuccess; + SECStatus rv2 = SECSuccess; + + if (slot->cert_array) { + for (i=0; i < slot->cert_count; i++) { + + cert = slot->cert_array[i]; + + oid = SECOID_FindOID(&cert->subjectPublicKeyInfo.algorithm.algorithm); + + if (oid != NULL) { + tag = oid->offset; + + /* Check if cert has a DSA or Fortezza public key */ + if ( (tag == SEC_OID_MISSI_KEA_DSS_OLD) || + (tag == SEC_OID_MISSI_DSS_OLD) || + (tag == SEC_OID_MISSI_KEA_DSS) || + (tag == SEC_OID_MISSI_DSS) || + (tag == SEC_OID_ANSIX9_DSA_SIGNATURE) || + (tag == SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST) || + (tag == SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST) ) { + + /* update PQG parameters */ + + rv1 = SECKEY_UpdateCertPQG(cert); + if (rv1 == SECFailure) { + rv2 = rv1; + } + } + } /* end of if oid != NULL */ + } /* end of for loop */ + } + return rv2; +} + + +/* + * Extract all the certs on a card from a slot. + */ +static SECStatus +pk11_ExtractCertsFromSlot(PK11SlotInfo *slot, void *arg) +{ + pk11TraverseSlotCert *slotcb = (pk11TraverseSlotCert *) arg; + int object_count; + SECStatus rv; + + rv = SECSuccess; + + PK11_FreeSlotCerts(slot); + + object_count = PK11_NumberObjectsFor(slot,slotcb->findTemplate, + slotcb->templateCount); + + /*Actually this isn't a failure... there just were no certs to be found*/ + if (object_count == 0) { + return SECSuccess; + } + + slot->cert_array = (CERTCertificate **) + PORT_Alloc(sizeof(CERTCertificate *)*object_count); + if (slot->cert_array == NULL) { + return SECFailure; + } + slot->cert_count = 0; + slot->array_size = object_count; + PK11_TraverseSlot(slot,arg); + + /* Update the PQG parameters for the extracted certs. */ + rv = pk11_UpdateSlotPQG(slot); + + return rv; +} + +/* + * read all the certs from a slot + */ +SECStatus +PK11_ReadSlotCerts(PK11SlotInfo *slot) +{ + + /* build slot list */ + pk11CertCallback caller; + pk11DoCertCallback saver; + pk11TraverseSlotCert creater; + CK_ATTRIBUTE theTemplate; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + + PK11_SETATTRS(&theTemplate, CKA_CLASS, &certClass, sizeof(certClass)); + + caller.callback = NULL; + caller.callbackArg = NULL; + saver.callback = pk11_SaveCert; + saver.noslotcallback = NULL; + saver.callbackArg = (void *) & caller; + creater.callback = pk11_DoCerts; + creater.callbackArg = (void *) & saver; + creater.findTemplate = &theTemplate; + creater.templateCount = 1; + + return pk11_ExtractCertsFromSlot(slot, &creater); +} + +/* + * Extract all the certs on a card from a slot. + */ +static SECStatus +pk11_TraverseAllSlots(PRBool loadCerts, + SECStatus (*callback)(PK11SlotInfo *,void *),void *arg,void *wincx) { + + PK11SlotList *list; + PK11SlotListElement *le; + SECStatus rv; + + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,loadCerts,wincx); + if (list == NULL) return SECFailure; + + /* look at each slot and authenticate as necessary */ + for (le = list->head ; le; le = le->next) { + /* don't nab internal slots */ + if ((!loadCerts) && le->slot->isInternal == PR_TRUE) { + continue; + } + if (loadCerts || !PK11_IsFriendly(le->slot)) { + rv = PK11_Authenticate(le->slot, loadCerts, wincx); + if (rv != SECSuccess) continue; + } + (*callback)(le->slot,arg); + } + + PK11_FreeSlotList(list); + + return SECSuccess; +} + +/* + * Extract all the certs on a card from a slot. + */ +SECStatus +PK11_TraverseSlotCerts(SECStatus(* callback)(CERTCertificate*,SECItem *,void *), + void *arg, void *wincx) { + pk11CertCallback caller; + pk11DoCertCallback saver; + pk11TraverseSlotCert creater; + CK_ATTRIBUTE theTemplate; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + + PK11_SETATTRS(&theTemplate, CKA_CLASS, &certClass, sizeof(certClass)); + + caller.callback = callback; + caller.callbackArg = arg; + saver.callback = pk11_SaveCert; + saver.noslotcallback = NULL; + saver.callbackArg = (void *) & caller; + creater.callback = pk11_DoCerts; + creater.callbackArg = (void *) & saver; + creater.findTemplate = &theTemplate; + creater.templateCount = 1; + + return pk11_TraverseAllSlots(PR_FALSE, pk11_ExtractCertsFromSlot, + &creater, wincx); +} + +CK_OBJECT_HANDLE * +PK11_FindObjectsFromNickname(char *nickname,PK11SlotInfo **slotptr, + CK_OBJECT_CLASS objclass, int *returnCount, void *wincx) { + char *tokenName; + char *delimit; + PK11SlotInfo *slot; + CK_OBJECT_HANDLE *objID; + CK_ATTRIBUTE findTemplate[] = { + { CKA_LABEL, NULL, 0}, + { CKA_CLASS, NULL, 0}, + }; + int findCount = sizeof(findTemplate)/sizeof(findTemplate[0]); + SECStatus rv; + PK11_SETATTRS(&findTemplate[1], CKA_CLASS, &objclass, sizeof(objclass)); + + *slotptr = slot = NULL; + *returnCount = 0; + /* first find the slot associated with this nickname */ + if ((delimit = PORT_Strchr(nickname,':')) != NULL) { + int len = delimit - nickname; + tokenName = (char*)PORT_Alloc(len+1); + PORT_Memcpy(tokenName,nickname,len); + tokenName[len] = 0; + + slot = *slotptr = PK11_FindSlotByName(tokenName); + PORT_Free(tokenName); + /* if we couldn't find a slot, assume the nickname is an internal cert + * with no proceding slot name */ + if (slot == NULL) { + slot = *slotptr = PK11_GetInternalKeySlot(); + } else { + nickname = delimit+1; + } + } else { + *slotptr = slot = PK11_GetInternalKeySlot(); + } + if (slot == NULL) { + return CK_INVALID_KEY; + } + + if (!PK11_IsFriendly(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + PK11_FreeSlot(slot); + *slotptr = NULL; + return CK_INVALID_KEY; + } + } + + findTemplate[0].pValue = nickname; + findTemplate[0].ulValueLen = PORT_Strlen(nickname); + objID = pk11_FindObjectsByTemplate(slot,findTemplate,findCount,returnCount); + if (objID == NULL) { + /* PKCS #11 isn't clear on whether or not the NULL is + * stored in the template.... try the find again with the + * full null terminated string. */ + findTemplate[0].ulValueLen += 1; + objID = pk11_FindObjectsByTemplate(slot,findTemplate,findCount, + returnCount); + if (objID == NULL) { + /* Well that's the best we can do. It's just not here */ + /* what about faked nicknames? */ + PK11_FreeSlot(slot); + *slotptr = NULL; + *returnCount = 0; + } + } + + return objID; +} + + +CERTCertificate * +PK11_FindCertFromNickname(char *nickname, void *wincx) { + PK11SlotInfo *slot; + int count=0; + CK_OBJECT_HANDLE *certID = PK11_FindObjectsFromNickname(nickname,&slot, + CKO_CERTIFICATE, &count, wincx); + CERTCertificate *cert; + + if (certID == CK_INVALID_KEY) return NULL; + cert = PK11_MakeCertFromHandle(slot,certID[0],NULL); + PK11_FreeSlot(slot); + PORT_Free(certID); + return cert; +} + +CERTCertList * +PK11_FindCertsFromNickname(char *nickname, void *wincx) { + PK11SlotInfo *slot; + int i,count = 0; + CK_OBJECT_HANDLE *certID = PK11_FindObjectsFromNickname(nickname,&slot, + CKO_CERTIFICATE, &count, wincx); + CERTCertList *certList = NULL; + + if (certID == NULL) return NULL; + + certList= CERT_NewCertList(); + + for (i=0; i < count; i++) { + CERTCertificate *cert = PK11_MakeCertFromHandle(slot,certID[i],NULL); + + if (cert) CERT_AddCertToListTail(certList,cert); + } + + if (CERT_LIST_HEAD(certList) == NULL) { + CERT_DestroyCertList(certList); + certList = NULL; + } + PK11_FreeSlot(slot); + PORT_Free(certID); + return certList; +} + +/* + * extract a key ID for a certificate... + * NOTE: We call this function from PKCS11.c If we ever use + * pkcs11 to extract the public key (we currently do not), this will break. + */ +SECItem * +PK11_GetPubIndexKeyID(CERTCertificate *cert) { + SECKEYPublicKey *pubk; + SECItem *newItem = NULL; + + pubk = CERT_ExtractPublicKey(cert); + if (pubk == NULL) return NULL; + + switch (pubk->keyType) { + case rsaKey: + newItem = SECITEM_DupItem(&pubk->u.rsa.modulus); + break; + case dsaKey: + newItem = SECITEM_DupItem(&pubk->u.dsa.publicValue); + break; + case dhKey: + newItem = SECITEM_DupItem(&pubk->u.dh.publicValue); + case fortezzaKey: + default: + newItem = NULL; /* Fortezza Fix later... */ + } + SECKEY_DestroyPublicKey(pubk); + /* make hash of it */ + return newItem; +} + +/* + * generate a CKA_ID from a certificate. + */ +SECItem * +pk11_mkcertKeyID(CERTCertificate *cert) { + SECItem *pubKeyData = PK11_GetPubIndexKeyID(cert) ; + SECItem *certCKA_ID; + + if (pubKeyData == NULL) return NULL; + + certCKA_ID = PK11_MakeIDFromPubKey(pubKeyData); + SECITEM_FreeItem(pubKeyData,PR_TRUE); + return certCKA_ID; +} + + +/* + * Generate a CKA_ID from the relevant public key data. The CKA_ID is generated + * from the pubKeyData by SHA1_Hashing it to produce a smaller CKA_ID (to make + * smart cards happy. + */ +SECItem * +PK11_MakeIDFromPubKey(SECItem *pubKeyData) { + PK11Context *context; + SECItem *certCKA_ID; + SECStatus rv; + + context = PK11_CreateDigestContext(SEC_OID_SHA1); + if (context == NULL) { + return NULL; + } + + rv = PK11_DigestBegin(context); + if (rv == SECSuccess) { + rv = PK11_DigestOp(context,pubKeyData->data,pubKeyData->len); + } + if (rv != SECSuccess) { + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + + certCKA_ID = (SECItem *)PORT_Alloc(sizeof(SECItem)); + if (certCKA_ID == NULL) { + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + + certCKA_ID->len = SHA1_LENGTH; + certCKA_ID->data = (unsigned char*)PORT_Alloc(certCKA_ID->len); + if (certCKA_ID->data == NULL) { + PORT_Free(certCKA_ID); + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + + rv = PK11_DigestFinal(context,certCKA_ID->data,&certCKA_ID->len, + SHA1_LENGTH); + PK11_DestroyContext(context,PR_TRUE); + if (rv != SECSuccess) { + SECITEM_FreeItem(certCKA_ID,PR_TRUE); + return NULL; + } + + return certCKA_ID; +} + +/* + * Write the cert into the token. + */ +SECStatus +PK11_ImportCert(PK11SlotInfo *slot, CERTCertificate *cert, + CK_OBJECT_HANDLE key, char *nickname, PRBool includeTrust) { + int len = 0; + SECItem *keyID = pk11_mkcertKeyID(cert); + CK_ATTRIBUTE keyAttrs[] = { + { CKA_LABEL, NULL, 0}, + { CKA_SUBJECT, NULL, 0}, + }; + CK_OBJECT_CLASS certc = CKO_CERTIFICATE; + CK_CERTIFICATE_TYPE certType = CKC_X_509; + CK_OBJECT_HANDLE certID; + CK_SESSION_HANDLE rwsession; + CK_BBOOL cktrue = CK_TRUE; + SECStatus rv = SECFailure; + CK_ATTRIBUTE certAttrs[] = { + { CKA_ID, NULL, 0 }, + { CKA_LABEL, NULL, 0}, + { CKA_CLASS, NULL, 0}, + { CKA_TOKEN, NULL, 0}, + { CKA_CERTIFICATE_TYPE, NULL, 0}, + { CKA_SUBJECT, NULL, 0}, + { CKA_ISSUER, NULL, 0}, + { CKA_SERIAL_NUMBER, NULL, 0}, + { CKA_VALUE, NULL, 0}, + { CKA_NETSCAPE_TRUST, NULL, 0}, + }; + int certCount = sizeof(certAttrs)/sizeof(certAttrs[0]), keyCount = 2; + CK_ATTRIBUTE *attrs; + CK_RV crv; + SECCertUsage *certUsage = NULL; + + if (keyID == NULL) { + PORT_SetError(SEC_ERROR_ADDING_CERT); + return rv; + } + + len = ((nickname) ? PORT_Strlen(nickname) : 0); + + attrs = certAttrs; + PK11_SETATTRS(attrs,CKA_ID, keyID->data, keyID->len); attrs++; + if(nickname) { + PK11_SETATTRS(attrs,CKA_LABEL, nickname, len ); attrs++; + } + PK11_SETATTRS(attrs,CKA_CLASS, &certc, sizeof(certc) ); attrs++; + PK11_SETATTRS(attrs,CKA_TOKEN, &cktrue, sizeof(cktrue) ); attrs++; + PK11_SETATTRS(attrs,CKA_CERTIFICATE_TYPE, &certType, + sizeof(certType)); attrs++; + PK11_SETATTRS(attrs,CKA_SUBJECT, cert->derSubject.data, + cert->derSubject.len ); attrs++; + PK11_SETATTRS(attrs,CKA_ISSUER, cert->derIssuer.data, + cert->derIssuer.len ); attrs++; + PK11_SETATTRS(attrs,CKA_SERIAL_NUMBER, cert->serialNumber.data, + cert->serialNumber.len); attrs++; + PK11_SETATTRS(attrs,CKA_VALUE, cert->derCert.data, cert->derCert.len); + if(includeTrust && PK11_IsInternal(slot)) { + attrs++; + certUsage = (SECCertUsage*)PORT_Alloc(sizeof(SECCertUsage)); + if(!certUsage) { + SECITEM_FreeItem(keyID,PR_TRUE); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return rv; + } + *certUsage = certUsageUserCertImport; + PK11_SETATTRS(attrs,CKA_NETSCAPE_TRUST, certUsage, sizeof(SECCertUsage)); + } else { + certCount--; + } + + attrs = keyAttrs; + if(nickname) { + PK11_SETATTRS(attrs,CKA_LABEL, nickname, len ); attrs++; + } + PK11_SETATTRS(attrs,CKA_SUBJECT, cert->derSubject.data, + cert->derSubject.len ); + + if(!nickname) { + certCount--; + keyCount--; + } + + rwsession = PK11_GetRWSession(slot); + crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession,key,keyAttrs, + keyCount); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + goto done; + } + + crv = PK11_GETTAB(slot)-> + C_CreateObject(rwsession,certAttrs,certCount,&certID); + if (crv == CKR_OK) { + rv = SECSuccess; + } else { + PORT_SetError( PK11_MapError(crv) ); + } + +done: + SECITEM_FreeItem(keyID,PR_TRUE); + PK11_RestoreROSession(slot,rwsession); + if(certUsage) { + PORT_Free(certUsage); + } + return rv; + +} + +/* + * get a certificate handle, look at the cached handle first.. + */ +CK_OBJECT_HANDLE +pk11_getcerthandle(PK11SlotInfo *slot, CERTCertificate *cert, + CK_ATTRIBUTE *theTemplate,int tsize) +{ + CK_OBJECT_HANDLE certh; + + if (cert->slot == slot) { + certh = cert->pkcs11ID; + if (certh == CK_INVALID_KEY) { + certh = pk11_FindObjectByTemplate(slot,theTemplate,tsize); + cert->pkcs11ID = certh; + } + } else { + certh = pk11_FindObjectByTemplate(slot,theTemplate,tsize); + } + return certh; +} + +/* + * return the private key From a given Cert + */ +SECKEYPrivateKey * +PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx) { + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_VALUE, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_OBJECT_HANDLE certh; + CK_OBJECT_HANDLE keyh; + CK_ATTRIBUTE *attrs = theTemplate; + SECStatus rv; + + PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, + cert->derCert.len); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + /* + * issue the find + */ + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + return NULL; + } + + certh = pk11_getcerthandle(slot,cert,theTemplate,tsize); + if (certh == CK_INVALID_KEY) { + return NULL; + } + keyh = PK11_MatchItem(slot,certh,CKO_PRIVATE_KEY); + if (keyh == CK_INVALID_KEY) { return NULL; } + return PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyh, wincx); +} + + +/* + * return the private key with the given ID + */ +static CK_OBJECT_HANDLE +pk11_FindPrivateKeyFromCertID(PK11SlotInfo *slot, SECItem *keyID) { + CK_OBJECT_CLASS privKey = CKO_PRIVATE_KEY; + CK_ATTRIBUTE theTemplate[] = { + { CKA_ID, NULL, 0 }, + { CKA_CLASS, NULL, 0 }, + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_ATTRIBUTE *attrs = theTemplate; + + PK11_SETATTRS(attrs, CKA_ID, keyID->data, keyID->len ); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &privKey, sizeof(privKey)); + + return pk11_FindObjectByTemplate(slot,theTemplate,tsize); +} + +/* + * import a cert for a private key we have already generated. Set the label + * on both to be the nickname. This is for the Key Gen, orphaned key case. + */ +PK11SlotInfo * +PK11_KeyForCertExists(CERTCertificate *cert, CK_OBJECT_HANDLE *keyPtr, + void *wincx) { + PK11SlotList *list; + PK11SlotListElement *le; + SECItem *keyID; + CK_OBJECT_HANDLE key; + PK11SlotInfo *slot = NULL; + SECStatus rv; + + keyID = pk11_mkcertKeyID(cert); + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); + if ((keyID == NULL) || (list == NULL)) { + if (keyID) SECITEM_FreeItem(keyID,PR_TRUE); + if (list) PK11_FreeSlotList(list); + return NULL; + } + + /* Look for the slot that holds the Key */ + for (le = list->head ; le; le = le->next) { + rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); + if (rv != SECSuccess) continue; + + key = pk11_FindPrivateKeyFromCertID(le->slot,keyID); + if (key != CK_INVALID_KEY) { + slot = PK11_ReferenceSlot(le->slot); + if (keyPtr) *keyPtr = key; + break; + } + } + + SECITEM_FreeItem(keyID,PR_TRUE); + PK11_FreeSlotList(list); + return slot; + +} + +PK11SlotInfo * +PK11_ImportCertForKey(CERTCertificate *cert, char *nickname,void *wincx) { + PK11SlotInfo *slot = NULL; + CK_OBJECT_HANDLE key; + + slot = PK11_KeyForCertExists(cert,&key,wincx); + + if (slot) { + if (PK11_ImportCert(slot,cert,key,nickname,PR_FALSE) != SECSuccess) { + PK11_FreeSlot(slot); + slot = NULL; + } + } else { + PORT_SetError(SEC_ERROR_ADDING_CERT); + } + + return slot; +} + +static CK_OBJECT_HANDLE +pk11_FindCertObjectByTemplate(PK11SlotInfo **slotPtr, + CK_ATTRIBUTE *searchTemplate, int count, void *wincx) { + PK11SlotList *list; + PK11SlotListElement *le; + CK_OBJECT_HANDLE certHandle = CK_INVALID_KEY; + PK11SlotInfo *slot = NULL; + SECStatus rv; + + *slotPtr = NULL; + + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); + if (list == NULL) { + if (list) PK11_FreeSlotList(list); + return CK_INVALID_KEY; + } + + + /* Look for the slot that holds the Key */ + for (le = list->head ; le; le = le->next) { + if (!PK11_IsFriendly(le->slot)) { + rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); + if (rv != SECSuccess) continue; + } + + certHandle = pk11_FindObjectByTemplate(le->slot,searchTemplate,count); + if (certHandle != CK_INVALID_KEY) { + slot = PK11_ReferenceSlot(le->slot); + break; + } + } + + PK11_FreeSlotList(list); + + if (slot == NULL) { + return CK_INVALID_KEY; + } + *slotPtr = slot; + return certHandle; +} + + +/* + * We're looking for a cert which we have the private key for that's on the + * list of recipients. This searches one slot. + */ +static CK_OBJECT_HANDLE +pk11_FindCertObjectByRecipient(PK11SlotInfo *slot, + SEC_PKCS7RecipientInfo **recipientArray,SEC_PKCS7RecipientInfo **rip) +{ + CK_OBJECT_HANDLE certHandle; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_OBJECT_CLASS peerClass ; + CK_ATTRIBUTE searchTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_ISSUER, NULL, 0 }, + { CKA_SERIAL_NUMBER, NULL, 0} + }; + int count = sizeof(searchTemplate)/sizeof(CK_ATTRIBUTE); + SEC_PKCS7RecipientInfo *ri = NULL; + CK_ATTRIBUTE *attrs; + int i; + + + peerClass = CKO_PRIVATE_KEY; + if (!PK11_IsLoggedIn(slot,NULL) && PK11_NeedLogin(slot)) { + peerClass = CKO_PUBLIC_KEY; + } + + for (i=0; (ri = recipientArray[i]) != NULL; i++) { + attrs = searchTemplate; + + PK11_SETATTRS(attrs, CKA_CLASS, &certClass,sizeof(certClass)); attrs++; + PK11_SETATTRS(attrs, CKA_ISSUER, ri->issuerAndSN->derIssuer.data, + ri->issuerAndSN->derIssuer.len); attrs++; + PK11_SETATTRS(attrs, CKA_SERIAL_NUMBER, + ri->issuerAndSN->serialNumber.data,ri->issuerAndSN->serialNumber.len); + + certHandle = pk11_FindObjectByTemplate(slot,searchTemplate,count); + if (certHandle != CK_INVALID_KEY) { + CERTCertificate *cert = pk11_fastCert(slot,certHandle,NULL,NULL); + if (PK11_IsUserCert(slot,cert,certHandle)) { + /* we've found a cert handle, now let's see if there is a key + * associated with it... */ + *rip = ri; + + CERT_DestroyCertificate(cert); + return certHandle; + } + CERT_DestroyCertificate(cert); + } + } + *rip = NULL; + return CK_INVALID_KEY; +} + +/* + * This function is the same as above, but it searches all the slots. + */ +static CK_OBJECT_HANDLE +pk11_AllFindCertObjectByRecipient(PK11SlotInfo **slotPtr, + SEC_PKCS7RecipientInfo **recipientArray,SEC_PKCS7RecipientInfo **rip, + void *wincx) { + PK11SlotList *list; + PK11SlotListElement *le; + CK_OBJECT_HANDLE certHandle = CK_INVALID_KEY; + PK11SlotInfo *slot = NULL; + SECStatus rv; + + *slotPtr = NULL; + + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); + if (list == NULL) { + if (list) PK11_FreeSlotList(list); + return CK_INVALID_KEY; + } + + *rip = NULL; + + /* Look for the slot that holds the Key */ + for (le = list->head ; le; le = le->next) { + if ( !PK11_IsFriendly(le->slot)) { + rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); + if (rv != SECSuccess) continue; + } + + certHandle = pk11_FindCertObjectByRecipient(le->slot, + recipientArray,rip); + if (certHandle != CK_INVALID_KEY) { + slot = PK11_ReferenceSlot(le->slot); + break; + } + } + + PK11_FreeSlotList(list); + + if (slot == NULL) { + return CK_INVALID_KEY; + } + *slotPtr = slot; + return certHandle; +} + +/* + * We need to invert the search logic for PKCS 7 because if we search for + * each cert on the list over all the slots, we wind up with lots of spurious + * password prompts. This way we get only one password prompt per slot, at + * the max, and most of the time we can find the cert, and only prompt for + * the key... + */ +CERTCertificate * +PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slotPtr, + SEC_PKCS7RecipientInfo **array, SEC_PKCS7RecipientInfo **rip, + SECKEYPrivateKey**privKey, void *wincx) +{ + CK_OBJECT_HANDLE certHandle = CK_INVALID_KEY; + CK_OBJECT_HANDLE keyHandle = CK_INVALID_KEY; + PK11SlotInfo *slot = NULL; + CERTCertificate *cert = NULL; + SECStatus rv; + + *privKey = NULL; + certHandle = pk11_AllFindCertObjectByRecipient(slotPtr,array,rip,wincx); + if (certHandle == CK_INVALID_KEY) { + return NULL; + } + + rv = PK11_Authenticate(*slotPtr,PR_TRUE,wincx); + if (rv != SECSuccess) { + PK11_FreeSlot(*slotPtr); + *slotPtr = NULL; + return NULL; + } + + keyHandle = PK11_MatchItem(*slotPtr,certHandle,CKO_PRIVATE_KEY); + if (keyHandle == CK_INVALID_KEY) { + PK11_FreeSlot(*slotPtr); + *slotPtr = NULL; + return NULL; + } + + *privKey = PK11_MakePrivKey(*slotPtr, nullKey, PR_TRUE, keyHandle, wincx); + if (*privKey == NULL) { + PK11_FreeSlot(*slotPtr); + *slotPtr = NULL; + return NULL; + } + cert = PK11_MakeCertFromHandle(*slotPtr,certHandle,NULL); + if (cert == NULL) { + PK11_FreeSlot(*slotPtr); + SECKEY_DestroyPrivateKey(*privKey); + *slotPtr = NULL; + *privKey = NULL; + return NULL; + } + return cert; +} + +CERTCertificate * +PK11_FindCertByIssuerAndSN(PK11SlotInfo **slotPtr, CERTIssuerAndSN *issuerSN, + void *wincx) +{ + CK_OBJECT_HANDLE certHandle; + CERTCertificate *cert = NULL; + CK_ATTRIBUTE searchTemplate[] = { + { CKA_ISSUER, NULL, 0 }, + { CKA_SERIAL_NUMBER, NULL, 0} + }; + int count = sizeof(searchTemplate)/sizeof(CK_ATTRIBUTE); + CK_ATTRIBUTE *attrs = searchTemplate; + + PK11_SETATTRS(attrs, CKA_ISSUER, issuerSN->derIssuer.data, + issuerSN->derIssuer.len); attrs++; + PK11_SETATTRS(attrs, CKA_SERIAL_NUMBER, issuerSN->serialNumber.data, + issuerSN->serialNumber.len); + + certHandle = pk11_FindCertObjectByTemplate + (slotPtr,searchTemplate,count,wincx); + if (certHandle == CK_INVALID_KEY) { + return NULL; + } + cert = PK11_MakeCertFromHandle(*slotPtr,certHandle,NULL); + if (cert == NULL) { + PK11_FreeSlot(*slotPtr); + return NULL; + } + return cert; +} + +CK_OBJECT_HANDLE +PK11_FindObjectForCert(CERTCertificate *cert, void *wincx, PK11SlotInfo **pSlot) +{ + CK_OBJECT_HANDLE certHandle; + CK_ATTRIBUTE searchTemplate = { CKA_VALUE, NULL, 0 }; + + PK11_SETATTRS(&searchTemplate, CKA_VALUE, cert->derCert.data, + cert->derCert.len); + + if (cert->slot) { + certHandle = pk11_getcerthandle(cert->slot,cert,&searchTemplate,1); + if (certHandle != CK_INVALID_KEY) { + *pSlot = PK11_ReferenceSlot(cert->slot); + return certHandle; + } + } + + certHandle = pk11_FindCertObjectByTemplate(pSlot,&searchTemplate,1,wincx); + if (certHandle != CK_INVALID_KEY) { + if (cert->slot == NULL) { + cert->slot = PK11_ReferenceSlot(*pSlot); + cert->pkcs11ID = certHandle; + cert->ownSlot = PR_FALSE; + } + } + + return(certHandle); +} + +SECKEYPrivateKey * +PK11_FindKeyByAnyCert(CERTCertificate *cert, void *wincx) +{ + CK_OBJECT_HANDLE certHandle; + CK_OBJECT_HANDLE keyHandle; + PK11SlotInfo *slot = NULL; + SECKEYPrivateKey *privKey; + SECStatus rv; + + certHandle = PK11_FindObjectForCert(cert, wincx, &slot); + if (certHandle == CK_INVALID_KEY) { + return NULL; + } + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + PK11_FreeSlot(slot); + return NULL; + } + keyHandle = PK11_MatchItem(slot,certHandle,CKO_PRIVATE_KEY); + if (keyHandle == CK_INVALID_KEY) { + PK11_FreeSlot(slot); + return NULL; + } + privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx); + PK11_FreeSlot(slot); + return privKey; +} + +CK_OBJECT_HANDLE +pk11_FindPubKeyByAnyCert(CERTCertificate *cert, PK11SlotInfo **slot, void *wincx) +{ + CK_OBJECT_HANDLE certHandle; + CK_OBJECT_HANDLE keyHandle; + + certHandle = PK11_FindObjectForCert(cert, wincx, slot); + if (certHandle == CK_INVALID_KEY) { + return CK_INVALID_KEY; + } + keyHandle = PK11_MatchItem(*slot,certHandle,CKO_PUBLIC_KEY); + if (keyHandle == CK_INVALID_KEY) { + PK11_FreeSlot(*slot); + return CK_INVALID_KEY; + } + return keyHandle; +} + +SECKEYPrivateKey * +PK11_FindKeyByKeyID(PK11SlotInfo *slot, SECItem *keyID, void *wincx) +{ + CK_OBJECT_HANDLE keyHandle; + SECKEYPrivateKey *privKey; + + keyHandle = pk11_FindPrivateKeyFromCertID(slot, keyID); + if (keyHandle == CK_INVALID_KEY) { + return NULL; + } + privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx); + return privKey; +} + +/* + * find the number of certs in the slot with the same subject name + */ +int +PK11_NumberCertsForCertSubject(CERTCertificate *cert) +{ + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, + }; + CK_ATTRIBUTE *attr = theTemplate; + int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]); + + PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++; + PK11_SETATTRS(attr,CKA_SUBJECT,cert->derSubject.data,cert->derSubject.len); + + if ((cert->slot == NULL) || (cert->slot->isInternal)) { + return 0; + } + + return PK11_NumberObjectsFor(cert->slot,theTemplate,templateSize); +} + +/* + * Walk all the certs with the same subject + */ +SECStatus +PK11_TraverseCertsForSubject(CERTCertificate *cert, + SECStatus(* callback)(CERTCertificate*, void *), void *arg) +{ + if(!cert) { + return SECFailure; + } + + return PK11_TraverseCertsForSubjectInSlot(cert, cert->slot, callback, arg); +} + +SECStatus +PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert, PK11SlotInfo *slot, + SECStatus(* callback)(CERTCertificate*, void *), void *arg) +{ + pk11DoCertCallback caller; + pk11TraverseSlotCert callarg; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, + }; + CK_ATTRIBUTE *attr = theTemplate; + int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]); + + PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++; + PK11_SETATTRS(attr,CKA_SUBJECT,cert->derSubject.data,cert->derSubject.len); + + if ((slot == NULL) || (slot->isInternal)) { + return SECSuccess; + } + caller.noslotcallback = callback; + caller.callback = NULL; + caller.callbackArg = arg; + callarg.callback = pk11_DoCerts; + callarg.callbackArg = (void *) & caller; + callarg.findTemplate = theTemplate; + callarg.templateCount = templateSize; + + return PK11_TraverseSlot(slot, &callarg); +} + +SECStatus +PK11_TraverseCertsForNicknameInSlot(SECItem *nickname, PK11SlotInfo *slot, + SECStatus(* callback)(CERTCertificate*, void *), void *arg) +{ + pk11DoCertCallback caller; + pk11TraverseSlotCert callarg; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_LABEL, NULL, 0 }, + }; + CK_ATTRIBUTE *attr = theTemplate; + int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]); + + if(!nickname) { + return SECSuccess; + } + + PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++; + PK11_SETATTRS(attr,CKA_LABEL,nickname->data,nickname->len); + + if ((slot == NULL) || (slot->isInternal)) { + return SECSuccess; + } + + caller.noslotcallback = callback; + caller.callback = NULL; + caller.callbackArg = arg; + callarg.callback = pk11_DoCerts; + callarg.callbackArg = (void *) & caller; + callarg.findTemplate = theTemplate; + callarg.templateCount = templateSize; + + return PK11_TraverseSlot(slot, &callarg); +} + +SECStatus +PK11_TraverseCertsInSlot(PK11SlotInfo *slot, + SECStatus(* callback)(CERTCertificate*, void *), void *arg) +{ + pk11DoCertCallback caller; + pk11TraverseSlotCert callarg; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + }; + CK_ATTRIBUTE *attr = theTemplate; + int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]); + + PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++; + + if (slot == NULL) { + return SECSuccess; + } + + caller.noslotcallback = callback; + caller.callback = NULL; + caller.callbackArg = arg; + callarg.callback = pk11_DoCerts; + callarg.callbackArg = (void *) & caller; + callarg.findTemplate = theTemplate; + callarg.templateCount = templateSize; + + return PK11_TraverseSlot(slot, &callarg); +} + +/* + * return the certificate associated with a derCert + */ +CERTCertificate * +PK11_FindCertFromDERCert(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx) +{ + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_VALUE, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_OBJECT_HANDLE certh; + CK_ATTRIBUTE *attrs = theTemplate; + SECStatus rv; + + PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, + cert->derCert.len); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + /* + * issue the find + */ + if ( !PK11_IsFriendly(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) return NULL; + } + + certh = pk11_getcerthandle(slot,cert,theTemplate,tsize); + if (certh == CK_INVALID_KEY) { + return NULL; + } + return PK11_MakeCertFromHandle(slot, certh, NULL); +} + +/* + * return the certificate associated with a derCert + */ +CERTCertificate * +PK11_FindCertFromDERSubjectAndNickname(PK11SlotInfo *slot, + CERTCertificate *cert, + char *nickname, void *wincx) +{ + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_SUBJECT, NULL, 0 }, + { CKA_LABEL, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_OBJECT_HANDLE certh; + CK_ATTRIBUTE *attrs = theTemplate; + SECStatus rv; + + PK11_SETATTRS(attrs, CKA_SUBJECT, cert->derSubject.data, + cert->derSubject.len); attrs++; + PK11_SETATTRS(attrs, CKA_LABEL, nickname, PORT_Strlen(nickname)); + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + /* + * issue the find + */ + if ( !PK11_IsFriendly(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) return NULL; + } + + certh = pk11_getcerthandle(slot,cert,theTemplate,tsize); + if (certh == CK_INVALID_KEY) { + return NULL; + } + + return PK11_MakeCertFromHandle(slot, certh, NULL); +} + +/* + * import a cert for a private key we have already generated. Set the label + * on both to be the nickname. + */ +static CK_OBJECT_HANDLE +pk11_findKeyObjectByDERCert(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx) +{ + SECItem *keyID; + CK_OBJECT_HANDLE key; + SECStatus rv; + + if((slot == NULL) || (cert == NULL)) { + return CK_INVALID_KEY; + } + + keyID = pk11_mkcertKeyID(cert); + if(keyID == NULL) { + return CK_INVALID_KEY; + } + + key = CK_INVALID_KEY; + + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) goto loser; + + key = pk11_FindPrivateKeyFromCertID(slot, keyID); + +loser: + SECITEM_ZfreeItem(keyID, PR_TRUE); + return key; +} + +SECKEYPrivateKey * +PK11_FindKeyByDERCert(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx) +{ + CK_OBJECT_HANDLE keyHandle; + + if((slot == NULL) || (cert == NULL)) { + return NULL; + } + + keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx); + if (keyHandle == CK_INVALID_KEY) { + return NULL; + } + + return PK11_MakePrivKey(slot,nullKey,PR_TRUE,keyHandle,wincx); +} + +SECStatus +PK11_ImportCertForKeyToSlot(PK11SlotInfo *slot, CERTCertificate *cert, + char *nickname, + PRBool addCertUsage,void *wincx) +{ + CK_OBJECT_HANDLE keyHandle; + + if((slot == NULL) || (cert == NULL) || (nickname == NULL)) { + return SECFailure; + } + + keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx); + if (keyHandle == CK_INVALID_KEY) { + return SECFailure; + } + + return PK11_ImportCert(slot, cert, keyHandle, nickname, addCertUsage); +} + + +/* remove when the real version comes out */ +#define SEC_OID_MISSI_KEA 300 /* until we have v3 stuff merged */ +PRBool +KEAPQGCompare(CERTCertificate *server,CERTCertificate *cert) { + + if ( SECKEY_KEAParamCompare(server,cert) == SECEqual ) { + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +PRBool +PK11_FortezzaHasKEA(CERTCertificate *cert) { + /* look at the subject and see if it is a KEA for MISSI key */ + SECOidData *oid; + + if ((cert->trust == NULL) || + ((cert->trust->sslFlags & CERTDB_USER) != CERTDB_USER)) { + return PR_FALSE; + } + + oid = SECOID_FindOID(&cert->subjectPublicKeyInfo.algorithm.algorithm); + + + return (PRBool)((oid->offset == SEC_OID_MISSI_KEA_DSS_OLD) || + (oid->offset == SEC_OID_MISSI_KEA_DSS) || + (oid->offset == SEC_OID_MISSI_KEA)) ; +} + +/* + * Find a kea cert on this slot that matches the domain of it's peer + */ +static CERTCertificate +*pk11_GetKEAMate(PK11SlotInfo *slot,CERTCertificate *peer) +{ + int i; + CERTCertificate *returnedCert = NULL; + + for (i=0; i < slot->cert_count; i++) { + CERTCertificate *cert = slot->cert_array[i]; + + if (PK11_FortezzaHasKEA(cert) && KEAPQGCompare(peer,cert)) { + returnedCert = CERT_DupCertificate(cert); + break; + } + } + return returnedCert; +} + +/* + * The following is a FORTEZZA only Certificate request. We call this when we + * are doing a non-client auth SSL connection. We are only interested in the + * fortezza slots, and we are only interested in certs that share the same root + * key as the server. + */ +CERTCertificate * +PK11_FindBestKEAMatch(CERTCertificate *server, void *wincx) +{ + PK11SlotList *keaList = PK11_GetAllTokens(CKM_KEA_KEY_DERIVE, + PR_FALSE,PR_TRUE,wincx); + PK11SlotListElement *le; + CERTCertificate *returnedCert = NULL; + SECStatus rv; + + /* loop through all the fortezza tokens */ + for (le = keaList->head; le; le = le->next) { + rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); + if (rv != SECSuccess) continue; + if (le->slot->session == CK_INVALID_SESSION) { + continue; + } + returnedCert = pk11_GetKEAMate(le->slot,server); + if (returnedCert) break; + } + PK11_FreeSlotList(keaList); + + return returnedCert; +} + +/* + * find a matched pair of kea certs to key exchange parameters from one + * fortezza card to another as necessary. + */ +SECStatus +PK11_GetKEAMatchedCerts(PK11SlotInfo *slot1, PK11SlotInfo *slot2, + CERTCertificate **cert1, CERTCertificate **cert2) +{ + PK11SlotList *keaList = PK11_GetAllTokens(CKM_KEA_KEY_DERIVE, + PR_FALSE,PR_TRUE,NULL); + CERTCertificate *returnedCert = NULL; + int i; + + for (i=0; i < slot1->cert_count; i++) { + CERTCertificate *cert = slot1->cert_array[i]; + + if (PK11_FortezzaHasKEA(cert)) { + returnedCert = pk11_GetKEAMate(slot2,cert); + if (returnedCert != NULL) { + *cert2 = returnedCert; + *cert1 = CERT_DupCertificate(cert); + return SECSuccess; + } + } + } + return SECFailure; +} + +SECOidTag +PK11_FortezzaMapSig(SECOidTag algTag) +{ + switch (algTag) { + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_DSS: + case SEC_OID_MISSI_DSS_OLD: + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: + return SEC_OID_ANSIX9_DSA_SIGNATURE; + default: + break; + } + return algTag; +} + +/* + * return the private key From a given Cert + */ +CK_OBJECT_HANDLE +PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) +{ + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_VALUE, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_ATTRIBUTE *attrs = theTemplate; + SECStatus rv; + + PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, + cert->derCert.len); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + /* + * issue the find + */ + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + return CK_INVALID_KEY; + } + + return pk11_getcerthandle(slot,cert,theTemplate,tsize); +} + +SECItem * +PK11_GetKeyIDFromCert(CERTCertificate *cert, void *wincx) +{ + CK_OBJECT_HANDLE handle; + PK11SlotInfo *slot = NULL; + CK_ATTRIBUTE theTemplate[] = { + { CKA_ID, NULL, 0 }, + }; + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + SECItem *item = NULL; + CK_RV crv; + + handle = PK11_FindObjectForCert(cert,wincx,&slot); + if (handle == CK_INVALID_KEY) { + goto loser; + } + + + crv = PK11_GetAttributes(NULL,slot,handle,theTemplate,tsize); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + goto loser; + } + + item = PORT_ZNew(SECItem); + if (item) { + item->data = theTemplate[0].pValue; + item->len = theTemplate[0].ulValueLen; + } + + +loser: + PK11_FreeSlot(slot); + return item; +} + +SECItem * +PK11_GetKeyIDFromPrivateKey(SECKEYPrivateKey *key, void *wincx) +{ + CK_ATTRIBUTE theTemplate[] = { + { CKA_ID, NULL, 0 }, + }; + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + SECItem *item = NULL; + CK_RV crv; + + crv = PK11_GetAttributes(NULL,key->pkcs11Slot,key->pkcs11ID, + theTemplate,tsize); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + goto loser; + } + + item = PORT_ZNew(SECItem); + if (item) { + item->data = theTemplate[0].pValue; + item->len = theTemplate[0].ulValueLen; + } + + +loser: + return item; +} diff --git a/security/nss/lib/pk11wrap/pk11db.c b/security/nss/lib/pk11wrap/pk11db.c new file mode 100644 index 000000000..799f894d1 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11db.c @@ -0,0 +1,643 @@ +/* + * 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. + */ +/* + * The following code handles the storage of PKCS 11 modules used by the + * NSS. This file is written to abstract away how the modules are + * stored so we can deside that later. + */ +#include "seccomon.h" +#include "secmod.h" +#include "prlock.h" +#include "pkcs11.h" +#include "secmodi.h" +#include "pk11func.h" +#include "mcom_db.h" + +/* create a new module */ +SECMODModule *SECMOD_NewModule(void) { + SECMODModule *newMod; + PRArenaPool *arena; + + + /* create an arena in which dllName and commonName can be + * allocated. + */ + arena = PORT_NewArena(512); + if (arena == NULL) { + return NULL; + } + + newMod = (SECMODModule *)PORT_ArenaAlloc(arena,sizeof (SECMODModule)); + if (newMod == NULL) { + PORT_FreeArena(arena,PR_FALSE); + return NULL; + } + + /* + * initialize of the fields of the module + */ + newMod->arena = arena; + newMod->internal = PR_FALSE; + newMod->loaded = PR_FALSE; + newMod->isFIPS = PR_FALSE; + newMod->dllName = NULL; + newMod->commonName = NULL; + newMod->library = NULL; + newMod->functionList = NULL; + newMod->slotCount = 0; + newMod->slots = NULL; + newMod->slotInfo = NULL; + newMod->slotInfoCount = 0; + newMod->refCount = 1; + newMod->ssl[0] = 0; + newMod->ssl[1] = 0; +#ifdef PKCS11_USE_THREADS + newMod->refLock = (void *)PR_NewLock(); + if (newMod->refLock == NULL) { + PORT_FreeArena(arena,PR_FALSE); + return NULL; + } +#else + newMod->refLock = NULL; +#endif + return newMod; + +} + +/* create a new ModuleListElement */ +SECMODModuleList *SECMOD_NewModuleListElement(void) { + SECMODModuleList *newModList; + + newModList= (SECMODModuleList *) PORT_Alloc(sizeof(SECMODModuleList)); + if (newModList) { + newModList->next = NULL; + newModList->module = NULL; + } + return newModList; +} + +static unsigned long internalFlags = SECMOD_RSA_FLAG|SECMOD_DSA_FLAG| + SECMOD_RC2_FLAG| SECMOD_RC4_FLAG|SECMOD_DES_FLAG|SECMOD_RANDOM_FLAG| + SECMOD_SHA1_FLAG|SECMOD_MD5_FLAG|SECMOD_MD2_FLAG; + +/* create a Internal module */ +SECMODModule *SECMOD_NewInternal(void) { + SECMODModule *intern; + static PK11PreSlotInfo internSlotInfo = + { 1, SECMOD_RSA_FLAG|SECMOD_DSA_FLAG|SECMOD_RC2_FLAG| + SECMOD_RC4_FLAG|SECMOD_DES_FLAG|SECMOD_RANDOM_FLAG| + SECMOD_SHA1_FLAG|SECMOD_MD5_FLAG|SECMOD_MD2_FLAG, -1, 30 }; + + intern = SECMOD_NewModule(); + if (intern == NULL) { + return NULL; + } + + /* + * make this module an internal module + */ + intern->commonName = "Netscape Internal PKCS #11 Module"; + intern->internal = PR_TRUE; + intern->slotInfoCount = 1; + intern->slotInfo = &internSlotInfo; + + return (intern); +} + +/* create a FIPS Internal module */ +SECMODModule *SECMOD_GetFIPSInternal(void) { + SECMODModule *intern; + + intern = SECMOD_NewInternal(); + if (intern == NULL) { + return NULL; + } + + /* + * make this module a FIPS internal module + */ + intern->slotInfo[0].slotID = 3; /* FIPS slot */ + intern->commonName = "Netscape Internal FIPS PKCS #11 Module"; + intern->isFIPS = PR_TRUE; + + return (intern); +} + +SECMODModule *SECMOD_DupModule(SECMODModule *old) { + SECMODModule *newMod; + + newMod = SECMOD_NewModule(); + if (newMod == NULL) { + return NULL; + } + + /* + * initialize of the fields of the module + */ + newMod->dllName = PORT_ArenaStrdup(newMod->arena,old->dllName); + newMod->commonName = PORT_ArenaStrdup(newMod->arena,old->commonName);; + + return newMod; + +} + +/* + * make a new reference to a module so It doesn't go away on us + */ +SECMODModule * +SECMOD_ReferenceModule(SECMODModule *module) { + PK11_USE_THREADS(PR_Lock((PRLock *)module->refLock);) + PORT_Assert(module->refCount > 0); + + module->refCount++; + PK11_USE_THREADS(PR_Unlock((PRLock*)module->refLock);) + return module; +} + + +/* destroy an existing module */ +void +SECMOD_DestroyModule(SECMODModule *module) { + PRBool willfree = PR_FALSE; + int slotCount; + int i; + + PK11_USE_THREADS(PR_Lock((PRLock *)module->refLock);) + if (module->refCount-- == 1) { + willfree = PR_TRUE; + } + PORT_Assert(willfree || (module->refCount > 0)); + PK11_USE_THREADS(PR_Unlock((PRLock *)module->refLock);) + + if (!willfree) { + return; + } + + /* slots can't really disappear until our module starts freeing them, + * so this check is safe */ + slotCount = module->slotCount; + if (slotCount == 0) { + SECMOD_SlotDestroyModule(module,PR_FALSE); + return; + } + + /* now free all out slots, when they are done, they will cause the + * module to disappear altogether */ + for (i=0 ; i < slotCount; i++) { + if (!module->slots[i]->disabled) { + PK11_ClearSlotList(module->slots[i]); + } + PK11_FreeSlot(module->slots[i]); + } + /* WARNING: once the last slot has been freed is it possible (even likely) + * that module is no more... touching it now is a good way to go south */ +} + + +/* we can only get here if we've destroyed the module, or some one has + * erroneously freed a slot that wasn't referenced. */ +void +SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot) { + PRBool willfree = PR_FALSE; + if (fromSlot) { + PORT_Assert(module->refCount == 0); + PK11_USE_THREADS(PR_Lock((PRLock *)module->refLock);) + if (module->slotCount-- == 1) { + willfree = PR_TRUE; + } + PORT_Assert(willfree || (module->slotCount > 0)); + PK11_USE_THREADS(PR_Unlock((PRLock *)module->refLock);) + if (!willfree) return; + } + if (module->loaded) { + SECMOD_UnloadModule(module); + } + PK11_USE_THREADS(PR_DestroyLock((PRLock *)module->refLock);) + PORT_FreeArena(module->arena,PR_FALSE); +} + +/* destroy a list element + * this destroys a single element, and returns the next element + * on the chain. It makes it easy to implement for loops to delete + * the chain. It also make deleting a single element easy */ +SECMODModuleList * +SECMOD_DestroyModuleListElement(SECMODModuleList *element) { + SECMODModuleList *next = element->next; + + if (element->module) { + SECMOD_DestroyModule(element->module); + element->module = NULL; + } + PORT_Free(element); + return next; +} + + +/* + * Destroy an entire module list + */ +void +SECMOD_DestroyModuleList(SECMODModuleList *list) { + SECMODModuleList *lp; + + for ( lp = list; lp != NULL; lp = SECMOD_DestroyModuleListElement(lp)) ; +} + + +/* Construct a database key for a given module */ +static SECStatus secmod_MakeKey(DBT *key, SECMODModule * module) { + int len = 0; + + len = PORT_Strlen(module->commonName); + key->data = module->commonName; + key->size = len; + return SECSuccess; +} + +/* free out constructed database key */ +static void secmod_FreeKey(DBT *key) { + key->data = NULL; + key->size = 0; +} + +typedef struct secmodDataStr secmodData; +typedef struct secmodSlotDataStr secmodSlotData; +struct secmodDataStr { + unsigned char major; + unsigned char minor; + unsigned char nameStart[2]; + unsigned char slotOffset[2]; + unsigned char internal; + unsigned char fips; + unsigned char ssl[8]; + unsigned char names[4]; /* enough space for the length fields */ +}; + +struct secmodSlotDataStr { + unsigned char slotID[4]; + unsigned char defaultFlags[4]; + unsigned char timeout[4]; + unsigned char askpw; + unsigned char reserved[19]; /* this makes it a round 32 bytes */ +}; + +#define SECMOD_DB_VERSION_MAJOR 0 +#define SECMOD_DB_VERSION_MINOR 4 +#define SECMOD_DB_NOUI_VERSION_MAJOR 0 +#define SECMOD_DB_NOUI_VERSION_MINOR 3 + +#define SECMOD_PUTSHORT(dest,src) \ + (dest)[1] = (unsigned char) ((src)&0xff); \ + (dest)[0] = (unsigned char) (((src) >> 8) & 0xff); +#define SECMOD_PUTLONG(dest,src) \ + (dest)[3] = (unsigned char) ((src)&0xff); \ + (dest)[2] = (unsigned char) (((src) >> 8) & 0xff); \ + (dest)[1] = (unsigned char) (((src) >> 16) & 0xff); \ + (dest)[0] = (unsigned char) (((src) >> 24) & 0xff); +#define SECMOD_GETSHORT(src) \ + ((unsigned short) (((src)[0] << 8) | (src)[1])) +#define SECMOD_GETLONG(src) \ + ((unsigned long) (( (unsigned long) (src)[0] << 24) | \ + ( (unsigned long) (src)[1] << 16) | \ + ( (unsigned long) (src)[2] << 8) | \ + (unsigned long) (src)[3])) +/* + * build a data base entry from a module + */ +static SECStatus secmod_EncodeData(DBT *data, SECMODModule * module) { + secmodData *encoded; + secmodSlotData *slot; + unsigned char *dataPtr; + unsigned short len, len2 = 0,count = 0; + unsigned short offset; + int dataLen, i, si; + + len = PORT_Strlen(module->commonName); + if (module->dllName) { + len2 = PORT_Strlen(module->dllName); + } + if (module->slotCount != 0) { + for (i=0; i < module->slotCount; i++) { + if (module->slots[i]->defaultFlags != 0) { + count++; + } + } + } else { + count = module->slotInfoCount; + } + dataLen = sizeof(secmodData) + len + len2 + 2 + + count*sizeof(secmodSlotData); + + data->data = (unsigned char *) + PORT_Alloc(dataLen); + encoded = (secmodData *)data->data; + dataPtr = (unsigned char *) data->data; + data->size = dataLen; + + if (encoded == NULL) return SECFailure; + + encoded->major = SECMOD_DB_VERSION_MAJOR; + encoded->minor = SECMOD_DB_VERSION_MINOR; + encoded->internal = (unsigned char) (module->internal ? 1 : 0); + encoded->fips = (unsigned char) (module->isFIPS ? 1 : 0); + SECMOD_PUTLONG(encoded->ssl,module->ssl[0]); + SECMOD_PUTLONG(&encoded->ssl[4],module->ssl[1]); + + offset = (unsigned long) &(((secmodData *)0)->names[0]); + SECMOD_PUTSHORT(encoded->nameStart,offset); + offset = offset +len + len2 + 4; + SECMOD_PUTSHORT(encoded->slotOffset,offset); + + + SECMOD_PUTSHORT(&dataPtr[offset],count); + slot = (secmodSlotData *)(dataPtr+offset+2); + + SECMOD_PUTSHORT(encoded->names,len); + PORT_Memcpy(&encoded->names[2],module->commonName,len); + + + SECMOD_PUTSHORT(&encoded->names[len+2],len2); + if (len2) PORT_Memcpy(&encoded->names[len+4],module->dllName,len2); + + if (module->slotCount) { + for (i=0,si=0; i < module->slotCount; i++) { + if (module->slots[i]->defaultFlags) { + SECMOD_PUTLONG(slot[si].slotID, module->slots[i]->slotID); + SECMOD_PUTLONG(slot[si].defaultFlags, + module->slots[i]->defaultFlags); + SECMOD_PUTLONG(slot[si].timeout,module->slots[i]->timeout); + slot[si].askpw = module->slots[i]->askpw; + PORT_Memset(slot[si].reserved, 0, sizeof(slot[si].reserved)); + si++; + } + } + } else { + for (i=0; i < module->slotInfoCount; i++) { + SECMOD_PUTLONG(slot[i].slotID, module->slotInfo[i].slotID); + SECMOD_PUTLONG(slot[i].defaultFlags, + module->slotInfo[i].defaultFlags); + SECMOD_PUTLONG(slot[i].timeout,module->slotInfo[i].timeout); + slot[i].askpw = module->slotInfo[i].askpw; + PORT_Memset(slot[i].reserved, 0, sizeof(slot[i].reserved)); + } + } + + return SECSuccess; + +} + +static void secmod_FreeData(DBT *data) { + if (data->data) { + PORT_Free(data->data); + } +} + + +/* + * build a module from the data base entry. + */ +static SECMODModule *secmod_DecodeData(DBT *data) { + SECMODModule * module; + secmodData *encoded; + secmodSlotData *slots; + unsigned char *names; + unsigned short len,len1; + unsigned long slotCount; + unsigned short offset; + PRBool isOldVersion = PR_FALSE; + int i; + + encoded = (secmodData *)data->data; + names = (unsigned char *)data->data; + offset = SECMOD_GETSHORT(encoded->slotOffset); + slots = (secmodSlotData *) (names + offset + 2); + slotCount = SECMOD_GETSHORT(names + offset); + names += SECMOD_GETSHORT(encoded->nameStart); + + module = SECMOD_NewModule(); + if (module == NULL) return NULL; + + module->internal = (encoded->internal != 0) ? PR_TRUE: PR_FALSE; + module->isFIPS = (encoded->fips != 0) ? PR_TRUE: PR_FALSE; + len = SECMOD_GETSHORT(names); + + if (module->internal && (encoded->major == SECMOD_DB_NOUI_VERSION_MAJOR) && + (encoded->minor <= SECMOD_DB_NOUI_VERSION_MINOR)) { + isOldVersion = PR_TRUE; + } + + /* decode the common name */ + module->commonName = (char*)PORT_ArenaAlloc(module->arena,len+1); + if (module->commonName == NULL) { + SECMOD_DestroyModule(module); + return NULL; + } + PORT_Memcpy(module->commonName,&names[2],len); + module->commonName[len] = 0; + + /* decode the DLL name */ + len1 = (names[len+2] << 8) | names[len+3]; + if (len1) { + module->dllName = (char*)PORT_ArenaAlloc(module->arena,len1 + 1); + if (module->dllName == NULL) { + SECMOD_DestroyModule(module); + return NULL; + } + PORT_Memcpy(module->dllName,&names[len+4],len1); + module->dllName[len1] = 0; + } + + module->slotInfoCount = slotCount; + module->slotInfo = (PK11PreSlotInfo *) PORT_ArenaAlloc(module->arena, + slotCount * sizeof(PK11PreSlotInfo)); + for (i=0; i < (int) slotCount; i++) { + module->slotInfo[i].slotID = SECMOD_GETLONG(slots[i].slotID); + module->slotInfo[i].defaultFlags = + SECMOD_GETLONG(slots[i].defaultFlags); + if (isOldVersion && module->internal && + (module->slotInfo[i].slotID != 2)) { + module->slotInfo[i].defaultFlags |= internalFlags; + } + module->slotInfo[i].timeout = SECMOD_GETLONG(slots[i].timeout); + module->slotInfo[i].askpw = slots[i].askpw; + if (module->slotInfo[i].askpw == 0xff) { + module->slotInfo[i].askpw = -1; + } + } + + /* decode SSL cipher enable flags */ + module->ssl[0] = SECMOD_GETLONG(encoded->ssl); + module->ssl[1] = SECMOD_GETLONG(&encoded->ssl[4]); + + return (module); +} + +/* + * open the PKCS #11 data base. + */ +static char *pkcs11dbName = NULL; +void SECMOD_InitDB(char *dbname) { + pkcs11dbName = PORT_Strdup(dbname); +} + + +static DB *secmod_OpenDB(PRBool readOnly) { + DB *pkcs11db = NULL; + char *dbname; + + if (pkcs11dbName == NULL) return NULL; + dbname = pkcs11dbName; + + /* I'm sure we should do more checks here sometime... */ + pkcs11db = dbopen(dbname, readOnly ? O_RDONLY : O_RDWR, 0600, DB_HASH, 0); + + /* didn't exist? create it */ + if (pkcs11db == NULL) { + if (readOnly) return NULL; + + pkcs11db = dbopen( dbname, + O_RDWR | O_CREAT | O_TRUNC, 0600, DB_HASH, 0 ); + if (pkcs11db) (* pkcs11db->sync)(pkcs11db, 0); + } + return pkcs11db; +} + +static void secmod_CloseDB(DB *pkcs11db) { + (*pkcs11db->close)(pkcs11db); +} + +/* + * Read all the existing modules in + */ +SECMODModuleList * +SECMOD_ReadPermDB(void) { + DBT key,data; + int ret; + DB *pkcs11db = NULL; + SECMODModuleList *newmod = NULL,*mod = NULL; + + pkcs11db = secmod_OpenDB(PR_TRUE); + if (pkcs11db == NULL) { + return NULL; + } + + /* read and parse the file or data base */ + ret = (*pkcs11db->seq)(pkcs11db, &key, &data, R_FIRST); + if (ret) goto done; + + do { + /* allocate space for modules */ + newmod = SECMOD_NewModuleListElement(); + if (newmod == NULL) break; + newmod->module = secmod_DecodeData(&data); + if (newmod->module == NULL) { + SECMOD_DestroyModuleListElement(newmod); + break; + } + newmod->next = mod; + mod = newmod; + } while ( (*pkcs11db->seq)(pkcs11db, &key, &data, R_NEXT) == 0); + +done: + secmod_CloseDB(pkcs11db); + return mod; +} + +/* + * Delete a module from the Data Base + */ +SECStatus +SECMOD_DeletePermDB(SECMODModule * module) { + DBT key; + SECStatus rv = SECFailure; + DB *pkcs11db = NULL; + int ret; + + /* make sure we have a db handle */ + pkcs11db = secmod_OpenDB(PR_FALSE); + if (pkcs11db == NULL) { + return SECFailure; + } + + rv = secmod_MakeKey(&key,module); + if (rv != SECSuccess) goto done; + rv = SECFailure; + ret = (*pkcs11db->del)(pkcs11db, &key, 0); + secmod_FreeKey(&key); + if (ret != 0) goto done; + + + ret = (*pkcs11db->sync)(pkcs11db, 0); + if (ret == 0) rv = SECSuccess; + +done: + secmod_CloseDB(pkcs11db); + return rv; +} + +/* + * Add a module to the Data base + */ +SECStatus +SECMOD_AddPermDB(SECMODModule *module) { + DBT key,data; + SECStatus rv = SECFailure; + DB *pkcs11db = NULL; + int ret; + + /* make sure we have a db handle */ + pkcs11db = secmod_OpenDB(PR_FALSE); + if (pkcs11db == NULL) { + return SECFailure; + } + + rv = secmod_MakeKey(&key,module); + if (rv != SECSuccess) goto done; + rv = secmod_EncodeData(&data,module); + if (rv != SECSuccess) { + secmod_FreeKey(&key); + goto done; + } + rv = SECFailure; + ret = (*pkcs11db->put)(pkcs11db, &key, &data, 0); + secmod_FreeKey(&key); + secmod_FreeData(&data); + if (ret != 0) goto done; + + ret = (*pkcs11db->sync)(pkcs11db, 0); + if (ret == 0) rv = SECSuccess; + +done: + secmod_CloseDB(pkcs11db); + return rv; +} diff --git a/security/nss/lib/pk11wrap/pk11err.c b/security/nss/lib/pk11wrap/pk11err.c new file mode 100644 index 000000000..b6846af84 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11err.c @@ -0,0 +1,147 @@ +/* + * 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 maps PKCS11 Errors into SECErrors + * This is an information reducing process, since most errors are reflected + * back to the user (the user doesn't care about invalid flags, or active + * operations). If any of these errors need more detail in the upper layers + * which call PK11 library functions, we can add more SEC_ERROR_XXX functions + * and change there mappings here. + */ +#include "pkcs11t.h" +#include "pk11func.h" +#include "secerr.h" + +#ifdef PK11_ERROR_USE_ARRAY + +/* + * build a static array of entries... + */ +static struct { + CK_RV pk11_error; + int sec_error; +} pk11_error_map = { +#define MAPERROR(x,y) {x, y}, + +#else + +/* the default is to use a big switch statement */ +int +PK11_MapError(CK_RV rv) { + + switch (rv) { +#define MAPERROR(x,y) case x: return y; + +#endif + +/* the guts mapping */ + MAPERROR(CKR_OK, 0) + MAPERROR(CKR_CANCEL, SEC_ERROR_IO) + MAPERROR(CKR_HOST_MEMORY, SEC_ERROR_NO_MEMORY) + MAPERROR(CKR_SLOT_ID_INVALID, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_ATTRIBUTE_READ_ONLY, SEC_ERROR_READ_ONLY) + MAPERROR(CKR_ATTRIBUTE_SENSITIVE, SEC_ERROR_IO) /* XX SENSITIVE */ + MAPERROR(CKR_ATTRIBUTE_TYPE_INVALID, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_ATTRIBUTE_VALUE_INVALID, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_DATA_INVALID, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_DATA_LEN_RANGE, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_DEVICE_ERROR, SEC_ERROR_IO) + MAPERROR(CKR_DEVICE_MEMORY, SEC_ERROR_NO_MEMORY) + MAPERROR(CKR_DEVICE_REMOVED, SEC_ERROR_NO_TOKEN) + MAPERROR(CKR_ENCRYPTED_DATA_INVALID, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_ENCRYPTED_DATA_LEN_RANGE, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_FUNCTION_CANCELED, SEC_ERROR_LIBRARY_FAILURE) + MAPERROR(CKR_FUNCTION_NOT_PARALLEL, SEC_ERROR_LIBRARY_FAILURE) + MAPERROR(CKR_KEY_HANDLE_INVALID, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_KEY_SIZE_RANGE, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_KEY_TYPE_INCONSISTENT, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_MECHANISM_INVALID, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_MECHANISM_PARAM_INVALID, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_OBJECT_HANDLE_INVALID, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_OPERATION_ACTIVE, SEC_ERROR_LIBRARY_FAILURE) + MAPERROR(CKR_OPERATION_NOT_INITIALIZED,SEC_ERROR_LIBRARY_FAILURE ) + MAPERROR(CKR_PIN_INCORRECT, SEC_ERROR_BAD_PASSWORD) + MAPERROR(CKR_PIN_INVALID, SEC_ERROR_BAD_PASSWORD) + MAPERROR(CKR_PIN_LEN_RANGE, SEC_ERROR_BAD_PASSWORD) + MAPERROR(CKR_SESSION_CLOSED, SEC_ERROR_LIBRARY_FAILURE) + MAPERROR(CKR_SESSION_COUNT, SEC_ERROR_NO_MEMORY) /* XXXX? */ + MAPERROR(CKR_SESSION_HANDLE_INVALID, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_SESSION_PARALLEL_NOT_SUPPORTED, SEC_ERROR_LIBRARY_FAILURE) + MAPERROR(CKR_SESSION_READ_ONLY, SEC_ERROR_LIBRARY_FAILURE) + MAPERROR(CKR_SIGNATURE_INVALID, SEC_ERROR_BAD_SIGNATURE) + MAPERROR(CKR_SIGNATURE_LEN_RANGE, SEC_ERROR_BAD_SIGNATURE) + MAPERROR(CKR_TEMPLATE_INCOMPLETE, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_TEMPLATE_INCONSISTENT, SEC_ERROR_BAD_DATA) + MAPERROR(CKR_TOKEN_NOT_PRESENT, SEC_ERROR_NO_TOKEN) + MAPERROR(CKR_TOKEN_NOT_RECOGNIZED, SEC_ERROR_IO) + MAPERROR(CKR_TOKEN_WRITE_PROTECTED, SEC_ERROR_READ_ONLY) + MAPERROR(CKR_UNWRAPPING_KEY_HANDLE_INVALID, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_UNWRAPPING_KEY_SIZE_RANGE, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_USER_ALREADY_LOGGED_IN, 0) + MAPERROR(CKR_USER_NOT_LOGGED_IN, SEC_ERROR_LIBRARY_FAILURE) /* XXXX */ + MAPERROR(CKR_USER_PIN_NOT_INITIALIZED, SEC_ERROR_NO_TOKEN) + MAPERROR(CKR_USER_TYPE_INVALID, SEC_ERROR_LIBRARY_FAILURE) + MAPERROR(CKR_WRAPPED_KEY_INVALID, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_WRAPPED_KEY_LEN_RANGE, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_WRAPPING_KEY_HANDLE_INVALID, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_WRAPPING_KEY_SIZE_RANGE, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_WRAPPING_KEY_TYPE_INCONSISTENT, SEC_ERROR_INVALID_KEY) + MAPERROR(CKR_VENDOR_DEFINED, SEC_ERROR_LIBRARY_FAILURE) + + +#ifdef PK11_ERROR_USE_ARRAY + +int +PK11_MapError(CK_RV rv) { + int size = sizeof(pk11_error_map)/sizeof(pk11_error_map[0]); + + for (i=0; i < size; i++) { + if (pk11_error_map[i].pk11_error == rv) { + return pk11_error_map[i].sec_error; + } + } + return SEC_ERROR_IO; + } + + +#else + + default: + break; + } + return SEC_ERROR_IO; +} + + +#endif diff --git a/security/nss/lib/pk11wrap/pk11func.h b/security/nss/lib/pk11wrap/pk11func.h new file mode 100644 index 000000000..a6a6b5a0f --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11func.h @@ -0,0 +1,440 @@ +/* + * 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. + * + * PKCS #11 Wrapper functions which handles authenticating to the card's + * choosing the best cards, etc. + */ +#ifndef _PK11FUNC_H_ +#define _PK11FUNC_H_ +#include "plarena.h" +#include "mcom_db.h" +#include "seccomon.h" +#include "secoidt.h" +#include "secdert.h" +#include "keyt.h" +#include "certt.h" +#include "pkcs11t.h" +#include "secmodt.h" +#include "seccomon.h" +#include "pkcs7t.h" + +SEC_BEGIN_PROTOS + +/************************************************************ + * Generic Slot Lists Management + ************************************************************/ +PK11SlotList * PK11_NewSlotList(void); +void PK11_FreeSlotList(PK11SlotList *list); +SECStatus PK11_AddSlotToList(PK11SlotList *list,PK11SlotInfo *slot); +SECStatus PK11_DeleteSlotFromList(PK11SlotList *list,PK11SlotListElement *le); +PK11SlotListElement * PK11_GetFirstSafe(PK11SlotList *list); +PK11SlotListElement *PK11_GetNextSafe(PK11SlotList *list, + PK11SlotListElement *le, PRBool restart); +PK11SlotListElement *PK11_FindSlotElement(PK11SlotList *list, + PK11SlotInfo *slot); + +/************************************************************ + * Generic Slot Management + ************************************************************/ +PK11SlotInfo *PK11_ReferenceSlot(PK11SlotInfo *slot); +PK11SlotInfo *PK11_FindSlotByID(SECMODModuleID modID,CK_SLOT_ID slotID); +void PK11_FreeSlot(PK11SlotInfo *slot); +SECStatus PK11_DestroyObject(PK11SlotInfo *slot,CK_OBJECT_HANDLE object); +SECStatus PK11_DestroyTokenObject(PK11SlotInfo *slot,CK_OBJECT_HANDLE object); +CK_OBJECT_HANDLE PK11_CopyKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE srcObject); +SECStatus PK11_ReadAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id, + CK_ATTRIBUTE_TYPE type, PRArenaPool *arena, SECItem *result); +CK_ULONG PK11_ReadULongAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id, + CK_ATTRIBUTE_TYPE type); +PK11SlotInfo *PK11_GetInternalKeySlot(void); +PK11SlotInfo *PK11_GetInternalSlot(void); +char * PK11_MakeString(PRArenaPool *arena,char *space,char *staticSring, + int stringLen); +int PK11_MapError(CK_RV error); +CK_SESSION_HANDLE PK11_GetRWSession(PK11SlotInfo *slot); +void PK11_RestoreROSession(PK11SlotInfo *slot,CK_SESSION_HANDLE rwsession); +PRBool PK11_RWSessionHasLock(PK11SlotInfo *slot, + CK_SESSION_HANDLE session_handle); +PK11SlotInfo *PK11_NewSlotInfo(void); +SECStatus PK11_Logout(PK11SlotInfo *slot); +void PK11_LogoutAll(void); +void PK11_EnterSlotMonitor(PK11SlotInfo *); +void PK11_ExitSlotMonitor(PK11SlotInfo *); + + +/************************************************************ + * Slot Password Management + ************************************************************/ +void PK11_SetSlotPWValues(PK11SlotInfo *slot,int askpw, int timeout); +void PK11_GetSlotPWValues(PK11SlotInfo *slot,int *askpw, int *timeout); +SECStatus PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw); +SECStatus PK11_CheckUserPassword(PK11SlotInfo *slot,char *pw); +SECStatus PK11_DoPassword(PK11SlotInfo *slot, PRBool loadCerts, void *wincx); +PRBool PK11_IsLoggedIn(PK11SlotInfo *slot, void *wincx); +SECStatus PK11_VerifyPW(PK11SlotInfo *slot,char *pw); +SECStatus PK11_InitPin(PK11SlotInfo *slot,char *ssopw, char *pk11_userpwd); +SECStatus PK11_ChangePW(PK11SlotInfo *slot,char *oldpw, char *newpw); +void PK11_HandlePasswordCheck(PK11SlotInfo *slot,void *wincx); +void PK11_SetPasswordFunc(PK11PasswordFunc func); +void PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func); +void PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func); +int PK11_GetMinimumPwdLength(PK11SlotInfo *slot); +SECStatus PK11_ResetToken(PK11SlotInfo *slot, char *sso_pwd); + +/************************************************************ + * Manage the built-In Slot Lists + ************************************************************/ +SECStatus PK11_InitSlotLists(void); +PK11SlotList *PK11_GetSlotList(CK_MECHANISM_TYPE type); +void PK11_LoadSlotList(PK11SlotInfo *slot, PK11PreSlotInfo *psi, int count); +void PK11_ClearSlotList(PK11SlotInfo *slot); + + +/****************************************************************** + * Slot initialization + ******************************************************************/ +PRBool PK11_VerifyMechanism(PK11SlotInfo *slot,PK11SlotInfo *intern, + CK_MECHANISM_TYPE mech, SECItem *data, SECItem *iv); +PRBool PK11_VerifySlotMechanisms(PK11SlotInfo *slot); +SECStatus pk11_CheckVerifyTest(PK11SlotInfo *slot); +SECStatus PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts); +SECStatus PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx); +void PK11_InitSlot(SECMODModule *mod,CK_SLOT_ID slotID,PK11SlotInfo *slot); + + +/****************************************************************** + * Slot info functions + ******************************************************************/ +PK11SlotInfo *PK11_FindSlotByName(char *name); +PK11SlotInfo *PK11_FindSlotBySerial(char *serial); +PRBool PK11_IsReadOnly(PK11SlotInfo *slot); +PRBool PK11_IsInternal(PK11SlotInfo *slot); +char * PK11_GetTokenName(PK11SlotInfo *slot); +char * PK11_GetSlotName(PK11SlotInfo *slot); +PRBool PK11_NeedLogin(PK11SlotInfo *slot); +PRBool PK11_IsFriendly(PK11SlotInfo *slot); +PRBool PK11_IsHW(PK11SlotInfo *slot); +PRBool PK11_NeedUserInit(PK11SlotInfo *slot); +int PK11_GetSlotSeries(PK11SlotInfo *slot); +int PK11_GetCurrentWrapIndex(PK11SlotInfo *slot); +unsigned long PK11_GetDefaultFlags(PK11SlotInfo *slot); +CK_SLOT_ID PK11_GetSlotID(PK11SlotInfo *slot); +SECMODModuleID PK11_GetModuleID(PK11SlotInfo *slot); +SECStatus PK11_GetSlotInfo(PK11SlotInfo *slot, CK_SLOT_INFO *info); +SECStatus PK11_GetTokenInfo(PK11SlotInfo *slot, CK_TOKEN_INFO *info); +PRBool PK11_IsDisabled(PK11SlotInfo *slot); +PK11DisableReasons PK11_GetDisabledReason(PK11SlotInfo *slot); +/* Prevents the slot from being used, and set disable reason to user-disable */ +/* NOTE: Mechanisms that were ON continue to stay ON */ +/* Therefore, when the slot is enabled, it will remember */ +/* what mechanisms needs to be turned on */ +PRBool PK11_UserDisableSlot(PK11SlotInfo *slot); +/* Allow all mechanisms that are ON before UserDisableSlot() */ +/* was called to be available again */ +PRBool PK11_UserEnableSlot(PK11SlotInfo *slot); + +PRBool PK11_NeedPWInit(void); +PRBool PK11_NeedPWInitForSlot(PK11SlotInfo *slot); +PRBool PK11_TokenExists(CK_MECHANISM_TYPE); +SECStatus PK11_GetModInfo(SECMODModule *mod, CK_INFO *info); +PRBool PK11_IsFIPS(void); +SECMODModule *PK11_GetModule(PK11SlotInfo *slot); + +/********************************************************************* + * Slot mapping utility functions. + *********************************************************************/ +PRBool PK11_IsPresent(PK11SlotInfo *slot); +PRBool PK11_DoesMechanism(PK11SlotInfo *slot, CK_MECHANISM_TYPE type); +PK11SlotList * PK11_GetAllTokens(CK_MECHANISM_TYPE type,PRBool needRW, + PRBool loadCerts, void *wincx); +PK11SlotList * PK11_GetPrivateKeyTokens(CK_MECHANISM_TYPE type, + PRBool needRW,void *wincx); +PK11SlotInfo *PK11_GetBestSlotMultiple(CK_MECHANISM_TYPE *type, int count, + void *wincx); +PK11SlotInfo *PK11_GetBestSlot(CK_MECHANISM_TYPE type, void *wincx); +CK_MECHANISM_TYPE PK11_GetBestWrapMechanism(PK11SlotInfo *slot); +int PK11_GetBestKeyLength(PK11SlotInfo *slot, CK_MECHANISM_TYPE type); + +/********************************************************************* + * Mechanism Mapping functions + *********************************************************************/ +void PK11_AddMechanismEntry(CK_MECHANISM_TYPE type, CK_KEY_TYPE key, + CK_MECHANISM_TYPE keygen, int ivLen, int blocksize); +CK_MECHANISM_TYPE PK11_GetKeyType(CK_MECHANISM_TYPE type,unsigned long len); +CK_MECHANISM_TYPE PK11_GetKeyGen(CK_MECHANISM_TYPE type); +int PK11_GetBlockSize(CK_MECHANISM_TYPE type,SECItem *params); +int PK11_GetIVLength(CK_MECHANISM_TYPE type); +SECItem *PK11_ParamFromIV(CK_MECHANISM_TYPE type,SECItem *iv); +unsigned char *PK11_IVFromParam(CK_MECHANISM_TYPE type,SECItem *param,int *len); +SECItem * PK11_BlockData(SECItem *data,unsigned long size); + +/* PKCS #11 to DER mapping functions */ +SECItem *PK11_ParamFromAlgid(SECAlgorithmID *algid); +SECItem *PK11_GenerateNewParam(CK_MECHANISM_TYPE, PK11SymKey *); +CK_MECHANISM_TYPE PK11_AlgtagToMechanism(SECOidTag algTag); +SECOidTag PK11_MechanismToAlgtag(CK_MECHANISM_TYPE type); +SECOidTag PK11_FortezzaMapSig(SECOidTag algTag); +SECStatus PK11_ParamToAlgid(SECOidTag algtag, SECItem *param, + PRArenaPool *arena, SECAlgorithmID *algid); +SECStatus PK11_SeedRandom(PK11SlotInfo *,unsigned char *data,int len); +SECStatus PK11_RandomUpdate(void *data, size_t bytes); +SECStatus PK11_GenerateRandom(unsigned char *data,int len); +CK_RV PK11_MapPBEMechanismToCryptoMechanism(CK_MECHANISM_PTR pPBEMechanism, + CK_MECHANISM_PTR pCryptoMechanism, + SECItem *pbe_pwd, PRBool bad3DES); +CK_MECHANISM_TYPE PK11_GetPadMechanism(CK_MECHANISM_TYPE); + +/********************************************************************** + * Symetric, Public, and Private Keys + **********************************************************************/ +PK11SymKey *PK11_CreateSymKey(PK11SlotInfo *slot, + CK_MECHANISM_TYPE type, void *wincx); +void PK11_FreeSymKey(PK11SymKey *key); +PK11SymKey *PK11_ReferenceSymKey(PK11SymKey *symKey); +PK11SymKey *PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,void *wincx); +PK11SymKey *PK11_SymKeyFromHandle(PK11SlotInfo *slot, PK11SymKey *parent, + PK11Origin origin, CK_MECHANISM_TYPE type, CK_OBJECT_HANDLE keyID, + PRBool owner, void *wincx); +PK11SymKey *PK11_GetWrapKey(PK11SlotInfo *slot, int wrap, + CK_MECHANISM_TYPE type,int series, void *wincx); +void PK11_SetWrapKey(PK11SlotInfo *slot, int wrap, PK11SymKey *wrapKey); +CK_MECHANISM_TYPE PK11_GetMechanism(PK11SymKey *symKey); +CK_OBJECT_HANDLE PK11_ImportPublicKey(PK11SlotInfo *slot, + SECKEYPublicKey *pubKey, PRBool isToken); +PK11SymKey *PK11_KeyGen(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, + SECItem *param, int keySize,void *wincx); +SECStatus PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey, + PK11SymKey *symKey, SECItem *wrappedKey); +SECStatus PK11_WrapSymKey(CK_MECHANISM_TYPE type, SECItem *params, + PK11SymKey *wrappingKey, PK11SymKey *symKey, SECItem *wrappedKey); +PK11SymKey *PK11_Derive(PK11SymKey *baseKey, CK_MECHANISM_TYPE mechanism, + SECItem *param, CK_MECHANISM_TYPE target, + CK_ATTRIBUTE_TYPE operation, int keySize); +PK11SymKey *PK11_DeriveWithFlags( PK11SymKey *baseKey, + CK_MECHANISM_TYPE derive, SECItem *param, CK_MECHANISM_TYPE target, + CK_ATTRIBUTE_TYPE operation, int keySize, CK_FLAGS flags); +PK11SymKey *PK11_PubDerive( SECKEYPrivateKey *privKey, + SECKEYPublicKey *pubKey, PRBool isSender, SECItem *randomA, SECItem *randomB, + CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target, + CK_ATTRIBUTE_TYPE operation, int keySize,void *wincx) ; +PK11SymKey *PK11_UnwrapSymKey(PK11SymKey *key, + CK_MECHANISM_TYPE wraptype, SECItem *param, SECItem *wrapppedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize); +PK11SymKey *PK11_UnwrapSymKeyWithFlags(PK11SymKey *wrappingKey, + CK_MECHANISM_TYPE wrapType, SECItem *param, SECItem *wrappedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize, + CK_FLAGS flags); +PK11SymKey *PK11_PubUnwrapSymKey(SECKEYPrivateKey *key, SECItem *wrapppedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize); +PK11SymKey *PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + SECItem *keyID, void *wincx); +SECStatus PK11_DeleteTokenPrivateKey(SECKEYPrivateKey *privKey); +SECStatus PK11_DeleteTokenCertAndKey(CERTCertificate *cert,void *wincx); + +/* size to hold key in bytes */ +unsigned int PK11_GetKeyLength(PK11SymKey *key); +/* size of actual secret parts of key in bits */ +/* algid is because RC4 strength is determined by the effective bits as well + * as the key bits */ +unsigned int PK11_GetKeyStrength(PK11SymKey *key,SECAlgorithmID *algid); +SECStatus PK11_ExtractKeyValue(PK11SymKey *symKey); +SECItem * PK11_GetKeyData(PK11SymKey *symKey); +PK11SlotInfo * PK11_GetSlotFromKey(PK11SymKey *symKey); +void *PK11_GetWindow(PK11SymKey *symKey); +SECKEYPrivateKey *PK11_GenerateKeyPair(PK11SlotInfo *slot, + CK_MECHANISM_TYPE type, void *param, SECKEYPublicKey **pubk, + PRBool isPerm, PRBool isSensitive, void *wincx); +SECKEYPrivateKey *PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType, + PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx); +SECKEYPrivateKey * PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot, + CERTCertificate *cert, void *wincx); +SECKEYPrivateKey * PK11_FindKeyByAnyCert(CERTCertificate *cert, void *wincx); +SECKEYPrivateKey * PK11_FindKeyByKeyID(PK11SlotInfo *slot, SECItem *keyID, + void *wincx); +CK_OBJECT_HANDLE PK11_FindObjectForCert(CERTCertificate *cert, + void *wincx, PK11SlotInfo **pSlot); +int PK11_GetPrivateModulusLen(SECKEYPrivateKey *key); +SECStatus PK11_PubDecryptRaw(SECKEYPrivateKey *key, unsigned char *data, + unsigned *outLen, unsigned int maxLen, unsigned char *enc, unsigned encLen); +/* The encrypt version of the above function */ +SECStatus PK11_PubEncryptRaw(SECKEYPublicKey *key, unsigned char *enc, + unsigned char *data, unsigned dataLen, void *wincx); +SECStatus PK11_ImportPrivateKeyInfo(PK11SlotInfo *slot, + SECKEYPrivateKeyInfo *pki, SECItem *nickname, + SECItem *publicValue, PRBool isPerm, PRBool isPrivate, + unsigned int usage, void *wincx); +SECStatus PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot, + SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem, + SECItem *nickname, SECItem *publicValue, PRBool isPerm, + PRBool isPrivate, KeyType type, + unsigned int usage, void *wincx); +SECKEYPrivateKeyInfo *PK11_ExportPrivateKeyInfo( + CERTCertificate *cert, void *wincx); +SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivateKeyInfo( + PK11SlotInfo *slot, SECOidTag algTag, SECItem *pwitem, + CERTCertificate *cert, int iteration, void *wincx); +SECKEYPrivateKey *PK11_FindKeyByDERCert(PK11SlotInfo *slot, + CERTCertificate *cert, void *wincx); +SECKEYPublicKey *PK11_MakeKEAPubKey(unsigned char *data, int length); +SECStatus PK11_DigestKey(PK11Context *context, PK11SymKey *key); +PRBool PK11_VerifyKeyOK(PK11SymKey *key); +SECKEYPrivateKey *PK11_UnwrapPrivKey(PK11SlotInfo *slot, + PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType, + SECItem *param, SECItem *wrappedKey, SECItem *label, + SECItem *publicValue, PRBool token, PRBool sensitive, + CK_KEY_TYPE keyType, CK_ATTRIBUTE_TYPE *usage, int usageCount, + void *wincx); +SECStatus PK11_WrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey, + SECKEYPrivateKey *privKey, CK_MECHANISM_TYPE wrapType, + SECItem *param, SECItem *wrappedKey, void *wincx); +PK11SymKey * pk11_CopyToSlot(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, + CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey); +SECItem *PK11_GetKeyIDFromCert(CERTCertificate *cert, void *wincx); +SECItem * PK11_GetKeyIDFromPrivateKey(SECKEYPrivateKey *key, void *wincx); + +/********************************************************************** + * Certs + **********************************************************************/ +SECItem *PK11_MakeIDFromPubKey(SECItem *pubKeyData); +CERTCertificate *PK11_GetCertFromPrivateKey(SECKEYPrivateKey *privKey); +SECStatus PK11_TraverseSlotCerts( + SECStatus(* callback)(CERTCertificate*,SECItem *,void *), + void *arg, void *wincx); +CERTCertificate * PK11_FindCertFromNickname(char *nickname, void *wincx); +CERTCertList * PK11_FindCertsFromNickname(char *nickname, void *wincx); +SECKEYPrivateKey * PK11_FindPrivateKeyFromNickname(char *nickname, void *wincx); +PK11SlotInfo *PK11_ImportCertForKey(CERTCertificate *cert, char *nickname, + void *wincx); +CK_OBJECT_HANDLE * PK11_FindObjectsFromNickname(char *nickname, + PK11SlotInfo **slotptr, CK_OBJECT_CLASS objclass, int *returnCount, + void *wincx); +PK11SlotInfo *PK11_KeyForCertExists(CERTCertificate *cert, + CK_OBJECT_HANDLE *keyPtr, void *wincx); +CK_OBJECT_HANDLE PK11_MatchItem(PK11SlotInfo *slot,CK_OBJECT_HANDLE peer, + CK_OBJECT_CLASS o_class); +CERTCertificate * PK11_FindCertByIssuerAndSN(PK11SlotInfo **slot, + CERTIssuerAndSN *sn, void *wincx); +CERTCertificate * PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slot, + SEC_PKCS7RecipientInfo **array, SEC_PKCS7RecipientInfo **rip, + SECKEYPrivateKey**privKey, void *wincx); +CK_BBOOL PK11_HasAttributeSet( PK11SlotInfo *slot, + CK_OBJECT_HANDLE id, + CK_ATTRIBUTE_TYPE type ); +CK_RV PK11_GetAttributes(PRArenaPool *arena,PK11SlotInfo *slot, + CK_OBJECT_HANDLE obj,CK_ATTRIBUTE *attr, int count); +int PK11_NumberCertsForCertSubject(CERTCertificate *cert); +SECStatus PK11_TraverseCertsForSubject(CERTCertificate *cert, + SECStatus(*callback)(CERTCertificate *, void *), void *arg); +SECStatus PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert, + PK11SlotInfo *slot, SECStatus(*callback)(CERTCertificate *, void *), + void *arg); +CERTCertificate *PK11_FindCertFromDERCert(PK11SlotInfo *slot, + CERTCertificate *cert, void *wincx); +CERTCertificate *PK11_FindCertFromDERSubjectAndNickname( + PK11SlotInfo *slot, + CERTCertificate *cert, char *nickname, + void *wincx); +SECStatus PK11_ImportCertForKeyToSlot(PK11SlotInfo *slot, CERTCertificate *cert, + char *nickname, PRBool addUsage, + void *wincx); +CERTCertificate *PK11_FindBestKEAMatch(CERTCertificate *serverCert,void *wincx); +SECStatus PK11_GetKEAMatchedCerts(PK11SlotInfo *slot1, + PK11SlotInfo *slot2, CERTCertificate **cert1, CERTCertificate **cert2); +PRBool PK11_FortezzaHasKEA(CERTCertificate *cert); +CK_OBJECT_HANDLE PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx); +SECStatus PK11_TraverseCertsForNicknameInSlot(SECItem *nickname, + PK11SlotInfo *slot, SECStatus(*callback)(CERTCertificate *, void *), + void *arg); +SECStatus PK11_TraverseCertsInSlot(PK11SlotInfo *slot, + SECStatus(* callback)(CERTCertificate*, void *), void *arg); + + +/********************************************************************** + * Sign/Verify + **********************************************************************/ +int PK11_SignatureLen(SECKEYPrivateKey *key); +PK11SlotInfo * PK11_GetSlotFromPrivateKey(SECKEYPrivateKey *key); +SECStatus PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, SECItem *hash); +SECStatus PK11_VerifyRecover(SECKEYPublicKey *key, SECItem *sig, + SECItem *dsig, void * wincx); +SECStatus PK11_Verify(SECKEYPublicKey *key, SECItem *sig, + SECItem *hash, void *wincx); + + + +/********************************************************************** + * Crypto Contexts + **********************************************************************/ +void PK11_DestroyContext(PK11Context *context, PRBool freeit); +PK11Context * PK11_CreateContextByRawKey(PK11SlotInfo *slot, + CK_MECHANISM_TYPE type, PK11Origin origin, CK_ATTRIBUTE_TYPE operation, + SECItem *key, SECItem *param, void *wincx); +PK11Context *PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type, + CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey, SECItem *param); +PK11Context *PK11_CreateDigestContext(SECOidTag hashAlg); +PK11Context *PK11_CloneContext(PK11Context *old); +SECStatus PK11_DigestBegin(PK11Context *cx); +SECStatus PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, unsigned char *in, + int32 len); +SECStatus PK11_DigestOp(PK11Context *context, const unsigned char *in, + unsigned len); +SECStatus PK11_CipherOp(PK11Context *context, unsigned char * out, int *outlen, + int maxout, unsigned char *in, int inlen); +SECStatus PK11_Finalize(PK11Context *context); +SECStatus PK11_DigestFinal(PK11Context *context, unsigned char *data, + unsigned int *outLen, unsigned int length); +PRBool PK11_HashOK(SECOidTag hashAlg); +SECStatus PK11_SaveContext(PK11Context *cx,unsigned char *save, + int *len, int saveLength); +SECStatus PK11_RestoreContext(PK11Context *cx,unsigned char *save,int len); +SECStatus PK11_GenerateFortezzaIV(PK11SymKey *symKey,unsigned char *iv,int len); +SECStatus PK11_ReadSlotCerts(PK11SlotInfo *slot); +void PK11_FreeSlotCerts(PK11SlotInfo *slot); +void PK11_SetFortezzaHack(PK11SymKey *symKey) ; + + +/********************************************************************** + * PBE functions + **********************************************************************/ +SECAlgorithmID * +PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt); +PK11SymKey * +PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem, + PRBool faulty3DES, void *wincx); +SECItem * +PK11_GetPBEIV(SECAlgorithmID *algid, SECItem *pwitem); + +SEC_END_PROTOS + +#endif diff --git a/security/nss/lib/pk11wrap/pk11kea.c b/security/nss/lib/pk11wrap/pk11kea.c new file mode 100644 index 000000000..ef9fe2aef --- /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 "prlock.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/pk11wrap/pk11list.c b/security/nss/lib/pk11wrap/pk11list.c new file mode 100644 index 000000000..35a68d8a2 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11list.c @@ -0,0 +1,165 @@ +/* + * 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. + */ +/* + * Locking and queue management primatives + * + */ + +#include "seccomon.h" +#include "prlock.h" +#include "prmon.h" +#include "secmod.h" +#include "secmodi.h" +#include "prlong.h" + +#define ISREADING 1 +#define ISWRITING 2 +#define WANTWRITE 4 +#define ISLOCKED 3 + +/* + * create a new lock for a Module List + */ +SECMODListLock *SECMOD_NewListLock() { + SECMODListLock *modLock; + + modLock = (SECMODListLock*)PORT_Alloc(sizeof(SECMODListLock)); +#ifdef PKCS11_USE_THREADS + modLock->mutex = NULL; + modLock->monitor = PR_NewMonitor(); +#else + modLock->mutex = NULL; + modLock->monitor = NULL; +#endif + modLock->state = 0; + modLock->count = 0; + return modLock; +} + +/* + * destroy the lock + */ +void SECMOD_DestroyListLock(SECMODListLock *lock) { + PK11_USE_THREADS(PR_DestroyMonitor(lock->monitor);) + PORT_Free(lock); +} + + +/* + * Lock the List for Read: NOTE: this assumes the reading isn't so common + * the writing will be starved. + */ +void SECMOD_GetReadLock(SECMODListLock *modLock) { +#ifdef PKCS11_USE_THREADS + PR_EnterMonitor(modLock->monitor); + while (modLock->state & ISWRITING) { + PR_Wait(modLock->monitor,PR_INTERVAL_NO_TIMEOUT); /* wait until woken up */ + } + modLock->state |= ISREADING; + modLock->count++; + PR_ExitMonitor(modLock->monitor); +#endif +} + +/* + * Release the Read lock + */ +void SECMOD_ReleaseReadLock(SECMODListLock *modLock) { +#ifdef PKCS11_USE_THREADS + PR_EnterMonitor(modLock->monitor); + modLock->count--; + if (modLock->count == 0) { + modLock->state &= ~ISREADING; + if (modLock->state & WANTWRITE) { + PR_Notify(modLock->monitor); /* only one writer at a time */ + } + } + PR_ExitMonitor(modLock->monitor); +#endif +} + + +/* + * lock the list for Write + */ +void SECMOD_GetWriteLock(SECMODListLock *modLock) { +#ifdef PKCS11_USE_THREADS + PR_EnterMonitor(modLock->monitor); + while (modLock->state & ISLOCKED) { + modLock->state |= WANTWRITE; + PR_Wait(modLock->monitor,PR_INTERVAL_NO_TIMEOUT); /* wait until woken up */ + } + modLock->state = ISWRITING; + PR_ExitMonitor(modLock->monitor); +#endif +} + + +/* + * Release the Write Lock: NOTE, this code is pretty inefficient if you have + * lots of write collisions. + */ +void SECMOD_ReleaseWriteLock(SECMODListLock *modLock) { +#ifdef PKCS11_USE_THREADS + PR_EnterMonitor(modLock->monitor); + modLock->state = 0; + PR_NotifyAll(modLock->monitor); /* enable all the readers */ + PR_ExitMonitor(modLock->monitor); +#endif +} + + +/* + * must Hold the Write lock + */ +void +SECMOD_RemoveList(SECMODModuleList **parent, SECMODModuleList *child) { + *parent = child->next; + child->next = NULL; +} + +/* + * if lock is not specified, it must already be held + */ +void +SECMOD_AddList(SECMODModuleList *parent, SECMODModuleList *child, + SECMODListLock *lock) { + if (lock) { SECMOD_GetWriteLock(lock); } + + child->next = parent->next; + parent->next = child; + + if (lock) { SECMOD_ReleaseWriteLock(lock); } +} + + diff --git a/security/nss/lib/pk11wrap/pk11load.c b/security/nss/lib/pk11wrap/pk11load.c new file mode 100644 index 000000000..65d898e6e --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11load.c @@ -0,0 +1,239 @@ +/* + * 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. + */ +/* + * The following handles the loading, unloading and management of + * various PCKS #11 modules + */ +#include "seccomon.h" +#include "pkcs11.h" +#include "secmod.h" +#include "prlink.h" +#include "pk11func.h" +#include "secmodi.h" +#include "prlock.h" + +extern void FC_GetFunctionList(void); +extern void NSC_GetFunctionList(void); + + +/* build the PKCS #11 2.01 lock files */ +CK_RV PR_CALLBACK secmodCreateMutext(CK_VOID_PTR_PTR pmutex) { + *pmutex = (CK_VOID_PTR) PR_NewLock(); + if ( *pmutex ) return CKR_OK; + return CKR_HOST_MEMORY; +} + +CK_RV PR_CALLBACK secmodDestroyMutext(CK_VOID_PTR mutext) { + PR_DestroyLock((PRLock *)mutext); + return CKR_OK; +} + +CK_RV PR_CALLBACK secmodLockMutext(CK_VOID_PTR mutext) { + PR_Lock((PRLock *)mutext); + return CKR_OK; +} + +CK_RV PR_CALLBACK secmodUnlockMutext(CK_VOID_PTR mutext) { + PR_Unlock((PRLock *)mutext); + return CKR_OK; +} + +static SECMODModuleID nextModuleID = 1; +static CK_C_INITIALIZE_ARGS secmodLockFunctions = { + secmodCreateMutext, secmodDestroyMutext, secmodLockMutext, + secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS| + CKF_OS_LOCKING_OK + ,NULL +}; + +/* + * load a new module into our address space and initialize it. + */ +SECStatus +SECMOD_LoadModule(SECMODModule *mod) { + PRLibrary *library = NULL; + CK_C_GetFunctionList entry; + char * full_name; + CK_INFO info; + CK_ULONG slotCount = 0; + + + if (mod->loaded) return SECSuccess; + + /* intenal modules get loaded from their internal list */ + if (mod->internal) { + /* internal, statically get the C_GetFunctionList function */ + if (mod->isFIPS) { + entry = (CK_C_GetFunctionList) FC_GetFunctionList; + } else { + entry = (CK_C_GetFunctionList) NSC_GetFunctionList; + } + } else { + /* Not internal, load the DLL and look up C_GetFunctionList */ + if (mod->dllName == NULL) { + return SECFailure; + } + +#ifdef notdef + /* look up the library name */ + full_name = PR_GetLibraryName(PR_GetLibraryPath(),mod->dllName); + if (full_name == NULL) { + return SECFailure; + } +#else + full_name = PORT_Strdup(mod->dllName); +#endif + + /* load the library. If this succeeds, then we have to remember to + * unload the library if anything goes wrong from here on out... + */ + library = PR_LoadLibrary(full_name); + mod->library = (void *)library; + PORT_Free(full_name); + if (library == NULL) { + return SECFailure; + } + + /* + * now we need to get the entry point to find the function pointers + */ + entry = (CK_C_GetFunctionList) + PR_FindSymbol(library, "C_GetFunctionList"); + if (entry == NULL) { + PR_UnloadLibrary(library); + return SECFailure; + } + } + + /* + * We need to get the function list + */ + if ((*entry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK) + goto fail; + + mod->isThreadSafe = PR_TRUE; + /* Now we initialize the module */ + if (PK11_GETTAB(mod)->C_Initialize(&secmodLockFunctions) != CKR_OK) { + mod->isThreadSafe = PR_FALSE; + if (PK11_GETTAB(mod)->C_Initialize(NULL) != CKR_OK) goto fail; + } + + /* check the version number */ + if (PK11_GETTAB(mod)->C_GetInfo(&info) != CKR_OK) goto fail2; + if (info.cryptokiVersion.major != 2) goto fail2; + /* all 2.0 are a priori *not* thread safe */ + if (info.cryptokiVersion.minor < 1) mod->isThreadSafe = PR_FALSE; + + + /* If we don't have a common name, get it from the PKCS 11 module */ + if ((mod->commonName == NULL) || (mod->commonName[0] == 0)) { + mod->commonName = PK11_MakeString(mod->arena,NULL, + (char *)info.libraryDescription, sizeof(info.libraryDescription)); + if (mod->commonName == NULL) goto fail2; + } + + + /* initialize the Slots */ + if (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &slotCount) == CKR_OK) { + CK_SLOT_ID *slotIDs; + int i; + CK_RV rv; + + mod->slots = (PK11SlotInfo **)PORT_ArenaAlloc(mod->arena, + sizeof(PK11SlotInfo *) * slotCount); + if (mod->slots == NULL) goto fail2; + + slotIDs = (CK_SLOT_ID *) PORT_Alloc(sizeof(CK_SLOT_ID)*slotCount); + if (slotIDs == NULL) { + goto fail2; + } + rv = PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, slotIDs, &slotCount); + if (rv != CKR_OK) { + PORT_Free(slotIDs); + goto fail2; + } + + /* Initialize each slot */ + for (i=0; i < (int)slotCount; i++) { + mod->slots[i] = PK11_NewSlotInfo(); + PK11_InitSlot(mod,slotIDs[i],mod->slots[i]); + /* look down the slot info table */ + PK11_LoadSlotList(mod->slots[i],mod->slotInfo,mod->slotInfoCount); + } + mod->slotCount = slotCount; + mod->slotInfoCount = 0; + PORT_Free(slotIDs); + } + + + + + mod->loaded = PR_TRUE; + mod->moduleID = nextModuleID++; + return SECSuccess; +fail2: + PK11_GETTAB(mod)->C_Finalize(NULL); +fail: + mod->functionList = NULL; + if (library) PR_UnloadLibrary(library); + return SECFailure; +} + +SECStatus +SECMOD_UnloadModule(SECMODModule *mod) { + PRLibrary *library; + + if (!mod->loaded) { + return SECFailure; + } + + PK11_GETTAB(mod)->C_Finalize(NULL); + mod->moduleID = 0; + mod->loaded = PR_FALSE; + + /* do we want the semantics to allow unloading the internal library? + * if not, we should change this to SECFailure and move it above the + * mod->loaded = PR_FALSE; */ + if (mod->internal) { + return SECSuccess; + } + + library = (PRLibrary *)mod->library; + /* paranoia */ + if (library == NULL) { + return SECFailure; + } + + PR_UnloadLibrary(library); + return SECSuccess; +} diff --git a/security/nss/lib/pk11wrap/pk11skey.c b/security/nss/lib/pk11wrap/pk11skey.c new file mode 100644 index 000000000..dd31f6769 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11skey.c @@ -0,0 +1,4676 @@ +/* + * 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 "prlock.h" +#include "secmodi.h" +#include "pkcs11.h" +#include "pk11func.h" +#include "secitem.h" +#include "key.h" +#include "secoid.h" +#include "secasn1.h" +#include "sechash.h" +#include "cert.h" +#include "secerr.h" + +#define PAIRWISE_SECITEM_TYPE siBuffer +#define PAIRWISE_DIGEST_LENGTH SHA1_LENGTH /* 160-bits */ +#define PAIRWISE_MESSAGE_LENGTH 20 /* 160-bits */ + +/* forward static declarations. */ +static PK11SymKey *pk11_DeriveWithTemplate(PK11SymKey *baseKey, + CK_MECHANISM_TYPE derive, SECItem *param, CK_MECHANISM_TYPE target, + CK_ATTRIBUTE_TYPE operation, int keySize, CK_ATTRIBUTE *userAttr, + unsigned int numAttrs); + + +/* + * strip leading zero's from key material + */ +void +pk11_SignedToUnsigned(CK_ATTRIBUTE *attrib) { + char *ptr = (char *)attrib->pValue; + unsigned long len = attrib->ulValueLen; + + while (len && (*ptr == 0)) { + len--; + ptr++; + } + attrib->pValue = ptr; + attrib->ulValueLen = len; +} + +/* + * get a new session on a slot. If we run out of session, use the slot's + * 'exclusive' session. In this case owner becomes false. + */ +static CK_SESSION_HANDLE +pk11_GetNewSession(PK11SlotInfo *slot,PRBool *owner) +{ + CK_SESSION_HANDLE session; + *owner = PR_TRUE; + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + if ( PK11_GETTAB(slot)->C_OpenSession(slot->slotID,CKF_SERIAL_SESSION, + slot,pk11_notify,&session) != CKR_OK) { + *owner = PR_FALSE; + session = slot->session; + } + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + + return session; +} + +static void +pk11_CloseSession(PK11SlotInfo *slot,CK_SESSION_HANDLE session,PRBool owner) +{ + if (!owner) return; + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + (void) PK11_GETTAB(slot)->C_CloseSession(session); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); +} + + +SECStatus +PK11_CreateNewObject(PK11SlotInfo *slot,CK_ATTRIBUTE *theTemplate, int count, + PRBool token, CK_OBJECT_HANDLE *objectID) +{ + CK_SESSION_HANDLE rwsession; + CK_RV crv; + SECStatus rv = SECSuccess; + + if (token) { + rwsession = PK11_GetRWSession(slot); + } else { + rwsession = slot->session; + PK11_EnterSlotMonitor(slot); + } + crv = PK11_GETTAB(slot)->C_CreateObject(rwsession, theTemplate, + count,objectID); + if(crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + rv = SECFailure; + } + if (token) { + PK11_RestoreROSession(slot, rwsession); + } else { + PK11_ExitSlotMonitor(slot); + } + return rv; +} + +static void +pk11_EnterKeyMonitor(PK11SymKey *symKey) { + if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe)) + PK11_EnterSlotMonitor(symKey->slot); +} + +static void +pk11_ExitKeyMonitor(PK11SymKey *symKey) { + if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe)) + PK11_ExitSlotMonitor(symKey->slot); +} + +/* + * create a symetric key: + * Slot is the slot to create the key in. + * type is the mechainism type + */ +PK11SymKey * +PK11_CreateSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, void *wincx) +{ + + PK11SymKey *symKey = (PK11SymKey *)PORT_Alloc(sizeof(PK11SymKey)); + if (symKey == NULL) { + return NULL; + } + symKey->refLock = PR_NewLock(); + if (symKey->refLock == NULL) { + PORT_Free(symKey); + return NULL; + } + symKey->type = type; + symKey->data.data = NULL; + symKey->data.len = 0; + symKey->owner = PR_TRUE; + symKey->objectID = CK_INVALID_KEY; + symKey->slot = slot; + symKey->series = slot->series; + symKey->cx = wincx; + symKey->size = 0; + symKey->refCount = 1; + symKey->origin = PK11_OriginNULL; + symKey->session = pk11_GetNewSession(slot,&symKey->sessionOwner); + symKey->origin = PK11_OriginNULL; + PK11_ReferenceSlot(slot); + return symKey; +} + +/* + * destroy a symetric key + */ +void +PK11_FreeSymKey(PK11SymKey *symKey) +{ + PRBool destroy = PR_FALSE; + + PK11_USE_THREADS(PR_Lock(symKey->refLock);) + if (symKey->refCount-- == 1) { + destroy= PR_TRUE; + } + PK11_USE_THREADS(PR_Unlock(symKey->refLock);) + if (destroy) { + if ((symKey->owner) && symKey->objectID != CK_INVALID_KEY) { + pk11_EnterKeyMonitor(symKey); + (void) PK11_GETTAB(symKey->slot)-> + C_DestroyObject(symKey->session, symKey->objectID); + pk11_ExitKeyMonitor(symKey); + } + pk11_CloseSession(symKey->slot, symKey->session,symKey->sessionOwner); + if (symKey->data.data) { + PORT_Memset(symKey->data.data, 0, symKey->data.len); + PORT_Free(symKey->data.data); + } + PK11_USE_THREADS(PR_DestroyLock(symKey->refLock);) + PK11_FreeSlot(symKey->slot); + PORT_Free(symKey); + } +} + +PK11SymKey * +PK11_ReferenceSymKey(PK11SymKey *symKey) +{ + PK11_USE_THREADS(PR_Lock(symKey->refLock);) + symKey->refCount++; + PK11_USE_THREADS(PR_Unlock(symKey->refLock);) + return symKey; +} + +/* + * turn key handle into an appropriate key object + */ +PK11SymKey * +PK11_SymKeyFromHandle(PK11SlotInfo *slot, PK11SymKey *parent, PK11Origin origin, + CK_MECHANISM_TYPE type, CK_OBJECT_HANDLE keyID, PRBool owner, void *wincx) +{ + PK11SymKey *symKey; + + if (keyID == CK_INVALID_KEY) { + return NULL; + } + + symKey = PK11_CreateSymKey(slot,type,wincx); + if (symKey == NULL) { + return NULL; + } + + symKey->objectID = keyID; + symKey->origin = origin; + symKey->owner = owner; + + /* adopt the parent's session */ + /* This is only used by SSL. What we really want here is a session + * structure with a ref count so the session goes away only after all the + * keys do. */ + if (owner && parent) { + pk11_CloseSession(symKey->slot, symKey->session,symKey->sessionOwner); + symKey->sessionOwner = parent->sessionOwner; + symKey->session = parent->session; + parent->sessionOwner = PR_FALSE; + } + + return symKey; +} + +/* + * turn key handle into an appropriate key object + */ +PK11SymKey * +PK11_GetWrapKey(PK11SlotInfo *slot, int wrap, CK_MECHANISM_TYPE type, + int series, void *wincx) +{ + PK11SymKey *symKey = NULL; + + if (slot->series != series) return NULL; + if (slot->refKeys[wrap] == CK_INVALID_KEY) return NULL; + if (type == CKM_INVALID_MECHANISM) type = slot->wrapMechanism; + + symKey = PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive, + slot->wrapMechanism, slot->refKeys[wrap], PR_FALSE, wincx); + return symKey; +} + +void +PK11_SetWrapKey(PK11SlotInfo *slot, int wrap, PK11SymKey *wrapKey) +{ + /* save the handle and mechanism for the wrapping key */ + /* mark the key and session as not owned by us to they don't get freed + * when the key goes way... that lets us reuse the key later */ + slot->refKeys[wrap] = wrapKey->objectID; + wrapKey->owner = PR_FALSE; + wrapKey->sessionOwner = PR_FALSE; + slot->wrapMechanism = wrapKey->type; +} + +CK_MECHANISM_TYPE +PK11_GetMechanism(PK11SymKey *symKey) +{ + return symKey->type; +} + +/* + * figure out if a key is still valid or if it is stale. + */ +PRBool +PK11_VerifyKeyOK(PK11SymKey *key) { + if (!PK11_IsPresent(key->slot)) { + return PR_FALSE; + } + return (PRBool)(key->series == key->slot->series); +} + +static PK11SymKey * +pk11_ImportSymKeyWithTempl(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE *keyTemplate, + unsigned int templateCount, SECItem *key, void *wincx) +{ + PK11SymKey * symKey; + CK_RV crv; + + symKey = PK11_CreateSymKey(slot,type,wincx); + if (symKey == NULL) { + return NULL; + } + + symKey->size = key->len; + + if (SECITEM_CopyItem(NULL,&symKey->data,key) != SECSuccess) { + PK11_FreeSymKey(symKey); + return NULL; + } + + symKey->origin = origin; + + /* import the keys */ + crv = PK11_CreateNewObject(slot, keyTemplate, templateCount, PR_FALSE, + &symKey->objectID); + if ( crv != CKR_OK) { + PK11_FreeSymKey(symKey); + PORT_SetError( PK11_MapError(crv)); + return NULL; + } + + return symKey; +} + +/* + * turn key bits into an appropriate key object + */ +PK11SymKey * +PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,void *wincx) +{ + PK11SymKey * symKey; + unsigned int templateCount = 0; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BBOOL cktrue = CK_TRUE; /* sigh */ + CK_ATTRIBUTE keyTemplate[5]; + CK_ATTRIBUTE * attrs = keyTemplate; + + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++; + PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, key->data, key->len); attrs++; + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE)); + + keyType = PK11_GetKeyType(type,key->len); + symKey = pk11_ImportSymKeyWithTempl(slot, type, origin, keyTemplate, + templateCount, key, wincx); + return symKey; +} + +/* + * import a public key into the desired slot + */ +CK_OBJECT_HANDLE +PK11_ImportPublicKey(PK11SlotInfo *slot, SECKEYPublicKey *pubKey, + PRBool isToken) +{ + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_OBJECT_HANDLE objectID; + CK_ATTRIBUTE theTemplate[10]; + CK_ATTRIBUTE *signedattr = NULL; + CK_ATTRIBUTE *attrs = theTemplate; + int signedcount = 0; + int templateCount = 0; + CK_RV crv; + + /* if we already have an object in the desired slot, use it */ + if (!isToken && pubKey->pkcs11Slot == slot) { + return pubKey->pkcs11ID; + } + + /* free the existing key */ + if (pubKey->pkcs11Slot != NULL) { + PK11SlotInfo *oSlot = pubKey->pkcs11Slot; + PK11_EnterSlotMonitor(oSlot); + (void) PK11_GETTAB(oSlot)->C_DestroyObject(oSlot->session, + pubKey->pkcs11ID); + PK11_ExitSlotMonitor(oSlot); + PK11_FreeSlot(oSlot); + pubKey->pkcs11Slot = NULL; + } + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, isToken ? &cktrue : &ckfalse, + sizeof(CK_BBOOL) ); attrs++; + + /* now import the key */ + { + switch (pubKey->keyType) { + case rsaKey: + keyType = CKK_RSA; + PK11_SETATTRS(attrs, CKA_WRAP, &cktrue, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_ENCRYPT, &cktrue, + sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL)); attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_MODULUS, pubKey->u.rsa.modulus.data, + pubKey->u.rsa.modulus.len); attrs++; + PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, + pubKey->u.rsa.publicExponent.data, + pubKey->u.rsa.publicExponent.len); attrs++; + break; + case dsaKey: + keyType = CKK_DSA; + PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, pubKey->u.dsa.params.prime.data, + pubKey->u.dsa.params.prime.len); attrs++; + PK11_SETATTRS(attrs,CKA_SUBPRIME,pubKey->u.dsa.params.subPrime.data, + pubKey->u.dsa.params.subPrime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.dsa.params.base.data, + pubKey->u.dsa.params.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.dsa.publicValue.data, + pubKey->u.dsa.publicValue.len); attrs++; + break; + case fortezzaKey: + keyType = CKK_DSA; + PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME,pubKey->u.fortezza.params.prime.data, + pubKey->u.fortezza.params.prime.len); attrs++; + PK11_SETATTRS(attrs,CKA_SUBPRIME, + pubKey->u.fortezza.params.subPrime.data, + pubKey->u.fortezza.params.subPrime.len);attrs++; + PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.fortezza.params.base.data, + pubKey->u.fortezza.params.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.fortezza.DSSKey.data, + pubKey->u.fortezza.DSSKey.len); attrs++; + break; + case dhKey: + keyType = CKK_DH; + PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL));attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, pubKey->u.dh.prime.data, + pubKey->u.dh.prime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.dh.base.data, + pubKey->u.dh.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.dh.publicValue.data, + pubKey->u.dh.publicValue.len); attrs++; + break; + /* what about fortezza??? */ + default: + PORT_SetError( SEC_ERROR_BAD_KEY ); + return CK_INVALID_KEY; + } + + templateCount = attrs - theTemplate; + signedcount = attrs - signedattr; + PORT_Assert(templateCount <= (sizeof(theTemplate)/sizeof(CK_ATTRIBUTE))); + for (attrs=signedattr; signedcount; attrs++, signedcount--) { + pk11_SignedToUnsigned(attrs); + } + crv = PK11_CreateNewObject(slot, theTemplate, templateCount, isToken, + &objectID); + if ( crv != CKR_OK) { + PORT_SetError (PK11_MapError(crv)); + return CK_INVALID_KEY; + } + } + + pubKey->pkcs11ID = objectID; + pubKey->pkcs11Slot = PK11_ReferenceSlot(slot); + + return objectID; +} + + +/* + * return the slot associated with a symetric key + */ +PK11SlotInfo * +PK11_GetSlotFromKey(PK11SymKey *symKey) +{ + return PK11_ReferenceSlot(symKey->slot); +} + +PK11SymKey * +PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *keyID, + void *wincx) +{ + CK_ATTRIBUTE findTemp[4]; + CK_ATTRIBUTE *attrs; + CK_BBOOL ckTrue = CK_TRUE; + CK_OBJECT_CLASS keyclass = CKO_SECRET_KEY; + int tsize = 0; + CK_OBJECT_HANDLE key_id; + + attrs = findTemp; + PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass)); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue)); attrs++; + if (keyID) { + PK11_SETATTRS(attrs, CKA_ID, keyID->data, keyID->len); attrs++; + } + tsize = attrs - findTemp; + PORT_Assert(tsize <= sizeof(findTemp)/sizeof(CK_ATTRIBUTE)); + + key_id = pk11_FindObjectByTemplate(slot,findTemp,tsize); + if (key_id == CK_INVALID_KEY) { + return NULL; + } + return PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive, type, key_id, + PR_FALSE, wincx); +} + +void * +PK11_GetWindow(PK11SymKey *key) +{ + return key->cx; +} + + +/* + * extract a symetric key value. NOTE: if the key is sensitive, we will + * not be able to do this operation. This function is used to move + * keys from one token to another */ +SECStatus +PK11_ExtractKeyValue(PK11SymKey *symKey) +{ + + if (symKey->data.data != NULL) return SECSuccess; + + if (symKey->slot == NULL) { + PORT_SetError( SEC_ERROR_INVALID_KEY ); + return SECFailure; + } + + return PK11_ReadAttribute(symKey->slot,symKey->objectID,CKA_VALUE,NULL, + &symKey->data); +} + +SECItem * +PK11_GetKeyData(PK11SymKey *symKey) +{ + return &symKey->data; +} + +/* + * take an attribute and copy it into a secitem, converting unsigned to signed. + */ +static CK_RV +pk11_Attr2SecItem(PRArenaPool *arena, CK_ATTRIBUTE *attr, SECItem *item) { + unsigned char *dataPtr; + + item->len = attr->ulValueLen; + dataPtr = (unsigned char*) PORT_ArenaAlloc(arena, item->len+1); + if ( dataPtr == NULL) { + return CKR_HOST_MEMORY; + } + *dataPtr = 0; + item->data = dataPtr+1; + PORT_Memcpy(item->data,attr->pValue,item->len); + if (item->data[0] & 0x80) { + item->data = item->data-1; + item->len++; + } + return CKR_OK; +} +/* + * extract a public key from a slot and id + */ +SECKEYPublicKey * +PK11_ExtractPublicKey(PK11SlotInfo *slot,KeyType keyType,CK_OBJECT_HANDLE id) +{ + CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; + PRArenaPool *arena; + PRArenaPool *tmp_arena; + SECKEYPublicKey *pubKey; + int templateCount = 0; + CK_KEY_TYPE pk11KeyType; + CK_RV crv; + CK_ATTRIBUTE template[8]; + CK_ATTRIBUTE *attrs= template; + CK_ATTRIBUTE *modulus,*exponent,*base,*prime,*subprime,*value; + + /* if we didn't know the key type, get it */ + if (keyType== nullKey) { + + pk11KeyType = PK11_ReadULongAttribute(slot,id,CKA_KEY_TYPE); + if (pk11KeyType == CK_UNAVAILABLE_INFORMATION) { + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + switch (pk11KeyType) { + case CKK_RSA: + keyType = rsaKey; + break; + case CKK_DSA: + keyType = dsaKey; + break; + case CKK_DH: + keyType = dhKey; + break; + default: + PORT_SetError( SEC_ERROR_BAD_KEY ); + return NULL; + } + } + + + /* now we need to create space for the public key */ + arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) return NULL; + tmp_arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (tmp_arena == NULL) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + + + pubKey = (SECKEYPublicKey *) + PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); + if (pubKey == NULL) { + PORT_FreeArena (arena, PR_FALSE); + PORT_FreeArena (tmp_arena, PR_FALSE); + return NULL; + } + + pubKey->arena = arena; + pubKey->keyType = keyType; + pubKey->pkcs11Slot = PK11_ReferenceSlot(slot); + pubKey->pkcs11ID = id; + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, + sizeof(keyClass)); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &pk11KeyType, + sizeof(pk11KeyType) ); attrs++; + switch (pubKey->keyType) { + case rsaKey: + modulus = attrs; + PK11_SETATTRS(attrs, CKA_MODULUS, NULL, 0); attrs++; + exponent = attrs; + PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, NULL, 0); attrs++; + + templateCount = attrs - template; + PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE)); + crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount); + if (crv != CKR_OK) break; + + if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_RSA)) { + crv = CKR_OBJECT_HANDLE_INVALID; + break; + } + crv = pk11_Attr2SecItem(arena,modulus,&pubKey->u.rsa.modulus); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,exponent,&pubKey->u.rsa.publicExponent); + if (crv != CKR_OK) break; + break; + case dsaKey: + prime = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, NULL, 0); attrs++; + subprime = attrs; + PK11_SETATTRS(attrs, CKA_SUBPRIME, NULL, 0); attrs++; + base = attrs; + PK11_SETATTRS(attrs, CKA_BASE, NULL, 0); attrs++; + value = attrs; + PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0); attrs++; + templateCount = attrs - template; + PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE)); + crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount); + if (crv != CKR_OK) break; + + if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_DSA)) { + crv = CKR_OBJECT_HANDLE_INVALID; + break; + } + crv = pk11_Attr2SecItem(arena,prime,&pubKey->u.dsa.params.prime); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,subprime,&pubKey->u.dsa.params.subPrime); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,base,&pubKey->u.dsa.params.base); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,value,&pubKey->u.dsa.publicValue); + if (crv != CKR_OK) break; + break; + case dhKey: + prime = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, NULL, 0); attrs++; + base = attrs; + PK11_SETATTRS(attrs, CKA_BASE, NULL, 0); attrs++; + value =attrs; + PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0); attrs++; + templateCount = attrs - template; + PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE)); + crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount); + if (crv != CKR_OK) break; + + if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_DSA)) { + crv = CKR_OBJECT_HANDLE_INVALID; + break; + } + crv = pk11_Attr2SecItem(arena,prime,&pubKey->u.dh.prime); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,base,&pubKey->u.dh.base); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,value,&pubKey->u.dh.publicValue); + if (crv != CKR_OK) break; + break; + case fortezzaKey: + case nullKey: + default: + crv = CKR_OBJECT_HANDLE_INVALID; + break; + } + + PORT_FreeArena(tmp_arena,PR_FALSE); + + if (crv != CKR_OK) { + PORT_FreeArena(arena,PR_FALSE); + PK11_FreeSlot(slot); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + + return pubKey; +} + +/* + * Build a Private Key structure from raw PKCS #11 information. + */ +SECKEYPrivateKey * +PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType, + PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx) +{ + PRArenaPool *arena; + SECKEYPrivateKey *privKey; + + /* don't know? look it up */ + if (keyType == nullKey) { + CK_KEY_TYPE pk11Type = CKK_RSA; + + pk11Type = PK11_ReadULongAttribute(slot,privID,CKA_KEY_TYPE); + isTemp = (PRBool)!PK11_HasAttributeSet(slot,privID,CKA_TOKEN); + switch (pk11Type) { + case CKK_RSA: keyType = rsaKey; break; + case CKK_DSA: keyType = dsaKey; break; + case CKK_DH: keyType = dhKey; break; + case CKK_KEA: keyType = fortezzaKey; break; + default: + break; + } + } + + /* now we need to create space for the private key */ + arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) return NULL; + + privKey = (SECKEYPrivateKey *) + PORT_ArenaZAlloc(arena, sizeof(SECKEYPrivateKey)); + if (privKey == NULL) { + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + + privKey->arena = arena; + privKey->keyType = keyType; + privKey->pkcs11Slot = PK11_ReferenceSlot(slot); + privKey->pkcs11ID = privID; + privKey->pkcs11IsTemp = isTemp; + privKey->wincx = wincx; + + return privKey; +} + +/* return the keylength if possible. '0' if not */ +unsigned int +PK11_GetKeyLength(PK11SymKey *key) +{ + if (key->size != 0) return key->size ; + if (key->data.data == NULL) { + PK11_ExtractKeyValue(key); + } + /* key is probably secret. Look up it's type and length */ + /* this is new PKCS #11 version 2.0 functionality. */ + if (key->size == 0) { + CK_ULONG keyLength; + + keyLength = PK11_ReadULongAttribute(key->slot,key->objectID,CKA_VALUE_LEN); + /* doesn't have a length field, check the known PKCS #11 key types, + * which don't have this field */ + if (keyLength == CK_UNAVAILABLE_INFORMATION) { + CK_KEY_TYPE keyType; + keyType = PK11_ReadULongAttribute(key->slot,key->objectID,CKA_KEY_TYPE); + switch (keyType) { + case CKK_DES: key->size = 8; break; + case CKK_DES2: key->size = 16; break; + case CKK_DES3: key->size = 24; break; + case CKK_SKIPJACK: key->size = 10; break; + case CKK_BATON: key->size = 20; break; + case CKK_JUNIPER: key->size = 20; break; + case CKK_GENERIC_SECRET: + if (key->type == CKM_SSL3_PRE_MASTER_KEY_GEN) { + key->size=48; + } + break; + default: break; + } + } else { + key->size = (unsigned int)keyLength; + } + } + + return key->size; +} + +/* return the strength of a key. This is different from length in that + * 1) it returns the size in bits, and 2) it returns only the secret portions + * of the key minus any checksums or parity. + */ +unsigned int +PK11_GetKeyStrength(PK11SymKey *key, SECAlgorithmID *algid) +{ + int size=0; + CK_MECHANISM_TYPE mechanism= CKM_INVALID_MECHANISM; /* RC2 only */ + SECItem *param = NULL; /* RC2 only */ + CK_RC2_CBC_PARAMS *rc2_params = NULL; /* RC2 ONLY */ + unsigned int effectiveBits = 0; /* RC2 ONLY */ + + switch (PK11_GetKeyType(key->type,0)) { + case CKK_CDMF: + return 40; + case CKK_DES: + return 56; + case CKK_DES3: + case CKK_DES2: + size = PK11_GetKeyLength(key); + if (size == 16) { + /* double des */ + return 112; /* 16*7 */ + } + return 168; + /* + * RC2 has is different than other ciphers in that it allows the user + * to deprecating keysize while still requiring all the bits for the + * original key. The info + * on what the effective key strength is in the parameter for the key. + * In S/MIME this parameter is stored in the DER encoded algid. In Our + * other uses of RC2, effectiveBits == keyBits, so this code functions + * correctly without an algid. + */ + case CKK_RC2: + /* if no algid was provided, fall through to default */ + if (!algid) { + break; + } + /* verify that the algid is for RC2 */ + mechanism = PK11_AlgtagToMechanism(SECOID_GetAlgorithmTag(algid)); + if ((mechanism != CKM_RC2_CBC) && (mechanism != CKM_RC2_ECB)) { + break; + } + + /* now get effective bits from the algorithm ID. */ + param = PK11_ParamFromAlgid(algid); + /* if we couldn't get memory just use key length */ + if (param == NULL) { + break; + } + + rc2_params = (CK_RC2_CBC_PARAMS *) param->data; + /* paranoia... shouldn't happen */ + PORT_Assert(param->data != NULL); + if (param->data == NULL) { + SECITEM_FreeItem(param,PR_TRUE); + break; + } + effectiveBits = (unsigned int)rc2_params->ulEffectiveBits; + SECITEM_FreeItem(param,PR_TRUE); + param = NULL; rc2_params=NULL; /* paranoia */ + + /* we have effective bits, is and allocated memory is free, now + * we need to return the smaller of effective bits and keysize */ + size = PK11_GetKeyLength(key); + if ((unsigned int)size*8 > effectiveBits) { + return effectiveBits; + } + + return size*8; /* the actual key is smaller, the strength can't be + * greater than the actual key size */ + + default: + break; + } + return PK11_GetKeyLength(key) * 8; +} + + +/* + * get the length of a signature object based on the key + */ +int +PK11_SignatureLen(SECKEYPrivateKey *key) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + int val; + + switch (key->keyType) { + case rsaKey: + val = PK11_GetPrivateModulusLen(key); + if (val == -1) { + break; /* failed */ + } + return (unsigned long) val; + + case fortezzaKey: + case dsaKey: + return 40; + + default: + break; + } + PORT_SetError( SEC_ERROR_INVALID_KEY ); + return 0; +} + +PK11SlotInfo * +PK11_GetSlotFromPrivateKey(SECKEYPrivateKey *key) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + slot = PK11_ReferenceSlot(slot); + return slot; +} + +/* + * Get the modulus length for raw parsing + */ +int +PK11_GetPrivateModulusLen(SECKEYPrivateKey *key) +{ + CK_ATTRIBUTE theTemplate = { CKA_MODULUS, NULL, 0 }; + PK11SlotInfo *slot = key->pkcs11Slot; + CK_RV crv; + int length; + + switch (key->keyType) { + case rsaKey: + crv = PK11_GetAttributes(NULL, slot, key->pkcs11ID, &theTemplate, 1); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return -1; + } + length = theTemplate.ulValueLen; + if ( *(unsigned char *)theTemplate.pValue == 0) { + length--; + } + if (theTemplate.pValue != NULL) + PORT_Free(theTemplate.pValue); + return (int) length; + + case fortezzaKey: + case dsaKey: + case dhKey: + default: + break; + } + if (theTemplate.pValue != NULL) + PORT_Free(theTemplate.pValue); + PORT_SetError( SEC_ERROR_INVALID_KEY ); + return -1; +} + + +/* Make a Key type to an appropriate signing/verification mechanism */ +static CK_MECHANISM_TYPE +pk11_mapSignKeyType(KeyType keyType) +{ + switch (keyType) { + case rsaKey: + return CKM_RSA_PKCS; + case fortezzaKey: + case dsaKey: + return CKM_DSA; + case dhKey: + default: + break; + } + return CKM_INVALID_MECHANISM; +} + +static CK_MECHANISM_TYPE +pk11_mapWrapKeyType(KeyType keyType) +{ + switch (keyType) { + case rsaKey: + return CKM_RSA_PKCS; + /* Add fortezza?? */ + default: + break; + } + return CKM_INVALID_MECHANISM; +} + +/* + * copy a key (or any other object) on a token + */ +CK_OBJECT_HANDLE +PK11_CopyKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE srcObject) +{ + CK_OBJECT_HANDLE destObject; + CK_RV crv; + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_CopyObject(slot->session,srcObject,NULL,0, + &destObject); + PK11_ExitSlotMonitor(slot); + if (crv == CKR_OK) return destObject; + PORT_SetError( PK11_MapError(crv) ); + return CK_INVALID_KEY; +} + + +PK11SymKey * +pk11_KeyExchange(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, + CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey); + +/* + * The next two utilities are to deal with the fact that a given operation + * may be a multi-slot affair. This creates a new key object that is copied + * into the new slot. + */ +PK11SymKey * +pk11_CopyToSlot(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, + CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey) +{ + SECStatus rv; + PK11SymKey *newKey = NULL; + + /* Extract the raw key data if possible */ + if (symKey->data.data == NULL) { + rv = PK11_ExtractKeyValue(symKey); + /* KEY is sensitive, we're try key exchanging it. */ + if (rv != SECSuccess) { + return pk11_KeyExchange(slot, type, operation, symKey); + } + } + newKey = PK11_ImportSymKey(slot, type, symKey->origin, operation, + &symKey->data, symKey->cx); + if (newKey == NULL) newKey = pk11_KeyExchange(slot,type,operation,symKey); + return newKey; +} + +/* + * Make sure the slot we are in the correct slot for the operation + */ +static PK11SymKey * +pk11_ForceSlot(PK11SymKey *symKey,CK_MECHANISM_TYPE type, + CK_ATTRIBUTE_TYPE operation) +{ + PK11SlotInfo *slot = symKey->slot; + PK11SymKey *newKey = NULL; + + if ((slot== NULL) || !PK11_DoesMechanism(slot,type)) { + slot = PK11_GetBestSlot(type,symKey->cx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + newKey = pk11_CopyToSlot(slot, type, operation, symKey); + PK11_FreeSlot(slot); + } + return newKey; +} + +/* + * Use the token to Generate a key. keySize must be 'zero' for fixed key + * length algorithms. NOTE: this means we can never generate a DES2 key + * from this interface! + */ +PK11SymKey * +PK11_KeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *param, + int keySize, void *wincx) +{ + CK_ULONG key_size = 0; + /* we have to use these native types because when we call PKCS 11 modules + * we have to make sure that we are using the correct sizes for all the + * parameters. */ + CK_BBOOL ckfalse = CK_FALSE; + CK_BBOOL cktrue = CK_TRUE; + CK_ATTRIBUTE genTemplate[2]; + int count = sizeof(genTemplate)/sizeof(genTemplate[0]); + CK_MECHANISM mechanism; + CK_MECHANISM_TYPE key_gen_mechanism; + PK11SymKey *symKey; + CK_RV crv; + CK_ATTRIBUTE *attrs = genTemplate; + PRBool weird = PR_FALSE; /* hack for fortezza */ + + if ((keySize == -1) && (type == CKM_SKIPJACK_CBC64)) { + weird = PR_TRUE; + keySize = 0; + } + + PK11_SETATTRS(attrs, (!weird) + ? CKA_ENCRYPT : CKA_DECRYPT, &cktrue, sizeof(CK_BBOOL)); attrs++; + key_size = keySize; + if (key_size != 0) { + PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size)); + attrs++; + } + count = attrs - genTemplate; + PR_ASSERT(count <= sizeof(genTemplate)/sizeof(CK_ATTRIBUTE)); + + /* find a slot to generate the key into */ + if ((slot == NULL) || (!PK11_DoesMechanism(slot,type))) { + slot = PK11_GetBestSlot(type,wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + } else { + PK11_ReferenceSlot(slot); + } + + /* Initialize the Key Gen Mechanism */ + key_gen_mechanism = PK11_GetKeyGen(type); + if (key_gen_mechanism == CKM_FAKE_RANDOM) { + PK11_FreeSlot(slot); + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + mechanism.mechanism = key_gen_mechanism; + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + if (param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } + + /* get our key Structure */ + symKey = PK11_CreateSymKey(slot,type,wincx); + PK11_FreeSlot(slot); + if (symKey == NULL) { + return NULL; + } + symKey->size = keySize; + symKey->origin = (!weird) ? PK11_OriginGenerated : PK11_OriginFortezzaHack; + + pk11_EnterKeyMonitor(symKey); + crv = PK11_GETTAB(symKey->slot)->C_GenerateKey(symKey->session, + &mechanism, genTemplate, count, &symKey->objectID); + pk11_ExitKeyMonitor(symKey); + if (crv != CKR_OK) { + PK11_FreeSymKey(symKey); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + return symKey; +} + +/* + * PKCS #11 pairwise consistency check utilized to validate key pair. + */ +static SECStatus +pk11_PairwiseConsistencyCheck(SECKEYPublicKey *pubKey, + SECKEYPrivateKey *privKey, CK_MECHANISM *mech, void* wincx ) +{ + /* Variables used for Encrypt/Decrypt functions. */ + unsigned char *known_message = (unsigned char *)"Known Crypto Message"; + CK_BBOOL isEncryptable = CK_FALSE; + CK_BBOOL canSignVerify = CK_FALSE; + CK_BBOOL isDerivable = CK_FALSE; + unsigned char plaintext[PAIRWISE_MESSAGE_LENGTH]; + CK_ULONG bytes_decrypted; + PK11SlotInfo *slot; + CK_OBJECT_HANDLE id; + unsigned char *ciphertext; + unsigned char *text_compared; + CK_ULONG max_bytes_encrypted; + CK_ULONG bytes_encrypted; + CK_ULONG bytes_compared; + CK_RV crv; + + /* Variables used for Signature/Verification functions. */ + unsigned char *known_digest = (unsigned char *)"Mozilla Rules World!"; + SECItem signature; + SECItem digest; /* always uses SHA-1 digest */ + int signature_length; + SECStatus rv; + + /**************************************************/ + /* Pairwise Consistency Check of Encrypt/Decrypt. */ + /**************************************************/ + + isEncryptable = PK11_HasAttributeSet( privKey->pkcs11Slot, + privKey->pkcs11ID, CKA_DECRYPT ); + + /* If the encryption attribute is set; attempt to encrypt */ + /* with the public key and decrypt with the private key. */ + if( isEncryptable ) { + /* Find a module to encrypt against */ + slot = PK11_GetBestSlot(pk11_mapWrapKeyType(privKey->keyType),wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + + id = PK11_ImportPublicKey(slot,pubKey,PR_FALSE); + if (id == CK_INVALID_KEY) { + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Compute max bytes encrypted from modulus length of private key. */ + max_bytes_encrypted = PK11_GetPrivateModulusLen( privKey ); + + + /* Prepare for encryption using the public key. */ + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB( slot )->C_EncryptInit( slot->session, + mech, id ); + if( crv != CKR_OK ) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError( crv ) ); + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Allocate space for ciphertext. */ + ciphertext = (unsigned char *) PORT_Alloc( max_bytes_encrypted ); + if( ciphertext == NULL ) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( SEC_ERROR_NO_MEMORY ); + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Initialize bytes encrypted to max bytes encrypted. */ + bytes_encrypted = max_bytes_encrypted; + + /* Encrypt using the public key. */ + crv = PK11_GETTAB( slot )->C_Encrypt( slot->session, + known_message, + PAIRWISE_MESSAGE_LENGTH, + ciphertext, + &bytes_encrypted ); + PK11_ExitSlotMonitor(slot); + PK11_FreeSlot(slot); + if( crv != CKR_OK ) { + PORT_SetError( PK11_MapError( crv ) ); + PORT_Free( ciphertext ); + return SECFailure; + } + + /* Always use the smaller of these two values . . . */ + bytes_compared = ( bytes_encrypted > PAIRWISE_MESSAGE_LENGTH ) + ? PAIRWISE_MESSAGE_LENGTH + : bytes_encrypted; + + /* If there was a failure, the plaintext */ + /* goes at the end, therefore . . . */ + text_compared = ( bytes_encrypted > PAIRWISE_MESSAGE_LENGTH ) + ? (ciphertext + bytes_encrypted - + PAIRWISE_MESSAGE_LENGTH ) + : ciphertext; + + /* Check to ensure that ciphertext does */ + /* NOT EQUAL known input message text */ + /* per FIPS PUB 140-1 directive. */ + if( ( bytes_encrypted != max_bytes_encrypted ) || + ( PORT_Memcmp( text_compared, known_message, + bytes_compared ) == 0 ) ) { + /* Set error to Invalid PRIVATE Key. */ + PORT_SetError( SEC_ERROR_INVALID_KEY ); + PORT_Free( ciphertext ); + return SECFailure; + } + + slot = privKey->pkcs11Slot; + /* Prepare for decryption using the private key. */ + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB( slot )->C_DecryptInit( slot->session, + mech, + privKey->pkcs11ID ); + if( crv != CKR_OK ) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + PORT_Free( ciphertext ); + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Initialize bytes decrypted to be the */ + /* expected PAIRWISE_MESSAGE_LENGTH. */ + bytes_decrypted = PAIRWISE_MESSAGE_LENGTH; + + /* Decrypt using the private key. */ + /* NOTE: No need to reset the */ + /* value of bytes_encrypted. */ + crv = PK11_GETTAB( slot )->C_Decrypt( slot->session, + ciphertext, + bytes_encrypted, + plaintext, + &bytes_decrypted ); + PK11_ExitSlotMonitor(slot); + + /* Finished with ciphertext; free it. */ + PORT_Free( ciphertext ); + + if( crv != CKR_OK ) { + PORT_SetError( PK11_MapError(crv) ); + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Check to ensure that the output plaintext */ + /* does EQUAL known input message text. */ + if( ( bytes_decrypted != PAIRWISE_MESSAGE_LENGTH ) || + ( PORT_Memcmp( plaintext, known_message, + PAIRWISE_MESSAGE_LENGTH ) != 0 ) ) { + /* Set error to Bad PUBLIC Key. */ + PORT_SetError( SEC_ERROR_BAD_KEY ); + PK11_FreeSlot(slot); + return SECFailure; + } + } + + /**********************************************/ + /* Pairwise Consistency Check of Sign/Verify. */ + /**********************************************/ + + canSignVerify = PK11_HasAttributeSet ( privKey->pkcs11Slot, + privKey->pkcs11ID, CKA_VERIFY); + + if (canSignVerify) + { + /* Initialize signature and digest data. */ + signature.data = NULL; + digest.data = NULL; + + /* Determine length of signature. */ + signature_length = PK11_SignatureLen( privKey ); + if( signature_length == 0 ) + goto failure; + + /* Allocate space for signature data. */ + signature.data = (unsigned char *) PORT_Alloc( signature_length ); + if( signature.data == NULL ) { + PORT_SetError( SEC_ERROR_NO_MEMORY ); + goto failure; + } + + /* Allocate space for known digest data. */ + digest.data = (unsigned char *) PORT_Alloc( PAIRWISE_DIGEST_LENGTH ); + if( digest.data == NULL ) { + PORT_SetError( SEC_ERROR_NO_MEMORY ); + goto failure; + } + + /* "Fill" signature type and length. */ + signature.type = PAIRWISE_SECITEM_TYPE; + signature.len = signature_length; + + /* "Fill" digest with known SHA-1 digest parameters. */ + digest.type = PAIRWISE_SECITEM_TYPE; + PORT_Memcpy( digest.data, known_digest, PAIRWISE_DIGEST_LENGTH ); + digest.len = PAIRWISE_DIGEST_LENGTH; + + /* Sign the known hash using the private key. */ + rv = PK11_Sign( privKey, &signature, &digest ); + if( rv != SECSuccess ) + goto failure; + + /* Verify the known hash using the public key. */ + rv = PK11_Verify( pubKey, &signature, &digest, wincx ); + if( rv != SECSuccess ) + goto failure; + + /* Free signature and digest data. */ + PORT_Free( signature.data ); + PORT_Free( digest.data ); + } + + + + /**********************************************/ + /* Pairwise Consistency Check for Derivation */ + /**********************************************/ + + isDerivable = PK11_HasAttributeSet ( privKey->pkcs11Slot, + privKey->pkcs11ID, CKA_DERIVE); + + if (isDerivable) + { + /* + * We are not doing consistency check for Diffie-Hellman Key - + * otherwise it would be here + */ + + } + + return SECSuccess; + +failure: + if( signature.data != NULL ) + PORT_Free( signature.data ); + if( digest.data != NULL ) + PORT_Free( digest.data ); + + return SECFailure; +} + + + +/* + * take a private key in one pkcs11 module and load it into another: + * NOTE: the source private key is a rare animal... it can't be sensitive. + * This is used to do a key gen using one pkcs11 module and storing the + * result into another. + */ +SECKEYPrivateKey * +pk11_loadPrivKey(PK11SlotInfo *slot,SECKEYPrivateKey *privKey, + SECKEYPublicKey *pubKey, PRBool token, PRBool sensitive) +{ + CK_ATTRIBUTE privTemplate[] = { + /* class must be first */ + { CKA_CLASS, NULL, 0 }, + { CKA_KEY_TYPE, NULL, 0 }, + /* these three must be next */ + { CKA_TOKEN, NULL, 0 }, + { CKA_PRIVATE, NULL, 0 }, + { CKA_SENSITIVE, NULL, 0 }, + { CKA_ID, NULL, 0 }, +#ifdef notdef + { CKA_LABEL, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, +#endif + /* RSA */ + { CKA_MODULUS, NULL, 0 }, + { CKA_PRIVATE_EXPONENT, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 }, + { CKA_PRIME_1, NULL, 0 }, + { CKA_PRIME_2, NULL, 0 }, + { CKA_EXPONENT_1, NULL, 0 }, + { CKA_EXPONENT_2, NULL, 0 }, + { CKA_COEFFICIENT, NULL, 0 }, + }; + CK_ATTRIBUTE *attrs = NULL, *ap; + int templateSize = sizeof(privTemplate)/sizeof(privTemplate[0]); + PRArenaPool *arena; + CK_OBJECT_HANDLE objectID; + int i, count = 0; + int extra_count = 0; + CK_RV crv; + SECStatus rv; + + for (i=0; i < templateSize; i++) { + if (privTemplate[i].type == CKA_MODULUS) { + attrs= &privTemplate[i]; + count = i; + break; + } + } + PORT_Assert(attrs != NULL); + if (attrs == NULL) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + + ap = attrs; + + switch (privKey->keyType) { + case rsaKey: + count = templateSize; + extra_count = templateSize - (attrs - privTemplate); + break; + case dsaKey: + ap->type = CKA_PRIME; ap++; count++; extra_count++; + ap->type = CKA_SUBPRIME; ap++; count++; extra_count++; + ap->type = CKA_BASE; ap++; count++; extra_count++; + ap->type = CKA_VALUE; ap++; count++; extra_count++; + break; + case dhKey: + ap->type = CKA_PRIME; ap++; count++; extra_count++; + ap->type = CKA_BASE; ap++; count++; extra_count++; + ap->type = CKA_VALUE; ap++; count++; extra_count++; + break; + default: + count = 0; + extra_count = 0; + break; + } + + if (count == 0) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + + arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) return NULL; + /* + * read out the old attributes. + */ + crv = PK11_GetAttributes(arena, privKey->pkcs11Slot, privKey->pkcs11ID, + privTemplate,count); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + PORT_FreeArena(arena, PR_TRUE); + return NULL; + } + + /* Reset sensitive, token, and private */ + *(CK_BBOOL *)(privTemplate[2].pValue) = token ? CK_TRUE : CK_FALSE; + *(CK_BBOOL *)(privTemplate[3].pValue) = token ? CK_TRUE : CK_FALSE; + *(CK_BBOOL *)(privTemplate[4].pValue) = sensitive ? CK_TRUE : CK_FALSE; + + /* Not everyone can handle zero padded key values, give + * them the raw data as unsigned */ + for (ap=attrs; extra_count; ap++, extra_count--) { + pk11_SignedToUnsigned(ap); + } + + /* now Store the puppies */ + rv = PK11_CreateNewObject(slot,privTemplate, count, token, &objectID); + PORT_FreeArena(arena, PR_TRUE); + if (rv != SECSuccess) { + return NULL; + } + + /* try loading the public key as a token object */ + if (pubKey) { + PK11_ImportPublicKey(slot, pubKey, PR_TRUE); + if (pubKey->pkcs11Slot) { + PK11_FreeSlot(pubKey->pkcs11Slot); + pubKey->pkcs11Slot = NULL; + pubKey->pkcs11ID = CK_INVALID_KEY; + } + } + + /* build new key structure */ + return PK11_MakePrivKey(slot, privKey->keyType, (PRBool)!token, + objectID, privKey->wincx); +} + + +/* + * Use the token to Generate a key. keySize must be 'zero' for fixed key + * length algorithms. NOTE: this means we can never generate a DES2 key + * from this interface! + */ +SECKEYPrivateKey * +PK11_GenerateKeyPair(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, + void *param, SECKEYPublicKey **pubKey, PRBool token, + PRBool sensitive, void *wincx) +{ + /* we have to use these native types because when we call PKCS 11 modules + * we have to make sure that we are using the correct sizes for all the + * parameters. */ + CK_BBOOL ckfalse = CK_FALSE; + CK_BBOOL cktrue = CK_TRUE; + CK_ULONG modulusBits; + CK_BYTE publicExponent[4]; + CK_ATTRIBUTE privTemplate[] = { + { CKA_SENSITIVE, NULL, 0}, + { CKA_TOKEN, NULL, 0}, + { CKA_PRIVATE, NULL, 0}, + { CKA_DERIVE, NULL, 0}, + { CKA_UNWRAP, NULL, 0}, + { CKA_SIGN, NULL, 0}, + { CKA_DECRYPT, NULL, 0}, + }; + CK_ATTRIBUTE rsaPubTemplate[] = { + { CKA_MODULUS_BITS, NULL, 0}, + { CKA_PUBLIC_EXPONENT, NULL, 0}, + { CKA_TOKEN, NULL, 0}, + { CKA_DERIVE, NULL, 0}, + { CKA_WRAP, NULL, 0}, + { CKA_VERIFY, NULL, 0}, + { CKA_VERIFY_RECOVER, NULL, 0}, + { CKA_ENCRYPT, NULL, 0}, + }; + CK_ATTRIBUTE dsaPubTemplate[] = { + { CKA_PRIME, NULL, 0 }, + { CKA_SUBPRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_TOKEN, NULL, 0}, + { CKA_DERIVE, NULL, 0}, + { CKA_WRAP, NULL, 0}, + { CKA_VERIFY, NULL, 0}, + { CKA_VERIFY_RECOVER, NULL, 0}, + { CKA_ENCRYPT, NULL, 0}, + }; + CK_ATTRIBUTE dhPubTemplate[] = { + { CKA_PRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_TOKEN, NULL, 0}, + { CKA_DERIVE, NULL, 0}, + { CKA_WRAP, NULL, 0}, + { CKA_VERIFY, NULL, 0}, + { CKA_VERIFY_RECOVER, NULL, 0}, + { CKA_ENCRYPT, NULL, 0}, + }; + + int dsaPubCount = sizeof(dsaPubTemplate)/sizeof(dsaPubTemplate[0]); + /*CK_ULONG key_size = 0;*/ + CK_ATTRIBUTE *pubTemplate; + int privCount = sizeof(privTemplate)/sizeof(privTemplate[0]); + int rsaPubCount = sizeof(rsaPubTemplate)/sizeof(rsaPubTemplate[0]); + int dhPubCount = sizeof(dhPubTemplate)/sizeof(dhPubTemplate[0]); + int pubCount = 0; + PK11RSAGenParams *rsaParams; + PQGParams *dsaParams; + DHParams * dhParams; + CK_MECHANISM mechanism; + CK_MECHANISM test_mech; + CK_SESSION_HANDLE session_handle; + CK_RV crv; + CK_OBJECT_HANDLE privID,pubID; + SECKEYPrivateKey *privKey; + KeyType keyType; + PRBool restore; + int peCount,i; + CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE *privattrs; + SECItem *pubKeyIndex; + CK_ATTRIBUTE setTemplate; + SECStatus rv; + CK_MECHANISM_INFO mechanism_info; + CK_OBJECT_CLASS keyClass; + SECItem *cka_id; + PRBool haslock = PR_FALSE; + PRBool pubIsToken = PR_FALSE; + + PORT_Assert(slot != NULL); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE); + return NULL; + } + + /* if our slot really doesn't do this mechanism, Generate the key + * in our internal token and write it out */ + if (!PK11_DoesMechanism(slot,type)) { + PK11SlotInfo *int_slot = PK11_GetInternalSlot(); + + /* don't loop forever looking for a slot */ + if (slot == int_slot) { + PK11_FreeSlot(int_slot); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + + /* if there isn't a suitable slot, then we can't do the keygen */ + if (int_slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + + /* generate the temporary key to load */ + privKey = PK11_GenerateKeyPair(int_slot,type, param, pubKey, PR_FALSE, + PR_FALSE, wincx); + PK11_FreeSlot(int_slot); + + /* if successful, load the temp key into the new token */ + if (privKey != NULL) { + SECKEYPrivateKey *newPrivKey = pk11_loadPrivKey(slot,privKey, + *pubKey,token,sensitive); + SECKEY_DestroyPrivateKey(privKey); + if (newPrivKey == NULL) { + SECKEY_DestroyPublicKey(*pubKey); + *pubKey = NULL; + } + return newPrivKey; + } + return NULL; + } + + + mechanism.mechanism = type; + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + test_mech.pParameter = NULL; + test_mech.ulParameterLen = 0; + + /* set up the private key template */ + privattrs = privTemplate; + PK11_SETATTRS(privattrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_TOKEN, token ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_PRIVATE, sensitive ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + + /* set up the mechanism specific info */ + switch (type) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + rsaParams = (PK11RSAGenParams *)param; + modulusBits = rsaParams->keySizeInBits; + peCount = 0; + + /* convert pe to a PKCS #11 string */ + for (i=0; i < 4; i++) { + if (peCount || (rsaParams->pe & + ((unsigned long)0xff000000L >> (i*8)))) { + publicExponent[peCount] = + (CK_BYTE)((rsaParams->pe >> (3-i)*8) & 0xff); + peCount++; + } + } + PORT_Assert(peCount != 0); + attrs = rsaPubTemplate; + PK11_SETATTRS(attrs, CKA_MODULUS_BITS, + &modulusBits, sizeof(modulusBits)); attrs++; + PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, + publicExponent, peCount);attrs++; + pubTemplate = rsaPubTemplate; + pubCount = rsaPubCount; + keyType = rsaKey; + test_mech.mechanism = CKM_RSA_PKCS; + break; + case CKM_DSA_KEY_PAIR_GEN: + dsaParams = (PQGParams *)param; + attrs = dsaPubTemplate; + PK11_SETATTRS(attrs, CKA_PRIME, dsaParams->prime.data, + dsaParams->prime.len); attrs++; + PK11_SETATTRS(attrs, CKA_SUBPRIME, dsaParams->subPrime.data, + dsaParams->subPrime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, dsaParams->base.data, + dsaParams->base.len); attrs++; + pubTemplate = dsaPubTemplate; + pubCount = dsaPubCount; + keyType = dsaKey; + test_mech.mechanism = CKM_DSA; + break; + case CKM_DH_PKCS_KEY_PAIR_GEN: + dhParams = (DHParams *)param; + attrs = dhPubTemplate; + PK11_SETATTRS(attrs, CKA_PRIME, dhParams->prime.data, + dhParams->prime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, dhParams->base.data, + dhParams->base.len); attrs++; + pubTemplate = dhPubTemplate; + pubCount = dhPubCount; + keyType = dhKey; + test_mech.mechanism = CKM_DH_PKCS_DERIVE; + break; + default: + PORT_SetError( SEC_ERROR_BAD_KEY ); + return NULL; + } + + /* now query the slot to find out how "good" a key we can generate */ + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID, + test_mech.mechanism,&mechanism_info); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if ((crv != CKR_OK) || (mechanism_info.flags == 0)) { + /* must be old module... guess what it should be... */ + switch (test_mech.mechanism) { + case CKM_RSA_PKCS: + mechanism_info.flags = (CKF_SIGN | CKF_DECRYPT | + CKF_WRAP | CKF_VERIFY_RECOVER | CKF_ENCRYPT | CKF_WRAP);; + break; + case CKM_DSA: + mechanism_info.flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_DH_PKCS_DERIVE: + mechanism_info.flags = CKF_DERIVE; + break; + default: + break; + } + } + /* set the public key objects */ + PK11_SETATTRS(attrs, CKA_TOKEN, token ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_DERIVE, + mechanism_info.flags & CKF_DERIVE ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_WRAP, + mechanism_info.flags & CKF_WRAP ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_VERIFY, + mechanism_info.flags & CKF_VERIFY ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_VERIFY_RECOVER, + mechanism_info.flags & CKF_VERIFY_RECOVER ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_ENCRYPT, + mechanism_info.flags & CKF_ENCRYPT? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(privattrs, CKA_DERIVE, + mechanism_info.flags & CKF_DERIVE ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_UNWRAP, + mechanism_info.flags & CKF_UNWRAP ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_SIGN, + mechanism_info.flags & CKF_SIGN ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_DECRYPT, + mechanism_info.flags & CKF_DECRYPT ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + + if (token) { + session_handle = PK11_GetRWSession(slot); + haslock = PK11_RWSessionHasLock(slot,session_handle); + restore = PR_TRUE; + } else { + PK11_EnterSlotMonitor(slot); /* gross!! */ + session_handle = slot->session; + restore = PR_FALSE; + haslock = PR_TRUE; + } + + crv = PK11_GETTAB(slot)->C_GenerateKeyPair(session_handle, &mechanism, + pubTemplate,pubCount,privTemplate,privCount,&pubID,&privID); + + + if (crv != CKR_OK) { + if (restore) { + PK11_RestoreROSession(slot,session_handle); + } else PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + /* This locking code is dangerous and needs to be more thought + * out... the real problem is that we're holding the mutex open this long + */ + if (haslock) { PK11_ExitSlotMonitor(slot); } + + /* swap around the ID's for older PKCS #11 modules */ + keyClass = PK11_ReadULongAttribute(slot,pubID,CKA_CLASS); + if (keyClass != CKO_PUBLIC_KEY) { + CK_OBJECT_HANDLE tmp = pubID; + pubID = privID; + privID = tmp; + } + + *pubKey = PK11_ExtractPublicKey(slot, keyType, pubID); + if (*pubKey == NULL) { + if (restore) { + /* we may have to restore the mutex so it get's exited properly + * in RestoreROSession */ + if (haslock) PK11_EnterSlotMonitor(slot); + PK11_RestoreROSession(slot,session_handle); + } + PK11_DestroyObject(slot,pubID); + PK11_DestroyObject(slot,privID); + return NULL; + } + + /* set the ID to the public key so we can find it again */ + pubKeyIndex = NULL; + switch (type) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + pubKeyIndex = &(*pubKey)->u.rsa.modulus; + break; + case CKM_DSA_KEY_PAIR_GEN: + pubKeyIndex = &(*pubKey)->u.dsa.publicValue; + break; + case CKM_DH_PKCS_KEY_PAIR_GEN: + pubKeyIndex = &(*pubKey)->u.dh.publicValue; + break; + } + PORT_Assert(pubKeyIndex != NULL); + + cka_id = PK11_MakeIDFromPubKey(pubKeyIndex); + pubIsToken = (PRBool)PK11_HasAttributeSet(slot,pubID, CKA_TOKEN); + + PK11_SETATTRS(&setTemplate, CKA_ID, cka_id->data, cka_id->len); + + if (haslock) { PK11_EnterSlotMonitor(slot); } + crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, privID, + &setTemplate, 1); + + if (crv == CKR_OK && pubIsToken) { + crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, pubID, + &setTemplate, 1); + } + + + if (restore) { + PK11_RestoreROSession(slot,session_handle); + } else { + PK11_ExitSlotMonitor(slot); + } + SECITEM_FreeItem(cka_id,PR_TRUE); + + + if (crv != CKR_OK) { + PK11_DestroyObject(slot,pubID); + PK11_DestroyObject(slot,privID); + PORT_SetError( PK11_MapError(crv) ); + *pubKey = NULL; + return NULL; + } + + privKey = PK11_MakePrivKey(slot,keyType,(PRBool)!token,privID,wincx); + if (privKey == NULL) { + SECKEY_DestroyPublicKey(*pubKey); + PK11_DestroyObject(slot,privID); + *pubKey = NULL; + return NULL; /* due to pairwise consistency check */ + } + + /* Perform PKCS #11 pairwise consistency check. */ + rv = pk11_PairwiseConsistencyCheck( *pubKey, privKey, &test_mech, wincx ); + if( rv != SECSuccess ) { + SECKEY_DestroyPublicKey( *pubKey ); + SECKEY_DestroyPrivateKey( privKey ); + *pubKey = NULL; + privKey = NULL; + return NULL; + } + + return privKey; +} + +/* + * This function does a straight public key wrap (which only RSA can do). + * Use PK11_PubGenKey and PK11_WrapSymKey to implement the FORTEZZA and + * Diffie-Hellman Ciphers. */ +SECStatus +PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey, + PK11SymKey *symKey, SECItem *wrappedKey) +{ + PK11SlotInfo *slot; + CK_ULONG len = wrappedKey->len; + PK11SymKey *newKey = NULL; + CK_OBJECT_HANDLE id; + CK_MECHANISM mechanism; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + /* if this slot doesn't support the mechanism, go to a slot that does */ + newKey = pk11_ForceSlot(symKey,type,CKA_ENCRYPT); + if (newKey != NULL) { + symKey = newKey; + } + + if ((symKey == NULL) || (symKey->slot == NULL)) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + + slot = symKey->slot; + mechanism.mechanism = pk11_mapWrapKeyType(pubKey->keyType); + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + + id = PK11_ImportPublicKey(slot,pubKey,PR_FALSE); + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_WrapKey(session,&mechanism, + id,symKey->objectID,wrappedKey->data,&len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + if (newKey) { + PK11_FreeSymKey(newKey); + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + wrappedKey->len = len; + return SECSuccess; +} + +/* + * this little function uses the Encrypt function to wrap a key, just in + * case we have problems with the wrap implementation for a token. + */ +static SECStatus +pk11_HandWrap(PK11SymKey *wrappingKey, SECItem *param, CK_MECHANISM_TYPE type, + SECItem *inKey, SECItem *outKey) +{ + PK11SlotInfo *slot; + CK_ULONG len; + SECItem *data; + CK_MECHANISM mech; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + slot = wrappingKey->slot; + /* use NULL IV's for wrapping */ + mech.mechanism = type; + if (param) { + mech.pParameter = param->data; + mech.ulParameterLen = param->len; + } else { + mech.pParameter = NULL; + mech.ulParameterLen = 0; + } + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_EncryptInit(session,&mech, + wrappingKey->objectID); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + + /* keys are almost always aligned, but if we get this far, + * we've gone above and beyond anyway... */ + data = PK11_BlockData(inKey,PK11_GetBlockSize(type,param)); + if (data == NULL) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + len = outKey->len; + crv = PK11_GETTAB(slot)->C_Encrypt(session,data->data,data->len, + outKey->data, &len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + SECITEM_FreeItem(data,PR_TRUE); + outKey->len = len; + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * This function does a symetric based wrap. + */ +SECStatus +PK11_WrapSymKey(CK_MECHANISM_TYPE type, SECItem *param, + PK11SymKey *wrappingKey, PK11SymKey *symKey, SECItem *wrappedKey) +{ + PK11SlotInfo *slot; + CK_ULONG len = wrappedKey->len; + PK11SymKey *newKey = NULL; + SECItem *param_save = NULL; + CK_MECHANISM mechanism; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + SECStatus rv; + + /* if this slot doesn't support the mechanism, go to a slot that does */ + /* Force symKey and wrappingKey into the same slot */ + if ((wrappingKey->slot == NULL) || (symKey->slot != wrappingKey->slot)) { + /* first try copying the wrapping Key to the symKey slot */ + if (symKey->slot && PK11_DoesMechanism(symKey->slot,type)) { + newKey = pk11_CopyToSlot(symKey->slot,type,CKA_WRAP,wrappingKey); + } + /* Nope, try it the other way */ + if (newKey == NULL) { + if (wrappingKey->slot) { + newKey = pk11_CopyToSlot(wrappingKey->slot, + symKey->type, CKA_ENCRYPT, symKey); + } + /* just not playing... one last thing, can we get symKey's data? + * If it's possible, we it should already be in the + * symKey->data.data pointer because pk11_CopyToSlot would have + * tried to put it there. */ + if (newKey == NULL) { + /* Can't get symKey's data: Game Over */ + if (symKey->data.data == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + if (param == NULL) { + param_save = param = PK11_ParamFromIV(type,NULL); + } + rv = pk11_HandWrap(wrappingKey, param, type, + &symKey->data,wrappedKey); + if (param_save) SECITEM_FreeItem(param_save,PR_TRUE); + return rv; + } + /* we successfully moved the sym Key */ + symKey = newKey; + } else { + /* we successfully moved the wrapping Key */ + wrappingKey = newKey; + } + } + + /* at this point both keys are in the same token */ + slot = wrappingKey->slot; + mechanism.mechanism = type; + /* use NULL IV's for wrapping */ + if (param == NULL) { + param_save = param = PK11_ParamFromIV(type,NULL); + } + if (param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } else { + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + } + + len = wrappedKey->len; + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_WrapKey(session, &mechanism, + wrappingKey->objectID, symKey->objectID, + wrappedKey->data, &len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + rv = SECSuccess; + if (crv != CKR_OK) { + /* can't wrap it? try hand wrapping it... */ + do { + if (symKey->data.data == NULL) { + rv = PK11_ExtractKeyValue(symKey); + if (rv != SECSuccess) break; + } + rv = pk11_HandWrap(wrappingKey, param, type, &symKey->data, + wrappedKey); + } while (PR_FALSE); + } else { + wrappedKey->len = len; + } + if (newKey) PK11_FreeSymKey(newKey); + if (param_save) SECITEM_FreeItem(param_save,PR_TRUE); + return rv; +} + +/* + * This Generates a new key based on a symetricKey + */ +PK11SymKey * +PK11_Derive( PK11SymKey *baseKey, CK_MECHANISM_TYPE derive, SECItem *param, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize) +{ + return pk11_DeriveWithTemplate(baseKey, derive, param, target, operation, + keySize, NULL, 0); +} + +#define MAX_TEMPL_ATTRS 16 /* maximum attributes in template */ + +/* This mask includes all CK_FLAGs with an equivalent CKA_ attribute. */ +#define CKF_KEY_OPERATION_FLAGS 0x000e7b00UL + +static unsigned int +pk11_FlagsToAttributes(CK_FLAGS flags, CK_ATTRIBUTE *attrs, CK_BBOOL *ckTrue) +{ + + const static CK_ATTRIBUTE_TYPE attrTypes[12] = { + CKA_ENCRYPT, CKA_DECRYPT, 0 /* DIGEST */, CKA_SIGN, + CKA_SIGN_RECOVER, CKA_VERIFY, CKA_VERIFY_RECOVER, 0 /* GEN */, + 0 /* GEN PAIR */, CKA_WRAP, CKA_UNWRAP, CKA_DERIVE + }; + + const CK_ATTRIBUTE_TYPE *pType = attrTypes; + CK_ATTRIBUTE *attr = attrs; + CK_FLAGS test = CKF_ENCRYPT; + + + PR_ASSERT(!(flags & ~CKF_KEY_OPERATION_FLAGS)); + flags &= CKF_KEY_OPERATION_FLAGS; + + for (; flags && test <= CKF_DERIVE; test <<= 1, ++pType) { + if (test & flags) { + flags ^= test; + PK11_SETATTRS(attr, *pType, ckTrue, sizeof *ckTrue); + ++attr; + } + } + return (attr - attrs); +} + +PK11SymKey * +PK11_DeriveWithFlags( PK11SymKey *baseKey, CK_MECHANISM_TYPE derive, + SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize, CK_FLAGS flags) +{ + CK_BBOOL ckTrue = CK_TRUE; + CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS]; + unsigned int templateCount; + + templateCount = pk11_FlagsToAttributes(flags, keyTemplate, &ckTrue); + return pk11_DeriveWithTemplate(baseKey, derive, param, target, operation, + keySize, keyTemplate, templateCount); +} + +static PRBool +pk11_FindAttrInTemplate(CK_ATTRIBUTE * attr, + unsigned int numAttrs, + CK_ATTRIBUTE_TYPE target) +{ + for (; numAttrs > 0; ++attr, --numAttrs) { + if (attr->type == target) + return PR_TRUE; + } + return PR_FALSE; +} + +static PK11SymKey * +pk11_DeriveWithTemplate( PK11SymKey *baseKey, CK_MECHANISM_TYPE derive, + SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize, CK_ATTRIBUTE *userAttr, unsigned int numAttrs) +{ + PK11SlotInfo * slot = baseKey->slot; + PK11SymKey * symKey; + PK11SymKey * newBaseKey = NULL; + CK_BBOOL cktrue = CK_TRUE; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_ULONG valueLen = 0; + CK_MECHANISM mechanism; + CK_RV crv; + CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS]; + CK_ATTRIBUTE * attrs = keyTemplate; + unsigned int templateCount; + + if (numAttrs > MAX_TEMPL_ATTRS) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + /* first copy caller attributes in. */ + for (templateCount = 0; templateCount < numAttrs; ++templateCount) { + *attrs++ = *userAttr++; + } + + /* We only add the following attributes to the template if the caller + ** didn't already supply them. + */ + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_CLASS)) { + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof keyClass); + attrs++; + } + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_KEY_TYPE)) { + keyType = PK11_GetKeyType(target, keySize); + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof keyType ); + attrs++; + } + if (keySize > 0 && + !pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_VALUE_LEN)) { + valueLen = (CK_ULONG)keySize; + PK11_SETATTRS(attrs, CKA_VALUE_LEN, &valueLen, sizeof valueLen); + attrs++; + } + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, operation)) { + PK11_SETATTRS(attrs, operation, &cktrue, sizeof cktrue); attrs++; + } + + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= MAX_TEMPL_ATTRS); + + /* move the key to a slot that can do the function */ + if (!PK11_DoesMechanism(slot,derive)) { + /* get a new base key & slot */ + PK11SlotInfo *newSlot = PK11_GetBestSlot(derive, baseKey->cx); + + if (newSlot == NULL) return NULL; + + newBaseKey = pk11_CopyToSlot (newSlot, derive, CKA_DERIVE, + baseKey); + PK11_FreeSlot(newSlot); + if (newBaseKey == NULL) return NULL; + baseKey = newBaseKey; + slot = baseKey->slot; + } + + + /* get our key Structure */ + symKey = PK11_CreateSymKey(slot,target,baseKey->cx); + if (symKey == NULL) { + return NULL; + } + + symKey->size = keySize; + + mechanism.mechanism = derive; + if (param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } else { + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + } + symKey->origin=PK11_OriginDerive; + + pk11_EnterKeyMonitor(symKey); + crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism, + baseKey->objectID, keyTemplate, templateCount, &symKey->objectID); + pk11_ExitKeyMonitor(symKey); + + if (newBaseKey) PK11_FreeSymKey(newBaseKey); + if (crv != CKR_OK) { + PK11_FreeSymKey(symKey); + return NULL; + } + return symKey; +} + +/* build a public KEA key from the public value */ +SECKEYPublicKey * +PK11_MakeKEAPubKey(unsigned char *keyData,int length) +{ + SECKEYPublicKey *pubk; + SECItem pkData; + SECStatus rv; + PRArenaPool *arena; + + pkData.data = keyData; + pkData.len = length; + + arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + return NULL; + + pubk = (SECKEYPublicKey *) PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); + if (pubk == NULL) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + + pubk->arena = arena; + pubk->pkcs11Slot = 0; + pubk->pkcs11ID = CK_INVALID_KEY; + pubk->keyType = fortezzaKey; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.KEAKey, &pkData); + if (rv != SECSuccess) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + return pubk; +} + + +/* + * This Generates a wrapping key based on a privateKey, publicKey, and two + * random numbers. For Mail usage RandomB should be NULL. In the Sender's + * case RandomA is generate, outherwize it is passed. + */ +static unsigned char *rb_email = NULL; + +PK11SymKey * +PK11_PubDerive(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey, + PRBool isSender, SECItem *randomA, SECItem *randomB, + CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target, + CK_ATTRIBUTE_TYPE operation, int keySize,void *wincx) +{ + PK11SlotInfo *slot = privKey->pkcs11Slot; + CK_MECHANISM mechanism; + PK11SymKey *symKey; + CK_RV crv; + + + if (rb_email == NULL) { + rb_email = PORT_ZAlloc(128); + if (rb_email == NULL) { + return NULL; + } + rb_email[127] = 1; + } + + /* get our key Structure */ + symKey = PK11_CreateSymKey(slot,target,wincx); + if (symKey == NULL) { + return NULL; + } + + symKey->origin = PK11_OriginDerive; + + switch (privKey->keyType) { + case rsaKey: + case nullKey: + PORT_SetError(SEC_ERROR_BAD_KEY); + break; + /* case keaKey: */ + case dsaKey: + case fortezzaKey: + { + CK_KEA_DERIVE_PARAMS param; + param.isSender = (CK_BBOOL) isSender; + param.ulRandomLen = randomA->len; + param.pRandomA = randomA->data; + param.pRandomB = rb_email; + if (randomB) + param.pRandomB = randomB->data; + if (pubKey->keyType == fortezzaKey) { + param.ulPublicDataLen = pubKey->u.fortezza.KEAKey.len; + param.pPublicData = pubKey->u.fortezza.KEAKey.data; + } else { + /* assert type == keaKey */ + /* XXX change to match key key types */ + param.ulPublicDataLen = pubKey->u.fortezza.KEAKey.len; + param.pPublicData = pubKey->u.fortezza.KEAKey.data; + } + + mechanism.mechanism = derive; + mechanism.pParameter = ¶m; + mechanism.ulParameterLen = sizeof(param); + + /* get a new symKey structure */ + pk11_EnterKeyMonitor(symKey); + crv=PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism, + privKey->pkcs11ID, NULL, 0, &symKey->objectID); + pk11_ExitKeyMonitor(symKey); + if (crv == CKR_OK) return symKey; + PORT_SetError( PK11_MapError(crv) ); + } + break; + case dhKey: + { + CK_BBOOL cktrue = CK_TRUE; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_ULONG key_size = 0; + CK_ATTRIBUTE keyTemplate[4]; + int templateCount; + CK_ATTRIBUTE *attrs = keyTemplate; + + if (pubKey->keyType != dhKey) { + PORT_SetError(SEC_ERROR_BAD_KEY); + break; + } + + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass)); + attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); + attrs++; + PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size)); + attrs++; + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE)); + + keyType = PK11_GetKeyType(target,keySize); + key_size = keySize; + symKey->size = keySize; + if (key_size == 0) templateCount--; + + mechanism.mechanism = derive; + + /* we can undefine these when we define diffie-helman keys */ + mechanism.pParameter = pubKey->u.dh.publicValue.data; + mechanism.ulParameterLen = pubKey->u.dh.publicValue.len; + + pk11_EnterKeyMonitor(symKey); + crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism, + privKey->pkcs11ID, keyTemplate, templateCount, &symKey->objectID); + pk11_ExitKeyMonitor(symKey); + if (crv == CKR_OK) return symKey; + PORT_SetError( PK11_MapError(crv) ); + } + break; + } + + PK11_FreeSymKey(symKey); + return NULL; +} + +/* + * this little function uses the Decrypt function to unwrap a key, just in + * case we are having problem with unwrap. NOTE: The key size may + * not be preserved properly for some algorithms! + */ +static PK11SymKey * +pk11_HandUnwrap(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey, + CK_MECHANISM *mech, SECItem *inKey, CK_MECHANISM_TYPE target, + CK_ATTRIBUTE *keyTemplate, unsigned int templateCount, + int key_size, void * wincx) +{ + CK_ULONG len; + SECItem outKey; + PK11SymKey *symKey; + CK_RV crv; + + /* keys are almost always aligned, but if we get this far, + * we've gone above and beyond anyway... */ + outKey.data = (unsigned char*)PORT_Alloc(inKey->len); + if (outKey.data == NULL) { + PORT_SetError( SEC_ERROR_NO_MEMORY ); + return NULL; + } + len = inKey->len; + + + /* use NULL IV's for wrapping */ + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_DecryptInit(slot->session,mech,wrappingKey); + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + PORT_Free(outKey.data); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + + crv = PK11_GETTAB(slot)->C_Decrypt(slot->session,inKey->data,inKey->len, + outKey.data, &len); + PK11_ExitSlotMonitor(slot); + outKey.len = (key_size == 0) ? len : key_size; + if (crv != CKR_OK) { + PORT_Free(outKey.data); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + + if (PK11_DoesMechanism(slot,target)) { + symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap, + keyTemplate, templateCount, + &outKey, wincx); + } else { + slot = PK11_GetBestSlot(target,wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + PORT_Free(outKey.data); + return NULL; + } + symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap, + keyTemplate, templateCount, + &outKey, wincx); + PK11_FreeSlot(slot); + } + PORT_Free(outKey.data); + return symKey; +} + +/* + * The wrap/unwrap function is pretty much the same for private and + * public keys. It's just getting the Object ID and slot right. This is + * the combined unwrap function. + */ +static PK11SymKey * +pk11_AnyUnwrapKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey, + CK_MECHANISM_TYPE wrapType, SECItem *param, SECItem *wrappedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize, + void *wincx, CK_ATTRIBUTE *userAttr, unsigned int numAttrs) +{ + PK11SymKey * symKey; + SECItem * param_free = NULL; + CK_BBOOL ckfalse = CK_FALSE; + CK_BBOOL cktrue = CK_TRUE; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_ULONG valueLen = 0; + CK_MECHANISM mechanism; + CK_RV crv; + CK_MECHANISM_INFO mechanism_info; + CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS]; + CK_ATTRIBUTE * attrs = keyTemplate; + unsigned int templateCount; + + if (numAttrs > MAX_TEMPL_ATTRS) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + /* first copy caller attributes in. */ + for (templateCount = 0; templateCount < numAttrs; ++templateCount) { + *attrs++ = *userAttr++; + } + + /* We only add the following attributes to the template if the caller + ** didn't already supply them. + */ + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_CLASS)) { + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof keyClass); + attrs++; + } + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_KEY_TYPE)) { + keyType = PK11_GetKeyType(target, keySize); + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof keyType ); + attrs++; + } + if (keySize > 0 && + !pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_VALUE_LEN)) { + valueLen = (CK_ULONG)keySize; + PK11_SETATTRS(attrs, CKA_VALUE_LEN, &valueLen, sizeof valueLen); + attrs++; + } + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, operation)) { + PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++; + } + + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE)); + + + /* find out if we can do wrap directly. Because the RSA case if *very* + * common, cache the results for it. */ + if ((wrapType == CKM_RSA_PKCS) && (slot->hasRSAInfo)) { + mechanism_info.flags = slot->RSAInfoFlags; + } else { + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,wrapType, + &mechanism_info); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + mechanism_info.flags = 0; + } + if (wrapType == CKM_RSA_PKCS) { + slot->RSAInfoFlags = mechanism_info.flags; + slot->hasRSAInfo = PR_TRUE; + } + } + + /* initialize the mechanism structure */ + mechanism.mechanism = wrapType; + /* use NULL IV's for wrapping */ + if (param == NULL) param = param_free = PK11_ParamFromIV(wrapType,NULL); + if (param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } else { + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + } + + if ((mechanism_info.flags & CKF_UNWRAP) == 0) { + symKey = pk11_HandUnwrap(slot, wrappingKey, &mechanism, wrappedKey, + target, keyTemplate, templateCount, keySize, + wincx); + if (symKey) return symKey; + /* fall through, maybe they incorrectly set CKF_UNWRAP */ + } + + /* get our key Structure */ + symKey = PK11_CreateSymKey(slot,target,wincx); + if (symKey == NULL) { + return NULL; + } + + symKey->size = keySize; + symKey->origin = PK11_OriginUnwrap; + + pk11_EnterKeyMonitor(symKey); + crv = PK11_GETTAB(slot)->C_UnwrapKey(symKey->session,&mechanism,wrappingKey, + wrappedKey->data, wrappedKey->len, keyTemplate, templateCount, + &symKey->objectID); + pk11_ExitKeyMonitor(symKey); + if (param_free) SECITEM_FreeItem(param_free,PR_TRUE); + if (crv != CKR_OK) { + /* try hand Unwrapping */ + PK11_FreeSymKey(symKey); + symKey = pk11_HandUnwrap(slot, wrappingKey, &mechanism, wrappedKey, + target, keyTemplate, templateCount, keySize, + wincx); + } + + return symKey; +} + +/* use a symetric key to unwrap another symetric key */ +PK11SymKey * +PK11_UnwrapSymKey( PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType, + SECItem *param, SECItem *wrappedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize) +{ + return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID, + wrapType, param, wrappedKey, target, operation, keySize, + wrappingKey->cx, NULL, 0); +} + +/* use a symetric key to unwrap another symetric key */ +PK11SymKey * +PK11_UnwrapSymKeyWithFlags(PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType, + SECItem *param, SECItem *wrappedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize, CK_FLAGS flags) +{ + CK_BBOOL ckTrue = CK_TRUE; + CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS]; + unsigned int templateCount; + + templateCount = pk11_FlagsToAttributes(flags, keyTemplate, &ckTrue); + return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID, + wrapType, param, wrappedKey, target, operation, keySize, + wrappingKey->cx, keyTemplate, templateCount); +} + + +/* unwrap a symetric key with a private key. */ +PK11SymKey * +PK11_PubUnwrapSymKey(SECKEYPrivateKey *wrappingKey, SECItem *wrappedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize) +{ + CK_MECHANISM_TYPE wrapType = pk11_mapWrapKeyType(wrappingKey->keyType); + + PK11_HandlePasswordCheck(wrappingKey->pkcs11Slot,wrappingKey->wincx); + + return pk11_AnyUnwrapKey(wrappingKey->pkcs11Slot, wrappingKey->pkcs11ID, + wrapType, NULL, wrappedKey, target, operation, keySize, + wrappingKey->wincx, NULL, 0); +} + +/* + * Recover the Signed data. We need this because our old verify can't + * figure out which hash algorithm to use until we decryptted this. + */ +SECStatus +PK11_VerifyRecover(SECKEYPublicKey *key, + SECItem *sig, SECItem *dsig, void *wincx) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + CK_OBJECT_HANDLE id = key->pkcs11ID; + CK_MECHANISM mech = {0, NULL, 0 }; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_ULONG len; + CK_RV crv; + + mech.mechanism = pk11_mapSignKeyType(key->keyType); + + if (slot == NULL) { + slot = PK11_GetBestSlot(mech.mechanism,wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + id = PK11_ImportPublicKey(slot,key,PR_FALSE); + } + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_VerifyRecoverInit(session,&mech,id); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + len = dsig->len; + crv = PK11_GETTAB(slot)->C_VerifyRecover(session,sig->data, + sig->len, dsig->data, &len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + dsig->len = len; + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * verify a signature from its hash. + */ +SECStatus +PK11_Verify(SECKEYPublicKey *key, SECItem *sig, SECItem *hash, void *wincx) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + PK11SlotInfo *tmpslot = key->pkcs11Slot; + CK_OBJECT_HANDLE id = key->pkcs11ID; + CK_MECHANISM mech = {0, NULL, 0 }; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + mech.mechanism = pk11_mapSignKeyType(key->keyType); + + if (slot == NULL) { + if (mech.mechanism == CKM_DSA) { + slot = PK11_GetInternalSlot(); /* use internal slot for + DSA verify */ + } else { + slot = PK11_GetBestSlot(mech.mechanism,wincx); + }; + + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + id = PK11_ImportPublicKey(slot,key,PR_FALSE); + + } + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_VerifyInit(session,&mech,id); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + crv = PK11_GETTAB(slot)->C_Verify(session,hash->data, + hash->len, sig->data, sig->len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * sign a hash. The algorithm is determined by the key. + */ +SECStatus +PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, SECItem *hash) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + CK_MECHANISM mech = {0, NULL, 0 }; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_ULONG len; + CK_RV crv; + + mech.mechanism = pk11_mapSignKeyType(key->keyType); + + PK11_HandlePasswordCheck(slot, key->wincx); + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_SignInit(session,&mech,key->pkcs11ID); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + len = sig->len; + crv = PK11_GETTAB(slot)->C_Sign(session,hash->data, + hash->len, sig->data, &len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + sig->len = len; + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * Now SSL 2.0 uses raw RSA stuff. These next to functions *must* use + * RSA keys, or they'll fail. We do the checks up front. If anyone comes + * up with a meaning for rawdecrypt for any other public key operation, + * then we need to move this check into some of PK11_PubDecrypt callers, + * (namely SSL 2.0). + */ +SECStatus +PK11_PubDecryptRaw(SECKEYPrivateKey *key, unsigned char *data, + unsigned *outLen, unsigned int maxLen, unsigned char *enc, + unsigned encLen) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + CK_MECHANISM mech = {CKM_RSA_X_509, NULL, 0 }; + CK_ULONG out = maxLen; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + if (key->keyType != rsaKey) { + PORT_SetError( SEC_ERROR_INVALID_KEY ); + return SECFailure; + } + + /* Why do we do a PK11_handle check here? for simple + * decryption? .. because the user may have asked for 'ask always' + * and this is a private key operation. In practice, thought, it's mute + * since only servers wind up using this function */ + PK11_HandlePasswordCheck(slot, key->wincx); + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_DecryptInit(session,&mech,key->pkcs11ID); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + crv = PK11_GETTAB(slot)->C_Decrypt(session,enc, encLen, + data, &out); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + *outLen = out; + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* The encrypt version of the above function */ +SECStatus +PK11_PubEncryptRaw(SECKEYPublicKey *key, unsigned char *enc, + unsigned char *data, unsigned dataLen, void *wincx) +{ + PK11SlotInfo *slot; + CK_MECHANISM mech = {CKM_RSA_X_509, NULL, 0 }; + CK_OBJECT_HANDLE id; + CK_ULONG out = dataLen; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + if (key->keyType != rsaKey) { + PORT_SetError( SEC_ERROR_BAD_KEY ); + return SECFailure; + } + + slot = PK11_GetBestSlot(mech.mechanism, wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + + id = PK11_ImportPublicKey(slot,key,PR_FALSE); + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_EncryptInit(session,&mech,id); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + crv = PK11_GETTAB(slot)->C_Encrypt(session,data,dataLen,enc,&out); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + + +/********************************************************************** + * + * Now Deal with Crypto Contexts + * + **********************************************************************/ + +/* + * the monitors... + */ +void +PK11_EnterContextMonitor(PK11Context *cx) { + /* if we own the session and our slot is ThreadSafe, only monitor + * the Context */ + if ((cx->ownSession) && (cx->slot->isThreadSafe)) { + /* Should this use monitors instead? */ + PR_Lock(cx->sessionLock); + } else { + PK11_EnterSlotMonitor(cx->slot); + } +} + +void +PK11_ExitContextMonitor(PK11Context *cx) { + /* if we own the session and our slot is ThreadSafe, only monitor + * the Context */ + if ((cx->ownSession) && (cx->slot->isThreadSafe)) { + /* Should this use monitors instead? */ + PR_Unlock(cx->sessionLock); + } else { + PK11_ExitSlotMonitor(cx->slot); + } +} + +/* + * Free up a Cipher Context + */ +void +PK11_DestroyContext(PK11Context *context, PRBool freeit) +{ + pk11_CloseSession(context->slot,context->session,context->ownSession); + /* initialize the critical fields of the context */ + if (context->savedData != NULL ) PORT_Free(context->savedData); + if (context->key) PK11_FreeSymKey(context->key); + if (context->param) SECITEM_FreeItem(context->param, PR_TRUE); + if (context->sessionLock) PR_DestroyLock(context->sessionLock); + PK11_FreeSlot(context->slot); + if (freeit) PORT_Free(context); +} + +/* + * save the current context. Allocate Space if necessary. + */ +void * +pk11_saveContext(PK11Context *context,void *space, unsigned long *savedLength) +{ + CK_ULONG length; + CK_RV crv; + + if (space == NULL) { + crv =PK11_GETTAB(context->slot)->C_GetOperationState(context->session, + NULL,&length); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + space = PORT_Alloc(length); + if (space == NULL) return NULL; + *savedLength = length; + } + crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session, + (CK_BYTE_PTR)space,savedLength); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + return space; +} + +/* + * restore the current context + */ +SECStatus +pk11_restoreContext(PK11Context *context,void *space, unsigned long savedLength) +{ + CK_RV crv; + CK_OBJECT_HANDLE objectID = (context->key) ? context->key->objectID: + CK_INVALID_KEY; + + PORT_Assert(space != NULL); + if (space == NULL) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + crv = PK11_GETTAB(context->slot)->C_SetOperationState(context->session, + (CK_BYTE_PTR)space, savedLength, objectID, 0); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv)); + return SECFailure; + } + return SECSuccess; +} + +SECStatus pk11_Finalize(PK11Context *context); + +/* + * Context initialization. Used by all flavors of CreateContext + */ +static SECStatus +pk11_context_init(PK11Context *context, CK_MECHANISM *mech_info) +{ + CK_RV crv; + PK11SymKey *symKey = context->key; + SECStatus rv = SECSuccess; + + switch (context->operation) { + case CKA_ENCRYPT: + crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session, + mech_info, symKey->objectID); + break; + case CKA_DECRYPT: + if (context->fortezzaHack) { + CK_ULONG count = 0;; + /* generate the IV for fortezza */ + crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session, + mech_info, symKey->objectID); + if (crv != CKR_OK) break; + PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, + NULL, &count); + } + crv=PK11_GETTAB(context->slot)->C_DecryptInit(context->session, + mech_info, symKey->objectID); + break; + case CKA_SIGN: + crv=PK11_GETTAB(context->slot)->C_SignInit(context->session, + mech_info, symKey->objectID); + break; + case CKA_VERIFY: + crv=PK11_GETTAB(context->slot)->C_SignInit(context->session, + mech_info, symKey->objectID); + break; + case CKA_DIGEST: + crv=PK11_GETTAB(context->slot)->C_DigestInit(context->session, + mech_info); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context,context->savedData, + &context->savedLength); + if (context->savedData == NULL) rv = SECFailure; + /* clear out out session for others to use */ + pk11_Finalize(context); + } + return rv; +} + + +/* + * Common Helper Function do come up with a new context. + */ +static PK11Context *pk11_CreateNewContextInSlot(CK_MECHANISM_TYPE type, + PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey, + SECItem *param) +{ + CK_MECHANISM mech_info; + PK11Context *context; + SECStatus rv; + + context = (PK11Context *) PORT_Alloc(sizeof(PK11Context)); + if (context == NULL) { + return NULL; + } + + /* now deal with the fortezza hack... the fortezza hack is an attempt + * to get around the issue of the card not allowing you to do a FORTEZZA + * LoadIV/Encrypt, which was added because such a combination could be + * use to circumvent the key escrow system. Unfortunately SSL needs to + * do this kind of operation, so in SSL we do a loadIV (to verify it), + * Then GenerateIV, and through away the first 8 bytes on either side + * of the connection.*/ + context->fortezzaHack = PR_FALSE; + if (type == CKM_SKIPJACK_CBC64) { + if (symKey->origin == PK11_OriginFortezzaHack) { + context->fortezzaHack = PR_TRUE; + } + } + + /* initialize the critical fields of the context */ + context->operation = operation; + context->key = symKey ? PK11_ReferenceSymKey(symKey) : NULL; + context->slot = PK11_ReferenceSlot(slot); + context->session = pk11_GetNewSession(slot,&context->ownSession); + context->cx = symKey ? symKey->cx : NULL; + /* get our session */ + context->savedData = NULL; + + /* save the parameters so that some digesting stuff can do multiple + * begins on a single context */ + context->type = type; + context->param = SECITEM_DupItem(param); + context->init = PR_FALSE; + context->sessionLock = PR_NewLock(); + if ((context->param == NULL) || (context->sessionLock == NULL)) { + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + + mech_info.mechanism = type; + mech_info.pParameter = param->data; + mech_info.ulParameterLen = param->len; + PK11_EnterContextMonitor(context); + rv = pk11_context_init(context,&mech_info); + PK11_ExitContextMonitor(context); + + if (rv != SECSuccess) { + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + context->init = PR_TRUE; + return context; +} + + +/* + * put together the various PK11_Create_Context calls used by different + * parts of libsec. + */ +PK11Context * +PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, + SECItem *param, void *wincx) +{ + PK11SymKey *symKey; + PK11Context *context; + + /* first get a slot */ + if (slot == NULL) { + slot = PK11_GetBestSlot(type,wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + } else { + PK11_ReferenceSlot(slot); + } + + /* now import the key */ + symKey = PK11_ImportSymKey(slot, type, origin, operation, key, wincx); + if (symKey == NULL) return NULL; + + context = PK11_CreateContextBySymKey(type, operation, symKey, param); + + PK11_FreeSymKey(symKey); + PK11_FreeSlot(slot); + + return context; +} + + +/* + * Create a context from a key. We really should make sure we aren't using + * the same key in multiple session! + */ +PK11Context * +PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type,CK_ATTRIBUTE_TYPE operation, + PK11SymKey *symKey, SECItem *param) +{ + PK11SymKey *newKey; + PK11Context *context; + + /* if this slot doesn't support the mechanism, go to a slot that does */ + newKey = pk11_ForceSlot(symKey,type,operation); + if (newKey == NULL) { + PK11_ReferenceSymKey(symKey); + } else { + symKey = newKey; + } + + + /* Context Adopts the symKey.... */ + context = pk11_CreateNewContextInSlot(type, symKey->slot, operation, symKey, + param); + PK11_FreeSymKey(symKey); + return context; +} + +/* + * Digest contexts don't need keys, but the do need to find a slot. + * Macing should use PK11_CreateContextBySymKey. + */ +PK11Context * +PK11_CreateDigestContext(SECOidTag hashAlg) +{ + /* digesting has to work without authentication to the slot */ + CK_MECHANISM_TYPE type; + PK11SlotInfo *slot; + PK11Context *context; + SECItem param; + + type = PK11_AlgtagToMechanism(hashAlg); + slot = PK11_GetBestSlot(type, NULL); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + + /* maybe should really be PK11_GenerateNewParam?? */ + param.data = NULL; + param.len = 0; + + context = pk11_CreateNewContextInSlot(type, slot, CKA_DIGEST, NULL, ¶m); + PK11_FreeSlot(slot); + return context; +} + +/* + * create a new context which is the clone of the state of old context. + */ +PK11Context * PK11_CloneContext(PK11Context *old) +{ + PK11Context *newcx; + PRBool needFree = PR_FALSE; + SECStatus rv = SECSuccess; + void *data; + unsigned long len; + + newcx = pk11_CreateNewContextInSlot(old->type, old->slot, old->operation, + old->key, old->param); + if (newcx == NULL) return NULL; + + /* now clone the save state. First we need to find the save state + * of the old session. If the old context owns it's session, + * the state needs to be saved, otherwise the state is in saveData. */ + if (old->ownSession) { + PK11_EnterContextMonitor(old); + data=pk11_saveContext(old,NULL,&len); + PK11_ExitContextMonitor(old); + needFree = PR_TRUE; + } else { + data = old->savedData; + len = old->savedLength; + } + + if (data == NULL) { + PK11_DestroyContext(newcx,PR_TRUE); + return NULL; + } + + /* now copy that state into our new context. Again we have different + * work if the new context owns it's own session. If it does, we + * restore the state gathered above. If it doesn't, we copy the + * saveData pointer... */ + if (newcx->ownSession) { + PK11_EnterContextMonitor(newcx); + rv = pk11_restoreContext(newcx,data,len); + PK11_ExitContextMonitor(newcx); + } else { + PORT_Assert(newcx->savedData != NULL); + if ((newcx->savedData == NULL) || (newcx->savedLength < len)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + } else { + PORT_Memcpy(newcx->savedData,data,len); + newcx->savedLength = len; + } + } + + if (needFree) PORT_Free(data); + + if (rv != SECSuccess) { + PK11_DestroyContext(newcx,PR_TRUE); + return NULL; + } + return newcx; +} + +/* + * save the current context state into a variable. Required to make FORTEZZA + * work. + */ +SECStatus +PK11_SaveContext(PK11Context *cx,unsigned char *save,int *len, int saveLength) +{ + unsigned char * data; + CK_ULONG length = saveLength; + + if (cx->ownSession) { + PK11_EnterContextMonitor(cx); + data = (unsigned char*)pk11_saveContext(cx,save,&length); + PK11_ExitContextMonitor(cx); + if (data) *len = length; + } else { + data = (unsigned char*)cx->savedData; + if (cx->savedData) { + PORT_Memcpy(save,cx->savedData,cx->savedLength); + } + *len = cx->savedLength; + } + return (data != NULL) ? SECSuccess : SECFailure; +} + +/* + * restore the context state into a new running context. Also required for + * FORTEZZA . + */ +SECStatus +PK11_RestoreContext(PK11Context *cx,unsigned char *save,int len) +{ + SECStatus rv = SECSuccess; + if (cx->ownSession) { + PK11_EnterContextMonitor(cx); + pk11_Finalize(cx); + rv = pk11_restoreContext(cx,save,len); + PK11_ExitContextMonitor(cx); + } else { + PORT_Assert(cx->savedData != NULL); + if ((cx->savedData == NULL) || (cx->savedLength < (unsigned) len)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + } else { + PORT_Memcpy(cx->savedData,save,len); + cx->savedLength = len; + } + } + return rv; +} + +/* + * This is to get FIPS compliance until we can convert + * libjar to use PK11_ hashing functions. It returns PR_FALSE + * if we can't get a PK11 Context. + */ +PRBool +PK11_HashOK(SECOidTag algID) { + PK11Context *cx; + + cx = PK11_CreateDigestContext(algID); + if (cx == NULL) return PR_FALSE; + PK11_DestroyContext(cx, PR_TRUE); + return PR_TRUE; +} + + + +/* + * start a new digesting or Mac'ing operation on this context + */ +SECStatus PK11_DigestBegin(PK11Context *cx) +{ + CK_MECHANISM mech_info; + SECStatus rv; + + if (cx->init == PR_TRUE) { + return SECSuccess; + } + + /* + * make sure the old context is clear first + */ + PK11_EnterContextMonitor(cx); + pk11_Finalize(cx); + + mech_info.mechanism = cx->type; + mech_info.pParameter = cx->param->data; + mech_info.ulParameterLen = cx->param->len; + rv = pk11_context_init(cx,&mech_info); + PK11_ExitContextMonitor(cx); + + if (rv != SECSuccess) { + return SECFailure; + } + cx->init = PR_TRUE; + return SECSuccess; +} + +SECStatus +PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, unsigned char *in, + int32 len) { + PK11Context *context; + unsigned int max_length; + unsigned int out_length; + SECStatus rv; + + context = PK11_CreateDigestContext(hashAlg); + if (context == NULL) return SECFailure; + + rv = PK11_DigestBegin(context); + if (rv != SECSuccess) { + PK11_DestroyContext(context, PR_TRUE); + return rv; + } + + rv = PK11_DigestOp(context, in, len); + if (rv != SECSuccess) { + PK11_DestroyContext(context, PR_TRUE); + return rv; + } + + /* we need the output length ... maybe this should be table driven...*/ + switch (hashAlg) { + case SEC_OID_SHA1: max_length = SHA1_LENGTH; break; + case SEC_OID_MD2: max_length = MD2_LENGTH; break; + case SEC_OID_MD5: max_length = MD5_LENGTH; break; + default: max_length = 16; break; + } + + rv = PK11_DigestFinal(context,out,&out_length,max_length); + PK11_DestroyContext(context, PR_TRUE); + return rv; +} + + +/* + * execute a bulk encryption operation + */ +SECStatus +PK11_CipherOp(PK11Context *context, unsigned char * out, int *outlen, + int maxout, unsigned char *in, int inlen) +{ + CK_RV crv = CKR_OK; + CK_ULONG length = maxout; + CK_ULONG offset =0; + PK11SymKey *symKey = context->key; + SECStatus rv = SECSuccess; + unsigned char *saveOut = out; + unsigned char *allocOut = NULL; + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context,context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + /* + * The fortezza hack is to send 8 extra bytes on the first encrypted and + * loose them on the first decrypt. + */ + if (context->fortezzaHack) { + unsigned char random[8]; + if (context->operation == CKA_ENCRYPT) { + PK11_ExitContextMonitor(context); + rv = PK11_GenerateRandom(random,sizeof(random)); + PK11_EnterContextMonitor(context); + + /* since we are offseting the output, we can't encrypt back into + * the same buffer... allocate a temporary buffer just for this + * call. */ + allocOut = out = (unsigned char*)PORT_Alloc(maxout); + if (out == NULL) { + PK11_ExitContextMonitor(context); + return SECFailure; + } + crv = PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, + random,sizeof(random),out,&length); + + out += length; + maxout -= length; + offset = length; + } else if (context->operation == CKA_DECRYPT) { + length = sizeof(random); + crv = PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, + in,sizeof(random),random,&length); + inlen -= length; + in += length; + context->fortezzaHack = PR_FALSE; + } + } + + switch (context->operation) { + case CKA_ENCRYPT: + length = maxout; + crv=PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, + in, inlen, out, &length); + length += offset; + break; + case CKA_DECRYPT: + length = maxout; + crv=PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, + in, inlen, out, &length); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + *outlen = 0; + rv = SECFailure; + } else { + *outlen = length; + } + + if (context->fortezzaHack) { + if (context->operation == CKA_ENCRYPT) { + PORT_Assert(allocOut); + PORT_Memcpy(saveOut, allocOut, length); + PORT_Free(allocOut); + } + context->fortezzaHack = PR_FALSE; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context,context->savedData, + &context->savedLength); + if (context->savedData == NULL) rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * execute a digest/signature operation + */ +SECStatus +PK11_DigestOp(PK11Context *context, const unsigned char * in, unsigned inLen) +{ + CK_RV crv = CKR_OK; + SECStatus rv = SECSuccess; + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + context->init = PR_FALSE; + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context,context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + switch (context->operation) { + /* also for MAC'ing */ + case CKA_SIGN: + crv=PK11_GETTAB(context->slot)->C_SignUpdate(context->session, + (unsigned char *)in, + inLen); + break; + case CKA_VERIFY: + crv=PK11_GETTAB(context->slot)->C_VerifyUpdate(context->session, + (unsigned char *)in, + inLen); + break; + case CKA_DIGEST: + crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, + (unsigned char *)in, + inLen); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + rv = SECFailure; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context,context->savedData, + &context->savedLength); + if (context->savedData == NULL) rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * Digest a key if possible./ + */ +SECStatus +PK11_DigestKey(PK11Context *context, PK11SymKey *key) +{ + CK_RV crv = CKR_OK; + SECStatus rv = SECSuccess; + PK11SymKey *newKey = NULL; + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + if (context->slot != key->slot) { + newKey = pk11_CopyToSlot(context->slot,CKM_SSL3_SHA1_MAC,CKA_SIGN,key); + } else { + newKey = PK11_ReferenceSymKey(key); + } + + context->init = PR_FALSE; + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context,context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + PK11_FreeSymKey(newKey); + return rv; + } + } + + + if (newKey == NULL) { + crv = CKR_KEY_TYPE_INCONSISTENT; + if (key->data.data) { + crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, + key->data.data,key->data.len); + } + } else { + crv=PK11_GETTAB(context->slot)->C_DigestKey(context->session, + newKey->objectID); + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + rv = SECFailure; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context,context->savedData, + &context->savedLength); + if (context->savedData == NULL) rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + if (newKey) PK11_FreeSymKey(newKey); + return rv; +} + +/* + * externally callable version of the lowercase pk11_finalize(). + */ +SECStatus +PK11_Finalize(PK11Context *context) { + SECStatus rv; + + PK11_EnterContextMonitor(context); + rv = pk11_Finalize(context); + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * clean up a cipher operation, so the session can be used by + * someone new. + */ +SECStatus +pk11_Finalize(PK11Context *context) +{ + CK_ULONG count = 0; + CK_RV crv; + + if (!context->ownSession) { + return SECSuccess; + } + + switch (context->operation) { + case CKA_ENCRYPT: + crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, + NULL,&count); + break; + case CKA_DECRYPT: + crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, + NULL,&count); + break; + case CKA_SIGN: + crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session, + NULL,&count); + break; + case CKA_VERIFY: + crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, + NULL,count); + break; + case CKA_DIGEST: + crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session, + NULL,&count); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * Return the final digested or signed data... + * this routine can either take pre initialized data, or allocate data + * either out of an arena or out of the standard heap. + */ +SECStatus +PK11_DigestFinal(PK11Context *context,unsigned char *data, + unsigned int *outLen, unsigned int length) +{ + CK_ULONG len; + CK_RV crv; + SECStatus rv; + + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context,context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + len = length; + switch (context->operation) { + case CKA_SIGN: + crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session, + data,&len); + break; + case CKA_VERIFY: + crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, + data,len); + break; + case CKA_DIGEST: + crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session, + data,&len); + break; + case CKA_ENCRYPT: + crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, + data, &len); + break; + case CKA_DECRYPT: + crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, + data, &len); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + PK11_ExitContextMonitor(context); + + *outLen = (unsigned int) len; + context->init = PR_FALSE; /* allow Begin to start up again */ + + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/**************************************************************************** + * + * Now Do The PBE Functions Here... + * + ****************************************************************************/ + +SECAlgorithmID * +PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt) +{ + SECAlgorithmID *algid; + + algid = SEC_PKCS5CreateAlgorithmID(algorithm, salt, iteration); + return algid; +} + +PK11SymKey * +PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem, + PRBool faulty3DES, void *wincx) +{ + /* pbe stuff */ + CK_PBE_PARAMS *pbe_params; + CK_MECHANISM_TYPE type; + SECItem *mech; + PK11SymKey *symKey; + + mech = PK11_ParamFromAlgid(algid); + type = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm)); + if(faulty3DES && (type == CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC)) { + type = CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC; + } + if(mech == NULL) { + return NULL; + } + + pbe_params = (CK_PBE_PARAMS *)mech->data; + pbe_params->pPassword = (CK_CHAR_PTR)PORT_ZAlloc(pwitem->len); + if(pbe_params->pPassword != NULL) { + PORT_Memcpy(pbe_params->pPassword, pwitem->data, pwitem->len); + pbe_params->ulPasswordLen = pwitem->len; + } else { + SECITEM_ZfreeItem(mech, PR_TRUE); + return NULL; + } + + symKey = PK11_KeyGen(slot, type, mech, 0, wincx); + + PORT_ZFree(pbe_params->pPassword, pwitem->len); + SECITEM_ZfreeItem(mech, PR_TRUE); + return symKey; +} + + +SECStatus +PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot, + SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem, + SECItem *nickname, SECItem *publicValue, PRBool isPerm, + PRBool isPrivate, KeyType keyType, unsigned int keyUsage, + void *wincx) +{ + CK_MECHANISM_TYPE mechanism; + SECItem *pbe_param, crypto_param; + PK11SymKey *key = NULL; + SECStatus rv = SECSuccess; + CK_MECHANISM cryptoMech, pbeMech; + CK_RV crv; + SECKEYPrivateKey *privKey = NULL; + PRBool faulty3DES = PR_FALSE; + int usageCount; + CK_KEY_TYPE key_type; + CK_ATTRIBUTE_TYPE *usage; + CK_ATTRIBUTE_TYPE rsaUsage[] = { + CKA_UNWRAP, CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER }; + CK_ATTRIBUTE_TYPE dsaUsage[] = { CKA_SIGN }; + CK_ATTRIBUTE_TYPE dhUsage[] = { CKA_DERIVE }; + + if((epki == NULL) || (pwitem == NULL)) + return SECFailure; + + crypto_param.data = NULL; + + mechanism = PK11_AlgtagToMechanism(SECOID_FindOIDTag( + &epki->algorithm.algorithm)); + + switch (keyType) { + default: + case rsaKey: + key_type = CKK_RSA; + switch (keyUsage & (KU_KEY_ENCIPHERMENT|KU_DIGITAL_SIGNATURE)) { + case KU_KEY_ENCIPHERMENT: + usage = rsaUsage; + usageCount = 2; + break; + case KU_DIGITAL_SIGNATURE: + usage = &rsaUsage[2]; + usageCount = 2; + break; + case KU_KEY_ENCIPHERMENT|KU_DIGITAL_SIGNATURE: + case 0: /* default to everything */ + usage = rsaUsage; + usageCount = 4; + break; + } + break; + case dhKey: + key_type = CKK_DH; + usage = dhUsage; + usageCount = sizeof(dhUsage)/sizeof(dhUsage[0]); + break; + case dsaKey: + key_type = CKK_DSA; + usage = dsaUsage; + usageCount = sizeof(dsaUsage)/sizeof(dsaUsage[0]); + break; + } + +try_faulty_3des: + pbe_param = PK11_ParamFromAlgid(&epki->algorithm); + + key = PK11_PBEKeyGen(slot, &epki->algorithm, pwitem, faulty3DES, wincx); + if((key == NULL) || (pbe_param == NULL)) { + rv = SECFailure; + goto done; + } + + pbeMech.mechanism = mechanism; + pbeMech.pParameter = pbe_param->data; + pbeMech.ulParameterLen = pbe_param->len; + + crv = PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, + pwitem, faulty3DES); + if(crv != CKR_OK) { + rv = SECFailure; + goto done; + } + + cryptoMech.mechanism = PK11_GetPadMechanism(cryptoMech.mechanism); + crypto_param.data = (unsigned char*)cryptoMech.pParameter; + crypto_param.len = cryptoMech.ulParameterLen; + + privKey = PK11_UnwrapPrivKey(slot, key, cryptoMech.mechanism, + &crypto_param, &epki->encryptedData, + nickname, publicValue, isPerm, isPrivate, + key_type, usage, usageCount, wincx); + if(privKey) { + SECKEY_DestroyPrivateKey(privKey); + privKey = NULL; + rv = SECSuccess; + goto done; + } + + /* if we are unable to import the key and the mechanism is + * CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC, then it is possible that + * the encrypted blob was created with a buggy key generation method + * which is described in the PKCS 12 implementation notes. So we + * need to try importing via that method. + */ + if((mechanism == CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC) && (!faulty3DES)) { + /* clean up after ourselves before redoing the key generation. */ + + PK11_FreeSymKey(key); + key = NULL; + + if(pbe_param) { + SECITEM_ZfreeItem(pbe_param, PR_TRUE); + pbe_param = NULL; + } + + if(crypto_param.data) { + SECITEM_ZfreeItem(&crypto_param, PR_FALSE); + crypto_param.data = NULL; + cryptoMech.pParameter = NULL; + crypto_param.len = cryptoMech.ulParameterLen = 0; + } + + faulty3DES = PR_TRUE; + goto try_faulty_3des; + } + + /* key import really did fail */ + rv = SECFailure; + +done: + if(pbe_param != NULL) { + SECITEM_ZfreeItem(pbe_param, PR_TRUE); + pbe_param = NULL; + } + + if(crypto_param.data != NULL) { + SECITEM_ZfreeItem(&crypto_param, PR_FALSE); + } + + if(key != NULL) { + PK11_FreeSymKey(key); + } + + return rv; +} + +/* + * import a private key info into the desired slot + */ +SECStatus +PK11_ImportPrivateKeyInfo(PK11SlotInfo *slot, SECKEYPrivateKeyInfo *pki, + SECItem *nickname, SECItem *publicValue, PRBool isPerm, + PRBool isPrivate, unsigned int keyUsage, void *wincx) +{ + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE keyType = CKK_RSA; + CK_OBJECT_HANDLE objectID; + CK_ATTRIBUTE theTemplate[20]; + int templateCount = 0; + SECStatus rv = SECFailure; + SECKEYLowPrivateKey *lpk = NULL; + const SEC_ASN1Template *keyTemplate, *paramTemplate; + void *paramDest = NULL; + PRArenaPool *arena; + CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE *signedattr = NULL; + int signedcount = 0; + CK_ATTRIBUTE *ap; + SECItem *ck_id = NULL; + + arena = PORT_NewArena(2048); + if(!arena) { + return SECFailure; + } + + /* need to change this to use RSA/DSA keys */ + lpk = (SECKEYLowPrivateKey *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYLowPrivateKey)); + if(lpk == NULL) { + goto loser; + } + lpk->arena = arena; + + attrs = theTemplate; + switch(SECOID_GetAlgorithmTag(&pki->algorithm)) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + keyTemplate = SECKEY_RSAPrivateKeyTemplate; + paramTemplate = NULL; + paramDest = NULL; + lpk->keyType = rsaKey; + keyType = CKK_RSA; + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + if(!publicValue) { + goto loser; + } + keyTemplate = SECKEY_DSAPrivateKeyExportTemplate; + paramTemplate = SECKEY_PQGParamsTemplate; + paramDest = &(lpk->u.dsa.params); + lpk->keyType = dsaKey; + keyType = CKK_DSA; + break; + case SEC_OID_X942_DIFFIE_HELMAN_KEY: + if(!publicValue) { + goto loser; + } + keyTemplate = SECKEY_DHPrivateKeyExportTemplate; + paramTemplate = NULL; + paramDest = NULL; + lpk->keyType = dhKey; + keyType = CKK_DH; + break; + + default: + keyTemplate = NULL; + paramTemplate = NULL; + paramDest = NULL; + break; + } + + if(!keyTemplate) { + goto loser; + } + + /* decode the private key and any algorithm parameters */ + rv = SEC_ASN1DecodeItem(arena, lpk, keyTemplate, &pki->privateKey); + if(rv != SECSuccess) { + goto loser; + } + if(paramDest && paramTemplate) { + rv = SEC_ASN1DecodeItem(arena, paramDest, paramTemplate, + &(pki->algorithm.parameters)); + if(rv != SECSuccess) { + goto loser; + } + } + + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, isPerm ? &cktrue : &ckfalse, + sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_SENSITIVE, isPrivate ? &cktrue : &ckfalse, + sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_PRIVATE, isPrivate ? &cktrue : &ckfalse, + sizeof(CK_BBOOL) ); attrs++; + + switch (lpk->keyType) { + case rsaKey: + PK11_SETATTRS(attrs, CKA_UNWRAP, (keyUsage & KU_KEY_ENCIPHERMENT) ? + &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_DECRYPT, (keyUsage & KU_DATA_ENCIPHERMENT) ? + &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? + &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, + (keyUsage & KU_DIGITAL_SIGNATURE) ? + &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++; + ck_id = PK11_MakeIDFromPubKey(&lpk->u.rsa.modulus); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data,ck_id->len); attrs++; + if (nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); attrs++; + } + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_MODULUS, lpk->u.rsa.modulus.data, + lpk->u.rsa.modulus.len); attrs++; + PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, + lpk->u.rsa.publicExponent.data, + lpk->u.rsa.publicExponent.len); attrs++; + PK11_SETATTRS(attrs, CKA_PRIVATE_EXPONENT, + lpk->u.rsa.privateExponent.data, + lpk->u.rsa.privateExponent.len); attrs++; + PK11_SETATTRS(attrs, CKA_PRIME_1, + lpk->u.rsa.prime1.data, + lpk->u.rsa.prime1.len); attrs++; + PK11_SETATTRS(attrs, CKA_PRIME_2, + lpk->u.rsa.prime2.data, + lpk->u.rsa.prime2.len); attrs++; + PK11_SETATTRS(attrs, CKA_EXPONENT_1, + lpk->u.rsa.exponent1.data, + lpk->u.rsa.exponent1.len); attrs++; + PK11_SETATTRS(attrs, CKA_EXPONENT_2, + lpk->u.rsa.exponent2.data, + lpk->u.rsa.exponent2.len); attrs++; + PK11_SETATTRS(attrs, CKA_COEFFICIENT, + lpk->u.rsa.coefficient.data, + lpk->u.rsa.coefficient.len); attrs++; + break; + case dsaKey: + /* To make our intenal PKCS #11 module work correctly with + * our database, we need to pass in the public key value for + * this dsa key. We have a netscape only CKA_ value to do this. + * Only send it to internal slots */ + if (PK11_IsInternal(slot)) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_DB, + publicValue->data, publicValue->len); attrs++; + } + PK11_SETATTRS(attrs, CKA_SIGN, &cktrue, sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &cktrue, sizeof(CK_BBOOL)); attrs++; + if(nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); + attrs++; + } + ck_id = PK11_MakeIDFromPubKey(publicValue); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data,ck_id->len); attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dsa.params.prime.data, + lpk->u.dsa.params.prime.len); attrs++; + PK11_SETATTRS(attrs,CKA_SUBPRIME,lpk->u.dsa.params.subPrime.data, + lpk->u.dsa.params.subPrime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dsa.params.base.data, + lpk->u.dsa.params.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dsa.privateValue.data, + lpk->u.dsa.privateValue.len); attrs++; + break; + case dhKey: + /* To make our intenal PKCS #11 module work correctly with + * our database, we need to pass in the public key value for + * this dh key. We have a netscape only CKA_ value to do this. + * Only send it to internal slots */ + if (PK11_IsInternal(slot)) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_DB, + publicValue->data, publicValue->len); attrs++; + } + PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL)); attrs++; + if(nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); + attrs++; + } + ck_id = PK11_MakeIDFromPubKey(publicValue); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data,ck_id->len); attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dh.prime.data, + lpk->u.dh.prime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dh.base.data, + lpk->u.dh.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dh.privateValue.data, + lpk->u.dh.privateValue.len); attrs++; + break; + /* what about fortezza??? */ + default: + PORT_SetError(SEC_ERROR_BAD_KEY); + goto loser; + } + templateCount = attrs - theTemplate; + PR_ASSERT(templateCount <= sizeof(theTemplate)/sizeof(CK_ATTRIBUTE)); + signedcount = attrs - signedattr; + + for (ap=signedattr; signedcount; ap++, signedcount--) { + pk11_SignedToUnsigned(ap); + } + + rv = PK11_CreateNewObject(slot,theTemplate,templateCount, isPerm, &objectID); + + if (ck_id) { + SECITEM_ZfreeItem(ck_id, PR_TRUE); + } + +loser: + if (lpk!= NULL) { + SECKEY_LowDestroyPrivateKey(lpk); + } + + return rv; +} + +SECKEYPrivateKeyInfo * +PK11_ExportPrivateKeyInfo(CERTCertificate *cert, void *wincx) +{ + return NULL; +} + +static int +pk11_private_key_encrypt_buffer_length(SECKEYPrivateKey *key) + +{ + CK_ATTRIBUTE rsaTemplate = { CKA_MODULUS, NULL, 0 }; + CK_ATTRIBUTE dsaTemplate = { CKA_PRIME, NULL, 0 }; + CK_ATTRIBUTE_PTR pTemplate; + CK_RV crv; + int length; + + if(!key) { + return -1; + } + + switch (key->keyType) { + case rsaKey: + pTemplate = &rsaTemplate; + break; + case dsaKey: + case dhKey: + pTemplate = &dsaTemplate; + break; + case fortezzaKey: + default: + pTemplate = NULL; + } + + if(!pTemplate) { + return -1; + } + + crv = PK11_GetAttributes(NULL, key->pkcs11Slot, key->pkcs11ID, + pTemplate, 1); + if(crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return -1; + } + + length = pTemplate->ulValueLen; + length *= 10; + + if(pTemplate->pValue != NULL) { + PORT_Free(pTemplate->pValue); + } + + return length; +} + +SECKEYEncryptedPrivateKeyInfo * +PK11_ExportEncryptedPrivateKeyInfo(PK11SlotInfo *slot, SECOidTag algTag, + SECItem *pwitem, CERTCertificate *cert, int iteration, void *wincx) +{ + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + SECKEYPrivateKey *pk; + PRArenaPool *arena = NULL; + SECAlgorithmID *algid; + CK_MECHANISM_TYPE mechanism; + SECItem *pbe_param = NULL, crypto_param; + PK11SymKey *key = NULL; + SECStatus rv = SECSuccess; + CK_MECHANISM pbeMech, cryptoMech; + CK_RV crv; + SECItem encryptedKey = {siBuffer,NULL,0}; + int encryptBufLen; + + if(!pwitem) + return NULL; + + crypto_param.data = NULL; + + arena = PORT_NewArena(2048); + epki = (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if(epki == NULL) { + rv = SECFailure; + goto loser; + } + epki->arena = arena; + algid = SEC_PKCS5CreateAlgorithmID(algTag, NULL, iteration); + if(algid == NULL) { + rv = SECFailure; + goto loser; + } + + mechanism = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm)); + pbe_param = PK11_ParamFromAlgid(algid); + pbeMech.mechanism = mechanism; + pbeMech.pParameter = pbe_param->data; + pbeMech.ulParameterLen = pbe_param->len; + key = PK11_PBEKeyGen(slot, algid, pwitem, PR_FALSE, wincx); + + if((key == NULL) || (pbe_param == NULL)) { + rv = SECFailure; + goto loser; + } + + crv = PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, + pwitem, PR_FALSE); + if(crv != CKR_OK) { + rv = SECFailure; + goto loser; + } + cryptoMech.mechanism = PK11_GetPadMechanism(cryptoMech.mechanism); + crypto_param.data = (unsigned char *)cryptoMech.pParameter; + crypto_param.len = cryptoMech.ulParameterLen; + + pk = PK11_FindKeyByAnyCert(cert, wincx); + if(pk == NULL) { + rv = SECFailure; + goto loser; + } + + encryptBufLen = pk11_private_key_encrypt_buffer_length(pk); + if(encryptBufLen == -1) { + rv = SECFailure; + goto loser; + } + encryptedKey.len = (unsigned int)encryptBufLen; + encryptedKey.data = (unsigned char *)PORT_ZAlloc(encryptedKey.len); + if(!encryptedKey.data) { + rv = SECFailure; + goto loser; + } + + /* we are extracting an encrypted privateKey structure. + * which needs to be freed along with the buffer into which it is + * returned. eventually, we should retrieve an encrypted key using + * pkcs8/pkcs5. + */ + PK11_EnterSlotMonitor(pk->pkcs11Slot); + crv = PK11_GETTAB(pk->pkcs11Slot)->C_WrapKey(pk->pkcs11Slot->session, + &cryptoMech, key->objectID, pk->pkcs11ID, encryptedKey.data, + (CK_ULONG_PTR)(&encryptedKey.len)); + PK11_ExitSlotMonitor(pk->pkcs11Slot); + if(crv != CKR_OK) { + rv = SECFailure; + goto loser; + } + + if(!encryptedKey.len) { + rv = SECFailure; + goto loser; + } + + rv = SECITEM_CopyItem(arena, &epki->encryptedData, &encryptedKey); + if(rv != SECSuccess) { + goto loser; + } + + rv = SECOID_CopyAlgorithmID(arena, &epki->algorithm, algid); + +loser: + if(pbe_param != NULL) { + SECITEM_ZfreeItem(pbe_param, PR_TRUE); + pbe_param = NULL; + } + + if(crypto_param.data != NULL) { + SECITEM_ZfreeItem(&crypto_param, PR_FALSE); + crypto_param.data = NULL; + } + + if(key != NULL) { + PK11_FreeSymKey(key); + } + + if(rv == SECFailure) { + if(arena != NULL) { + PORT_FreeArena(arena, PR_TRUE); + } + epki = NULL; + } + + return epki; +} + + +/* + * This is required to allow FORTEZZA_NULL and FORTEZZA_RC4 + * working. This function simply gets a valid IV for the keys. + */ +SECStatus +PK11_GenerateFortezzaIV(PK11SymKey *symKey,unsigned char *iv,int len) +{ + CK_MECHANISM mech_info; + CK_ULONG count = 0; + CK_RV crv; + SECStatus rv = SECFailure; + + mech_info.mechanism = CKM_SKIPJACK_CBC64; + mech_info.pParameter = iv; + mech_info.ulParameterLen = len; + + /* generate the IV for fortezza */ + PK11_EnterSlotMonitor(symKey->slot); + crv=PK11_GETTAB(symKey->slot)->C_EncryptInit(symKey->slot->session, + &mech_info, symKey->objectID); + if (crv == CKR_OK) { + PK11_GETTAB(symKey->slot)->C_EncryptFinal(symKey->slot->session, + NULL, &count); + rv = SECSuccess; + } + PK11_ExitSlotMonitor(symKey->slot); + return rv; +} + +SECKEYPrivateKey * +PK11_UnwrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey, + CK_MECHANISM_TYPE wrapType, SECItem *param, + SECItem *wrappedKey, SECItem *label, + SECItem *idValue, PRBool perm, PRBool sensitive, + CK_KEY_TYPE keyType, CK_ATTRIBUTE_TYPE *usage, int usageCount, + void *wincx) +{ + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; + CK_ATTRIBUTE keyTemplate[15] ; + int templateCount = 0; + CK_OBJECT_HANDLE privKeyID; + CK_MECHANISM mechanism; + CK_ATTRIBUTE *attrs = keyTemplate; + SECItem *param_free = NULL, *ck_id; + CK_RV crv; + CK_SESSION_HANDLE rwsession; + PK11SymKey *newKey = NULL; + int i; + + if(!slot || !wrappedKey || !idValue) { + /* SET AN ERROR!!! */ + return NULL; + } + + ck_id = PK11_MakeIDFromPubKey(idValue); + if(!ck_id) { + return NULL; + } + + PK11_SETATTRS(attrs, CKA_TOKEN, perm ? &cktrue : &ckfalse, + sizeof(cktrue)); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass)); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); attrs++; + PK11_SETATTRS(attrs, CKA_PRIVATE, sensitive ? &cktrue : &ckfalse, + sizeof(cktrue)); attrs++; + PK11_SETATTRS(attrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse, + sizeof(cktrue)); attrs++; + PK11_SETATTRS(attrs, CKA_LABEL, label->data, label->len); attrs++; + PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len); attrs++; + for (i=0; i < usageCount; i++) { + PK11_SETATTRS(attrs, usage[i], &cktrue, sizeof(cktrue)); attrs++; + } + + if (PK11_IsInternal(slot)) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_DB, idValue->data, + idValue->len); attrs++; + } + + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= (sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE)) ); + + mechanism.mechanism = wrapType; + if(!param) param = param_free= PK11_ParamFromIV(wrapType, NULL); + if(param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } else { + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + } + + if (wrappingKey->slot != slot) { + newKey = pk11_CopyToSlot(slot,wrapType,CKA_WRAP,wrappingKey); + } else { + newKey = PK11_ReferenceSymKey(wrappingKey); + } + + if (newKey) { + if (perm) { + rwsession = PK11_GetRWSession(slot); + } else { + rwsession = slot->session; + } + crv = PK11_GETTAB(slot)->C_UnwrapKey(rwsession, &mechanism, + newKey->objectID, + wrappedKey->data, + wrappedKey->len, keyTemplate, + templateCount, &privKeyID); + + if (perm) PK11_RestoreROSession(slot, rwsession); + PK11_FreeSymKey(newKey); + } else { + crv = CKR_FUNCTION_NOT_SUPPORTED; + } + + if(ck_id) { + SECITEM_FreeItem(ck_id, PR_TRUE); + ck_id = NULL; + } + + if (crv != CKR_OK) { + /* we couldn't unwrap the key, use the internal module to do the + * unwrap, then load the new key into the token */ + PK11SlotInfo *int_slot = PK11_GetInternalSlot(); + + if (int_slot && (slot != int_slot)) { + SECKEYPrivateKey *privKey = PK11_UnwrapPrivKey(int_slot, + wrappingKey, wrapType, param, wrappedKey, label, + idValue, PR_FALSE, PR_FALSE, + keyType, usage, usageCount, wincx); + if (privKey) { + SECKEYPrivateKey *newPrivKey = pk11_loadPrivKey(slot,privKey, + NULL,perm,sensitive); + SECKEY_DestroyPrivateKey(privKey); + PK11_FreeSlot(int_slot); + return newPrivKey; + } + } + if (int_slot) PK11_FreeSlot(int_slot); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + return PK11_MakePrivKey(slot, nullKey, PR_FALSE, privKeyID, wincx); +} + +#define ALLOC_BLOCK 10 + +/* + * Now we're going to wrap a SECKEYPrivateKey with a PK11SymKey + * The strategy is to get both keys to reside in the same slot, + * one that can perform the desired crypto mechanism and then + * call C_WrapKey after all the setup has taken place. + */ +SECStatus +PK11_WrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey, + SECKEYPrivateKey *privKey, CK_MECHANISM_TYPE wrapType, + SECItem *param, SECItem *wrappedKey, void *wincx) +{ + PK11SlotInfo *privSlot = privKey->pkcs11Slot; /* The slot where + * the private key + * we are going to + * wrap lives. + */ + PK11SymKey *newSymKey = NULL; + SECKEYPrivateKey *newPrivKey = NULL; + SECItem *param_free = NULL; + CK_ULONG len = wrappedKey->len; + CK_MECHANISM mech; + CK_RV crv; + + if (!privSlot || !PK11_DoesMechanism(privSlot, wrapType)) { + /* Figure out a slot that does the mechanism and try to import + * the private key onto that slot. + */ + PK11SlotInfo *int_slot = PK11_GetInternalSlot(); + + privSlot = int_slot; /* The private key has a new home */ + newPrivKey = pk11_loadPrivKey(privSlot,privKey,NULL,PR_FALSE,PR_FALSE); + if (newPrivKey == NULL) { + PK11_FreeSlot (int_slot); + return SECFailure; + } + privKey = newPrivKey; + } + + if (privSlot != wrappingKey->slot) { + newSymKey = pk11_CopyToSlot (privSlot, wrapType, CKA_WRAP, + wrappingKey); + wrappingKey = newSymKey; + } + + if (wrappingKey == NULL) { + if (newPrivKey) { + SECKEY_DestroyPrivateKey(newPrivKey); + } + return SECFailure; + } + mech.mechanism = wrapType; + if (!param) { + param = param_free = PK11_ParamFromIV(wrapType, NULL); + } + if (param) { + mech.pParameter = param->data; + mech.ulParameterLen = param->len; + } else { + mech.pParameter = NULL; + mech.ulParameterLen = 0; + } + + PK11_EnterSlotMonitor(privSlot); + crv = PK11_GETTAB(privSlot)->C_WrapKey(privSlot->session, &mech, + wrappingKey->objectID, + privKey->pkcs11ID, + wrappedKey->data, &len); + PK11_ExitSlotMonitor(privSlot); + + if (newSymKey) { + PK11_FreeSymKey(newSymKey); + } + if (newPrivKey) { + SECKEY_DestroyPrivateKey(newPrivKey); + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + + wrappedKey->len = len; + return SECSuccess; +} + +void +PK11_SetFortezzaHack(PK11SymKey *symKey) { + symKey->origin = PK11_OriginFortezzaHack; +} + diff --git a/security/nss/lib/pk11wrap/pk11slot.c b/security/nss/lib/pk11wrap/pk11slot.c new file mode 100644 index 000000000..0cfff2344 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11slot.c @@ -0,0 +1,4244 @@ +/* + * 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. + */ +/* + * Deal with PKCS #11 Slots. + */ +#include "seccomon.h" +#include "secmod.h" +#include "prlock.h" +#include "secmodi.h" +#include "pkcs11t.h" +#include "pk11func.h" +#include "cert.h" +#include "key.h" +#include "secitem.h" +#include "secder.h" +#include "secasn1.h" +#include "secoid.h" +#include "prtime.h" +#include "prlong.h" +#include "secerr.h" + +/************************************************************* + * local static and global data + *************************************************************/ + +/* + * This array helps parsing between names, mechanisms, and flags. + * to make the config files understand more entries, add them + * to this table. (NOTE: we need function to export this table and it's size) + */ +PK11DefaultArrayEntry PK11_DefaultArray[] = { + { "RSA", SECMOD_RSA_FLAG, CKM_RSA_PKCS }, + { "DSA", SECMOD_DSA_FLAG, CKM_DSA }, + { "DH", SECMOD_DH_FLAG, CKM_DH_PKCS_DERIVE }, + { "RC2", SECMOD_RC2_FLAG, CKM_RC2_CBC }, + { "RC4", SECMOD_RC4_FLAG, CKM_RC4 }, + { "DES", SECMOD_DES_FLAG, CKM_DES_CBC }, + { "RC5", SECMOD_RC5_FLAG, CKM_RC5_CBC }, + { "SHA-1", SECMOD_SHA1_FLAG, CKM_SHA_1 }, + { "MD5", SECMOD_MD5_FLAG, CKM_MD5 }, + { "MD2", SECMOD_MD2_FLAG, CKM_MD2 }, + { "SKIPJACK", SECMOD_FORTEZZA_FLAG, CKM_SKIPJACK_CBC64 }, + { "Publicly-readable certs", SECMOD_FRIENDLY_FLAG, CKM_INVALID_MECHANISM }, + { "Random Num Generator", SECMOD_RANDOM_FLAG, CKM_FAKE_RANDOM }, +}; +int num_pk11_default_mechanisms = sizeof(PK11_DefaultArray) / sizeof(PK11_DefaultArray[0]); + +/* + * These slotlists are lists of modules which provide default support for + * a given algorithm or mechanism. + */ +static PK11SlotList pk11_desSlotList, + pk11_rc4SlotList, + pk11_rc2SlotList, + pk11_rc5SlotList, + pk11_sha1SlotList, + pk11_md5SlotList, + pk11_md2SlotList, + pk11_rsaSlotList, + pk11_dsaSlotList, + pk11_dhSlotList, + pk11_idea, + pk11_random; + +/* + * Tables used for Extended mechanism mapping (currently not used) + */ +typedef struct { + CK_MECHANISM_TYPE keyGen; + CK_KEY_TYPE keyType; + CK_MECHANISM_TYPE type; + int blockSize; + int iv; +} pk11MechanismData; + +static pk11MechanismData pk11_default = + { CKM_GENERIC_SECRET_KEY_GEN, CKK_GENERIC_SECRET, CKM_FAKE_RANDOM, 8, 8 }; +static pk11MechanismData *pk11_MechanismTable = NULL; +static int pk11_MechTableSize = 0; +static int pk11_MechEntrySize = 0; + +/* + * list of mechanisms we're willing to wrap secret keys with. + * This list is ordered by preference. + */ +CK_MECHANISM_TYPE wrapMechanismList[] = { + CKM_DES3_ECB, + CKM_CAST5_ECB, + CKM_DES_ECB, + CKM_KEY_WRAP_LYNKS, + CKM_IDEA_ECB, + CKM_CAST3_ECB, + CKM_CAST_ECB, + CKM_RC5_ECB, + CKM_RC2_ECB, + CKM_CDMF_ECB, + CKM_SKIPJACK_WRAP, +}; + +int wrapMechanismCount = sizeof(wrapMechanismList)/sizeof(wrapMechanismList[0]); + +/* + * This structure keeps track of status that spans all the Slots. + * NOTE: This is a global data structure. It semantics expect thread crosstalk + * be very careful when you see it used. + * It's major purpose in life is to allow the user to log in one PER + * Tranaction, even if a transaction spans threads. The problem is the user + * may have to enter a password one just to be able to look at the + * personalities/certificates (s)he can use. Then if Auth every is one, they + * may have to enter the password again to use the card. See PK11_StartTransac + * and PK11_EndTransaction. + */ +static struct PK11GlobalStruct { + int transaction; + PRBool inTransaction; + char *(*getPass)(PK11SlotInfo *,PRBool,void *); + PRBool (*verifyPass)(PK11SlotInfo *,void *); + PRBool (*isLoggedIn)(PK11SlotInfo *,void *); +} PK11_Global = { 1, PR_FALSE, NULL, NULL, NULL }; + +/************************************************************ + * Generic Slot List and Slot List element manipulations + ************************************************************/ + +/* + * allocate a new list + */ +PK11SlotList * +PK11_NewSlotList(void) +{ + PK11SlotList *list; + + list = (PK11SlotList *)PORT_Alloc(sizeof(PK11SlotList)); + if (list == NULL) return NULL; + list->head = NULL; + list->tail = NULL; +#ifdef PKCS11_USE_THREADS + list->lock = PR_NewLock(); + if (list->lock == NULL) { + PORT_Free(list); + return NULL; + } +#else + list->lock = NULL; +#endif + + return list; +} + +/* + * free a list element when all the references go away. + */ +static void +pk11_FreeListElement(PK11SlotList *list, PK11SlotListElement *le) +{ + PRBool freeit = PR_FALSE; + + PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));) + if (le->refCount-- == 1) { + freeit = PR_TRUE; + } + PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));) + if (freeit) { + PK11_FreeSlot(le->slot); + PORT_Free(le); + } +} + +/* + * if we are freeing the list, we must be the only ones with a pointer + * to the list. + */ +void +PK11_FreeSlotList(PK11SlotList *list) +{ + PK11SlotListElement *le, *next ; + if (list == NULL) return; + + for (le = list->head ; le; le = next) { + next = le->next; + pk11_FreeListElement(list,le); + } + PK11_USE_THREADS(PR_DestroyLock((PRLock *)(list->lock));) + PORT_Free(list); +} + +/* + * add a slot to a list + */ +SECStatus +PK11_AddSlotToList(PK11SlotList *list,PK11SlotInfo *slot) +{ + PK11SlotListElement *le; + + le = (PK11SlotListElement *) PORT_Alloc(sizeof(PK11SlotListElement)); + if (le == NULL) return SECFailure; + + le->slot = PK11_ReferenceSlot(slot); + le->prev = NULL; + le->refCount = 1; + PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));) + if (list->head) list->head->prev = le; else list->tail = le; + le->next = list->head; + list->head = le; + PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));) + + return SECSuccess; +} + +/* + * remove a slot entry from the list + */ +SECStatus +PK11_DeleteSlotFromList(PK11SlotList *list,PK11SlotListElement *le) +{ + PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));) + if (le->prev) le->prev->next = le->next; else list->head = le->next; + if (le->next) le->next->prev = le->prev; else list->tail = le->prev; + le->next = le->prev = NULL; + PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));) + pk11_FreeListElement(list,le); + return SECSuccess; +} + +/* + * Move a list to the end of the target list. NOTE: There is no locking + * here... This assumes BOTH lists are private copy lists. + */ +SECStatus +PK11_MoveListToList(PK11SlotList *target,PK11SlotList *src) +{ + if (src->head == NULL) return SECSuccess; + + if (target->tail == NULL) { + target->head = src->head; + } else { + target->tail->next = src->head; + } + src->head->prev = target->tail; + target->tail = src->tail; + src->head = src->tail = NULL; + return SECSuccess; +} + +/* + * get an element safely from the list. This just makes sure that if + * this element is not deleted while we deal with it. + */ +PK11SlotListElement * +PK11_GetFirstSafe(PK11SlotList *list) +{ + PK11SlotListElement *le; + + PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));) + le = list->head; + if (le != NULL) (le)->refCount++; + PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));) + return le; +} + +/* + * NOTE: if this element gets deleted, we can no longer safely traverse using + * it's pointers. We can either terminate the loop, or restart from the + * beginning. This is controlled by the restart option. + */ +PK11SlotListElement * +PK11_GetNextSafe(PK11SlotList *list, PK11SlotListElement *le, PRBool restart) +{ + PK11SlotListElement *new_le; + PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));) + new_le = le->next; + if (le->next == NULL) { + /* if the prev and next fields are NULL then either this element + * has been removed and we need to walk the list again (if restart + * is true) or this was the only element on the list */ + if ((le->prev == NULL) && restart && (list->head != le)) { + new_le = list->head; + } + } + if (new_le) new_le->refCount++; + PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));) + pk11_FreeListElement(list,le); + return new_le; +} + + +/* + * Find the element that holds this slot + */ +PK11SlotListElement * +PK11_FindSlotElement(PK11SlotList *list,PK11SlotInfo *slot) +{ + PK11SlotListElement *le; + + for (le = PK11_GetFirstSafe(list); le; + le = PK11_GetNextSafe(list,le,PR_TRUE)) { + if (le->slot == slot) return le; + } + return NULL; +} + +/************************************************************ + * Generic Slot Utilities + ************************************************************/ +/* + * Create a new slot structure + */ +PK11SlotInfo * +PK11_NewSlotInfo(void) +{ + PK11SlotInfo *slot; + + slot = (PK11SlotInfo *)PORT_Alloc(sizeof(PK11SlotInfo)); + if (slot == NULL) return slot; + +#ifdef PKCS11_USE_THREADS + slot->refLock = PR_NewLock(); + if (slot->refLock == NULL) { + PORT_Free(slot); + return slot; + } + slot->sessionLock = PR_NewLock(); + if (slot->sessionLock == NULL) { + PR_DestroyLock(slot->refLock); + PORT_Free(slot); + return slot; + } +#else + slot->sessionLock = NULL; + slot->refLock = NULL; +#endif + slot->functionList = NULL; + slot->needTest = PR_TRUE; + slot->isPerm = PR_FALSE; + slot->isHW = PR_FALSE; + slot->isInternal = PR_FALSE; + slot->isThreadSafe = PR_FALSE; + slot->disabled = PR_FALSE; + slot->series = 0; + slot->wrapKey = 0; + slot->wrapMechanism = CKM_INVALID_MECHANISM; + slot->refKeys[0] = CK_INVALID_KEY; + slot->reason = PK11_DIS_NONE; + slot->readOnly = PR_TRUE; + slot->needLogin = PR_FALSE; + slot->hasRandom = PR_FALSE; + slot->defRWSession = PR_FALSE; + slot->flags = 0; + slot->session = CK_INVALID_SESSION; + slot->slotID = 0; + slot->defaultFlags = 0; + slot->refCount = 1; + slot->askpw = 0; + slot->timeout = 0; + slot->mechanismList = NULL; + slot->mechanismCount = 0; + slot->cert_array = NULL; + slot->cert_count = 0; + slot->slot_name[0] = 0; + slot->token_name[0] = 0; + PORT_Memset(slot->serial,' ',sizeof(slot->serial)); + slot->module = NULL; + slot->authTransact = 0; + slot->authTime = LL_ZERO; + slot->minPassword = 0; + slot->maxPassword = 0; + return slot; +} + +/* create a new reference to a slot so it doesn't go away */ +PK11SlotInfo * +PK11_ReferenceSlot(PK11SlotInfo *slot) +{ + PK11_USE_THREADS(PR_Lock(slot->refLock);) + slot->refCount++; + PK11_USE_THREADS(PR_Unlock(slot->refLock);) + return slot; +} + +/* Destroy all info on a slot we have built up */ +void +PK11_DestroySlot(PK11SlotInfo *slot) +{ + /* first free up all the sessions on this slot */ + if (slot->functionList) { + PK11_GETTAB(slot)->C_CloseAllSessions(slot->slotID); + } + + /* now free up all the certificates we grabbed on this slot */ + PK11_FreeSlotCerts(slot); + + /* finally Tell our parent module that we've gone away so it can unload */ + if (slot->module) { + SECMOD_SlotDestroyModule(slot->module,PR_TRUE); + } +#ifdef PKCS11_USE_THREADS + if (slot->refLock) { + PR_DestroyLock(slot->refLock); + slot->refLock = NULL; + } + if (slot->sessionLock) { + PR_DestroyLock(slot->sessionLock); + slot->sessionLock = NULL; + } +#endif + + /* ok, well not quit finally... now we free the memory */ + PORT_Free(slot); +} + + +/* We're all done with the slot, free it */ +void +PK11_FreeSlot(PK11SlotInfo *slot) +{ + PRBool freeit = PR_FALSE; + + PK11_USE_THREADS(PR_Lock(slot->refLock);) + if (slot->refCount-- == 1) freeit = PR_TRUE; + PK11_USE_THREADS(PR_Unlock(slot->refLock);) + + if (freeit) PK11_DestroySlot(slot); +} + +void +PK11_EnterSlotMonitor(PK11SlotInfo *slot) { + PR_Lock(slot->sessionLock); +} + +void +PK11_ExitSlotMonitor(PK11SlotInfo *slot) { + PR_Unlock(slot->sessionLock); +} + +/*********************************************************** + * Functions to find specific slots. + ***********************************************************/ +PK11SlotInfo * +PK11_FindSlotByName(char *name) +{ + SECMODModuleList *mlp; + SECMODModuleList *modules = SECMOD_GetDefaultModuleList(); + SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); + int i; + PK11SlotInfo *slot = NULL; + + if ((name == NULL) || (*name == 0)) { + return PK11_GetInternalKeySlot(); + } + + /* work through all the slots */ + SECMOD_GetReadLock(moduleLock); + for(mlp = modules; mlp != NULL; mlp = mlp->next) { + for (i=0; i < mlp->module->slotCount; i++) { + PK11SlotInfo *tmpSlot = mlp->module->slots[i]; + if (PK11_IsPresent(tmpSlot)) { + if (PORT_Strcmp(tmpSlot->token_name,name) == 0) { + slot = PK11_ReferenceSlot(tmpSlot); + break; + } + } + } + if (slot != NULL) break; + } + SECMOD_ReleaseReadLock(moduleLock); + + if (slot == NULL) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + } + + return slot; +} + + +PK11SlotInfo * +PK11_FindSlotBySerial(char *serial) +{ + SECMODModuleList *mlp; + SECMODModuleList *modules = SECMOD_GetDefaultModuleList(); + SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); + int i; + PK11SlotInfo *slot = NULL; + + /* work through all the slots */ + SECMOD_GetReadLock(moduleLock); + for(mlp = modules; mlp != NULL; mlp = mlp->next) { + for (i=0; i < mlp->module->slotCount; i++) { + PK11SlotInfo *tmpSlot = mlp->module->slots[i]; + if (PK11_IsPresent(tmpSlot)) { + if (PORT_Memcmp(tmpSlot->serial,serial, + sizeof(tmpSlot->serial)) == 0) { + slot = PK11_ReferenceSlot(tmpSlot); + break; + } + } + } + if (slot != NULL) break; + } + SECMOD_ReleaseReadLock(moduleLock); + + if (slot == NULL) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + } + + return slot; +} + + + + +/*********************************************************** + * Password Utilities + ***********************************************************/ +/* + * Check the user's password. Log into the card if it's correct. + * succeed if the user is already logged in. + */ +SECStatus +pk11_CheckPassword(PK11SlotInfo *slot,char *pw) +{ + int len = PORT_Strlen(pw); + CK_RV crv; + SECStatus rv; + int64 currtime = PR_Now(); + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER, + (unsigned char *)pw,len); + PK11_ExitSlotMonitor(slot); + switch (crv) { + /* if we're already logged in, we're good to go */ + case CKR_OK: + slot->authTransact = PK11_Global.transaction; + case CKR_USER_ALREADY_LOGGED_IN: + slot->authTime = currtime; + rv = SECSuccess; + break; + case CKR_PIN_INCORRECT: + PORT_SetError(SEC_ERROR_BAD_PASSWORD); + rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ + break; + default: + PORT_SetError(PK11_MapError(crv)); + rv = SECFailure; /* some failure we can't fix by retrying */ + } + return rv; +} + +/* + * Check the user's password. Logout before hand to make sure that + * we are really checking the password. + */ +SECStatus +PK11_CheckUserPassword(PK11SlotInfo *slot,char *pw) +{ + int len = PORT_Strlen(pw); + CK_RV crv; + SECStatus rv; + int64 currtime = PR_Now(); + + /* force a logout */ + PK11_EnterSlotMonitor(slot); + PK11_GETTAB(slot)->C_Logout(slot->session); + + crv = PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER, + (unsigned char *)pw,len); + PK11_ExitSlotMonitor(slot); + switch (crv) { + /* if we're already logged in, we're good to go */ + case CKR_OK: + slot->authTransact = PK11_Global.transaction; + slot->authTime = currtime; + rv = SECSuccess; + break; + case CKR_PIN_INCORRECT: + PORT_SetError(SEC_ERROR_BAD_PASSWORD); + rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ + break; + default: + PORT_SetError(PK11_MapError(crv)); + rv = SECFailure; /* some failure we can't fix by retrying */ + } + return rv; +} + +SECStatus +PK11_Logout(PK11SlotInfo *slot) +{ + CK_RV crv; + + /* force a logout */ + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_Logout(slot->session); + PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + return SECSuccess; +} + +/* + * transaction stuff is for when we test for the need to do every + * time auth to see if we already did it for this slot/transaction + */ +void PK11_StartAuthTransaction(void) +{ +PK11_Global.transaction++; +PK11_Global.inTransaction = PR_TRUE; +} + +void PK11_EndAuthTransaction(void) +{ +PK11_Global.transaction++; +PK11_Global.inTransaction = PR_FALSE; +} + +/* + * before we do a private key op, we check to see if we + * need to reauthenticate. + */ +void +PK11_HandlePasswordCheck(PK11SlotInfo *slot,void *wincx) +{ + int askpw = slot->askpw; + PRBool NeedAuth = PR_FALSE; + + if (!slot->needLogin) return; + + if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { + PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); + + if (def_slot) { + askpw = def_slot->askpw; + PK11_FreeSlot(def_slot); + } + } + + /* timeouts are handled by isLoggedIn */ + if (!PK11_IsLoggedIn(slot,wincx)) { + NeedAuth = PR_TRUE; + } else if (slot->askpw == -1) { + if (!PK11_Global.inTransaction || + (PK11_Global.transaction != slot->authTransact)) { + PK11_EnterSlotMonitor(slot); + PK11_GETTAB(slot)->C_Logout(slot->session); + PK11_ExitSlotMonitor(slot); + NeedAuth = PR_TRUE; + } + } + if (NeedAuth) PK11_DoPassword(slot,PR_TRUE,wincx); +} + +void +PK11_SlotDBUpdate(PK11SlotInfo *slot) +{ + SECMOD_AddPermDB(slot->module); +} + +/* + * set new askpw and timeout values + */ +void +PK11_SetSlotPWValues(PK11SlotInfo *slot,int askpw, int timeout) +{ + slot->askpw = askpw; + slot->timeout = timeout; + slot->defaultFlags |= PK11_OWN_PW_DEFAULTS; + PK11_SlotDBUpdate(slot); +} + +/* + * Get the askpw and timeout values for this slot + */ +void +PK11_GetSlotPWValues(PK11SlotInfo *slot,int *askpw, int *timeout) +{ + *askpw = slot->askpw; + *timeout = slot->timeout; + + if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { + PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); + + if (def_slot) { + *askpw = def_slot->askpw; + *timeout = def_slot->timeout; + PK11_FreeSlot(def_slot); + } + } +} + +/* + * make sure a slot is authenticated... + */ +SECStatus +PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) { + if (slot->needLogin && !PK11_IsLoggedIn(slot,wincx)) { + return PK11_DoPassword(slot,loadCerts,wincx); + } + return SECSuccess; +} + +/* + * notification stub. If we ever get interested in any events that + * the pkcs11 functions may pass back to use, we can catch them here... + * currently pdata is a slotinfo structure. + */ +CK_RV pk11_notify(CK_SESSION_HANDLE session, CK_NOTIFICATION event, + CK_VOID_PTR pdata) +{ + return CKR_OK; +} + + +/* + * grab a new RW session + * !!! has a side effect of grabbing the Monitor if either the slot's default + * session is RW or the slot is not thread safe. Monitor is release in function + * below + */ +CK_SESSION_HANDLE PK11_GetRWSession(PK11SlotInfo *slot) +{ + CK_SESSION_HANDLE rwsession; + CK_RV crv; + + if (!slot->isThreadSafe || slot->defRWSession) PK11_EnterSlotMonitor(slot); + if (slot->defRWSession) return slot->session; + + crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID, + CKF_RW_SESSION|CKF_SERIAL_SESSION, + slot, pk11_notify,&rwsession); + if (crv == CKR_SESSION_COUNT) { + PK11_GETTAB(slot)->C_CloseSession(slot->session); + slot->session = CK_INVALID_SESSION; + crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID, + CKF_RW_SESSION|CKF_SERIAL_SESSION, + slot,pk11_notify,&rwsession); + } + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + if (slot->session == CK_INVALID_SESSION) { + PK11_GETTAB(slot)->C_OpenSession(slot->slotID,CKF_SERIAL_SESSION, + slot,pk11_notify,&slot->session); + } + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + return CK_INVALID_SESSION; + } + + return rwsession; +} + +PRBool +PK11_RWSessionHasLock(PK11SlotInfo *slot,CK_SESSION_HANDLE session_handle) { + return (PRBool)(!slot->isThreadSafe || slot->defRWSession); +} + +/* + * close the rwsession and restore our readonly session + * !!! has a side effect of releasing the Monitor if either the slot's default + * session is RW or the slot is not thread safe. + */ +void +PK11_RestoreROSession(PK11SlotInfo *slot,CK_SESSION_HANDLE rwsession) +{ + if (slot->defRWSession) { + PK11_ExitSlotMonitor(slot); + return; + } + PK11_GETTAB(slot)->C_CloseSession(rwsession); + if (slot->session == CK_INVALID_SESSION) { + PK11_GETTAB(slot)->C_OpenSession(slot->slotID,CKF_SERIAL_SESSION, + slot,pk11_notify,&slot->session); + } + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); +} + +/* + * NOTE: this assumes that we are logged out of the card before hand + */ +SECStatus +PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw) +{ + CK_SESSION_HANDLE rwsession; + CK_RV crv; + SECStatus rv = SECFailure; + int len = PORT_Strlen(ssopw); + + /* get a rwsession */ + rwsession = PK11_GetRWSession(slot); + if (rwsession == CK_INVALID_SESSION) return rv; + + /* check the password */ + crv = PK11_GETTAB(slot)->C_Login(rwsession,CKU_SO, + (unsigned char *)ssopw,len); + switch (crv) { + /* if we're already logged in, we're good to go */ + case CKR_OK: + rv = SECSuccess; + break; + case CKR_PIN_INCORRECT: + PORT_SetError(SEC_ERROR_BAD_PASSWORD); + rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ + break; + default: + PORT_SetError(PK11_MapError(crv)); + rv = SECFailure; /* some failure we can't fix by retrying */ + } + PK11_GETTAB(slot)->C_Logout(rwsession); + /* release rwsession */ + PK11_RestoreROSession(slot,rwsession); + return rv; +} + +/* + * make sure the password conforms to your token's requirements. + */ +SECStatus +PK11_VerifyPW(PK11SlotInfo *slot,char *pw) +{ + int len = PORT_Strlen(pw); + + if ((slot->minPassword > len) || (slot->maxPassword < len)) { + PORT_SetError(SEC_ERROR_BAD_DATA); + return SECFailure; + } + return SECSuccess; +} + +/* + * initialize a user PIN Value + */ +SECStatus +PK11_InitPin(PK11SlotInfo *slot,char *ssopw, char *userpw) +{ + CK_SESSION_HANDLE rwsession = CK_INVALID_SESSION; + CK_RV crv; + SECStatus rv = SECFailure; + int len; + int ssolen; + + if (userpw == NULL) userpw = ""; + if (ssopw == NULL) ssopw = ""; + + len = PORT_Strlen(userpw); + ssolen = PORT_Strlen(ssopw); + + /* get a rwsession */ + rwsession = PK11_GetRWSession(slot); + if (rwsession == CK_INVALID_SESSION) goto done; + + /* check the password */ + crv = PK11_GETTAB(slot)->C_Login(rwsession,CKU_SO, + (unsigned char *)ssopw,ssolen); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + goto done; + } + + crv = PK11_GETTAB(slot)->C_InitPIN(rwsession,(unsigned char *)userpw,len); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + } else { + rv = SECSuccess; + } + +done: + PK11_GETTAB(slot)->C_Logout(rwsession); + PK11_RestoreROSession(slot,rwsession); + if (rv == SECSuccess) { + /* update our view of the world */ + PK11_InitToken(slot,PR_TRUE); + PK11_EnterSlotMonitor(slot); + PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER, + (unsigned char *)userpw,len); + PK11_ExitSlotMonitor(slot); + } + return rv; +} + +/* + * Change an existing user password + */ +SECStatus +PK11_ChangePW(PK11SlotInfo *slot,char *oldpw, char *newpw) +{ + CK_RV crv; + SECStatus rv = SECFailure; + int newLen; + int oldLen; + CK_SESSION_HANDLE rwsession; + + if (newpw == NULL) newpw = ""; + if (oldpw == NULL) oldpw = ""; + newLen = PORT_Strlen(newpw); + oldLen = PORT_Strlen(oldpw); + + /* get a rwsession */ + rwsession = PK11_GetRWSession(slot); + + crv = PK11_GETTAB(slot)->C_SetPIN(rwsession, + (unsigned char *)oldpw,oldLen,(unsigned char *)newpw,newLen); + if (crv == CKR_OK) { + rv = SECSuccess; + } else { + PORT_SetError(PK11_MapError(crv)); + } + + PK11_RestoreROSession(slot,rwsession); + + /* update our view of the world */ + PK11_InitToken(slot,PR_TRUE); + return rv; +} + +static char * +pk11_GetPassword(PK11SlotInfo *slot, PRBool retry, void * wincx) +{ + if (PK11_Global.getPass == NULL) return NULL; + return (*PK11_Global.getPass)(slot, retry, wincx); +} + +void +PK11_SetPasswordFunc(PK11PasswordFunc func) +{ + PK11_Global.getPass = func; +} + +void +PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func) +{ + PK11_Global.verifyPass = func; +} + +void +PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func) +{ + PK11_Global.isLoggedIn = func; +} + + +/* + * authenticate to a slot. This loops until we can't recover, the user + * gives up, or we succeed. If we're already logged in and this function + * is called we will still prompt for a password, but we will probably + * succeed no matter what the password was (depending on the implementation + * of the PKCS 11 module. + */ +SECStatus +PK11_DoPassword(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) +{ + SECStatus rv = SECFailure; + char * password; + PRBool attempt = PR_FALSE; + + if (PK11_NeedUserInit(slot)) { + PORT_SetError(SEC_ERROR_IO); + return SECFailure; + } + + + /* + * Central server type applications which control access to multiple + * slave applications to single crypto devices need to virtuallize the + * login state. This is done by a callback out of PK11_IsLoggedIn and + * here. If we are actually logged in, then we got here because the + * higher level code told us that the particular client application may + * still need to be logged in. If that is the case, we simply tell the + * server code that it should now verify the clients password and tell us + * the results. + */ + if (PK11_IsLoggedIn(slot,NULL) && + (PK11_Global.verifyPass != NULL)) { + if (!PK11_Global.verifyPass(slot,wincx)) { + PORT_SetError(SEC_ERROR_BAD_PASSWORD); + return SECFailure; + } + return SECSuccess; + } + + /* get the password. This can drop out of the while loop + * for the following reasons: + * (1) the user refused to enter a password. + * (return error to caller) + * (2) the token user password is disabled [usually due to + * too many failed authentication attempts]. + * (return error to caller) + * (3) the password was successful. + */ + while ((password = pk11_GetPassword(slot, attempt, wincx)) != NULL) { + attempt = PR_TRUE; + rv = pk11_CheckPassword(slot,password); + PORT_Memset(password, 0, PORT_Strlen(password)); + PORT_Free(password); + if (rv != SECWouldBlock) break; + } + if (rv == SECSuccess) { + if ((loadCerts) && (!slot->isInternal) && (slot->cert_count == 0)) { + PK11_ReadSlotCerts(slot); + } + rv = pk11_CheckVerifyTest(slot); + } else if (!attempt) PORT_SetError(SEC_ERROR_BAD_PASSWORD); + return rv; +} + +void PK11_LogoutAll(void) +{ + SECMODListLock *lock = SECMOD_GetDefaultModuleListLock(); + SECMODModuleList *modList = SECMOD_GetDefaultModuleList(); + SECMODModuleList *mlp = NULL; + int i; + + SECMOD_GetReadLock(lock); + /* find the number of entries */ + for (mlp = modList; mlp != NULL; mlp = mlp->next) { + for (i=0; i < mlp->module->slotCount; i++) { + PK11_Logout(mlp->module->slots[i]); + } + } + + SECMOD_ReleaseReadLock(lock); +} + +int +PK11_GetMinimumPwdLength(PK11SlotInfo *slot) +{ + return ((int)slot->minPassword); +} + +/************************************************************ + * Manage the built-In Slot Lists + ************************************************************/ + +/* Init the static built int slot list (should actually integrate + * with PK11_NewSlotList */ +static void +pk11_initSlotList(PK11SlotList *list) +{ +#ifdef PKCS11_USE_THREADS + list->lock = PR_NewLock(); +#else + list->lock = NULL; +#endif + list->head = NULL; +} + +/* initialize the system slotlists */ +SECStatus +PK11_InitSlotLists(void) +{ + pk11_initSlotList(&pk11_desSlotList); + pk11_initSlotList(&pk11_rc4SlotList); + pk11_initSlotList(&pk11_rc2SlotList); + pk11_initSlotList(&pk11_rc5SlotList); + pk11_initSlotList(&pk11_md5SlotList); + pk11_initSlotList(&pk11_md2SlotList); + pk11_initSlotList(&pk11_sha1SlotList); + pk11_initSlotList(&pk11_rsaSlotList); + pk11_initSlotList(&pk11_dsaSlotList); + pk11_initSlotList(&pk11_dhSlotList); + pk11_initSlotList(&pk11_idea); + pk11_initSlotList(&pk11_random); + return SECSuccess; +} + +/* return a system slot list based on mechanism */ +PK11SlotList * +PK11_GetSlotList(CK_MECHANISM_TYPE type) +{ + switch (type) { + case CKM_DES_CBC: + case CKM_DES_ECB: + case CKM_DES3_ECB: + case CKM_DES3_CBC: + return &pk11_desSlotList; + case CKM_RC4: + return &pk11_rc4SlotList; + case CKM_RC5_CBC: + return &pk11_rc5SlotList; + case CKM_SHA_1: + return &pk11_sha1SlotList; + case CKM_MD5: + return &pk11_md5SlotList; + case CKM_MD2: + return &pk11_md2SlotList; + case CKM_RC2_ECB: + case CKM_RC2_CBC: + return &pk11_rc2SlotList; + case CKM_RSA_PKCS: + case CKM_RSA_PKCS_KEY_PAIR_GEN: + case CKM_RSA_X_509: + return &pk11_rsaSlotList; + case CKM_DSA: + return &pk11_dsaSlotList; + case CKM_DH_PKCS_KEY_PAIR_GEN: + case CKM_DH_PKCS_DERIVE: + return &pk11_dhSlotList; + case CKM_IDEA_CBC: + case CKM_IDEA_ECB: + return &pk11_idea; + case CKM_FAKE_RANDOM: + return &pk11_random; + } + return NULL; +} + +/* + * load the static SlotInfo structures used to select a PKCS11 slot. + * preSlotInfo has a list of all the default flags for the slots on this + * module. + */ +void +PK11_LoadSlotList(PK11SlotInfo *slot, PK11PreSlotInfo *psi, int count) +{ + int i; + + for (i=0; i < count; i++) { + if (psi[i].slotID == slot->slotID) + break; + } + + if (i == count) return; + + slot->defaultFlags = psi[i].defaultFlags; + slot->askpw = psi[i].askpw; + slot->timeout = psi[i].timeout; + + /* if the slot is already disabled, don't load them into the + * default slot lists. We get here so we can save the default + * list value. */ + if (slot->disabled) return; + + /* if the user has disabled us, don't load us in */ + if (slot->defaultFlags & PK11_DISABLE_FLAG) { + slot->disabled = PR_TRUE; + slot->reason = PK11_DIS_USER_SELECTED; + /* free up sessions and things?? */ + return; + } + + for (i=0; i < sizeof(PK11_DefaultArray)/sizeof(PK11_DefaultArray[0]); + i++) { + if (slot->defaultFlags & PK11_DefaultArray[i].flag) { + CK_MECHANISM_TYPE mechanism = PK11_DefaultArray[i].mechanism; + PK11SlotList *slotList = PK11_GetSlotList(mechanism); + + if (slotList) PK11_AddSlotToList(slotList,slot); + } + } + + return; +} + + +/* + * update a slot to its new attribute according to the slot list + * returns: SECSuccess if nothing to do or add/delete is successful + */ +SECStatus +PK11_UpdateSlotAttribute(PK11SlotInfo *slot, PK11DefaultArrayEntry *entry, + PRBool add) + /* add: PR_TRUE if want to turn on */ +{ + SECStatus result = SECSuccess; + PK11SlotList *slotList = PK11_GetSlotList(entry->mechanism); + + if (add) { /* trying to turn on a mechanism */ + + /* turn on the default flag in the slot */ + slot->defaultFlags |= entry->flag; + + /* add this slot to the list */ + if (slotList!=NULL) + result = PK11_AddSlotToList(slotList, slot); + + } else { /* trying to turn off */ + + /* turn OFF the flag in the slot */ + slot->defaultFlags &= ~entry->flag; + + if (slotList) { + /* find the element in the list & delete it */ + PK11SlotListElement *le = PK11_FindSlotElement(slotList, slot); + + /* remove the slot from the list */ + if (le) + result = PK11_DeleteSlotFromList(slotList, le); + } + } + return result; +} + +/* + * clear a slot off of all of it's default list + */ +void +PK11_ClearSlotList(PK11SlotInfo *slot) +{ + int i; + + if (slot->disabled) return; + if (slot->defaultFlags == 0) return; + + for (i=0; i < sizeof(PK11_DefaultArray)/sizeof(PK11_DefaultArray[0]); + i++) { + if (slot->defaultFlags & PK11_DefaultArray[i].flag) { + CK_MECHANISM_TYPE mechanism = PK11_DefaultArray[i].mechanism; + PK11SlotList *slotList = PK11_GetSlotList(mechanism); + PK11SlotListElement *le = NULL; + + if (slotList) le = PK11_FindSlotElement(slotList,slot); + + if (le) { + PK11_DeleteSlotFromList(slotList,le); + pk11_FreeListElement(slotList,le); + } + } + } +} + + +/****************************************************************** + * Slot initialization + ******************************************************************/ +/* + * turn a PKCS11 Static Label into a string + */ +char * +PK11_MakeString(PRArenaPool *arena,char *space, + char *staticString,int stringLen) +{ + int i; + char *newString; + for(i=(stringLen-1); i >= 0; i--) { + if (staticString[i] != ' ') break; + } + /* move i to point to the last space */ + i++; + if (arena) { + newString = (char*)PORT_ArenaAlloc(arena,i+1 /* space for NULL */); + } else if (space) { + newString = space; + } else { + newString = (char*)PORT_Alloc(i+1 /* space for NULL */); + } + if (newString == NULL) return NULL; + + if (i) PORT_Memcpy(newString,staticString, i); + newString[i] = 0; + + return newString; +} + +/* + * verify that slot implements Mechanism mech properly by checking against + * our internal implementation + */ +PRBool +PK11_VerifyMechanism(PK11SlotInfo *slot,PK11SlotInfo *intern, + CK_MECHANISM_TYPE mech, SECItem *data, SECItem *iv) +{ + PK11Context *test = NULL, *reference = NULL; + PK11SymKey *symKey = NULL, *testKey = NULL; + SECItem *param = NULL; + unsigned char encTest[8]; + unsigned char encRef[8]; + int outLenTest,outLenRef; + int key_size = 0; + SECStatus rv; + + if ((mech == CKM_RC2_CBC) || (mech == CKM_RC2_ECB) || (mech == CKM_RC4)) { + key_size = 16; + } + + /* initialize the mechanism parameter */ + param = PK11_ParamFromIV(mech,iv); + if (param == NULL) goto loser; + + /* load the keys and contexts */ + symKey = PK11_KeyGen(intern,mech,NULL, key_size, NULL); + if (symKey == NULL) goto loser; + + reference = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symKey, param); + if (reference == NULL) goto loser; + + testKey = pk11_CopyToSlot(slot, mech, CKA_ENCRYPT, symKey); + if (testKey == NULL) goto loser; + + test = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, testKey, param); + if (test == NULL) goto loser; + SECITEM_FreeItem(param,PR_TRUE); param = NULL; + + /* encrypt the test data */ + rv = PK11_CipherOp(test,encTest,&outLenTest,sizeof(encTest), + data->data,data->len); + if (rv != SECSuccess) goto loser; + rv = PK11_CipherOp(reference,encRef,&outLenRef,sizeof(encRef), + data->data,data->len); + if (rv != SECSuccess) goto loser; + + PK11_DestroyContext(reference,PR_TRUE); reference = NULL; + PK11_DestroyContext(test,PR_TRUE); test = NULL; + + if (outLenTest != outLenRef) goto loser; + if (PORT_Memcmp(encTest, encRef, outLenTest) != 0) goto loser; + + return PR_TRUE; + +loser: + if (test) PK11_DestroyContext(test,PR_TRUE); + if (symKey) PK11_FreeSymKey(symKey); + if (testKey) PK11_FreeSymKey(testKey); + if (reference) PK11_DestroyContext(reference,PR_TRUE); + if (param) SECITEM_FreeItem(param,PR_TRUE); + + return PR_FALSE; +} + +/* + * this code verifies that the advertised mechanisms are what they + * seem to be. + */ +#define MAX_MECH_LIST_SIZE 30 /* we only know of about 30 odd mechanisms */ +PRBool +PK11_VerifySlotMechanisms(PK11SlotInfo *slot) +{ + CK_MECHANISM_TYPE mechListArray[MAX_MECH_LIST_SIZE]; + CK_MECHANISM_TYPE *mechList = mechListArray; + static SECItem data; + static SECItem iv; + static SECItem key; + static unsigned char dataV[8]; + static unsigned char ivV[8]; + static unsigned char keyV[8]; + static PRBool generated = PR_FALSE; + CK_ULONG count; + int i; + CK_RV crv; + + PRBool alloced = PR_FALSE; + PK11SlotInfo *intern = PK11_GetInternalSlot(); + + /* if we couldn't initialize an internal module, + * we can't check external ones */ + if (intern == NULL) return PR_FALSE; + + /* first get the count of mechanisms */ + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID,NULL,&count); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PK11_FreeSlot(intern); + return PR_FALSE; + } + + + /* don't blow up just because the card supports more mechanisms than + * we know about, just alloc space for them */ + if (count > MAX_MECH_LIST_SIZE) { + mechList = (CK_MECHANISM_TYPE *) + PORT_Alloc(count *sizeof(CK_MECHANISM_TYPE)); + alloced = PR_TRUE; + if (mechList == NULL) return PR_FALSE; + } + /* get the list */ + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv =PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID, mechList, &count); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + if (alloced) PORT_Free(mechList); + PK11_FreeSlot(intern); + return PR_FALSE; + } + + if (!generated) { + data.data = dataV; + data.len = sizeof(dataV); + iv.data = ivV; + iv.len = sizeof(ivV); + /* ok, this is a cheat, we know our internal random number generater + * is thread safe */ + PK11_GETTAB(intern)->C_GenerateRandom(intern->session, + data.data, data.len); + PK11_GETTAB(intern)->C_GenerateRandom(intern->session, + iv.data, iv.len); + } + for (i=0; i < (int) count; i++) { + switch (mechList[i]) { + case CKM_DES_CBC: + case CKM_DES_ECB: + case CKM_RC4: + case CKM_RC2_CBC: + case CKM_RC2_ECB: + if (!PK11_VerifyMechanism(slot,intern,mechList[i],&data,&iv)){ + if (alloced) PORT_Free(mechList); + PK11_FreeSlot(intern); + return PR_FALSE; + } + } + } + if (alloced) PORT_Free(mechList); + PK11_FreeSlot(intern); + return PR_TRUE; +} + +/* + * See if we need to run the verify test, do so if necessary. If we fail, + * disable the slot. + */ +SECStatus +pk11_CheckVerifyTest(PK11SlotInfo *slot) +{ + PK11_EnterSlotMonitor(slot); + if (slot->needTest) { + slot->needTest = PR_FALSE; + PK11_ExitSlotMonitor(slot); + if (!PK11_VerifySlotMechanisms(slot)) { + (void)PK11_GETTAB(slot)->C_CloseSession(slot->session); + slot->session = CK_INVALID_SESSION; + PK11_ClearSlotList(slot); + slot->disabled = PR_TRUE; + slot->reason = PK11_DIS_TOKEN_VERIFY_FAILED; + slot->needTest = PR_TRUE; + PORT_SetError(SEC_ERROR_IO); + return SECFailure; + } + } else { + PK11_ExitSlotMonitor(slot); + } + return SECSuccess; +} + +/* + * Reads in the slots mechanism list for later use + */ +SECStatus +PK11_ReadMechanismList(PK11SlotInfo *slot) +{ + CK_ULONG count; + CK_RV crv; + + if (slot->mechanismList) { + PORT_Free(slot->mechanismList); + slot->mechanismList = NULL; + } + slot->mechanismCount = 0; + + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID,NULL,&count); + if (crv != CKR_OK) { + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + + slot->mechanismList = (CK_MECHANISM_TYPE *) + PORT_Alloc(count *sizeof(CK_MECHANISM_TYPE)); + if (slot->mechanismList == NULL) { + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + return SECFailure; + } + crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID, + slot->mechanismList, &count); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_Free(slot->mechanismList); + slot->mechanismList = NULL; + PORT_SetError(PK11_MapError(crv)); + return SECSuccess; + } + slot->mechanismCount = count; + return SECSuccess; +} + +/* + * initialize a new token + * unlike initialize slot, this can be called multiple times in the lifetime + * of NSS. It reads the information associated with a card or token, + * that is not going to change unless the card or token changes. + */ +SECStatus +PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts) +{ + CK_TOKEN_INFO tokenInfo; + CK_RV crv; + char *tmp; + SECStatus rv; + + /* set the slot flags to the current token values */ + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetTokenInfo(slot->slotID,&tokenInfo); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + + /* set the slot flags to the current token values */ + slot->series++; /* allow other objects to detect that the + * slot is different */ + slot->flags = tokenInfo.flags; + slot->needLogin = ((tokenInfo.flags & CKF_LOGIN_REQUIRED) ? + PR_TRUE : PR_FALSE); + slot->readOnly = ((tokenInfo.flags & CKF_WRITE_PROTECTED) ? + PR_TRUE : PR_FALSE); + slot->hasRandom = ((tokenInfo.flags & CKF_RNG) ? PR_TRUE : PR_FALSE); + tmp = PK11_MakeString(NULL,slot->token_name, + (char *)tokenInfo.label, sizeof(tokenInfo.label)); + slot->minPassword = tokenInfo.ulMinPinLen; + slot->maxPassword = tokenInfo.ulMaxPinLen; + PORT_Memcpy(slot->serial,tokenInfo.serialNumber,sizeof(slot->serial)); + + slot->defRWSession = (PRBool)((!slot->readOnly) && + (tokenInfo.ulMaxSessionCount == 1)); + rv = PK11_ReadMechanismList(slot); + if (rv != SECSuccess) return rv; + + slot->hasRSAInfo = PR_FALSE; + slot->RSAInfoFlags = 0; + + /* Make sure our session handle is valid */ + if (slot->session == CK_INVALID_SESSION) { + /* we know we don't have a valid session, go get one */ + CK_SESSION_HANDLE session; + + /* session should be Readonly, serial */ + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID, + (slot->defRWSession ? CKF_RW_SESSION : 0) | CKF_SERIAL_SESSION, + slot,pk11_notify,&session); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + slot->session = session; + } else { + /* The session we have may be defunct (the token associated with it) + * has been removed */ + CK_SESSION_INFO sessionInfo; + + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session,&sessionInfo); + if (crv == CKR_DEVICE_ERROR) { + PK11_GETTAB(slot)->C_CloseSession(slot->session); + crv = CKR_SESSION_CLOSED; + } + if ((crv==CKR_SESSION_CLOSED) || (crv==CKR_SESSION_HANDLE_INVALID)) { + crv =PK11_GETTAB(slot)->C_OpenSession(slot->slotID, + (slot->defRWSession ? CKF_RW_SESSION : 0) | CKF_SERIAL_SESSION, + slot,pk11_notify,&slot->session); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + slot->session = CK_INVALID_SESSION; + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + return SECFailure; + } + } + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + } + + /*if we have cached slotcerts, free them they are almost certainly stale*/ + PK11_FreeSlotCerts(slot); + + if (loadCerts && (!slot->isInternal) && + ((!slot->needLogin) || (slot->defaultFlags & SECMOD_FRIENDLY_FLAG))) { + PK11_ReadSlotCerts(slot); + } + + if (!(slot->needLogin)) { + return pk11_CheckVerifyTest(slot); + } + + + if (!(slot->isInternal) && (slot->hasRandom)) { + /* if this slot has a random number generater, use it to add entropy + * to the internal slot. */ + PK11SlotInfo *int_slot = PK11_GetInternalSlot(); + + if (int_slot) { + unsigned char random_bytes[32]; + + /* if this slot can issue random numbers, get some entropy from + * that random number generater and give it to our internal token. + */ + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GenerateRandom + (slot->session,random_bytes, sizeof(random_bytes)); + PK11_ExitSlotMonitor(slot); + if (crv == CKR_OK) { + PK11_EnterSlotMonitor(int_slot); + PK11_GETTAB(int_slot)->C_SeedRandom(int_slot->session, + random_bytes, sizeof(random_bytes)); + PK11_ExitSlotMonitor(int_slot); + } + + /* Now return the favor and send entropy to the token's random + * number generater */ + PK11_EnterSlotMonitor(int_slot); + crv = PK11_GETTAB(int_slot)->C_GenerateRandom(int_slot->session, + random_bytes, sizeof(random_bytes)); + PK11_ExitSlotMonitor(int_slot); + if (crv == CKR_OK) { + PK11_EnterSlotMonitor(slot); + PK11_GETTAB(slot)->C_SeedRandom(slot->session, + random_bytes, sizeof(random_bytes)); + PK11_ExitSlotMonitor(slot); + } + } + } + + return SECSuccess; +} + +/* + * Initialize the slot : + * This initialization code is called on each slot a module supports when + * it is loaded. It does the bringup initialization. The difference between + * this and InitToken is Init slot does those one time initialization stuff, + * usually associated with the reader, while InitToken may get called multiple + * times as tokens are removed and re-inserted. + */ +void +PK11_InitSlot(SECMODModule *mod,CK_SLOT_ID slotID,PK11SlotInfo *slot) +{ + SECStatus rv; + char *tmp; + CK_SLOT_INFO slotInfo; + + slot->functionList = mod->functionList; + slot->isInternal = mod->internal; + slot->slotID = slotID; + slot->isThreadSafe = mod->isThreadSafe; + slot->hasRSAInfo = PR_FALSE; + + if (PK11_GETTAB(slot)->C_GetSlotInfo(slotID,&slotInfo) != CKR_OK) { + slot->disabled = PR_TRUE; + slot->reason = PK11_DIS_COULD_NOT_INIT_TOKEN; + return; + } + + /* test to make sure claimed mechanism work */ + slot->needTest = mod->internal ? PR_FALSE : PR_TRUE; + slot->module = mod; /* NOTE: we don't make a reference here because + * modules have references to their slots. This + * works because modules keep implicit references + * from their slots, and won't unload and disappear + * until all their slots have been freed */ + tmp = PK11_MakeString(NULL,slot->slot_name, + (char *)slotInfo.slotDescription, sizeof(slotInfo.slotDescription)); + slot->isHW = (PRBool)((slotInfo.flags & CKF_HW_SLOT) == CKF_HW_SLOT); + if ((slotInfo.flags & CKF_REMOVABLE_DEVICE) == 0) { + slot->isPerm = PR_TRUE; + /* permanment slots must have the token present always */ + if ((slotInfo.flags & CKF_TOKEN_PRESENT) == 0) { + slot->disabled = PR_TRUE; + slot->reason = PK11_DIS_TOKEN_NOT_PRESENT; + return; /* nothing else to do */ + } + } + /* if the token is present, initialize it */ + if ((slotInfo.flags & CKF_TOKEN_PRESENT) != 0) { + rv = PK11_InitToken(slot,PR_TRUE); + /* the only hard failures are on permanent devices, or function + * verify failures... function verify failures are already handled + * by tokenInit */ + if ((rv != SECSuccess) && (slot->isPerm) && (!slot->disabled)) { + slot->disabled = PR_TRUE; + slot->reason = PK11_DIS_COULD_NOT_INIT_TOKEN; + } + } +} + + + +/********************************************************************* + * Slot mapping utility functions. + *********************************************************************/ + +/* + * determine if the token is present. If the token is present, make sure + * we have a valid session handle. Also set the value of needLogin + * appropriately. + */ +static PRBool +pk11_IsPresentCertLoad(PK11SlotInfo *slot, PRBool loadCerts) +{ + CK_SLOT_INFO slotInfo; + CK_SESSION_INFO sessionInfo; + CK_RV crv; + + /* disabled slots are never present */ + if (slot->disabled) { + return PR_FALSE; + } + + /* permanent slots are always present */ + if (slot->isPerm && (slot->session != CK_INVALID_SESSION)) { + return PR_TRUE; + } + + /* removable slots have a flag that says they are present */ + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + if (PK11_GETTAB(slot)->C_GetSlotInfo(slot->slotID,&slotInfo) != CKR_OK) { + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + return PR_FALSE; + } + if ((slotInfo.flags & CKF_TOKEN_PRESENT) == 0) { + /* if the slot is no longer present, close the session */ + if (slot->session != CK_INVALID_SESSION) { + PK11_GETTAB(slot)->C_CloseSession(slot->session); + slot->session = CK_INVALID_SESSION; + /* force certs to be freed */ + PK11_FreeSlotCerts(slot); + } + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + return PR_FALSE; + } + + /* use the session Info to determine if the card has been removed and then + * re-inserted */ + if (slot->session != CK_INVALID_SESSION) { + crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session, &sessionInfo); + if (crv != CKR_OK) { + PK11_GETTAB(slot)->C_CloseSession(slot->session); + slot->session = CK_INVALID_SESSION; + PK11_FreeSlotCerts(slot); + } + } + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + + /* card has not been removed, current token info is correct */ + if (slot->session != CK_INVALID_SESSION) return PR_TRUE; + + /* initialize the token info state */ + if (PK11_InitToken(slot,loadCerts) != SECSuccess) { + return PR_FALSE; + } + + return PR_TRUE; +} + +/* + * old version of the routine + */ +PRBool +PK11_IsPresent(PK11SlotInfo *slot) { + return pk11_IsPresentCertLoad(slot,PR_TRUE); +} + +/* is the slot disabled? */ +PRBool +PK11_IsDisabled(PK11SlotInfo *slot) +{ + return slot->disabled; +} + +/* and why? */ +PK11DisableReasons +PK11_GetDisabledReason(PK11SlotInfo *slot) +{ + return slot->reason; +} + +/* returns PR_TRUE if successfully disable the slot */ +/* returns PR_FALSE otherwise */ +PRBool PK11_UserDisableSlot(PK11SlotInfo *slot) { + + slot->defaultFlags |= PK11_DISABLE_FLAG; + slot->disabled = PR_TRUE; + slot->reason = PK11_DIS_USER_SELECTED; + + return PR_TRUE; +} + +PRBool PK11_UserEnableSlot(PK11SlotInfo *slot) { + + slot->defaultFlags &= ~PK11_DISABLE_FLAG; + slot->disabled = PR_FALSE; + slot->reason = PK11_DIS_NONE; + return PR_TRUE; +} + +/* Get the module this slot is attatched to */ +SECMODModule * +PK11_GetModule(PK11SlotInfo *slot) +{ + return slot->module; +} + +/* returnt the default flags of a slot */ +unsigned long +PK11_GetDefaultFlags(PK11SlotInfo *slot) +{ + return slot->defaultFlags; +} + +/* + * we can initialize the password if 1) The toke is not inited + * (need login == true and see need UserInit) or 2) the token has + * a NULL password. (slot->needLogin = false & need user Init = false). + */ +PRBool PK11_NeedPWInitForSlot(PK11SlotInfo *slot) +{ + if (slot->needLogin && PK11_NeedUserInit(slot)) { + return PR_TRUE; + } + if (!slot->needLogin && !PK11_NeedUserInit(slot)) { + return PR_TRUE; + } + return PR_FALSE; +} + +PRBool PK11_NeedPWInit() +{ + PK11SlotInfo *slot = PK11_GetInternalKeySlot(); + PRBool ret = PK11_NeedPWInitForSlot(slot); + + PK11_FreeSlot(slot); + return ret; +} + +/* + * The following wrapper functions allow us to export an opaque slot + * function to the rest of libsec and the world... */ +PRBool +PK11_IsReadOnly(PK11SlotInfo *slot) +{ + return slot->readOnly; +} + +PRBool +PK11_IsHW(PK11SlotInfo *slot) +{ + return slot->isHW; +} + +PRBool +PK11_IsInternal(PK11SlotInfo *slot) +{ + return slot->isInternal; +} + +PRBool +PK11_NeedLogin(PK11SlotInfo *slot) +{ + return slot->needLogin; +} + +PRBool +PK11_IsFriendly(PK11SlotInfo *slot) +{ + /* internal slot always has public readable certs */ + return (PRBool)(slot->isInternal || + ((slot->defaultFlags & SECMOD_FRIENDLY_FLAG) == + SECMOD_FRIENDLY_FLAG)); +} + +char * +PK11_GetTokenName(PK11SlotInfo *slot) +{ + return slot->token_name; +} + +char * +PK11_GetSlotName(PK11SlotInfo *slot) +{ + return slot->slot_name; +} + +int +PK11_GetSlotSeries(PK11SlotInfo *slot) +{ + return slot->series; +} + +int +PK11_GetCurrentWrapIndex(PK11SlotInfo *slot) +{ + return slot->wrapKey; +} + +CK_SLOT_ID +PK11_GetSlotID(PK11SlotInfo *slot) +{ + return slot->slotID; +} + +SECMODModuleID +PK11_GetModuleID(PK11SlotInfo *slot) +{ + return slot->module->moduleID; +} + + +/* return the slot info structure */ +SECStatus +PK11_GetSlotInfo(PK11SlotInfo *slot, CK_SLOT_INFO *info) +{ + CK_RV crv; + + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetSlotInfo(slot->slotID,info); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + return SECSuccess; +} + +/* return the token info structure */ +SECStatus +PK11_GetTokenInfo(PK11SlotInfo *slot, CK_TOKEN_INFO *info) +{ + CK_RV crv; + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetTokenInfo(slot->slotID,info); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + return SECSuccess; +} + +/* Find out if we need to initialize the user's pin */ +PRBool +PK11_NeedUserInit(PK11SlotInfo *slot) +{ + return (PRBool)((slot->flags & CKF_USER_PIN_INITIALIZED) == 0); +} + +/* get the internal key slot. FIPS has only one slot for both key slots and + * default slots */ +PK11SlotInfo * +PK11_GetInternalKeySlot(void) +{ + SECMODModule *mod = SECMOD_GetInternalModule(); + return PK11_ReferenceSlot(mod->isFIPS ? mod->slots[0] : mod->slots[1]); +} + +/* get the internal default slot */ +PK11SlotInfo * +PK11_GetInternalSlot(void) +{ + return PK11_ReferenceSlot(SECMOD_GetInternalModule()->slots[0]); +} + +/* + * Determine if the token is logged in. We have to actually query the token, + * because it's state can change without intervention from us. + */ +PRBool +PK11_IsLoggedIn(PK11SlotInfo *slot,void *wincx) +{ + CK_SESSION_INFO sessionInfo; + int askpw = slot->askpw; + int timeout = slot->timeout; + CK_RV crv; + + /* If we don't have our own password default values, use the system + * ones */ + if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) { + PK11SlotInfo *def_slot = PK11_GetInternalKeySlot(); + + if (def_slot) { + askpw = def_slot->askpw; + timeout = def_slot->timeout; + PK11_FreeSlot(def_slot); + } + } + + if ((wincx != NULL) && (PK11_Global.isLoggedIn != NULL) && + (*PK11_Global.isLoggedIn)(slot, wincx) == PR_FALSE) { return PR_FALSE; } + + + /* forget the password if we've been inactive too long */ + if (askpw == 1) { + int64 currtime = PR_Now(); + int64 result; + int64 mult; + + LL_I2L(result, timeout); + LL_I2L(mult, 60*1000*1000); + LL_MUL(result,result,mult); + LL_ADD(result, result, slot->authTime); + if (LL_CMP(result, <, currtime) ) { + PK11_EnterSlotMonitor(slot); + PK11_GETTAB(slot)->C_Logout(slot->session); + PK11_ExitSlotMonitor(slot); + } else { + slot->authTime = currtime; + } + } + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session,&sessionInfo); + PK11_ExitSlotMonitor(slot); + /* if we can't get session info, something is really wrong */ + if (crv != CKR_OK) { + slot->session = CK_INVALID_SESSION; + return PR_FALSE; + } + + switch (sessionInfo.state) { + case CKS_RW_PUBLIC_SESSION: + case CKS_RO_PUBLIC_SESSION: + default: + break; /* fail */ + case CKS_RW_USER_FUNCTIONS: + case CKS_RW_SO_FUNCTIONS: + case CKS_RO_USER_FUNCTIONS: + return PR_TRUE; + } + return PR_FALSE; +} + + +/* + * check if a given slot supports the requested mechanism + */ +PRBool +PK11_DoesMechanism(PK11SlotInfo *slot, CK_MECHANISM_TYPE type) +{ + int i; + + /* CKM_FAKE_RANDOM is not a real PKCS mechanism. It's a marker to + * tell us we're looking form someone that has implemented get + * random bits */ + if (type == CKM_FAKE_RANDOM) { + return slot->hasRandom; + } + + for (i=0; i < (int) slot->mechanismCount; i++) { + if (slot->mechanismList[i] == type) return PR_TRUE; + } + return PR_FALSE; +} + +/* + * Return true if a token that can do the desired mechanism exists. + * This allows us to have hardware tokens that can do function XYZ magically + * allow SSL Ciphers to appear if they are plugged in. + */ +PRBool +PK11_TokenExists(CK_MECHANISM_TYPE type) +{ + SECMODModuleList *mlp; + SECMODModuleList *modules = SECMOD_GetDefaultModuleList(); + SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); + PK11SlotInfo *slot; + PRBool found = PR_FALSE; + int i; + + /* we only need to know if there is a token that does this mechanism. + * check the internal module first because it's fast, and supports + * almost everything. */ + slot = PK11_GetInternalSlot(); + if (slot) { + found = PK11_DoesMechanism(slot,type); + PK11_FreeSlot(slot); + } + if (found) return PR_TRUE; /* bypass getting module locks */ + + SECMOD_GetReadLock(moduleLock); + for(mlp = modules; mlp != NULL && (!found); mlp = mlp->next) { + for (i=0; i < mlp->module->slotCount; i++) { + slot = mlp->module->slots[i]; + if (PK11_IsPresent(slot)) { + if (PK11_DoesMechanism(slot,type)) { + found = PR_TRUE; + break; + } + } + } + } + SECMOD_ReleaseReadLock(moduleLock); + return found; +} + +/* + * get all the currently available tokens in a list. + * that can perform the given mechanism. If mechanism is CKM_INVALID_MECHANISM, + * get all the tokens. Make sure tokens that need authentication are put at + * the end of this list. + */ +PK11SlotList * +PK11_GetAllTokens(CK_MECHANISM_TYPE type, PRBool needRW, PRBool loadCerts, + void *wincx) +{ + PK11SlotList * list = PK11_NewSlotList(); + PK11SlotList * loginList = PK11_NewSlotList(); + PK11SlotList * friendlyList = PK11_NewSlotList(); + SECMODModuleList * mlp; + SECMODModuleList * modules = SECMOD_GetDefaultModuleList(); + SECMODListLock * moduleLock = SECMOD_GetDefaultModuleListLock(); + int i; +#if defined( XP_WIN32 ) + int j = 0; + PRInt32 waste[16]; +#endif + + if ((list == NULL) || (loginList == NULL) || (friendlyList == NULL)) { + if (list) PK11_FreeSlotList(list); + if (loginList) PK11_FreeSlotList(loginList); + if (friendlyList) PK11_FreeSlotList(friendlyList); + return NULL; + } + + SECMOD_GetReadLock(moduleLock); + for(mlp = modules; mlp != NULL; mlp = mlp->next) { + +#if defined( XP_WIN32 ) + /* This is works around some horrible cache/page thrashing problems + ** on Win32. Without this, this loop can take up to 6 seconds at + ** 100% CPU on a Pentium-Pro 200. The thing this changes is to + ** increase the size of the stack frame and modify it. + ** Moving the loop code itself seems to have no effect. + ** Dunno why this combination makes a difference, but it does. + */ + waste[ j & 0xf] = j++; +#endif + + for (i = 0; i < mlp->module->slotCount; i++) { + PK11SlotInfo *slot = mlp->module->slots[i]; + + if (pk11_IsPresentCertLoad(slot, loadCerts)) { + if (needRW && slot->readOnly) continue; + if ((type == CKM_INVALID_MECHANISM) + || PK11_DoesMechanism(slot, type)) { + if (slot->needLogin && !PK11_IsLoggedIn(slot, wincx)) { + if (PK11_IsFriendly(slot)) { + PK11_AddSlotToList(friendlyList, slot); + } else { + PK11_AddSlotToList(loginList, slot); + } + } else { + PK11_AddSlotToList(list, slot); + } + } + } + } + } + SECMOD_ReleaseReadLock(moduleLock); + + PK11_MoveListToList(list,friendlyList); + PK11_FreeSlotList(friendlyList); + PK11_MoveListToList(list,loginList); + PK11_FreeSlotList(loginList); + + return list; +} + +/* + * NOTE: This routine is working from a private List generated by + * PK11_GetAllTokens. That is why it does not need to lock. + */ +PK11SlotList * +PK11_GetPrivateKeyTokens(CK_MECHANISM_TYPE type,PRBool needRW,void *wincx) +{ + PK11SlotList *list = PK11_GetAllTokens(type,needRW,PR_TRUE,wincx); + PK11SlotListElement *le, *next ; + SECStatus rv; + + if (list == NULL) return list; + + for (le = list->head ; le; le = next) { + next = le->next; /* save the pointer here in case we have to + * free the element later */ + rv = PK11_Authenticate(le->slot,PR_TRUE,wincx); + if (rv != SECSuccess) { + PK11_DeleteSlotFromList(list,le); + continue; + } + } + return list; +} + + +/* + * find the best slot which supports the given + * Mechanism. In normal cases this should grab the first slot on the list + * with no fuss. + */ +PK11SlotInfo * +PK11_GetBestSlotMultiple(CK_MECHANISM_TYPE *type, int mech_count, void *wincx) +{ + PK11SlotList *list = NULL; + PK11SlotListElement *le ; + PK11SlotInfo *slot = NULL; + PRBool freeit = PR_FALSE; + PRBool listNeedLogin = PR_FALSE; + int i; + SECStatus rv; + + list = PK11_GetSlotList(type[0]); + + if (list == NULL) { + /* We need to look up all the tokens for the mechanism */ + list = PK11_GetAllTokens(type[0],PR_FALSE,PR_TRUE,wincx); + freeit = PR_TRUE; + } + + /* no one can do it! */ + if (list == NULL) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return NULL; + } + + PORT_SetError(0); + + + listNeedLogin = PR_FALSE; + for (i=0; i < mech_count; i++) { + if ((type[i] != CKM_FAKE_RANDOM) && (type[i] != CKM_SHA_1) && + (type[i] != CKM_MD5) && (type[i] != CKM_MD2)) { + listNeedLogin = PR_TRUE; + break; + } + } + + for (le = PK11_GetFirstSafe(list); le; + le = PK11_GetNextSafe(list,le,PR_TRUE)) { + if (PK11_IsPresent(le->slot)) { + PRBool doExit = PR_FALSE; + for (i=0; i < mech_count; i++) { + if (!PK11_DoesMechanism(le->slot,type[i])) { + doExit = PR_TRUE; + break; + } + } + if (doExit) continue; + + if (listNeedLogin && le->slot->needLogin) { + rv = PK11_Authenticate(le->slot,PR_TRUE,wincx); + if (rv != SECSuccess) continue; + } + slot = le->slot; + PK11_ReferenceSlot(slot); + pk11_FreeListElement(list,le); + if (freeit) { PK11_FreeSlotList(list); } + return slot; + } + } + if (freeit) { PK11_FreeSlotList(list); } + if (PORT_GetError() == 0) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + } + return NULL; +} + +/* original get best slot now calls the multiple version with only one type */ +PK11SlotInfo * +PK11_GetBestSlot(CK_MECHANISM_TYPE type, void *wincx) +{ + return PK11_GetBestSlotMultiple(&type, 1, wincx); +} + +/* + * find the best key wrap mechanism for this slot. + */ +CK_MECHANISM_TYPE +PK11_GetBestWrapMechanism(PK11SlotInfo *slot) +{ + int i; + for (i=0; i < wrapMechanismCount; i++) { + if (PK11_DoesMechanism(slot,wrapMechanismList[i])) { + return wrapMechanismList[i]; + } + } + return CKM_INVALID_MECHANISM; +} + +int +PK11_GetBestKeyLength(PK11SlotInfo *slot,CK_MECHANISM_TYPE mechanism) +{ + CK_MECHANISM_INFO mechanism_info; + CK_RV crv; + + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID, + mechanism,&mechanism_info); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) return 0; + + if (mechanism_info.ulMinKeySize == mechanism_info.ulMaxKeySize) + return 0; + return mechanism_info.ulMaxKeySize; +} + + +/********************************************************************* + * Mechanism Mapping functions + *********************************************************************/ + +/* + * lookup an entry in the mechanism table. If none found, return the + * default structure. + */ +static pk11MechanismData * +pk11_lookup(CK_MECHANISM_TYPE type) +{ + int i; + for (i=0; i < pk11_MechEntrySize; i++) { + if (pk11_MechanismTable[i].type == type) { + return (&pk11_MechanismTable[i]); + } + } + return &pk11_default; +} + +/* + * NOTE: This is not thread safe. Called at init time, and when loading + * a new Entry. It is reasonably safe as long as it is not re-entered + * (readers will always see a consistant table) + * + * This routine is called to add entries to the mechanism table, once there, + * they can not be removed. + */ +void +PK11_AddMechanismEntry(CK_MECHANISM_TYPE type, CK_KEY_TYPE key, + CK_MECHANISM_TYPE keyGen, int ivLen, int blockSize) +{ + int tableSize = pk11_MechTableSize; + int size = pk11_MechEntrySize; + int entry = size++; + pk11MechanismData *old = pk11_MechanismTable; + pk11MechanismData *newt = pk11_MechanismTable; + + + if (size > tableSize) { + int oldTableSize = tableSize; + tableSize += 10; + newt = (pk11MechanismData *) + PORT_Alloc(tableSize*sizeof(pk11MechanismData)); + if (newt == NULL) return; + + if (old) PORT_Memcpy(newt,old,oldTableSize*sizeof(pk11MechanismData)); + } else old = NULL; + + newt[entry].type = type; + newt[entry].keyType = key; + newt[entry].keyGen = keyGen; + newt[entry].iv = ivLen; + newt[entry].blockSize = blockSize; + + pk11_MechanismTable = newt; + pk11_MechTableSize = tableSize; + pk11_MechEntrySize = size; + if (old) PORT_Free(old); +} + +/* + * Get the key type needed for the given mechanism + */ +CK_MECHANISM_TYPE +PK11_GetKeyType(CK_MECHANISM_TYPE type,unsigned long len) +{ + switch (type) { + case CKM_DES_ECB: + case CKM_DES_CBC: + case CKM_DES_MAC: + case CKM_DES_MAC_GENERAL: + case CKM_DES_CBC_PAD: + case CKM_DES_KEY_GEN: + case CKM_KEY_WRAP_LYNKS: + case CKM_PBE_MD2_DES_CBC: + case CKM_PBE_MD5_DES_CBC: + return CKK_DES; + case CKM_DES3_ECB: + case CKM_DES3_CBC: + case CKM_DES3_MAC: + case CKM_DES3_MAC_GENERAL: + case CKM_DES3_CBC_PAD: + return (len == 128) ? CKK_DES2 : CKK_DES3; + case CKM_DES2_KEY_GEN: + case CKM_PBE_SHA1_DES2_EDE_CBC: + return CKK_DES2; + case CKM_PBE_SHA1_DES3_EDE_CBC: + case CKM_DES3_KEY_GEN: + return CKK_DES3; + case CKM_CDMF_ECB: + case CKM_CDMF_CBC: + case CKM_CDMF_MAC: + case CKM_CDMF_MAC_GENERAL: + case CKM_CDMF_CBC_PAD: + case CKM_CDMF_KEY_GEN: + return CKK_CDMF; + case CKM_RC2_ECB: + case CKM_RC2_CBC: + case CKM_RC2_MAC: + case CKM_RC2_MAC_GENERAL: + case CKM_RC2_CBC_PAD: + case CKM_RC2_KEY_GEN: + case CKM_PBE_SHA1_RC2_128_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: + return CKK_RC2; + case CKM_RC4: + case CKM_RC4_KEY_GEN: + return CKK_RC4; + case CKM_RC5_ECB: + case CKM_RC5_CBC: + case CKM_RC5_MAC: + case CKM_RC5_MAC_GENERAL: + case CKM_RC5_CBC_PAD: + case CKM_RC5_KEY_GEN: + return CKK_RC5; + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + case CKM_SKIPJACK_KEY_GEN: + case CKM_SKIPJACK_WRAP: + case CKM_SKIPJACK_PRIVATE_WRAP: + return CKK_SKIPJACK; + case CKM_BATON_ECB128: + case CKM_BATON_ECB96: + case CKM_BATON_CBC128: + case CKM_BATON_COUNTER: + case CKM_BATON_SHUFFLE: + case CKM_BATON_WRAP: + case CKM_BATON_KEY_GEN: + return CKK_BATON; + case CKM_JUNIPER_ECB128: + case CKM_JUNIPER_CBC128: + case CKM_JUNIPER_COUNTER: + case CKM_JUNIPER_SHUFFLE: + case CKM_JUNIPER_WRAP: + case CKM_JUNIPER_KEY_GEN: + return CKK_JUNIPER; + case CKM_IDEA_CBC: + case CKM_IDEA_ECB: + case CKM_IDEA_MAC: + case CKM_IDEA_MAC_GENERAL: + case CKM_IDEA_CBC_PAD: + case CKM_IDEA_KEY_GEN: + return CKK_IDEA; + case CKM_CAST_ECB: + case CKM_CAST_CBC: + case CKM_CAST_MAC: + case CKM_CAST_MAC_GENERAL: + case CKM_CAST_CBC_PAD: + case CKM_CAST_KEY_GEN: + case CKM_PBE_MD5_CAST_CBC: + return CKK_CAST; + case CKM_CAST3_ECB: + case CKM_CAST3_CBC: + case CKM_CAST3_MAC: + case CKM_CAST3_MAC_GENERAL: + case CKM_CAST3_CBC_PAD: + case CKM_CAST3_KEY_GEN: + case CKM_PBE_MD5_CAST3_CBC: + return CKK_CAST3; + case CKM_CAST5_ECB: + case CKM_CAST5_CBC: + case CKM_CAST5_MAC: + case CKM_CAST5_MAC_GENERAL: + case CKM_CAST5_CBC_PAD: + case CKM_CAST5_KEY_GEN: + case CKM_PBE_MD5_CAST5_CBC: + return CKK_CAST5; + case CKM_RSA_PKCS: + case CKM_RSA_9796: + case CKM_RSA_X_509: + case CKM_MD2_RSA_PKCS: + case CKM_MD5_RSA_PKCS: + case CKM_SHA1_RSA_PKCS: + case CKM_KEY_WRAP_SET_OAEP: + case CKM_RSA_PKCS_KEY_PAIR_GEN: + return CKK_RSA; + case CKM_DSA: + case CKM_DSA_SHA1: + case CKM_DSA_KEY_PAIR_GEN: + return CKK_DSA; + case CKM_DH_PKCS_DERIVE: + case CKM_DH_PKCS_KEY_PAIR_GEN: + return CKK_DH; + case CKM_KEA_KEY_DERIVE: + case CKM_KEA_KEY_PAIR_GEN: + return CKK_KEA; + case CKM_ECDSA_KEY_PAIR_GEN: + case CKM_ECDSA: + case CKM_ECDSA_SHA1: + return CKK_ECDSA; + case CKM_SSL3_PRE_MASTER_KEY_GEN: + case CKM_GENERIC_SECRET_KEY_GEN: + case CKM_SSL3_MASTER_KEY_DERIVE: + case CKM_SSL3_KEY_AND_MAC_DERIVE: + case CKM_SSL3_SHA1_MAC: + case CKM_SSL3_MD5_MAC: + case CKM_TLS_MASTER_KEY_DERIVE: + case CKM_TLS_KEY_AND_MAC_DERIVE: + case CKM_SHA_1_HMAC: + case CKM_SHA_1_HMAC_GENERAL: + case CKM_MD2_HMAC: + case CKM_MD2_HMAC_GENERAL: + case CKM_MD5_HMAC: + case CKM_MD5_HMAC_GENERAL: + case CKM_TLS_PRF_GENERAL: + return CKK_GENERIC_SECRET; + default: + return pk11_lookup(type)->keyType; + } +} + +/* + * Get the Key Gen Mechanism needed for the given + * crypto mechanism + */ +CK_MECHANISM_TYPE +PK11_GetKeyGen(CK_MECHANISM_TYPE type) +{ + switch (type) { + case CKM_DES_ECB: + case CKM_DES_CBC: + case CKM_DES_MAC: + case CKM_DES_MAC_GENERAL: + case CKM_KEY_WRAP_LYNKS: + case CKM_DES_CBC_PAD: + return CKM_DES_KEY_GEN; + case CKM_DES3_ECB: + case CKM_DES3_CBC: + case CKM_DES3_MAC: + case CKM_DES3_MAC_GENERAL: + case CKM_DES3_CBC_PAD: + return CKM_DES3_KEY_GEN; + case CKM_CDMF_ECB: + case CKM_CDMF_CBC: + case CKM_CDMF_MAC: + case CKM_CDMF_MAC_GENERAL: + case CKM_CDMF_CBC_PAD: + return CKM_CDMF_KEY_GEN; + case CKM_RC2_ECB: + case CKM_RC2_CBC: + case CKM_RC2_MAC: + case CKM_RC2_MAC_GENERAL: + case CKM_RC2_CBC_PAD: + return CKM_RC2_KEY_GEN; + case CKM_RC4: + return CKM_RC4_KEY_GEN; + case CKM_RC5_ECB: + case CKM_RC5_CBC: + case CKM_RC5_MAC: + case CKM_RC5_MAC_GENERAL: + case CKM_RC5_CBC_PAD: + return CKM_RC5_KEY_GEN; + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + case CKM_SKIPJACK_WRAP: + return CKM_SKIPJACK_KEY_GEN; + case CKM_BATON_ECB128: + case CKM_BATON_ECB96: + case CKM_BATON_CBC128: + case CKM_BATON_COUNTER: + case CKM_BATON_SHUFFLE: + case CKM_BATON_WRAP: + return CKM_BATON_KEY_GEN; + case CKM_JUNIPER_ECB128: + case CKM_JUNIPER_CBC128: + case CKM_JUNIPER_COUNTER: + case CKM_JUNIPER_SHUFFLE: + case CKM_JUNIPER_WRAP: + return CKM_JUNIPER_KEY_GEN; + case CKM_IDEA_CBC: + case CKM_IDEA_ECB: + case CKM_IDEA_MAC: + case CKM_IDEA_MAC_GENERAL: + case CKM_IDEA_CBC_PAD: + return CKM_IDEA_KEY_GEN; + case CKM_CAST_ECB: + case CKM_CAST_CBC: + case CKM_CAST_MAC: + case CKM_CAST_MAC_GENERAL: + case CKM_CAST_CBC_PAD: + return CKM_CAST_KEY_GEN; + case CKM_CAST3_ECB: + case CKM_CAST3_CBC: + case CKM_CAST3_MAC: + case CKM_CAST3_MAC_GENERAL: + case CKM_CAST3_CBC_PAD: + return CKM_CAST3_KEY_GEN; + case CKM_CAST5_ECB: + case CKM_CAST5_CBC: + case CKM_CAST5_MAC: + case CKM_CAST5_MAC_GENERAL: + case CKM_CAST5_CBC_PAD: + return CKM_CAST5_KEY_GEN; + case CKM_RSA_PKCS: + case CKM_RSA_9796: + case CKM_RSA_X_509: + case CKM_MD2_RSA_PKCS: + case CKM_MD5_RSA_PKCS: + case CKM_SHA1_RSA_PKCS: + case CKM_KEY_WRAP_SET_OAEP: + return CKM_RSA_PKCS_KEY_PAIR_GEN; + case CKM_DSA: + case CKM_DSA_SHA1: + return CKM_DSA_KEY_PAIR_GEN; + case CKM_DH_PKCS_DERIVE: + return CKM_DH_PKCS_KEY_PAIR_GEN; + case CKM_KEA_KEY_DERIVE: + return CKM_KEA_KEY_PAIR_GEN; + case CKM_ECDSA: + return CKM_ECDSA_KEY_PAIR_GEN; + case CKM_SSL3_PRE_MASTER_KEY_GEN: + case CKM_SSL3_MASTER_KEY_DERIVE: + case CKM_SSL3_KEY_AND_MAC_DERIVE: + case CKM_SSL3_SHA1_MAC: + case CKM_SSL3_MD5_MAC: + case CKM_TLS_MASTER_KEY_DERIVE: + case CKM_TLS_KEY_AND_MAC_DERIVE: + return CKM_SSL3_PRE_MASTER_KEY_GEN; + case CKM_SHA_1_HMAC: + case CKM_SHA_1_HMAC_GENERAL: + case CKM_MD2_HMAC: + case CKM_MD2_HMAC_GENERAL: + case CKM_MD5_HMAC: + case CKM_MD5_HMAC_GENERAL: + case CKM_TLS_PRF_GENERAL: + return CKM_GENERIC_SECRET_KEY_GEN; + case CKM_PBE_MD2_DES_CBC: + case CKM_PBE_MD5_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: + case CKM_PBE_SHA1_RC2_128_CBC: + case CKM_PBE_SHA1_RC4_40: + case CKM_PBE_SHA1_RC4_128: + case CKM_PBE_SHA1_DES3_EDE_CBC: + case CKM_PBE_SHA1_DES2_EDE_CBC: + return type; + default: + return pk11_lookup(type)->keyGen; + } +} + +/* + * get the mechanism block size + */ +int +PK11_GetBlockSize(CK_MECHANISM_TYPE type,SECItem *params) +{ + CK_RC5_PARAMS *rc5_params; + CK_RC5_CBC_PARAMS *rc5_cbc_params; + switch (type) { + case CKM_RC5_ECB: + if ((params) && (params->data)) { + rc5_params = (CK_RC5_PARAMS *) params->data; + return (rc5_params->ulWordsize)*2; + } + return 8; + case CKM_RC5_CBC: + case CKM_RC5_CBC_PAD: + if ((params) && (params->data)) { + rc5_cbc_params = (CK_RC5_CBC_PARAMS *) params->data; + return (rc5_cbc_params->ulWordsize)*2; + } + return 8; + case CKM_DES_ECB: + case CKM_DES3_ECB: + case CKM_RC2_ECB: + case CKM_IDEA_ECB: + case CKM_CAST_ECB: + case CKM_CAST3_ECB: + case CKM_CAST5_ECB: + case CKM_RC2_CBC: + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_DES_CBC: + case CKM_DES3_CBC: + case CKM_IDEA_CBC: + case CKM_CAST_CBC: + case CKM_CAST3_CBC: + case CKM_CAST5_CBC: + case CKM_DES_CBC_PAD: + case CKM_DES3_CBC_PAD: + case CKM_RC2_CBC_PAD: + case CKM_IDEA_CBC_PAD: + case CKM_CAST_CBC_PAD: + case CKM_CAST3_CBC_PAD: + case CKM_CAST5_CBC_PAD: + case CKM_PBE_MD2_DES_CBC: + case CKM_PBE_MD5_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: + case CKM_PBE_SHA1_RC2_128_CBC: + case CKM_PBE_SHA1_DES3_EDE_CBC: + case CKM_PBE_SHA1_DES2_EDE_CBC: + return 8; + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + return 4; + case CKM_BATON_ECB128: + case CKM_BATON_CBC128: + case CKM_BATON_COUNTER: + case CKM_BATON_SHUFFLE: + case CKM_JUNIPER_ECB128: + case CKM_JUNIPER_CBC128: + case CKM_JUNIPER_COUNTER: + case CKM_JUNIPER_SHUFFLE: + return 16; + case CKM_BATON_ECB96: + return 12; + case CKM_RC4: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: + case CKM_PBE_SHA1_RC4_40: + case CKM_PBE_SHA1_RC4_128: + return 0; + case CKM_RSA_PKCS: + case CKM_RSA_9796: + case CKM_RSA_X_509: + /*actually it's the modulus length of the key!*/ + return -1; /* failure */ + default: + return pk11_lookup(type)->blockSize; + } +} + +/* + * get the iv length + */ +int +PK11_GetIVLength(CK_MECHANISM_TYPE type) +{ + switch (type) { + case CKM_DES_ECB: + case CKM_DES3_ECB: + case CKM_RC2_ECB: + case CKM_IDEA_ECB: + case CKM_SKIPJACK_WRAP: + case CKM_BATON_WRAP: + case CKM_RC5_ECB: + case CKM_CAST_ECB: + case CKM_CAST3_ECB: + case CKM_CAST5_ECB: + return 0; + case CKM_RC2_CBC: + case CKM_DES_CBC: + case CKM_DES3_CBC: + case CKM_IDEA_CBC: + case CKM_PBE_MD2_DES_CBC: + case CKM_PBE_MD5_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: + case CKM_PBE_SHA1_RC2_128_CBC: + case CKM_PBE_SHA1_DES3_EDE_CBC: + case CKM_PBE_SHA1_DES2_EDE_CBC: + case CKM_RC5_CBC: + case CKM_CAST_CBC: + case CKM_CAST3_CBC: + case CKM_CAST5_CBC: + case CKM_RC2_CBC_PAD: + case CKM_DES_CBC_PAD: + case CKM_DES3_CBC_PAD: + case CKM_IDEA_CBC_PAD: + case CKM_RC5_CBC_PAD: + case CKM_CAST_CBC_PAD: + case CKM_CAST3_CBC_PAD: + case CKM_CAST5_CBC_PAD: + return 8; + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + case CKM_BATON_ECB128: + case CKM_BATON_ECB96: + case CKM_BATON_CBC128: + case CKM_BATON_COUNTER: + case CKM_BATON_SHUFFLE: + case CKM_JUNIPER_ECB128: + case CKM_JUNIPER_CBC128: + case CKM_JUNIPER_COUNTER: + case CKM_JUNIPER_SHUFFLE: + return 24; + case CKM_RC4: + case CKM_RSA_PKCS: + case CKM_RSA_9796: + case CKM_RSA_X_509: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: + case CKM_PBE_SHA1_RC4_40: + case CKM_PBE_SHA1_RC4_128: + return 0; + default: + return pk11_lookup(type)->iv; + } +} + + +/* These next two utilities are here to help facilitate future + * Dynamic Encrypt/Decrypt symetric key mechanisms, and to allow functions + * like SSL and S-MIME to automatically add them. + */ +SECItem * +PK11_ParamFromIV(CK_MECHANISM_TYPE type,SECItem *iv) +{ + CK_RC2_CBC_PARAMS *rc2_params = NULL; + CK_RC2_PARAMS *rc2_ecb_params = NULL; + CK_RC5_PARAMS *rc5_params = NULL; + CK_RC5_CBC_PARAMS *rc5_cbc_params = NULL; + SECItem *param; + + param = (SECItem *)PORT_Alloc(sizeof(SECItem)); + if (param == NULL) return NULL; + param->data = NULL; + param->len = 0; + switch (type) { + case CKM_DES_ECB: + case CKM_DES3_ECB: + case CKM_RSA_PKCS: + case CKM_RSA_X_509: + case CKM_RSA_9796: + case CKM_IDEA_ECB: + case CKM_CDMF_ECB: + case CKM_CAST_ECB: + case CKM_CAST3_ECB: + case CKM_CAST5_ECB: + case CKM_RC4: + break; + case CKM_RC2_ECB: + rc2_ecb_params = (CK_RC2_PARAMS *)PORT_Alloc(sizeof(CK_RC2_PARAMS)); + if (rc2_ecb_params == NULL) break; + /* Maybe we should pass the key size in too to get this value? */ + *rc2_ecb_params = 128; + param->data = (unsigned char *) rc2_ecb_params; + param->len = sizeof(CK_RC2_PARAMS); + break; + case CKM_RC2_CBC: + case CKM_RC2_CBC_PAD: + rc2_params = (CK_RC2_CBC_PARAMS *)PORT_Alloc(sizeof(CK_RC2_CBC_PARAMS)); + if (rc2_params == NULL) break; + /* Maybe we should pass the key size in too to get this value? */ + rc2_params->ulEffectiveBits = 128; + if (iv && iv->data) + PORT_Memcpy(rc2_params->iv,iv->data,sizeof(rc2_params->iv)); + param->data = (unsigned char *) rc2_params; + param->len = sizeof(CK_RC2_CBC_PARAMS); + break; + case CKM_RC5_CBC: + case CKM_RC5_CBC_PAD: + rc5_cbc_params = (CK_RC5_CBC_PARAMS *) + PORT_Alloc(sizeof(CK_RC5_CBC_PARAMS) + ((iv) ? iv->len : 0)); + if (rc5_cbc_params == NULL) break; + if (iv && iv->data) { + rc5_cbc_params->pIv = ((CK_BYTE_PTR) rc5_cbc_params) + + sizeof(CK_RC5_CBC_PARAMS); + PORT_Memcpy(rc5_cbc_params->pIv,iv->data,iv->len); + rc5_cbc_params->ulIvLen = iv->len; + rc5_cbc_params->ulWordsize = iv->len/2; + } else { + rc5_cbc_params->ulWordsize = 4; + rc5_cbc_params->pIv = NULL; + rc5_cbc_params->ulIvLen = iv->len; + } + rc5_cbc_params->ulRounds = 16; + param->data = (unsigned char *) rc5_cbc_params; + param->len = sizeof(CK_RC5_CBC_PARAMS); + break; + case CKM_RC5_ECB: + rc5_params = (CK_RC5_PARAMS *)PORT_Alloc(sizeof(CK_RC5_PARAMS)); + if (rc5_params == NULL) break; + if (iv && iv->data && iv->len) { + rc5_params->ulWordsize = iv->len/2; + } else { + rc5_params->ulWordsize = 4; + } + rc5_params->ulRounds = 16; + param->data = (unsigned char *) rc5_params; + param->len = sizeof(CK_RC5_PARAMS); + break; + case CKM_DES_CBC: + case CKM_DES3_CBC: + case CKM_IDEA_CBC: + case CKM_CDMF_CBC: + case CKM_CAST_CBC: + case CKM_CAST3_CBC: + case CKM_CAST5_CBC: + case CKM_DES_CBC_PAD: + case CKM_DES3_CBC_PAD: + case CKM_IDEA_CBC_PAD: + case CKM_CDMF_CBC_PAD: + case CKM_CAST_CBC_PAD: + case CKM_CAST3_CBC_PAD: + case CKM_CAST5_CBC_PAD: + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + case CKM_BATON_ECB128: + case CKM_BATON_ECB96: + case CKM_BATON_CBC128: + case CKM_BATON_COUNTER: + case CKM_BATON_SHUFFLE: + case CKM_JUNIPER_ECB128: + case CKM_JUNIPER_CBC128: + case CKM_JUNIPER_COUNTER: + case CKM_JUNIPER_SHUFFLE: + if ((iv == NULL) || (iv->data == NULL)) break; + param->data = (unsigned char*)PORT_Alloc(iv->len); + if (param->data != NULL) { + PORT_Memcpy(param->data,iv->data,iv->len); + param->len = iv->len; + } + break; + /* unknown mechanism, pass IV in if it's there */ + default: + if (pk11_lookup(type)->iv == 0) { + break; + } + if ((iv == NULL) || (iv->data == NULL)) { + break; + } + param->data = (unsigned char*)PORT_Alloc(iv->len); + if (param->data != NULL) { + PORT_Memcpy(param->data,iv->data,iv->len); + param->len = iv->len; + } + break; + } + return param; +} + +unsigned char * +PK11_IVFromParam(CK_MECHANISM_TYPE type,SECItem *param,int *len) +{ + CK_RC2_CBC_PARAMS *rc2_params; + CK_RC5_CBC_PARAMS *rc5_cbc_params; + + *len = 0; + switch (type) { + case CKM_DES_ECB: + case CKM_DES3_ECB: + case CKM_RSA_PKCS: + case CKM_RSA_X_509: + case CKM_RSA_9796: + case CKM_IDEA_ECB: + case CKM_CDMF_ECB: + case CKM_CAST_ECB: + case CKM_CAST3_ECB: + case CKM_CAST5_ECB: + case CKM_RC4: + return NULL; + case CKM_RC2_ECB: + return NULL; + case CKM_RC2_CBC: + case CKM_RC2_CBC_PAD: + rc2_params = (CK_RC2_CBC_PARAMS *)param->data; + *len = sizeof(rc2_params->iv); + return &rc2_params->iv[0]; + case CKM_RC5_CBC: + case CKM_RC5_CBC_PAD: + rc5_cbc_params = (CK_RC5_CBC_PARAMS *) param->data; + *len = rc5_cbc_params->ulIvLen; + return rc5_cbc_params->pIv; + case CKM_DES_CBC: + case CKM_DES3_CBC: + case CKM_IDEA_CBC: + case CKM_CDMF_CBC: + case CKM_CAST_CBC: + case CKM_CAST3_CBC: + case CKM_CAST5_CBC: + case CKM_DES_CBC_PAD: + case CKM_DES3_CBC_PAD: + case CKM_IDEA_CBC_PAD: + case CKM_CDMF_CBC_PAD: + case CKM_CAST_CBC_PAD: + case CKM_CAST3_CBC_PAD: + case CKM_CAST5_CBC_PAD: + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + case CKM_BATON_ECB128: + case CKM_BATON_ECB96: + case CKM_BATON_CBC128: + case CKM_BATON_COUNTER: + case CKM_BATON_SHUFFLE: + case CKM_JUNIPER_ECB128: + case CKM_JUNIPER_CBC128: + case CKM_JUNIPER_COUNTER: + case CKM_JUNIPER_SHUFFLE: + break; + /* unknown mechanism, pass IV in if it's there */ + default: + break; + } + if (param->data) { + *len = param->len; + } + return param->data; +} + +typedef struct sec_rc5cbcParameterStr { + SECItem version; + SECItem rounds; + SECItem blockSizeInBits; + SECItem iv; +} sec_rc5cbcParameter; + +static const SEC_ASN1Template sec_rc5ecb_parameter_template[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(sec_rc5cbcParameter) }, + { SEC_ASN1_INTEGER, + offsetof(sec_rc5cbcParameter,version) }, + { SEC_ASN1_INTEGER, + offsetof(sec_rc5cbcParameter,rounds) }, + { SEC_ASN1_INTEGER, + offsetof(sec_rc5cbcParameter,blockSizeInBits) }, + { 0 } +}; + +static const SEC_ASN1Template sec_rc5cbc_parameter_template[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(sec_rc5cbcParameter) }, + { SEC_ASN1_INTEGER, + offsetof(sec_rc5cbcParameter,version) }, + { SEC_ASN1_INTEGER, + offsetof(sec_rc5cbcParameter,rounds) }, + { SEC_ASN1_INTEGER, + offsetof(sec_rc5cbcParameter,blockSizeInBits) }, + { SEC_ASN1_OCTET_STRING, + offsetof(sec_rc5cbcParameter,iv) }, + { 0 } +}; + +typedef struct sec_rc2cbcParameterStr { + SECItem rc2ParameterVersion; + SECItem iv; +} sec_rc2cbcParameter; + +static const SEC_ASN1Template sec_rc2cbc_parameter_template[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(sec_rc2cbcParameter) }, + { SEC_ASN1_INTEGER, + offsetof(sec_rc2cbcParameter,rc2ParameterVersion) }, + { SEC_ASN1_OCTET_STRING, + offsetof(sec_rc2cbcParameter,iv) }, + { 0 } +}; + +static const SEC_ASN1Template sec_rc2ecb_parameter_template[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(sec_rc2cbcParameter) }, + { SEC_ASN1_INTEGER, + offsetof(sec_rc2cbcParameter,rc2ParameterVersion) }, + { 0 } +}; + +/* S/MIME picked id values to represent differnt keysizes */ +/* I do have a formula, but it ain't pretty, and it only works because you + * can always match three points to a parabola:) */ +static unsigned char rc2_map(SECItem *version) +{ + long x; + + x = DER_GetInteger(version); + + switch (x) { + case 58: return 128; + case 120: return 64; + case 160: return 40; + } + return 128; +} + +static unsigned long rc2_unmap(unsigned long x) +{ + switch (x) { + case 128: return 58; + case 64: return 120; + case 40: return 160; + } + return 58; +} + + +/* + * Helper function to decode a PKCS5 DER encode paramter block into a PKCS #11 + * PBE_Parameter structure. + */ +SECStatus +pk11_pbe_decode(SECAlgorithmID *algid, SECItem *mech) +{ + CK_PBE_PARAMS *pbe_params = NULL; + SEC_PKCS5PBEParameter *p5_param; + SECItem *p5_misc = NULL; + int paramSize = 0; + + p5_param = SEC_PKCS5GetPBEParameter(algid); + if(p5_param == NULL) { + return SECFailure; + } + + + p5_misc = &p5_param->salt; + paramSize = sizeof(CK_PBE_PARAMS); + + pbe_params = (CK_PBE_PARAMS *)PORT_ZAlloc(paramSize); + if (pbe_params == NULL) { + SEC_PKCS5DestroyPBEParameter(p5_param); + return SECFailure; + } + + /* get salt */ + pbe_params->pSalt = (CK_CHAR_PTR)PORT_ZAlloc(p5_misc->len); + if (pbe_params->pSalt == CK_NULL_PTR) { + goto loser; + } + PORT_Memcpy(pbe_params->pSalt, p5_misc->data, p5_misc->len); + pbe_params->ulSaltLen = (CK_ULONG) p5_misc->len; + + /* get iteration count */ + p5_misc = &p5_param->iteration; + pbe_params->ulIteration = (CK_ULONG) DER_GetInteger(p5_misc); + + /* copy into the mechanism sec item */ + mech->data = (unsigned char *)pbe_params; + mech->len = paramSize; + SEC_PKCS5DestroyPBEParameter(p5_param); + return SECSuccess; + +loser: + if (pbe_params->pSalt != CK_NULL_PTR) { + PORT_Free(pbe_params->pSalt); + } + PORT_Free(pbe_params); + SEC_PKCS5DestroyPBEParameter(p5_param); + return SECFailure; +} + +/* Generate a mechaism param from a type, and iv. */ +SECItem * +PK11_ParamFromAlgid(SECAlgorithmID *algid) { + CK_RC2_CBC_PARAMS *rc2_params = NULL; + CK_RC2_PARAMS *rc2_ecb_params = NULL; + CK_RC5_CBC_PARAMS *rc5_params_cbc; + CK_RC5_PARAMS *rc5_params_ecb; + SECItem iv; + sec_rc2cbcParameter rc2; + sec_rc5cbcParameter rc5; + SECItem *mech; + CK_MECHANISM_TYPE type; + SECOidTag algtag; + SECStatus rv; + + algtag = SECOID_GetAlgorithmTag(algid); + type = PK11_AlgtagToMechanism(algtag); + + mech = (SECItem *) PORT_Alloc(sizeof(SECItem)); + if (mech == NULL) return NULL; + + + /* handle the complicated cases */ + switch (type) { + case CKM_RC2_ECB: + rv = SEC_ASN1DecodeItem(NULL, &rc2 ,sec_rc2ecb_parameter_template, + &(algid->parameters)); + if (rv != SECSuccess) { + PORT_Free(mech); + return NULL; + } + rc2_ecb_params = (CK_RC2_PARAMS *)PORT_Alloc(sizeof(CK_RC2_PARAMS)); + if (rc2_ecb_params == NULL) { + PORT_Free(rc2.rc2ParameterVersion.data); + PORT_Free(mech); + return NULL; + } + *rc2_ecb_params = rc2_map(&rc2.rc2ParameterVersion); + PORT_Free(rc2.rc2ParameterVersion.data); + mech->data = (unsigned char *) rc2_ecb_params; + mech->len = sizeof(CK_RC2_PARAMS); + return mech; + case CKM_RC2_CBC: + case CKM_RC2_CBC_PAD: + rv = SEC_ASN1DecodeItem(NULL, &rc2 ,sec_rc2cbc_parameter_template, + &(algid->parameters)); + if (rv != SECSuccess) { + PORT_Free(mech); + return NULL; + } + rc2_params = (CK_RC2_CBC_PARAMS *)PORT_Alloc(sizeof(CK_RC2_CBC_PARAMS)); + if (rc2_params == NULL) { + PORT_Free(rc2.iv.data); + PORT_Free(rc2.rc2ParameterVersion.data); + PORT_Free(mech); + return NULL; + } + rc2_params->ulEffectiveBits = rc2_map(&rc2.rc2ParameterVersion); + PORT_Free(rc2.rc2ParameterVersion.data); + PORT_Memcpy(rc2_params->iv,rc2.iv.data,sizeof(rc2_params->iv)); + PORT_Free(rc2.iv.data); + mech->data = (unsigned char *) rc2_params; + mech->len = sizeof(CK_RC2_CBC_PARAMS); + return mech; + case CKM_RC5_ECB: + rv = SEC_ASN1DecodeItem(NULL, &rc5 ,sec_rc5ecb_parameter_template, + &(algid->parameters)); + if (rv != SECSuccess) { + PORT_Free(mech); + return NULL; + } + rc5_params_ecb=(CK_RC5_PARAMS *)PORT_Alloc(sizeof(CK_RC5_PARAMS)); + PORT_Free(rc5.version.data); + if (rc5_params_ecb == NULL) { + PORT_Free(rc5.rounds.data); + PORT_Free(rc5.blockSizeInBits.data); + PORT_Free(mech); + return NULL; + } + rc5_params_ecb->ulRounds = DER_GetInteger(&rc5.rounds); + rc5_params_ecb->ulWordsize = DER_GetInteger(&rc5.blockSizeInBits)/8; + PORT_Free(rc5.rounds.data); + PORT_Free(rc5.blockSizeInBits.data); + mech->data = (unsigned char *) rc5_params_ecb; + mech->len = sizeof(CK_RC5_PARAMS); + return mech; + case CKM_RC5_CBC: + case CKM_RC5_CBC_PAD: + rv = SEC_ASN1DecodeItem(NULL, &rc5 ,sec_rc5cbc_parameter_template, + &(algid->parameters)); + if (rv != SECSuccess) { + PORT_Free(mech); + return NULL; + } + rc5_params_cbc = (CK_RC5_CBC_PARAMS *) + PORT_Alloc(sizeof(CK_RC5_CBC_PARAMS) + rc5.iv.len); + PORT_Free(rc5.version.data); + if (rc2_params == NULL) { + PORT_Free(rc5.iv.data); + PORT_Free(rc5.rounds.data); + PORT_Free(rc5.blockSizeInBits.data); + PORT_Free(mech); + return NULL; + } + rc5_params_cbc->ulRounds = DER_GetInteger(&rc5.rounds); + rc5_params_cbc->ulWordsize = DER_GetInteger(&rc5.blockSizeInBits)/8; + PORT_Free(rc5.rounds.data); + PORT_Free(rc5.blockSizeInBits.data); + rc5_params_cbc->pIv = ((CK_BYTE_PTR)rc5_params_cbc) + + sizeof(CK_RC5_CBC_PARAMS); + PORT_Memcpy(rc5_params_cbc->pIv,rc5.iv.data,rc5.iv.len); + rc5_params_cbc->ulIvLen = rc5.iv.len; + PORT_Free(rc5.iv.data); + mech->data = (unsigned char *) rc5_params_cbc; + mech->len = sizeof(CK_RC5_CBC_PARAMS); + return mech; + case CKM_PBE_MD2_DES_CBC: + case CKM_PBE_MD5_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: + case CKM_PBE_SHA1_DES2_EDE_CBC: + case CKM_PBE_SHA1_DES3_EDE_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: + case CKM_PBE_SHA1_RC2_128_CBC: + case CKM_PBE_SHA1_RC4_40: + case CKM_PBE_SHA1_RC4_128: + rv = pk11_pbe_decode(algid,mech); + if (rv != SECSuccess) { + PORT_Free(mech); + return NULL; + } + return mech; + default: + /* must be a simple case */ + break; + } + + /* simple cases are simpley Octect encoded IV's */ + rv = SEC_ASN1DecodeItem(NULL, &iv, SEC_OctetStringTemplate, + &(algid->parameters)); + if (rv != SECSuccess) { + iv.data = NULL; + iv.len = 0; + } + + rv = SECSuccess; + switch (type) { + case CKM_RC4: + case CKM_DES_ECB: + case CKM_DES3_ECB: + case CKM_IDEA_ECB: + case CKM_CDMF_ECB: + case CKM_CAST_ECB: + case CKM_CAST3_ECB: + case CKM_CAST5_ECB: + mech->data = NULL; + mech->len = 0; + break; + default: + if (pk11_lookup(type)->iv == 0) { + mech->data = NULL; + mech->len = 0; + break; + } + case CKM_DES_CBC: + case CKM_DES3_CBC: + case CKM_IDEA_CBC: + case CKM_CDMF_CBC: + case CKM_CAST_CBC: + case CKM_CAST3_CBC: + case CKM_CAST5_CBC: + case CKM_DES_CBC_PAD: + case CKM_DES3_CBC_PAD: + case CKM_IDEA_CBC_PAD: + case CKM_CDMF_CBC_PAD: + case CKM_CAST_CBC_PAD: + case CKM_CAST3_CBC_PAD: + case CKM_CAST5_CBC_PAD: + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + case CKM_BATON_ECB128: + case CKM_BATON_ECB96: + case CKM_BATON_CBC128: + case CKM_BATON_COUNTER: + case CKM_BATON_SHUFFLE: + case CKM_JUNIPER_ECB128: + case CKM_JUNIPER_CBC128: + case CKM_JUNIPER_COUNTER: + case CKM_JUNIPER_SHUFFLE: + if (iv.data == NULL) { + rv = SECFailure; + break; + } + mech->data = (unsigned char*)PORT_Alloc(iv.len); + if (mech->data == NULL) { + rv = SECFailure; + break; + } + PORT_Memcpy(mech->data,iv.data,iv.len); + mech->len = iv.len; + break; + } + if (iv.data) PORT_Free(iv.data); + if (rv != SECSuccess) { + SECITEM_FreeItem(mech,PR_TRUE); + return NULL; + } + return mech; +} + +SECStatus +PK11_SeedRandom(PK11SlotInfo *slot, unsigned char *data, int len) { + CK_RV crv; + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_SeedRandom(slot->session,data, (CK_ULONG)len); + PK11_ExitSlotMonitor(slot); + return (crv != CKR_OK) ? SECFailure : SECSuccess; +} + +/* Attempts to update the Best Slot for "FAKE RANDOM" generation. +** If that's not the internal slot, then it also attempts to update the +** internal slot. +** The return value indicates if the INTERNAL slot was updated OK. +*/ +SECStatus +PK11_RandomUpdate(void *data, size_t bytes) +{ + PK11SlotInfo *slot; + PRBool bestIsInternal; + SECStatus status; + + slot = PK11_GetBestSlot(CKM_FAKE_RANDOM, NULL); + if (slot == NULL) { + slot = PK11_GetInternalSlot(); + if (!slot) + return SECFailure; + } + + bestIsInternal = PK11_IsInternal(slot); + status = PK11_SeedRandom(slot, data, bytes); + PK11_FreeSlot(slot); + + if (!bestIsInternal) { + /* do internal slot, too. */ + slot = PK11_GetInternalSlot(); /* can't fail */ + status = PK11_SeedRandom(slot, data, bytes); + PK11_FreeSlot(slot); + } + return status; +} + + +SECStatus +PK11_GenerateRandom(unsigned char *data,int len) { + PK11SlotInfo *slot; + CK_RV crv; + + slot = PK11_GetBestSlot(CKM_FAKE_RANDOM,NULL); + if (slot == NULL) return SECFailure; + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GenerateRandom(slot->session,data, + (CK_ULONG)len); + PK11_ExitSlotMonitor(slot); + PK11_FreeSlot(slot); + return (crv != CKR_OK) ? SECFailure : SECSuccess; +} + +/* + * Generate an IV for the given mechanism + */ +static SECStatus +pk11_GenIV(CK_MECHANISM_TYPE type, SECItem *iv) { + int iv_size = PK11_GetIVLength(type); + SECStatus rv; + + iv->len = iv_size; + if (iv_size == 0) { + iv->data = NULL; + return SECSuccess; + } + + iv->data = (unsigned char *) PORT_Alloc(iv_size); + if (iv->data == NULL) { + iv->len = 0; + return SECFailure; + } + + rv = PK11_GenerateRandom(iv->data,iv->len); + if (rv != SECSuccess) { + PORT_Free(iv->data); + iv->data = NULL; iv->len = 0; + return SECFailure; + } + return SECSuccess; +} + + +/* + * create a new paramter block from the passed in MECHANISM and the + * key. Use Netscape's S/MIME Rules for the New param block. + */ +SECItem * +PK11_GenerateNewParam(CK_MECHANISM_TYPE type, PK11SymKey *key) { + CK_RC2_CBC_PARAMS *rc2_params; + CK_RC2_PARAMS *rc2_ecb_params; + SECItem *mech; + SECItem iv; + SECStatus rv; + + + mech = (SECItem *) PORT_Alloc(sizeof(SECItem)); + if (mech == NULL) return NULL; + + rv = SECSuccess; + switch (type) { + case CKM_RC4: + case CKM_DES_ECB: + case CKM_DES3_ECB: + case CKM_IDEA_ECB: + case CKM_CDMF_ECB: + case CKM_CAST_ECB: + case CKM_CAST3_ECB: + case CKM_CAST5_ECB: + mech->data = NULL; + mech->len = 0; + break; + case CKM_RC2_ECB: + rc2_ecb_params = (CK_RC2_PARAMS *)PORT_Alloc(sizeof(CK_RC2_PARAMS)); + if (rc2_ecb_params == NULL) { + rv = SECFailure; + break; + } + /* NOTE PK11_GetKeyLength can return -1 if the key isn't and RC2, RC5, + * or RC4 key. Of course that wouldn't happen here doing RC2:).*/ + *rc2_ecb_params = PK11_GetKeyLength(key)*8; + mech->data = (unsigned char *) rc2_ecb_params; + mech->len = sizeof(CK_RC2_PARAMS); + break; + case CKM_RC2_CBC: + case CKM_RC2_CBC_PAD: + rv = pk11_GenIV(type,&iv); + if (rv != SECSuccess) { + break; + } + rc2_params = (CK_RC2_CBC_PARAMS *)PORT_Alloc(sizeof(CK_RC2_CBC_PARAMS)); + if (rc2_params == NULL) { + PORT_Free(iv.data); + rv = SECFailure; + break; + } + /* NOTE PK11_GetKeyLength can return -1 if the key isn't and RC2, RC5, + * or RC4 key. Of course that wouldn't happen here doing RC2:).*/ + rc2_params->ulEffectiveBits = PK11_GetKeyLength(key)*8; + if (iv.data) + PORT_Memcpy(rc2_params->iv,iv.data,sizeof(rc2_params->iv)); + mech->data = (unsigned char *) rc2_params; + mech->len = sizeof(CK_RC2_CBC_PARAMS); + PORT_Free(iv.data); + break; + case CKM_RC5_ECB: + PORT_Free(mech); + return PK11_ParamFromIV(type,NULL); + case CKM_RC5_CBC: + case CKM_RC5_CBC_PAD: + rv = pk11_GenIV(type,&iv); + if (rv != SECSuccess) { + break; + } + PORT_Free(mech); + return PK11_ParamFromIV(type,&iv); + default: + if (pk11_lookup(type)->iv == 0) { + mech->data = NULL; + mech->len = 0; + break; + } + case CKM_DES_CBC: + case CKM_DES3_CBC: + case CKM_IDEA_CBC: + case CKM_CDMF_CBC: + case CKM_CAST_CBC: + case CKM_CAST3_CBC: + case CKM_CAST5_CBC: + case CKM_DES_CBC_PAD: + case CKM_DES3_CBC_PAD: + case CKM_IDEA_CBC_PAD: + case CKM_CDMF_CBC_PAD: + case CKM_CAST_CBC_PAD: + case CKM_CAST3_CBC_PAD: + case CKM_CAST5_CBC_PAD: + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + case CKM_BATON_ECB128: + case CKM_BATON_ECB96: + case CKM_BATON_CBC128: + case CKM_BATON_COUNTER: + case CKM_BATON_SHUFFLE: + case CKM_JUNIPER_ECB128: + case CKM_JUNIPER_CBC128: + case CKM_JUNIPER_COUNTER: + case CKM_JUNIPER_SHUFFLE: + rv = pk11_GenIV(type,&iv); + if (rv != SECSuccess) { + break; + } + mech->data = (unsigned char*)PORT_Alloc(iv.len); + if (mech->data == NULL) { + PORT_Free(iv.data); + rv = SECFailure; + break; + } + PORT_Memcpy(mech->data,iv.data,iv.len); + mech->len = iv.len; + PORT_Free(iv.data); + break; + } + if (rv != SECSuccess) { + SECITEM_FreeItem(mech,PR_TRUE); + return NULL; + } + return mech; + +} + +#define RC5_V10 0x10 + +/* turn a PKCS #11 parameter into a DER Encoded Algorithm ID */ +SECStatus +PK11_ParamToAlgid(SECOidTag algTag, SECItem *param, + PRArenaPool *arena, SECAlgorithmID *algid) { + CK_RC2_CBC_PARAMS *rc2_params; + sec_rc2cbcParameter rc2; + CK_RC5_CBC_PARAMS *rc5_params; + sec_rc5cbcParameter rc5; + CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(algTag); + SECItem *newParams = NULL; + SECStatus rv = SECFailure; + unsigned long rc2version; + + rv = SECSuccess; + switch (type) { + case CKM_RC4: + case CKM_DES_ECB: + case CKM_DES3_ECB: + case CKM_IDEA_ECB: + case CKM_CDMF_ECB: + case CKM_CAST_ECB: + case CKM_CAST3_ECB: + case CKM_CAST5_ECB: + newParams = NULL; + rv = SECSuccess; + break; + case CKM_RC2_ECB: + break; + case CKM_RC2_CBC: + case CKM_RC2_CBC_PAD: + rc2_params = (CK_RC2_CBC_PARAMS *)param->data; + rc2version = rc2_unmap(rc2_params->ulEffectiveBits); + if (SEC_ASN1EncodeUnsignedInteger (NULL, &(rc2.rc2ParameterVersion), + rc2version) == NULL) + break; + rc2.iv.data = rc2_params->iv; + rc2.iv.len = sizeof(rc2_params->iv); + newParams = SEC_ASN1EncodeItem (NULL, NULL, &rc2, + sec_rc2cbc_parameter_template); + PORT_Free(rc2.rc2ParameterVersion.data); + if (newParams == NULL) + break; + rv = SECSuccess; + break; + + case CKM_RC5_ECB: /* well not really... */ + break; + case CKM_RC5_CBC: + case CKM_RC5_CBC_PAD: + rc5_params = (CK_RC5_CBC_PARAMS *)param->data; + if (SEC_ASN1EncodeUnsignedInteger (NULL, &rc5.version, RC5_V10) == NULL) + break; + if (SEC_ASN1EncodeUnsignedInteger (NULL, &rc5.blockSizeInBits, + rc5_params->ulWordsize*8) == NULL) { + PORT_Free(rc5.version.data); + break; + } + if (SEC_ASN1EncodeUnsignedInteger (NULL, &rc5.rounds, + rc5_params->ulWordsize*8) == NULL) { + PORT_Free(rc5.blockSizeInBits.data); + PORT_Free(rc5.version.data); + break; + } + rc5.iv.data = rc5_params->pIv; + rc5.iv.len = rc5_params->ulIvLen; + newParams = SEC_ASN1EncodeItem (NULL, NULL, &rc5, + sec_rc5cbc_parameter_template); + PORT_Free(rc5.version.data); + PORT_Free(rc5.blockSizeInBits.data); + PORT_Free(rc5.rounds.data); + if (newParams == NULL) + break; + rv = SECSuccess; + break; + case CKM_PBE_MD2_DES_CBC: + case CKM_PBE_MD5_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: + case CKM_PBE_SHA1_DES3_EDE_CBC: + case CKM_PBE_SHA1_DES2_EDE_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: + case CKM_PBE_SHA1_RC2_128_CBC: + case CKM_PBE_SHA1_RC4_40: + case CKM_PBE_SHA1_RC4_128: + return PBE_PK11ParamToAlgid(algTag, param, arena, algid); + default: + if (pk11_lookup(type)->iv == 0) { + rv = SECSuccess; + newParams = NULL; + break; + } + case CKM_DES_CBC: + case CKM_DES3_CBC: + case CKM_IDEA_CBC: + case CKM_CDMF_CBC: + case CKM_CAST_CBC: + case CKM_CAST3_CBC: + case CKM_CAST5_CBC: + case CKM_DES_CBC_PAD: + case CKM_DES3_CBC_PAD: + case CKM_IDEA_CBC_PAD: + case CKM_CDMF_CBC_PAD: + case CKM_CAST_CBC_PAD: + case CKM_CAST3_CBC_PAD: + case CKM_CAST5_CBC_PAD: + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + case CKM_BATON_ECB128: + case CKM_BATON_ECB96: + case CKM_BATON_CBC128: + case CKM_BATON_COUNTER: + case CKM_BATON_SHUFFLE: + case CKM_JUNIPER_ECB128: + case CKM_JUNIPER_CBC128: + case CKM_JUNIPER_COUNTER: + case CKM_JUNIPER_SHUFFLE: + newParams = SEC_ASN1EncodeItem(NULL,NULL,param, + SEC_OctetStringTemplate); + rv = SECSuccess; + break; + } + + if (rv != SECSuccess) { + if (newParams) SECITEM_FreeItem(newParams,PR_TRUE); + return rv; + } + + rv = SECOID_SetAlgorithmID(arena, algid, algTag, newParams); + SECITEM_FreeItem(newParams,PR_TRUE); + return rv; +} + +/* turn an OID algorithm tag into a PKCS #11 mechanism. This allows us to + * map OID's directly into the PKCS #11 mechanism we want to call. We find + * this mapping in our standard OID table */ +CK_MECHANISM_TYPE +PK11_AlgtagToMechanism(SECOidTag algTag) { + SECOidData *oid = SECOID_FindOIDByTag(algTag); + + if (oid) return (CK_MECHANISM_TYPE) oid->mechanism; + return CKM_INVALID_MECHANISM; +} + +/* turn a mechanism into an oid. */ +SECOidTag +PK11_MechanismToAlgtag(CK_MECHANISM_TYPE type) { + SECOidData *oid = SECOID_FindOIDByMechanism((unsigned long)type); + + if (oid) return oid->offset; + return SEC_OID_UNKNOWN; +} + +/* Determine appropriate blocking mechanism, used when wrapping private keys + * which require PKCS padding. If the mechanism does not map to a padding + * mechanism, we simply return the mechanism. + */ +CK_MECHANISM_TYPE +PK11_GetPadMechanism(CK_MECHANISM_TYPE type) { + switch(type) { + case CKM_DES_CBC: + return CKM_DES_CBC_PAD; + case CKM_DES3_CBC: + return CKM_DES3_CBC_PAD; + case CKM_RC2_CBC: + return CKM_RC2_CBC_PAD; + case CKM_CDMF_CBC: + return CKM_CDMF_CBC_PAD; + case CKM_CAST_CBC: + return CKM_CAST_CBC_PAD; + case CKM_CAST3_CBC: + return CKM_CAST3_CBC_PAD; + case CKM_CAST5_CBC: + return CKM_CAST5_CBC_PAD; + case CKM_RC5_CBC: + return CKM_RC5_CBC_PAD; + case CKM_IDEA_CBC: + return CKM_IDEA_CBC_PAD; + default: + break; + } + + return type; +} + +/* + * Build a block big enough to hold the data + */ +SECItem * +PK11_BlockData(SECItem *data,unsigned long size) { + SECItem *newData; + + newData = (SECItem *)PORT_Alloc(sizeof(SECItem)); + if (newData == NULL) return NULL; + + newData->len = (data->len + (size-1))/size; + newData->len *= size; + + newData->data = (unsigned char *) PORT_ZAlloc(newData->len); + if (newData->data == NULL) { + PORT_Free(newData); + return NULL; + } + PORT_Memset(newData->data,newData->len-data->len,newData->len); + PORT_Memcpy(newData->data,data->data,data->len); + return newData; +} + + +SECStatus +PK11_DestroyObject(PK11SlotInfo *slot,CK_OBJECT_HANDLE object) { + CK_RV crv; + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_DestroyObject(slot->session,object); + PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + return SECFailure; + } + return SECSuccess; +} + +SECStatus +PK11_DestroyTokenObject(PK11SlotInfo *slot,CK_OBJECT_HANDLE object) { + CK_RV crv; + SECStatus rv = SECSuccess; + CK_SESSION_HANDLE rwsession; + + + rwsession = PK11_GetRWSession(slot); + + crv = PK11_GETTAB(slot)->C_DestroyObject(rwsession,object); + if (crv != CKR_OK) { + rv = SECFailure; + PORT_SetError(PK11_MapError(crv)); + } + PK11_RestoreROSession(slot,rwsession); + return rv; +} + +/* + * Read in a single attribute into a SECItem. Allocate space for it with + * PORT_Alloc unless an arena is supplied. In the latter case use the arena + * to allocate the space. + */ +SECStatus +PK11_ReadAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id, + CK_ATTRIBUTE_TYPE type, PRArenaPool *arena, SECItem *result) { + CK_ATTRIBUTE attr = { 0, NULL, 0 }; + CK_RV crv; + + attr.type = type; + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,id,&attr,1); + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + if (arena) { + attr.pValue = PORT_ArenaAlloc(arena,attr.ulValueLen); + } else { + attr.pValue = PORT_Alloc(attr.ulValueLen); + } + if (attr.pValue == NULL) return SECFailure; + crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,id,&attr,1); + PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + if (!arena) PORT_Free(attr.pValue); + return SECFailure; + } + + result->data = (unsigned char*)attr.pValue; + result->len = attr.ulValueLen; + + return SECSuccess; +} + +/* + * Read in a single attribute into As a Ulong. + */ +CK_ULONG +PK11_ReadULongAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id, + CK_ATTRIBUTE_TYPE type) { + CK_ATTRIBUTE attr; + CK_ULONG value = CK_UNAVAILABLE_INFORMATION; + CK_RV crv; + + PK11_SETATTRS(&attr,type,&value,sizeof(value)); + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,id,&attr,1); + PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + } + return value; +} + +/* + * check to see if a bool has been set. + */ +CK_BBOOL +PK11_HasAttributeSet( PK11SlotInfo *slot, CK_OBJECT_HANDLE id, + CK_ATTRIBUTE_TYPE type ) +{ + CK_BBOOL ckvalue = CK_FALSE; + CK_ATTRIBUTE theTemplate; + CK_RV crv; + + /* Prepare to retrieve the attribute. */ + PK11_SETATTRS( &theTemplate, type, &ckvalue, sizeof( CK_BBOOL ) ); + + /* Retrieve attribute value. */ + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB( slot )->C_GetAttributeValue( slot->session, id, + &theTemplate, 1 ); + PK11_ExitSlotMonitor(slot); + if( crv != CKR_OK ) { + PORT_SetError( PK11_MapError( crv ) ); + return CK_FALSE; + } + + return ckvalue; +} + +/* + * returns a full list of attributes. Allocate space for them. If an arena is + * provided, allocate space out of the arena. + */ +CK_RV +PK11_GetAttributes(PRArenaPool *arena,PK11SlotInfo *slot, + CK_OBJECT_HANDLE obj,CK_ATTRIBUTE *attr, int count) +{ + int i; + /* make pedantic happy... note that it's only used arena != NULL */ + void *mark = NULL; + CK_RV crv; + + /* + * first get all the lengths of the parameters. + */ + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,obj,attr,count); + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + return crv; + } + + if (arena) { + mark = PORT_ArenaMark(arena); + if (mark == NULL) return CKR_HOST_MEMORY; + } + + /* + * now allocate space to store the results. + */ + for (i=0; i < count; i++) { + if (arena) { + attr[i].pValue = PORT_ArenaAlloc(arena,attr[i].ulValueLen); + if (attr[i].pValue == NULL) { + /* arena failures, just release the mark */ + PORT_ArenaRelease(arena,mark); + PK11_ExitSlotMonitor(slot); + return CKR_HOST_MEMORY; + } + } else { + attr[i].pValue = PORT_Alloc(attr[i].ulValueLen); + if (attr[i].pValue == NULL) { + /* Separate malloc failures, loop to release what we have + * so far */ + int j; + for (j= 0; j < i; j++) { + PORT_Free(attr[j].pValue); + /* don't give the caller pointers to freed memory */ + attr[j].pValue = NULL; + } + PK11_ExitSlotMonitor(slot); + return CKR_HOST_MEMORY; + } + } + } + + /* + * finally get the results. + */ + crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,obj,attr,count); + PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + if (arena) { + PORT_ArenaRelease(arena,mark); + } else { + for (i= 0; i < count; i++) { + PORT_Free(attr[i].pValue); + /* don't give the caller pointers to freed memory */ + attr[i].pValue = NULL; + } + } + } else if (arena && mark) { + PORT_ArenaUnmark(arena,mark); + } + return crv; +} + +/* + * Reset the token to it's initial state. For the internal module, this will + * Purge your keydb, and reset your cert db certs to USER_INIT. + */ +SECStatus +PK11_ResetToken(PK11SlotInfo *slot, char *sso_pwd) +{ + unsigned char tokenName[32]; + int tokenNameLen; + CK_RV crv; + + /* reconstruct the token name */ + tokenNameLen = PORT_Strlen(slot->token_name); + if (tokenNameLen > sizeof(tokenName)) { + tokenNameLen = sizeof(tokenName); + } + + PORT_Memcpy(tokenName,slot->token_name,tokenNameLen); + if (tokenNameLen < sizeof(tokenName)) { + PORT_Memset(&tokenName[tokenNameLen],' ', + sizeof(tokenName)-tokenNameLen); + } + + /* initialize the token */ + PK11_EnterSlotMonitor(slot); + + /* first shutdown the token. Existing sessions will get closed here */ + PK11_GETTAB(slot)->C_CloseAllSessions(slot->slotID); + slot->session = CK_INVALID_SESSION; + PK11_FreeSlotCerts(slot); + + /* now re-init the token */ + crv = PK11_GETTAB(slot)->C_InitToken(slot->slotID, + (unsigned char *)sso_pwd, sso_pwd ? PORT_Strlen(sso_pwd): 0, tokenName); + + /* finally bring the token back up */ + PK11_InitToken(slot,PR_TRUE); + PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + return SECSuccess; +} + + + + +static SECOidTag +pk11_MapPBEMechanismTypeToAlgtag(CK_MECHANISM_TYPE mech) +{ + switch(mech) { + case CKM_PBE_MD2_DES_CBC: + return SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC; + case CKM_PBE_MD5_DES_CBC: + return SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC; + case CKM_NETSCAPE_PBE_SHA1_DES_CBC: + return SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC; + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC; + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC; + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4; + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: + return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4; + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC; + case CKM_PBE_SHA1_RC2_128_CBC: + return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC; + case CKM_PBE_SHA1_RC2_40_CBC: + return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; + case CKM_PBE_SHA1_RC4_40: + return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4; + case CKM_PBE_SHA1_RC4_128: + return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4; + case CKM_PBE_SHA1_DES3_EDE_CBC: + return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC; + case CKM_PBE_SHA1_DES2_EDE_CBC: + return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC; + default: + break; + } + return SEC_OID_UNKNOWN; +} + +CK_RV +PK11_MapPBEMechanismToCryptoMechanism(CK_MECHANISM_PTR pPBEMechanism, + CK_MECHANISM_PTR pCryptoMechanism, + SECItem *pbe_pwd, PRBool faulty3DES) +{ + int iv_len = 0; + CK_PBE_PARAMS_PTR pPBEparams; + CK_RC2_CBC_PARAMS_PTR rc2_params; + CK_ULONG rc2_key_len; + SECStatus rv = SECFailure; + SECAlgorithmID temp_algid; + SECItem param, *iv; + + if((pPBEMechanism == CK_NULL_PTR) || (pCryptoMechanism == CK_NULL_PTR)) { + return CKR_HOST_MEMORY; + } + + pPBEparams = (CK_PBE_PARAMS_PTR)pPBEMechanism->pParameter; + iv_len = PK11_GetIVLength(pPBEMechanism->mechanism); + + if(pPBEparams->pInitVector == CK_NULL_PTR) { + pPBEparams->pInitVector = (CK_CHAR_PTR)PORT_ZAlloc(iv_len); + if(pPBEparams->pInitVector == NULL) { + return CKR_HOST_MEMORY; + } + param.data = (unsigned char*)pPBEMechanism->pParameter; + param.len = pPBEMechanism->ulParameterLen; + rv = PK11_ParamToAlgid(pk11_MapPBEMechanismTypeToAlgtag( + pPBEMechanism->mechanism), + ¶m, NULL, &temp_algid); + if(rv != SECSuccess) { + SECOID_DestroyAlgorithmID(&temp_algid, PR_FALSE); + return CKR_HOST_MEMORY; + } else { + iv = SEC_PKCS5GetIV(&temp_algid, pbe_pwd, faulty3DES); + if((iv == NULL) && (iv_len != 0)) { + SECOID_DestroyAlgorithmID(&temp_algid, PR_FALSE); + return CKR_HOST_MEMORY; + } + SECOID_DestroyAlgorithmID(&temp_algid, PR_FALSE); + if(iv != NULL) { + PORT_Memcpy((char *)pPBEparams->pInitVector, + (char *)iv->data, + iv->len); + SECITEM_ZfreeItem(iv, PR_TRUE); + } + } + } + + switch(pPBEMechanism->mechanism) { + case CKM_PBE_MD2_DES_CBC: + case CKM_PBE_MD5_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_DES_CBC: + pCryptoMechanism->mechanism = CKM_DES_CBC; + goto have_crypto_mechanism; + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + case CKM_PBE_SHA1_DES3_EDE_CBC: + case CKM_PBE_SHA1_DES2_EDE_CBC: + pCryptoMechanism->mechanism = CKM_DES3_CBC; +have_crypto_mechanism: + pCryptoMechanism->pParameter = PORT_Alloc(iv_len); + pCryptoMechanism->ulParameterLen = (CK_ULONG)iv_len; + if(pCryptoMechanism->pParameter == NULL) { + return CKR_HOST_MEMORY; + } + PORT_Memcpy((unsigned char *)(pCryptoMechanism->pParameter), + (unsigned char *)(pPBEparams->pInitVector), + iv_len); + break; + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: + case CKM_PBE_SHA1_RC4_40: + case CKM_PBE_SHA1_RC4_128: + pCryptoMechanism->mechanism = CKM_RC4; + pCryptoMechanism->ulParameterLen = 0; + pCryptoMechanism->pParameter = CK_NULL_PTR; + break; + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: + rc2_key_len = 40; + goto have_key_len; + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + rc2_key_len = 128; +have_key_len: + pCryptoMechanism->mechanism = CKM_RC2_CBC; + pCryptoMechanism->ulParameterLen = (CK_ULONG)sizeof(CK_RC2_CBC_PARAMS); + pCryptoMechanism->pParameter = + (CK_RC2_CBC_PARAMS_PTR)PORT_ZAlloc(sizeof(CK_RC2_CBC_PARAMS)); + if(pCryptoMechanism->pParameter == NULL) { + return CKR_HOST_MEMORY; + } + rc2_params = (CK_RC2_CBC_PARAMS_PTR)pCryptoMechanism->pParameter; + PORT_Memcpy((unsigned char *)rc2_params->iv, + (unsigned char *)pPBEparams->pInitVector, + iv_len); + rc2_params->ulEffectiveBits = rc2_key_len; + break; + default: + return CKR_MECHANISM_INVALID; + } + + return CKR_OK; +} diff --git a/security/nss/lib/pk11wrap/pk11util.c b/security/nss/lib/pk11wrap/pk11util.c new file mode 100644 index 000000000..709bf32c8 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11util.c @@ -0,0 +1,518 @@ +/* + * 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. + */ +/* + * Initialize the PCKS 11 subsystem + */ +#include "seccomon.h" +#include "secmod.h" +#include "prlock.h" +#include "secmodi.h" +#include "pk11func.h" + +/* these are for displaying error messages */ + +static SECMODModuleList *modules = NULL; +static SECMODModule *internalModule = NULL; +static SECMODListLock *moduleLock = NULL; + +extern SECStatus +PK11_UpdateSlotAttribute(PK11SlotInfo *slot, PK11DefaultArrayEntry *entry, + PRBool add); + + +extern int XP_SEC_MODULE_NO_LIB; + +extern PK11DefaultArrayEntry PK11_DefaultArray[]; +extern int num_pk11_default_mechanisms; + +void SECMOD_init(char *dbname) { + SECMODModuleList *thisModule; + int found=0; + SECStatus rv = SECFailure; + + + /* don't initialize twice */ + if (modules) return; + + PK11_InitSlotLists(); + + SECMOD_InitDB(dbname); + + /* + * read in the current modules from the database + */ + modules = SECMOD_ReadPermDB(); + + /* make sure that the internal module is loaded */ + for (thisModule = modules; thisModule ; thisModule = thisModule->next) { + if (thisModule->module->internal) { + found++; + internalModule = SECMOD_ReferenceModule(thisModule->module); + break; + } + } + + if (!found) { + thisModule = modules; + modules = SECMOD_NewModuleListElement(); + modules->module = SECMOD_NewInternal(); + PORT_Assert(modules->module != NULL); + modules->next = thisModule; + SECMOD_AddPermDB(modules->module); + internalModule = SECMOD_ReferenceModule(modules->module); + } + + /* load it first... we need it to verify the external modules + * which we are loading.... */ + rv = SECMOD_LoadModule(internalModule); + if( rv != SECSuccess ) + internalModule = NULL; + + /* Load each new module */ + for (thisModule = modules; thisModule ; thisModule = thisModule->next) { + if( !( thisModule->module->internal ) ) + SECMOD_LoadModule(thisModule->module); + } + + moduleLock = SECMOD_NewListLock(); +} + +/* + * retrieve the internal module + */ +SECMODModule * +SECMOD_GetInternalModule(void) { + return internalModule; +} + +/* called from security/cmd/swfort/instinit, which doesn't need a full + * security LIBRARY (it used the swfortezza code, but it does have to verify + * cert chains against it's own list of certs. We need to initialize the + * security code without any database. + */ +void +secmod_GetInternalModule( SECMODModule *mod) { + internalModule = SECMOD_ReferenceModule(mod); +} + +/* + * get the list of PKCS11 modules that are available. + */ +SECMODModuleList *SECMOD_GetDefaultModuleList() { return modules; } +SECMODListLock *SECMOD_GetDefaultModuleListLock() { return moduleLock; } + + + +/* + * find a module by name, and add a reference to it. + * return that module. + */ +SECMODModule *SECMOD_FindModule(char *name) { + SECMODModuleList *mlp; + SECMODModule *module = NULL; + + SECMOD_GetReadLock(moduleLock); + for(mlp = modules; mlp != NULL; mlp = mlp->next) { + if (PORT_Strcmp(name,mlp->module->commonName) == 0) { + module = mlp->module; + SECMOD_ReferenceModule(module); + break; + } + } + SECMOD_ReleaseReadLock(moduleLock); + + return module; +} + +/* + * find a module by ID, and add a reference to it. + * return that module. + */ +SECMODModule *SECMOD_FindModuleByID(SECMODModuleID id) { + SECMODModuleList *mlp; + SECMODModule *module = NULL; + + SECMOD_GetReadLock(moduleLock); + for(mlp = modules; mlp != NULL; mlp = mlp->next) { + if (id == mlp->module->moduleID) { + module = mlp->module; + SECMOD_ReferenceModule(module); + break; + } + } + SECMOD_ReleaseReadLock(moduleLock); + + return module; +} + +/* + * lookup the Slot module based on it's module ID and slot ID. + */ +PK11SlotInfo *SECMOD_LookupSlot(SECMODModuleID moduleID,CK_SLOT_ID slotID) { + int i; + SECMODModule *module; + + module = SECMOD_FindModuleByID(moduleID); + if (module == NULL) return NULL; + + for (i=0; i < module->slotCount; i++) { + PK11SlotInfo *slot = module->slots[i]; + + if (slot->slotID == slotID) { + SECMOD_DestroyModule(module); + return PK11_ReferenceSlot(slot); + } + } + SECMOD_DestroyModule(module); + return NULL; +} + + +/* + * find a module by name and delete it of the module list + */ +SECStatus +SECMOD_DeleteModule(char *name, int *type) { + SECMODModuleList *mlp; + SECMODModuleList **mlpp; + SECStatus rv = SECFailure; + + + *type = SECMOD_EXTERNAL; + + SECMOD_GetWriteLock(moduleLock); + for(mlpp = &modules,mlp = modules; + mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) { + if (PORT_Strcmp(name,mlp->module->commonName) == 0) { + /* don't delete the internal module */ + if (!mlp->module->internal) { + SECMOD_RemoveList(mlpp,mlp); + /* delete it after we release the lock */ + rv = SECSuccess; + } else if (mlp->module->isFIPS) { + *type = SECMOD_FIPS; + } else { + *type = SECMOD_INTERNAL; + } + break; + } + } + SECMOD_ReleaseWriteLock(moduleLock); + + + if (rv == SECSuccess) { + SECMOD_DeletePermDB(mlp->module); + SECMOD_DestroyModuleListElement(mlp); + } + return rv; +} + +/* + * find a module by name and delete it of the module list + */ +SECStatus +SECMOD_DeleteInternalModule(char *name) { + SECMODModuleList *mlp; + SECMODModuleList **mlpp; + SECStatus rv = SECFailure; + + SECMOD_GetWriteLock(moduleLock); + for(mlpp = &modules,mlp = modules; + mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) { + if (PORT_Strcmp(name,mlp->module->commonName) == 0) { + /* don't delete the internal module */ + if (mlp->module->internal) { + rv = SECSuccess; + SECMOD_RemoveList(mlpp,mlp); + } + break; + } + } + SECMOD_ReleaseWriteLock(moduleLock); + + if (rv == SECSuccess) { + SECMODModule *newModule,*oldModule; + + if (mlp->module->isFIPS) { + newModule = SECMOD_NewInternal(); + } else { + newModule = SECMOD_GetFIPSInternal(); + } + if (newModule == NULL) { + SECMODModuleList *last,*mlp2; + /* we're in pretty deep trouble if this happens...Security + * not going to work well... try to put the old module back on + * the list */ + SECMOD_GetWriteLock(moduleLock); + for(mlp2 = modules; mlp2 != NULL; mlp2 = mlp->next) { + last = mlp2; + } + + if (last == NULL) { + modules = mlp; + } else { + SECMOD_AddList(last,mlp,NULL); + } + SECMOD_ReleaseWriteLock(moduleLock); + return SECFailure; + } + oldModule = internalModule; + internalModule = SECMOD_ReferenceModule(newModule); + SECMOD_AddModule(internalModule); + SECMOD_DestroyModule(oldModule); + SECMOD_DeletePermDB(mlp->module); + SECMOD_DestroyModuleListElement(mlp); + } + return rv; +} + +SECStatus +SECMOD_AddModule(SECMODModule *newModule) { + SECStatus rv; + SECMODModuleList *mlp, *newListElement, *last = NULL; + + /* Test if a module w/ the same name already exists */ + /* and return SECWouldBlock if so. */ + /* We should probably add a new return value such as */ + /* SECDublicateModule, but to minimize ripples, I'll */ + /* give SECWouldBlock a new meaning */ + if (SECMOD_FindModule(newModule->commonName)) { + return SECWouldBlock; + /* module already exists. */ + } + + rv = SECMOD_LoadModule(newModule); + if (rv != SECSuccess) { + return rv; + } + + newListElement = SECMOD_NewModuleListElement(); + if (newListElement == NULL) { + return SECFailure; + } + + SECMOD_AddPermDB(newModule); + + newListElement->module = newModule; + + SECMOD_GetWriteLock(moduleLock); + /* Added it to the end (This is very inefficient, but Adding a module + * on the fly should happen maybe 2-3 times through the life this program + * on a given computer, and this list should be *SHORT*. */ + for(mlp = modules; mlp != NULL; mlp = mlp->next) { + last = mlp; + } + + if (last == NULL) { + modules = newListElement; + } else { + SECMOD_AddList(last,newListElement,NULL); + } + SECMOD_ReleaseWriteLock(moduleLock); + return SECSuccess; +} + +PK11SlotInfo *SECMOD_FindSlot(SECMODModule *module,char *name) { + int i; + char *string; + + for (i=0; i < module->slotCount; i++) { + PK11SlotInfo *slot = module->slots[i]; + + if (PK11_IsPresent(slot)) { + string = PK11_GetTokenName(slot); + } else { + string = PK11_GetSlotName(slot); + } + if (PORT_Strcmp(name,string) == 0) { + return PK11_ReferenceSlot(slot); + } + } + return NULL; +} + +SECStatus +PK11_GetModInfo(SECMODModule *mod,CK_INFO *info) { + CK_RV crv; + + if (mod->functionList == NULL) return SECFailure; + crv = PK11_GETTAB(mod)->C_GetInfo(info); + return (crv == CKR_OK) ? SECSuccess : SECFailure; +} + +/* Determine if we have the FIP's module loaded as the default + * module to trigger other bogus FIPS requirements in PKCS #12 and + * SSL + */ +PRBool +PK11_IsFIPS(void) +{ + SECMODModule *mod = SECMOD_GetInternalModule(); + + if (mod && mod->internal) { + return mod->isFIPS; + } + + return PR_FALSE; +} + +/* combines NewModule() & AddModule */ +/* give a string for the module name & the full-path for the dll, */ +/* installs the PKCS11 module & update registry */ +SECStatus SECMOD_AddNewModule(char* moduleName, char* dllPath, + unsigned long defaultMechanismFlags, + unsigned long cipherEnableFlags) { + SECMODModule *module; + SECStatus result; + int s,i; + PK11SlotInfo* slot; + + module = SECMOD_NewModule(); + + if (moduleName) { + module->commonName=PORT_ArenaStrdup(module->arena,moduleName); + } else { + module->commonName=NULL; + } + + if (dllPath) { + module->dllName=PORT_ArenaStrdup(module->arena,dllPath); + } else { + module->dllName=NULL; + } + + if (module->dllName != NULL) { + if (module->dllName[0] != 0) { + SECStatus rv = SECMOD_AddModule(module); + if (rv != SECSuccess) { + /* SECFailure: failed to add module, corrupt or missing module etc. */ + /* SECBlock: a module with the same name already exists */ + return rv; + } else { /* successfully added module */ + /* turn on SSL cipher enable flags */ + module->ssl[0] = cipherEnableFlags; + + /* check each slot to turn on appropriate mechanisms */ + for (s = 0; s < module->slotCount; s++) { + slot = (module->slots)[s]; + /* for each possible mechanism */ + for (i=0; i < num_pk11_default_mechanisms; i++) { + /* we are told to turn it on by default ? */ + if (PK11_DefaultArray[i].flag & defaultMechanismFlags) { + /* it ignores if slot attribute update failes */ + result = PK11_UpdateSlotAttribute(slot, &(PK11_DefaultArray[i]), PR_TRUE); + } else { /* turn this mechanism of the slot off by default */ + result = PK11_UpdateSlotAttribute(slot, &(PK11_DefaultArray[i]), PR_FALSE); + } + } /* for each mechanism */ + /* disable each slot if the defaultFlags say so */ + if (defaultMechanismFlags & PK11_DISABLE_FLAG) { + PK11_UserDisableSlot(slot); + } + } /* for each slot of this module */ + + /* delete and re-add module in order to save changes to the module */ + result = SECMOD_DeletePermDB(module); + + if (result == SECSuccess) { + result = SECMOD_AddPermDB(module); + if (result == SECSuccess) { + return SECSuccess; + } + } + + } + } + } + SECMOD_DestroyModule(module); + return SECFailure; +} + +/* Public & Internal(Security Library) representation of + * encryption mechanism flags conversion */ + +/* Currently, the only difference is that internal representation + * puts RANDOM_FLAG at bit 31 (Most-significant bit), but + * public representation puts this bit at bit 28 + */ +unsigned long SECMOD_PubMechFlagstoInternal(unsigned long publicFlags) { + unsigned long internalFlags = publicFlags; + + if (publicFlags & PUBLIC_MECH_RANDOM_FLAG) { + internalFlags &= ~PUBLIC_MECH_RANDOM_FLAG; + internalFlags |= SECMOD_RANDOM_FLAG; + } + return internalFlags; +} + +unsigned long SECMOD_InternaltoPubMechFlags(unsigned long internalFlags) { + unsigned long publicFlags = internalFlags; + + if (internalFlags & SECMOD_RANDOM_FLAG) { + publicFlags &= ~SECMOD_RANDOM_FLAG; + publicFlags |= PUBLIC_MECH_RANDOM_FLAG; + } + return publicFlags; +} + + +/* Public & Internal(Security Library) representation of */ +/* cipher flags conversion */ +/* Note: currently they are just stubs */ +unsigned long SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags) { + return publicFlags; +} + +unsigned long SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags) { + return internalFlags; +} + +/* Funtion reports true if module of modType is installed/configured */ +PRBool +SECMOD_IsModulePresent( unsigned long int pubCipherEnableFlags ) +{ + PRBool result = PR_FALSE; + SECMODModuleList *mods = SECMOD_GetDefaultModuleList(); + SECMODListLock *modsLock = SECMOD_GetDefaultModuleListLock(); + SECMOD_GetReadLock(moduleLock); + + + for ( ; mods != NULL; mods = mods->next) { + if (mods->module->ssl[0] & SECMOD_PubCipherFlagstoInternal(pubCipherEnableFlags)) { + result = PR_TRUE; + } + } + + SECMOD_ReleaseReadLock(moduleLock); + return result; +} diff --git a/security/nss/lib/pk11wrap/secmod.h b/security/nss/lib/pk11wrap/secmod.h new file mode 100644 index 000000000..9c0b9100a --- /dev/null +++ b/security/nss/lib/pk11wrap/secmod.h @@ -0,0 +1,132 @@ +/* + * 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. + * + * Definition of Security Module Data Structure. There is a separate data + * structure for each loaded PKCS #11 module. + */ +#ifndef _SECMOD_H_ +#define _SEDMOD_H_ +#include "seccomon.h" +#include "secmodt.h" + +#define PKCS11_USE_THREADS + +/* These mechanisms flags are visible to all other libraries. */ +/* They must be converted to internal SECMOD_*_FLAG */ +/* if used inside the functions of the security library */ +#define PUBLIC_MECH_RSA_FLAG 0x00000001ul +#define PUBLIC_MECH_DSA_FLAG 0x00000002ul +#define PUBLIC_MECH_RC2_FLAG 0x00000004ul +#define PUBLIC_MECH_RC4_FLAG 0x00000008ul +#define PUBLIC_MECH_DES_FLAG 0x00000010ul +#define PUBLIC_MECH_DH_FLAG 0x00000020ul +#define PUBLIC_MECH_FORTEZZA_FLAG 0x00000040ul +#define PUBLIC_MECH_RC5_FLAG 0x00000080ul +#define PUBLIC_MECH_SHA1_FLAG 0x00000100ul +#define PUBLIC_MECH_MD5_FLAG 0x00000200ul +#define PUBLIC_MECH_MD2_FLAG 0x00000400ul + +#define PUBLIC_MECH_RANDOM_FLAG 0x08000000ul +#define PUBLIC_MECH_FRIENDLY_FLAG 0x10000000ul +#define PUBLIC_OWN_PW_DEFAULTS 0X20000000ul +#define PUBLIC_DISABLE_FLAG 0x40000000ul + +/* warning: reserved means reserved */ +#define PUBLIC_MECH_RESERVED_FLAGS 0x87FFF800ul + +/* These cipher flags are visible to all other libraries, */ +/* But they must be converted before used in functions */ +/* withing the security module */ +#define PUBLIC_CIPHER_FORTEZZA_FLAG 0x00000001ul + +/* warning: reserved means reserved */ +#define PUBLIC_CIPHER_RESERVED_FLAGS 0xFFFFFFFEul + +SEC_BEGIN_PROTOS + +/* protoypes */ +extern void SECMOD_init(char *dbname); +extern SECMODModuleList *SECMOD_GetDefaultModuleList(void); +extern SECMODListLock *SECMOD_GetDefaultModuleListLock(void); + +/* lock management */ +extern SECMODListLock *SECMOD_NewListLock(void); +extern void SECMOD_DestroyListLock(SECMODListLock *); +extern void SECMOD_GetReadLock(SECMODListLock *); +extern void SECMOD_ReleaseReadLock(SECMODListLock *); +extern void SECMOD_GetWriteLock(SECMODListLock *); +extern void SECMOD_ReleaseWriteLock(SECMODListLock *); + +/* list managment */ +extern void SECMOD_RemoveList(SECMODModuleList **,SECMODModuleList *); +extern void SECMOD_AddList(SECMODModuleList *,SECMODModuleList *,SECMODListLock *); + +/* Operate on modules by name */ +extern SECMODModule *SECMOD_FindModule(char *name); +extern SECMODModule *SECMOD_FindModuleByID(SECMODModuleID); +extern SECStatus SECMOD_DeleteModule(char *name, int *type); +extern SECStatus SECMOD_DeleteInternalModule(char *name); +extern SECStatus SECMOD_AddNewModule(char* moduleName, char* dllPath, + unsigned long defaultMechanismFlags, + unsigned long cipherEnableFlags); +/* database/memory management */ +extern SECMODModule *SECMOD_NewModule(void); +extern SECMODModuleList *SECMOD_NewModuleListElement(void); +extern SECMODModule *SECMOD_GetInternalModule(void); +extern SECMODModule *SECMOD_GetFIPSInternal(void); +extern SECMODModule *SECMOD_ReferenceModule(SECMODModule *module); +extern void SECMOD_DestroyModule(SECMODModule *module); +extern SECMODModuleList *SECMOD_DestroyModuleListElement(SECMODModuleList *); +extern void SECMOD_DestroyModuleList(SECMODModuleList *); +extern SECMODModule *SECMOD_DupModule(SECMODModule *old); +extern SECStatus SECMOD_AddModule(SECMODModule *newModule); +extern PK11SlotInfo *SECMOD_FindSlot(SECMODModule *module,char *name); +extern PK11SlotInfo *SECMOD_LookupSlot(SECMODModuleID module, + unsigned long slotID); +SECStatus SECMOD_DeletePermDB(SECMODModule *); +SECStatus SECMOD_AddPermDB(SECMODModule *); + +/* Funtion reports true if at least one of the modules */ +/* of modType has been installed */ +PRBool SECMOD_IsModulePresent( unsigned long int pubCipherEnableFlags ); + +/* Functions used to convert between internal & public representation + * of Mechanism Flags and Cipher Enable Flags */ +extern unsigned long SECMOD_PubMechFlagstoInternal(unsigned long publicFlags); +extern unsigned long SECMOD_InternaltoPubMechFlags(unsigned long internalFlags); + +extern unsigned long SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags); +extern unsigned long SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags); + +SEC_END_PROTOS + +#endif diff --git a/security/nss/lib/pk11wrap/secmodi.h b/security/nss/lib/pk11wrap/secmodi.h new file mode 100644 index 000000000..e2ea081d2 --- /dev/null +++ b/security/nss/lib/pk11wrap/secmodi.h @@ -0,0 +1,81 @@ +/* + * 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. + */ +/* + * Internal header file included only by files in pkcs11 dir, or in + * pkcs11 specific client and server files. + */ +#ifndef _SECMODI_H_ +#define _SECMODI_H_ 1 +#include "pkcs11.h" +#include "prlock.h" +#include "mcom_db.h" +#include "secoidt.h" +#include "secdert.h" +#include "certt.h" +#include "secmodti.h" + +#ifdef PKCS11_USE_THREADS +#define PK11_USE_THREADS(x) x +#else +#define PK11_USE_THREADS(x) +#endif + +SEC_BEGIN_PROTOS + +/* proto-types */ +SECMODModule * SECMOD_NewModule(void); /* create a new module */ +SECMODModule * SECMOD_NewInternal(void); /* create an internal module */ + +/* Data base functions */ +void SECMOD_InitDB(char *); +SECMODModuleList * SECMOD_ReadPermDB(void); + +/*void SECMOD_ReferenceModule(SECMODModule *); */ + +/* Library functions */ +SECStatus SECMOD_LoadModule(SECMODModule *); +SECStatus SECMOD_UnloadModule(SECMODModule *); + +void SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot); +CK_RV pk11_notify(CK_SESSION_HANDLE session, CK_NOTIFICATION event, + CK_VOID_PTR pdata); +void pk11_SignedToUnsigned(CK_ATTRIBUTE *attrib); +CK_OBJECT_HANDLE pk11_FindObjectByTemplate(PK11SlotInfo *slot, + CK_ATTRIBUTE *inTemplate,int tsize); +SEC_END_PROTOS + +#define PK11_GETTAB(x) ((CK_FUNCTION_LIST_PTR)((x)->functionList)) +#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \ + (x)->pValue=(v); (x)->ulValueLen = (l); +#endif + diff --git a/security/nss/lib/pk11wrap/secmodt.h b/security/nss/lib/pk11wrap/secmodt.h new file mode 100644 index 000000000..8d7ff18a8 --- /dev/null +++ b/security/nss/lib/pk11wrap/secmodt.h @@ -0,0 +1,173 @@ +/* + * 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. + * + * Definition of Security Module Data Structure. There is a separate data + * structure for each loaded PKCS #11 module. + */ +#ifndef _SECMODT_H_ +#define _SECMODT_H_ 1 + +/* PKCS11 needs to be included */ +typedef struct SECMODModuleStr SECMODModule; +typedef struct SECMODModuleListStr SECMODModuleList; +typedef struct SECMODListLockStr SECMODListLock; /* defined in secmodi.h */ +typedef struct PK11SlotInfoStr PK11SlotInfo; /* defined in secmodti.h */ +typedef struct PK11PreSlotInfoStr PK11PreSlotInfo; /* defined in secmodti.h */ +typedef struct PK11SymKeyStr PK11SymKey; /* defined in secmodti.h */ +typedef struct PK11ContextStr PK11Context; /* defined in secmodti.h */ +typedef struct PK11SlotListStr PK11SlotList; +typedef struct PK11SlotListElementStr PK11SlotListElement; +typedef struct PK11RSAGenParamsStr PK11RSAGenParams; +typedef unsigned long SECMODModuleID; +typedef struct PK11DefaultArrayEntryStr PK11DefaultArrayEntry; + +struct SECMODModuleStr { + PRArenaPool *arena; + PRBool internal; /* true of internally linked modules, false + * for the loaded modules */ + PRBool loaded; /* Set to true if module has been loaded */ + PRBool isFIPS; /* Set to true if module is finst internal */ + char *dllName; /* name of the shared library which implements + * this module */ + char *commonName; /* name of the module to display to the user */ + void *library; /* pointer to the library. opaque. used only by + * pk11load.c */ + void *functionList; /* The PKCS #11 function table */ + void *refLock; /* only used pk11db.c */ + int refCount; /* Module reference count */ + PK11SlotInfo **slots; /* array of slot points attatched to this mod*/ + int slotCount; /* count of slot in above array */ + PK11PreSlotInfo *slotInfo; /* special info about slots default settings */ + int slotInfoCount; /* count */ + SECMODModuleID moduleID; /* ID so we can find this module again */ + PRBool isThreadSafe; + unsigned long ssl[2]; /* SSL cipher enable flags */ +}; + +struct SECMODModuleListStr { + SECMODModuleList *next; + SECMODModule *module; +}; + +struct PK11SlotListStr { + PK11SlotListElement *head; + PK11SlotListElement *tail; + void *lock; +}; + +struct PK11SlotListElementStr { + PK11SlotListElement *next; + PK11SlotListElement *prev; + PK11SlotInfo *slot; + int refCount; +}; + +struct PK11RSAGenParamsStr { + int keySizeInBits; + unsigned long pe; +}; + +/* + * Entry into the Array which lists all the legal bits for the default flags + * in the slot, their definition, and the PKCS #11 mechanism the represent + * Always Statically allocated. + */ +struct PK11DefaultArrayEntryStr { + char *name; + unsigned long flag; + unsigned long mechanism; /* this is a long so we don't include the + * whole pkcs 11 world to use this header */ +}; + + +#define SECMOD_RSA_FLAG 0x00000001L +#define SECMOD_DSA_FLAG 0x00000002L +#define SECMOD_RC2_FLAG 0x00000004L +#define SECMOD_RC4_FLAG 0x00000008L +#define SECMOD_DES_FLAG 0x00000010L +#define SECMOD_DH_FLAG 0x00000020L +#define SECMOD_FORTEZZA_FLAG 0x00000040L +#define SECMOD_RC5_FLAG 0x00000080L +#define SECMOD_SHA1_FLAG 0x00000100L +#define SECMOD_MD5_FLAG 0x00000200L +#define SECMOD_MD2_FLAG 0x00000400L +/* reserved bit for future, do not use */ +#define SECMOD_RESERVED_FLAG 0X08000000L +#define SECMOD_FRIENDLY_FLAG 0x10000000L +#define SECMOD_RANDOM_FLAG 0x80000000L + +/* need to make SECMOD and PK11 prefixes consistant. */ +#define PK11_OWN_PW_DEFAULTS 0x20000000L +#define PK11_DISABLE_FLAG 0x40000000L + +/* FAKE PKCS #11 defines */ +#define CKM_FAKE_RANDOM 0x80000efeL +#define CKM_INVALID_MECHANISM 0xffffffffL +#define CKA_DIGEST 0x81000000L +#define CK_INVALID_KEY 0 +#define CK_INVALID_SESSION 0 + +/* Cryptographic module types */ +#define SECMOD_EXTERNAL 0 /* external module */ +#define SECMOD_INTERNAL 1 /* internal default module */ +#define SECMOD_FIPS 2 /* internal fips module */ + +/* + * What is the origin of a given Key. Normally this doesn't matter, but + * the fortezza code needs to know if it needs to invoke the SSL3 fortezza + * hack. + */ +typedef enum { + PK11_OriginNULL, /* There is not key, it's a null SymKey */ + PK11_OriginDerive, /* Key was derived from some other key */ + PK11_OriginGenerated, /* Key was generated (also PBE keys) */ + PK11_OriginFortezzaHack,/* Key was marked for fortezza hack */ + PK11_OriginUnwrap /* Key was unwrapped or decrypted */ +} PK11Origin; + +/* PKCS #11 disable reasons */ +typedef enum { + PK11_DIS_NONE = 0, + PK11_DIS_USER_SELECTED, + PK11_DIS_COULD_NOT_INIT_TOKEN, + PK11_DIS_TOKEN_VERIFY_FAILED, + PK11_DIS_TOKEN_NOT_PRESENT +} PK11DisableReasons; + +/* function pointer type for password callback function. + * This type is passed in to PK11_SetPasswordFunc() + */ +typedef char *(*PK11PasswordFunc)(PK11SlotInfo *slot, PRBool retry, void *arg); +typedef PRBool (*PK11VerifyPasswordFunc)(PK11SlotInfo *slot, void *arg); +typedef PRBool (*PK11IsLoggedInFunc)(PK11SlotInfo *slot, void *arg); + +#endif /*_SECMODT_H_ */ diff --git a/security/nss/lib/pk11wrap/secmodti.h b/security/nss/lib/pk11wrap/secmodti.h new file mode 100644 index 000000000..c7028641c --- /dev/null +++ b/security/nss/lib/pk11wrap/secmodti.h @@ -0,0 +1,179 @@ +/* + * 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. + */ +/* + * Internal header file included only by files in pkcs11 dir, or in + * pkcs11 specific client and server files. + */ + +#include "prmon.h" +#include "prtypes.h" + +/* internal data structures */ + +/* structure to allow us to implement the read/write locks for our + * module lists */ +struct SECMODListLockStr { + PRLock *mutex; /*general mutex to protect this data structure*/ + PRMonitor *monitor; /* monitor to allow us to signal */ + int state; /* read/write/waiting state */ + int count; /* how many waiters on this lock */ +}; + +/* represent a pkcs#11 slot reference counted. */ +struct PK11SlotInfoStr { + /* the PKCS11 function list for this slot */ + void *functionList; + SECMODModule *module; /* our parent module */ + /* Boolean to indicate the current state of this slot */ + PRBool needTest; /* Has this slot been tested for Export complience */ + PRBool isPerm; /* is this slot a permanment device */ + PRBool isHW; /* is this slot a hardware device */ + PRBool isInternal; /* is this slot one of our internal PKCS #11 devices */ + PRBool disabled; /* is this slot disabled... */ + PK11DisableReasons reason; /* Why this slot is disabled */ + PRBool readOnly; /* is the token in this slot read-only */ + PRBool needLogin; /* does the token of the type that needs + * authentication (still true even if token is logged + * in) */ + PRBool hasRandom; /* can this token generated random numbers */ + PRBool defRWSession; /* is the default session RW (we open our default + * session rw if the token can only handle one session + * at a time. */ + PRBool isThreadSafe; /* copied from the module */ + /* The actual flags (many of which are distilled into the above PRBools) */ + CK_FLAGS flags; /* flags from PKCS #11 token Info */ + /* a default session handle to do quick and dirty functions */ + CK_SESSION_HANDLE session; + PRLock *sessionLock; /* lock for this session */ + /* our ID */ + CK_SLOT_ID slotID; + /* persistant flags saved from startup to startup */ + unsigned long defaultFlags; + /* keep track of who is using us so we don't accidently get freed while + * still in use */ + int refCount; + PRLock *refLock; + /* Password control functions for this slot. many of these are only + * active if the appropriate flag is on in defaultFlags */ + int askpw; /* what our password options are */ + int timeout; /* If we're ask_timeout, what is our timeout time is + * seconds */ + int authTransact; /* allow multiple authentications off one password if + * they are all part of the same transaction */ + int64 authTime; /* when were we last authenticated */ + int minPassword; /* smallest legal password */ + int maxPassword; /* largest legal password */ + uint16 series; /* break up the slot info into various groups of + * inserted tokens so that keys and certs can be + * invalidated */ + uint16 wrapKey; /* current wrapping key for SSL master secrets */ + CK_MECHANISM_TYPE wrapMechanism; + /* current wrapping mechanism for current wrapKey */ + CK_OBJECT_HANDLE refKeys[1]; /* array of existing wrapping keys for */ + CK_MECHANISM_TYPE *mechanismList; /* list of mechanism supported by this + * token */ + int mechanismCount; + /* cache the certificates stored on the token of this slot */ + CERTCertificate **cert_array; + int array_size; + int cert_count; + char serial[16]; + /* since these are odd sizes, keep them last. They are odd sizes to + * allow them to become null terminated strings */ + char slot_name[65]; + char token_name[33]; + PRBool hasRSAInfo; + CK_FLAGS RSAInfoFlags; +}; + +/* hold slot default flags until we initialize a slot. This structure is only + * useful between the time we define a module (either by hand or from the + * database) and the time the module is loaded. Not reference counted */ +struct PK11PreSlotInfoStr { + CK_SLOT_ID slotID; /* slot these flags are for */ + unsigned long defaultFlags; /* bit mask of default implementation this slot + * provides */ + int askpw; /* slot specific password bits */ + long timeout; /* slot specific timeout value */ +}; + +/* Symetric Key structure. Reference Counted */ +struct PK11SymKeyStr { + CK_MECHANISM_TYPE type; /* type of operation this key was created for*/ + CK_OBJECT_HANDLE objectID; /* object id of this key in the slot */ + PK11SlotInfo *slot; /* Slot this key is loaded into */ + void *cx; /* window context in case we need to loggin */ + PRBool owner; + SECItem data; /* raw key data if available */ + CK_SESSION_HANDLE session; + PRBool sessionOwner; + int refCount; /* number of references to this key */ + PRLock *refLock; + int size; /* key size in bytes */ + PK11Origin origin; /* where this key came from + (see def in secmodt.h) */ + uint16 series; /* break up the slot info into various groups of + * inserted tokens so that keys and certs can be + * invalidated */ +}; + + +/* + * hold a hash, encryption or signing context for multi-part operations. + * hold enough information so that multiple contexts can be interleaved + * if necessary. ... Not RefCounted. + */ +struct PK11ContextStr { + CK_ATTRIBUTE_TYPE operation; /* type of operation this context is doing + * (CKA_ENCRYPT, CKA_SIGN, CKA_HASH, etc. */ + PK11SymKey *key; /* symetric key used in this context */ + PK11SlotInfo *slot; /* slot this context is operationing on */ + CK_SESSION_HANDLE session; /* session this context is using */ + PRLock *sessionLock; /* lock before accessing a PKCS #11 + * session */ + PRBool ownSession;/* do we own the session? */ + void *cx; /* window context in case we need to loggin*/ + void *savedData;/* save data when we are multiplexing on a + * single context */ + unsigned long savedLength; /* length of the saved context */ + SECItem *param; /* mechanism parameters used to build this + context */ + PRBool init; /* has this contexted been initialized */ + CK_MECHANISM_TYPE type; /* what is the PKCS #11 this context is + * representing (usually what algorithm is + * being used (CKM_RSA_PKCS, CKM_DES, + * CKM_SHA, etc.*/ + PRBool fortezzaHack; /*Fortezza SSL has some special + * non-standard semantics*/ +}; + diff --git a/security/nss/lib/pkcs12/Makefile b/security/nss/lib/pkcs12/Makefile new file mode 100644 index 000000000..5742208b5 --- /dev/null +++ b/security/nss/lib/pkcs12/Makefile @@ -0,0 +1,77 @@ +#! gmake +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include config.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + + diff --git a/security/nss/lib/pkcs12/config.mk b/security/nss/lib/pkcs12/config.mk new file mode 100644 index 000000000..1684cf3fa --- /dev/null +++ b/security/nss/lib/pkcs12/config.mk @@ -0,0 +1,45 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + + +# +# Override TARGETS variable so that only static libraries +# are specifed as dependencies within rules.mk. +# + +TARGETS = $(LIBRARY) +SHARED_LIBRARY = +IMPORT_LIBRARY = +PURE_LIBRARY = +PROGRAM = + diff --git a/security/nss/lib/pkcs12/manifest.mn b/security/nss/lib/pkcs12/manifest.mn new file mode 100644 index 000000000..c8b4981d3 --- /dev/null +++ b/security/nss/lib/pkcs12/manifest.mn @@ -0,0 +1,58 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +CORE_DEPTH = ../../.. + +EXPORTS = \ + pkcs12t.h \ + pkcs12.h \ + p12plcy.h \ + p12.h \ + p12t.h \ + $(NULL) + +MODULE = security + +CSRCS = \ + p12local.c \ + p12creat.c \ + p12dec.c \ + p12plcy.c \ + p12tmpl.c \ + p12e.c \ + p12d.c \ + $(NULL) + +REQUIRES = security dbm + +LIBRARY_NAME = pkcs12 diff --git a/security/nss/lib/pkcs12/p12.h b/security/nss/lib/pkcs12/p12.h new file mode 100644 index 000000000..eac067c93 --- /dev/null +++ b/security/nss/lib/pkcs12/p12.h @@ -0,0 +1,173 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + + +#ifndef _P12_H_ +#define _P12_H_ + +#include "secoid.h" +#include "key.h" +#include "secpkcs7.h" +#include "p12t.h" + +typedef int (* PKCS12OpenFunction)(void *arg); +typedef int (* PKCS12ReadFunction)(void *arg, unsigned char *buffer, + unsigned int *lenRead, unsigned int maxLen); +typedef int (* PKCS12WriteFunction)(void *arg, unsigned char *buffer, + unsigned int *bufLen, unsigned int *lenWritten); +typedef int (* PKCS12CloseFunction)(void *arg); +typedef SECStatus (* PKCS12UnicodeConvertFunction)(PRArenaPool *arena, + SECItem *dest, SECItem *src, + PRBool toUnicode, + PRBool swapBytes); +typedef void (* SEC_PKCS12EncoderOutputCallback)(void *arg, const char *buf, + unsigned long len); +typedef void (* SEC_PKCS12DecoderOutputCallback)(void *arg, const char *buf, + unsigned long len); +typedef SECItem * (* SEC_PKCS12NicknameCollisionCallback)(SECItem *old_nickname, + PRBool *cancel, + void *arg); + + + + +typedef SECStatus (*digestOpenFn)(void *arg, PRBool readData); +typedef SECStatus (*digestCloseFn)(void *arg, PRBool removeFile); +typedef int (*digestIOFn)(void *arg, unsigned char *buf, + unsigned long len); + +typedef struct SEC_PKCS12ExportContextStr SEC_PKCS12ExportContext; +typedef struct SEC_PKCS12SafeInfoStr SEC_PKCS12SafeInfo; +typedef struct SEC_PKCS12DecoderContextStr SEC_PKCS12DecoderContext; + +struct sec_PKCS12PasswordModeInfo { + SECItem *password; + SECOidTag algorithm; +}; + +struct sec_PKCS12PublicKeyModeInfo { + CERTCertificate *cert; + CERTCertDBHandle *certDb; + SECOidTag algorithm; + int keySize; +}; + +SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt, + CERTCertDBHandle *certDb, + CERTCertificate *signer, + CERTCertificate **recipients, + SECOidTag algorithm, int keysize); + +extern SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag privAlg); + +extern SEC_PKCS12SafeInfo * +SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt); + +extern SECStatus +SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag integAlg); +extern SECStatus +SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt, + CERTCertificate *cert, CERTCertDBHandle *certDb, + SECOidTag algorithm, int keySize); + +extern SEC_PKCS12ExportContext * +SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg, + PK11SlotInfo *slot, void *wincx); + +extern SECStatus +SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, + SEC_PKCS12SafeInfo *safe, void *nestedDest, + CERTCertificate *cert, CERTCertDBHandle *certDb, + SECItem *keyId, PRBool includeCertChain); + +extern SECStatus +SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, + SEC_PKCS12SafeInfo *safe, + void *nestedDest, CERTCertificate *cert, + PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem, + SECItem *keyId, SECItem *nickName); + +extern SECStatus +SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, + CERTCertificate *cert, CERTCertDBHandle *certDb, + void *keySafe, void *keyNestedDest, + PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm); + +extern SECStatus +SEC_PKCS12AddDERCertAndEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, SECItem *derCert, + void *keySafe, void *keyNestedDest, + SECKEYEncryptedPrivateKeyInfo *epki, char *nickname); + +extern void * +SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt, + void *baseSafe, void *nestedDest); + +extern SECStatus +SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, + SEC_PKCS12EncoderOutputCallback output, void *outputarg); + +extern void +SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12exp); + +extern SEC_PKCS12DecoderContext * +SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx, + digestOpenFn dOpen, digestCloseFn dClose, + digestIOFn dRead, digestIOFn dWrite, void *dArg); + +extern SECStatus +SEC_PKCS12DecoderUpdate(SEC_PKCS12DecoderContext *p12dcx, unsigned char *data, + unsigned long len); + +extern void +SEC_PKCS12DecoderFinish(SEC_PKCS12DecoderContext *p12dcx); + +extern SECStatus +SEC_PKCS12DecoderVerify(SEC_PKCS12DecoderContext *p12dcx); + +extern SECStatus +SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx, + SEC_PKCS12NicknameCollisionCallback nicknameCb); + +extern SECStatus +SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx); + +CERTCertList * +SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx); + +#endif diff --git a/security/nss/lib/pkcs12/p12creat.c b/security/nss/lib/pkcs12/p12creat.c new file mode 100644 index 000000000..65678b299 --- /dev/null +++ b/security/nss/lib/pkcs12/p12creat.c @@ -0,0 +1,251 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "pkcs12.h" +#include "secitem.h" +#include "secport.h" +#include "secder.h" +#include "secoid.h" +#include "p12local.h" +#include "secerr.h" + + +/* allocate space for a PFX structure and set up initial + * arena pool. pfx structure is cleared and a pointer to + * the new structure is returned. + */ +SEC_PKCS12PFXItem * +sec_pkcs12_new_pfx(void) +{ + SEC_PKCS12PFXItem *pfx = NULL; + PRArenaPool *poolp = NULL; + + poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); /* XXX Different size? */ + if(poolp == NULL) + goto loser; + + pfx = (SEC_PKCS12PFXItem *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12PFXItem)); + if(pfx == NULL) + goto loser; + pfx->poolp = poolp; + + return pfx; + +loser: + PORT_FreeArena(poolp, PR_TRUE); + return NULL; +} + +/* allocate space for a PFX structure and set up initial + * arena pool. pfx structure is cleared and a pointer to + * the new structure is returned. + */ +SEC_PKCS12AuthenticatedSafe * +sec_pkcs12_new_asafe(PRArenaPool *poolp) +{ + SEC_PKCS12AuthenticatedSafe *asafe = NULL; + void *mark; + + mark = PORT_ArenaMark(poolp); + asafe = (SEC_PKCS12AuthenticatedSafe *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12AuthenticatedSafe)); + if(asafe == NULL) + goto loser; + asafe->poolp = poolp; + PORT_Memset(&asafe->old_baggage, 0, sizeof(SEC_PKCS7ContentInfo)); + + PORT_ArenaUnmark(poolp, mark); + return asafe; + +loser: + PORT_ArenaRelease(poolp, mark); + return NULL; +} + +/* create a safe contents structure with a list of + * length 0 with the first element being NULL + */ +SEC_PKCS12SafeContents * +sec_pkcs12_create_safe_contents(PRArenaPool *poolp) +{ + SEC_PKCS12SafeContents *safe; + void *mark; + + if(poolp == NULL) + return NULL; + + /* allocate structure */ + mark = PORT_ArenaMark(poolp); + safe = (SEC_PKCS12SafeContents *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12SafeContents)); + if(safe == NULL) + { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + + /* init list */ + safe->contents = (SEC_PKCS12SafeBag**)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12SafeBag *)); + if(safe->contents == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + safe->contents[0] = NULL; + safe->poolp = poolp; + safe->safe_size = 0; + PORT_ArenaUnmark(poolp, mark); + return safe; +} + +/* create a new external bag which is appended onto the list + * of bags in baggage. the bag is created in the same arena + * as baggage + */ +SEC_PKCS12BaggageItem * +sec_pkcs12_create_external_bag(SEC_PKCS12Baggage *luggage) +{ + void *dummy, *mark; + SEC_PKCS12BaggageItem *bag; + + if(luggage == NULL) { + return NULL; + } + + mark = PORT_ArenaMark(luggage->poolp); + + /* allocate space for null terminated bag list */ + if(luggage->bags == NULL) { + luggage->bags=(SEC_PKCS12BaggageItem**)PORT_ArenaZAlloc(luggage->poolp, + sizeof(SEC_PKCS12BaggageItem *)); + if(luggage->bags == NULL) { + goto loser; + } + luggage->luggage_size = 0; + } + + /* grow the list */ + dummy = PORT_ArenaGrow(luggage->poolp, luggage->bags, + sizeof(SEC_PKCS12BaggageItem *) * (luggage->luggage_size + 1), + sizeof(SEC_PKCS12BaggageItem *) * (luggage->luggage_size + 2)); + if(dummy == NULL) { + goto loser; + } + luggage->bags = (SEC_PKCS12BaggageItem**)dummy; + + luggage->bags[luggage->luggage_size] = + (SEC_PKCS12BaggageItem *)PORT_ArenaZAlloc(luggage->poolp, + sizeof(SEC_PKCS12BaggageItem)); + if(luggage->bags[luggage->luggage_size] == NULL) { + goto loser; + } + + /* create new bag and append it to the end */ + bag = luggage->bags[luggage->luggage_size]; + bag->espvks = (SEC_PKCS12ESPVKItem **)PORT_ArenaZAlloc( + luggage->poolp, + sizeof(SEC_PKCS12ESPVKItem *)); + bag->unencSecrets = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc( + luggage->poolp, + sizeof(SEC_PKCS12SafeBag *)); + if((bag->espvks == NULL) || (bag->unencSecrets == NULL)) { + goto loser; + } + + bag->poolp = luggage->poolp; + luggage->luggage_size++; + luggage->bags[luggage->luggage_size] = NULL; + bag->espvks[0] = NULL; + bag->unencSecrets[0] = NULL; + bag->nEspvks = bag->nSecrets = 0; + + PORT_ArenaUnmark(luggage->poolp, mark); + return bag; + +loser: + PORT_ArenaRelease(luggage->poolp, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; +} + +/* creates a baggage witha NULL terminated 0 length list */ +SEC_PKCS12Baggage * +sec_pkcs12_create_baggage(PRArenaPool *poolp) +{ + SEC_PKCS12Baggage *luggage; + void *mark; + + if(poolp == NULL) + return NULL; + + mark = PORT_ArenaMark(poolp); + + /* allocate bag */ + luggage = (SEC_PKCS12Baggage *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12Baggage)); + if(luggage == NULL) + { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + + /* init list */ + luggage->bags = (SEC_PKCS12BaggageItem **)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12BaggageItem *)); + if(luggage->bags == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + + luggage->bags[0] = NULL; + luggage->luggage_size = 0; + luggage->poolp = poolp; + + PORT_ArenaUnmark(poolp, mark); + return luggage; +} + +/* free pfx structure and associated items in the arena */ +void +SEC_PKCS12DestroyPFX(SEC_PKCS12PFXItem *pfx) +{ + if (pfx != NULL && pfx->poolp != NULL) + { + PORT_FreeArena(pfx->poolp, PR_TRUE); + } +} diff --git a/security/nss/lib/pkcs12/p12d.c b/security/nss/lib/pkcs12/p12d.c new file mode 100644 index 000000000..aea26a9e9 --- /dev/null +++ b/security/nss/lib/pkcs12/p12d.c @@ -0,0 +1,3224 @@ +/* + * 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 "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) { + 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]; + void *hmacCx; + unsigned int bufLen; + int iteration; + + 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 */ + hmacCx = HMAC_Create(SECOID_GetAlgorithmTag( + &p12dcx->macData.safeMac.digestAlgorithm), + hmacKey->data, hmacKey->len); + SECITEM_ZfreeItem(hmacKey, PR_TRUE); + hmacKey = NULL; + if(!hmacCx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + HMAC_Begin((HMACContext*)hmacCx); + + /* 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; + } + + HMAC_Update((HMACContext*)hmacCx, buf, bytesRead); + if(bytesRead < IN_BUF_LEN) { + break; + } + } + + /* finish the hmac context */ + HMAC_Finish((HMACContext*)hmacCx, buf, &bufLen, IN_BUF_LEN); + HMAC_Destroy((HMACContext*)hmacCx); + hmacCx = NULL; + + 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(hmacCx) { + HMAC_Destroy((HMACContext*)hmacCx); + } + + 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/pkcs12/p12dec.c b/security/nss/lib/pkcs12/p12dec.c new file mode 100644 index 000000000..024a61dae --- /dev/null +++ b/security/nss/lib/pkcs12/p12dec.c @@ -0,0 +1,692 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "pkcs12.h" +#include "plarena.h" +#include "secpkcs7.h" +#include "p12local.h" +#include "secoid.h" +#include "secitem.h" +#include "secport.h" +#include "secasn1.h" +#include "secder.h" +#include "secerr.h" +#include "cert.h" +#include "certdb.h" +#include "p12plcy.h" +#include "p12.h" + +/* PFX extraction and validation routines */ + +/* decode the DER encoded PFX item. if unable to decode, check to see if it + * is an older PFX item. If that fails, assume the file was not a valid + * pfx file. + * the returned pfx structure should be destroyed using SEC_PKCS12DestroyPFX + */ +static SEC_PKCS12PFXItem * +sec_pkcs12_decode_pfx(SECItem *der_pfx) +{ + SEC_PKCS12PFXItem *pfx; + SECStatus rv; + + if(der_pfx == NULL) { + return NULL; + } + + /* allocate the space for a new PFX item */ + pfx = sec_pkcs12_new_pfx(); + if(pfx == NULL) { + return NULL; + } + + rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate, + der_pfx); + + /* if a failure occurred, check for older version... + * we also get rid of the old pfx structure, because we don't + * know where it failed and what data in may contain + */ + if(rv != SECSuccess) { + SEC_PKCS12DestroyPFX(pfx); + pfx = sec_pkcs12_new_pfx(); + if(pfx == NULL) { + return NULL; + } + rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate_OLD, + der_pfx); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_PKCS12_DECODING_PFX); + PORT_FreeArena(pfx->poolp, PR_TRUE); + return NULL; + } + pfx->old = PR_TRUE; + SGN_CopyDigestInfo(pfx->poolp, &pfx->macData.safeMac, &pfx->old_safeMac); + SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, &pfx->old_macSalt); + } else { + pfx->old = PR_FALSE; + } + + /* convert bit string from bits to bytes */ + pfx->macData.macSalt.len /= 8; + + return pfx; +} + +/* validate the integrity MAC used in the PFX. The MAC is generated + * per the PKCS 12 document. If the MAC is incorrect, it is most likely + * due to an invalid password. + * pwitem is the integrity password + * pfx is the decoded pfx item + */ +static PRBool +sec_pkcs12_check_pfx_mac(SEC_PKCS12PFXItem *pfx, + SECItem *pwitem) +{ + SECItem *key = NULL, *mac = NULL, *data = NULL; + SECItem *vpwd = NULL; + SECOidTag algorithm; + PRBool ret = PR_FALSE; + + if(pfx == NULL) { + return PR_FALSE; + } + + algorithm = SECOID_GetAlgorithmTag(&pfx->macData.safeMac.digestAlgorithm); + switch(algorithm) { + /* only SHA1 hashing supported as a MACing algorithm */ + case SEC_OID_SHA1: + if(pfx->old == PR_FALSE) { + pfx->swapUnicode = PR_FALSE; + } + +recheckUnicodePassword: + vpwd = sec_pkcs12_create_virtual_password(pwitem, + &pfx->macData.macSalt, + pfx->swapUnicode); + if(vpwd == NULL) { + return PR_FALSE; + } + + key = sec_pkcs12_generate_key_from_password(algorithm, + &pfx->macData.macSalt, + (pfx->old ? pwitem : vpwd)); + /* free vpwd only for newer PFX */ + if(vpwd) { + SECITEM_ZfreeItem(vpwd, PR_TRUE); + } + if(key == NULL) { + return PR_FALSE; + } + + data = SEC_PKCS7GetContent(&pfx->authSafe); + if(data == NULL) { + break; + } + + /* check MAC */ + mac = sec_pkcs12_generate_mac(key, data, pfx->old); + ret = PR_TRUE; + if(mac) { + SECItem *safeMac = &pfx->macData.safeMac.digest; + if(SECITEM_CompareItem(mac, safeMac) != SECEqual) { + + /* if we encounter an invalid mac, lets invert the + * password in case of unicode changes + */ + if(((!pfx->old) && pfx->swapUnicode) || (pfx->old)){ + PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC); + ret = PR_FALSE; + } else { + SECITEM_ZfreeItem(mac, PR_TRUE); + pfx->swapUnicode = PR_TRUE; + goto recheckUnicodePassword; + } + } + SECITEM_ZfreeItem(mac, PR_TRUE); + } else { + ret = PR_FALSE; + } + break; + default: + PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM); + ret = PR_FALSE; + break; + } + + /* let success fall through */ + if(key != NULL) + SECITEM_ZfreeItem(key, PR_TRUE); + + return ret; +} + +/* check the validity of the pfx structure. we currently only support + * password integrity mode, so we check the MAC. + */ +static PRBool +sec_pkcs12_validate_pfx(SEC_PKCS12PFXItem *pfx, + SECItem *pwitem) +{ + SECOidTag contentType; + + contentType = SEC_PKCS7ContentType(&pfx->authSafe); + switch(contentType) + { + case SEC_OID_PKCS7_DATA: + return sec_pkcs12_check_pfx_mac(pfx, pwitem); + break; + case SEC_OID_PKCS7_SIGNED_DATA: + default: + PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE); + break; + } + + return PR_FALSE; +} + +/* decode and return the valid PFX. if the PFX item is not valid, + * NULL is returned. + */ +static SEC_PKCS12PFXItem * +sec_pkcs12_get_pfx(SECItem *pfx_data, + SECItem *pwitem) +{ + SEC_PKCS12PFXItem *pfx; + PRBool valid_pfx; + + if((pfx_data == NULL) || (pwitem == NULL)) { + return NULL; + } + + pfx = sec_pkcs12_decode_pfx(pfx_data); + if(pfx == NULL) { + return NULL; + } + + valid_pfx = sec_pkcs12_validate_pfx(pfx, pwitem); + if(valid_pfx != PR_TRUE) { + SEC_PKCS12DestroyPFX(pfx); + pfx = NULL; + } + + return pfx; +} + +/* authenticated safe decoding, validation, and access routines + */ + +/* convert dogbert beta 3 authenticated safe structure to a post + * beta three structure, so that we don't have to change more routines. + */ +static SECStatus +sec_pkcs12_convert_old_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe) +{ + SEC_PKCS12Baggage *baggage; + SEC_PKCS12BaggageItem *bag; + SECStatus rv = SECSuccess; + + if(asafe->old_baggage.espvks == NULL) { + /* XXX should the ASN1 engine produce a single NULL element list + * rather than setting the pointer to NULL? + * There is no need to return an error -- assume that the list + * was empty. + */ + return SECSuccess; + } + + baggage = sec_pkcs12_create_baggage(asafe->poolp); + if(!baggage) { + return SECFailure; + } + bag = sec_pkcs12_create_external_bag(baggage); + if(!bag) { + return SECFailure; + } + + PORT_Memcpy(&asafe->baggage, baggage, sizeof(SEC_PKCS12Baggage)); + + /* if there are shrouded keys, append them to the bag */ + rv = SECSuccess; + if(asafe->old_baggage.espvks[0] != NULL) { + int nEspvk = 0; + rv = SECSuccess; + while((asafe->old_baggage.espvks[nEspvk] != NULL) && + (rv == SECSuccess)) { + rv = sec_pkcs12_append_shrouded_key(bag, + asafe->old_baggage.espvks[nEspvk]); + nEspvk++; + } + } + + return rv; +} + +/* decodes the authenticated safe item. a return of NULL indicates + * an error. however, the error will have occured either in memory + * allocation or in decoding the authenticated safe. + * + * if an old PFX item has been found, we want to convert the + * old authenticated safe to the new one. + */ +static SEC_PKCS12AuthenticatedSafe * +sec_pkcs12_decode_authenticated_safe(SEC_PKCS12PFXItem *pfx) +{ + SECItem *der_asafe = NULL; + SEC_PKCS12AuthenticatedSafe *asafe = NULL; + SECStatus rv; + + if(pfx == NULL) { + return NULL; + } + + der_asafe = SEC_PKCS7GetContent(&pfx->authSafe); + if(der_asafe == NULL) { + /* XXX set error ? */ + goto loser; + } + + asafe = sec_pkcs12_new_asafe(pfx->poolp); + if(asafe == NULL) { + goto loser; + } + + if(pfx->old == PR_FALSE) { + rv = SEC_ASN1DecodeItem(pfx->poolp, asafe, + SEC_PKCS12AuthenticatedSafeTemplate, + der_asafe); + asafe->old = PR_FALSE; + asafe->swapUnicode = pfx->swapUnicode; + } else { + /* handle beta exported files */ + rv = SEC_ASN1DecodeItem(pfx->poolp, asafe, + SEC_PKCS12AuthenticatedSafeTemplate_OLD, + der_asafe); + asafe->safe = &(asafe->old_safe); + rv = sec_pkcs12_convert_old_auth_safe(asafe); + asafe->old = PR_TRUE; + } + + if(rv != SECSuccess) { + goto loser; + } + + asafe->poolp = pfx->poolp; + + return asafe; + +loser: + return NULL; +} + +/* validates the safe within the authenticated safe item. + * in order to be valid: + * 1. the privacy salt must be present + * 2. the encryption algorithm must be supported (including + * export policy) + * PR_FALSE indicates an error, PR_TRUE indicates a valid safe + */ +static PRBool +sec_pkcs12_validate_encrypted_safe(SEC_PKCS12AuthenticatedSafe *asafe) +{ + PRBool valid = PR_FALSE; + SECAlgorithmID *algid; + + if(asafe == NULL) { + return PR_FALSE; + } + + /* if mode is password privacy, then privacySalt is assumed + * to be non-zero. + */ + if(asafe->privacySalt.len != 0) { + valid = PR_TRUE; + asafe->privacySalt.len /= 8; + } else { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + return PR_FALSE; + } + + /* until spec changes, content will have between 2 and 8 bytes depending + * upon the algorithm used if certs are unencrypted... + * also want to support case where content is empty -- which we produce + */ + if(SEC_PKCS7IsContentEmpty(asafe->safe, 8) == PR_TRUE) { + asafe->emptySafe = PR_TRUE; + return PR_TRUE; + } + + asafe->emptySafe = PR_FALSE; + + /* make sure that a pbe algorithm is being used */ + algid = SEC_PKCS7GetEncryptionAlgorithm(asafe->safe); + if(algid != NULL) { + if(SEC_PKCS5IsAlgorithmPBEAlg(algid)) { + valid = SEC_PKCS12DecryptionAllowed(algid); + + if(valid == PR_FALSE) { + PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM); + } + } else { + PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM); + valid = PR_FALSE; + } + } else { + valid = PR_FALSE; + PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM); + } + + return valid; +} + +/* validates authenticates safe: + * 1. checks that the version is supported + * 2. checks that only password privacy mode is used (currently) + * 3. further, makes sure safe has appropriate policies per above function + * PR_FALSE indicates failure. + */ +static PRBool +sec_pkcs12_validate_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe) +{ + PRBool valid = PR_TRUE; + SECOidTag safe_type; + int version; + + if(asafe == NULL) { + return PR_FALSE; + } + + /* check version, since it is default it may not be present. + * therefore, assume ok + */ + if((asafe->version.len > 0) && (asafe->old == PR_FALSE)) { + version = DER_GetInteger(&asafe->version); + if(version > SEC_PKCS12_PFX_VERSION) { + PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_VERSION); + return PR_FALSE; + } + } + + /* validate password mode is being used */ + safe_type = SEC_PKCS7ContentType(asafe->safe); + switch(safe_type) + { + case SEC_OID_PKCS7_ENCRYPTED_DATA: + valid = sec_pkcs12_validate_encrypted_safe(asafe); + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + default: + PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE); + valid = PR_FALSE; + break; + } + + return valid; +} + +/* retrieves the authenticated safe item from the PFX item + * before returning the authenticated safe, the validity of the + * authenticated safe is checked and if valid, returned. + * a return of NULL indicates that an error occured. + */ +static SEC_PKCS12AuthenticatedSafe * +sec_pkcs12_get_auth_safe(SEC_PKCS12PFXItem *pfx) +{ + SEC_PKCS12AuthenticatedSafe *asafe; + PRBool valid_safe; + + if(pfx == NULL) { + return NULL; + } + + asafe = sec_pkcs12_decode_authenticated_safe(pfx); + if(asafe == NULL) { + return NULL; + } + + valid_safe = sec_pkcs12_validate_auth_safe(asafe); + if(valid_safe != PR_TRUE) { + asafe = NULL; + } else if(asafe) { + asafe->baggage.poolp = asafe->poolp; + } + + return asafe; +} + +/* decrypts the authenticated safe. + * a return of anything but SECSuccess indicates an error. the + * password is not known to be valid until the call to the + * function sec_pkcs12_get_safe_contents. If decoding the safe + * fails, it is assumed the password was incorrect and the error + * is set then. any failure here is assumed to be due to + * internal problems in SEC_PKCS7DecryptContents or below. + */ +static SECStatus +sec_pkcs12_decrypt_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe, + SECItem *pwitem, + void *wincx) +{ + SECStatus rv = SECFailure; + SECItem *vpwd = NULL; + + if((asafe == NULL) || (pwitem == NULL)) { + return SECFailure; + } + + if(asafe->old == PR_FALSE) { + vpwd = sec_pkcs12_create_virtual_password(pwitem, &asafe->privacySalt, + asafe->swapUnicode); + if(vpwd == NULL) { + return SECFailure; + } + } + + rv = SEC_PKCS7DecryptContents(asafe->poolp, asafe->safe, + (asafe->old ? pwitem : vpwd), wincx); + + if(asafe->old == PR_FALSE) { + SECITEM_ZfreeItem(vpwd, PR_TRUE); + } + + return rv; +} + +/* extract the safe from the authenticated safe. + * if we are unable to decode the safe, then it is likely that the + * safe has not been decrypted or the password used to decrypt + * the safe was invalid. we assume that the password was invalid and + * set an error accordingly. + * a return of NULL indicates that an error occurred. + */ +static SEC_PKCS12SafeContents * +sec_pkcs12_get_safe_contents(SEC_PKCS12AuthenticatedSafe *asafe) +{ + SECItem *src = NULL; + SEC_PKCS12SafeContents *safe = NULL; + SECStatus rv = SECFailure; + + if(asafe == NULL) { + return NULL; + } + + safe = (SEC_PKCS12SafeContents *)PORT_ArenaZAlloc(asafe->poolp, + sizeof(SEC_PKCS12SafeContents)); + if(safe == NULL) { + return NULL; + } + safe->poolp = asafe->poolp; + safe->old = asafe->old; + safe->swapUnicode = asafe->swapUnicode; + + src = SEC_PKCS7GetContent(asafe->safe); + if(src != NULL) { + const SEC_ASN1Template *theTemplate; + if(asafe->old != PR_TRUE) { + theTemplate = SEC_PKCS12SafeContentsTemplate; + } else { + theTemplate = SEC_PKCS12SafeContentsTemplate_OLD; + } + + rv = SEC_ASN1DecodeItem(asafe->poolp, safe, theTemplate, src); + + /* if we could not decode the item, password was probably invalid */ + if(rv != SECSuccess) { + safe = NULL; + PORT_SetError(SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT); + } + } else { + PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE); + rv = SECFailure; + } + + return safe; +} + +/* import PFX item + * der_pfx is the der encoded pfx structure + * pbef and pbearg are the integrity/encryption password call back + * ncCall is the nickname collision calllback + * slot is the destination token + * wincx window handler + * + * on error, error code set and SECFailure returned + */ +SECStatus +SEC_PKCS12PutPFX(SECItem *der_pfx, SECItem *pwitem, + SEC_PKCS12NicknameCollisionCallback ncCall, + PK11SlotInfo *slot, + void *wincx) +{ + SEC_PKCS12PFXItem *pfx; + SEC_PKCS12AuthenticatedSafe *asafe; + SEC_PKCS12SafeContents *safe_contents = NULL; + SECStatus rv; + + if(!der_pfx || !pwitem || !slot) { + return SECFailure; + } + + /* decode and validate each section */ + rv = SECFailure; + + pfx = sec_pkcs12_get_pfx(der_pfx, pwitem); + if(pfx != NULL) { + asafe = sec_pkcs12_get_auth_safe(pfx); + if(asafe != NULL) { + + /* decrypt safe -- only if not empty */ + if(asafe->emptySafe != PR_TRUE) { + rv = sec_pkcs12_decrypt_auth_safe(asafe, pwitem, wincx); + if(rv == SECSuccess) { + safe_contents = sec_pkcs12_get_safe_contents(asafe); + if(safe_contents == NULL) { + rv = SECFailure; + } + } + } else { + safe_contents = sec_pkcs12_create_safe_contents(asafe->poolp); + safe_contents->swapUnicode = pfx->swapUnicode; + if(safe_contents == NULL) { + rv = SECFailure; + } else { + rv = SECSuccess; + } + } + + /* get safe contents and begin import */ + if(rv == SECSuccess) { + SEC_PKCS12DecoderContext *p12dcx; + + p12dcx = sec_PKCS12ConvertOldSafeToNew(pfx->poolp, slot, + pfx->swapUnicode, + pwitem, wincx, safe_contents, + &asafe->baggage); + if(!p12dcx) { + rv = SECFailure; + goto loser; + } + + if(SEC_PKCS12DecoderValidateBags(p12dcx, ncCall) + != SECSuccess) { + rv = SECFailure; + goto loser; + } + + rv = SEC_PKCS12DecoderImportBags(p12dcx); + } + + } + } + +loser: + + if(pfx) { + SEC_PKCS12DestroyPFX(pfx); + } + + return rv; +} + +PRBool +SEC_PKCS12ValidData(char *buf, int bufLen, long int totalLength) +{ + int lengthLength; + + PRBool valid = PR_FALSE; + + if(buf == NULL) { + return PR_FALSE; + } + + /* check for constructed sequence identifier tag */ + if(*buf == (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) { + totalLength--; /* header byte taken care of */ + buf++; + + lengthLength = (long int)SEC_ASN1LengthLength(totalLength - 1); + if(totalLength > 0x7f) { + lengthLength--; + *buf &= 0x7f; /* remove bit 8 indicator */ + if((*buf - (char)lengthLength) == 0) { + valid = PR_TRUE; + } + } else { + lengthLength--; + if((*buf - (char)lengthLength) == 0) { + valid = PR_TRUE; + } + } + } + + return valid; +} diff --git a/security/nss/lib/pkcs12/p12e.c b/security/nss/lib/pkcs12/p12e.c new file mode 100644 index 000000000..8cc683667 --- /dev/null +++ b/security/nss/lib/pkcs12/p12e.c @@ -0,0 +1,2254 @@ +/* + * 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 "p12t.h" +#include "p12.h" +#include "plarena.h" +#include "secitem.h" +#include "secoid.h" +#include "seccomon.h" +#include "secport.h" +#include "cert.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "secerr.h" +#include "pk11func.h" +#include "p12plcy.h" +#include "p12local.h" +#include "alghmac.h" +#include "prcpucfg.h" + +/********************************* + * Structures used in exporting the PKCS 12 blob + *********************************/ + +/* A SafeInfo is used for each ContentInfo which makes up the + * sequence of safes in the AuthenticatedSafe portion of the + * PFX structure. + */ +struct SEC_PKCS12SafeInfoStr { + PRArenaPool *arena; + + /* information for setting up password encryption */ + SECItem pwitem; + SECOidTag algorithm; + PK11SymKey *encryptionKey; + + /* how many items have been stored in this safe, + * we will skip any safe which does not contain any + * items + */ + unsigned int itemCount; + + /* the content info for the safe */ + SEC_PKCS7ContentInfo *cinfo; + + sec_PKCS12SafeContents *safe; +}; + +/* An opaque structure which contains information needed for exporting + * certificates and keys through PKCS 12. + */ +struct SEC_PKCS12ExportContextStr { + PRArenaPool *arena; + PK11SlotInfo *slot; + void *wincx; + + /* integrity information */ + PRBool integrityEnabled; + PRBool pwdIntegrity; + union { + struct sec_PKCS12PasswordModeInfo pwdInfo; + struct sec_PKCS12PublicKeyModeInfo pubkeyInfo; + } integrityInfo; + + /* helper functions */ + /* retrieve the password call back */ + SECKEYGetPasswordKey pwfn; + void *pwfnarg; + + /* safe contents bags */ + SEC_PKCS12SafeInfo **safeInfos; + unsigned int safeInfoCount; + + /* the sequence of safes */ + sec_PKCS12AuthenticatedSafe authSafe; + + /* information needing deletion */ + CERTCertificate **certList; +}; + +/* structures for passing information to encoder callbacks when processing + * data through the ASN1 engine. + */ +struct sec_pkcs12_encoder_output { + SEC_PKCS12EncoderOutputCallback outputfn; + void *outputarg; +}; + +struct sec_pkcs12_hmac_and_output_info { + void *arg; + struct sec_pkcs12_encoder_output output; +}; + +/* An encoder context which is used for the actual encoding + * portion of PKCS 12. + */ +typedef struct sec_PKCS12EncoderContextStr { + PRArenaPool *arena; + SEC_PKCS12ExportContext *p12exp; + PK11SymKey *encryptionKey; + + /* encoder information - this is set up based on whether + * password based or public key pased privacy is being used + */ + SEC_ASN1EncoderContext *ecx; + union { + struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo; + struct sec_pkcs12_encoder_output encOutput; + } output; + + /* structures for encoding of PFX and MAC */ + sec_PKCS12PFXItem pfx; + sec_PKCS12MacData mac; + + /* authenticated safe encoding tracking information */ + SEC_PKCS7ContentInfo *aSafeCinfo; + SEC_PKCS7EncoderContext *aSafeP7Ecx; + SEC_ASN1EncoderContext *aSafeEcx; + unsigned int currentSafe; + + /* hmac context */ + void *hmacCx; +} sec_PKCS12EncoderContext; + + +/********************************* + * Export setup routines + *********************************/ + +/* SEC_PKCS12CreateExportContext + * Creates an export context and sets the unicode and password retrieval + * callbacks. This is the first call which must be made when exporting + * a PKCS 12 blob. + * + * pwfn, pwfnarg - password retrieval callback and argument. these are + * required for password-authentication mode. + */ +SEC_PKCS12ExportContext * +SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg, + PK11SlotInfo *slot, void *wincx) +{ + PRArenaPool *arena = NULL; + SEC_PKCS12ExportContext *p12ctxt = NULL; + + /* allocate the arena and create the context */ + arena = PORT_NewArena(4096); + if(!arena) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena, + sizeof(SEC_PKCS12ExportContext)); + if(!p12ctxt) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* password callback for key retrieval */ + p12ctxt->pwfn = pwfn; + p12ctxt->pwfnarg = pwfnarg; + + p12ctxt->integrityEnabled = PR_FALSE; + p12ctxt->arena = arena; + p12ctxt->wincx = wincx; + p12ctxt->slot = (slot) ? slot : PK11_GetInternalSlot(); + + return p12ctxt; + +loser: + if(arena) { + PORT_FreeArena(arena, PR_TRUE); + } + + return NULL; +} + +/* + * Adding integrity mode + */ + +/* SEC_PKCS12AddPasswordIntegrity + * Add password integrity to the exported data. If an integrity method + * has already been set, then return an error. + * + * p12ctxt - the export context + * pwitem - the password for integrity mode + * integAlg - the integrity algorithm to use for authentication. + */ +SECStatus +SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag integAlg) +{ + if(!p12ctxt || p12ctxt->integrityEnabled) { + return SECFailure; + } + + /* set up integrity information */ + p12ctxt->pwdIntegrity = PR_TRUE; + p12ctxt->integrityInfo.pwdInfo.password = + (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); + if(!p12ctxt->integrityInfo.pwdInfo.password) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + if(SECITEM_CopyItem(p12ctxt->arena, + p12ctxt->integrityInfo.pwdInfo.password, pwitem) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg; + p12ctxt->integrityEnabled = PR_TRUE; + + return SECSuccess; +} + +/* SEC_PKCS12AddPublicKeyIntegrity + * Add public key integrity to the exported data. If an integrity method + * has already been set, then return an error. The certificate must be + * allowed to be used as a signing cert. + * + * p12ctxt - the export context + * cert - signer certificate + * certDb - the certificate database + * algorithm - signing algorithm + * keySize - size of the signing key (?) + */ +SECStatus +SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt, + CERTCertificate *cert, CERTCertDBHandle *certDb, + SECOidTag algorithm, int keySize) +{ + if(!p12ctxt) { + return SECFailure; + } + + p12ctxt->integrityInfo.pubkeyInfo.cert = cert; + p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb; + p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm; + p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize; + p12ctxt->integrityEnabled = PR_TRUE; + + return SECSuccess; +} + + +/* + * Adding safes - encrypted (password/public key) or unencrypted + * Each of the safe creation routines return an opaque pointer which + * are later passed into the routines for exporting certificates and + * keys. + */ + +/* append the newly created safeInfo to list of safeInfos in the export + * context. + */ +static SECStatus +sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *info) +{ + void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL; + + if(!p12ctxt || !info) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + /* if no safeInfos have been set, create the list, otherwise expand it. */ + if(!p12ctxt->safeInfoCount) { + p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->arena, + 2 * sizeof(SEC_PKCS12SafeInfo *)); + dummy1 = p12ctxt->safeInfos; + p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, + 2 * sizeof(SECItem *)); + dummy2 = p12ctxt->authSafe.encodedSafes; + } else { + dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos, + (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12SafeInfo *), + (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12SafeInfo *)); + p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1; + dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes, + (p12ctxt->authSafe.safeCount + 1) * sizeof(SECItem *), + (p12ctxt->authSafe.safeCount + 2) * sizeof(SECItem *)); + p12ctxt->authSafe.encodedSafes = (SECItem**)dummy2; + } + if(!dummy1 || !dummy2) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* append the new safeInfo and null terminate the list */ + p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info; + p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL; + p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] = + (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); + if(!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL; + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(p12ctxt->arena, mark); + return SECFailure; +} + +/* SEC_PKCS12CreatePasswordPrivSafe + * Create a password privacy safe to store exported information in. + * + * p12ctxt - export context + * pwitem - password for encryption + * privAlg - pbe algorithm through which encryption is done. + */ +SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag privAlg) +{ + SEC_PKCS12SafeInfo *safeInfo = NULL; + void *mark = NULL; + PK11SlotInfo *slot; + SECAlgorithmID *algId; + SECItem uniPwitem = {siBuffer, NULL, 0}; + + if(!p12ctxt) { + return NULL; + } + + /* allocate the safe info */ + mark = PORT_ArenaMark(p12ctxt->arena); + safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SEC_PKCS12SafeInfo)); + if(!safeInfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; + } + + safeInfo->itemCount = 0; + + /* create the encrypted safe */ + safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn, + p12ctxt->pwfnarg); + if(!safeInfo->cinfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + safeInfo->arena = p12ctxt->arena; + + /* convert the password to unicode */ + if(!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem, + PR_TRUE, PR_TRUE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* generate the encryption key */ + slot = p12ctxt->slot; + if(!slot) { + slot = PK11_GetInternalKeySlot(); + if(!slot) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + } + + algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo); + safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem, + PR_FALSE, p12ctxt->wincx); + if(!safeInfo->encryptionKey) { + goto loser; + } + + safeInfo->arena = p12ctxt->arena; + safeInfo->safe = NULL; + if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { + goto loser; + } + + if(uniPwitem.data) { + SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); + } + PORT_ArenaUnmark(p12ctxt->arena, mark); + return safeInfo; + +loser: + if(safeInfo->cinfo) { + SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); + } + + if(uniPwitem.data) { + SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); + } + + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/* SEC_PKCS12CreateUnencryptedSafe + * Creates an unencrypted safe within the export context. + * + * p12ctxt - the export context + */ +SEC_PKCS12SafeInfo * +SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt) +{ + SEC_PKCS12SafeInfo *safeInfo = NULL; + void *mark = NULL; + + if(!p12ctxt) { + return NULL; + } + + /* create the safe info */ + mark = PORT_ArenaMark(p12ctxt->arena); + safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SEC_PKCS12SafeInfo)); + if(!safeInfo) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + safeInfo->itemCount = 0; + + /* create the safe content */ + safeInfo->cinfo = SEC_PKCS7CreateData(); + if(!safeInfo->cinfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return safeInfo; + +loser: + if(safeInfo->cinfo) { + SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); + } + + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/* SEC_PKCS12CreatePubKeyEncryptedSafe + * Creates a safe which is protected by public key encryption. + * + * p12ctxt - the export context + * certDb - the certificate database + * signer - the signer's certificate + * recipients - the list of recipient certificates. + * algorithm - the encryption algorithm to use + * keysize - the algorithms key size (?) + */ +SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt, + CERTCertDBHandle *certDb, + CERTCertificate *signer, + CERTCertificate **recipients, + SECOidTag algorithm, int keysize) +{ + SEC_PKCS12SafeInfo *safeInfo = NULL; + void *mark = NULL; + + if(!p12ctxt || !signer || !recipients || !(*recipients)) { + return NULL; + } + + /* allocate the safeInfo */ + mark = PORT_ArenaMark(p12ctxt->arena); + safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SEC_PKCS12SafeInfo)); + if(!safeInfo) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + safeInfo->itemCount = 0; + safeInfo->arena = p12ctxt->arena; + + /* create the enveloped content info using certUsageEmailSigner currently. + * XXX We need to eventually use something other than certUsageEmailSigner + */ + safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner, + certDb, algorithm, keysize, + p12ctxt->pwfn, p12ctxt->pwfnarg); + if(!safeInfo->cinfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* add recipients */ + if(recipients) { + unsigned int i = 0; + while(recipients[i] != NULL) { + SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i], + certUsageEmailRecipient, certDb); + if(rv != SECSuccess) { + goto loser; + } + i++; + } + } + + if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return safeInfo; + +loser: + if(safeInfo->cinfo) { + SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); + safeInfo->cinfo = NULL; + } + + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/********************************* + * Routines to handle the exporting of the keys and certificates + *********************************/ + +/* creates a safe contents which safeBags will be appended to */ +sec_PKCS12SafeContents * +sec_PKCS12CreateSafeContents(PRArenaPool *arena) +{ + sec_PKCS12SafeContents *safeContents; + + if(arena == NULL) { + return NULL; + } + + /* create the safe contents */ + safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena, + sizeof(sec_PKCS12SafeContents)); + if(!safeContents) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* set up the internal contents info */ + safeContents->safeBags = NULL; + safeContents->arena = arena; + safeContents->bagCount = 0; + + return safeContents; + +loser: + return NULL; +} + +/* appends a safe bag to a safeContents using the specified arena. + */ +SECStatus +sec_pkcs12_append_bag_to_safe_contents(PRArenaPool *arena, + sec_PKCS12SafeContents *safeContents, + sec_PKCS12SafeBag *safeBag) +{ + void *mark = NULL, *dummy = NULL; + + if(!arena || !safeBag || !safeContents) { + return SECFailure; + } + + mark = PORT_ArenaMark(arena); + if(!mark) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + /* allocate space for the list, or reallocate to increase space */ + if(!safeContents->safeBags) { + safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena, + (2 * sizeof(sec_PKCS12SafeBag *))); + dummy = safeContents->safeBags; + safeContents->bagCount = 0; + } else { + dummy = PORT_ArenaGrow(arena, safeContents->safeBags, + (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag *), + (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag *)); + safeContents->safeBags = (sec_PKCS12SafeBag **)dummy; + } + + if(!dummy) { + PORT_ArenaRelease(arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + /* append the bag at the end and null terminate the list */ + safeContents->safeBags[safeContents->bagCount++] = safeBag; + safeContents->safeBags[safeContents->bagCount] = NULL; + + PORT_ArenaUnmark(arena, mark); + + return SECSuccess; +} + +/* appends a safeBag to a specific safeInfo. + */ +SECStatus +sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt, + SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag) +{ + sec_PKCS12SafeContents *dest; + SECStatus rv = SECFailure; + + if(!p12ctxt || !safeBag || !safeInfo) { + return SECFailure; + } + + if(!safeInfo->safe) { + safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena); + if(!safeInfo->safe) { + return SECFailure; + } + } + + dest = safeInfo->safe; + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag); + if(rv == SECSuccess) { + safeInfo->itemCount++; + } + + return rv; +} + +/* Creates a safeBag of the specified type, and if bagData is specified, + * the contents are set. The contents could be set later by the calling + * routine. + */ +sec_PKCS12SafeBag * +sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType, + void *bagData) +{ + sec_PKCS12SafeBag *safeBag; + PRBool setName = PR_TRUE; + void *mark = NULL; + SECStatus rv = SECSuccess; + SECOidData *oidData = NULL; + + if(!p12ctxt) { + return NULL; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + if(!mark) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(sec_PKCS12SafeBag)); + if(!safeBag) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* set the bags content based upon bag type */ + switch(bagType) { + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + safeBag->safeBagContent.pkcs8KeyBag = + (SECKEYPrivateKeyInfo *)bagData; + break; + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData; + break; + case SEC_OID_PKCS12_V1_CRL_BAG_ID: + safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData; + break; + case SEC_OID_PKCS12_V1_SECRET_BAG_ID: + safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData; + break; + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + safeBag->safeBagContent.pkcs8ShroudedKeyBag = + (SECKEYEncryptedPrivateKeyInfo *)bagData; + break; + case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: + safeBag->safeBagContent.safeContents = + (sec_PKCS12SafeContents *)bagData; + setName = PR_FALSE; + break; + default: + goto loser; + } + + oidData = SECOID_FindOIDByTag(bagType); + if(oidData) { + rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->oid); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + } else { + goto loser; + } + + safeBag->arena = p12ctxt->arena; + PORT_ArenaUnmark(p12ctxt->arena, mark); + + return safeBag; + +loser: + if(mark) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } + + return NULL; +} + +/* Creates a new certificate bag and returns a pointer to it. If an error + * occurs NULL is returned. + */ +sec_PKCS12CertBag * +sec_PKCS12NewCertBag(PRArenaPool *arena, SECOidTag certType) +{ + sec_PKCS12CertBag *certBag = NULL; + SECOidData *bagType = NULL; + SECStatus rv; + void *mark = NULL; + + if(!arena) { + return NULL; + } + + mark = PORT_ArenaMark(arena); + certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena, + sizeof(sec_PKCS12CertBag)); + if(!certBag) { + PORT_ArenaRelease(arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + bagType = SECOID_FindOIDByTag(certType); + if(!bagType) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_ArenaUnmark(arena, mark); + return certBag; + +loser: + PORT_ArenaRelease(arena, mark); + return NULL; +} + +/* Creates a new CRL bag and returns a pointer to it. If an error + * occurs NULL is returned. + */ +sec_PKCS12CRLBag * +sec_PKCS12NewCRLBag(PRArenaPool *arena, SECOidTag crlType) +{ + sec_PKCS12CRLBag *crlBag = NULL; + SECOidData *bagType = NULL; + SECStatus rv; + void *mark = NULL; + + if(!arena) { + return NULL; + } + + mark = PORT_ArenaMark(arena); + crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena, + sizeof(sec_PKCS12CRLBag)); + if(!crlBag) { + PORT_ArenaRelease(arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + bagType = SECOID_FindOIDByTag(crlType); + if(!bagType) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_ArenaUnmark(arena, mark); + return crlBag; + +loser: + PORT_ArenaRelease(arena, mark); + return NULL; +} + +/* sec_PKCS12AddAttributeToBag + * adds an attribute to a safeBag. currently, the only attributes supported + * are those which are specified within PKCS 12. + * + * p12ctxt - the export context + * safeBag - the safeBag to which attributes are appended + * attrType - the attribute type + * attrData - the attribute data + */ +SECStatus +sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt, + sec_PKCS12SafeBag *safeBag, SECOidTag attrType, + SECItem *attrData) +{ + sec_PKCS12Attribute *attribute; + void *mark = NULL, *dummy = NULL; + SECOidData *oiddata = NULL; + SECItem unicodeName = { siBuffer, NULL, 0}; + void *src = NULL; + unsigned int nItems = 0; + SECStatus rv; + + if(!safeBag || !p12ctxt) { + return SECFailure; + } + + mark = PORT_ArenaMark(safeBag->arena); + + /* allocate the attribute */ + attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena, + sizeof(sec_PKCS12Attribute)); + if(!attribute) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* set up the attribute */ + oiddata = SECOID_FindOIDByTag(attrType); + if(!oiddata) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) != + SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + nItems = 1; + switch(attrType) { + case SEC_OID_PKCS9_LOCAL_KEY_ID: + { + src = attrData; + break; + } + case SEC_OID_PKCS9_FRIENDLY_NAME: + { + if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, + &unicodeName, attrData, PR_FALSE, + PR_FALSE, PR_TRUE)) { + goto loser; + } + src = &unicodeName; + break; + } + default: + goto loser; + } + + /* append the attribute to the attribute value list */ + attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, + ((nItems + 1) * sizeof(SECItem *))); + if(!attribute->attrValue) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* XXX this will need to be changed if attributes requiring more than + * one element are ever used. + */ + attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SECItem)); + if(!attribute->attrValue[0]) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + attribute->attrValue[1] = NULL; + + rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0], + (SECItem*)src); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* append the attribute to the safeBag attributes */ + if(safeBag->nAttribs) { + dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs, + ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)), + ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *))); + safeBag->attribs = (sec_PKCS12Attribute **)dummy; + } else { + safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->arena, + 2 * sizeof(sec_PKCS12Attribute *)); + dummy = safeBag->attribs; + } + if(!dummy) { + goto loser; + } + + safeBag->attribs[safeBag->nAttribs] = attribute; + safeBag->attribs[++safeBag->nAttribs] = NULL; + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + if(mark) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } + + return SECFailure; +} + +/* SEC_PKCS12AddCert + * Adds a certificate to the data being exported. + * + * p12ctxt - the export context + * safe - the safeInfo to which the certificate is placed + * nestedDest - if the cert is to be placed within a nested safeContents then, + * this value is to be specified with the destination + * cert - the cert to export + * certDb - the certificate database handle + * keyId - a unique identifier to associate a certificate/key pair + * includeCertChain - PR_TRUE if the certificate chain is to be included. + */ +SECStatus +SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, + void *nestedDest, CERTCertificate *cert, + CERTCertDBHandle *certDb, SECItem *keyId, + PRBool includeCertChain) +{ + sec_PKCS12CertBag *certBag; + sec_PKCS12SafeBag *safeBag; + void *mark; + SECStatus rv; + SECItem nick = {siBuffer, NULL,0}; + + if(!p12ctxt || !cert) { + return SECFailure; + } + mark = PORT_ArenaMark(p12ctxt->arena); + + /* allocate the cert bag */ + certBag = sec_PKCS12NewCertBag(p12ctxt->arena, + SEC_OID_PKCS9_X509_CERT); + if(!certBag) { + goto loser; + } + + if(SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert, + &cert->derCert) != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* if the cert chain is to be included, we should only be exporting + * the cert from our internal database. + */ + if(includeCertChain) { + CERTCertificateList *certList = CERT_CertChainFromCert(cert, + certUsageSSLClient, + PR_TRUE); + unsigned int count = 0; + if(!certList) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* add cert chain */ + for(count = 0; count < (unsigned int)certList->len; count++) { + if(SECITEM_CompareItem(&certList->certs[count], &cert->derCert) + != SECEqual) { + CERTCertificate *tempCert; + + /* decode the certificate */ + tempCert = CERT_NewTempCertificate(certDb, + &certList->certs[count], NULL, + PR_FALSE, PR_TRUE); + if(!tempCert) { + CERT_DestroyCertificateList(certList); + goto loser; + } + + /* add the certificate */ + if(SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert, certDb, + NULL, PR_FALSE) != SECSuccess) { + CERT_DestroyCertificate(tempCert); + CERT_DestroyCertificateList(certList); + goto loser; + } + CERT_DestroyCertificate(tempCert); + } + } + CERT_DestroyCertificateList(certList); + } + + /* if the certificate has a nickname, we will set the friendly name + * to that. + */ + if(cert->nickname) { + if (cert->slot && !PK11_IsInternal(cert->slot)) { + /* + * The cert is coming off of an external token, + * let's strip the token name from the nickname + * and only add what comes after the colon as the + * nickname. -javi + */ + char *delimit; + + delimit = PORT_Strchr(cert->nickname,':'); + if (delimit == NULL) { + nick.data = (unsigned char *)cert->nickname; + nick.len = PORT_Strlen(cert->nickname); + } else { + delimit++; + nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena, + delimit); + nick.len = PORT_Strlen(delimit); + } + } else { + nick.data = (unsigned char *)cert->nickname; + nick.len = PORT_Strlen(cert->nickname); + } + } + + safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID, + certBag); + if(!safeBag) { + goto loser; + } + + /* add the friendly name and keyId attributes, if necessary */ + if(nick.data) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, + SEC_OID_PKCS9_FRIENDLY_NAME, &nick) + != SECSuccess) { + goto loser; + } + } + + if(keyId) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + goto loser; + } + } + + /* append the cert safeBag */ + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + safeBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag); + } + + if(rv != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + if(mark) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } + + return SECFailure; +} + +/* SEC_PKCS12AddEncryptedKey + * Extracts the key associated with a particular certificate and exports + * it. + * + * p12ctxt - the export context + * safe - the safeInfo to place the key in + * nestedDest - the nested safeContents to place a key + * cert - the certificate which the key belongs to + * shroudKey - encrypt the private key for export. This value should + * always be true. lower level code will not allow the export + * of unencrypted private keys. + * algorithm - the algorithm with which to encrypt the private key + * pwitem - the password to encrypted the private key with + * keyId - the keyID attribute + * nickName - the nickname attribute + */ +static SECStatus +SEC_PKCS12AddEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, + SECKEYEncryptedPrivateKeyInfo *epki, SEC_PKCS12SafeInfo *safe, + void *nestedDest, SECItem *keyId, SECItem *nickName) +{ + void *mark; + void *keyItem; + SECOidTag keyType; + SECStatus rv = SECFailure; + sec_PKCS12SafeBag *returnBag; + + if(!p12ctxt || !safe || !epki) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + keyItem = PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if(!keyItem) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, + (SECKEYEncryptedPrivateKeyInfo *)keyItem, + epki); + keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID; + + if(rv != SECSuccess) { + goto loser; + } + + /* create the safe bag and set any attributes */ + returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem); + if(!returnBag) { + rv = SECFailure; + goto loser; + } + + if(nickName) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, + SEC_OID_PKCS9_FRIENDLY_NAME, nickName) + != SECSuccess) { + goto loser; + } + } + + if(keyId) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + goto loser; + } + } + + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + returnBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag); + } + +loser: + + if (rv != SECSuccess) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } else { + PORT_ArenaUnmark(p12ctxt->arena, mark); + } + + return rv; +} + +/* SEC_PKCS12AddKeyForCert + * Extracts the key associated with a particular certificate and exports + * it. + * + * p12ctxt - the export context + * safe - the safeInfo to place the key in + * nestedDest - the nested safeContents to place a key + * cert - the certificate which the key belongs to + * shroudKey - encrypt the private key for export. This value should + * always be true. lower level code will not allow the export + * of unencrypted private keys. + * algorithm - the algorithm with which to encrypt the private key + * pwitem - the password to encrypt the private key with + * keyId - the keyID attribute + * nickName - the nickname attribute + */ +SECStatus +SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, + void *nestedDest, CERTCertificate *cert, + PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem, + SECItem *keyId, SECItem *nickName) +{ + void *mark; + void *keyItem; + SECOidTag keyType; + SECStatus rv = SECFailure; + SECItem nickname = {siBuffer,NULL,0}, uniPwitem = {siBuffer, NULL, 0}; + sec_PKCS12SafeBag *returnBag; + + if(!p12ctxt || !cert || !safe) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + /* retrieve the key based upon the type that it is and + * specify the type of safeBag to store the key in + */ + if(!shroudKey) { + + /* extract the key unencrypted. this will most likely go away */ + SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert, + p12ctxt->wincx); + if(!pki) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + return SECFailure; + } + keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo)); + if(!keyItem) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena, + (SECKEYPrivateKeyInfo *)keyItem, pki); + keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID; + SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); + } else { + + /* extract the key encrypted */ + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + PK11SlotInfo *slot = p12ctxt->slot; + + if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, &uniPwitem, + pwitem, PR_TRUE, PR_TRUE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* we want to make sure to take the key out of the key slot */ + if(PK11_IsInternal(p12ctxt->slot)) { + slot = PK11_GetInternalKeySlot(); + } + + epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, + &uniPwitem, cert, 1, + p12ctxt->wincx); + if(PK11_IsInternal(p12ctxt->slot)) { + PK11_FreeSlot(slot); + } + + keyItem = PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if(!keyItem) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(!epki) { + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + return SECFailure; + } + rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, + (SECKEYEncryptedPrivateKeyInfo *)keyItem, + epki); + keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID; + SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); + } + + if(rv != SECSuccess) { + goto loser; + } + + /* if no nickname specified, let's see if the certificate has a + * nickname. + */ + if(!nickName) { + if(cert->nickname) { + nickname.data = (unsigned char *)cert->nickname; + nickname.len = PORT_Strlen(cert->nickname); + nickName = &nickname; + } + } + + /* create the safe bag and set any attributes */ + returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem); + if(!returnBag) { + rv = SECFailure; + goto loser; + } + + if(nickName) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, + SEC_OID_PKCS9_FRIENDLY_NAME, nickName) + != SECSuccess) { + goto loser; + } + } + + if(keyId) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + goto loser; + } + } + + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + returnBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag); + } + +loser: + + if (rv != SECSuccess) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } else { + PORT_ArenaUnmark(p12ctxt->arena, mark); + } + + return rv; +} + +/* SEC_PKCS12AddCertAndEncryptedKey + * Add a certificate and key pair to be exported. + * + * p12ctxt - the export context + * certSafe - the safeInfo where the cert is stored + * certNestedDest - the nested safeContents to store the cert + * keySafe - the safeInfo where the key is stored + * keyNestedDest - the nested safeContents to store the key + * shroudKey - extract the private key encrypted? + * pwitem - the password with which the key is encrypted + * algorithm - the algorithm with which the key is encrypted + */ +SECStatus +SEC_PKCS12AddDERCertAndEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, + SECItem *derCert, void *keySafe, + void *keyNestedDest, SECKEYEncryptedPrivateKeyInfo *epki, + char *nickname) +{ + SECStatus rv = SECFailure; + SGNDigestInfo *digest = NULL; + void *mark = NULL; + CERTCertificate *cert; + SECItem nick = {siBuffer, NULL,0}, *nickPtr = NULL; + + if(!p12ctxt || !certSafe || !keySafe || !derCert) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert, + NULL, PR_FALSE, PR_TRUE); + if(!cert) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + cert->nickname = nickname; + + /* generate the thumbprint of the cert to use as a keyId */ + digest = sec_pkcs12_compute_thumbprint(&cert->derCert); + if(!digest) { + CERT_DestroyCertificate(cert); + return SECFailure; + } + + /* add the certificate */ + rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, + certNestedDest, cert, NULL, + &digest->digest, PR_FALSE); + if(rv != SECSuccess) { + goto loser; + } + + if(nickname) { + nick.data = (unsigned char *)nickname; + nick.len = PORT_Strlen(nickname); + nickPtr = &nick; + } else { + nickPtr = NULL; + } + + /* add the key */ + rv = SEC_PKCS12AddEncryptedKey(p12ctxt, epki, (SEC_PKCS12SafeInfo*)keySafe, + keyNestedDest, &digest->digest, nickPtr ); + if(rv != SECSuccess) { + goto loser; + } + + SGN_DestroyDigestInfo(digest); + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + SGN_DestroyDigestInfo(digest); + CERT_DestroyCertificate(cert); + PORT_ArenaRelease(p12ctxt->arena, mark); + + return SECFailure; +} + +/* SEC_PKCS12AddCertAndKey + * Add a certificate and key pair to be exported. + * + * p12ctxt - the export context + * certSafe - the safeInfo where the cert is stored + * certNestedDest - the nested safeContents to store the cert + * keySafe - the safeInfo where the key is stored + * keyNestedDest - the nested safeContents to store the key + * shroudKey - extract the private key encrypted? + * pwitem - the password with which the key is encrypted + * algorithm - the algorithm with which the key is encrypted + */ +SECStatus +SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, + CERTCertificate *cert, CERTCertDBHandle *certDb, + void *keySafe, void *keyNestedDest, + PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm) +{ + SECStatus rv = SECFailure; + SGNDigestInfo *digest = NULL; + void *mark = NULL; + + if(!p12ctxt || !certSafe || !keySafe || !cert) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + /* generate the thumbprint of the cert to use as a keyId */ + digest = sec_pkcs12_compute_thumbprint(&cert->derCert); + if(!digest) { + PORT_ArenaRelease(p12ctxt->arena, mark); + return SECFailure; + } + + /* add the certificate */ + rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, + (SEC_PKCS12SafeInfo*)certNestedDest, cert, certDb, + &digest->digest, PR_TRUE); + if(rv != SECSuccess) { + goto loser; + } + + /* add the key */ + rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo*)keySafe, + keyNestedDest, cert, + shroudKey, algorithm, pwitem, + &digest->digest, NULL ); + if(rv != SECSuccess) { + goto loser; + } + + SGN_DestroyDigestInfo(digest); + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + SGN_DestroyDigestInfo(digest); + PORT_ArenaRelease(p12ctxt->arena, mark); + + return SECFailure; +} + +/* SEC_PKCS12CreateNestedSafeContents + * Allows nesting of safe contents to be implemented. No limit imposed on + * depth. + * + * p12ctxt - the export context + * baseSafe - the base safeInfo + * nestedDest - a parent safeContents (?) + */ +void * +SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt, + void *baseSafe, void *nestedDest) +{ + sec_PKCS12SafeContents *newSafe; + sec_PKCS12SafeBag *safeContentsBag; + void *mark; + SECStatus rv; + + if(!p12ctxt || !baseSafe) { + return NULL; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena); + if(!newSafe) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* create the safeContents safeBag */ + safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt, + SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID, + newSafe); + if(!safeContentsBag) { + goto loser; + } + + /* append the safeContents to the appropriate area */ + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + safeContentsBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo*)baseSafe, + safeContentsBag); + } + if(rv != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return newSafe; + +loser: + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/********************************* + * Encoding routines + *********************************/ + +/* set up the encoder context based on information in the export context + * and return the newly allocated enocoder context. A return of NULL + * indicates an error occurred. + */ +sec_PKCS12EncoderContext * +sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp) +{ + sec_PKCS12EncoderContext *p12enc = NULL; + unsigned int i, nonEmptyCnt; + + if(!p12exp || !p12exp->safeInfos) { + return NULL; + } + + /* check for any empty safes and skip them */ + i = nonEmptyCnt = 0; + while(p12exp->safeInfos[i]) { + if(p12exp->safeInfos[i]->itemCount) { + nonEmptyCnt++; + } + i++; + } + if(nonEmptyCnt == 0) { + return NULL; + } + p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL; + + /* allocate the encoder context */ + p12enc = (sec_PKCS12EncoderContext*)PORT_ArenaZAlloc(p12exp->arena, + sizeof(sec_PKCS12EncoderContext)); + if(!p12enc) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + p12enc->arena = p12exp->arena; + p12enc->p12exp = p12exp; + + /* set up the PFX version and information */ + PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem)); + if(!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version), + SEC_PKCS12_VERSION) ) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* set up the authenticated safe content info based on the + * type of integrity being used. this should be changed to + * enforce integrity mode, but will not be implemented until + * it is confirmed that integrity must be in place + */ + if(p12exp->integrityEnabled && !p12exp->pwdIntegrity) { + SECStatus rv; + + /* create public key integrity mode */ + p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData( + p12exp->integrityInfo.pubkeyInfo.cert, + certUsageEmailSigner, + p12exp->integrityInfo.pubkeyInfo.certDb, + p12exp->integrityInfo.pubkeyInfo.algorithm, + NULL, + p12exp->pwfn, + p12exp->pwfnarg); + if(!p12enc->aSafeCinfo) { + goto loser; + } + if(SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo,NULL) != SECSuccess) { + SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo); + goto loser; + } + rv = SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo); + PORT_Assert(rv == SECSuccess); + } else { + p12enc->aSafeCinfo = SEC_PKCS7CreateData(); + + /* init password pased integrity mode */ + if(p12exp->integrityEnabled) { + SECItem pwd = {siBuffer,NULL, 0}, *key; + SECItem *salt = sec_pkcs12_generate_salt(); + PBEBitGenContext *pbeCtxt = NULL; + + /* zero out macData and set values */ + PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData)); + + if(!salt) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + SECITEM_ZfreeItem(salt, PR_TRUE); + + /* generate HMAC key */ + if(!sec_pkcs12_convert_item_to_unicode(p12exp->arena, &pwd, + p12exp->integrityInfo.pwdInfo.password, PR_TRUE, + PR_TRUE, PR_TRUE)) { + goto loser; + } + pbeCtxt = PBE_CreateContext(p12exp->integrityInfo.pwdInfo.algorithm, + pbeBitGenIntegrityKey, &pwd, + &(p12enc->mac.macSalt), 160, 1); + if(!pbeCtxt) { + goto loser; + } + key = PBE_GenerateBits(pbeCtxt); + PBE_DestroyContext(pbeCtxt); + if(!key) { + goto loser; + } + + /* initialize hmac */ + p12enc->hmacCx = HMAC_Create( + p12exp->integrityInfo.pwdInfo.algorithm, + key->data, key->len); + SECITEM_ZfreeItem(key, PR_TRUE); + if(!p12enc->hmacCx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + HMAC_Begin((HMACContext*)p12enc->hmacCx); + } + } + + if(!p12enc->aSafeCinfo) { + goto loser; + } + + return p12enc; + +loser: + if(p12enc) { + if(p12enc->aSafeCinfo) { + SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo); + } + + PORT_Free(p12enc); + } + + return NULL; +} + +/* callback wrapper to allow the ASN1 engine to call the PKCS 12 + * output routines. + */ +static void +sec_pkcs12_encoder_out(void *arg, const char *buf, unsigned long len, + int depth, SEC_ASN1EncodingPart data_kind) +{ + struct sec_pkcs12_encoder_output *output; + + output = (struct sec_pkcs12_encoder_output*)arg; + (* output->outputfn)(output->outputarg, buf, len); +} + +/* callback wrapper to wrap SEC_PKCS7EncoderUpdate for ASN1 encoder + */ +static void +sec_pkcs12_wrap_pkcs7_encoder_update(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + SEC_PKCS7EncoderContext *ecx; + if(!buf || !len) { + return; + } + + ecx = (SEC_PKCS7EncoderContext*)arg; + SEC_PKCS7EncoderUpdate(ecx, buf, len); +} + +/* callback wrapper to wrap SEC_ASN1EncoderUpdate for PKCS 7 encoding + */ +static void +sec_pkcs12_wrap_asn1_update_for_p7_update(void *arg, const char *buf, + unsigned long len) +{ + if(!buf && !len) return; + + SEC_ASN1EncoderUpdate((SEC_ASN1EncoderContext*)arg, buf, len); +} + +/* callback wrapper which updates the HMAC and passes on bytes to the + * appropriate output function. + */ +static void +sec_pkcs12_asafe_update_hmac_and_encode_bits(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + sec_PKCS12EncoderContext *p12ecx; + + p12ecx = (sec_PKCS12EncoderContext*)arg; + HMAC_Update((HMACContext*)p12ecx->hmacCx, (unsigned char *)buf, len); + sec_pkcs12_wrap_pkcs7_encoder_update(p12ecx->aSafeP7Ecx, buf, len, + depth, data_kind); +} + +/* this function encodes content infos which are part of the + * sequence of content infos labeled AuthenticatedSafes + */ +static SECStatus +sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx) +{ + SECStatus rv = SECSuccess; + SEC_PKCS5KeyAndPassword keyPwd; + SEC_PKCS7EncoderContext *p7ecx; + SEC_PKCS7ContentInfo *cinfo; + SEC_ASN1EncoderContext *ecx = NULL; + + void *arg = NULL; + + if(p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) { + SEC_PKCS12SafeInfo *safeInfo; + SECOidTag cinfoType; + + safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe]; + + /* skip empty safes */ + if(safeInfo->itemCount == 0) { + return SECSuccess; + } + + cinfo = safeInfo->cinfo; + cinfoType = SEC_PKCS7ContentType(cinfo); + + /* determine the safe type and set the appropriate argument */ + switch(cinfoType) { + case SEC_OID_PKCS7_DATA: + arg = NULL; + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + keyPwd.pwitem = &safeInfo->pwitem; + keyPwd.key = safeInfo->encryptionKey; + arg = &keyPwd; + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + arg = NULL; + break; + default: + return SECFailure; + + } + + /* start the PKCS7 encoder */ + p7ecx = SEC_PKCS7EncoderStart(cinfo, + sec_pkcs12_wrap_asn1_update_for_p7_update, + p12ecx->aSafeEcx, (PK11SymKey *)arg); + if(!p7ecx) { + goto loser; + } + + /* encode safe contents */ + ecx = SEC_ASN1EncoderStart(safeInfo->safe, sec_PKCS12SafeContentsTemplate, + sec_pkcs12_wrap_pkcs7_encoder_update, p7ecx); + if(!ecx) { + goto loser; + } + rv = SEC_ASN1EncoderUpdate(ecx, NULL, 0); + SEC_ASN1EncoderFinish(ecx); + ecx = NULL; + if(rv != SECSuccess) { + goto loser; + } + + + /* finish up safe content info */ + rv = SEC_PKCS7EncoderFinish(p7ecx, p12ecx->p12exp->pwfn, + p12ecx->p12exp->pwfnarg); + } + + return SECSuccess; + +loser: + if(p7ecx) { + SEC_PKCS7EncoderFinish(p7ecx, p12ecx->p12exp->pwfn, + p12ecx->p12exp->pwfnarg); + } + + if(ecx) { + SEC_ASN1EncoderFinish(ecx); + } + + return SECFailure; +} + +/* finish the HMAC and encode the macData so that it can be + * encoded. + */ +static SECStatus +sec_pkcs12_update_mac(sec_PKCS12EncoderContext *p12ecx) +{ + SECItem hmac = { siBuffer, NULL, 0 }; + SECStatus rv; + SGNDigestInfo *di = NULL; + void *dummy; + + if(!p12ecx) { + return SECFailure; + } + + /* make sure we are using password integrity mode */ + if(!p12ecx->p12exp->integrityEnabled) { + return SECSuccess; + } + + if(!p12ecx->p12exp->pwdIntegrity) { + return SECSuccess; + } + + /* finish the hmac */ + hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH); + if(!hmac.data) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + rv = HMAC_Finish((HMACContext*)p12ecx->hmacCx, + hmac.data, &hmac.len, SHA1_LENGTH); + + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* create the digest info */ + di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm, + hmac.data, hmac.len); + if(!di) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + + rv = SGN_CopyDigestInfo(p12ecx->arena, &p12ecx->mac.safeMac, di); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* encode the mac data */ + dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData, + &p12ecx->mac, sec_PKCS12MacDataTemplate); + if(!dummy) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + } + +loser: + if(di) { + SGN_DestroyDigestInfo(di); + } + if(hmac.data) { + SECITEM_ZfreeItem(&hmac, PR_FALSE); + } + HMAC_Destroy((HMACContext*)p12ecx->hmacCx); + p12ecx->hmacCx = NULL; + + return rv; +} + +/* wraps the ASN1 encoder update for PKCS 7 encoder */ +static void +sec_pkcs12_wrap_asn1_encoder_update(void *arg, const char *buf, + unsigned long len) +{ + SEC_ASN1EncoderContext *cx; + + cx = (SEC_ASN1EncoderContext*)arg; + SEC_ASN1EncoderUpdate(cx, buf, len); +} + +/* pfx notify function for ASN1 encoder. we want to stop encoding, once we reach + * the authenticated safe. at that point, the encoder will be updated via streaming + * as the authenticated safe is encoded. + */ +static void +sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_depth) +{ + sec_PKCS12EncoderContext *p12ecx; + + if(!before) { + return; + } + + /* look for authenticated safe */ + p12ecx = (sec_PKCS12EncoderContext*)arg; + if(dest != &p12ecx->pfx.encodedAuthSafe) { + return; + } + + SEC_ASN1EncoderSetTakeFromBuf(p12ecx->ecx); + SEC_ASN1EncoderSetStreaming(p12ecx->ecx); + SEC_ASN1EncoderClearNotifyProc(p12ecx->ecx); +} + +/* SEC_PKCS12Encode + * Encodes the PFX item and returns it to the output function, via + * callback. the output function must be capable of multiple updates. + * + * p12exp - the export context + * output - the output function callback, will be called more than once, + * must be able to accept streaming data. + * outputarg - argument for the output callback. + */ +SECStatus +SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, + SEC_PKCS12EncoderOutputCallback output, void *outputarg) +{ + sec_PKCS12EncoderContext *p12enc; + struct sec_pkcs12_encoder_output outInfo; + SECStatus rv; + + if(!p12exp || !output) { + return SECFailure; + } + + /* get the encoder context */ + p12enc = sec_pkcs12_encoder_start_context(p12exp); + if(!p12enc) { + return SECFailure; + } + + outInfo.outputfn = output; + outInfo.outputarg = outputarg; + + /* set up PFX encoder. Set it for streaming */ + p12enc->ecx = SEC_ASN1EncoderStart(&p12enc->pfx, sec_PKCS12PFXItemTemplate, + sec_pkcs12_encoder_out, + &outInfo); + if(!p12enc->ecx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + SEC_ASN1EncoderSetStreaming(p12enc->ecx); + SEC_ASN1EncoderSetNotifyProc(p12enc->ecx, sec_pkcs12_encoder_pfx_notify, p12enc); + rv = SEC_ASN1EncoderUpdate(p12enc->ecx, NULL, 0); + if(rv != SECSuccess) { + rv = SECFailure; + goto loser; + } + + /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */ + p12enc->aSafeP7Ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo, + sec_pkcs12_wrap_asn1_encoder_update, + p12enc->ecx, NULL); + if(!p12enc->aSafeP7Ecx) { + rv = SECFailure; + goto loser; + } + + /* encode asafe */ + if(p12enc->p12exp->integrityEnabled && p12enc->p12exp->pwdIntegrity) { + p12enc->aSafeEcx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe, + sec_PKCS12AuthenticatedSafeTemplate, + sec_pkcs12_asafe_update_hmac_and_encode_bits, + p12enc); + } else { + p12enc->aSafeEcx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe, + sec_PKCS12AuthenticatedSafeTemplate, + sec_pkcs12_wrap_pkcs7_encoder_update, + p12enc->aSafeP7Ecx); + } + if(!p12enc->aSafeEcx) { + rv = SECFailure; + goto loser; + } + SEC_ASN1EncoderSetStreaming(p12enc->aSafeEcx); + SEC_ASN1EncoderSetTakeFromBuf(p12enc->aSafeEcx); + + /* encode each of the safes */ + while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) { + sec_pkcs12_encoder_asafe_process(p12enc); + p12enc->currentSafe++; + } + SEC_ASN1EncoderClearTakeFromBuf(p12enc->aSafeEcx); + SEC_ASN1EncoderClearStreaming(p12enc->aSafeEcx); + SEC_ASN1EncoderUpdate(p12enc->aSafeEcx, NULL, 0); + SEC_ASN1EncoderFinish(p12enc->aSafeEcx); + + /* finish the encoding of the authenticated safes */ + rv = SEC_PKCS7EncoderFinish(p12enc->aSafeP7Ecx, p12exp->pwfn, + p12exp->pwfnarg); + if(rv != SECSuccess) { + goto loser; + } + + SEC_ASN1EncoderClearTakeFromBuf(p12enc->ecx); + SEC_ASN1EncoderClearStreaming(p12enc->ecx); + + /* update the mac, if necessary */ + rv = sec_pkcs12_update_mac(p12enc); + if(rv != SECSuccess) { + goto loser; + } + + /* finish encoding the pfx */ + rv = SEC_ASN1EncoderUpdate(p12enc->ecx, NULL, 0); + + SEC_ASN1EncoderFinish(p12enc->ecx); + +loser: + return rv; +} + +void +SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx) +{ + int i = 0; + + if(!p12ecx) { + return; + } + + if(p12ecx->safeInfos) { + i = 0; + while(p12ecx->safeInfos[i] != NULL) { + if(p12ecx->safeInfos[i]->encryptionKey) { + PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey); + } + if(p12ecx->safeInfos[i]->cinfo) { + SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo); + } + i++; + } + } + + PORT_FreeArena(p12ecx->arena, PR_TRUE); +} + + +/********************************* + * All-in-one routines for exporting certificates + *********************************/ +struct inPlaceEncodeInfo { + PRBool error; + SECItem outItem; +}; + +static void +sec_pkcs12_in_place_encoder_output(void *arg, const char *buf, unsigned long len) +{ + struct inPlaceEncodeInfo *outInfo = (struct inPlaceEncodeInfo*)arg; + + if(!outInfo || !len || outInfo->error) { + return; + } + + if(!outInfo->outItem.data) { + outInfo->outItem.data = (unsigned char*)PORT_ZAlloc(len); + outInfo->outItem.len = 0; + } else { + if(!PORT_Realloc(&(outInfo->outItem.data), (outInfo->outItem.len + len))) { + SECITEM_ZfreeItem(&(outInfo->outItem), PR_FALSE); + outInfo->outItem.data = NULL; + PORT_SetError(SEC_ERROR_NO_MEMORY); + outInfo->error = PR_TRUE; + return; + } + } + + PORT_Memcpy(&(outInfo->outItem.data[outInfo->outItem.len]), buf, len); + outInfo->outItem.len += len; + + return; +} + +/* + * SEC_PKCS12ExportCertifcateAndKeyUsingPassword + * Exports a certificate/key pair using password-based encryption and + * authentication. + * + * pwfn, pwfnarg - password function and argument for the key database + * cert - the certificate to export + * certDb - certificate database + * pwitem - the password to use + * shroudKey - encrypt the key externally, + * keyShroudAlg - encryption algorithm for key + * encryptionAlg - the algorithm with which data is encrypted + * integrityAlg - the algorithm for integrity + */ +SECItem * +SEC_PKCS12ExportCertificateAndKeyUsingPassword( + SECKEYGetPasswordKey pwfn, void *pwfnarg, + CERTCertificate *cert, PK11SlotInfo *slot, + CERTCertDBHandle *certDb, SECItem *pwitem, + PRBool shroudKey, SECOidTag shroudAlg, + PRBool encryptCert, SECOidTag certEncAlg, + SECOidTag integrityAlg, void *wincx) +{ + struct inPlaceEncodeInfo outInfo; + SEC_PKCS12ExportContext *p12ecx = NULL; + SEC_PKCS12SafeInfo *keySafe, *certSafe; + SECItem *returnItem = NULL; + + if(!cert || !pwitem || !slot) { + return NULL; + } + + outInfo.error = PR_FALSE; + outInfo.outItem.data = NULL; + outInfo.outItem.len = 0; + + p12ecx = SEC_PKCS12CreateExportContext(pwfn, pwfnarg, slot, wincx); + if(!p12ecx) { + return NULL; + } + + /* set up cert safe */ + if(encryptCert) { + certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certEncAlg); + } else { + certSafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx); + } + if(!certSafe) { + goto loser; + } + + /* set up key safe */ + if(shroudKey) { + keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx); + } else { + keySafe = certSafe; + } + if(!keySafe) { + goto loser; + } + + /* add integrity mode */ + if(SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, integrityAlg) + != SECSuccess) { + goto loser; + } + + /* add cert and key pair */ + if(SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert, certDb, + keySafe, NULL, shroudKey, pwitem, shroudAlg) + != SECSuccess) { + goto loser; + } + + /* encode the puppy */ + if(SEC_PKCS12Encode(p12ecx, sec_pkcs12_in_place_encoder_output, &outInfo) + != SECSuccess) { + goto loser; + } + if(outInfo.error) { + goto loser; + } + + SEC_PKCS12DestroyExportContext(p12ecx); + + returnItem = SECITEM_DupItem(&outInfo.outItem); + SECITEM_ZfreeItem(&outInfo.outItem, PR_FALSE); + + return returnItem; + +loser: + if(outInfo.outItem.data) { + SECITEM_ZfreeItem(&(outInfo.outItem), PR_TRUE); + } + + if(p12ecx) { + SEC_PKCS12DestroyExportContext(p12ecx); + } + + return NULL; +} + + diff --git a/security/nss/lib/pkcs12/p12exp.c b/security/nss/lib/pkcs12/p12exp.c new file mode 100644 index 000000000..242d98288 --- /dev/null +++ b/security/nss/lib/pkcs12/p12exp.c @@ -0,0 +1,1407 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "plarena.h" +#include "secitem.h" +#include "secoid.h" +#include "seccomon.h" +#include "secport.h" +#include "cert.h" +#include "pkcs12.h" +#include "p12local.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "secerr.h" +#include "p12plcy.h" + +/* release the memory taken up by the list of nicknames */ +static void +sec_pkcs12_destroy_nickname_list(SECItem **nicknames) +{ + int i = 0; + + if(nicknames == NULL) { + return; + } + + while(nicknames[i] != NULL) { + SECITEM_FreeItem(nicknames[i], PR_FALSE); + i++; + } + + PORT_Free(nicknames); +} + +/* release the memory taken up by the list of certificates */ +static void +sec_pkcs12_destroy_certificate_list(CERTCertificate **ref_certs) +{ + int i = 0; + + if(ref_certs == NULL) { + return; + } + + while(ref_certs[i] != NULL) { + CERT_DestroyCertificate(ref_certs[i]); + i++; + } +} + +static void +sec_pkcs12_destroy_cinfos_for_cert_bags(SEC_PKCS12CertAndCRLBag *certBag) +{ + int j = 0; + j = 0; + while(certBag->certAndCRLs[j] != NULL) { + SECOidTag certType = SECOID_FindOIDTag(&certBag->certAndCRLs[j]->BagID); + if(certType == SEC_OID_PKCS12_X509_CERT_CRL_BAG) { + SEC_PKCS12X509CertCRL *x509; + x509 = certBag->certAndCRLs[j]->value.x509; + SEC_PKCS7DestroyContentInfo(&x509->certOrCRL); + } + j++; + } +} + +/* destroy all content infos since they were not allocated in common + * pool + */ +static void +sec_pkcs12_destroy_cert_content_infos(SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + int i, j; + + if((safe != NULL) && (safe->contents != NULL)) { + i = 0; + while(safe->contents[i] != NULL) { + SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType); + if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) { + SEC_PKCS12CertAndCRLBag *certBag; + certBag = safe->contents[i]->safeContent.certAndCRLBag; + sec_pkcs12_destroy_cinfos_for_cert_bags(certBag); + } + i++; + } + } + + if((baggage != NULL) && (baggage->bags != NULL)) { + i = 0; + while(baggage->bags[i] != NULL) { + if(baggage->bags[i]->unencSecrets != NULL) { + j = 0; + while(baggage->bags[i]->unencSecrets[j] != NULL) { + SECOidTag bagType; + bagType = SECOID_FindOIDTag(&baggage->bags[i]->unencSecrets[j]->safeBagType); + if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) { + SEC_PKCS12CertAndCRLBag *certBag; + certBag = baggage->bags[i]->unencSecrets[j]->safeContent.certAndCRLBag; + sec_pkcs12_destroy_cinfos_for_cert_bags(certBag); + } + j++; + } + } + i++; + } + } +} + +/* convert the nickname list from a NULL termincated Char list + * to a NULL terminated SECItem list + */ +static SECItem ** +sec_pkcs12_convert_nickname_list(char **nicknames) +{ + SECItem **nicks; + int i, j; + PRBool error = PR_FALSE; + + if(nicknames == NULL) { + return NULL; + } + + i = j = 0; + while(nicknames[i] != NULL) { + i++; + } + + /* allocate the space and copy the data */ + nicks = (SECItem **)PORT_ZAlloc(sizeof(SECItem *) * (i + 1)); + if(nicks != NULL) { + for(j = 0; ((j < i) && (error == PR_FALSE)); j++) { + nicks[j] = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(nicks[j] != NULL) { + nicks[j]->data = + (unsigned char *)PORT_ZAlloc(PORT_Strlen(nicknames[j])+1); + if(nicks[j]->data != NULL) { + nicks[j]->len = PORT_Strlen(nicknames[j]); + PORT_Memcpy(nicks[j]->data, nicknames[j], nicks[j]->len); + nicks[j]->data[nicks[j]->len] = 0; + } else { + error = PR_TRUE; + } + } else { + error = PR_TRUE; + } + } + } + + if(error == PR_TRUE) { + for(i = 0; i < j; i++) { + SECITEM_FreeItem(nicks[i], PR_TRUE); + } + PORT_Free(nicks); + nicks = NULL; + } + + return nicks; +} + +/* package the certificate add_cert into PKCS12 structures, + * retrieve the certificate chain for the cert and return + * the packaged contents. + * poolp -- common memory pool; + * add_cert -- certificate to package up + * nickname for the certificate + * a return of NULL indicates an error + */ +static SEC_PKCS12CertAndCRL * +sec_pkcs12_get_cert(PRArenaPool *poolp, + CERTCertificate *add_cert, + SECItem *nickname) +{ + SEC_PKCS12CertAndCRL *cert; + SEC_PKCS7ContentInfo *cinfo; + SGNDigestInfo *t_di; + void *mark; + SECStatus rv; + + if((poolp == NULL) || (add_cert == NULL) || (nickname == NULL)) { + return NULL; + } + mark = PORT_ArenaMark(poolp); + + cert = sec_pkcs12_new_cert_crl(poolp, SEC_OID_PKCS12_X509_CERT_CRL_BAG); + if(cert != NULL) { + + /* copy the nickname */ + rv = SECITEM_CopyItem(poolp, &cert->nickname, nickname); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + cert = NULL; + } else { + + /* package the certificate and cert chain into a NULL signer + * PKCS 7 SignedData content Info and prepare it for encoding + * since we cannot use DER_ANY_TEMPLATE + */ + cinfo = SEC_PKCS7CreateCertsOnly(add_cert, PR_TRUE, NULL); + rv = SEC_PKCS7PrepareForEncode(cinfo, NULL, NULL, NULL); + + /* thumbprint the certificate */ + if((cinfo != NULL) && (rv == SECSuccess)) + { + PORT_Memcpy(&cert->value.x509->certOrCRL, cinfo, sizeof(*cinfo)); + t_di = sec_pkcs12_compute_thumbprint(&add_cert->derCert); + if(t_di != NULL) + { + /* test */ + rv = SGN_CopyDigestInfo(poolp, &cert->value.x509->thumbprint, + t_di); + if(rv != SECSuccess) { + cert = NULL; + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + SGN_DestroyDigestInfo(t_di); + } + else + cert = NULL; + } + } + } + + if (cert == NULL) { + PORT_ArenaRelease(poolp, mark); + } else { + PORT_ArenaUnmark(poolp, mark); + } + + return cert; +} + +/* package the private key associated with the certificate and + * return the appropriate PKCS 12 structure + * poolp common memory pool + * nickname key nickname + * cert -- cert to look up + * wincx -- window handle + * an error is indicated by a return of NULL + */ +static SEC_PKCS12PrivateKey * +sec_pkcs12_get_private_key(PRArenaPool *poolp, + SECItem *nickname, + CERTCertificate *cert, + void *wincx) +{ + SECKEYPrivateKeyInfo *pki; + SEC_PKCS12PrivateKey *pk; + SECStatus rv; + void *mark; + + if((poolp == NULL) || (nickname == NULL)) { + return NULL; + } + + mark = PORT_ArenaMark(poolp); + + /* retrieve key from the data base */ + pki = PK11_ExportPrivateKeyInfo(nickname, cert, wincx); + if(pki == NULL) { + PORT_ArenaRelease(poolp, mark); + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + return NULL; + } + + pk = (SEC_PKCS12PrivateKey *)PORT_ArenaZAlloc(poolp, + sizeof(SEC_PKCS12PrivateKey)); + if(pk != NULL) { + rv = sec_pkcs12_init_pvk_data(poolp, &pk->pvkData); + + if(rv == SECSuccess) { + /* copy the key into poolp memory space */ + rv = SECKEY_CopyPrivateKeyInfo(poolp, &pk->pkcs8data, pki); + if(rv == SECSuccess) { + rv = SECITEM_CopyItem(poolp, &pk->pvkData.nickname, nickname); + } + } + + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + pk = NULL; + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + /* destroy private key, zeroing out data */ + SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); + if (pk == NULL) { + PORT_ArenaRelease(poolp, mark); + } else { + PORT_ArenaUnmark(poolp, mark); + } + + return pk; +} + +/* get a shrouded key item associated with a certificate + * return the appropriate PKCS 12 structure + * poolp common memory pool + * nickname key nickname + * cert -- cert to look up + * wincx -- window handle + * an error is indicated by a return of NULL + */ +static SEC_PKCS12ESPVKItem * +sec_pkcs12_get_shrouded_key(PRArenaPool *poolp, + SECItem *nickname, + CERTCertificate *cert, + SECOidTag algorithm, + SECItem *pwitem, + PKCS12UnicodeConvertFunction unicodeFn, + void *wincx) +{ + SECKEYEncryptedPrivateKeyInfo *epki; + SEC_PKCS12ESPVKItem *pk; + void *mark; + SECStatus rv; + PK11SlotInfo *slot = NULL; + PRBool swapUnicodeBytes = PR_FALSE; + +#ifdef IS_LITTLE_ENDIAN + swapUnicodeBytes = PR_TRUE; +#endif + + if((poolp == NULL) || (nickname == NULL)) + return NULL; + + mark = PORT_ArenaMark(poolp); + + /* use internal key slot */ + slot = PK11_GetInternalKeySlot(); + + /* retrieve encrypted prviate key */ + epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, pwitem, + nickname, cert, 1, 0, NULL); + PK11_FreeSlot(slot); + if(epki == NULL) { + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + PORT_ArenaRelease(poolp, mark); + return NULL; + } + + /* create a private key and store the data into the poolp memory space */ + pk = sec_pkcs12_create_espvk(poolp, SEC_OID_PKCS12_PKCS8_KEY_SHROUDING); + if(pk != NULL) { + rv = sec_pkcs12_init_pvk_data(poolp, &pk->espvkData); + rv = SECITEM_CopyItem(poolp, &pk->espvkData.nickname, nickname); + pk->espvkCipherText.pkcs8KeyShroud = + (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(poolp, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if((pk->espvkCipherText.pkcs8KeyShroud != NULL) && (rv == SECSuccess)) { + rv = SECKEY_CopyEncryptedPrivateKeyInfo(poolp, + pk->espvkCipherText.pkcs8KeyShroud, epki); + if(rv == SECSuccess) { + rv = (*unicodeFn)(poolp, &pk->espvkData.uniNickName, nickname, + PR_TRUE, swapUnicodeBytes); + } + } + + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + pk = NULL; + } + } + + SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); + if(pk == NULL) { + PORT_ArenaRelease(poolp, mark); + } else { + PORT_ArenaUnmark(poolp, mark); + } + + return pk; +} + +/* add a thumbprint to a private key associated certs list + * pvk is the area where the list is stored + * thumb is the thumbprint to copy + * a return of SECFailure indicates an error + */ +static SECStatus +sec_pkcs12_add_thumbprint(SEC_PKCS12PVKSupportingData *pvk, + SGNDigestInfo *thumb) +{ + SGNDigestInfo **thumb_list = NULL; + int nthumbs, size; + void *mark, *dummy; + SECStatus rv = SECFailure; + + if((pvk == NULL) || (thumb == NULL)) { + return SECFailure; + } + + mark = PORT_ArenaMark(pvk->poolp); + + thumb_list = pvk->assocCerts; + nthumbs = pvk->nThumbs; + + /* allocate list space needed -- either growing or allocating + * list must be NULL terminated + */ + size = sizeof(SGNDigestInfo *); + dummy = PORT_ArenaGrow(pvk->poolp, thumb_list, (size * (nthumbs + 1)), + (size * (nthumbs + 2))); + thumb_list = dummy; + if(dummy != NULL) { + thumb_list[nthumbs] = (SGNDigestInfo *)PORT_ArenaZAlloc(pvk->poolp, + sizeof(SGNDigestInfo)); + if(thumb_list[nthumbs] != NULL) { + SGN_CopyDigestInfo(pvk->poolp, thumb_list[nthumbs], thumb); + nthumbs += 1; + thumb_list[nthumbs] = 0; + } else { + dummy = NULL; + } + } + + if(dummy == NULL) { + PORT_ArenaRelease(pvk->poolp, mark); + return SECFailure; + } + + pvk->assocCerts = thumb_list; + pvk->nThumbs = nthumbs; + + PORT_ArenaUnmark(pvk->poolp, mark); + return SECSuccess; +} + +/* search the list of shrouded keys in the baggage for the desired + * name. return a pointer to the item. a return of NULL indicates + * that no match was present or that an error occurred. + */ +static SEC_PKCS12ESPVKItem * +sec_pkcs12_get_espvk_by_name(SEC_PKCS12Baggage *luggage, + SECItem *name) +{ + PRBool found = PR_FALSE; + SEC_PKCS12ESPVKItem *espvk = NULL; + int i, j; + SECComparison rv = SECEqual; + SECItem *t_name; + SEC_PKCS12BaggageItem *bag; + + if((luggage == NULL) || (name == NULL)) { + return NULL; + } + + i = 0; + while((found == PR_FALSE) && (i < luggage->luggage_size)) { + j = 0; + bag = luggage->bags[i]; + while((found == PR_FALSE) && (j < bag->nEspvks)) { + espvk = bag->espvks[j]; + if(espvk->poolp == NULL) { + espvk->poolp = luggage->poolp; + } + t_name = SECITEM_DupItem(&espvk->espvkData.nickname); + if(t_name != NULL) { + rv = SECITEM_CompareItem(name, t_name); + if(rv == SECEqual) { + found = PR_TRUE; + } + SECITEM_FreeItem(t_name, PR_TRUE); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + j++; + } + i++; + } + + if(found != PR_TRUE) { + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME); + return NULL; + } + + return espvk; +} + +/* locates a certificate and copies the thumbprint to the + * appropriate private key + */ +static SECStatus +sec_pkcs12_propagate_thumbprints(SECItem **nicknames, + CERTCertificate **ref_certs, + SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage) +{ + SEC_PKCS12CertAndCRL *cert; + SEC_PKCS12PrivateKey *key; + SEC_PKCS12ESPVKItem *espvk; + int i; + PRBool error = PR_FALSE; + SECStatus rv = SECFailure; + + if((nicknames == NULL) || (safe == NULL)) { + return SECFailure; + } + + i = 0; + while((nicknames[i] != NULL) && (error == PR_FALSE)) { + /* process all certs */ + cert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage, + SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID, + nicknames[i], NULL); + if(cert != NULL) { + /* locate key and copy thumbprint */ + key = (SEC_PKCS12PrivateKey *)sec_pkcs12_find_object(safe, baggage, + SEC_OID_PKCS12_KEY_BAG_ID, + nicknames[i], NULL); + if(key != NULL) { + key->pvkData.poolp = key->poolp; + rv = sec_pkcs12_add_thumbprint(&key->pvkData, + &cert->value.x509->thumbprint); + if(rv == SECFailure) + error = PR_TRUE; /* XXX Set error? */ + } + + /* look in the baggage as well...*/ + if((baggage != NULL) && (error == PR_FALSE)) { + espvk = sec_pkcs12_get_espvk_by_name(baggage, nicknames[i]); + if(espvk != NULL) { + espvk->espvkData.poolp = espvk->poolp; + rv = sec_pkcs12_add_thumbprint(&espvk->espvkData, + &cert->value.x509->thumbprint); + if(rv == SECFailure) + error = PR_TRUE; /* XXX Set error? */ + } + } + } + i++; + } + + if(error == PR_TRUE) { + return SECFailure; + } + + return SECSuccess; +} + +/* append a safe bag to the end of the safe contents list */ +SECStatus +sec_pkcs12_append_safe_bag(SEC_PKCS12SafeContents *safe, + SEC_PKCS12SafeBag *bag) +{ + int size; + void *mark = NULL, *dummy = NULL; + + if((bag == NULL) || (safe == NULL)) + return SECFailure; + + mark = PORT_ArenaMark(safe->poolp); + + size = (safe->safe_size * sizeof(SEC_PKCS12SafeBag *)); + + if(safe->safe_size > 0) { + dummy = (SEC_PKCS12SafeBag **)PORT_ArenaGrow(safe->poolp, + safe->contents, + size, + (size + sizeof(SEC_PKCS12SafeBag *))); + safe->contents = dummy; + } else { + safe->contents = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc(safe->poolp, + (2 * sizeof(SEC_PKCS12SafeBag *))); + dummy = safe->contents; + } + + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + safe->contents[safe->safe_size] = bag; + safe->safe_size++; + safe->contents[safe->safe_size] = NULL; + + PORT_ArenaUnmark(safe->poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(safe->poolp, mark); + return SECFailure; +} + +/* append a certificate onto the end of a cert bag */ +static SECStatus +sec_pkcs12_append_cert_to_bag(PRArenaPool *arena, + SEC_PKCS12SafeBag *safebag, + CERTCertificate *cert, + SECItem *nickname) +{ + int size; + void *dummy = NULL, *mark = NULL; + SEC_PKCS12CertAndCRL *p12cert; + SEC_PKCS12CertAndCRLBag *bag; + + if((arena == NULL) || (safebag == NULL) || + (cert == NULL) || (nickname == NULL)) { + return SECFailure; + } + + bag = safebag->safeContent.certAndCRLBag; + if(bag == NULL) { + return SECFailure; + } + + mark = PORT_ArenaMark(arena); + + p12cert = sec_pkcs12_get_cert(arena, cert, nickname); + if(p12cert == NULL) { + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; + } + + size = bag->bag_size * sizeof(SEC_PKCS12CertAndCRL *); + if(bag->bag_size > 0) { + dummy = (SEC_PKCS12CertAndCRL **)PORT_ArenaGrow(bag->poolp, + bag->certAndCRLs, size, size + sizeof(SEC_PKCS12CertAndCRL *)); + bag->certAndCRLs = dummy; + } else { + bag->certAndCRLs = (SEC_PKCS12CertAndCRL **)PORT_ArenaZAlloc(bag->poolp, + (2 * sizeof(SEC_PKCS12CertAndCRL *))); + dummy = bag->certAndCRLs; + } + + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + bag->certAndCRLs[bag->bag_size] = p12cert; + bag->bag_size++; + bag->certAndCRLs[bag->bag_size] = NULL; + + PORT_ArenaUnmark(bag->poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; +} + +/* append a key onto the end of a list of keys in a key bag */ +SECStatus +sec_pkcs12_append_key_to_bag(SEC_PKCS12SafeBag *safebag, + SEC_PKCS12PrivateKey *pk) +{ + void *mark, *dummy; + SEC_PKCS12PrivateKeyBag *bag; + int size; + + if((safebag == NULL) || (pk == NULL)) + return SECFailure; + + bag = safebag->safeContent.keyBag; + if(bag == NULL) { + return SECFailure; + } + + mark = PORT_ArenaMark(bag->poolp); + + size = (bag->bag_size * sizeof(SEC_PKCS12PrivateKey *)); + + if(bag->bag_size > 0) { + dummy = (SEC_PKCS12PrivateKey **)PORT_ArenaGrow(bag->poolp, + bag->privateKeys, + size, + size + sizeof(SEC_PKCS12PrivateKey *)); + bag->privateKeys = dummy; + } else { + bag->privateKeys = (SEC_PKCS12PrivateKey **)PORT_ArenaZAlloc(bag->poolp, + (2 * sizeof(SEC_PKCS12PrivateKey *))); + dummy = bag->privateKeys; + } + + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + bag->privateKeys[bag->bag_size] = pk; + bag->bag_size++; + bag->privateKeys[bag->bag_size] = NULL; + + PORT_ArenaUnmark(bag->poolp, mark); + return SECSuccess; + +loser: + /* XXX Free memory? */ + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; +} + +/* append a safe bag to the baggage area */ +static SECStatus +sec_pkcs12_append_unshrouded_bag(SEC_PKCS12BaggageItem *bag, + SEC_PKCS12SafeBag *u_bag) +{ + int size; + void *mark = NULL, *dummy = NULL; + + if((bag == NULL) || (u_bag == NULL)) + return SECFailure; + + mark = PORT_ArenaMark(bag->poolp); + + /* dump things into the first bag */ + size = (bag->nSecrets + 1) * sizeof(SEC_PKCS12SafeBag *); + dummy = PORT_ArenaGrow(bag->poolp, + bag->unencSecrets, size, + size + sizeof(SEC_PKCS12SafeBag *)); + bag->unencSecrets = dummy; + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + bag->unencSecrets[bag->nSecrets] = u_bag; + bag->nSecrets++; + bag->unencSecrets[bag->nSecrets] = NULL; + + PORT_ArenaUnmark(bag->poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; +} + +/* gather up all certificates and keys and package them up + * in the safe, baggage, or both. + * nicknames is the list of nicknames and corresponding certs in ref_certs + * ref_certs a null terminated list of certificates + * rSafe, rBaggage -- return areas for safe and baggage + * shroud_keys -- store keys externally + * pwitem -- password for computing integrity mac and encrypting contents + * wincx -- window handle + * + * if a failure occurs, an error is set and SECFailure returned. + */ +static SECStatus +sec_pkcs12_package_certs_and_keys(SECItem **nicknames, + CERTCertificate **ref_certs, + PRBool unencryptedCerts, + SEC_PKCS12SafeContents **rSafe, + SEC_PKCS12Baggage **rBaggage, + PRBool shroud_keys, + SECOidTag shroud_alg, + SECItem *pwitem, + PKCS12UnicodeConvertFunction unicodeFn, + void *wincx) +{ + PRArenaPool *permArena; + SEC_PKCS12SafeContents *safe = NULL; + SEC_PKCS12Baggage *baggage = NULL; + + SECStatus rv = SECFailure; + PRBool problem = PR_FALSE; + + SEC_PKCS12ESPVKItem *espvk = NULL; + SEC_PKCS12PrivateKey *pk = NULL; + CERTCertificate *add_cert = NULL; + SEC_PKCS12SafeBag *certbag = NULL, *keybag = NULL; + SEC_PKCS12BaggageItem *external_bag = NULL; + int ncerts = 0, nkeys = 0; + int i; + + if((nicknames == NULL) || (rSafe == NULL) || (rBaggage == NULL)) { + return SECFailure; + } + + *rBaggage = baggage; + *rSafe = safe; + + permArena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(permArena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + /* allocate structures */ + safe = sec_pkcs12_create_safe_contents(permArena); + if(safe == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + + certbag = sec_pkcs12_create_safe_bag(permArena, + SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID); + if(certbag == NULL) { + rv = SECFailure; + goto loser; + } + + if(shroud_keys != PR_TRUE) { + keybag = sec_pkcs12_create_safe_bag(permArena, + SEC_OID_PKCS12_KEY_BAG_ID); + if(keybag == NULL) { + rv = SECFailure; + goto loser; + } + } + + if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) { + baggage = sec_pkcs12_create_baggage(permArena); + if(baggage == NULL) { + rv = SECFailure; + goto loser; + } + external_bag = sec_pkcs12_create_external_bag(baggage); + } + + /* package keys and certs */ + i = 0; + while((nicknames[i] != NULL) && (problem == PR_FALSE)) { + if(ref_certs[i] != NULL) { + /* append cert to bag o certs */ + rv = sec_pkcs12_append_cert_to_bag(permArena, certbag, + ref_certs[i], + nicknames[i]); + if(rv == SECFailure) { + problem = PR_FALSE; + } else { + ncerts++; + } + + if(rv == SECSuccess) { + /* package up them keys */ + if(shroud_keys == PR_TRUE) { + espvk = sec_pkcs12_get_shrouded_key(permArena, + nicknames[i], + ref_certs[i], + shroud_alg, + pwitem, unicodeFn, + wincx); + if(espvk != NULL) { + rv = sec_pkcs12_append_shrouded_key(external_bag, espvk); + SECITEM_CopyItem(permArena, &espvk->derCert, + &ref_certs[i]->derCert); + } else { + rv = SECFailure; + } + } else { + pk = sec_pkcs12_get_private_key(permArena, nicknames[i], + ref_certs[i], wincx); + if(pk != NULL) { + rv = sec_pkcs12_append_key_to_bag(keybag, pk); + SECITEM_CopyItem(permArena, &espvk->derCert, + &ref_certs[i]->derCert); + } else { + rv = SECFailure; + } + } + + if(rv == SECFailure) { + problem = PR_TRUE; + } else { + nkeys++; + } + } + } else { + /* handle only keys here ? */ + problem = PR_TRUE; + } + i++; + } + + /* let success fall through */ +loser: + if(problem == PR_FALSE) { + /* if we have certs, we want to append the cert bag to the + * appropriate area + */ + if(ncerts > 0) { + if(unencryptedCerts != PR_TRUE) { + rv = sec_pkcs12_append_safe_bag(safe, certbag); + } else { + rv = sec_pkcs12_append_unshrouded_bag(external_bag, certbag); + } + } else { + rv = SECSuccess; + } + + /* append key bag, if they are stored in safe contents */ + if((rv == SECSuccess) && (shroud_keys == PR_FALSE) && (nkeys > 0)) { + rv = sec_pkcs12_append_safe_bag(safe, keybag); + } + } else { + rv = SECFailure; + } + + /* if baggage not used, NULLify it */ + if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) { + if(((unencryptedCerts == PR_TRUE) && (ncerts == 0)) && + ((shroud_keys == PR_TRUE) && (nkeys == 0))) + baggage = NULL; + } else { + baggage = NULL; + } + + if((problem == PR_TRUE) || (rv == SECFailure)) { + PORT_FreeArena(permArena, PR_TRUE); + rv = SECFailure; + baggage = NULL; + safe = NULL; + } + + *rBaggage = baggage; + *rSafe = safe; + + return rv; +} + +/* DER encode the safe contents and return a SECItem. if an error + * occurs, NULL is returned. + */ +static SECItem * +sec_pkcs12_encode_safe_contents(SEC_PKCS12SafeContents *safe) +{ + SECItem *dsafe = NULL, *tsafe; + void *dummy = NULL; + PRArenaPool *arena; + + if(safe == NULL) { + return NULL; + } + +/* rv = sec_pkcs12_prepare_for_der_code_safe(safe, PR_TRUE); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + }*/ + + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + tsafe = (SECItem *)PORT_ArenaZAlloc(arena, sizeof(SECItem)); + if(tsafe != NULL) { + dummy = SEC_ASN1EncodeItem(arena, tsafe, safe, + SEC_PKCS12SafeContentsTemplate); + if(dummy != NULL) { + dsafe = SECITEM_DupItem(tsafe); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena(arena, PR_TRUE); + + return dsafe; +} + +/* prepare the authenicated safe for encoding and encode it. + * baggage is copied to the appropriate area, safe is encoded and + * encrypted. the version and transport mode are set on the asafe. + * the whole ball of wax is then der encoded and packaged up into + * data content info + * safe -- container of certs and keys, is encrypted. + * baggage -- container of certs and keys, keys assumed to be encrypted by + * another method, certs are in the clear + * algorithm -- algorithm by which to encrypt safe + * pwitem -- password for encryption + * wincx - window handle + * + * return of NULL is an error condition. + */ +static SEC_PKCS7ContentInfo * +sec_pkcs12_get_auth_safe(SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage, + SECOidTag algorithm, + SECItem *pwitem, + PKCS12UnicodeConvertFunction unicodeFn, + void *wincx) +{ + SECItem *src = NULL, *dest = NULL, *psalt = NULL; + PRArenaPool *poolp; + SEC_PKCS12AuthenticatedSafe *asafe; + SEC_PKCS7ContentInfo *safe_cinfo = NULL; + SEC_PKCS7ContentInfo *asafe_cinfo = NULL; + void *dummy; + SECStatus rv = SECSuccess; + PRBool swapUnicodeBytes = PR_FALSE; + +#ifdef IS_LITTLE_ENDIAN + swapUnicodeBytes = PR_TRUE; +#endif + + if(((safe != NULL) && (pwitem == NULL)) && (baggage == NULL)) + return NULL; + + poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(poolp == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* prepare authenticated safe for encode */ + asafe = sec_pkcs12_new_asafe(poolp); + if(asafe != NULL) { + + /* set version */ + dummy = SEC_ASN1EncodeInteger(asafe->poolp, &asafe->version, + SEC_PKCS12_PFX_VERSION); + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + + /* generate the privacy salt used to create virtual pwd */ + psalt = sec_pkcs12_generate_salt(); + if(psalt != NULL) { + rv = SECITEM_CopyItem(asafe->poolp, &asafe->privacySalt, + psalt); + if(rv == SECSuccess) { + asafe->privacySalt.len *= 8; + } + else { + SECITEM_ZfreeItem(psalt, PR_TRUE); + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + } + + if((psalt == NULL) || (rv == SECFailure)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + + /* package up safe contents */ + if(safe != NULL) + { + safe_cinfo = SEC_PKCS7CreateEncryptedData(algorithm, NULL, wincx); + if((safe_cinfo != NULL) && (safe->safe_size > 0)) { + /* encode the safe and encrypt the contents of the + * content info + */ + src = sec_pkcs12_encode_safe_contents(safe); + + if(src != NULL) { + rv = SEC_PKCS7SetContent(safe_cinfo, (char *)src->data, src->len); + SECITEM_ZfreeItem(src, PR_TRUE); + if(rv == SECSuccess) { + SECItem *vpwd; + vpwd = sec_pkcs12_create_virtual_password(pwitem, psalt, + unicodeFn, swapUnicodeBytes); + if(vpwd != NULL) { + rv = SEC_PKCS7EncryptContents(NULL, safe_cinfo, + vpwd, wincx); + SECITEM_ZfreeItem(vpwd, PR_TRUE); + } else { + rv = SECFailure; + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + rv = SECFailure; + } + } else if(safe->safe_size > 0) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } else { + /* case where there is NULL content in the safe contents */ + rv = SEC_PKCS7SetContent(safe_cinfo, NULL, 0); + if(rv != SECFailure) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } + + if(rv != SECSuccess) { + SEC_PKCS7DestroyContentInfo(safe_cinfo); + safe_cinfo = NULL; + goto loser; + } + + asafe->safe = safe_cinfo; + /* + PORT_Memcpy(&asafe->safe, safe_cinfo, sizeof(*safe_cinfo)); + */ + } + + /* copy the baggage to the authenticated safe baggage if present */ + if(baggage != NULL) { + PORT_Memcpy(&asafe->baggage, baggage, sizeof(*baggage)); + } + + /* encode authenticated safe and store it in a Data content info */ + dest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem)); + if(dest != NULL) { + dummy = SEC_ASN1EncodeItem(poolp, dest, asafe, + SEC_PKCS12AuthenticatedSafeTemplate); + if(dummy != NULL) { + asafe_cinfo = SEC_PKCS7CreateData(); + if(asafe_cinfo != NULL) { + rv = SEC_PKCS7SetContent(asafe_cinfo, + (char *)dest->data, + dest->len); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + SEC_PKCS7DestroyContentInfo(asafe_cinfo); + asafe_cinfo = NULL; + } + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + } + } + } + +loser: + PORT_FreeArena(poolp, PR_TRUE); + if(safe_cinfo != NULL) { + SEC_PKCS7DestroyContentInfo(safe_cinfo); + } + if(psalt != NULL) { + SECITEM_ZfreeItem(psalt, PR_TRUE); + } + + if(rv == SECFailure) { + return NULL; + } + + return asafe_cinfo; +} + +/* generates the PFX and computes the mac on the authenticated safe + * NULL implies an error + */ +static SEC_PKCS12PFXItem * +sec_pkcs12_get_pfx(SEC_PKCS7ContentInfo *cinfo, + PRBool do_mac, + SECItem *pwitem, PKCS12UnicodeConvertFunction unicodeFn) +{ + SECItem *dest = NULL, *mac = NULL, *salt = NULL, *key = NULL; + SEC_PKCS12PFXItem *pfx; + SECStatus rv = SECFailure; + SGNDigestInfo *di; + SECItem *vpwd; + PRBool swapUnicodeBytes = PR_FALSE; + +#ifdef IS_LITTLE_ENDIAN + swapUnicodeBytes = PR_TRUE; +#endif + + if((cinfo == NULL) || ((do_mac == PR_TRUE) && (pwitem == NULL))) { + return NULL; + } + + /* allocate new pfx structure */ + pfx = sec_pkcs12_new_pfx(); + if(pfx == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + PORT_Memcpy(&pfx->authSafe, cinfo, sizeof(*cinfo)); + if(do_mac == PR_TRUE) { + + /* salt for computing mac */ + salt = sec_pkcs12_generate_salt(); + if(salt != NULL) { + rv = SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, salt); + pfx->macData.macSalt.len *= 8; + + vpwd = sec_pkcs12_create_virtual_password(pwitem, salt, + unicodeFn, swapUnicodeBytes); + if(vpwd == NULL) { + rv = SECFailure; + key = NULL; + } else { + key = sec_pkcs12_generate_key_from_password(SEC_OID_SHA1, + salt, vpwd); + SECITEM_ZfreeItem(vpwd, PR_TRUE); + } + + if((key != NULL) && (rv == SECSuccess)) { + dest = SEC_PKCS7GetContent(cinfo); + if(dest != NULL) { + + /* compute mac on data -- for password integrity mode */ + mac = sec_pkcs12_generate_mac(key, dest, PR_FALSE); + if(mac != NULL) { + di = SGN_CreateDigestInfo(SEC_OID_SHA1, + mac->data, mac->len); + if(di != NULL) { + rv = SGN_CopyDigestInfo(pfx->poolp, + &pfx->macData.safeMac, di); + SGN_DestroyDigestInfo(di); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + SECITEM_ZfreeItem(mac, PR_TRUE); + } + } else { + rv = SECFailure; + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + } + + if(key != NULL) { + SECITEM_ZfreeItem(key, PR_TRUE); + } + SECITEM_ZfreeItem(salt, PR_TRUE); + } + } + + if(rv == SECFailure) { + SEC_PKCS12DestroyPFX(pfx); + pfx = NULL; + } + + return pfx; +} + +/* der encode the pfx */ +static SECItem * +sec_pkcs12_encode_pfx(SEC_PKCS12PFXItem *pfx) +{ + SECItem *dest; + void *dummy; + + if(pfx == NULL) { + return NULL; + } + + dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(dest == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + dummy = SEC_ASN1EncodeItem(NULL, dest, pfx, SEC_PKCS12PFXItemTemplate); + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + SECITEM_ZfreeItem(dest, PR_TRUE); + dest = NULL; + } + + return dest; +} + +SECItem * +SEC_PKCS12GetPFX(char **nicknames, + CERTCertificate **ref_certs, + PRBool shroud_keys, + SEC_PKCS5GetPBEPassword pbef, + void *pbearg, + PKCS12UnicodeConvertFunction unicodeFn, + void *wincx) +{ + SECItem **nicks = NULL; + SEC_PKCS12PFXItem *pfx = NULL; + SEC_PKCS12Baggage *baggage = NULL; + SEC_PKCS12SafeContents *safe = NULL; + SEC_PKCS7ContentInfo *cinfo = NULL; + SECStatus rv = SECFailure; + SECItem *dest = NULL, *pwitem = NULL; + PRBool problem = PR_FALSE; + PRBool unencryptedCerts; + SECOidTag shroud_alg, safe_alg; + + /* how should we encrypt certs ? */ + unencryptedCerts = !SEC_PKCS12IsEncryptionAllowed(); + if(!unencryptedCerts) { + safe_alg = SEC_PKCS12GetPreferredEncryptionAlgorithm(); + if(safe_alg == SEC_OID_UNKNOWN) { + safe_alg = SEC_PKCS12GetStrongestAllowedAlgorithm(); + } + if(safe_alg == SEC_OID_UNKNOWN) { + unencryptedCerts = PR_TRUE; + /* for export where no encryption is allowed, we still need + * to encrypt the NULL contents per the spec. encrypted info + * is known plaintext, so it shouldn't be a problem. + */ + safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; + } + } else { + /* for export where no encryption is allowed, we still need + * to encrypt the NULL contents per the spec. encrypted info + * is known plaintext, so it shouldn't be a problem. + */ + safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; + } + + /* keys are always stored with triple DES */ + shroud_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC; + + /* check for FIPS, if so, do not encrypt certs */ + if(PK11_IsFIPS() && !unencryptedCerts) { + unencryptedCerts = PR_TRUE; + } + + if((nicknames == NULL) || (pbef == NULL) || (ref_certs == NULL)) { + problem = PR_TRUE; + goto loser; + } + + + /* get password */ + pwitem = (*pbef)(pbearg); + if(pwitem == NULL) { + problem = PR_TRUE; + goto loser; + } + nicks = sec_pkcs12_convert_nickname_list(nicknames); + + /* get safe and baggage */ + rv = sec_pkcs12_package_certs_and_keys(nicks, ref_certs, unencryptedCerts, + &safe, &baggage, shroud_keys, + shroud_alg, pwitem, unicodeFn, wincx); + if(rv == SECFailure) { + problem = PR_TRUE; + } + + if((safe != NULL) && (problem == PR_FALSE)) { + /* copy thumbprints */ + rv = sec_pkcs12_propagate_thumbprints(nicks, ref_certs, safe, baggage); + + /* package everything up into AuthenticatedSafe */ + cinfo = sec_pkcs12_get_auth_safe(safe, baggage, + safe_alg, pwitem, unicodeFn, wincx); + + sec_pkcs12_destroy_cert_content_infos(safe, baggage); + + /* get the pfx and mac it */ + if(cinfo != NULL) { + pfx = sec_pkcs12_get_pfx(cinfo, PR_TRUE, pwitem, unicodeFn); + if(pfx != NULL) { + dest = sec_pkcs12_encode_pfx(pfx); + SEC_PKCS12DestroyPFX(pfx); + } + SEC_PKCS7DestroyContentInfo(cinfo); + } + + if(safe != NULL) { + PORT_FreeArena(safe->poolp, PR_TRUE); + } + } else { + if(safe != NULL) { + PORT_FreeArena(safe->poolp, PR_TRUE); + } + } + +loser: + if(nicks != NULL) { + sec_pkcs12_destroy_nickname_list(nicks); + } + + if(ref_certs != NULL) { + sec_pkcs12_destroy_certificate_list(ref_certs); + } + + if(pwitem != NULL) { + SECITEM_ZfreeItem(pwitem, PR_TRUE); + } + + if(problem == PR_TRUE) { + dest = NULL; + } + + return dest; +} diff --git a/security/nss/lib/pkcs12/p12local.c b/security/nss/lib/pkcs12/p12local.c new file mode 100644 index 000000000..d6e02ad02 --- /dev/null +++ b/security/nss/lib/pkcs12/p12local.c @@ -0,0 +1,1325 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "pkcs12.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "seccomon.h" +#include "secoid.h" +#include "sechash.h" +#include "secitem.h" +#include "secerr.h" +#include "pk11func.h" +#include "p12local.h" +#include "alghmac.h" +#include "p12.h" + +#define SALT_LENGTH 16 + +/* helper functions */ +/* returns proper bag type template based upon object type tag */ +const SEC_ASN1Template * +sec_pkcs12_choose_bag_type_old(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12SafeBag *safebag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + safebag = (SEC_PKCS12SafeBag*)src_or_dest; + + oiddata = safebag->safeBagTypeTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&safebag->safeBagType); + safebag->safeBagTypeTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_PointerToAnyTemplate; + break; + case SEC_OID_PKCS12_KEY_BAG_ID: + theTemplate = SEC_PointerToPKCS12KeyBagTemplate; + break; + case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID: + theTemplate = SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD; + break; + case SEC_OID_PKCS12_SECRET_BAG_ID: + theTemplate = SEC_PointerToPKCS12SecretBagTemplate; + break; + } + return theTemplate; +} + +const SEC_ASN1Template * +sec_pkcs12_choose_bag_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12SafeBag *safebag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + safebag = (SEC_PKCS12SafeBag*)src_or_dest; + + oiddata = safebag->safeBagTypeTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&safebag->safeBagType); + safebag->safeBagTypeTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_AnyTemplate; + break; + case SEC_OID_PKCS12_KEY_BAG_ID: + theTemplate = SEC_PKCS12PrivateKeyBagTemplate; + break; + case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID: + theTemplate = SEC_PKCS12CertAndCRLBagTemplate; + break; + case SEC_OID_PKCS12_SECRET_BAG_ID: + theTemplate = SEC_PKCS12SecretBagTemplate; + break; + } + return theTemplate; +} + +/* returns proper cert crl template based upon type tag */ +const SEC_ASN1Template * +sec_pkcs12_choose_cert_crl_type_old(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12CertAndCRL *certbag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + certbag = (SEC_PKCS12CertAndCRL*)src_or_dest; + oiddata = certbag->BagTypeTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&certbag->BagID); + certbag->BagTypeTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_PointerToAnyTemplate; + break; + case SEC_OID_PKCS12_X509_CERT_CRL_BAG: + theTemplate = SEC_PointerToPKCS12X509CertCRLTemplate_OLD; + break; + case SEC_OID_PKCS12_SDSI_CERT_BAG: + theTemplate = SEC_PointerToPKCS12SDSICertTemplate; + break; + } + return theTemplate; +} + +const SEC_ASN1Template * +sec_pkcs12_choose_cert_crl_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12CertAndCRL *certbag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + certbag = (SEC_PKCS12CertAndCRL*)src_or_dest; + oiddata = certbag->BagTypeTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&certbag->BagID); + certbag->BagTypeTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_PointerToAnyTemplate; + break; + case SEC_OID_PKCS12_X509_CERT_CRL_BAG: + theTemplate = SEC_PointerToPKCS12X509CertCRLTemplate; + break; + case SEC_OID_PKCS12_SDSI_CERT_BAG: + theTemplate = SEC_PointerToPKCS12SDSICertTemplate; + break; + } + return theTemplate; +} + +/* returns appropriate shroud template based on object type tag */ +const SEC_ASN1Template * +sec_pkcs12_choose_shroud_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS12ESPVKItem *espvk; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + espvk = (SEC_PKCS12ESPVKItem*)src_or_dest; + oiddata = espvk->espvkTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&espvk->espvkOID); + espvk->espvkTag = oiddata; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_PointerToAnyTemplate; + break; + case SEC_OID_PKCS12_PKCS8_KEY_SHROUDING: + theTemplate = + SECKEY_PointerToEncryptedPrivateKeyInfoTemplate; + break; + } + return theTemplate; +} + +/* generate SALT placing it into the character array passed in. + * it is assumed that salt_dest is an array of appropriate size + * XXX We might want to generate our own random context + */ +SECItem * +sec_pkcs12_generate_salt(void) +{ + SECItem *salt; + + salt = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(salt == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + salt->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * + SALT_LENGTH); + salt->len = SALT_LENGTH; + if(salt->data == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + SECITEM_ZfreeItem(salt, PR_TRUE); + return NULL; + } + + PK11_GenerateRandom(salt->data, salt->len); + + return salt; +} + +/* generate KEYS -- as per PKCS12 section 7. + * only used for MAC + */ +SECItem * +sec_pkcs12_generate_key_from_password(SECOidTag algorithm, + SECItem *salt, + SECItem *password) +{ + unsigned char *pre_hash=NULL; + unsigned char *hash_dest=NULL; + SECStatus res; + PRArenaPool *poolp; + SECItem *key = NULL; + int key_len = 0; + + if((salt == NULL) || (password == NULL)) { + return NULL; + } + + poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if(poolp == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + pre_hash = (unsigned char *)PORT_ArenaZAlloc(poolp, sizeof(char) * + (salt->len+password->len)); + if(pre_hash == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + hash_dest = (unsigned char *)PORT_ArenaZAlloc(poolp, + sizeof(unsigned char) * SHA1_LENGTH); + if(hash_dest == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_Memcpy(pre_hash, salt->data, salt->len); + /* handle password of 0 length case */ + if(password->len > 0) { + PORT_Memcpy(&(pre_hash[salt->len]), password->data, password->len); + } + + res = PK11_HashBuf(SEC_OID_SHA1, hash_dest, pre_hash, + (salt->len+password->len)); + if(res == SECFailure) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + switch(algorithm) { + case SEC_OID_SHA1: + if(key_len == 0) + key_len = 16; + key = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(key == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + key->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) + * key_len); + if(key->data == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + key->len = key_len; + PORT_Memcpy(key->data, &hash_dest[SHA1_LENGTH-key->len], key->len); + break; + default: + goto loser; + break; + } + + PORT_FreeArena(poolp, PR_TRUE); + return key; + +loser: + PORT_FreeArena(poolp, PR_TRUE); + if(key != NULL) { + SECITEM_ZfreeItem(key, PR_TRUE); + } + return NULL; +} + +/* MAC is generated per PKCS 12 section 6. It is expected that key, msg + * and mac_dest are pre allocated, non-NULL arrays. msg_len is passed in + * because it is not known how long the message actually is. String + * manipulation routines will not necessarily work because msg may have + * imbedded NULLs + */ +static SECItem * +sec_pkcs12_generate_old_mac(SECItem *key, + SECItem *msg) +{ + SECStatus res; + PRArenaPool *temparena = NULL; + unsigned char *hash_dest=NULL, *hash_src1=NULL, *hash_src2 = NULL; + int i; + SECItem *mac = NULL; + + if((key == NULL) || (msg == NULL)) + goto loser; + + /* allocate return item */ + mac = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(mac == NULL) + return NULL; + mac->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) + * SHA1_LENGTH); + mac->len = SHA1_LENGTH; + if(mac->data == NULL) + goto loser; + + /* allocate temporary items */ + temparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if(temparena == NULL) + goto loser; + + hash_src1 = (unsigned char *)PORT_ArenaZAlloc(temparena, + sizeof(unsigned char) * (16+msg->len)); + if(hash_src1 == NULL) + goto loser; + + hash_src2 = (unsigned char *)PORT_ArenaZAlloc(temparena, + sizeof(unsigned char) * (SHA1_LENGTH+16)); + if(hash_src2 == NULL) + goto loser; + + hash_dest = (unsigned char *)PORT_ArenaZAlloc(temparena, + sizeof(unsigned char) * SHA1_LENGTH); + if(hash_dest == NULL) + goto loser; + + /* perform mac'ing as per PKCS 12 */ + + /* first round of hashing */ + for(i = 0; i < 16; i++) + hash_src1[i] = key->data[i] ^ 0x36; + PORT_Memcpy(&(hash_src1[16]), msg->data, msg->len); + res = PK11_HashBuf(SEC_OID_SHA1, hash_dest, hash_src1, (16+msg->len)); + if(res == SECFailure) + goto loser; + + /* second round of hashing */ + for(i = 0; i < 16; i++) + hash_src2[i] = key->data[i] ^ 0x5c; + PORT_Memcpy(&(hash_src2[16]), hash_dest, SHA1_LENGTH); + res = PK11_HashBuf(SEC_OID_SHA1, mac->data, hash_src2, SHA1_LENGTH+16); + if(res == SECFailure) + goto loser; + + PORT_FreeArena(temparena, PR_TRUE); + return mac; + +loser: + if(temparena != NULL) + PORT_FreeArena(temparena, PR_TRUE); + if(mac != NULL) + SECITEM_ZfreeItem(mac, PR_TRUE); + return NULL; +} + +/* MAC is generated per PKCS 12 section 6. It is expected that key, msg + * and mac_dest are pre allocated, non-NULL arrays. msg_len is passed in + * because it is not known how long the message actually is. String + * manipulation routines will not necessarily work because msg may have + * imbedded NULLs + */ +SECItem * +sec_pkcs12_generate_mac(SECItem *key, + SECItem *msg, + PRBool old_method) +{ + SECStatus res = SECFailure; + SECItem *mac = NULL; + HMACContext *cx; + + if((key == NULL) || (msg == NULL)) { + return NULL; + } + + if(old_method == PR_TRUE) { + return sec_pkcs12_generate_old_mac(key, msg); + } + + /* allocate return item */ + mac = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(mac == NULL) { + return NULL; + } + mac->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) + * SHA1_LENGTH); + mac->len = SHA1_LENGTH; + if(mac->data == NULL) { + PORT_Free(mac); + return NULL; + } + + /* compute MAC using HMAC */ + cx = HMAC_Create(SEC_OID_SHA1, key->data, key->len); + if(cx != NULL) { + HMAC_Begin(cx); + HMAC_Update(cx, msg->data, msg->len); + res = HMAC_Finish(cx, mac->data, &mac->len, SHA1_LENGTH); + HMAC_Destroy(cx); + } + + + if(res != SECSuccess) { + SECITEM_ZfreeItem(mac, PR_TRUE); + mac = NULL; + } + + return mac; +} + +/* compute the thumbprint of the DER cert and create a digest info + * to store it in and return the digest info. + * a return of NULL indicates an error. + */ +SGNDigestInfo * +sec_pkcs12_compute_thumbprint(SECItem *der_cert) +{ + SGNDigestInfo *thumb = NULL; + SECItem digest; + PRArenaPool *temparena = NULL; + SECStatus rv = SECFailure; + + if(der_cert == NULL) + return NULL; + + temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if(temparena == NULL) { + return NULL; + } + + digest.data = (unsigned char *)PORT_ArenaZAlloc(temparena, + sizeof(unsigned char) * + SHA1_LENGTH); + /* digest data and create digest info */ + if(digest.data != NULL) { + digest.len = SHA1_LENGTH; + rv = PK11_HashBuf(SEC_OID_SHA1, digest.data, der_cert->data, + der_cert->len); + if(rv == SECSuccess) { + thumb = SGN_CreateDigestInfo(SEC_OID_SHA1, + digest.data, + digest.len); + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + PORT_FreeArena(temparena, PR_TRUE); + + return thumb; +} + +/* create a virtual password per PKCS 12, the password is converted + * to unicode, the salt is prepended to it, and then the whole thing + * is returned */ +SECItem * +sec_pkcs12_create_virtual_password(SECItem *password, SECItem *salt, + PRBool swap) +{ + SECItem uniPwd = {siBuffer, NULL,0}, *retPwd = NULL; + + if((password == NULL) || (salt == NULL)) { + return NULL; + } + + if(password->len == 0) { + uniPwd.data = (unsigned char*)PORT_ZAlloc(2); + uniPwd.len = 2; + if(!uniPwd.data) { + return NULL; + } + } else { + uniPwd.data = (unsigned char*)PORT_ZAlloc(password->len * 3); + uniPwd.len = password->len * 3; + if(!PORT_UCS2_ASCIIConversion(PR_TRUE, password->data, password->len, + uniPwd.data, uniPwd.len, &uniPwd.len, swap)) { + SECITEM_ZfreeItem(&uniPwd, PR_FALSE); + return NULL; + } + } + + retPwd = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(retPwd == NULL) { + goto loser; + } + + /* allocate space and copy proper data */ + retPwd->len = uniPwd.len + salt->len; + retPwd->data = (unsigned char *)PORT_Alloc(retPwd->len); + if(retPwd->data == NULL) { + PORT_Free(retPwd); + goto loser; + } + + PORT_Memcpy(retPwd->data, salt->data, salt->len); + PORT_Memcpy((retPwd->data + salt->len), uniPwd.data, uniPwd.len); + + SECITEM_ZfreeItem(&uniPwd, PR_FALSE); + + return retPwd; + +loser: + PORT_SetError(SEC_ERROR_NO_MEMORY); + SECITEM_ZfreeItem(&uniPwd, PR_FALSE); + return NULL; +} + +/* appends a shrouded key to a key bag. this is used for exporting + * to store externally wrapped keys. it is used when importing to convert + * old items to new + */ +SECStatus +sec_pkcs12_append_shrouded_key(SEC_PKCS12BaggageItem *bag, + SEC_PKCS12ESPVKItem *espvk) +{ + int size; + void *mark = NULL, *dummy = NULL; + + if((bag == NULL) || (espvk == NULL)) + return SECFailure; + + mark = PORT_ArenaMark(bag->poolp); + + /* grow the list */ + size = (bag->nEspvks + 1) * sizeof(SEC_PKCS12ESPVKItem *); + dummy = (SEC_PKCS12ESPVKItem **)PORT_ArenaGrow(bag->poolp, + bag->espvks, size, + size + sizeof(SEC_PKCS12ESPVKItem *)); + bag->espvks = (SEC_PKCS12ESPVKItem**)dummy; + if(dummy == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + bag->espvks[bag->nEspvks] = espvk; + bag->nEspvks++; + bag->espvks[bag->nEspvks] = NULL; + + PORT_ArenaUnmark(bag->poolp, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(bag->poolp, mark); + return SECFailure; +} + +/* search a certificate list for a nickname, a thumbprint, or both + * within a certificate bag. if the certificate could not be + * found or an error occurs, NULL is returned; + */ +static SEC_PKCS12CertAndCRL * +sec_pkcs12_find_cert_in_certbag(SEC_PKCS12CertAndCRLBag *certbag, + SECItem *nickname, SGNDigestInfo *thumbprint) +{ + PRBool search_both = PR_FALSE, search_nickname = PR_FALSE; + int i, j; + + if((certbag == NULL) || ((nickname == NULL) && (thumbprint == NULL))) { + return NULL; + } + + if(thumbprint && nickname) { + search_both = PR_TRUE; + } + + if(nickname) { + search_nickname = PR_TRUE; + } + +search_again: + i = 0; + while(certbag->certAndCRLs[i] != NULL) { + SEC_PKCS12CertAndCRL *cert = certbag->certAndCRLs[i]; + + if(SECOID_FindOIDTag(&cert->BagID) == SEC_OID_PKCS12_X509_CERT_CRL_BAG) { + + /* check nicknames */ + if(search_nickname) { + if(SECITEM_CompareItem(nickname, &cert->nickname) == SECEqual) { + return cert; + } + } else { + /* check thumbprints */ + SECItem **derCertList; + + /* get pointer to certificate list, does not need to + * be freed since it is within the arena which will + * be freed later. + */ + derCertList = SEC_PKCS7GetCertificateList(&cert->value.x509->certOrCRL); + j = 0; + if(derCertList != NULL) { + while(derCertList[j] != NULL) { + SECComparison eq; + SGNDigestInfo *di; + di = sec_pkcs12_compute_thumbprint(derCertList[j]); + if(di) { + eq = SGN_CompareDigestInfo(thumbprint, di); + SGN_DestroyDigestInfo(di); + if(eq == SECEqual) { + /* copy the derCert for later reference */ + cert->value.x509->derLeafCert = derCertList[j]; + return cert; + } + } else { + /* an error occurred */ + return NULL; + } + j++; + } + } + } + } + + i++; + } + + if(search_both) { + search_both = PR_FALSE; + search_nickname = PR_FALSE; + goto search_again; + } + + return NULL; +} + +/* search a key list for a nickname, a thumbprint, or both + * within a key bag. if the key could not be + * found or an error occurs, NULL is returned; + */ +static SEC_PKCS12PrivateKey * +sec_pkcs12_find_key_in_keybag(SEC_PKCS12PrivateKeyBag *keybag, + SECItem *nickname, SGNDigestInfo *thumbprint) +{ + PRBool search_both = PR_FALSE, search_nickname = PR_FALSE; + int i, j; + + if((keybag == NULL) || ((nickname == NULL) && (thumbprint == NULL))) { + return NULL; + } + + if(keybag->privateKeys == NULL) { + return NULL; + } + + if(thumbprint && nickname) { + search_both = PR_TRUE; + } + + if(nickname) { + search_nickname = PR_TRUE; + } + +search_again: + i = 0; + while(keybag->privateKeys[i] != NULL) { + SEC_PKCS12PrivateKey *key = keybag->privateKeys[i]; + + /* check nicknames */ + if(search_nickname) { + if(SECITEM_CompareItem(nickname, &key->pvkData.nickname) == SECEqual) { + return key; + } + } else { + /* check digests */ + SGNDigestInfo **assocCerts = key->pvkData.assocCerts; + if((assocCerts == NULL) || (assocCerts[0] == NULL)) { + return NULL; + } + + j = 0; + while(assocCerts[j] != NULL) { + SECComparison eq; + eq = SGN_CompareDigestInfo(thumbprint, assocCerts[j]); + if(eq == SECEqual) { + return key; + } + j++; + } + } + i++; + } + + if(search_both) { + search_both = PR_FALSE; + search_nickname = PR_FALSE; + goto search_again; + } + + return NULL; +} + +/* seach the safe first then try the baggage bag + * safe and bag contain certs and keys to search + * objType is the object type to look for + * bagType is the type of bag that was found by sec_pkcs12_find_object + * index is the entity in safe->safeContents or bag->unencSecrets which + * is being searched + * nickname and thumbprint are the search criteria + * + * a return of null indicates no match + */ +static void * +sec_pkcs12_try_find(SEC_PKCS12SafeContents *safe, + SEC_PKCS12BaggageItem *bag, + SECOidTag objType, SECOidTag bagType, int index, + SECItem *nickname, SGNDigestInfo *thumbprint) +{ + PRBool searchSafe; + int i = index; + + if((safe == NULL) && (bag == NULL)) { + return NULL; + } + + searchSafe = (safe == NULL ? PR_FALSE : PR_TRUE); + switch(objType) { + case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID: + if(objType == bagType) { + SEC_PKCS12CertAndCRLBag *certBag; + + if(searchSafe) { + certBag = safe->contents[i]->safeContent.certAndCRLBag; + } else { + certBag = bag->unencSecrets[i]->safeContent.certAndCRLBag; + } + return sec_pkcs12_find_cert_in_certbag(certBag, nickname, + thumbprint); + } + break; + case SEC_OID_PKCS12_KEY_BAG_ID: + if(objType == bagType) { + SEC_PKCS12PrivateKeyBag *keyBag; + + if(searchSafe) { + keyBag = safe->contents[i]->safeContent.keyBag; + } else { + keyBag = bag->unencSecrets[i]->safeContent.keyBag; + } + return sec_pkcs12_find_key_in_keybag(keyBag, nickname, + thumbprint); + } + break; + default: + break; + } + + return NULL; +} + +/* searches both the baggage and the safe areas looking for + * object of specified type matching either the nickname or the + * thumbprint specified. + * + * safe and baggage store certs and keys + * objType is the OID for the bag type to be searched: + * SEC_OID_PKCS12_KEY_BAG_ID, or + * SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID + * nickname and thumbprint are the search criteria + * + * if no match found, NULL returned and error set + */ +void * +sec_pkcs12_find_object(SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage, + SECOidTag objType, + SECItem *nickname, + SGNDigestInfo *thumbprint) +{ + int i, j; + void *retItem; + + if(((safe == NULL) && (thumbprint == NULL)) || + ((nickname == NULL) && (thumbprint == NULL))) { + return NULL; + } + + i = 0; + if((safe != NULL) && (safe->contents != NULL)) { + while(safe->contents[i] != NULL) { + SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType); + retItem = sec_pkcs12_try_find(safe, NULL, objType, bagType, i, + nickname, thumbprint); + if(retItem != NULL) { + return retItem; + } + i++; + } + } + + if((baggage != NULL) && (baggage->bags != NULL)) { + i = 0; + while(baggage->bags[i] != NULL) { + SEC_PKCS12BaggageItem *xbag = baggage->bags[i]; + j = 0; + if(xbag->unencSecrets != NULL) { + while(xbag->unencSecrets[j] != NULL) { + SECOidTag bagType; + bagType = SECOID_FindOIDTag(&xbag->unencSecrets[j]->safeBagType); + retItem = sec_pkcs12_try_find(NULL, xbag, objType, bagType, + j, nickname, thumbprint); + if(retItem != NULL) { + return retItem; + } + j++; + } + } + i++; + } + } + + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME); + return NULL; +} + +/* this function converts a password to unicode and encures that the + * required double 0 byte be placed at the end of the string + */ +PRBool +sec_pkcs12_convert_item_to_unicode(PRArenaPool *arena, SECItem *dest, + SECItem *src, PRBool zeroTerm, + PRBool asciiConvert, PRBool toUnicode) +{ + PRBool success = PR_FALSE; + if(!src || !dest) { + return PR_FALSE; + } + + dest->len = src->len * 3 + 2; + if(arena) { + dest->data = (unsigned char*)PORT_ArenaZAlloc(arena, dest->len); + } else { + dest->data = (unsigned char*)PORT_ZAlloc(dest->len); + } + + if(!dest->data) { + dest->len = 0; + return PR_FALSE; + } + + if(!asciiConvert) { + success = PORT_UCS2_UTF8Conversion(toUnicode, src->data, src->len, dest->data, + dest->len, &dest->len); + } else { +#ifndef IS_LITTLE_ENDIAN + PRBool swapUnicode = PR_FALSE; +#else + PRBool swapUnicode = PR_TRUE; +#endif + success = PORT_UCS2_ASCIIConversion(toUnicode, src->data, src->len, dest->data, + dest->len, &dest->len, swapUnicode); + } + + if(!success) { + if(!arena) { + PORT_Free(dest->data); + dest->data = NULL; + dest->len = 0; + } + return PR_FALSE; + } + + if((dest->data[dest->len-1] || dest->data[dest->len-2]) && zeroTerm) { + if(dest->len + 2 > 3 * src->len) { + if(arena) { + dest->data = (unsigned char*)PORT_ArenaGrow(arena, + dest->data, dest->len, + dest->len + 2); + } else { + dest->data = (unsigned char*)PORT_Realloc(dest->data, + dest->len + 2); + } + + if(!dest->data) { + return PR_FALSE; + } + } + dest->len += 2; + dest->data[dest->len-1] = dest->data[dest->len-2] = 0; + } + + return PR_TRUE; +} + +/* pkcs 12 templates */ +static SEC_ChooseASN1TemplateFunc sec_pkcs12_shroud_chooser = + sec_pkcs12_choose_shroud_type; + +const SEC_ASN1Template SEC_PKCS12CodedSafeBagTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) }, + { SEC_ASN1_ANY, offsetof(SEC_PKCS12SafeBag, derSafeContent) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12CodedCertBagTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) }, + { SEC_ASN1_ANY, offsetof(SEC_PKCS12CertAndCRL, derValue) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12CodedCertAndCRLBagTemplate[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs), + SEC_PKCS12CodedCertBagTemplate }, +}; + +const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12ESPVKItem) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12ESPVKItem, espvkOID) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12ESPVKItem, espvkData), + SEC_PKCS12PVKSupportingDataTemplate_OLD }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_DYNAMIC | 0, offsetof(SEC_PKCS12ESPVKItem, espvkCipherText), + &sec_pkcs12_shroud_chooser }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12ESPVKItem) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12ESPVKItem, espvkOID) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12ESPVKItem, espvkData), + SEC_PKCS12PVKSupportingDataTemplate }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_DYNAMIC | 0, offsetof(SEC_PKCS12ESPVKItem, espvkCipherText), + &sec_pkcs12_shroud_chooser }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PVKAdditionalDataTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKAdditionalData) }, + { SEC_ASN1_OBJECT_ID, + offsetof(SEC_PKCS12PVKAdditionalData, pvkAdditionalType) }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12PVKAdditionalData, pvkAdditionalContent) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKSupportingData) }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12PVKSupportingData, assocCerts), + sgn_DigestInfoTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, + offsetof(SEC_PKCS12PVKSupportingData, regenerable) }, + { SEC_ASN1_PRINTABLE_STRING, + offsetof(SEC_PKCS12PVKSupportingData, nickname) }, + { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, + offsetof(SEC_PKCS12PVKSupportingData, pvkAdditionalDER) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKSupportingData) }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12PVKSupportingData, assocCerts), + sgn_DigestInfoTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, + offsetof(SEC_PKCS12PVKSupportingData, regenerable) }, + { SEC_ASN1_BMP_STRING, + offsetof(SEC_PKCS12PVKSupportingData, uniNickName) }, + { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, + offsetof(SEC_PKCS12PVKSupportingData, pvkAdditionalDER) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12BaggageItemTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12BaggageItem) }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, espvks), + SEC_PKCS12ESPVKItemTemplate }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, unencSecrets), + SEC_PKCS12SafeBagTemplate }, + /*{ SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, unencSecrets), + SEC_PKCS12CodedSafeBagTemplate }, */ + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12BaggageTemplate[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12Baggage, bags), + SEC_PKCS12BaggageItemTemplate }, +}; + +const SEC_ASN1Template SEC_PKCS12BaggageTemplate_OLD[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12Baggage_OLD, espvks), + SEC_PKCS12ESPVKItemTemplate_OLD }, +}; + +static SEC_ChooseASN1TemplateFunc sec_pkcs12_bag_chooser = + sec_pkcs12_choose_bag_type; + +static SEC_ChooseASN1TemplateFunc sec_pkcs12_bag_chooser_old = + sec_pkcs12_choose_bag_type_old; + +const SEC_ASN1Template SEC_PKCS12SafeBagTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12SafeBag, safeContent), + &sec_pkcs12_bag_chooser_old }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SafeBagTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_POINTER, + offsetof(SEC_PKCS12SafeBag, safeContent), + &sec_pkcs12_bag_chooser }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BMP_STRING, + offsetof(SEC_PKCS12SafeBag, uniSafeBagName) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate_OLD[] = +{ + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS12SafeContents, contents), + SEC_PKCS12SafeBagTemplate_OLD } +}; + +const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate[] = +{ + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS12SafeContents, contents), + SEC_PKCS12SafeBagTemplate } /* here */ +}; + +const SEC_ASN1Template SEC_PKCS12PrivateKeyTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PrivateKey) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12PrivateKey, pvkData), + SEC_PKCS12PVKSupportingDataTemplate }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12PrivateKey, pkcs8data), + SECKEY_PrivateKeyInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PrivateKeyBagTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PrivateKeyBag) }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12PrivateKeyBag, privateKeys), + SEC_PKCS12PrivateKeyTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, certOrCRL), + sec_PKCS7ContentInfoTemplate }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, thumbprint), + sgn_DigestInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, certOrCRL), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SDSICertTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) }, + { SEC_ASN1_IA5_STRING, offsetof(SEC_PKCS12SDSICert, value) }, + { 0 } +}; + +static SEC_ChooseASN1TemplateFunc sec_pkcs12_cert_crl_chooser_old = + sec_pkcs12_choose_cert_crl_type_old; + +static SEC_ChooseASN1TemplateFunc sec_pkcs12_cert_crl_chooser = + sec_pkcs12_choose_cert_crl_type; + +const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) }, + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_EXPLICIT | + SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | 0, + offsetof(SEC_PKCS12CertAndCRL, value), + &sec_pkcs12_cert_crl_chooser_old }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) }, + { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12CertAndCRL, value), + &sec_pkcs12_cert_crl_chooser }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs), + SEC_PKCS12CertAndCRLTemplate }, +}; + +const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRLBag) }, + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs), + SEC_PKCS12CertAndCRLTemplate_OLD }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SecretAdditionalTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SecretAdditional) }, + { SEC_ASN1_OBJECT_ID, + offsetof(SEC_PKCS12SecretAdditional, secretAdditionalType) }, + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_EXPLICIT, + offsetof(SEC_PKCS12SecretAdditional, secretAdditionalContent) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SecretTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12Secret) }, + { SEC_ASN1_BMP_STRING, offsetof(SEC_PKCS12Secret, uniSecretName) }, + { SEC_ASN1_ANY, offsetof(SEC_PKCS12Secret, value) }, + { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL, + offsetof(SEC_PKCS12Secret, secretAdditional), + SEC_PKCS12SecretAdditionalTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SecretItemTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12Secret) }, + { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12SecretItem, secret), SEC_PKCS12SecretTemplate }, + { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS12SecretItem, subFolder), SEC_PKCS12SafeBagTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12SecretBagTemplate[] = +{ + { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12SecretBag, secrets), + SEC_PKCS12SecretItemTemplate }, +}; + +const SEC_ASN1Template SEC_PKCS12MacDataTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) }, + { SEC_ASN1_INLINE, offsetof(SEC_PKCS12MacData, safeMac), + sgn_DigestInfoTemplate }, + { SEC_ASN1_BIT_STRING, offsetof(SEC_PKCS12MacData, macSalt) }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PFXItemTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) }, + { SEC_ASN1_OPTIONAL | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12PFXItem, macData), SEC_PKCS12MacDataTemplate }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS12PFXItem, authSafe), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12PFXItemTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) }, + { SEC_ASN1_OPTIONAL | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12PFXItem, old_safeMac), sgn_DigestInfoTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BIT_STRING, + offsetof(SEC_PKCS12PFXItem, old_macSalt) }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS12PFXItem, authSafe), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12AuthenticatedSafe) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(SEC_PKCS12AuthenticatedSafe, version) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_OBJECT_ID, + offsetof(SEC_PKCS12AuthenticatedSafe, transportMode) }, + { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL, + offsetof(SEC_PKCS12AuthenticatedSafe, privacySalt) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SET_OF, + offsetof(SEC_PKCS12AuthenticatedSafe, baggage.bags), + SEC_PKCS12BaggageItemTemplate }, + { SEC_ASN1_POINTER, + offsetof(SEC_PKCS12AuthenticatedSafe, safe), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate_OLD[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12AuthenticatedSafe) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(SEC_PKCS12AuthenticatedSafe, version) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(SEC_PKCS12AuthenticatedSafe, transportMode) }, + { SEC_ASN1_BIT_STRING, + offsetof(SEC_PKCS12AuthenticatedSafe, privacySalt) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS12AuthenticatedSafe, old_baggage), + SEC_PKCS12BaggageTemplate_OLD }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS12AuthenticatedSafe, old_safe), + sec_PKCS7ContentInfoTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12KeyBagTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12PrivateKeyBagTemplate } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12CertAndCRLBagTemplate_OLD } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12CertAndCRLBagTemplate } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12SecretBagTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12SecretBagTemplate } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate_OLD[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12X509CertCRLTemplate_OLD } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12X509CertCRLTemplate } +}; + +const SEC_ASN1Template SEC_PointerToPKCS12SDSICertTemplate[] = +{ + { SEC_ASN1_POINTER, 0, SEC_PKCS12SDSICertTemplate } +}; + + diff --git a/security/nss/lib/pkcs12/p12local.h b/security/nss/lib/pkcs12/p12local.h new file mode 100644 index 000000000..af56d9ceb --- /dev/null +++ b/security/nss/lib/pkcs12/p12local.h @@ -0,0 +1,87 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + + +#ifndef _P12LOCAL_H_ +#define _P12LOCAL_H_ + +#include "plarena.h" +#include "secoidt.h" +#include "secasn1.h" +#include "secder.h" +#include "certt.h" +#include "secpkcs7.h" +#include "pkcs12.h" +#include "p12.h" + +/* helper functions */ +extern const SEC_ASN1Template * +sec_pkcs12_choose_bag_type(void *src_or_dest, PRBool encoding); +extern const SEC_ASN1Template * +sec_pkcs12_choose_cert_crl_type(void *src_or_dest, PRBool encoding); +extern const SEC_ASN1Template * +sec_pkcs12_choose_shroud_type(void *src_or_dest, PRBool encoding); +extern SECItem *sec_pkcs12_generate_salt(void); +extern SECItem *sec_pkcs12_generate_key_from_password(SECOidTag algorithm, + SECItem *salt, SECItem *password); +extern SECItem *sec_pkcs12_generate_mac(SECItem *key, SECItem *msg, + PRBool old_method); +extern SGNDigestInfo *sec_pkcs12_compute_thumbprint(SECItem *der_cert); +extern SECItem *sec_pkcs12_create_virtual_password(SECItem *password, + SECItem *salt, PRBool swapUnicodeBytes); +extern SECStatus sec_pkcs12_append_shrouded_key(SEC_PKCS12BaggageItem *bag, + SEC_PKCS12ESPVKItem *espvk); +extern void *sec_pkcs12_find_object(SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage, SECOidTag objType, + SECItem *nickname, SGNDigestInfo *thumbprint); +extern PRBool sec_pkcs12_convert_item_to_unicode(PRArenaPool *arena, SECItem *dest, + SECItem *src, PRBool zeroTerm, + PRBool asciiConvert, PRBool toUnicode); + +/* create functions */ +extern SEC_PKCS12PFXItem *sec_pkcs12_new_pfx(void); +extern SEC_PKCS12SafeContents *sec_pkcs12_create_safe_contents( + PRArenaPool *poolp); +extern SEC_PKCS12Baggage *sec_pkcs12_create_baggage(PRArenaPool *poolp); +extern SEC_PKCS12BaggageItem *sec_pkcs12_create_external_bag(SEC_PKCS12Baggage *luggage); +extern void SEC_PKCS12DestroyPFX(SEC_PKCS12PFXItem *pfx); +extern SEC_PKCS12AuthenticatedSafe *sec_pkcs12_new_asafe(PRArenaPool *poolp); + +/* conversion from old to new */ +extern SEC_PKCS12DecoderContext * +sec_PKCS12ConvertOldSafeToNew(PRArenaPool *arena, PK11SlotInfo *slot, + PRBool swapUnicode, SECItem *pwitem, + void *wincx, SEC_PKCS12SafeContents *safe, + SEC_PKCS12Baggage *baggage); + +#endif diff --git a/security/nss/lib/pkcs12/p12plcy.c b/security/nss/lib/pkcs12/p12plcy.c new file mode 100644 index 000000000..e9616ade0 --- /dev/null +++ b/security/nss/lib/pkcs12/p12plcy.c @@ -0,0 +1,198 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + + +#include "p12plcy.h" +#include "secoid.h" +#include "secport.h" +#include "secpkcs5.h" /* LOTS of PKCS5 calls below. XXX EVIL. */ + +#define PKCS12_NULL 0x0000 + +typedef struct pkcs12SuiteMapStr { + SECOidTag algTag; + unsigned int keyLengthBits; /* in bits */ + unsigned long suite; + PRBool allowed; + PRBool preferred; +} pkcs12SuiteMap; + +static pkcs12SuiteMap pkcs12SuiteMaps[] = { + { SEC_OID_RC4, 40, PKCS12_RC4_40, PR_FALSE, PR_FALSE}, + { SEC_OID_RC4, 128, PKCS12_RC4_128, PR_FALSE, PR_FALSE}, + { SEC_OID_RC2_CBC, 40, PKCS12_RC2_CBC_40, PR_FALSE, PR_TRUE}, + { SEC_OID_RC2_CBC, 128, PKCS12_RC2_CBC_128, PR_FALSE, PR_FALSE}, + { SEC_OID_DES_CBC, 64, PKCS12_DES_56, PR_FALSE, PR_FALSE}, + { SEC_OID_DES_EDE3_CBC, 192, PKCS12_DES_EDE3_168, PR_FALSE, PR_FALSE}, + { SEC_OID_UNKNOWN, 0, PKCS12_NULL, PR_FALSE, PR_FALSE}, + { SEC_OID_UNKNOWN, 0, 0L, PR_FALSE, PR_FALSE} +}; + +/* determine if algid is an algorithm which is allowed */ +PRBool +SEC_PKCS12DecryptionAllowed(SECAlgorithmID *algid) +{ + unsigned int keyLengthBits; + SECOidTag algId; + int i; + + algId = SEC_PKCS5GetCryptoAlgorithm(algid); + if(algId == SEC_OID_UNKNOWN) { + return PR_FALSE; + } + + keyLengthBits = (unsigned int)(SEC_PKCS5GetKeyLength(algid) * 8); + + i = 0; + while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) { + if((pkcs12SuiteMaps[i].algTag == algId) && + (pkcs12SuiteMaps[i].keyLengthBits == keyLengthBits)) { + + return pkcs12SuiteMaps[i].allowed; + } + i++; + } + + return PR_FALSE; +} + +/* is any encryption allowed? */ +PRBool +SEC_PKCS12IsEncryptionAllowed(void) +{ + int i; + + i = 0; + while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) { + if(pkcs12SuiteMaps[i].allowed == PR_TRUE) { + return PR_TRUE; + } + i++; + } + + return PR_FALSE; +} + +/* get the preferred algorithm. + */ +SECOidTag +SEC_PKCS12GetPreferredEncryptionAlgorithm(void) +{ + int i; + + i = 0; + while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) { + if((pkcs12SuiteMaps[i].preferred == PR_TRUE) && + (pkcs12SuiteMaps[i].allowed == PR_TRUE)) { + return SEC_PKCS5GetPBEAlgorithm(pkcs12SuiteMaps[i].algTag, + pkcs12SuiteMaps[i].keyLengthBits); + } + i++; + } + + return SEC_OID_UNKNOWN; +} + +/* return the strongest algorithm allowed */ +SECOidTag +SEC_PKCS12GetStrongestAllowedAlgorithm(void) +{ + int i, keyLengthBits = 0; + SECOidTag algorithm = SEC_OID_UNKNOWN; + + i = 0; + while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) { + if((pkcs12SuiteMaps[i].allowed == PR_TRUE) && + (pkcs12SuiteMaps[i].keyLengthBits > (unsigned int)keyLengthBits) && + (pkcs12SuiteMaps[i].algTag != SEC_OID_RC4)) { + algorithm = pkcs12SuiteMaps[i].algTag; + keyLengthBits = pkcs12SuiteMaps[i].keyLengthBits; + } + i++; + } + + if(algorithm == SEC_OID_UNKNOWN) { + return SEC_OID_UNKNOWN; + } + + return SEC_PKCS5GetPBEAlgorithm(algorithm, keyLengthBits); +} + +SECStatus +SEC_PKCS12EnableCipher(long which, int on) +{ + int i; + + i = 0; + while(pkcs12SuiteMaps[i].suite != 0L) { + if(pkcs12SuiteMaps[i].suite == (unsigned long)which) { + if(on) { + pkcs12SuiteMaps[i].allowed = PR_TRUE; + } else { + pkcs12SuiteMaps[i].allowed = PR_FALSE; + } + return SECSuccess; + } + i++; + } + + return SECFailure; +} + +SECStatus +SEC_PKCS12SetPreferredCipher(long which, int on) +{ + int i; + PRBool turnedOff = PR_FALSE; + PRBool turnedOn = PR_FALSE; + + i = 0; + while(pkcs12SuiteMaps[i].suite != 0L) { + if(pkcs12SuiteMaps[i].preferred == PR_TRUE) { + pkcs12SuiteMaps[i].preferred = PR_FALSE; + turnedOff = PR_TRUE; + } + if(pkcs12SuiteMaps[i].suite == (unsigned long)which) { + pkcs12SuiteMaps[i].preferred = PR_TRUE; + turnedOn = PR_TRUE; + } + i++; + } + + if((turnedOn) && (turnedOff)) { + return SECSuccess; + } + + return SECFailure; +} + diff --git a/security/nss/lib/pkcs12/p12plcy.h b/security/nss/lib/pkcs12/p12plcy.h new file mode 100644 index 000000000..1a3a9ee2f --- /dev/null +++ b/security/nss/lib/pkcs12/p12plcy.h @@ -0,0 +1,57 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +#ifndef _P12PLCY_H_ +#define _P12PLCY_H_ + +#include "secoid.h" +#include "ciferfam.h" + +/* for the algid specified, can we decrypt it ? */ +extern PRBool SEC_PKCS12DecryptionAllowed(SECAlgorithmID *algid); + +/* is encryption allowed? */ +extern PRBool SEC_PKCS12IsEncryptionAllowed(void); + +/* get the preferred encryption algorithm */ +extern SECOidTag SEC_PKCS12GetPreferredEncryptionAlgorithm(void); + +/* get the stronget crypto allowed (based on order in the table */ +extern SECOidTag SEC_PKCS12GetStrongestAllowedAlgorithm(void); + +/* enable a cipher for encryption/decryption */ +extern SECStatus SEC_PKCS12EnableCipher(long which, int on); + +/* return the preferred cipher for encryption */ +extern SECStatus SEC_PKCS12SetPreferredCipher(long which, int on); + +#endif diff --git a/security/nss/lib/pkcs12/p12t.h b/security/nss/lib/pkcs12/p12t.h new file mode 100644 index 000000000..6b9d3da1b --- /dev/null +++ b/security/nss/lib/pkcs12/p12t.h @@ -0,0 +1,182 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef _P12T_H_ +#define _P12T_H_ + +#include "secoid.h" +#include "key.h" +#include "pkcs11.h" +#include "secpkcs7.h" +#include "secdig.h" /* for SGNDigestInfo */ + +#define SEC_PKCS12_VERSION 3 + +/* structure declarations */ +typedef struct sec_PKCS12PFXItemStr sec_PKCS12PFXItem; +typedef struct sec_PKCS12MacDataStr sec_PKCS12MacData; +typedef struct sec_PKCS12AuthenticatedSafeStr sec_PKCS12AuthenticatedSafe; +typedef struct sec_PKCS12SafeContentsStr sec_PKCS12SafeContents; +typedef struct sec_PKCS12SafeBagStr sec_PKCS12SafeBag; +typedef struct sec_PKCS12PKCS8ShroudedKeyBagStr sec_PKCS12PKCS8ShroudedKeyBag; +typedef struct sec_PKCS12CertBagStr sec_PKCS12CertBag; +typedef struct sec_PKCS12CRLBagStr sec_PKCS12CRLBag; +typedef struct sec_PKCS12SecretBag sec_PKCS12SecretBag; +typedef struct sec_PKCS12AttributeStr sec_PKCS12Attribute; + +struct sec_PKCS12CertBagStr { + /* what type of cert is stored? */ + SECItem bagID; + + /* certificate information */ + union { + SECItem x509Cert; + SECItem SDSICert; + } value; +}; + +struct sec_PKCS12CRLBagStr { + /* what type of cert is stored? */ + SECItem bagID; + + /* certificate information */ + union { + SECItem x509CRL; + } value; +}; + +struct sec_PKCS12SecretBag { + /* what type of secret? */ + SECItem secretType; + + /* secret information. ssshhhh be vewy vewy quiet. */ + SECItem secretContent; +}; + +struct sec_PKCS12AttributeStr { + SECItem attrType; + SECItem **attrValue; +}; + +struct sec_PKCS12SafeBagStr { + + /* What type of bag are we using? */ + SECItem safeBagType; + + /* Dependent upon the type of bag being used. */ + union { + SECKEYPrivateKeyInfo *pkcs8KeyBag; + SECKEYEncryptedPrivateKeyInfo *pkcs8ShroudedKeyBag; + sec_PKCS12CertBag *certBag; + sec_PKCS12CRLBag *crlBag; + sec_PKCS12SecretBag *secretBag; + sec_PKCS12SafeContents *safeContents; + } safeBagContent; + + sec_PKCS12Attribute **attribs; + + /* used locally */ + SECOidData *bagTypeTag; + PRArenaPool *arena; + unsigned int nAttribs; + + /* used for validation/importing */ + PRBool problem, noInstall, validated, hasKey, removeExisting, installed; + int error; + + PRBool swapUnicodeBytes; + PK11SlotInfo *slot; + SECItem *pwitem; + PRBool oldBagType; +}; + +struct sec_PKCS12SafeContentsStr { + sec_PKCS12SafeBag **safeBags; + SECItem **encodedSafeBags; + + /* used locally */ + PRArenaPool *arena; + unsigned int bagCount; +}; + +struct sec_PKCS12MacDataStr { + SGNDigestInfo safeMac; + SECItem macSalt; + SECItem iter; +}; + +struct sec_PKCS12PFXItemStr { + + SECItem version; + + /* Content type will either be Data (password integrity mode) + * or signedData (public-key integrity mode) + */ + SEC_PKCS7ContentInfo *authSafe; + SECItem encodedAuthSafe; + + /* Only present in password integrity mode */ + sec_PKCS12MacData macData; + SECItem encodedMacData; +}; + +struct sec_PKCS12AuthenticatedSafeStr { + /* Content type will either be encryptedData (password privacy mode) + * or envelopedData (public-key privacy mode) + */ + SEC_PKCS7ContentInfo **safes; + SECItem **encodedSafes; + + /* used locally */ + unsigned int safeCount; + SECItem dummySafe; +}; + +extern const SEC_ASN1Template sec_PKCS12PFXItemTemplate[]; +extern const SEC_ASN1Template sec_PKCS12MacDataTemplate[]; +extern const SEC_ASN1Template sec_PKCS12AuthenticatedSafeTemplate[]; +extern const SEC_ASN1Template sec_PKCS12SafeContentsTemplate[]; +extern const SEC_ASN1Template sec_PKCS12SafeContentsDecodeTemplate[]; +extern const SEC_ASN1Template sec_PKCS12NestedSafeContentsDecodeTemplate[]; +extern const SEC_ASN1Template sec_PKCS12CertBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12CRLBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12SecretBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToCertBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToCRLBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToSecretBagTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToSafeContentsTemplate[]; +extern const SEC_ASN1Template sec_PKCS12AttributeTemplate[]; +extern const SEC_ASN1Template sec_PKCS12PointerToContentInfoTemplate[]; +extern const SEC_ASN1Template sec_PKCS12SafeBagTemplate[]; + +#endif diff --git a/security/nss/lib/pkcs12/p12tmpl.c b/security/nss/lib/pkcs12/p12tmpl.c new file mode 100644 index 000000000..ebaed1183 --- /dev/null +++ b/security/nss/lib/pkcs12/p12tmpl.c @@ -0,0 +1,315 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "plarena.h" +#include "secitem.h" +#include "secoid.h" +#include "seccomon.h" +#include "secport.h" +#include "cert.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "p12t.h" + +static const SEC_ASN1Template * +sec_pkcs12_choose_safe_bag_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + sec_PKCS12SafeBag *safeBag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + safeBag = (sec_PKCS12SafeBag*)src_or_dest; + + oiddata = SECOID_FindOID(&safeBag->safeBagType); + if(oiddata == NULL) { + return SEC_AnyTemplate; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_AnyTemplate; + break; + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + theTemplate = SECKEY_PointerToPrivateKeyInfoTemplate; + break; + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + theTemplate = sec_PKCS12PointerToCertBagTemplate; + break; + case SEC_OID_PKCS12_V1_CRL_BAG_ID: + theTemplate = sec_PKCS12PointerToCRLBagTemplate; + break; + case SEC_OID_PKCS12_V1_SECRET_BAG_ID: + theTemplate = sec_PKCS12PointerToSecretBagTemplate; + break; + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + theTemplate = SECKEY_PointerToEncryptedPrivateKeyInfoTemplate; + break; + case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: + if(encoding) { + theTemplate = sec_PKCS12PointerToSafeContentsTemplate; + } else { + theTemplate = SEC_PointerToAnyTemplate; + } + break; + } + return theTemplate; +} + +static const SEC_ASN1Template * +sec_pkcs12_choose_crl_bag_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + sec_PKCS12CRLBag *crlbag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + crlbag = (sec_PKCS12CRLBag*)src_or_dest; + + oiddata = SECOID_FindOID(&crlbag->bagID); + if(oiddata == NULL) { + return SEC_AnyTemplate; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_AnyTemplate; + break; + case SEC_OID_PKCS9_X509_CRL: + theTemplate = SEC_OctetStringTemplate; + break; + } + return theTemplate; +} + +static const SEC_ASN1Template * +sec_pkcs12_choose_cert_bag_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + sec_PKCS12CertBag *certbag; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + certbag = (sec_PKCS12CertBag*)src_or_dest; + + oiddata = SECOID_FindOID(&certbag->bagID); + if(oiddata == NULL) { + return SEC_AnyTemplate; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_AnyTemplate; + break; + case SEC_OID_PKCS9_X509_CERT: + theTemplate = SEC_OctetStringTemplate; + break; + case SEC_OID_PKCS9_SDSI_CERT: + theTemplate = SEC_IA5StringTemplate; + break; + } + return theTemplate; +} + +static const SEC_ASN1Template * +sec_pkcs12_choose_attr_type(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + sec_PKCS12Attribute *attr; + SECOidData *oiddata; + + if (src_or_dest == NULL) { + return NULL; + } + + attr = (sec_PKCS12Attribute*)src_or_dest; + + oiddata = SECOID_FindOID(&attr->attrType); + if(oiddata == NULL) { + return SEC_AnyTemplate; + } + + switch (oiddata->offset) { + default: + theTemplate = SEC_AnyTemplate; + break; + case SEC_OID_PKCS9_FRIENDLY_NAME: + theTemplate = SEC_BMPStringTemplate; + break; + case SEC_OID_PKCS9_LOCAL_KEY_ID: + theTemplate = SEC_OctetStringTemplate; + break; + case SEC_OID_PKCS12_KEY_USAGE: + theTemplate = SEC_BitStringTemplate; + break; + } + + return theTemplate; +} + + +const SEC_ASN1Template sec_PKCS12PointerToContentInfoTemplate[] = { + { SEC_ASN1_POINTER | SEC_ASN1_MAY_STREAM, 0, sec_PKCS7ContentInfoTemplate } +}; + +static SEC_ChooseASN1TemplateFunc sec_pkcs12_crl_bag_chooser = + sec_pkcs12_choose_crl_bag_type; + +static SEC_ChooseASN1TemplateFunc sec_pkcs12_cert_bag_chooser = + sec_pkcs12_choose_cert_bag_type; + +static SEC_ChooseASN1TemplateFunc sec_pkcs12_safe_bag_chooser = + sec_pkcs12_choose_safe_bag_type; + +static SEC_ChooseASN1TemplateFunc sec_pkcs12_attr_chooser = + sec_pkcs12_choose_attr_type; + +const SEC_ASN1Template sec_PKCS12PointerToCertBagTemplate[] = { + { SEC_ASN1_POINTER, 0, sec_PKCS12CertBagTemplate } +}; + +const SEC_ASN1Template sec_PKCS12PointerToCRLBagTemplate[] = { + { SEC_ASN1_POINTER, 0, sec_PKCS12CRLBagTemplate } +}; + +const SEC_ASN1Template sec_PKCS12PointerToSecretBagTemplate[] = { + { SEC_ASN1_POINTER, 0, sec_PKCS12SecretBagTemplate } +}; + +const SEC_ASN1Template sec_PKCS12PointerToSafeContentsTemplate[] = { + { SEC_ASN1_POINTER, 0, sec_PKCS12SafeContentsTemplate } +}; + +const SEC_ASN1Template sec_PKCS12PFXItemTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, 0, NULL, + sizeof(sec_PKCS12PFXItem) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(sec_PKCS12PFXItem, version) }, + { SEC_ASN1_ANY | SEC_ASN1_MAY_STREAM, + offsetof(sec_PKCS12PFXItem, encodedAuthSafe) }, + { SEC_ASN1_ANY | SEC_ASN1_MAY_STREAM, + offsetof(sec_PKCS12PFXItem, encodedMacData) }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12MacDataTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12MacData) }, + { SEC_ASN1_INLINE, offsetof(sec_PKCS12MacData, safeMac), + sgn_DigestInfoTemplate }, + { SEC_ASN1_OCTET_STRING, offsetof(sec_PKCS12MacData, macSalt) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, offsetof(sec_PKCS12MacData, iter) }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12AuthenticatedSafeTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM, + offsetof(sec_PKCS12AuthenticatedSafe, encodedSafes), SEC_AnyTemplate } +}; + +const SEC_ASN1Template sec_PKCS12SafeBagTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, 0, NULL, + sizeof(sec_PKCS12SafeBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12SafeBag, safeBagType) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_MAY_STREAM | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(sec_PKCS12SafeBag, safeBagContent), + &sec_pkcs12_safe_bag_chooser }, + { SEC_ASN1_SET_OF | SEC_ASN1_OPTIONAL, offsetof(sec_PKCS12SafeBag, attribs), + sec_PKCS12AttributeTemplate }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12SafeContentsTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM, + offsetof(sec_PKCS12SafeContents, safeBags), + sec_PKCS12SafeBagTemplate } +}; + +const SEC_ASN1Template sec_PKCS12SequenceOfAnyTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM, 0, + SEC_AnyTemplate } +}; + +const SEC_ASN1Template sec_PKCS12NestedSafeContentsDecodeTemplate[] = { + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0, + offsetof(sec_PKCS12SafeContents, encodedSafeBags), + sec_PKCS12SequenceOfAnyTemplate } +}; + +const SEC_ASN1Template sec_PKCS12SafeContentsDecodeTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM, + offsetof(sec_PKCS12SafeContents, encodedSafeBags), + SEC_AnyTemplate } +}; + +const SEC_ASN1Template sec_PKCS12CRLBagTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12CRLBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12CRLBag, bagID) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_POINTER, + offsetof(sec_PKCS12CRLBag, value), &sec_pkcs12_crl_bag_chooser }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12CertBagTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12CertBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12CertBag, bagID) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(sec_PKCS12CertBag, value), &sec_pkcs12_cert_bag_chooser }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12SecretBagTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12SecretBag) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12SecretBag, secretType) }, + { SEC_ASN1_ANY, offsetof(sec_PKCS12SecretBag, secretContent) }, + { 0 } +}; + +const SEC_ASN1Template sec_PKCS12AttributeTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12Attribute) }, + { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12Attribute, attrType) }, + { SEC_ASN1_SET_OF | SEC_ASN1_DYNAMIC, + offsetof(sec_PKCS12Attribute, attrValue), + &sec_pkcs12_attr_chooser }, + { 0 } +}; diff --git a/security/nss/lib/pkcs12/pkcs12.h b/security/nss/lib/pkcs12/pkcs12.h new file mode 100644 index 000000000..dd9d99594 --- /dev/null +++ b/security/nss/lib/pkcs12/pkcs12.h @@ -0,0 +1,67 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + + +#ifndef _PKCS12_H_ +#define _PKCS12_H_ + +#include "pkcs12t.h" +#include "p12.h" + +typedef SECItem * (* SEC_PKCS12GetPassword)(void *arg); + +/* Decode functions */ +/* Import a PFX item. + * der_pfx is the der-encoded pfx item to import. + * pbef, and pbefarg are used to retrieve passwords for the HMAC, + * and any passwords needed for passing to PKCS5 encryption + * routines. + * algorithm is the algorithm by which private keys are stored in + * the key database. this could be a specific algorithm or could + * be based on a global setting. + * slot is the slot to where the certificates will be placed. if NULL, + * the internal key slot is used. + * If the process is successful, a SECSuccess is returned, otherwise + * a failure occurred. + */ +SECStatus +SEC_PKCS12PutPFX(SECItem *der_pfx, SECItem *pwitem, + SEC_PKCS12NicknameCollisionCallback ncCall, + PK11SlotInfo *slot, void *wincx); + +/* check the first two bytes of a file to make sure that it matches + * the desired header for a PKCS 12 file + */ +PRBool SEC_PKCS12ValidData(char *buf, int bufLen, long int totalLength); + +#endif diff --git a/security/nss/lib/pkcs12/pkcs12t.h b/security/nss/lib/pkcs12/pkcs12t.h new file mode 100644 index 000000000..53c36c3f7 --- /dev/null +++ b/security/nss/lib/pkcs12/pkcs12t.h @@ -0,0 +1,386 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef _PKCS12T_H_ +#define _PKCS12T_H_ + +#include "seccomon.h" +#include "secoid.h" +#include "cert.h" +#include "key.h" +#include "plarena.h" +#include "secpkcs7.h" +#include "secdig.h" /* for SGNDigestInfo */ + +/* PKCS12 Structures */ +typedef struct SEC_PKCS12PFXItemStr SEC_PKCS12PFXItem; +typedef struct SEC_PKCS12MacDataStr SEC_PKCS12MacData; +typedef struct SEC_PKCS12AuthenticatedSafeStr SEC_PKCS12AuthenticatedSafe; +typedef struct SEC_PKCS12BaggageItemStr SEC_PKCS12BaggageItem; +typedef struct SEC_PKCS12BaggageStr SEC_PKCS12Baggage; +typedef struct SEC_PKCS12Baggage_OLDStr SEC_PKCS12Baggage_OLD; +typedef struct SEC_PKCS12ESPVKItemStr SEC_PKCS12ESPVKItem; +typedef struct SEC_PKCS12PVKSupportingDataStr SEC_PKCS12PVKSupportingData; +typedef struct SEC_PKCS12PVKAdditionalDataStr SEC_PKCS12PVKAdditionalData; +typedef struct SEC_PKCS12SafeContentsStr SEC_PKCS12SafeContents; +typedef struct SEC_PKCS12SafeBagStr SEC_PKCS12SafeBag; +typedef struct SEC_PKCS12PrivateKeyStr SEC_PKCS12PrivateKey; +typedef struct SEC_PKCS12PrivateKeyBagStr SEC_PKCS12PrivateKeyBag; +typedef struct SEC_PKCS12CertAndCRLBagStr SEC_PKCS12CertAndCRLBag; +typedef struct SEC_PKCS12CertAndCRLStr SEC_PKCS12CertAndCRL; +typedef struct SEC_PKCS12X509CertCRLStr SEC_PKCS12X509CertCRL; +typedef struct SEC_PKCS12SDSICertStr SEC_PKCS12SDSICert; +typedef struct SEC_PKCS12SecretStr SEC_PKCS12Secret; +typedef struct SEC_PKCS12SecretAdditionalStr SEC_PKCS12SecretAdditional; +typedef struct SEC_PKCS12SecretItemStr SEC_PKCS12SecretItem; +typedef struct SEC_PKCS12SecretBagStr SEC_PKCS12SecretBag; + +typedef SECItem *(* SEC_PKCS12PasswordFunc)(SECItem *args); + +/* PKCS12 types */ + +/* stores shrouded keys */ +struct SEC_PKCS12BaggageStr +{ + PRArenaPool *poolp; + SEC_PKCS12BaggageItem **bags; + + int luggage_size; /* used locally */ +}; + +/* additional data to be associated with keys. currently there + * is nothing defined to be stored here. allows future expansion. + */ +struct SEC_PKCS12PVKAdditionalDataStr +{ + PRArenaPool *poolp; + SECOidData *pvkAdditionalTypeTag; /* used locally */ + SECItem pvkAdditionalType; + SECItem pvkAdditionalContent; +}; + +/* cert and other supporting data for private keys. used + * for both shrouded and non-shrouded keys. + */ +struct SEC_PKCS12PVKSupportingDataStr +{ + PRArenaPool *poolp; + SGNDigestInfo **assocCerts; + SECItem regenerable; + SECItem nickname; + SEC_PKCS12PVKAdditionalData pvkAdditional; + SECItem pvkAdditionalDER; + + SECItem uniNickName; + /* used locally */ + int nThumbs; +}; + +/* shrouded key structure. supports only pkcs8 shrouding + * currently. + */ +struct SEC_PKCS12ESPVKItemStr +{ + PRArenaPool *poolp; /* used locally */ + SECOidData *espvkTag; /* used locally */ + SECItem espvkOID; + SEC_PKCS12PVKSupportingData espvkData; + union + { + SECKEYEncryptedPrivateKeyInfo *pkcs8KeyShroud; + } espvkCipherText; + + PRBool duplicate; /* used locally */ + PRBool problem_cert; /* used locally */ + PRBool single_cert; /* used locally */ + int nCerts; /* used locally */ + SECItem derCert; /* used locally */ +}; + +/* generic bag store for the safe. safeBagType identifies + * the type of bag stored. + */ +struct SEC_PKCS12SafeBagStr +{ + PRArenaPool *poolp; + SECOidData *safeBagTypeTag; /* used locally */ + SECItem safeBagType; + union + { + SEC_PKCS12PrivateKeyBag *keyBag; + SEC_PKCS12CertAndCRLBag *certAndCRLBag; + SEC_PKCS12SecretBag *secretBag; + } safeContent; + + SECItem derSafeContent; + SECItem safeBagName; + + SECItem uniSafeBagName; +}; + +/* stores private keys and certificates in a list. each safebag + * has an ID identifying the type of content stored. + */ +struct SEC_PKCS12SafeContentsStr +{ + PRArenaPool *poolp; + SEC_PKCS12SafeBag **contents; + + /* used for tracking purposes */ + int safe_size; + PRBool old; + PRBool swapUnicode; + PRBool possibleSwapUnicode; +}; + +/* private key structure which holds encrypted private key and + * supporting data including nickname and certificate thumbprint. + */ +struct SEC_PKCS12PrivateKeyStr +{ + PRArenaPool *poolp; + SEC_PKCS12PVKSupportingData pvkData; + SECKEYPrivateKeyInfo pkcs8data; /* borrowed from PKCS 8 */ + + PRBool duplicate; /* used locally */ + PRBool problem_cert;/* used locally */ + PRBool single_cert; /* used locally */ + int nCerts; /* used locally */ + SECItem derCert; /* used locally */ +}; + +/* private key bag, holds a (null terminated) list of private key + * structures. + */ +struct SEC_PKCS12PrivateKeyBagStr +{ + PRArenaPool *poolp; + SEC_PKCS12PrivateKey **privateKeys; + + int bag_size; /* used locally */ +}; + +/* container to hold certificates. currently supports x509 + * and sdsi certificates + */ +struct SEC_PKCS12CertAndCRLStr +{ + PRArenaPool *poolp; + SECOidData *BagTypeTag; /* used locally */ + SECItem BagID; + union + { + SEC_PKCS12X509CertCRL *x509; + SEC_PKCS12SDSICert *sdsi; + } value; + + SECItem derValue; + SECItem nickname; /* used locally */ + PRBool duplicate; /* used locally */ +}; + +/* x509 certificate structure. typically holds the der encoding + * of the x509 certificate. thumbprint contains a digest of the + * certificate + */ +struct SEC_PKCS12X509CertCRLStr +{ + PRArenaPool *poolp; + SEC_PKCS7ContentInfo certOrCRL; + SGNDigestInfo thumbprint; + + SECItem *derLeafCert; /* used locally */ +}; + +/* sdsi certificate structure. typically holds the der encoding + * of the sdsi certificate. thumbprint contains a digest of the + * certificate + */ +struct SEC_PKCS12SDSICertStr +{ + PRArenaPool *poolp; + SECItem value; + SGNDigestInfo thumbprint; +}; + +/* contains a null terminated list of certs and crls */ +struct SEC_PKCS12CertAndCRLBagStr +{ + PRArenaPool *poolp; + SEC_PKCS12CertAndCRL **certAndCRLs; + + int bag_size; /* used locally */ +}; + +/* additional secret information. currently no information + * stored in this structure. + */ +struct SEC_PKCS12SecretAdditionalStr +{ + PRArenaPool *poolp; + SECOidData *secretTypeTag; /* used locally */ + SECItem secretAdditionalType; + SECItem secretAdditionalContent; +}; + +/* secrets container. this will be used to contain currently + * unspecified secrets. (it's a secret) + */ +struct SEC_PKCS12SecretStr +{ + PRArenaPool *poolp; + SECItem secretName; + SECItem value; + SEC_PKCS12SecretAdditional secretAdditional; + + SECItem uniSecretName; +}; + +struct SEC_PKCS12SecretItemStr +{ + PRArenaPool *poolp; + SEC_PKCS12Secret secret; + SEC_PKCS12SafeBag subFolder; +}; + +/* a bag of secrets. holds a null terminated list of secrets. + */ +struct SEC_PKCS12SecretBagStr +{ + PRArenaPool *poolp; + SEC_PKCS12SecretItem **secrets; + + int bag_size; /* used locally */ +}; + +struct SEC_PKCS12MacDataStr +{ + SGNDigestInfo safeMac; + SECItem macSalt; +}; + +/* outer transfer unit */ +struct SEC_PKCS12PFXItemStr +{ + PRArenaPool *poolp; + SEC_PKCS12MacData macData; + SEC_PKCS7ContentInfo authSafe; + + /* for compatibility with beta */ + PRBool old; + SGNDigestInfo old_safeMac; + SECItem old_macSalt; + + /* compatibility between platforms for unicode swapping */ + PRBool swapUnicode; +}; + +struct SEC_PKCS12BaggageItemStr { + PRArenaPool *poolp; + SEC_PKCS12ESPVKItem **espvks; + SEC_PKCS12SafeBag **unencSecrets; + + int nEspvks; + int nSecrets; +}; + +/* stores shrouded keys */ +struct SEC_PKCS12Baggage_OLDStr +{ + PRArenaPool *poolp; + SEC_PKCS12ESPVKItem **espvks; + + int luggage_size; /* used locally */ +}; + +/* authenticated safe, stores certs, keys, and shrouded keys */ +struct SEC_PKCS12AuthenticatedSafeStr +{ + PRArenaPool *poolp; + SECItem version; + SECOidData *transportTypeTag; /* local not part of encoding*/ + SECItem transportMode; + SECItem privacySalt; + SEC_PKCS12Baggage baggage; + SEC_PKCS7ContentInfo *safe; + + /* used for beta compatibility */ + PRBool old; + PRBool emptySafe; + SEC_PKCS12Baggage_OLD old_baggage; + SEC_PKCS7ContentInfo old_safe; + PRBool swapUnicode; +}; +#define SEC_PKCS12_PFX_VERSION 1 /* what we create */ + + + +/* PKCS 12 Templates */ +extern const SEC_ASN1Template SEC_PKCS12PFXItemTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12BaggageTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12PFXItemTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12MacDataTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12BaggageTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PVKAdditionalTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SafeBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PrivateKeyTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PrivateKeyBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SDSICertTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SecretBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SecretTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SecretItemTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12SecretAdditionalTemplate[]; +extern const SEC_ASN1Template SGN_DigestInfoTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12KeyBagTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12SecretBagTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate[]; +extern const SEC_ASN1Template SEC_PointerToPKCS12SDSICertTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CodedSafeBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CodedCertBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12CodedCertAndCRLBagTemplate[]; +extern const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate_OLD[]; +extern const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate_OLD[]; +#endif diff --git a/security/nss/lib/pkcs7/Makefile b/security/nss/lib/pkcs7/Makefile new file mode 100644 index 000000000..cb85677bc --- /dev/null +++ b/security/nss/lib/pkcs7/Makefile @@ -0,0 +1,76 @@ +#! gmake +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include config.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + + diff --git a/security/nss/lib/pkcs7/config.mk b/security/nss/lib/pkcs7/config.mk new file mode 100644 index 000000000..a73a1086e --- /dev/null +++ b/security/nss/lib/pkcs7/config.mk @@ -0,0 +1,44 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +# +# Override TARGETS variable so that only static libraries +# are specifed as dependencies within rules.mk. +# + +TARGETS = $(LIBRARY) +SHARED_LIBRARY = +IMPORT_LIBRARY = +PURE_LIBRARY = +PROGRAM = + diff --git a/security/nss/lib/pkcs7/manifest.mn b/security/nss/lib/pkcs7/manifest.mn new file mode 100644 index 000000000..b2b0e45d9 --- /dev/null +++ b/security/nss/lib/pkcs7/manifest.mn @@ -0,0 +1,59 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +CORE_DEPTH = ../../.. + +EXPORTS = \ + secmime.h \ + secpkcs7.h \ + pkcs7t.h \ + $(NULL) + +PRIVATE_EXPORTS = \ + p7local.h \ + $(NULL) + +MODULE = security + +CSRCS = \ + p7common.c \ + p7create.c \ + p7decode.c \ + p7encode.c \ + p7local.c \ + secmime.c \ + $(NULL) + +REQUIRES = security dbm + +LIBRARY_NAME = pkcs7 diff --git a/security/nss/lib/pkcs7/p7common.c b/security/nss/lib/pkcs7/p7common.c new file mode 100644 index 000000000..e11a7f586 --- /dev/null +++ b/security/nss/lib/pkcs7/p7common.c @@ -0,0 +1,738 @@ +/* + * 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. + */ + +/* + * PKCS7 implementation -- the exported parts that are used whether + * creating or decoding. + * + * $Id$ + */ + +#include "p7local.h" + +#include "cert.h" +#include "secitem.h" +#include "secoid.h" +#include "secpkcs5.h" +#include "pk11func.h" + +/* + * Find out (saving pointer to lookup result for future reference) + * and return the inner content type. + */ +SECOidTag +SEC_PKCS7ContentType (SEC_PKCS7ContentInfo *cinfo) +{ + if (cinfo->contentTypeTag == NULL) + cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType)); + + if (cinfo->contentTypeTag == NULL) + return SEC_OID_UNKNOWN; + + return cinfo->contentTypeTag->offset; +} + + +/* + * Destroy a PKCS7 contentInfo and all of its sub-pieces. + */ +void +SEC_PKCS7DestroyContentInfo(SEC_PKCS7ContentInfo *cinfo) +{ + SECOidTag kind; + CERTCertificate **certs; + CERTCertificateList **certlists; + SEC_PKCS7SignerInfo **signerinfos; + SEC_PKCS7RecipientInfo **recipientinfos; + + PORT_Assert (cinfo->refCount > 0); + if (cinfo->refCount <= 0) + return; + + cinfo->refCount--; + if (cinfo->refCount > 0) + return; + + certs = NULL; + certlists = NULL; + recipientinfos = NULL; + signerinfos = NULL; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + case SEC_OID_PKCS7_ENVELOPED_DATA: + { + SEC_PKCS7EnvelopedData *edp; + + edp = cinfo->content.envelopedData; + if (edp != NULL) { + recipientinfos = edp->recipientInfos; + } + } + break; + case SEC_OID_PKCS7_SIGNED_DATA: + { + SEC_PKCS7SignedData *sdp; + + sdp = cinfo->content.signedData; + if (sdp != NULL) { + certs = sdp->certs; + certlists = sdp->certLists; + signerinfos = sdp->signerInfos; + } + } + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saedp; + + saedp = cinfo->content.signedAndEnvelopedData; + if (saedp != NULL) { + certs = saedp->certs; + certlists = saedp->certLists; + recipientinfos = saedp->recipientInfos; + signerinfos = saedp->signerInfos; + if (saedp->sigKey != NULL) + PK11_FreeSymKey (saedp->sigKey); + } + } + break; + default: + /* XXX Anything else that needs to be "manually" freed/destroyed? */ + break; + } + + if (certs != NULL) { + CERTCertificate *cert; + + while ((cert = *certs++) != NULL) { + CERT_DestroyCertificate (cert); + } + } + + if (certlists != NULL) { + CERTCertificateList *certlist; + + while ((certlist = *certlists++) != NULL) { + CERT_DestroyCertificateList (certlist); + } + } + + if (recipientinfos != NULL) { + SEC_PKCS7RecipientInfo *ri; + + while ((ri = *recipientinfos++) != NULL) { + if (ri->cert != NULL) + CERT_DestroyCertificate (ri->cert); + } + } + + if (signerinfos != NULL) { + SEC_PKCS7SignerInfo *si; + + while ((si = *signerinfos++) != NULL) { + if (si->cert != NULL) + CERT_DestroyCertificate (si->cert); + if (si->certList != NULL) + CERT_DestroyCertificateList (si->certList); + } + } + + if (cinfo->poolp != NULL) { + PORT_FreeArena (cinfo->poolp, PR_FALSE); /* XXX clear it? */ + } +} + + +/* + * Return a copy of the given contentInfo. The copy may be virtual + * or may be real -- either way, the result needs to be passed to + * SEC_PKCS7DestroyContentInfo later (as does the original). + */ +SEC_PKCS7ContentInfo * +SEC_PKCS7CopyContentInfo(SEC_PKCS7ContentInfo *cinfo) +{ + if (cinfo == NULL) + return NULL; + + PORT_Assert (cinfo->refCount > 0); + + if (cinfo->created) { + /* + * Want to do a real copy of these; otherwise subsequent + * changes made to either copy are likely to be a surprise. + * XXX I suspect that this will not actually be called for yet, + * which is why the assert, so to notice if it is... + */ + PORT_Assert (0); + /* + * XXX Create a new pool here, and copy everything from + * within. For cert stuff, need to call the appropriate + * copy functions, etc. + */ + } + + cinfo->refCount++; + return cinfo; +} + + +/* + * Return a pointer to the actual content. In the case of those types + * which are encrypted, this returns the *plain* content. + * XXX Needs revisiting if/when we handle nested encrypted types. + */ +SECItem * +SEC_PKCS7GetContent(SEC_PKCS7ContentInfo *cinfo) +{ + SECOidTag kind; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + case SEC_OID_PKCS7_DATA: + return cinfo->content.data; + case SEC_OID_PKCS7_DIGESTED_DATA: + { + SEC_PKCS7DigestedData *digd; + + digd = cinfo->content.digestedData; + if (digd == NULL) + break; + return SEC_PKCS7GetContent (&(digd->contentInfo)); + } + case SEC_OID_PKCS7_ENCRYPTED_DATA: + { + SEC_PKCS7EncryptedData *encd; + + encd = cinfo->content.encryptedData; + if (encd == NULL) + break; + return &(encd->encContentInfo.plainContent); + } + case SEC_OID_PKCS7_ENVELOPED_DATA: + { + SEC_PKCS7EnvelopedData *envd; + + envd = cinfo->content.envelopedData; + if (envd == NULL) + break; + return &(envd->encContentInfo.plainContent); + } + case SEC_OID_PKCS7_SIGNED_DATA: + { + SEC_PKCS7SignedData *sigd; + + sigd = cinfo->content.signedData; + if (sigd == NULL) + break; + return SEC_PKCS7GetContent (&(sigd->contentInfo)); + } + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saed; + + saed = cinfo->content.signedAndEnvelopedData; + if (saed == NULL) + break; + return &(saed->encContentInfo.plainContent); + } + default: + PORT_Assert(0); + break; + } + + return NULL; +} + + +/* + * XXX Fix the placement and formatting of the + * following routines (i.e. make them consistent with the rest of + * the pkcs7 code -- I think some/many belong in other files and + * they all need a formatting/style rehaul) + */ + +/* retrieve the algorithm identifier for encrypted data. + * the identifier returned is a copy of the algorithm identifier + * in the content info and needs to be freed after being used. + * + * cinfo is the content info for which to retrieve the + * encryption algorithm. + * + * if the content info is not encrypted data or an error + * occurs NULL is returned. + */ +SECAlgorithmID * +SEC_PKCS7GetEncryptionAlgorithm(SEC_PKCS7ContentInfo *cinfo) +{ + SECAlgorithmID *alg = 0; + switch (SEC_PKCS7ContentType(cinfo)) + { + case SEC_OID_PKCS7_ENCRYPTED_DATA: + alg = &cinfo->content.encryptedData->encContentInfo.contentEncAlg; + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + alg = &cinfo->content.envelopedData->encContentInfo.contentEncAlg; + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + alg = &cinfo->content.signedAndEnvelopedData + ->encContentInfo.contentEncAlg; + break; + default: + alg = 0; + break; + } + + return alg; +} + +/* set the content of the content info. For data content infos, + * the data is set. For encrytped content infos, the plainContent + * is set, and is expected to be encrypted later. + * + * cinfo is the content info where the data will be set + * + * buf is a buffer of the data to set + * + * len is the length of the data being set. + * + * in the event of an error, SECFailure is returned. SECSuccess + * indicates the content was successfully set. + */ +SECStatus +SEC_PKCS7SetContent(SEC_PKCS7ContentInfo *cinfo, + const char *buf, + unsigned long len) +{ + SECOidTag cinfo_type; + SECStatus rv; + SECItem content; + SECOidData *contentTypeTag = NULL; + + content.data = (unsigned char *)buf; + content.len = len; + + cinfo_type = SEC_PKCS7ContentType(cinfo); + + /* set inner content */ + switch(cinfo_type) + { + case SEC_OID_PKCS7_SIGNED_DATA: + if(content.len > 0) { + /* we "leak" the old content here, but as it's all in the pool */ + /* it does not really matter */ + + /* create content item if necessary */ + if (cinfo->content.signedData->contentInfo.content.data == NULL) + cinfo->content.signedData->contentInfo.content.data = SECITEM_AllocItem(cinfo->poolp, NULL, 0); + rv = SECITEM_CopyItem(cinfo->poolp, + cinfo->content.signedData->contentInfo.content.data, + &content); + } else { + cinfo->content.signedData->contentInfo.content.data->data = NULL; + cinfo->content.signedData->contentInfo.content.data->len = 0; + rv = SECSuccess; + } + if(rv == SECFailure) + goto loser; + + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + /* XXX this forces the inner content type to be "data" */ + /* do we really want to override without asking or reason? */ + contentTypeTag = SECOID_FindOIDByTag(SEC_OID_PKCS7_DATA); + if(contentTypeTag == NULL) + goto loser; + rv = SECITEM_CopyItem(cinfo->poolp, + &(cinfo->content.encryptedData->encContentInfo.contentType), + &(contentTypeTag->oid)); + if(rv == SECFailure) + goto loser; + if(content.len > 0) { + rv = SECITEM_CopyItem(cinfo->poolp, + &(cinfo->content.encryptedData->encContentInfo.plainContent), + &content); + } else { + cinfo->content.encryptedData->encContentInfo.plainContent.data = NULL; + cinfo->content.encryptedData->encContentInfo.encContent.data = NULL; + cinfo->content.encryptedData->encContentInfo.plainContent.len = 0; + cinfo->content.encryptedData->encContentInfo.encContent.len = 0; + rv = SECSuccess; + } + if(rv == SECFailure) + goto loser; + break; + case SEC_OID_PKCS7_DATA: + cinfo->content.data = (SECItem *)PORT_ArenaZAlloc(cinfo->poolp, + sizeof(SECItem)); + if(cinfo->content.data == NULL) + goto loser; + if(content.len > 0) { + rv = SECITEM_CopyItem(cinfo->poolp, + cinfo->content.data, &content); + } else { + /* handle case with NULL content */ + rv = SECSuccess; + } + if(rv == SECFailure) + goto loser; + break; + default: + goto loser; + } + + return SECSuccess; + +loser: + + return SECFailure; +} + +/* the content of an encrypted data content info is encrypted. + * it is assumed that for encrypted data, that the data has already + * been set and is in the "plainContent" field of the content info. + * + * cinfo is the content info to encrypt + * + * key is the key with which to perform the encryption. if the + * algorithm is a password based encryption algorithm, the + * key is actually a password which will be processed per + * PKCS #5. + * + * in the event of an error, SECFailure is returned. SECSuccess + * indicates a success. + */ +SECStatus +SEC_PKCS7EncryptContents(PRArenaPool *poolp, + SEC_PKCS7ContentInfo *cinfo, + SECItem *key, + void *wincx) +{ + SECAlgorithmID *algid = NULL; + SECItem * result = NULL; + SECItem * src; + SECItem * dest; + SECItem * blocked_data = NULL; + void * mark; + void * cx; + PK11SymKey * eKey = NULL; + PK11SlotInfo * slot = NULL; + + CK_MECHANISM pbeMech; + CK_MECHANISM cryptoMech; + int bs; + SECOidTag algtag; + SECStatus rv = SECFailure; + SECItem c_param; + + if((cinfo == NULL) || (key == NULL)) + return SECFailure; + + if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA) + return SECFailure; + + algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo); + if(algid == NULL) + return SECFailure; + + if(poolp == NULL) + poolp = cinfo->poolp; + + mark = PORT_ArenaMark(poolp); + + src = &cinfo->content.encryptedData->encContentInfo.plainContent; + dest = &cinfo->content.encryptedData->encContentInfo.encContent; + algtag = SECOID_GetAlgorithmTag(algid); + c_param.data = NULL; + dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64)); + dest->len = (src->len + 64); + if(dest->data == NULL) { + rv = SECFailure; + goto loser; + } + + slot = PK11_GetInternalKeySlot(); + if(slot == NULL) { + rv = SECFailure; + goto loser; + } + eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx); + if(eKey == NULL) { + rv = SECFailure; + goto loser; + } + + pbeMech.mechanism = PK11_AlgtagToMechanism(algtag); + result = PK11_ParamFromAlgid(algid); + pbeMech.pParameter = result->data; + pbeMech.ulParameterLen = result->len; + if(PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, key, + PR_FALSE) != CKR_OK) { + rv = SECFailure; + goto loser; + } + c_param.data = (unsigned char *)cryptoMech.pParameter; + c_param.len = cryptoMech.ulParameterLen; + + /* block according to PKCS 8 */ + bs = PK11_GetBlockSize(cryptoMech.mechanism, &c_param); + rv = SECSuccess; + if(bs) { + char pad_char; + pad_char = (char)(bs - (src->len % bs)); + if(src->len % bs) { + rv = SECSuccess; + blocked_data = PK11_BlockData(src, bs); + if(blocked_data) { + PORT_Memset((blocked_data->data + blocked_data->len - (int)pad_char), + pad_char, (int)pad_char); + } else { + rv = SECFailure; + goto loser; + } + } else { + blocked_data = SECITEM_DupItem(src); + if(blocked_data) { + blocked_data->data = (unsigned char*)PORT_Realloc( + blocked_data->data, + blocked_data->len + bs); + if(blocked_data->data) { + blocked_data->len += bs; + PORT_Memset((blocked_data->data + src->len), (char)bs, bs); + } else { + rv = SECFailure; + goto loser; + } + } else { + rv = SECFailure; + goto loser; + } + } + } else { + blocked_data = SECITEM_DupItem(src); + if(!blocked_data) { + rv = SECFailure; + goto loser; + } + } + + cx = PK11_CreateContextBySymKey(cryptoMech.mechanism, CKA_ENCRYPT, + eKey, &c_param); + if(cx == NULL) { + rv = SECFailure; + goto loser; + } + + rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len), + (int)(src->len + 64), blocked_data->data, + (int)blocked_data->len); + PK11_DestroyContext((PK11Context*)cx, PR_TRUE); + +loser: + /* let success fall through */ + if(blocked_data != NULL) + SECITEM_ZfreeItem(blocked_data, PR_TRUE); + + if(result != NULL) + SECITEM_ZfreeItem(result, PR_TRUE); + + if(rv == SECFailure) + PORT_ArenaRelease(poolp, mark); + else + PORT_ArenaUnmark(poolp, mark); + + if(eKey != NULL) + PK11_FreeSymKey(eKey); + + if(slot != NULL) + PK11_FreeSlot(slot); + + if(c_param.data != NULL) + SECITEM_ZfreeItem(&c_param, PR_FALSE); + + return rv; +} + +/* the content of an encrypted data content info is decrypted. + * it is assumed that for encrypted data, that the data has already + * been set and is in the "encContent" field of the content info. + * + * cinfo is the content info to decrypt + * + * key is the key with which to perform the decryption. if the + * algorithm is a password based encryption algorithm, the + * key is actually a password which will be processed per + * PKCS #5. + * + * in the event of an error, SECFailure is returned. SECSuccess + * indicates a success. + */ +SECStatus +SEC_PKCS7DecryptContents(PRArenaPool *poolp, + SEC_PKCS7ContentInfo *cinfo, + SECItem *key, + void *wincx) +{ + SECAlgorithmID *algid = NULL; + SECOidTag algtag; + SECStatus rv = SECFailure; + SECItem *result = NULL, *dest, *src; + void *mark; + + PK11SymKey *eKey = NULL; + PK11SlotInfo *slot = NULL; + CK_MECHANISM pbeMech, cryptoMech; + void *cx; + SECItem c_param; + int bs; + + if((cinfo == NULL) || (key == NULL)) + return SECFailure; + + if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA) + return SECFailure; + + algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo); + if(algid == NULL) + return SECFailure; + + if(poolp == NULL) + poolp = cinfo->poolp; + + mark = PORT_ArenaMark(poolp); + + src = &cinfo->content.encryptedData->encContentInfo.encContent; + dest = &cinfo->content.encryptedData->encContentInfo.plainContent; + algtag = SECOID_GetAlgorithmTag(algid); + c_param.data = NULL; + dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64)); + dest->len = (src->len + 64); + if(dest->data == NULL) { + rv = SECFailure; + goto loser; + } + + slot = PK11_GetInternalKeySlot(); + if(slot == NULL) { + rv = SECFailure; + goto loser; + } + eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx); + if(eKey == NULL) { + rv = SECFailure; + goto loser; + } + + pbeMech.mechanism = PK11_AlgtagToMechanism(algtag); + result = PK11_ParamFromAlgid(algid); + pbeMech.pParameter = result->data; + pbeMech.ulParameterLen = result->len; + if(PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, key, + PR_FALSE) != CKR_OK) { + rv = SECFailure; + goto loser; + } + c_param.data = (unsigned char *)cryptoMech.pParameter; + c_param.len = cryptoMech.ulParameterLen; + + cx = PK11_CreateContextBySymKey(cryptoMech.mechanism, CKA_DECRYPT, + eKey, &c_param); + if(cx == NULL) { + rv = SECFailure; + goto loser; + } + + rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len), + (int)(src->len + 64), src->data, (int)src->len); + PK11_DestroyContext((PK11Context *)cx, PR_TRUE); + + bs = PK11_GetBlockSize(cryptoMech.mechanism, &c_param); + if(bs) { + /* check for proper badding in block algorithms. this assumes + * RC2 cbc or a DES cbc variant. and the padding is thus defined + */ + if(((int)dest->data[dest->len-1] <= bs) && + ((int)dest->data[dest->len-1] > 0)) { + dest->len -= (int)dest->data[dest->len-1]; + } else { + rv = SECFailure; + /* set an error ? */ + } + } + +loser: + /* let success fall through */ + if(result != NULL) + SECITEM_ZfreeItem(result, PR_TRUE); + + if(rv == SECFailure) + PORT_ArenaRelease(poolp, mark); + else + PORT_ArenaUnmark(poolp, mark); + + if(eKey != NULL) + PK11_FreeSymKey(eKey); + + if(slot != NULL) + PK11_FreeSlot(slot); + + if(c_param.data != NULL) + SECITEM_ZfreeItem(&c_param, PR_FALSE); + + return rv; +} + +SECItem ** +SEC_PKCS7GetCertificateList(SEC_PKCS7ContentInfo *cinfo) +{ + switch(SEC_PKCS7ContentType(cinfo)) + { + case SEC_OID_PKCS7_SIGNED_DATA: + return cinfo->content.signedData->rawCerts; + break; + default: + return NULL; + break; + } +} + + +int +SEC_PKCS7GetKeyLength(SEC_PKCS7ContentInfo *cinfo) +{ + if (cinfo->contentTypeTag->offset == SEC_OID_PKCS7_ENVELOPED_DATA) + return cinfo->content.envelopedData->encContentInfo.keysize; + else + return 0; +} + diff --git a/security/nss/lib/pkcs7/p7create.c b/security/nss/lib/pkcs7/p7create.c new file mode 100644 index 000000000..665d8495c --- /dev/null +++ b/security/nss/lib/pkcs7/p7create.c @@ -0,0 +1,1320 @@ +/* + * 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. + */ + +/* + * PKCS7 creation. + * + * $Id$ + */ + +#include "p7local.h" + +#include "cert.h" +#include "secasn1.h" +#include "secitem.h" +#include "secoid.h" +#include "secpkcs5.h" +#include "pk11func.h" +#include "prtime.h" +#include "secerr.h" + +static SECStatus +sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PRArenaPool *poolp, + SECOidTag kind, PRBool detached) +{ + void *thing; + int version; + SECItem *versionp; + SECStatus rv; + + PORT_Assert (cinfo != NULL && poolp != NULL); + if (cinfo == NULL || poolp == NULL) + return SECFailure; + + cinfo->contentTypeTag = SECOID_FindOIDByTag (kind); + PORT_Assert (cinfo->contentTypeTag + && cinfo->contentTypeTag->offset == kind); + + rv = SECITEM_CopyItem (poolp, &(cinfo->contentType), + &(cinfo->contentTypeTag->oid)); + if (rv != SECSuccess) + return rv; + + if (detached) + return SECSuccess; + + switch (kind) { + default: + case SEC_OID_PKCS7_DATA: + thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem)); + cinfo->content.data = (SECItem*)thing; + versionp = NULL; + version = -1; + break; + case SEC_OID_PKCS7_DIGESTED_DATA: + thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData)); + cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing; + versionp = &(cinfo->content.digestedData->version); + version = SEC_PKCS7_DIGESTED_DATA_VERSION; + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData)); + cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing; + versionp = &(cinfo->content.encryptedData->version); + version = SEC_PKCS7_ENCRYPTED_DATA_VERSION; + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData)); + cinfo->content.envelopedData = + (SEC_PKCS7EnvelopedData*)thing; + versionp = &(cinfo->content.envelopedData->version); + version = SEC_PKCS7_ENVELOPED_DATA_VERSION; + break; + case SEC_OID_PKCS7_SIGNED_DATA: + thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData)); + cinfo->content.signedData = + (SEC_PKCS7SignedData*)thing; + versionp = &(cinfo->content.signedData->version); + version = SEC_PKCS7_SIGNED_DATA_VERSION; + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData)); + cinfo->content.signedAndEnvelopedData = + (SEC_PKCS7SignedAndEnvelopedData*)thing; + versionp = &(cinfo->content.signedAndEnvelopedData->version); + version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION; + break; + } + + if (thing == NULL) + return SECFailure; + + if (versionp != NULL) { + SECItem *dummy; + + PORT_Assert (version >= 0); + dummy = SEC_ASN1EncodeInteger (poolp, versionp, version); + if (dummy == NULL) + return SECFailure; + PORT_Assert (dummy == versionp); + } + + return SECSuccess; +} + + +static SEC_PKCS7ContentInfo * +sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached, + SECKEYGetPasswordKey pwfn, void *pwfn_arg) +{ + SEC_PKCS7ContentInfo *cinfo; + PRArenaPool *poolp; + SECStatus rv; + + poolp = PORT_NewArena (1024); /* XXX what is right value? */ + if (poolp == NULL) + return NULL; + + cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); + if (cinfo == NULL) { + PORT_FreeArena (poolp, PR_FALSE); + return NULL; + } + + cinfo->poolp = poolp; + cinfo->pwfn = pwfn; + cinfo->pwfn_arg = pwfn_arg; + cinfo->created = PR_TRUE; + cinfo->refCount = 1; + + rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached); + if (rv != SECSuccess) { + PORT_FreeArena (poolp, PR_FALSE); + return NULL; + } + + return cinfo; +} + + +/* + * Add a signer to a PKCS7 thing, verifying the signature cert first. + * Any error returns SECFailure. + * + * XXX Right now this only adds the *first* signer. It fails if you try + * to add a second one -- this needs to be fixed. + */ +static SECStatus +sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo, + CERTCertificate * cert, + SECCertUsage certusage, + CERTCertDBHandle * certdb, + SECOidTag digestalgtag, + SECItem * digestdata) +{ + SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp; + SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp; + SECItem *digest, **digests, ***digestsp; + SECItem * dummy; + void * mark; + SECStatus rv; + SECOidTag kind; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + case SEC_OID_PKCS7_SIGNED_DATA: + { + SEC_PKCS7SignedData *sdp; + + sdp = cinfo->content.signedData; + digestalgsp = &(sdp->digestAlgorithms); + digestsp = &(sdp->digests); + signerinfosp = &(sdp->signerInfos); + } + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saedp; + + saedp = cinfo->content.signedAndEnvelopedData; + digestalgsp = &(saedp->digestAlgorithms); + digestsp = &(saedp->digests); + signerinfosp = &(saedp->signerInfos); + } + break; + default: + return SECFailure; /* XXX set an error? */ + } + + /* + * XXX I think that CERT_VerifyCert should do this if *it* is passed + * a NULL database. + */ + if (certdb == NULL) { + certdb = CERT_GetDefaultCertDB(); + if (certdb == NULL) + return SECFailure; /* XXX set an error? */ + } + + if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), + cinfo->pwfn_arg, NULL) != SECSuccess) + { + /* XXX Did CERT_VerifyCert set an error? */ + return SECFailure; + } + + /* + * XXX This is the check that we do not already have a signer. + * This is not what we really want -- we want to allow this + * and *add* the new signer. + */ + PORT_Assert (*signerinfosp == NULL + && *digestalgsp == NULL && *digestsp == NULL); + if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL) + return SECFailure; + + mark = PORT_ArenaMark (cinfo->poolp); + + signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp, + sizeof(SEC_PKCS7SignerInfo)); + if (signerinfo == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + + dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version, + SEC_PKCS7_SIGNER_INFO_VERSION); + if (dummy == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + PORT_Assert (dummy == &signerinfo->version); + + signerinfo->cert = CERT_DupCertificate (cert); + if (signerinfo->cert == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + + signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); + if (signerinfo->issuerAndSN == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + + rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg, + digestalgtag, NULL); + if (rv != SECSuccess) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + + /* + * Okay, now signerinfo is all set. We just need to put it and its + * companions (another copy of the digest algorithm, and the digest + * itself if given) into the main structure. + * + * XXX If we are handling more than one signer, the following code + * needs to look through the digest algorithms already specified + * and see if the same one is there already. If it is, it does not + * need to be added again. Also, if it is there *and* the digest + * is not null, then the digest given should match the digest already + * specified -- if not, that is an error. Finally, the new signerinfo + * should be *added* to the set already found. + */ + + signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp, + 2 * sizeof(SEC_PKCS7SignerInfo *)); + if (signerinfos == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + signerinfos[0] = signerinfo; + signerinfos[1] = NULL; + + digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID)); + digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *)); + if (digestalg == NULL || digestalgs == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL); + if (rv != SECSuccess) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + digestalgs[0] = digestalg; + digestalgs[1] = NULL; + + if (digestdata != NULL) { + digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem)); + digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp, + 2 * sizeof(SECItem *)); + if (digest == NULL || digests == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata); + if (rv != SECSuccess) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + digests[0] = digest; + digests[1] = NULL; + } else { + digests = NULL; + } + + *signerinfosp = signerinfos; + *digestalgsp = digestalgs; + *digestsp = digests; + + PORT_ArenaUnmark(cinfo->poolp, mark); + return SECSuccess; +} + + +/* + * Helper function for creating an empty signedData. + */ +static SEC_PKCS7ContentInfo * +sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg) +{ + SEC_PKCS7ContentInfo *cinfo; + SEC_PKCS7SignedData *sigd; + SECStatus rv; + + cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE, + pwfn, pwfn_arg); + if (cinfo == NULL) + return NULL; + + sigd = cinfo->content.signedData; + PORT_Assert (sigd != NULL); + + /* + * XXX Might we want to allow content types other than data? + * If so, via what interface? + */ + rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp, + SEC_OID_PKCS7_DATA, PR_TRUE); + if (rv != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + + return cinfo; +} + + +/* + * Start a PKCS7 signing context. + * + * "cert" is the cert that will be used to sign the data. It will be + * checked for validity. + * + * "certusage" describes the signing usage (e.g. certUsageEmailSigner) + * XXX Maybe SECCertUsage should be split so that our caller just says + * "email" and *we* add the "signing" part -- otherwise our caller + * could be lying about the usage; we do not want to allow encryption + * certs for signing or vice versa. + * + * "certdb" is the cert database to use for verifying the cert. + * It can be NULL if a default database is available (like in the client). + * + * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). + * + * "digest" is the actual digest of the data. It must be provided in + * the case of detached data or NULL if the content will be included. + * + * The return value can be passed to functions which add things to + * it like attributes, then eventually to SEC_PKCS7Encode() or to + * SEC_PKCS7EncoderStart() to create the encoded data, and finally to + * SEC_PKCS7DestroyContentInfo(). + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +SEC_PKCS7ContentInfo * +SEC_PKCS7CreateSignedData (CERTCertificate *cert, + SECCertUsage certusage, + CERTCertDBHandle *certdb, + SECOidTag digestalg, + SECItem *digest, + SECKEYGetPasswordKey pwfn, void *pwfn_arg) +{ + SEC_PKCS7ContentInfo *cinfo; + SECStatus rv; + + cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg); + if (cinfo == NULL) + return NULL; + + rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb, + digestalg, digest); + if (rv != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + + return cinfo; +} + + +static SEC_PKCS7Attribute * +sec_pkcs7_create_attribute (PRArenaPool *poolp, SECOidTag oidtag, + SECItem *value, PRBool encoded) +{ + SEC_PKCS7Attribute *attr; + SECItem **values; + void *mark; + + PORT_Assert (poolp != NULL); + mark = PORT_ArenaMark (poolp); + + attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp, + sizeof(SEC_PKCS7Attribute)); + if (attr == NULL) + goto loser; + + attr->typeTag = SECOID_FindOIDByTag (oidtag); + if (attr->typeTag == NULL) + goto loser; + + if (SECITEM_CopyItem (poolp, &(attr->type), + &(attr->typeTag->oid)) != SECSuccess) + goto loser; + + values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *)); + if (values == NULL) + goto loser; + + if (value != NULL) { + SECItem *copy; + + copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem)); + if (copy == NULL) + goto loser; + + if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess) + goto loser; + + value = copy; + } + + values[0] = value; + values[1] = NULL; + attr->values = values; + attr->encoded = encoded; + + PORT_ArenaUnmark (poolp, mark); + return attr; + +loser: + PORT_Assert (mark != NULL); + PORT_ArenaRelease (poolp, mark); + return NULL; +} + + +static SECStatus +sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo, + SEC_PKCS7Attribute ***attrsp, + SEC_PKCS7Attribute *attr) +{ + SEC_PKCS7Attribute **attrs; + SECItem *ct_value; + void *mark; + + PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); + if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) + return SECFailure; + + attrs = *attrsp; + if (attrs != NULL) { + int count; + + /* + * We already have some attributes, and just need to add this + * new one. + */ + + /* + * We should already have the *required* attributes, which were + * created/added at the same time the first attribute was added. + */ + PORT_Assert (sec_PKCS7FindAttribute (attrs, + SEC_OID_PKCS9_CONTENT_TYPE, + PR_FALSE) != NULL); + PORT_Assert (sec_PKCS7FindAttribute (attrs, + SEC_OID_PKCS9_MESSAGE_DIGEST, + PR_FALSE) != NULL); + + for (count = 0; attrs[count] != NULL; count++) + ; + attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs, + (count + 1) * sizeof(SEC_PKCS7Attribute *), + (count + 2) * sizeof(SEC_PKCS7Attribute *)); + if (attrs == NULL) + return SECFailure; + + attrs[count] = attr; + attrs[count+1] = NULL; + *attrsp = attrs; + + return SECSuccess; + } + + /* + * This is the first time an attribute is going in. + * We need to create and add the required attributes, and then + * we will also add in the one our caller gave us. + */ + + /* + * There are 2 required attributes, plus the one our caller wants + * to add, plus we always end with a NULL one. Thus, four slots. + */ + attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp, + 4 * sizeof(SEC_PKCS7Attribute *)); + if (attrs == NULL) + return SECFailure; + + mark = PORT_ArenaMark (cinfo->poolp); + + /* + * First required attribute is the content type of the data + * being signed. + */ + ct_value = &(cinfo->content.signedData->contentInfo.contentType); + attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp, + SEC_OID_PKCS9_CONTENT_TYPE, + ct_value, PR_FALSE); + /* + * Second required attribute is the message digest of the data + * being signed; we leave the value NULL for now (just create + * the place for it to go), and the encoder will fill it in later. + */ + attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp, + SEC_OID_PKCS9_MESSAGE_DIGEST, + NULL, PR_FALSE); + if (attrs[0] == NULL || attrs[1] == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + + attrs[2] = attr; + attrs[3] = NULL; + *attrsp = attrs; + + PORT_ArenaUnmark (cinfo->poolp, mark); + return SECSuccess; +} + + +/* + * Add the signing time to the authenticated (i.e. signed) attributes + * of "cinfo". This is expected to be included in outgoing signed + * messages for email (S/MIME) but is likely useful in other situations. + * + * This should only be added once; a second call will either do + * nothing or replace an old signing time with a newer one. + * + * XXX This will probably just shove the current time into "cinfo" + * but it will not actually get signed until the entire item is + * processed for encoding. Is this (expected to be small) delay okay? + * + * "cinfo" should be of type signedData (the only kind of pkcs7 data + * that is allowed authenticated attributes); SECFailure will be returned + * if it is not. + */ +SECStatus +SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo) +{ + SEC_PKCS7SignerInfo **signerinfos; + SEC_PKCS7Attribute *attr; + SECItem stime; + SECStatus rv; + int si; + + PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); + if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) + return SECFailure; + + signerinfos = cinfo->content.signedData->signerInfos; + + /* There has to be a signer, or it makes no sense. */ + if (signerinfos == NULL || signerinfos[0] == NULL) + return SECFailure; + + rv = DER_TimeToUTCTime (&stime, PR_Now()); + if (rv != SECSuccess) + return rv; + + attr = sec_pkcs7_create_attribute (cinfo->poolp, + SEC_OID_PKCS9_SIGNING_TIME, + &stime, PR_FALSE); + SECITEM_FreeItem (&stime, PR_FALSE); + + if (attr == NULL) + return SECFailure; + + rv = SECSuccess; + for (si = 0; signerinfos[si] != NULL; si++) { + SEC_PKCS7Attribute *oattr; + + oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr, + SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE); + PORT_Assert (oattr == NULL); + if (oattr != NULL) + continue; /* XXX or would it be better to replace it? */ + + rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr), + attr); + if (rv != SECSuccess) + break; /* could try to continue, but may as well give up now */ + } + + return rv; +} + + +/* + * Add the specified attribute to the authenticated (i.e. signed) attributes + * of "cinfo" -- "oidtag" describes the attribute and "value" is the + * value to be associated with it. NOTE! "value" must already be encoded; + * no interpretation of "oidtag" is done. Also, it is assumed that this + * signedData has only one signer -- if we ever need to add attributes + * when there is more than one signature, we need a way to specify *which* + * signature should get the attribute. + * + * XXX Technically, a signed attribute can have multiple values; if/when + * we ever need to support an attribute which takes multiple values, we + * either need to change this interface or create an AddSignedAttributeValue + * which can be called subsequently, and would then append a value. + * + * "cinfo" should be of type signedData (the only kind of pkcs7 data + * that is allowed authenticated attributes); SECFailure will be returned + * if it is not. + */ +SECStatus +SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo, + SECOidTag oidtag, + SECItem *value) +{ + SEC_PKCS7SignerInfo **signerinfos; + SEC_PKCS7Attribute *attr; + + PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA); + if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) + return SECFailure; + + signerinfos = cinfo->content.signedData->signerInfos; + + /* + * No signature or more than one means no deal. + */ + if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) + return SECFailure; + + attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE); + if (attr == NULL) + return SECFailure; + + return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr); +} + + +/* + * Mark that the signer certificates and their issuing chain should + * be included in the encoded data. This is expected to be used + * in outgoing signed messages for email (S/MIME). + * + * "certdb" is the cert database to use for finding the chain. + * It can be NULL, meaning use the default database. + * + * "cinfo" should be of type signedData or signedAndEnvelopedData; + * SECFailure will be returned if it is not. + */ +SECStatus +SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo, + CERTCertDBHandle *certdb) +{ + SECOidTag kind; + SEC_PKCS7SignerInfo *signerinfo, **signerinfos; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + case SEC_OID_PKCS7_SIGNED_DATA: + signerinfos = cinfo->content.signedData->signerInfos; + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; + break; + default: + return SECFailure; /* XXX set an error? */ + } + + if (signerinfos == NULL) /* no signer, no certs? */ + return SECFailure; /* XXX set an error? */ + + if (certdb == NULL) { + certdb = CERT_GetDefaultCertDB(); + if (certdb == NULL) { + PORT_SetError (SEC_ERROR_BAD_DATABASE); + return SECFailure; + } + } + + /* XXX Should it be an error if we find no signerinfo or no certs? */ + while ((signerinfo = *signerinfos++) != NULL) { + if (signerinfo->cert != NULL) + /* get the cert chain. don't send the root to avoid contamination + * of old clients with a new root that they don't trust + */ + signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert, + certUsageEmailSigner, + PR_FALSE); + } + + return SECSuccess; +} + + +/* + * Helper function to add a certificate chain for inclusion in the + * bag of certificates in a signedData. + */ +static SECStatus +sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo, + CERTCertificate *cert, + CERTCertDBHandle *certdb) +{ + SECOidTag kind; + CERTCertificateList *certlist, **certlists, ***certlistsp; + int count; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + case SEC_OID_PKCS7_SIGNED_DATA: + { + SEC_PKCS7SignedData *sdp; + + sdp = cinfo->content.signedData; + certlistsp = &(sdp->certLists); + } + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saedp; + + saedp = cinfo->content.signedAndEnvelopedData; + certlistsp = &(saedp->certLists); + } + break; + default: + return SECFailure; /* XXX set an error? */ + } + + if (certdb == NULL) { + certdb = CERT_GetDefaultCertDB(); + if (certdb == NULL) { + PORT_SetError (SEC_ERROR_BAD_DATABASE); + return SECFailure; + } + } + + certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE); + if (certlist == NULL) + return SECFailure; + + certlists = *certlistsp; + if (certlists == NULL) { + count = 0; + certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp, + 2 * sizeof(CERTCertificateList *)); + } else { + for (count = 0; certlists[count] != NULL; count++) + ; + PORT_Assert (count); /* should be at least one already */ + certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp, + certlists, + (count + 1) * sizeof(CERTCertificateList *), + (count + 2) * sizeof(CERTCertificateList *)); + } + + if (certlists == NULL) { + CERT_DestroyCertificateList (certlist); + return SECFailure; + } + + certlists[count] = certlist; + certlists[count + 1] = NULL; + + *certlistsp = certlists; + + return SECSuccess; +} + + +/* + * Helper function to add a certificate for inclusion in the bag of + * certificates in a signedData. + */ +static SECStatus +sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo, + CERTCertificate *cert) +{ + SECOidTag kind; + CERTCertificate **certs, ***certsp; + int count; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + case SEC_OID_PKCS7_SIGNED_DATA: + { + SEC_PKCS7SignedData *sdp; + + sdp = cinfo->content.signedData; + certsp = &(sdp->certs); + } + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saedp; + + saedp = cinfo->content.signedAndEnvelopedData; + certsp = &(saedp->certs); + } + break; + default: + return SECFailure; /* XXX set an error? */ + } + + cert = CERT_DupCertificate (cert); + if (cert == NULL) + return SECFailure; + + certs = *certsp; + if (certs == NULL) { + count = 0; + certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp, + 2 * sizeof(CERTCertificate *)); + } else { + for (count = 0; certs[count] != NULL; count++) + ; + PORT_Assert (count); /* should be at least one already */ + certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs, + (count + 1) * sizeof(CERTCertificate *), + (count + 2) * sizeof(CERTCertificate *)); + } + + if (certs == NULL) { + CERT_DestroyCertificate (cert); + return SECFailure; + } + + certs[count] = cert; + certs[count + 1] = NULL; + + *certsp = certs; + + return SECSuccess; +} + + +/* + * Create a PKCS7 certs-only container. + * + * "cert" is the (first) cert that will be included. + * + * "include_chain" specifies whether the entire chain for "cert" should + * be included. + * + * "certdb" is the cert database to use for finding the chain. + * It can be NULL in when "include_chain" is false, or when meaning + * use the default database. + * + * More certs and chains can be added via AddCertificate and AddCertChain. + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +SEC_PKCS7ContentInfo * +SEC_PKCS7CreateCertsOnly (CERTCertificate *cert, + PRBool include_chain, + CERTCertDBHandle *certdb) +{ + SEC_PKCS7ContentInfo *cinfo; + SECStatus rv; + + cinfo = sec_pkcs7_create_signed_data (NULL, NULL); + if (cinfo == NULL) + return NULL; + + if (include_chain) + rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb); + else + rv = sec_pkcs7_add_certificate (cinfo, cert); + + if (rv != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + + return cinfo; +} + + +/* + * Add "cert" and its entire chain to the set of certs included in "cinfo". + * + * "certdb" is the cert database to use for finding the chain. + * It can be NULL, meaning use the default database. + * + * "cinfo" should be of type signedData or signedAndEnvelopedData; + * SECFailure will be returned if it is not. + */ +SECStatus +SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo, + CERTCertificate *cert, + CERTCertDBHandle *certdb) +{ + SECOidTag kind; + + kind = SEC_PKCS7ContentType (cinfo); + if (kind != SEC_OID_PKCS7_SIGNED_DATA + && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) + return SECFailure; /* XXX set an error? */ + + return sec_pkcs7_add_cert_chain (cinfo, cert, certdb); +} + + +/* + * Add "cert" to the set of certs included in "cinfo". + * + * "cinfo" should be of type signedData or signedAndEnvelopedData; + * SECFailure will be returned if it is not. + */ +SECStatus +SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert) +{ + SECOidTag kind; + + kind = SEC_PKCS7ContentType (cinfo); + if (kind != SEC_OID_PKCS7_SIGNED_DATA + && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA) + return SECFailure; /* XXX set an error? */ + + return sec_pkcs7_add_certificate (cinfo, cert); +} + + +static SECStatus +sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo, + PRArenaPool *poolp, + SECOidTag kind, PRBool detached, + SECOidTag encalg, int keysize) +{ + SECStatus rv; + + PORT_Assert (enccinfo != NULL && poolp != NULL); + if (enccinfo == NULL || poolp == NULL) + return SECFailure; + + /* + * XXX Some day we may want to allow for other kinds. That needs + * more work and modifications to the creation interface, etc. + * For now, allow but notice callers who pass in other kinds. + * They are responsible for creating the inner type and encoding, + * if it is other than DATA. + */ + PORT_Assert (kind == SEC_OID_PKCS7_DATA); + + enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind); + PORT_Assert (enccinfo->contentTypeTag + && enccinfo->contentTypeTag->offset == kind); + + rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType), + &(enccinfo->contentTypeTag->oid)); + if (rv != SECSuccess) + return rv; + + /* Save keysize and algorithm for later. */ + enccinfo->keysize = keysize; + enccinfo->encalg = encalg; + + return SECSuccess; +} + + +/* + * Add a recipient to a PKCS7 thing, verifying their cert first. + * Any error returns SECFailure. + */ +static SECStatus +sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo, + CERTCertificate *cert, + SECCertUsage certusage, + CERTCertDBHandle *certdb) +{ + SECOidTag kind; + SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp; + SECItem *dummy; + void *mark; + int count; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + case SEC_OID_PKCS7_ENVELOPED_DATA: + { + SEC_PKCS7EnvelopedData *edp; + + edp = cinfo->content.envelopedData; + recipientinfosp = &(edp->recipientInfos); + } + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saedp; + + saedp = cinfo->content.signedAndEnvelopedData; + recipientinfosp = &(saedp->recipientInfos); + } + break; + default: + return SECFailure; /* XXX set an error? */ + } + + /* + * XXX I think that CERT_VerifyCert should do this if *it* is passed + * a NULL database. + */ + if (certdb == NULL) { + certdb = CERT_GetDefaultCertDB(); + if (certdb == NULL) + return SECFailure; /* XXX set an error? */ + } + + if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(), + cinfo->pwfn_arg, NULL) != SECSuccess) + { + /* XXX Did CERT_VerifyCert set an error? */ + return SECFailure; + } + + mark = PORT_ArenaMark (cinfo->poolp); + + recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp, + sizeof(SEC_PKCS7RecipientInfo)); + if (recipientinfo == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + + dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version, + SEC_PKCS7_RECIPIENT_INFO_VERSION); + if (dummy == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + PORT_Assert (dummy == &recipientinfo->version); + + recipientinfo->cert = CERT_DupCertificate (cert); + if (recipientinfo->cert == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + + recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert); + if (recipientinfo->issuerAndSN == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + + /* + * Okay, now recipientinfo is all set. We just need to put it into + * the main structure. + * + * If this is the first recipient, allocate a new recipientinfos array; + * otherwise, reallocate the array, making room for the new entry. + */ + recipientinfos = *recipientinfosp; + if (recipientinfos == NULL) { + count = 0; + recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc ( + cinfo->poolp, + 2 * sizeof(SEC_PKCS7RecipientInfo *)); + } else { + for (count = 0; recipientinfos[count] != NULL; count++) + ; + PORT_Assert (count); /* should be at least one already */ + recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow ( + cinfo->poolp, recipientinfos, + (count + 1) * sizeof(SEC_PKCS7RecipientInfo *), + (count + 2) * sizeof(SEC_PKCS7RecipientInfo *)); + } + + if (recipientinfos == NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + return SECFailure; + } + + recipientinfos[count] = recipientinfo; + recipientinfos[count + 1] = NULL; + + *recipientinfosp = recipientinfos; + + PORT_ArenaUnmark (cinfo->poolp, mark); + return SECSuccess; +} + + +/* + * Start a PKCS7 enveloping context. + * + * "cert" is the cert for the recipient. It will be checked for validity. + * + * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) + * XXX Maybe SECCertUsage should be split so that our caller just says + * "email" and *we* add the "recipient" part -- otherwise our caller + * could be lying about the usage; we do not want to allow encryption + * certs for signing or vice versa. + * + * "certdb" is the cert database to use for verifying the cert. + * It can be NULL if a default database is available (like in the client). + * + * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2). + * + * "keysize" specifies the bulk encryption key size, in bits. + * + * The return value can be passed to functions which add things to + * it like more recipients, then eventually to SEC_PKCS7Encode() or to + * SEC_PKCS7EncoderStart() to create the encoded data, and finally to + * SEC_PKCS7DestroyContentInfo(). + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +extern SEC_PKCS7ContentInfo * +SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert, + SECCertUsage certusage, + CERTCertDBHandle *certdb, + SECOidTag encalg, + int keysize, + SECKEYGetPasswordKey pwfn, void *pwfn_arg) +{ + SEC_PKCS7ContentInfo *cinfo; + SEC_PKCS7EnvelopedData *envd; + SECStatus rv; + + cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA, + PR_FALSE, pwfn, pwfn_arg); + if (cinfo == NULL) + return NULL; + + rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); + if (rv != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + + envd = cinfo->content.envelopedData; + PORT_Assert (envd != NULL); + + /* + * XXX Might we want to allow content types other than data? + * If so, via what interface? + */ + rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo), + cinfo->poolp, + SEC_OID_PKCS7_DATA, PR_FALSE, + encalg, keysize); + if (rv != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + + /* XXX Anything more to do here? */ + + return cinfo; +} + + +/* + * Add another recipient to an encrypted message. + * + * "cinfo" should be of type envelopedData or signedAndEnvelopedData; + * SECFailure will be returned if it is not. + * + * "cert" is the cert for the recipient. It will be checked for validity. + * + * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) + * XXX Maybe SECCertUsage should be split so that our caller just says + * "email" and *we* add the "recipient" part -- otherwise our caller + * could be lying about the usage; we do not want to allow encryption + * certs for signing or vice versa. + * + * "certdb" is the cert database to use for verifying the cert. + * It can be NULL if a default database is available (like in the client). + */ +SECStatus +SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo, + CERTCertificate *cert, + SECCertUsage certusage, + CERTCertDBHandle *certdb) +{ + return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb); +} + + +/* + * Create an empty PKCS7 data content info. + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +SEC_PKCS7ContentInfo * +SEC_PKCS7CreateData (void) +{ + return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE, + NULL, NULL); +} + + +/* + * Create an empty PKCS7 encrypted content info. + * + * "algorithm" specifies the bulk encryption algorithm to use. + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +SEC_PKCS7ContentInfo * +SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize, + SECKEYGetPasswordKey pwfn, void *pwfn_arg) +{ + SEC_PKCS7ContentInfo *cinfo; + SECAlgorithmID *algid; + SEC_PKCS7EncryptedData *enc_data; + SECStatus rv; + + cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA, + PR_FALSE, pwfn, pwfn_arg); + if (cinfo == NULL) + return NULL; + + enc_data = cinfo->content.encryptedData; + algid = &(enc_data->encContentInfo.contentEncAlg); + + switch (algorithm) { + case SEC_OID_RC2_CBC: + case SEC_OID_DES_EDE3_CBC: + case SEC_OID_DES_CBC: + rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL); + break; + default: + { + /* + * Assume password-based-encryption. At least, try that. + */ + SECAlgorithmID *pbe_algid; + pbe_algid = PK11_CreatePBEAlgorithmID (algorithm, 1, NULL); + if (pbe_algid == NULL) { + rv = SECFailure; + } else { + rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid); + SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE); + } + } + break; + } + + if (rv != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + + rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo), + cinfo->poolp, + SEC_OID_PKCS7_DATA, PR_FALSE, + algorithm, keysize); + if (rv != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + + return cinfo; +} + diff --git a/security/nss/lib/pkcs7/p7decode.c b/security/nss/lib/pkcs7/p7decode.c new file mode 100644 index 000000000..0eee743c2 --- /dev/null +++ b/security/nss/lib/pkcs7/p7decode.c @@ -0,0 +1,2087 @@ +/* + * 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. + */ + +/* + * PKCS7 decoding, verification. + * + * $Id$ + */ + +#include "p7local.h" + +#include "cert.h" + /* XXX do not want to have to include */ +#include "certdb.h" /* certdb.h -- the trust stuff needed by */ + /* the add certificate code needs to get */ + /* rewritten/abstracted and then this */ + /* include should be removed! */ +#include "cdbhdl.h" +#include "cryptohi.h" +#include "key.h" +#include "secasn1.h" +#include "secitem.h" +#include "secoid.h" +#include "pk11func.h" +#include "prtime.h" +#include "secerr.h" + + +struct sec_pkcs7_decoder_worker { + int depth; + int digcnt; + void **digcxs; + SECHashObject **digobjs; + sec_PKCS7CipherObject *decryptobj; + PRBool saw_contents; +}; + +struct SEC_PKCS7DecoderContextStr { + SEC_ASN1DecoderContext *dcx; + SEC_PKCS7ContentInfo *cinfo; + SEC_PKCS7DecoderContentCallback cb; + void *cb_arg; + SECKEYGetPasswordKey pwfn; + void *pwfn_arg; + struct sec_pkcs7_decoder_worker worker; + PRArenaPool *tmp_poolp; + int error; + SEC_PKCS7GetDecryptKeyCallback dkcb; + void *dkcb_arg; + SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb; +}; + +/* + * Handle one worker, decrypting and digesting the data as necessary. + * + * XXX If/when we support nested contents, this probably needs to be + * revised somewhat to get passed the content-info (which unfortunately + * can be two different types depending on whether it is encrypted or not) + * corresponding to the given worker. + */ +static void +sec_pkcs7_decoder_work_data (SEC_PKCS7DecoderContext *p7dcx, + struct sec_pkcs7_decoder_worker *worker, + const unsigned char *data, unsigned long len, + PRBool final) +{ + unsigned char *buf = NULL; + SECStatus rv; + int i; + + /* + * We should really have data to process, or we should be trying + * to finish/flush the last block. (This is an overly paranoid + * check since all callers are in this file and simple inspection + * proves they do it right. But it could find a bug in future + * modifications/development, that is why it is here.) + */ + PORT_Assert ((data != NULL && len) || final); + + /* + * Decrypt this chunk. + * + * XXX If we get an error, we do not want to do the digest or callback, + * but we want to keep decoding. Or maybe we want to stop decoding + * altogether if there is a callback, because obviously we are not + * sending the data back and they want to know that. + */ + if (worker->decryptobj != NULL) { + /* XXX the following lengths should all be longs? */ + unsigned int inlen; /* length of data being decrypted */ + unsigned int outlen; /* length of decrypted data */ + unsigned int buflen; /* length available for decrypted data */ + SECItem *plain; + + inlen = len; + buflen = sec_PKCS7DecryptLength (worker->decryptobj, inlen, final); + if (buflen == 0) { + if (inlen == 0) /* no input and no output */ + return; + /* + * No output is expected, but the input data may be buffered + * so we still have to call Decrypt. + */ + rv = sec_PKCS7Decrypt (worker->decryptobj, NULL, NULL, 0, + data, inlen, final); + if (rv != SECSuccess) { + p7dcx->error = PORT_GetError(); + return; /* XXX indicate error? */ + } + return; + } + + if (p7dcx->cb != NULL) { + buf = (unsigned char *) PORT_Alloc (buflen); + plain = NULL; + } else { + unsigned long oldlen; + + /* + * XXX This assumes one level of content only. + * See comment above about nested content types. + * XXX Also, it should work for signedAndEnvelopedData, too! + */ + plain = &(p7dcx->cinfo-> + content.envelopedData->encContentInfo.plainContent); + + oldlen = plain->len; + if (oldlen == 0) { + buf = (unsigned char*)PORT_ArenaAlloc (p7dcx->cinfo->poolp, + buflen); + } else { + buf = (unsigned char*)PORT_ArenaGrow (p7dcx->cinfo->poolp, + plain->data, + oldlen, oldlen + buflen); + if (buf != NULL) + buf += oldlen; + } + plain->data = buf; + } + if (buf == NULL) { + p7dcx->error = SEC_ERROR_NO_MEMORY; + return; /* XXX indicate error? */ + } + rv = sec_PKCS7Decrypt (worker->decryptobj, buf, &outlen, buflen, + data, inlen, final); + if (rv != SECSuccess) { + p7dcx->error = PORT_GetError(); + return; /* XXX indicate error? */ + } + if (plain != NULL) { + PORT_Assert (final || outlen == buflen); + plain->len += outlen; + } + data = buf; + len = outlen; + } + + /* + * Update the running digests. + */ + if (len) { + for (i = 0; i < worker->digcnt; i++) { + (* worker->digobjs[i]->update) (worker->digcxs[i], data, len); + } + } + + /* + * Pass back the contents bytes, and free the temporary buffer. + */ + if (p7dcx->cb != NULL) { + if (len) + (* p7dcx->cb) (p7dcx->cb_arg, (const char *)data, len); + if (worker->decryptobj != NULL) { + PORT_Assert (buf != NULL); + PORT_Free (buf); + } + } +} + +static void +sec_pkcs7_decoder_filter (void *arg, const char *data, unsigned long len, + int depth, SEC_ASN1EncodingPart data_kind) +{ + SEC_PKCS7DecoderContext *p7dcx; + struct sec_pkcs7_decoder_worker *worker; + + /* + * Since we do not handle any nested contents, the only bytes we + * are really interested in are the actual contents bytes (not + * the identifier, length, or end-of-contents bytes). If we were + * handling nested types we would probably need to do something + * smarter based on depth and data_kind. + */ + if (data_kind != SEC_ASN1_Contents) + return; + + /* + * The ASN.1 decoder should not even call us with a length of 0. + * Just being paranoid. + */ + PORT_Assert (len); + if (len == 0) + return; + + p7dcx = (SEC_PKCS7DecoderContext*)arg; + + /* + * Handling nested contents would mean that there is a chain + * of workers -- one per each level of content. The following + * would start with the first worker and loop over them. + */ + worker = &(p7dcx->worker); + + worker->saw_contents = PR_TRUE; + + sec_pkcs7_decoder_work_data (p7dcx, worker, + (const unsigned char *) data, len, PR_FALSE); +} + + +/* + * Create digest contexts for each algorithm in "digestalgs". + * No algorithms is not an error, we just do not do anything. + * An error (like trouble allocating memory), marks the error + * in "p7dcx" and returns SECFailure, which means that our caller + * should just give up altogether. + */ +static SECStatus +sec_pkcs7_decoder_start_digests (SEC_PKCS7DecoderContext *p7dcx, int depth, + SECAlgorithmID **digestalgs) +{ + SECAlgorithmID *algid; + SECOidData *oiddata; + SECHashObject *digobj; + void *digcx; + int i, digcnt; + + if (digestalgs == NULL) + return SECSuccess; + + /* + * Count the algorithms. + */ + digcnt = 0; + while (digestalgs[digcnt] != NULL) + digcnt++; + + /* + * No algorithms means no work to do. + * This is not expected, so cause an assert. + * But if it does happen, just act as if there were + * no algorithms specified. + */ + PORT_Assert (digcnt != 0); + if (digcnt == 0) + return SECSuccess; + + p7dcx->worker.digcxs = (void**)PORT_ArenaAlloc (p7dcx->tmp_poolp, + digcnt * sizeof (void *)); + p7dcx->worker.digobjs = (SECHashObject**)PORT_ArenaAlloc (p7dcx->tmp_poolp, + digcnt * sizeof (SECHashObject *)); + if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) { + p7dcx->error = SEC_ERROR_NO_MEMORY; + return SECFailure; + } + + p7dcx->worker.depth = depth; + p7dcx->worker.digcnt = 0; + + /* + * Create a digest context for each algorithm. + */ + for (i = 0; i < digcnt; i++) { + algid = digestalgs[i]; + oiddata = SECOID_FindOID(&(algid->algorithm)); + if (oiddata == NULL) { + digobj = NULL; + } else { + switch (oiddata->offset) { + case SEC_OID_MD2: + digobj = &SECHashObjects[HASH_AlgMD2]; + break; + case SEC_OID_MD5: + digobj = &SECHashObjects[HASH_AlgMD5]; + break; + case SEC_OID_SHA1: + digobj = &SECHashObjects[HASH_AlgSHA1]; + break; + default: + digobj = NULL; + break; + } + } + + /* + * Skip any algorithm we do not even recognize; obviously, + * this could be a problem, but if it is critical then the + * result will just be that the signature does not verify. + * We do not necessarily want to error out here, because + * the particular algorithm may not actually be important, + * but we cannot know that until later. + */ + if (digobj == NULL) { + p7dcx->worker.digcnt--; + continue; + } + + digcx = (* digobj->create)(); + if (digcx != NULL) { + (* digobj->begin) (digcx); + p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj; + p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx; + p7dcx->worker.digcnt++; + } + } + + if (p7dcx->worker.digcnt != 0) + SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, + sec_pkcs7_decoder_filter, + p7dcx, + (PRBool)(p7dcx->cb != NULL)); + return SECSuccess; +} + + +/* + * Close out all of the digest contexts, storing the results in "digestsp". + */ +static SECStatus +sec_pkcs7_decoder_finish_digests (SEC_PKCS7DecoderContext *p7dcx, + PRArenaPool *poolp, + SECItem ***digestsp) +{ + struct sec_pkcs7_decoder_worker *worker; + SECHashObject *digobj; + void *digcx; + SECItem **digests, *digest; + int i; + void *mark; + + /* + * XXX Handling nested contents would mean that there is a chain + * of workers -- one per each level of content. The following + * would want to find the last worker in the chain. + */ + worker = &(p7dcx->worker); + + /* + * If no digests, then we have nothing to do. + */ + if (worker->digcnt == 0) + return SECSuccess; + + /* + * No matter what happens after this, we want to stop filtering. + * XXX If we handle nested contents, we only want to stop filtering + * if we are finishing off the *last* worker. + */ + SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); + + /* + * If we ended up with no contents, just destroy each + * digest context -- they are meaningless and potentially + * confusing, because their presence would imply some content + * was digested. + */ + if (! worker->saw_contents) { + for (i = 0; i < worker->digcnt; i++) { + digcx = worker->digcxs[i]; + digobj = worker->digobjs[i]; + (* digobj->destroy) (digcx, PR_TRUE); + } + return SECSuccess; + } + + mark = PORT_ArenaMark (poolp); + + /* + * Close out each digest context, saving digest away. + */ + digests = + (SECItem**)PORT_ArenaAlloc (poolp,(worker->digcnt+1)*sizeof(SECItem *)); + digest = (SECItem*)PORT_ArenaAlloc (poolp, worker->digcnt*sizeof(SECItem)); + if (digests == NULL || digest == NULL) { + p7dcx->error = PORT_GetError(); + PORT_ArenaRelease (poolp, mark); + return SECFailure; + } + + for (i = 0; i < worker->digcnt; i++, digest++) { + digcx = worker->digcxs[i]; + digobj = worker->digobjs[i]; + + digest->data = (unsigned char*)PORT_ArenaAlloc (poolp, digobj->length); + if (digest->data == NULL) { + p7dcx->error = PORT_GetError(); + PORT_ArenaRelease (poolp, mark); + return SECFailure; + } + + digest->len = digobj->length; + (* digobj->end) (digcx, digest->data, &(digest->len), digest->len); + (* digobj->destroy) (digcx, PR_TRUE); + + digests[i] = digest; + } + digests[i] = NULL; + *digestsp = digests; + + PORT_ArenaUnmark (poolp, mark); + return SECSuccess; +} + +/* + * XXX Need comment explaining following helper function (which is used + * by sec_pkcs7_decoder_start_decrypt). + */ +extern const SEC_ASN1Template SEC_SMIMEKEAParamTemplateAllParams[]; + +static PK11SymKey * +sec_pkcs7_decoder_get_recipient_key (SEC_PKCS7DecoderContext *p7dcx, + SEC_PKCS7RecipientInfo **recipientinfos, + SEC_PKCS7EncryptedContentInfo *enccinfo) +{ + SEC_PKCS7RecipientInfo *ri; + CERTCertificate *cert = NULL; + SECKEYPrivateKey *privkey = NULL; + PK11SymKey *bulkkey; + SECOidTag keyalgtag, bulkalgtag, encalgtag; + PK11SlotInfo *slot; + int i, bulkLength = 0; + + if (recipientinfos == NULL || recipientinfos[0] == NULL) { + p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; + goto no_key_found; + } + + cert = PK11_FindCertAndKeyByRecipientList(&slot,recipientinfos,&ri, + &privkey, p7dcx->pwfn_arg); + if (cert == NULL) { + p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; + goto no_key_found; + } + + ri->cert = cert; /* so we can find it later */ + PORT_Assert(privkey != NULL); + + keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); + encalgtag = SECOID_GetAlgorithmTag (&(ri->keyEncAlg)); + if ((encalgtag != SEC_OID_NETSCAPE_SMIME_KEA) && (keyalgtag != encalgtag)) { + p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH; + goto no_key_found; + } + bulkalgtag = SECOID_GetAlgorithmTag (&(enccinfo->contentEncAlg)); + + switch (encalgtag) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + bulkkey = PK11_PubUnwrapSymKey (privkey, &ri->encKey, + PK11_AlgtagToMechanism (bulkalgtag), + CKA_DECRYPT, 0); + if (bulkkey == NULL) { + p7dcx->error = PORT_GetError(); + PORT_SetError(0); + goto no_key_found; + } + break; + /* ### mwelch -- KEA */ + case SEC_OID_NETSCAPE_SMIME_KEA: + { + SECStatus err; + CK_MECHANISM_TYPE bulkType; + PK11SymKey *tek; + SECKEYPublicKey *senderPubKey; + SEC_PKCS7SMIMEKEAParameters keaParams; + + (void) memset(&keaParams, 0, sizeof(keaParams)); + + /* Decode the KEA algorithm parameters. */ + err = SEC_ASN1DecodeItem(NULL, + &keaParams, + SEC_SMIMEKEAParamTemplateAllParams, + &(ri->keyEncAlg.parameters)); + if (err != SECSuccess) + { + p7dcx->error = err; + PORT_SetError(0); + goto no_key_found; + } + + + /* We just got key data, no key structure. So, we + create one. */ + senderPubKey = + PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data, + keaParams.originatorKEAKey.len); + if (senderPubKey == NULL) + { + p7dcx->error = PORT_GetError(); + PORT_SetError(0); + goto no_key_found; + } + + /* Generate the TEK (token exchange key) which we use + to unwrap the bulk encryption key. */ + tek = PK11_PubDerive(privkey, senderPubKey, + PR_FALSE, + &keaParams.originatorRA, + NULL, + CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, + CKA_WRAP, 0, p7dcx->pwfn_arg); + SECKEY_DestroyPublicKey(senderPubKey); + + if (tek == NULL) + { + p7dcx->error = PORT_GetError(); + PORT_SetError(0); + goto no_key_found; + } + + /* Now that we have the TEK, unwrap the bulk key + with which to decrypt the message. We have to + do one of two different things depending on + whether Skipjack was used for bulk encryption + of the message. */ + bulkType = PK11_AlgtagToMechanism (bulkalgtag); + switch(bulkType) + { + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + /* Skipjack is being used as the bulk encryption algorithm.*/ + /* Unwrap the bulk key. */ + bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, + NULL, &ri->encKey, + CKM_SKIPJACK_CBC64, + CKA_DECRYPT, 0); + break; + default: + /* Skipjack was not used for bulk encryption of this + message. Use Skipjack CBC64, with the nonSkipjackIV + part of the KEA key parameters, to decrypt + the bulk key. If we got a parameter indicating that the + bulk key size is different than the encrypted key size, + pass in the real key size. */ + + /* Check for specified bulk key length (unspecified implies + that the bulk key length is the same as encrypted length) */ + if (keaParams.bulkKeySize.len > 0) + { + p7dcx->error = SEC_ASN1DecodeItem(NULL, &bulkLength, + SEC_IntegerTemplate, + &keaParams.bulkKeySize); + } + + if (p7dcx->error != SECSuccess) + goto no_key_found; + + bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64, + &keaParams.nonSkipjackIV, + &ri->encKey, + bulkType, + CKA_DECRYPT, bulkLength); + } + + + if (bulkkey == NULL) + { + p7dcx->error = PORT_GetError(); + PORT_SetError(0); + goto no_key_found; + } + break; + } + default: + p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG; + goto no_key_found; + } + + return bulkkey; + +no_key_found: + if (privkey != NULL) + SECKEY_DestroyPrivateKey (privkey); + + return NULL; +} + +/* + * XXX The following comment is old -- the function used to only handle + * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData + * as well (and it had all of the code of the helper function above + * built into it), though the comment was left as is. Fix it... + * + * We are just about to decode the content of an EnvelopedData. + * Set up a decryption context so we can decrypt as we go. + * Presumably we are one of the recipients listed in "recipientinfos". + * (XXX And if we are not, or if we have trouble, what should we do? + * It would be nice to let the decoding still work. Maybe it should + * be an error if there is a content callback, but not an error otherwise?) + * The encryption key and related information can be found in "enccinfo". + */ +static SECStatus +sec_pkcs7_decoder_start_decrypt (SEC_PKCS7DecoderContext *p7dcx, int depth, + SEC_PKCS7RecipientInfo **recipientinfos, + SEC_PKCS7EncryptedContentInfo *enccinfo, + PK11SymKey **copy_key_for_signature) +{ + PK11SymKey *bulkkey = NULL; + sec_PKCS7CipherObject *decryptobj; + + /* + * If a callback is supplied to retrieve the encryption key, + * for instance, for Encrypted Content infos, then retrieve + * the bulkkey from the callback. Otherwise, assume that + * we are processing Enveloped or SignedAndEnveloped data + * content infos. + * + * XXX Put an assert here? + */ + if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) { + if (p7dcx->dkcb != NULL) { + bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg, + &(enccinfo->contentEncAlg)); + } + enccinfo->keysize = 0; + } else { + bulkkey = sec_pkcs7_decoder_get_recipient_key (p7dcx, recipientinfos, + enccinfo); + if (bulkkey == NULL) goto no_decryption; + enccinfo->keysize = PK11_GetKeyStrength(bulkkey, + &(enccinfo->contentEncAlg)); + + } + + /* + * XXX I think following should set error in p7dcx and clear set error + * (as used to be done here, or as is done in get_receipient_key above. + */ + if(bulkkey == NULL) { + goto no_decryption; + } + + /* + * We want to make sure decryption is allowed. This is done via + * a callback specified in SEC_PKCS7DecoderStart(). + */ + if (p7dcx->decrypt_allowed_cb) { + if ((*p7dcx->decrypt_allowed_cb) (&(enccinfo->contentEncAlg), + bulkkey) == PR_FALSE) { + p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED; + goto no_decryption; + } + } else { + p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED; + goto no_decryption; + } + + /* + * When decrypting a signedAndEnvelopedData, the signature also has + * to be decrypted with the bulk encryption key; to avoid having to + * get it all over again later (and do another potentially expensive + * RSA operation), copy it for later signature verification to use. + */ + if (copy_key_for_signature != NULL) + *copy_key_for_signature = PK11_ReferenceSymKey (bulkkey); + + /* + * Now we have the bulk encryption key (in bulkkey) and the + * the algorithm (in enccinfo->contentEncAlg). Using those, + * create a decryption context. + */ + decryptobj = sec_PKCS7CreateDecryptObject (bulkkey, + &(enccinfo->contentEncAlg)); + + /* + * For PKCS5 Encryption Algorithms, the bulkkey is actually a different + * structure. Therefore, we need to set the bulkkey to the actual key + * prior to freeing it. + */ + if ( SEC_PKCS5IsAlgorithmPBEAlg(&(enccinfo->contentEncAlg)) && bulkkey ) { + SEC_PKCS5KeyAndPassword *keyPwd = (SEC_PKCS5KeyAndPassword *)bulkkey; + bulkkey = keyPwd->key; + } + + /* + * We are done with (this) bulkkey now. + */ + PK11_FreeSymKey (bulkkey); + + if (decryptobj == NULL) { + p7dcx->error = PORT_GetError(); + PORT_SetError(0); + goto no_decryption; + } + + SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, + sec_pkcs7_decoder_filter, + p7dcx, + (PRBool)(p7dcx->cb != NULL)); + + p7dcx->worker.depth = depth; + p7dcx->worker.decryptobj = decryptobj; + + return SECSuccess; + +no_decryption: + /* + * For some reason (error set already, if appropriate), we cannot + * decrypt the content. I am not sure what exactly is the right + * thing to do here; in some cases we want to just stop, and in + * others we want to let the decoding finish even though we cannot + * decrypt the content. My current thinking is that if the caller + * set up a content callback, then they are really interested in + * getting (decrypted) content, and if they cannot they will want + * to know about it. However, if no callback was specified, then + * maybe it is not important that the decryption failed. + */ + if (p7dcx->cb != NULL) + return SECFailure; + else + return SECSuccess; /* Let the decoding continue. */ +} + + +static SECStatus +sec_pkcs7_decoder_finish_decrypt (SEC_PKCS7DecoderContext *p7dcx, + PRArenaPool *poolp, + SEC_PKCS7EncryptedContentInfo *enccinfo) +{ + struct sec_pkcs7_decoder_worker *worker; + + /* + * XXX Handling nested contents would mean that there is a chain + * of workers -- one per each level of content. The following + * would want to find the last worker in the chain. + */ + worker = &(p7dcx->worker); + + /* + * If no decryption context, then we have nothing to do. + */ + if (worker->decryptobj == NULL) + return SECSuccess; + + /* + * No matter what happens after this, we want to stop filtering. + * XXX If we handle nested contents, we only want to stop filtering + * if we are finishing off the *last* worker. + */ + SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); + + /* + * Handle the last block. + */ + sec_pkcs7_decoder_work_data (p7dcx, worker, NULL, 0, PR_TRUE); + + /* + * All done, destroy it. + */ + sec_PKCS7DestroyDecryptObject (worker->decryptobj); + + return SECSuccess; +} + + +static void +sec_pkcs7_decoder_notify (void *arg, PRBool before, void *dest, int depth) +{ + SEC_PKCS7DecoderContext *p7dcx; + SEC_PKCS7ContentInfo *cinfo; + SEC_PKCS7SignedData *sigd; + SEC_PKCS7EnvelopedData *envd; + SEC_PKCS7SignedAndEnvelopedData *saed; + SEC_PKCS7EncryptedData *encd; + SEC_PKCS7DigestedData *digd; + PRBool after; + SECStatus rv; + + /* + * Just to make the code easier to read, create an "after" variable + * that is equivalent to "not before". + * (This used to be just the statement "after = !before", but that + * causes a warning on the mac; to avoid that, we do it the long way.) + */ + if (before) + after = PR_FALSE; + else + after = PR_TRUE; + + p7dcx = (SEC_PKCS7DecoderContext*)arg; + cinfo = p7dcx->cinfo; + + if (cinfo->contentTypeTag == NULL) { + if (after && dest == &(cinfo->contentType)) + cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType)); + return; + } + + switch (cinfo->contentTypeTag->offset) { + case SEC_OID_PKCS7_SIGNED_DATA: + sigd = cinfo->content.signedData; + if (sigd == NULL) + break; + + if (sigd->contentInfo.contentTypeTag == NULL) { + if (after && dest == &(sigd->contentInfo.contentType)) + sigd->contentInfo.contentTypeTag = + SECOID_FindOID(&(sigd->contentInfo.contentType)); + break; + } + + /* + * We only set up a filtering digest if the content is + * plain DATA; anything else needs more work because a + * second pass is required to produce a DER encoding from + * an input that can be BER encoded. (This is a requirement + * of PKCS7 that is unfortunate, but there you have it.) + * + * XXX Also, since we stop here if this is not DATA, the + * inner content is not getting processed at all. Someday + * we may want to fix that. + */ + if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) { + /* XXX Set an error in p7dcx->error */ + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + break; + } + + /* + * Just before the content, we want to set up a digest context + * for each digest algorithm listed, and start a filter which + * will run all of the contents bytes through that digest. + */ + if (before && dest == &(sigd->contentInfo.content)) { + rv = sec_pkcs7_decoder_start_digests (p7dcx, depth, + sigd->digestAlgorithms); + if (rv != SECSuccess) + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + + break; + } + + /* + * XXX To handle nested types, here is where we would want + * to check for inner boundaries that need handling. + */ + + /* + * Are we done? + */ + if (after && dest == &(sigd->contentInfo.content)) { + /* + * Close out the digest contexts. We ignore any error + * because we are stopping anyway; the error status left + * behind in p7dcx will be seen by outer functions. + */ + (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp, + &(sigd->digests)); + + /* + * XXX To handle nested contents, we would need to remove + * the worker from the chain (and free it). + */ + + /* + * Stop notify. + */ + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + } + break; + + case SEC_OID_PKCS7_ENVELOPED_DATA: + envd = cinfo->content.envelopedData; + if (envd == NULL) + break; + + if (envd->encContentInfo.contentTypeTag == NULL) { + if (after && dest == &(envd->encContentInfo.contentType)) + envd->encContentInfo.contentTypeTag = + SECOID_FindOID(&(envd->encContentInfo.contentType)); + break; + } + + /* + * Just before the content, we want to set up a decryption + * context, and start a filter which will run all of the + * contents bytes through it to determine the plain content. + */ + if (before && dest == &(envd->encContentInfo.encContent)) { + rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, + envd->recipientInfos, + &(envd->encContentInfo), + NULL); + if (rv != SECSuccess) + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + + break; + } + + /* + * Are we done? + */ + if (after && dest == &(envd->encContentInfo.encContent)) { + /* + * Close out the decryption context. We ignore any error + * because we are stopping anyway; the error status left + * behind in p7dcx will be seen by outer functions. + */ + (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, + &(envd->encContentInfo)); + + /* + * XXX To handle nested contents, we would need to remove + * the worker from the chain (and free it). + */ + + /* + * Stop notify. + */ + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + } + break; + + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + saed = cinfo->content.signedAndEnvelopedData; + if (saed == NULL) + break; + + if (saed->encContentInfo.contentTypeTag == NULL) { + if (after && dest == &(saed->encContentInfo.contentType)) + saed->encContentInfo.contentTypeTag = + SECOID_FindOID(&(saed->encContentInfo.contentType)); + break; + } + + /* + * Just before the content, we want to set up a decryption + * context *and* digest contexts, and start a filter which + * will run all of the contents bytes through both. + */ + if (before && dest == &(saed->encContentInfo.encContent)) { + rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, + saed->recipientInfos, + &(saed->encContentInfo), + &(saed->sigKey)); + if (rv == SECSuccess) + rv = sec_pkcs7_decoder_start_digests (p7dcx, depth, + saed->digestAlgorithms); + if (rv != SECSuccess) + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + + break; + } + + /* + * Are we done? + */ + if (after && dest == &(saed->encContentInfo.encContent)) { + /* + * Close out the decryption and digests contexts. + * We ignore any errors because we are stopping anyway; + * the error status left behind in p7dcx will be seen by + * outer functions. + * + * Note that the decrypt stuff must be called first; + * it may have a last buffer to do which in turn has + * to be added to the digest. + */ + (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, + &(saed->encContentInfo)); + (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp, + &(saed->digests)); + + /* + * XXX To handle nested contents, we would need to remove + * the worker from the chain (and free it). + */ + + /* + * Stop notify. + */ + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + } + break; + + case SEC_OID_PKCS7_DIGESTED_DATA: + digd = cinfo->content.digestedData; + + /* + * XXX Want to do the digest or not? Maybe future enhancement... + */ + if (before && dest == &(digd->contentInfo.content.data)) { + SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter, + p7dcx, + (PRBool)(p7dcx->cb != NULL)); + break; + } + + /* + * Are we done? + */ + if (after && dest == &(digd->contentInfo.content.data)) { + SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); + } + break; + + case SEC_OID_PKCS7_ENCRYPTED_DATA: + encd = cinfo->content.encryptedData; + + /* + * XXX If the decryption key callback is set, we want to start + * the decryption. If the callback is not set, we will treat the + * content as plain data, since we do not have the key. + * + * Is this the proper thing to do? + */ + if (before && dest == &(encd->encContentInfo.encContent)) { + /* + * Start the encryption process if the decryption key callback + * is present. Otherwise, treat the content like plain data. + */ + rv = SECSuccess; + if (p7dcx->dkcb != NULL) { + rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, NULL, + &(encd->encContentInfo), + NULL); + } + + if (rv != SECSuccess) + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + + break; + } + + /* + * Are we done? + */ + if (after && dest == &(encd->encContentInfo.encContent)) { + /* + * Close out the decryption context. We ignore any error + * because we are stopping anyway; the error status left + * behind in p7dcx will be seen by outer functions. + */ + (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, + &(encd->encContentInfo)); + + /* + * Stop notify. + */ + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + } + break; + + case SEC_OID_PKCS7_DATA: + /* + * If a output callback has been specified, we want to set the filter + * to call the callback. This is taken care of in + * sec_pkcs7_decoder_start_decrypt() or + * sec_pkcs7_decoder_start_digests() for the other content types. + */ + + if (before && dest == &(cinfo->content.data)) { + + /* + * Set the filter proc up. + */ + SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, + sec_pkcs7_decoder_filter, + p7dcx, + (PRBool)(p7dcx->cb != NULL)); + break; + } + + if (after && dest == &(cinfo->content.data)) { + /* + * Time to clean up after ourself, stop the Notify and Filter + * procedures. + */ + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); + } + break; + + default: + SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); + break; + } +} + + +SEC_PKCS7DecoderContext * +SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg, + SECKEYGetPasswordKey pwfn, void *pwfn_arg, + SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, + void *decrypt_key_cb_arg, + SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb) +{ + SEC_PKCS7DecoderContext *p7dcx; + SEC_ASN1DecoderContext *dcx; + SEC_PKCS7ContentInfo *cinfo; + PRArenaPool *poolp; + + poolp = PORT_NewArena (1024); /* XXX what is right value? */ + if (poolp == NULL) + return NULL; + + cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); + if (cinfo == NULL) { + PORT_FreeArena (poolp, PR_FALSE); + return NULL; + } + + cinfo->poolp = poolp; + cinfo->pwfn = pwfn; + cinfo->pwfn_arg = pwfn_arg; + cinfo->created = PR_FALSE; + cinfo->refCount = 1; + + p7dcx = + (SEC_PKCS7DecoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7DecoderContext)); + if (p7dcx == NULL) { + PORT_FreeArena (poolp, PR_FALSE); + return NULL; + } + + p7dcx->tmp_poolp = PORT_NewArena (1024); /* XXX what is right value? */ + if (p7dcx->tmp_poolp == NULL) { + PORT_Free (p7dcx); + PORT_FreeArena (poolp, PR_FALSE); + return NULL; + } + + dcx = SEC_ASN1DecoderStart (poolp, cinfo, sec_PKCS7ContentInfoTemplate); + if (dcx == NULL) { + PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); + PORT_Free (p7dcx); + PORT_FreeArena (poolp, PR_FALSE); + return NULL; + } + + SEC_ASN1DecoderSetNotifyProc (dcx, sec_pkcs7_decoder_notify, p7dcx); + + p7dcx->dcx = dcx; + p7dcx->cinfo = cinfo; + p7dcx->cb = cb; + p7dcx->cb_arg = cb_arg; + p7dcx->pwfn = pwfn; + p7dcx->pwfn_arg = pwfn_arg; + p7dcx->dkcb = decrypt_key_cb; + p7dcx->dkcb_arg = decrypt_key_cb_arg; + p7dcx->decrypt_allowed_cb = decrypt_allowed_cb; + + return p7dcx; +} + + +/* + * Do the next chunk of PKCS7 decoding. If there is a problem, set + * an error and return a failure status. Note that in the case of + * an error, this routine is still prepared to be called again and + * again in case that is the easiest route for our caller to take. + * We simply detect it and do not do anything except keep setting + * that error in case our caller has not noticed it yet... + */ +SECStatus +SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx, + const char *buf, unsigned long len) +{ + if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) { + PORT_Assert (p7dcx->error == 0); + if (p7dcx->error == 0) { + if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) { + p7dcx->error = PORT_GetError(); + PORT_Assert (p7dcx->error); + if (p7dcx->error == 0) + p7dcx->error = -1; + } + } + } + + if (p7dcx->error) { + if (p7dcx->dcx != NULL) { + (void) SEC_ASN1DecoderFinish (p7dcx->dcx); + p7dcx->dcx = NULL; + } + if (p7dcx->cinfo != NULL) { + SEC_PKCS7DestroyContentInfo (p7dcx->cinfo); + p7dcx->cinfo = NULL; + } + PORT_SetError (p7dcx->error); + return SECFailure; + } + + return SECSuccess; +} + + +SEC_PKCS7ContentInfo * +SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx) +{ + SEC_PKCS7ContentInfo *cinfo; + + cinfo = p7dcx->cinfo; + if (p7dcx->dcx != NULL) { + if (SEC_ASN1DecoderFinish (p7dcx->dcx) != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + cinfo = NULL; + } + } + PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); + PORT_Free (p7dcx); + return cinfo; +} + + +SEC_PKCS7ContentInfo * +SEC_PKCS7DecodeItem(SECItem *p7item, + SEC_PKCS7DecoderContentCallback cb, void *cb_arg, + SECKEYGetPasswordKey pwfn, void *pwfn_arg, + SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, + void *decrypt_key_cb_arg, + SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb) +{ + SEC_PKCS7DecoderContext *p7dcx; + + p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, + decrypt_key_cb_arg, decrypt_allowed_cb); + (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len); + return SEC_PKCS7DecoderFinish(p7dcx); +} + + +/* + * If the thing contains any certs or crls return true; false otherwise. + */ +PRBool +SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo) +{ + SECOidTag kind; + SECItem **certs; + CERTSignedCrl **crls; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + default: + case SEC_OID_PKCS7_DATA: + case SEC_OID_PKCS7_DIGESTED_DATA: + case SEC_OID_PKCS7_ENVELOPED_DATA: + case SEC_OID_PKCS7_ENCRYPTED_DATA: + return PR_FALSE; + case SEC_OID_PKCS7_SIGNED_DATA: + certs = cinfo->content.signedData->rawCerts; + crls = cinfo->content.signedData->crls; + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + certs = cinfo->content.signedAndEnvelopedData->rawCerts; + crls = cinfo->content.signedAndEnvelopedData->crls; + break; + } + + /* + * I know this could be collapsed, but I was in a mood to be explicit. + */ + if (certs != NULL && certs[0] != NULL) + return PR_TRUE; + else if (crls != NULL && crls[0] != NULL) + return PR_TRUE; + else + return PR_FALSE; +} + +/* return the content length...could use GetContent, however we + * need the encrypted content length + */ +PRBool +SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen) +{ + SECItem *item = NULL; + + if(cinfo == NULL) { + return PR_TRUE; + } + + switch(SEC_PKCS7ContentType(cinfo)) + { + case SEC_OID_PKCS7_DATA: + item = cinfo->content.data; + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + item = &cinfo->content.encryptedData->encContentInfo.encContent; + break; + default: + /* add other types */ + return PR_FALSE; + } + + if(!item) { + return PR_TRUE; + } else if(item->len <= minLen) { + return PR_TRUE; + } + + return PR_FALSE; +} + + +PRBool +SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo) +{ + SECOidTag kind; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + default: + case SEC_OID_PKCS7_DATA: + case SEC_OID_PKCS7_DIGESTED_DATA: + case SEC_OID_PKCS7_SIGNED_DATA: + return PR_FALSE; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + case SEC_OID_PKCS7_ENVELOPED_DATA: + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + return PR_TRUE; + } +} + + +/* + * If the PKCS7 content has a signature (not just *could* have a signature) + * return true; false otherwise. This can/should be called before calling + * VerifySignature, which will always indicate failure if no signature is + * present, but that does not mean there even was a signature! + * Note that the content itself can be empty (detached content was sent + * another way); it is the presence of the signature that matters. + */ +PRBool +SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo) +{ + SECOidTag kind; + SEC_PKCS7SignerInfo **signerinfos; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + default: + case SEC_OID_PKCS7_DATA: + case SEC_OID_PKCS7_DIGESTED_DATA: + case SEC_OID_PKCS7_ENVELOPED_DATA: + case SEC_OID_PKCS7_ENCRYPTED_DATA: + return PR_FALSE; + case SEC_OID_PKCS7_SIGNED_DATA: + signerinfos = cinfo->content.signedData->signerInfos; + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; + break; + } + + /* + * I know this could be collapsed; but I kind of think it will get + * more complicated before I am finished, so... + */ + if (signerinfos != NULL && signerinfos[0] != NULL) + return PR_TRUE; + else + return PR_FALSE; +} + + +/* + * SEC_PKCS7ContentVerifySignature + * Look at a PKCS7 contentInfo and check if the signature is good. + * The digest was either calculated earlier (and is stored in the + * contentInfo itself) or is passed in via "detached_digest". + * + * The verification checks that the signing cert is valid and trusted + * for the purpose specified by "certusage". + * + * In addition, if "keepcerts" is true, add any new certificates found + * into our local database. + * + * XXX Each place which returns PR_FALSE should be sure to have a good + * error set for inspection by the caller. Alternatively, we could create + * an enumeration of success and each type of failure and return that + * instead of a boolean. For now, the default in a bad situation is to + * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE. But this should be + * reviewed; better (more specific) errors should be possible (to distinguish + * a signature failure from a badly-formed pkcs7 signedData, for example). + * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE, + * but that has a less helpful error string associated with it right now; + * if/when that changes, review and change these as needed. + * + * XXX This is broken wrt signedAndEnvelopedData. In that case, the + * message digest is doubly encrypted -- first encrypted with the signer + * private key but then again encrypted with the bulk encryption key used + * to encrypt the content. So before we can pass the digest to VerifyDigest, + * we need to decrypt it with the bulk encryption key. Also, in this case, + * there should be NO authenticatedAttributes (signerinfo->authAttr should + * be NULL). + */ +static PRBool +sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo, + SECCertUsage certusage, + SECItem *detached_digest, + HASH_HashType digest_type, + PRBool keepcerts) +{ + SECAlgorithmID **digestalgs, *bulkid; + SECItem *digest; + SECItem **digests; + SECItem **rawcerts; + CERTSignedCrl **crls; + SEC_PKCS7SignerInfo **signerinfos, *signerinfo; + CERTCertificate *cert, **certs; + PRBool goodsig; + CERTCertDBHandle local_certdb, *certdb, *defaultdb; + SECOidData *algiddata; + int i, certcount; + SECKEYPublicKey *publickey; + SECItem *content_type; + PK11SymKey *sigkey; + SECItem *utc_stime; + int64 stime; + SECStatus rv; + + /* + * Everything needed in order to "goto done" safely. + */ + goodsig = PR_FALSE; + certcount = 0; + cert = NULL; + certs = NULL; + certdb = NULL; + defaultdb = CERT_GetDefaultCertDB(); + publickey = NULL; + + if (! SEC_PKCS7ContentIsSigned(cinfo)) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + PORT_Assert (cinfo->contentTypeTag != NULL); + + switch (cinfo->contentTypeTag->offset) { + default: + case SEC_OID_PKCS7_DATA: + case SEC_OID_PKCS7_DIGESTED_DATA: + case SEC_OID_PKCS7_ENVELOPED_DATA: + case SEC_OID_PKCS7_ENCRYPTED_DATA: + /* Could only get here if SEC_PKCS7ContentIsSigned is broken. */ + PORT_Assert (0); + case SEC_OID_PKCS7_SIGNED_DATA: + { + SEC_PKCS7SignedData *sdp; + + sdp = cinfo->content.signedData; + digestalgs = sdp->digestAlgorithms; + digests = sdp->digests; + rawcerts = sdp->rawCerts; + crls = sdp->crls; + signerinfos = sdp->signerInfos; + content_type = &(sdp->contentInfo.contentType); + sigkey = NULL; + bulkid = NULL; + } + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saedp; + + saedp = cinfo->content.signedAndEnvelopedData; + digestalgs = saedp->digestAlgorithms; + digests = saedp->digests; + rawcerts = saedp->rawCerts; + crls = saedp->crls; + signerinfos = saedp->signerInfos; + content_type = &(saedp->encContentInfo.contentType); + sigkey = saedp->sigKey; + bulkid = &(saedp->encContentInfo.contentEncAlg); + } + break; + } + + if ((signerinfos == NULL) || (signerinfos[0] == NULL)) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + /* + * XXX Need to handle multiple signatures; checking them is easy, + * but what should be the semantics here (like, return value)? + */ + if (signerinfos[1] != NULL) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + signerinfo = signerinfos[0]; + + /* + * XXX I would like to just pass the issuerAndSN, along with the rawcerts + * and crls, to some function that did all of this certificate stuff + * (open/close the database if necessary, verifying the certs, etc.) + * and gave me back a cert pointer if all was good. + */ + certdb = defaultdb; + if (certdb == NULL) { + if (CERT_OpenCertDBFilename (&local_certdb, NULL, + (PRBool)!keepcerts) != SECSuccess) + goto done; + certdb = &local_certdb; + } + + certcount = 0; + if (rawcerts != NULL) { + for (; rawcerts[certcount] != NULL; certcount++) { + /* just counting */ + } + } + + /* + * Note that the result of this is that each cert in "certs" + * needs to be destroyed. + */ + rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs, + keepcerts, PR_FALSE, NULL); + if ( rv != SECSuccess ) { + goto done; + } + + /* + * This cert will also need to be freed, but since we save it + * in signerinfo for later, we do not want to destroy it when + * we leave this function -- we let the clean-up of the entire + * cinfo structure later do the destroy of this cert. + */ + cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN); + if (cert == NULL) { + goto done; + } + + signerinfo->cert = cert; + + /* + * Get and convert the signing time; if available, it will be used + * both on the cert verification and for importing the sender + * email profile. + */ + utc_stime = SEC_PKCS7GetSigningTime (cinfo); + if (utc_stime != NULL) { + if (DER_UTCTimeToTime (&stime, utc_stime) != SECSuccess) + utc_stime = NULL; /* conversion failed, so pretend none */ + } + + /* + * XXX This uses the signing time, if available. Additionally, we + * might want to, if there is no signing time, get the message time + * from the mail header itself, and use that. That would require + * a change to our interface though, and for S/MIME callers to pass + * in a time (and for non-S/MIME callers to pass in nothing, or + * maybe make them pass in the current time, always?). + */ + if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, + utc_stime != NULL ? stime : PR_Now(), + cinfo->pwfn_arg, NULL) != SECSuccess) + { + /* + * XXX Give the user an option to check the signature anyway? + * If we want to do this, need to give a way to leave and display + * some dialog and get the answer and come back through (or do + * the rest of what we do below elsewhere, maybe by putting it + * in a function that we call below and could call from a dialog + * finish handler). + */ + goto savecert; + } + + publickey = CERT_ExtractPublicKey (cert); + if (publickey == NULL) + goto done; + + /* + * XXX No! If digests is empty, see if we can create it now by + * digesting the contents. This is necessary if we want to allow + * somebody to do a simple decode (without filtering, etc.) and + * then later call us here to do the verification. + * OR, we can just specify that the interface to this routine + * *requires* that the digest(s) be done before calling and either + * stashed in the struct itself or passed in explicitly (as would + * be done for detached contents). + */ + if ((digests == NULL || digests[0] == NULL) + && (detached_digest == NULL || detached_digest->data == NULL)) + goto done; + + /* + * Find and confirm digest algorithm. + */ + algiddata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); + + if (detached_digest != NULL) { + switch (digest_type) { + default: + case HASH_AlgNULL: + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + case HASH_AlgMD2: + PORT_Assert (detached_digest->len == MD2_LENGTH); + if (algiddata->offset != SEC_OID_MD2) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + break; + case HASH_AlgMD5: + PORT_Assert (detached_digest->len == MD5_LENGTH); + if (algiddata->offset != SEC_OID_MD5) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + break; + case HASH_AlgSHA1: + PORT_Assert (detached_digest->len == SHA1_LENGTH); + if (algiddata->offset != SEC_OID_SHA1) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + break; + } + digest = detached_digest; + } else { + PORT_Assert (digestalgs != NULL && digestalgs[0] != NULL); + if (digestalgs == NULL || digestalgs[0] == NULL) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + /* + * pick digest matching signerinfo->digestAlg from digests + */ + if (algiddata == NULL) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + for (i = 0; digestalgs[i] != NULL; i++) { + if (SECOID_FindOID (&(digestalgs[i]->algorithm)) == algiddata) + break; + } + if (digestalgs[i] == NULL) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + digest = digests[i]; + } + + /* + * XXX This may not be the right set of algorithms to check. + * I'd prefer to trust that just calling VFY_Verify{Data,Digest} + * would do the right thing (and set an error if it could not); + * then additional algorithms could be handled by that code + * and we would Just Work. So this check should just be removed, + * but not until the VFY code is better at setting errors. + */ + algiddata = SECOID_FindOID (&(signerinfo->digestEncAlg.algorithm)); + if (algiddata == NULL || + ((algiddata->offset != SEC_OID_PKCS1_RSA_ENCRYPTION) && + (algiddata->offset != SEC_OID_ANSIX9_DSA_SIGNATURE))) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + if (signerinfo->authAttr != NULL) { + SEC_PKCS7Attribute *attr; + SECItem *value; + SECItem encoded_attrs; + + /* + * We have a sigkey only for signedAndEnvelopedData, which is + * not supposed to have any authenticated attributes. + */ + if (sigkey != NULL) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + /* + * PKCS #7 says that if there are any authenticated attributes, + * then there must be one for content type which matches the + * content type of the content being signed, and there must + * be one for message digest which matches our message digest. + * So check these things first. + * XXX Might be nice to have a compare-attribute-value function + * which could collapse the following nicely. + */ + attr = sec_PKCS7FindAttribute (signerinfo->authAttr, + SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE); + value = sec_PKCS7AttributeValue (attr); + if (value == NULL || value->len != content_type->len) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + if (PORT_Memcmp (value->data, content_type->data, value->len) != 0) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + attr = sec_PKCS7FindAttribute (signerinfo->authAttr, + SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE); + value = sec_PKCS7AttributeValue (attr); + if (value == NULL || value->len != digest->len) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + if (PORT_Memcmp (value->data, digest->data, value->len) != 0) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + /* + * Okay, we met the constraints of the basic attributes. + * Now check the signature, which is based on a digest of + * the DER-encoded authenticated attributes. So, first we + * encode and then we digest/verify. + */ + encoded_attrs.data = NULL; + encoded_attrs.len = 0; + if (sec_PKCS7EncodeAttributes (NULL, &encoded_attrs, + &(signerinfo->authAttr)) == NULL) + goto done; + + if (encoded_attrs.data == NULL || encoded_attrs.len == 0) { + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + goodsig = (PRBool)(VFY_VerifyData (encoded_attrs.data, + encoded_attrs.len, + publickey, &(signerinfo->encDigest), + SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)), + cinfo->pwfn_arg) == SECSuccess); + PORT_Free (encoded_attrs.data); + } else { + SECItem *sig; + SECItem holder; + SECStatus rv; + + /* + * No authenticated attributes. + * The signature is based on the plain message digest. + */ + + sig = &(signerinfo->encDigest); + if (sig->len == 0) { /* bad signature */ + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + goto done; + } + + if (sigkey != NULL) { + sec_PKCS7CipherObject *decryptobj; + unsigned int buflen; + + /* + * For signedAndEnvelopedData, we first must decrypt the encrypted + * digest with the bulk encryption key. The result is the normal + * encrypted digest (aka the signature). + */ + decryptobj = sec_PKCS7CreateDecryptObject (sigkey, bulkid); + if (decryptobj == NULL) + goto done; + + buflen = sec_PKCS7DecryptLength (decryptobj, sig->len, PR_TRUE); + PORT_Assert (buflen); + if (buflen == 0) { /* something is wrong */ + sec_PKCS7DestroyDecryptObject (decryptobj); + goto done; + } + + holder.data = (unsigned char*)PORT_Alloc (buflen); + if (holder.data == NULL) { + sec_PKCS7DestroyDecryptObject (decryptobj); + goto done; + } + + rv = sec_PKCS7Decrypt (decryptobj, holder.data, &holder.len, buflen, + sig->data, sig->len, PR_TRUE); + if (rv != SECSuccess) { + sec_PKCS7DestroyDecryptObject (decryptobj); + goto done; + } + + sig = &holder; + } + + goodsig = (PRBool)(VFY_VerifyDigest (digest, publickey, sig, + SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)), + cinfo->pwfn_arg) + == SECSuccess); + + if (sigkey != NULL) { + PORT_Assert (sig == &holder); + PORT_ZFree (holder.data, holder.len); + } + } + + if (! goodsig) { + /* + * XXX Change the generic error into our specific one, because + * in that case we get a better explanation out of the Security + * Advisor. This is really a bug in our error strings (the + * "generic" error has a lousy/wrong message associated with it + * which assumes the signature verification was done for the + * purposes of checking the issuer signature on a certificate) + * but this is at least an easy workaround and/or in the + * Security Advisor, which specifically checks for the error + * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation + * in that case but does not similarly check for + * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would + * probably say the wrong thing in the case that it *was* the + * certificate signature check that failed during the cert + * verification done above. Our error handling is really a mess. + */ + if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE) + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); + } + +savecert: + /* + * Only save the smime profile if we are checking an email message and + * the cert has an email address in it. + */ + if ( ( cert->emailAddr != NULL ) && + ( ( certusage == certUsageEmailSigner ) || + ( certusage == certUsageEmailRecipient ) ) ) { + SECItem *profile = NULL; + int save_error; + + /* + * Remember the current error set because we do not care about + * anything set by the functions we are about to call. + */ + save_error = PORT_GetError(); + + if (goodsig && (signerinfo->authAttr != NULL)) { + /* + * If the signature is good, then we can save the S/MIME profile, + * if we have one. + */ + SEC_PKCS7Attribute *attr; + + attr = sec_PKCS7FindAttribute (signerinfo->authAttr, + SEC_OID_PKCS9_SMIME_CAPABILITIES, + PR_TRUE); + profile = sec_PKCS7AttributeValue (attr); + } + + rv = CERT_SaveSMimeProfile (cert, profile, utc_stime); + + /* + * Restore the saved error in case the calls above set a new + * one that we do not actually care about. + */ + PORT_SetError (save_error); + + /* + * XXX Failure is not indicated anywhere -- the signature + * verification itself is unaffected by whether or not the + * profile was successfully saved. + */ + } + + +done: + + /* + * See comment above about why we do not want to destroy cert + * itself here. + */ + + if (certs != NULL) + CERT_DestroyCertArray (certs, certcount); + + if (defaultdb == NULL && certdb != NULL) + CERT_ClosePermCertDB (certdb); + + if (publickey != NULL) + SECKEY_DestroyPublicKey (publickey); + + return goodsig; +} + +/* + * SEC_PKCS7VerifySignature + * Look at a PKCS7 contentInfo and check if the signature is good. + * The verification checks that the signing cert is valid and trusted + * for the purpose specified by "certusage". + * + * In addition, if "keepcerts" is true, add any new certificates found + * into our local database. + */ +PRBool +SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo, + SECCertUsage certusage, + PRBool keepcerts) +{ + return sec_pkcs7_verify_signature (cinfo, certusage, + NULL, HASH_AlgNULL, keepcerts); +} + +/* + * SEC_PKCS7VerifyDetachedSignature + * Look at a PKCS7 contentInfo and check if the signature matches + * a passed-in digest (calculated, supposedly, from detached contents). + * The verification checks that the signing cert is valid and trusted + * for the purpose specified by "certusage". + * + * In addition, if "keepcerts" is true, add any new certificates found + * into our local database. + */ +PRBool +SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo, + SECCertUsage certusage, + SECItem *detached_digest, + HASH_HashType digest_type, + PRBool keepcerts) +{ + return sec_pkcs7_verify_signature (cinfo, certusage, + detached_digest, digest_type, + keepcerts); +} + + +/* + * Return the asked-for portion of the name of the signer of a PKCS7 + * signed object. + * + * Returns a pointer to allocated memory, which must be freed. + * A NULL return value is an error. + */ + +#define sec_common_name 1 +#define sec_email_address 2 + +static char * +sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector) +{ + SECOidTag kind; + SEC_PKCS7SignerInfo **signerinfos; + CERTCertificate *signercert; + char *container; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + default: + case SEC_OID_PKCS7_DATA: + case SEC_OID_PKCS7_DIGESTED_DATA: + case SEC_OID_PKCS7_ENVELOPED_DATA: + case SEC_OID_PKCS7_ENCRYPTED_DATA: + PORT_Assert (0); + return NULL; + case SEC_OID_PKCS7_SIGNED_DATA: + { + SEC_PKCS7SignedData *sdp; + + sdp = cinfo->content.signedData; + signerinfos = sdp->signerInfos; + } + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saedp; + + saedp = cinfo->content.signedAndEnvelopedData; + signerinfos = saedp->signerInfos; + } + break; + } + + if (signerinfos == NULL || signerinfos[0] == NULL) + return NULL; + + signercert = signerinfos[0]->cert; + + /* + * No cert there; see if we can find one by calling verify ourselves. + */ + if (signercert == NULL) { + /* + * The cert usage does not matter in this case, because we do not + * actually care about the verification itself, but we have to pick + * some valid usage to pass in. + */ + (void) sec_pkcs7_verify_signature (cinfo, certUsageEmailSigner, + NULL, HASH_AlgNULL, PR_FALSE); + signercert = signerinfos[0]->cert; + if (signercert == NULL) + return NULL; + } + + switch (selector) { + case sec_common_name: + container = CERT_GetCommonName (&signercert->subject); + break; + case sec_email_address: + if(signercert->emailAddr) { + container = PORT_Strdup(signercert->emailAddr); + } else { + container = NULL; + } + break; + default: + PORT_Assert (0); + container = NULL; + break; + } + + return container; +} + +char * +SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo) +{ + return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name); +} + +char * +SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo) +{ + return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address); +} + + +/* + * Return the signing time, in UTCTime format, of a PKCS7 contentInfo. + */ +SECItem * +SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo) +{ + SEC_PKCS7SignerInfo **signerinfos; + SEC_PKCS7Attribute *attr; + + if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) + return NULL; + + signerinfos = cinfo->content.signedData->signerInfos; + + /* + * No signature, or more than one, means no deal. + */ + if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) + return NULL; + + attr = sec_PKCS7FindAttribute (signerinfos[0]->authAttr, + SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); + return sec_PKCS7AttributeValue (attr); +} diff --git a/security/nss/lib/pkcs7/p7encode.c b/security/nss/lib/pkcs7/p7encode.c new file mode 100644 index 000000000..b45d2d916 --- /dev/null +++ b/security/nss/lib/pkcs7/p7encode.c @@ -0,0 +1,1329 @@ +/* + * 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. + */ + +/* + * PKCS7 encoding. + * + * $Id$ + */ + +#include "p7local.h" + +#include "cert.h" +#include "cryptohi.h" +#include "keyhi.h" +#include "secasn1.h" +#include "secoid.h" +#include "secitem.h" +#include "pk11func.h" +#include "secerr.h" + +struct sec_pkcs7_encoder_output { + SEC_PKCS7EncoderOutputCallback outputfn; + void *outputarg; +}; + +struct SEC_PKCS7EncoderContextStr { + SEC_ASN1EncoderContext *ecx; + SEC_PKCS7ContentInfo *cinfo; + struct sec_pkcs7_encoder_output output; + sec_PKCS7CipherObject *encryptobj; + SECHashObject *digestobj; + void *digestcx; +}; + + +/* + * The little output function that the ASN.1 encoder calls to hand + * us bytes which we in turn hand back to our caller (via the callback + * they gave us). + */ +static void +sec_pkcs7_encoder_out(void *arg, const char *buf, unsigned long len, + int depth, SEC_ASN1EncodingPart data_kind) +{ + struct sec_pkcs7_encoder_output *output; + + output = (struct sec_pkcs7_encoder_output*)arg; + output->outputfn (output->outputarg, buf, len); +} + +static sec_PKCS7CipherObject * +sec_pkcs7_encoder_start_encrypt (SEC_PKCS7ContentInfo *cinfo, + PK11SymKey *orig_bulkkey) +{ + SECOidTag kind; + sec_PKCS7CipherObject *encryptobj; + SEC_PKCS7RecipientInfo **recipientinfos, *ri; + SEC_PKCS7EncryptedContentInfo *enccinfo; + SEC_PKCS7SMIMEKEAParameters keaParams; + SECKEYPublicKey *publickey = NULL; + SECKEYPrivateKey *ourPrivKey = NULL; + PK11SymKey *bulkkey; + void *mark, *wincx; + int i; + PRArenaPool *arena = NULL; + unsigned char zero = 0; + + /* Get the context in case we need it below. */ + wincx = cinfo->pwfn_arg; + + /* Clear keaParams, since cleanup code checks the lengths */ + (void) memset(&keaParams, 0, sizeof(keaParams)); + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + default: + case SEC_OID_PKCS7_DATA: + case SEC_OID_PKCS7_DIGESTED_DATA: + case SEC_OID_PKCS7_SIGNED_DATA: + recipientinfos = NULL; + enccinfo = NULL; + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + { + SEC_PKCS7EncryptedData *encdp; + + /* To do EncryptedData we *must* be given a bulk key. */ + PORT_Assert (orig_bulkkey != NULL); + if (orig_bulkkey == NULL) { + /* XXX error? */ + return NULL; + } + + encdp = cinfo->content.encryptedData; + recipientinfos = NULL; + enccinfo = &(encdp->encContentInfo); + } + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + { + SEC_PKCS7EnvelopedData *envdp; + + envdp = cinfo->content.envelopedData; + recipientinfos = envdp->recipientInfos; + enccinfo = &(envdp->encContentInfo); + } + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saedp; + + saedp = cinfo->content.signedAndEnvelopedData; + recipientinfos = saedp->recipientInfos; + enccinfo = &(saedp->encContentInfo); + } + break; + } + + if (enccinfo == NULL) + return NULL; + + bulkkey = orig_bulkkey; + if (bulkkey == NULL) { + CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(enccinfo->encalg); + PK11SlotInfo *slot; + + + slot = PK11_GetBestSlot(type,cinfo->pwfn_arg); + if (slot == NULL) { + return NULL; + } + bulkkey = PK11_KeyGen(slot,type,NULL, enccinfo->keysize/8, + cinfo->pwfn_arg); + PK11_FreeSlot(slot); + if (bulkkey == NULL) { + return NULL; + } + } + + encryptobj = NULL; + mark = PORT_ArenaMark (cinfo->poolp); + + /* + * Encrypt the bulk key with the public key of each recipient. + */ + for (i = 0; recipientinfos && (ri = recipientinfos[i]) != NULL; i++) { + CERTCertificate *cert; + SECOidTag certalgtag, encalgtag; + SECStatus rv; + int data_len; + SECItem *params = NULL; + + cert = ri->cert; + PORT_Assert (cert != NULL); + if (cert == NULL) + continue; + + /* + * XXX Want an interface that takes a cert and some data and + * fills in an algorithmID and encrypts the data with the public + * key from the cert. Or, give me two interfaces -- one which + * gets the algorithm tag from a cert (I should not have to go + * down into the subjectPublicKeyInfo myself) and another which + * takes a public key and algorithm tag and data and encrypts + * the data. Or something like that. The point is that all + * of the following hardwired RSA and KEA stuff should be done + * elsewhere. + */ + + certalgtag=SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); + + switch (certalgtag) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + encalgtag = certalgtag; + publickey = CERT_ExtractPublicKey (cert); + if (publickey == NULL) goto loser; + + data_len = SECKEY_PublicKeyStrength(publickey); + ri->encKey.data = + (unsigned char*)PORT_ArenaAlloc(cinfo->poolp ,data_len); + ri->encKey.len = data_len; + if (ri->encKey.data == NULL) goto loser; + + rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(certalgtag),publickey, + bulkkey,&ri->encKey); + + SECKEY_DestroyPublicKey(publickey); + publickey = NULL; + if (rv != SECSuccess) goto loser; + params = NULL; /* paranoia */ + break; + /* ### mwelch -- KEA */ + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_KEA: + { +#define SMIME_FORTEZZA_RA_LENGTH 128 +#define SMIME_FORTEZZA_IV_LENGTH 24 +#define SMIME_FORTEZZA_MAX_KEY_SIZE 256 + SECStatus err; + PK11SymKey *tek; + CERTCertificate *ourCert; + SECKEYPublicKey *ourPubKey; + SECKEATemplateSelector whichKEA; + + /* We really want to show our KEA tag as the + key exchange algorithm tag. */ + encalgtag = SEC_OID_NETSCAPE_SMIME_KEA; + + /* Get the public key of the recipient. */ + publickey = CERT_ExtractPublicKey(cert); + if (publickey == NULL) goto loser; + + /* Find our own cert, and extract its keys. */ + ourCert = PK11_FindBestKEAMatch(cert,wincx); + if (ourCert == NULL) goto loser; + + arena = PORT_NewArena(1024); + if (arena == NULL) goto loser; + + ourPubKey = CERT_ExtractPublicKey(ourCert); + if (ourPubKey == NULL) + { + CERT_DestroyCertificate(ourCert); + goto loser; + } + + /* While we're here, copy the public key into the outgoing + * KEA parameters. */ + SECITEM_CopyItem(arena, &(keaParams.originatorKEAKey), + &(ourPubKey->u.fortezza.KEAKey)); + SECKEY_DestroyPublicKey(ourPubKey); + ourPubKey = NULL; + + /* Extract our private key in order to derive the + * KEA key. */ + ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx); + CERT_DestroyCertificate(ourCert); /* we're done with this */ + if (!ourPrivKey) goto loser; + + /* Prepare raItem with 128 bytes (filled with zeros). */ + keaParams.originatorRA.data = + (unsigned char*)PORT_ArenaAlloc(arena,SMIME_FORTEZZA_RA_LENGTH); + keaParams.originatorRA.len = SMIME_FORTEZZA_RA_LENGTH; + + + /* Generate the TEK (token exchange key) which we use + * to wrap the bulk encryption key. (raItem) will be + * filled with a random seed which we need to send to + * the recipient. */ + tek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE, + &keaParams.originatorRA, NULL, + CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, + CKA_WRAP, 0, wincx); + + SECKEY_DestroyPublicKey(publickey); + SECKEY_DestroyPrivateKey(ourPrivKey); + publickey = NULL; + ourPrivKey = NULL; + + if (!tek) + goto loser; + + ri->encKey.data = (unsigned char*)PORT_ArenaAlloc(cinfo->poolp, + SMIME_FORTEZZA_MAX_KEY_SIZE); + ri->encKey.len = SMIME_FORTEZZA_MAX_KEY_SIZE; + + if (ri->encKey.data == NULL) + { + PK11_FreeSymKey(tek); + goto loser; + } + + /* Wrap the bulk key. What we do with the resulting data + depends on whether we're using Skipjack to wrap the key. */ + switch(PK11_AlgtagToMechanism(enccinfo->encalg)) + { + case CKM_SKIPJACK_CBC64: + case CKM_SKIPJACK_ECB64: + case CKM_SKIPJACK_OFB64: + case CKM_SKIPJACK_CFB64: + case CKM_SKIPJACK_CFB32: + case CKM_SKIPJACK_CFB16: + case CKM_SKIPJACK_CFB8: + /* do SKIPJACK, we use the wrap mechanism */ + err = PK11_WrapSymKey(CKM_SKIPJACK_WRAP, NULL, + tek, bulkkey, &ri->encKey); + whichKEA = SECKEAUsesSkipjack; + break; + default: + /* Not SKIPJACK, we encrypt the raw key data */ + keaParams.nonSkipjackIV .data = + (unsigned char*)PORT_ArenaAlloc(arena, + SMIME_FORTEZZA_IV_LENGTH); + keaParams.nonSkipjackIV.len = SMIME_FORTEZZA_IV_LENGTH; + err = PK11_WrapSymKey(CKM_SKIPJACK_CBC64, + &keaParams.nonSkipjackIV, + tek, bulkkey, &ri->encKey); + if (err != SECSuccess) + goto loser; + + if (ri->encKey.len != PK11_GetKeyLength(bulkkey)) + { + /* The size of the encrypted key is not the same as + that of the original bulk key, presumably due to + padding. Encode and store the real size of the + bulk key. */ + if (SEC_ASN1EncodeInteger(arena, + &keaParams.bulkKeySize, + PK11_GetKeyLength(bulkkey)) + == NULL) + err = (SECStatus)PORT_GetError(); + else + /* use full template for encoding */ + whichKEA = SECKEAUsesNonSkipjackWithPaddedEncKey; + } + else + /* enc key length == bulk key length */ + whichKEA = SECKEAUsesNonSkipjack; + break; + } + + PK11_FreeSymKey(tek); + if (err != SECSuccess) + goto loser; + + /* Encode the KEA parameters into the recipient info. */ + params = SEC_ASN1EncodeItem(arena,NULL, &keaParams, + sec_pkcs7_get_kea_template(whichKEA)); + if (params == NULL) goto loser; + break; + } + default: + PORT_SetError (SEC_ERROR_INVALID_ALGORITHM); + goto loser; + } + + rv = SECOID_SetAlgorithmID(cinfo->poolp, &ri->keyEncAlg, encalgtag, + params); + if (rv != SECSuccess) + goto loser; + if (arena) PORT_FreeArena(arena,PR_FALSE); + arena = NULL; + } + + encryptobj = sec_PKCS7CreateEncryptObject (cinfo->poolp, bulkkey, + enccinfo->encalg, + &(enccinfo->contentEncAlg)); + if (encryptobj != NULL) { + PORT_ArenaUnmark (cinfo->poolp, mark); + mark = NULL; /* good one; do not want to release */ + } + /* fallthru */ + +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + if (publickey) { + SECKEY_DestroyPublicKey(publickey); + } + if (ourPrivKey) { + SECKEY_DestroyPrivateKey(ourPrivKey); + } + if (mark != NULL) { + PORT_ArenaRelease (cinfo->poolp, mark); + } + if (orig_bulkkey == NULL) { + if (bulkkey) PK11_FreeSymKey(bulkkey); + } + + return encryptobj; +} + + +static void +sec_pkcs7_encoder_notify (void *arg, PRBool before, void *dest, int depth) +{ + SEC_PKCS7EncoderContext *p7ecx; + SEC_PKCS7ContentInfo *cinfo; + SECOidTag kind; + PRBool before_content; + + /* + * We want to notice just before the content field. After fields are + * not interesting to us. + */ + if (!before) + return; + + p7ecx = (SEC_PKCS7EncoderContext*)arg; + cinfo = p7ecx->cinfo; + + before_content = PR_FALSE; + + /* + * Watch for the content field, at which point we want to instruct + * the ASN.1 encoder to start taking bytes from the buffer. + * + * XXX The following assumes the inner content type is data; + * if/when we want to handle fully nested types, this will have + * to recurse until reaching the innermost data content. + */ + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + default: + case SEC_OID_PKCS7_DATA: + if (dest == &(cinfo->content.data)) + before_content = PR_TRUE; + break; + + case SEC_OID_PKCS7_DIGESTED_DATA: + { + SEC_PKCS7DigestedData *digd; + + digd = cinfo->content.digestedData; + if (digd == NULL) + break; + + if (dest == &(digd->contentInfo.content)) + before_content = PR_TRUE; + } + break; + + case SEC_OID_PKCS7_ENCRYPTED_DATA: + { + SEC_PKCS7EncryptedData *encd; + + encd = cinfo->content.encryptedData; + if (encd == NULL) + break; + + if (dest == &(encd->encContentInfo.encContent)) + before_content = PR_TRUE; + } + break; + + case SEC_OID_PKCS7_ENVELOPED_DATA: + { + SEC_PKCS7EnvelopedData *envd; + + envd = cinfo->content.envelopedData; + if (envd == NULL) + break; + + if (dest == &(envd->encContentInfo.encContent)) + before_content = PR_TRUE; + } + break; + + case SEC_OID_PKCS7_SIGNED_DATA: + { + SEC_PKCS7SignedData *sigd; + + sigd = cinfo->content.signedData; + if (sigd == NULL) + break; + + if (dest == &(sigd->contentInfo.content)) + before_content = PR_TRUE; + } + break; + + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saed; + + saed = cinfo->content.signedAndEnvelopedData; + if (saed == NULL) + break; + + if (dest == &(saed->encContentInfo.encContent)) + before_content = PR_TRUE; + } + break; + } + + if (before_content) { + /* + * This will cause the next SEC_ASN1EncoderUpdate to take the + * contents bytes from the passed-in buffer. + */ + SEC_ASN1EncoderSetTakeFromBuf (p7ecx->ecx); + /* + * And that is all we needed this notify function for. + */ + SEC_ASN1EncoderClearNotifyProc (p7ecx->ecx); + } +} + + +static SEC_PKCS7EncoderContext * +sec_pkcs7_encoder_start_contexts (SEC_PKCS7ContentInfo *cinfo, + PK11SymKey *bulkkey) +{ + SEC_PKCS7EncoderContext *p7ecx; + SECOidTag kind; + PRBool encrypt; + SECItem **digests; + SECAlgorithmID *digestalg, **digestalgs; + + p7ecx = + (SEC_PKCS7EncoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7EncoderContext)); + if (p7ecx == NULL) + return NULL; + + digests = NULL; + digestalg = NULL; + digestalgs = NULL; + encrypt = PR_FALSE; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + default: + case SEC_OID_PKCS7_DATA: + break; + case SEC_OID_PKCS7_DIGESTED_DATA: + digestalg = &(cinfo->content.digestedData->digestAlg); + break; + case SEC_OID_PKCS7_SIGNED_DATA: + digests = cinfo->content.signedData->digests; + digestalgs = cinfo->content.signedData->digestAlgorithms; + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + case SEC_OID_PKCS7_ENVELOPED_DATA: + encrypt = PR_TRUE; + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + digests = cinfo->content.signedAndEnvelopedData->digests; + digestalgs = cinfo->content.signedAndEnvelopedData->digestAlgorithms; + encrypt = PR_TRUE; + break; + } + + if (encrypt) { + p7ecx->encryptobj = sec_pkcs7_encoder_start_encrypt (cinfo, bulkkey); + if (p7ecx->encryptobj == NULL) { + PORT_Free (p7ecx); + return NULL; + } + } + + if (digestalgs != NULL) { + if (digests != NULL) { + /* digests already created (probably for detached data) */ + digestalg = NULL; + } else { + /* + * XXX Some day we should handle multiple digests; for now, + * assume only one will be done. + */ + PORT_Assert (digestalgs[0] != NULL && digestalgs[1] == NULL); + digestalg = digestalgs[0]; + } + } + + if (digestalg != NULL) { + SECOidData *oiddata; + + oiddata = SECOID_FindOID (&(digestalg->algorithm)); + if (oiddata != NULL) { + switch (oiddata->offset) { + case SEC_OID_MD2: + p7ecx->digestobj = &SECHashObjects[HASH_AlgMD2]; + break; + case SEC_OID_MD5: + p7ecx->digestobj = &SECHashObjects[HASH_AlgMD5]; + break; + case SEC_OID_SHA1: + p7ecx->digestobj = &SECHashObjects[HASH_AlgSHA1]; + break; + default: + /* XXX right error? */ + PORT_SetError (SEC_ERROR_INVALID_ALGORITHM); + break; + } + } + if (p7ecx->digestobj != NULL) { + p7ecx->digestcx = (* p7ecx->digestobj->create) (); + if (p7ecx->digestcx == NULL) + p7ecx->digestobj = NULL; + else + (* p7ecx->digestobj->begin) (p7ecx->digestcx); + } + if (p7ecx->digestobj == NULL) { + if (p7ecx->encryptobj != NULL) + sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj); + PORT_Free (p7ecx); + return NULL; + } + } + + p7ecx->cinfo = cinfo; + return p7ecx; +} + + +SEC_PKCS7EncoderContext * +SEC_PKCS7EncoderStart (SEC_PKCS7ContentInfo *cinfo, + SEC_PKCS7EncoderOutputCallback outputfn, + void *outputarg, + PK11SymKey *bulkkey) +{ + SEC_PKCS7EncoderContext *p7ecx; + SECStatus rv; + + p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey); + if (p7ecx == NULL) + return NULL; + + p7ecx->output.outputfn = outputfn; + p7ecx->output.outputarg = outputarg; + + /* + * Initialize the BER encoder. + */ + p7ecx->ecx = SEC_ASN1EncoderStart (cinfo, sec_PKCS7ContentInfoTemplate, + sec_pkcs7_encoder_out, &(p7ecx->output)); + if (p7ecx->ecx == NULL) { + PORT_Free (p7ecx); + return NULL; + } + + /* + * Indicate that we are streaming. We will be streaming until we + * get past the contents bytes. + */ + SEC_ASN1EncoderSetStreaming (p7ecx->ecx); + + /* + * The notify function will watch for the contents field. + */ + SEC_ASN1EncoderSetNotifyProc (p7ecx->ecx, sec_pkcs7_encoder_notify, p7ecx); + + /* + * This will encode everything up to the content bytes. (The notify + * function will then cause the encoding to stop there.) Then our + * caller can start passing contents bytes to our Update, which we + * will pass along. + */ + rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0); + if (rv != SECSuccess) { + PORT_Free (p7ecx); + return NULL; + } + + return p7ecx; +} + + +/* + * XXX If/when we support nested contents, this needs to be revised. + */ +static SECStatus +sec_pkcs7_encoder_work_data (SEC_PKCS7EncoderContext *p7ecx, SECItem *dest, + const unsigned char *data, unsigned long len, + PRBool final) +{ + unsigned char *buf = NULL; + SECStatus rv; + + + rv = SECSuccess; /* may as well be optimistic */ + + /* + * We should really have data to process, or we should be trying + * to finish/flush the last block. (This is an overly paranoid + * check since all callers are in this file and simple inspection + * proves they do it right. But it could find a bug in future + * modifications/development, that is why it is here.) + */ + PORT_Assert ((data != NULL && len) || final); + + /* + * Update the running digest. + * XXX This needs modification if/when we handle multiple digests. + */ + if (len && p7ecx->digestobj != NULL) { + (* p7ecx->digestobj->update) (p7ecx->digestcx, data, len); + } + + /* + * Encrypt this chunk. + */ + if (p7ecx->encryptobj != NULL) { + /* XXX the following lengths should all be longs? */ + unsigned int inlen; /* length of data being encrypted */ + unsigned int outlen; /* length of encrypted data */ + unsigned int buflen; /* length available for encrypted data */ + + inlen = len; + buflen = sec_PKCS7EncryptLength (p7ecx->encryptobj, inlen, final); + if (buflen == 0) { + /* + * No output is expected, but the input data may be buffered + * so we still have to call Encrypt. + */ + rv = sec_PKCS7Encrypt (p7ecx->encryptobj, NULL, NULL, 0, + data, inlen, final); + if (final) { + len = 0; + goto done; + } + return rv; + } + + if (dest != NULL) + buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cinfo->poolp, buflen); + else + buf = (unsigned char*)PORT_Alloc (buflen); + + if (buf == NULL) { + rv = SECFailure; + } else { + rv = sec_PKCS7Encrypt (p7ecx->encryptobj, buf, &outlen, buflen, + data, inlen, final); + data = buf; + len = outlen; + } + if (rv != SECSuccess) { + if (final) + goto done; + return rv; + } + } + + if (p7ecx->ecx != NULL) { + /* + * Encode the contents bytes. + */ + if(len) { + rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, (const char *)data, len); + } + } + +done: + if (p7ecx->encryptobj != NULL) { + if (final) + sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj); + if (dest != NULL) { + dest->data = buf; + dest->len = len; + } else if (buf != NULL) { + PORT_Free (buf); + } + } + + if (final && p7ecx->digestobj != NULL) { + SECItem *digest, **digests, ***digestsp; + unsigned char *digdata; + SECOidTag kind; + + kind = SEC_PKCS7ContentType (p7ecx->cinfo); + switch (kind) { + default: + PORT_Assert (0); + return SECFailure; + case SEC_OID_PKCS7_DIGESTED_DATA: + digest = &(p7ecx->cinfo->content.digestedData->digest); + digestsp = NULL; + break; + case SEC_OID_PKCS7_SIGNED_DATA: + digest = NULL; + digestsp = &(p7ecx->cinfo->content.signedData->digests); + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + digest = NULL; + digestsp = &(p7ecx->cinfo->content.signedAndEnvelopedData->digests); + break; + } + + digdata = (unsigned char*)PORT_ArenaAlloc (p7ecx->cinfo->poolp, + p7ecx->digestobj->length); + if (digdata == NULL) + return SECFailure; + + if (digestsp != NULL) { + PORT_Assert (digest == NULL); + + digest = (SECItem*)PORT_ArenaAlloc (p7ecx->cinfo->poolp, + sizeof(SECItem)); + digests = (SECItem**)PORT_ArenaAlloc (p7ecx->cinfo->poolp, + 2 * sizeof(SECItem *)); + if (digests == NULL || digest == NULL) + return SECFailure; + + digests[0] = digest; + digests[1] = NULL; + + *digestsp = digests; + } + + PORT_Assert (digest != NULL); + + digest->data = digdata; + digest->len = p7ecx->digestobj->length; + + (* p7ecx->digestobj->end) (p7ecx->digestcx, digest->data, + &(digest->len), digest->len); + (* p7ecx->digestobj->destroy) (p7ecx->digestcx, PR_TRUE); + } + + return rv; +} + + +SECStatus +SEC_PKCS7EncoderUpdate (SEC_PKCS7EncoderContext *p7ecx, + const char *data, unsigned long len) +{ + /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ + return sec_pkcs7_encoder_work_data (p7ecx, NULL, + (const unsigned char *)data, len, + PR_FALSE); +} + + +/* + * XXX I would *really* like to not have to do this, but the current + * signing interface gives me little choice. + */ +static SECOidTag +sec_pkcs7_pick_sign_alg (SECOidTag hashalg, SECOidTag encalg) +{ + switch (encalg) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + switch (hashalg) { + case SEC_OID_MD2: + return SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION; + case SEC_OID_MD5: + return SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; + case SEC_OID_SHA1: + return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; + default: + return SEC_OID_UNKNOWN; + } + case SEC_OID_ANSIX9_DSA_SIGNATURE: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_DSS: + switch (hashalg) { + case SEC_OID_SHA1: + return SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST; + default: + return SEC_OID_UNKNOWN; + } + default: + break; + } + + return encalg; /* maybe it is already the right algid */ +} + + +static SECStatus +sec_pkcs7_encoder_sig_and_certs (SEC_PKCS7ContentInfo *cinfo, + SECKEYGetPasswordKey pwfn, void *pwfnarg) +{ + SECOidTag kind; + CERTCertificate **certs; + CERTCertificateList **certlists; + SECAlgorithmID **digestalgs; + SECItem **digests; + SEC_PKCS7SignerInfo *signerinfo, **signerinfos; + SECItem **rawcerts, ***rawcertsp; + PRArenaPool *poolp; + int certcount; + int ci, cli, rci, si; + + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + default: + case SEC_OID_PKCS7_DATA: + case SEC_OID_PKCS7_DIGESTED_DATA: + case SEC_OID_PKCS7_ENCRYPTED_DATA: + case SEC_OID_PKCS7_ENVELOPED_DATA: + certs = NULL; + certlists = NULL; + digestalgs = NULL; + digests = NULL; + signerinfos = NULL; + rawcertsp = NULL; + break; + case SEC_OID_PKCS7_SIGNED_DATA: + { + SEC_PKCS7SignedData *sdp; + + sdp = cinfo->content.signedData; + certs = sdp->certs; + certlists = sdp->certLists; + digestalgs = sdp->digestAlgorithms; + digests = sdp->digests; + signerinfos = sdp->signerInfos; + rawcertsp = &(sdp->rawCerts); + } + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + { + SEC_PKCS7SignedAndEnvelopedData *saedp; + + saedp = cinfo->content.signedAndEnvelopedData; + certs = saedp->certs; + certlists = saedp->certLists; + digestalgs = saedp->digestAlgorithms; + digests = saedp->digests; + signerinfos = saedp->signerInfos; + rawcertsp = &(saedp->rawCerts); + } + break; + } + + if (certs == NULL && certlists == NULL && signerinfos == NULL) + return SECSuccess; /* nothing for us to do! */ + + poolp = cinfo->poolp; + certcount = 0; + + if (signerinfos != NULL) { + SECOidTag digestalgtag; + int di; + SECStatus rv; + CERTCertificate *cert; + SECKEYPrivateKey *privkey; + SECItem signature; + SECOidTag signalgtag; + + PORT_Assert (digestalgs != NULL && digests != NULL); + + /* + * If one fails, we bail right then. If we want to continue and + * try to do subsequent signatures, this loop, and the departures + * from it, will need to be reworked. + */ + for (si = 0; signerinfos[si] != NULL; si++) { + + signerinfo = signerinfos[si]; + + /* find right digest */ + digestalgtag = SECOID_GetAlgorithmTag (&(signerinfo->digestAlg)); + for (di = 0; digestalgs[di] != NULL; di++) { + /* XXX Should I be comparing more than the tag? */ + if (digestalgtag == SECOID_GetAlgorithmTag (digestalgs[di])) + break; + } + if (digestalgs[di] == NULL) { + /* XXX oops; do what? set an error? */ + return SECFailure; + } + PORT_Assert (digests[di] != NULL); + + cert = signerinfo->cert; + privkey = PK11_FindKeyByAnyCert (cert, pwfnarg); + if (privkey == NULL) + return SECFailure; + + /* + * XXX I think there should be a cert-level interface for this, + * so that I do not have to know about subjectPublicKeyInfo... + */ + signalgtag = SECOID_GetAlgorithmTag (&(cert->subjectPublicKeyInfo.algorithm)); + + /* Fortezza MISSI have weird signature formats. Map them + * to standard DSA formats */ + signalgtag = PK11_FortezzaMapSig(signalgtag); + + if (signerinfo->authAttr != NULL) { + SEC_PKCS7Attribute *attr; + SECItem encoded_attrs; + SECItem *dummy; + + /* + * First, find and fill in the message digest attribute. + */ + attr = sec_PKCS7FindAttribute (signerinfo->authAttr, + SEC_OID_PKCS9_MESSAGE_DIGEST, + PR_TRUE); + PORT_Assert (attr != NULL); + if (attr == NULL) { + SECKEY_DestroyPrivateKey (privkey); + return SECFailure; + } + + /* + * XXX The second half of the following assertion prevents + * the encoder from being called twice on the same content. + * Either just remove the second half the assertion, or + * change the code to check if the value already there is + * the same as digests[di], whichever seems more right. + */ + PORT_Assert (attr->values != NULL && attr->values[0] == NULL); + attr->values[0] = digests[di]; + + /* + * Before encoding, reorder the attributes so that when they + * are encoded, they will be conforming DER, which is required + * to have a specific order and that is what must be used for + * the hash/signature. We do this here, rather than building + * it into EncodeAttributes, because we do not want to do + * such reordering on incoming messages (which also uses + * EncodeAttributes) or our old signatures (and other "broken" + * implementations) will not verify. So, we want to guarantee + * that we send out good DER encodings of attributes, but not + * to expect to receive them. + */ + rv = sec_PKCS7ReorderAttributes (signerinfo->authAttr); + if (rv != SECSuccess) { + SECKEY_DestroyPrivateKey (privkey); + return SECFailure; + } + + encoded_attrs.data = NULL; + encoded_attrs.len = 0; + dummy = sec_PKCS7EncodeAttributes (NULL, &encoded_attrs, + &(signerinfo->authAttr)); + if (dummy == NULL) { + SECKEY_DestroyPrivateKey (privkey); + return SECFailure; + } + + rv = SEC_SignData (&signature, + encoded_attrs.data, encoded_attrs.len, + privkey, + sec_pkcs7_pick_sign_alg (digestalgtag, + signalgtag)); + SECITEM_FreeItem (&encoded_attrs, PR_FALSE); + } else { + rv = SGN_Digest (privkey, digestalgtag, &signature, + digests[di]); + } + + SECKEY_DestroyPrivateKey (privkey); + + if (rv != SECSuccess) + return rv; + + rv = SECITEM_CopyItem (poolp, &(signerinfo->encDigest), &signature); + if (rv != SECSuccess) + return rv; + + SECITEM_FreeItem (&signature, PR_FALSE); + + rv = SECOID_SetAlgorithmID (poolp, &(signerinfo->digestEncAlg), + signalgtag, NULL); + if (rv != SECSuccess) + return SECFailure; + + /* + * Count the cert chain for this signer. + */ + if (signerinfo->certList != NULL) + certcount += signerinfo->certList->len; + } + } + + if (certs != NULL) { + for (ci = 0; certs[ci] != NULL; ci++) + certcount++; + } + + if (certlists != NULL) { + for (cli = 0; certlists[cli] != NULL; cli++) + certcount += certlists[cli]->len; + } + + if (certcount == 0) + return SECSuccess; /* signing done; no certs */ + + /* + * Combine all of the certs and cert chains into rawcerts. + * Note: certcount is an upper bound; we may not need that many slots + * but we will allocate anyway to avoid having to do another pass. + * (The temporary space saving is not worth it.) + */ + rawcerts = (SECItem**)PORT_ArenaAlloc (poolp, + (certcount + 1) * sizeof(SECItem *)); + if (rawcerts == NULL) + return SECFailure; + + /* + * XXX Want to check for duplicates and not add *any* cert that is + * already in the set. This will be more important when we start + * dealing with larger sets of certs, dual-key certs (signing and + * encryption), etc. For the time being we can slide by... + */ + rci = 0; + if (signerinfos != NULL) { + for (si = 0; signerinfos[si] != NULL; si++) { + signerinfo = signerinfos[si]; + for (ci = 0; ci < signerinfo->certList->len; ci++) + rawcerts[rci++] = &(signerinfo->certList->certs[ci]); + } + + } + + if (certs != NULL) { + for (ci = 0; certs[ci] != NULL; ci++) + rawcerts[rci++] = &(certs[ci]->derCert); + } + + if (certlists != NULL) { + for (cli = 0; certlists[cli] != NULL; cli++) { + for (ci = 0; ci < certlists[cli]->len; ci++) + rawcerts[rci++] = &(certlists[cli]->certs[ci]); + } + } + + rawcerts[rci] = NULL; + *rawcertsp = rawcerts; + + return SECSuccess; +} + + +SECStatus +SEC_PKCS7EncoderFinish (SEC_PKCS7EncoderContext *p7ecx, + SECKEYGetPasswordKey pwfn, void *pwfnarg) +{ + SECStatus rv; + + /* + * Flush out any remaining data. + */ + rv = sec_pkcs7_encoder_work_data (p7ecx, NULL, NULL, 0, PR_TRUE); + + /* + * Turn off streaming stuff. + */ + SEC_ASN1EncoderClearTakeFromBuf (p7ecx->ecx); + SEC_ASN1EncoderClearStreaming (p7ecx->ecx); + + if (rv != SECSuccess) + goto loser; + + rv = sec_pkcs7_encoder_sig_and_certs (p7ecx->cinfo, pwfn, pwfnarg); + if (rv != SECSuccess) + goto loser; + + rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0); + +loser: + SEC_ASN1EncoderFinish (p7ecx->ecx); + PORT_Free (p7ecx); + return rv; +} + + +/* + * After this routine is called, the entire PKCS7 contentInfo is ready + * to be encoded. This is used internally, but can also be called from + * elsewhere for those who want to be able to just have pointers to + * the ASN1 template for pkcs7 contentInfo built into their own encodings. + */ +SECStatus +SEC_PKCS7PrepareForEncode (SEC_PKCS7ContentInfo *cinfo, + PK11SymKey *bulkkey, + SECKEYGetPasswordKey pwfn, + void *pwfnarg) +{ + SEC_PKCS7EncoderContext *p7ecx; + SECItem *content, *enc_content; + SECStatus rv; + + p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey); + if (p7ecx == NULL) + return SECFailure; + + content = SEC_PKCS7GetContent (cinfo); + + if (p7ecx->encryptobj != NULL) { + SECOidTag kind; + SEC_PKCS7EncryptedContentInfo *enccinfo; + + kind = SEC_PKCS7ContentType (p7ecx->cinfo); + switch (kind) { + default: + PORT_Assert (0); + rv = SECFailure; + goto loser; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo); + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo); + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encContentInfo); + break; + } + enc_content = &(enccinfo->encContent); + } else { + enc_content = NULL; + } + + if (content != NULL && content->data != NULL && content->len) { + rv = sec_pkcs7_encoder_work_data (p7ecx, enc_content, + content->data, content->len, PR_TRUE); + if (rv != SECSuccess) + goto loser; + } + + rv = sec_pkcs7_encoder_sig_and_certs (cinfo, pwfn, pwfnarg); + +loser: + PORT_Free (p7ecx); + return rv; +} + + +/* + * Encode a PKCS7 object, in one shot. All necessary components + * of the object must already be specified. Either the data has + * already been included (via SetContent), or the data is detached, + * or there is no data at all (certs-only). + * + * "cinfo" specifies the object to be encoded. + * + * "outputfn" is where the encoded bytes will be passed. + * + * "outputarg" is an opaque argument to the above callback. + * + * "bulkkey" specifies the bulk encryption key to use. This argument + * can be NULL if no encryption is being done, or if the bulk key should + * be generated internally (usually the case for EnvelopedData but never + * for EncryptedData, which *must* provide a bulk encryption key). + * + * "pwfn" is a callback for getting the password which protects the + * private key of the signer. This argument can be NULL if it is known + * that no signing is going to be done. + * + * "pwfnarg" is an opaque argument to the above callback. + */ +SECStatus +SEC_PKCS7Encode (SEC_PKCS7ContentInfo *cinfo, + SEC_PKCS7EncoderOutputCallback outputfn, + void *outputarg, + PK11SymKey *bulkkey, + SECKEYGetPasswordKey pwfn, + void *pwfnarg) +{ + SECStatus rv; + + rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg); + if (rv == SECSuccess) { + struct sec_pkcs7_encoder_output outputcx; + + outputcx.outputfn = outputfn; + outputcx.outputarg = outputarg; + + rv = SEC_ASN1Encode (cinfo, sec_PKCS7ContentInfoTemplate, + sec_pkcs7_encoder_out, &outputcx); + } + + return rv; +} + + +/* + * Encode a PKCS7 object, in one shot. All necessary components + * of the object must already be specified. Either the data has + * already been included (via SetContent), or the data is detached, + * or there is no data at all (certs-only). The output, rather than + * being passed to an output function as is done above, is all put + * into a SECItem. + * + * "pool" specifies a pool from which to allocate the result. + * It can be NULL, in which case memory is allocated generically. + * + * "dest" specifies a SECItem in which to put the result data. + * It can be NULL, in which case the entire item is allocated, too. + * + * "cinfo" specifies the object to be encoded. + * + * "bulkkey" specifies the bulk encryption key to use. This argument + * can be NULL if no encryption is being done, or if the bulk key should + * be generated internally (usually the case for EnvelopedData but never + * for EncryptedData, which *must* provide a bulk encryption key). + * + * "pwfn" is a callback for getting the password which protects the + * private key of the signer. This argument can be NULL if it is known + * that no signing is going to be done. + * + * "pwfnarg" is an opaque argument to the above callback. + */ +SECItem * +SEC_PKCS7EncodeItem (PRArenaPool *pool, + SECItem *dest, + SEC_PKCS7ContentInfo *cinfo, + PK11SymKey *bulkkey, + SECKEYGetPasswordKey pwfn, + void *pwfnarg) +{ + SECStatus rv; + + rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg); + if (rv != SECSuccess) + return NULL; + + return SEC_ASN1EncodeItem (pool, dest, cinfo, sec_PKCS7ContentInfoTemplate); +} + diff --git a/security/nss/lib/pkcs7/p7local.c b/security/nss/lib/pkcs7/p7local.c new file mode 100644 index 000000000..d3e58be50 --- /dev/null +++ b/security/nss/lib/pkcs7/p7local.c @@ -0,0 +1,1431 @@ +/* + * 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. + */ + +/* + * Support routines for PKCS7 implementation, none of which are exported. + * This file should only contain things that are needed by both the + * encoding/creation side *and* the decoding/decryption side. Anything + * else should be static routines in the appropriate file. + * + * $Id$ + */ + +#include "p7local.h" + +#include "cryptohi.h" +#include "secasn1.h" +#include "secoid.h" +#include "secitem.h" +#include "pk11func.h" +#include "secpkcs5.h" +#include "secerr.h" + +/* + * ------------------------------------------------------------------- + * Cipher stuff. + */ + +typedef SECStatus (*sec_pkcs7_cipher_function) (void *, + unsigned char *, + unsigned *, + unsigned int, + const unsigned char *, + unsigned int); +typedef SECStatus (*sec_pkcs7_cipher_destroy) (void *, PRBool); + +#define BLOCK_SIZE 4096 + +struct sec_pkcs7_cipher_object { + void *cx; + sec_pkcs7_cipher_function doit; + sec_pkcs7_cipher_destroy destroy; + PRBool encrypt; + int block_size; + int pad_size; + int pending_count; + unsigned char pending_buf[BLOCK_SIZE]; +}; + +/* + * Create a cipher object to do decryption, based on the given bulk + * encryption key and algorithm identifier (which may include an iv). + * + * XXX This interface, or one similar, would be really nice available + * in general... I tried to keep the pkcs7-specific stuff (mostly + * having to do with padding) out of here. + * + * XXX Once both are working, it might be nice to combine this and the + * function below (for starting up encryption) into one routine, and just + * have two simple cover functions which call it. + */ +sec_PKCS7CipherObject * +sec_PKCS7CreateDecryptObject (PK11SymKey *key, SECAlgorithmID *algid) +{ + sec_PKCS7CipherObject *result; + SECOidTag algtag; + void *ciphercx; + CK_MECHANISM_TYPE mechanism; + SECItem *param; + PK11SlotInfo *slot; + + result = (struct sec_pkcs7_cipher_object*) + PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object)); + if (result == NULL) + return NULL; + + ciphercx = NULL; + algtag = SECOID_GetAlgorithmTag (algid); + + if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { + CK_MECHANISM pbeMech, cryptoMech; + SECItem *pbeParams, *pwitem; + SEC_PKCS5KeyAndPassword *keyPwd; + + keyPwd = (SEC_PKCS5KeyAndPassword *)key; + key = keyPwd->key; + pwitem = keyPwd->pwitem; + + pbeMech.mechanism = PK11_AlgtagToMechanism(algtag); + pbeParams = PK11_ParamFromAlgid(algid); + if (!pbeParams) { + PORT_Free(result); + return NULL; + } + + pbeMech.pParameter = pbeParams->data; + pbeMech.ulParameterLen = pbeParams->len; + if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, pwitem, + PR_FALSE) != CKR_OK) { + PORT_Free(result); + SECITEM_ZfreeItem(pbeParams, PR_TRUE); + return NULL; + } + SECITEM_ZfreeItem(pbeParams, PR_TRUE); + + param = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(!param) { + PORT_Free(result); + return NULL; + } + param->data = (unsigned char *)cryptoMech.pParameter; + param->len = cryptoMech.ulParameterLen; + mechanism = cryptoMech.mechanism; + } else { + mechanism = PK11_AlgtagToMechanism(algtag); + param = PK11_ParamFromAlgid(algid); + if (param == NULL) { + PORT_Free(result); + return NULL; + } + } + + result->pad_size = PK11_GetBlockSize(mechanism,param); + slot = PK11_GetSlotFromKey(key); + result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size; + PK11_FreeSlot(slot); + ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_DECRYPT, key, param); + SECITEM_FreeItem(param,PR_TRUE); + if (ciphercx == NULL) { + PORT_Free (result); + return NULL; + } + + result->cx = ciphercx; + result->doit = (sec_pkcs7_cipher_function) PK11_CipherOp; + result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext; + result->encrypt = PR_FALSE; + result->pending_count = 0; + + return result; +} + +/* + * Create a cipher object to do encryption, based on the given bulk + * encryption key and algorithm tag. Fill in the algorithm identifier + * (which may include an iv) appropriately. + * + * XXX This interface, or one similar, would be really nice available + * in general... I tried to keep the pkcs7-specific stuff (mostly + * having to do with padding) out of here. + * + * XXX Once both are working, it might be nice to combine this and the + * function above (for starting up decryption) into one routine, and just + * have two simple cover functions which call it. + */ +sec_PKCS7CipherObject * +sec_PKCS7CreateEncryptObject (PRArenaPool *poolp, PK11SymKey *key, + SECOidTag algtag, SECAlgorithmID *algid) +{ + sec_PKCS7CipherObject *result; + void *ciphercx; + SECItem *param; + SECStatus rv; + CK_MECHANISM_TYPE mechanism; + PRBool needToEncodeAlgid = PR_FALSE; + PK11SlotInfo *slot; + + result = (struct sec_pkcs7_cipher_object*) + PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object)); + if (result == NULL) + return NULL; + + ciphercx = NULL; + if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { + CK_MECHANISM pbeMech, cryptoMech; + SECItem *pbeParams; + SEC_PKCS5KeyAndPassword *keyPwd; + + PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM)); + PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM)); + + pbeMech.mechanism = PK11_AlgtagToMechanism(algtag); + pbeParams = PK11_ParamFromAlgid(algid); + if(!pbeParams) { + PORT_Free(result); + return NULL; + } + keyPwd = (SEC_PKCS5KeyAndPassword *)key; + key = keyPwd->key; + + pbeMech.pParameter = pbeParams->data; + pbeMech.ulParameterLen = pbeParams->len; + if(PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, + keyPwd->pwitem, PR_FALSE) != CKR_OK) { + PORT_Free(result); + SECITEM_ZfreeItem(pbeParams, PR_TRUE); + return NULL; + } + SECITEM_ZfreeItem(pbeParams, PR_TRUE); + + param = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if(!param) { + PORT_Free(result); + return NULL; + } + param->data = (unsigned char *)cryptoMech.pParameter; + param->len = cryptoMech.ulParameterLen; + mechanism = cryptoMech.mechanism; + } else { + mechanism = PK11_AlgtagToMechanism(algtag); + param = PK11_GenerateNewParam(mechanism,key); + if (param == NULL) { + PORT_Free(result); + return NULL; + } + needToEncodeAlgid = PR_TRUE; + } + + result->pad_size = PK11_GetBlockSize(mechanism,param); + slot = PK11_GetSlotFromKey(key); + result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size; + PK11_FreeSlot(slot); + ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_ENCRYPT, + key, param); + if (ciphercx == NULL) { + PORT_Free (result); + SECITEM_FreeItem(param,PR_TRUE); + return NULL; + } + + /* + * These are placed after the CreateContextBySymKey() because some + * mechanisms have to generate their IVs from their card (i.e. FORTEZZA). + * Don't move it from here. + */ + if (needToEncodeAlgid) { + rv = PK11_ParamToAlgid(algtag,param,poolp,algid); + if(rv != SECSuccess) { + return NULL; + } + } + SECITEM_FreeItem(param,PR_TRUE); + + result->cx = ciphercx; + result->doit = (sec_pkcs7_cipher_function) PK11_CipherOp; + result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext; + result->encrypt = PR_TRUE; + result->pending_count = 0; + + return result; +} + + +/* + * Destroy the cipher object. + */ +static void +sec_pkcs7_destroy_cipher (sec_PKCS7CipherObject *obj) +{ + (* obj->destroy) (obj->cx, PR_TRUE); + PORT_Free (obj); +} + +void +sec_PKCS7DestroyDecryptObject (sec_PKCS7CipherObject *obj) +{ + PORT_Assert (obj != NULL); + if (obj == NULL) + return; + PORT_Assert (! obj->encrypt); + sec_pkcs7_destroy_cipher (obj); +} + +void +sec_PKCS7DestroyEncryptObject (sec_PKCS7CipherObject *obj) +{ + PORT_Assert (obj != NULL); + if (obj == NULL) + return; + PORT_Assert (obj->encrypt); + sec_pkcs7_destroy_cipher (obj); +} + + +/* + * XXX I think all of the following lengths should be longs instead + * of ints, but our current crypto interface uses ints, so I did too. + */ + + +/* + * What will be the output length of the next call to decrypt? + * Result can be used to perform memory allocations. Note that the amount + * is exactly accurate only when not doing a block cipher or when final + * is false, otherwise it is an upper bound on the amount because until + * we see the data we do not know how many padding bytes there are + * (always between 1 and bsize). + * + * Note that this can return zero, which does not mean that the decrypt + * operation can be skipped! (It simply means that there are not enough + * bytes to make up an entire block; the bytes will be reserved until + * there are enough to encrypt/decrypt at least one block.) However, + * if zero is returned it *does* mean that no output buffer need be + * passed in to the subsequent decrypt operation, as no output bytes + * will be stored. + */ +unsigned int +sec_PKCS7DecryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len, + PRBool final) +{ + int blocks, block_size; + + PORT_Assert (! obj->encrypt); + + block_size = obj->block_size; + + /* + * If this is not a block cipher, then we always have the same + * number of output bytes as we had input bytes. + */ + if (block_size == 0) + return input_len; + + /* + * On the final call, we will always use up all of the pending + * bytes plus all of the input bytes, *but*, there will be padding + * at the end and we cannot predict how many bytes of padding we + * will end up removing. The amount given here is actually known + * to be at least 1 byte too long (because we know we will have + * at least 1 byte of padding), but seemed clearer/better to me. + */ + if (final) + return obj->pending_count + input_len; + + /* + * Okay, this amount is exactly what we will output on the + * next cipher operation. We will always hang onto the last + * 1 - block_size bytes for non-final operations. That is, + * we will do as many complete blocks as we can *except* the + * last block (complete or partial). (This is because until + * we know we are at the end, we cannot know when to interpret + * and removing the padding byte(s), which are guaranteed to + * be there.) + */ + blocks = (obj->pending_count + input_len - 1) / block_size; + return blocks * block_size; +} + +/* + * What will be the output length of the next call to encrypt? + * Result can be used to perform memory allocations. + * + * Note that this can return zero, which does not mean that the encrypt + * operation can be skipped! (It simply means that there are not enough + * bytes to make up an entire block; the bytes will be reserved until + * there are enough to encrypt/decrypt at least one block.) However, + * if zero is returned it *does* mean that no output buffer need be + * passed in to the subsequent encrypt operation, as no output bytes + * will be stored. + */ +unsigned int +sec_PKCS7EncryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len, + PRBool final) +{ + int blocks, block_size; + int pad_size; + + PORT_Assert (obj->encrypt); + + block_size = obj->block_size; + pad_size = obj->pad_size; + + /* + * If this is not a block cipher, then we always have the same + * number of output bytes as we had input bytes. + */ + if (block_size == 0) + return input_len; + + /* + * On the final call, we only send out what we need for + * remaining bytes plus the padding. (There is always padding, + * so even if we have an exact number of blocks as input, we + * will add another full block that is just padding.) + */ + if (final) { + if (pad_size == 0) { + return obj->pending_count + input_len; + } else { + blocks = (obj->pending_count + input_len) / pad_size; + blocks++; + return blocks*pad_size; + } + } + + /* + * Now, count the number of complete blocks of data we have. + */ + blocks = (obj->pending_count + input_len) / block_size; + + + return blocks * block_size; +} + + +/* + * Decrypt a given length of input buffer (starting at "input" and + * containing "input_len" bytes), placing the decrypted bytes in + * "output" and storing the output length in "*output_len_p". + * "obj" is the return value from sec_PKCS7CreateDecryptObject. + * When "final" is true, this is the last of the data to be decrypted. + * + * This is much more complicated than it sounds when the cipher is + * a block-type, meaning that the decryption function will only + * operate on whole blocks. But our caller is operating stream-wise, + * and can pass in any number of bytes. So we need to keep track + * of block boundaries. We save excess bytes between calls in "obj". + * We also need to determine which bytes are padding, and remove + * them from the output. We can only do this step when we know we + * have the final block of data. PKCS #7 specifies that the padding + * used for a block cipher is a string of bytes, each of whose value is + * the same as the length of the padding, and that all data is padded. + * (Even data that starts out with an exact multiple of blocks gets + * added to it another block, all of which is padding.) + */ +SECStatus +sec_PKCS7Decrypt (sec_PKCS7CipherObject *obj, unsigned char *output, + unsigned int *output_len_p, unsigned int max_output_len, + const unsigned char *input, unsigned int input_len, + PRBool final) +{ + int blocks, bsize, pcount, padsize; + unsigned int max_needed, ifraglen, ofraglen, output_len; + unsigned char *pbuf; + SECStatus rv; + + PORT_Assert (! obj->encrypt); + + /* + * Check that we have enough room for the output. Our caller should + * already handle this; failure is really an internal error (i.e. bug). + */ + max_needed = sec_PKCS7DecryptLength (obj, input_len, final); + PORT_Assert (max_output_len >= max_needed); + if (max_output_len < max_needed) { + /* PORT_SetError (XXX); */ + return SECFailure; + } + + /* + * hardware encryption does not like small decryption sizes here, so we + * allow both blocking and padding. + */ + bsize = obj->block_size; + padsize = obj->pad_size; + + /* + * When no blocking or padding work to do, we can simply call the + * cipher function and we are done. + */ + if (bsize == 0) { + return (* obj->doit) (obj->cx, output, output_len_p, max_output_len, + input, input_len); + } + + pcount = obj->pending_count; + pbuf = obj->pending_buf; + + output_len = 0; + + if (pcount) { + /* + * Try to fill in an entire block, starting with the bytes + * we already have saved away. + */ + while (input_len && pcount < bsize) { + pbuf[pcount++] = *input++; + input_len--; + } + /* + * If we have at most a whole block and this is not our last call, + * then we are done for now. (We do not try to decrypt a lone + * single block because we cannot interpret the padding bytes + * until we know we are handling the very last block of all input.) + */ + if (input_len == 0 && !final) { + obj->pending_count = pcount; + if (output_len_p) + *output_len_p = 0; + return SECSuccess; + } + /* + * Given the logic above, we expect to have a full block by now. + * If we do not, there is something wrong, either with our own + * logic or with (length of) the data given to us. + */ + PORT_Assert ((padsize == 0) || (pcount % padsize) == 0); + if ((padsize != 0) && (pcount % padsize) != 0) { + PORT_Assert (final); + PORT_SetError (SEC_ERROR_BAD_DATA); + return SECFailure; + } + /* + * Decrypt the block. + */ + rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len, + pbuf, pcount); + if (rv != SECSuccess) + return rv; + + /* + * For now anyway, all of our ciphers have the same number of + * bytes of output as they do input. If this ever becomes untrue, + * then sec_PKCS7DecryptLength needs to be made smarter! + */ + PORT_Assert (ofraglen == pcount); + + /* + * Account for the bytes now in output. + */ + max_output_len -= ofraglen; + output_len += ofraglen; + output += ofraglen; + } + + /* + * If this is our last call, we expect to have an exact number of + * blocks left to be decrypted; we will decrypt them all. + * + * If not our last call, we always save between 1 and bsize bytes + * until next time. (We must do this because we cannot be sure + * that none of the decrypted bytes are padding bytes until we + * have at least another whole block of data. You cannot tell by + * looking -- the data could be anything -- you can only tell by + * context, knowing you are looking at the last block.) We could + * decrypt a whole block now but it is easier if we just treat it + * the same way we treat partial block bytes. + */ + if (final) { + if (padsize) { + blocks = input_len / padsize; + ifraglen = blocks * padsize; + } else ifraglen = input_len; + PORT_Assert (ifraglen == input_len); + + if (ifraglen != input_len) { + PORT_SetError (SEC_ERROR_BAD_DATA); + return SECFailure; + } + } else { + blocks = (input_len - 1) / bsize; + ifraglen = blocks * bsize; + PORT_Assert (ifraglen < input_len); + + pcount = input_len - ifraglen; + PORT_Memcpy (pbuf, input + ifraglen, pcount); + obj->pending_count = pcount; + } + + if (ifraglen) { + rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len, + input, ifraglen); + if (rv != SECSuccess) + return rv; + + /* + * For now anyway, all of our ciphers have the same number of + * bytes of output as they do input. If this ever becomes untrue, + * then sec_PKCS7DecryptLength needs to be made smarter! + */ + PORT_Assert (ifraglen == ofraglen); + if (ifraglen != ofraglen) { + PORT_SetError (SEC_ERROR_BAD_DATA); + return SECFailure; + } + + output_len += ofraglen; + } else { + ofraglen = 0; + } + + /* + * If we just did our very last block, "remove" the padding by + * adjusting the output length. + */ + if (final && (padsize != 0)) { + unsigned int padlen = *(output + ofraglen - 1); + PORT_Assert (padlen > 0 && padlen <= padsize); + if (padlen == 0 || padlen > padsize) { + PORT_SetError (SEC_ERROR_BAD_DATA); + return SECFailure; + } + output_len -= padlen; + } + + PORT_Assert (output_len_p != NULL || output_len == 0); + if (output_len_p != NULL) + *output_len_p = output_len; + + return SECSuccess; +} + +/* + * Encrypt a given length of input buffer (starting at "input" and + * containing "input_len" bytes), placing the encrypted bytes in + * "output" and storing the output length in "*output_len_p". + * "obj" is the return value from sec_PKCS7CreateEncryptObject. + * When "final" is true, this is the last of the data to be encrypted. + * + * This is much more complicated than it sounds when the cipher is + * a block-type, meaning that the encryption function will only + * operate on whole blocks. But our caller is operating stream-wise, + * and can pass in any number of bytes. So we need to keep track + * of block boundaries. We save excess bytes between calls in "obj". + * We also need to add padding bytes at the end. PKCS #7 specifies + * that the padding used for a block cipher is a string of bytes, + * each of whose value is the same as the length of the padding, + * and that all data is padded. (Even data that starts out with + * an exact multiple of blocks gets added to it another block, + * all of which is padding.) + * + * XXX I would kind of like to combine this with the function above + * which does decryption, since they have a lot in common. But the + * tricky parts about padding and filling blocks would be much + * harder to read that way, so I left them separate. At least for + * now until it is clear that they are right. + */ +SECStatus +sec_PKCS7Encrypt (sec_PKCS7CipherObject *obj, unsigned char *output, + unsigned int *output_len_p, unsigned int max_output_len, + const unsigned char *input, unsigned int input_len, + PRBool final) +{ + int blocks, bsize, padlen, pcount, padsize; + unsigned int max_needed, ifraglen, ofraglen, output_len; + unsigned char *pbuf; + SECStatus rv; + + PORT_Assert (obj->encrypt); + + /* + * Check that we have enough room for the output. Our caller should + * already handle this; failure is really an internal error (i.e. bug). + */ + max_needed = sec_PKCS7EncryptLength (obj, input_len, final); + PORT_Assert (max_output_len >= max_needed); + if (max_output_len < max_needed) { + /* PORT_SetError (XXX); */ + return SECFailure; + } + + bsize = obj->block_size; + padsize = obj->pad_size; + + /* + * When no blocking and padding work to do, we can simply call the + * cipher function and we are done. + */ + if (bsize == 0) { + return (* obj->doit) (obj->cx, output, output_len_p, max_output_len, + input, input_len); + } + + pcount = obj->pending_count; + pbuf = obj->pending_buf; + + output_len = 0; + + if (pcount) { + /* + * Try to fill in an entire block, starting with the bytes + * we already have saved away. + */ + while (input_len && pcount < bsize) { + pbuf[pcount++] = *input++; + input_len--; + } + /* + * If we do not have a full block and we know we will be + * called again, then we are done for now. + */ + if (pcount < bsize && !final) { + obj->pending_count = pcount; + if (output_len_p != NULL) + *output_len_p = 0; + return SECSuccess; + } + /* + * If we have a whole block available, encrypt it. + */ + if ((padsize == 0) || (pcount % padsize) == 0) { + rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len, + pbuf, pcount); + if (rv != SECSuccess) + return rv; + + /* + * For now anyway, all of our ciphers have the same number of + * bytes of output as they do input. If this ever becomes untrue, + * then sec_PKCS7EncryptLength needs to be made smarter! + */ + PORT_Assert (ofraglen == pcount); + + /* + * Account for the bytes now in output. + */ + max_output_len -= ofraglen; + output_len += ofraglen; + output += ofraglen; + + pcount = 0; + } + } + + if (input_len) { + PORT_Assert (pcount == 0); + + blocks = input_len / bsize; + ifraglen = blocks * bsize; + + if (ifraglen) { + rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len, + input, ifraglen); + if (rv != SECSuccess) + return rv; + + /* + * For now anyway, all of our ciphers have the same number of + * bytes of output as they do input. If this ever becomes untrue, + * then sec_PKCS7EncryptLength needs to be made smarter! + */ + PORT_Assert (ifraglen == ofraglen); + + max_output_len -= ofraglen; + output_len += ofraglen; + output += ofraglen; + } + + pcount = input_len - ifraglen; + PORT_Assert (pcount < bsize); + if (pcount) + PORT_Memcpy (pbuf, input + ifraglen, pcount); + } + + if (final) { + padlen = padsize - (pcount % padsize); + PORT_Memset (pbuf + pcount, padlen, padlen); + rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len, + pbuf, pcount+padlen); + if (rv != SECSuccess) + return rv; + + /* + * For now anyway, all of our ciphers have the same number of + * bytes of output as they do input. If this ever becomes untrue, + * then sec_PKCS7EncryptLength needs to be made smarter! + */ + PORT_Assert (ofraglen == (pcount+padlen)); + output_len += ofraglen; + } else { + obj->pending_count = pcount; + } + + PORT_Assert (output_len_p != NULL || output_len == 0); + if (output_len_p != NULL) + *output_len_p = output_len; + + return SECSuccess; +} + +/* + * End of cipher stuff. + * ------------------------------------------------------------------- + */ + + +/* + * ------------------------------------------------------------------- + * XXX The following Attribute stuff really belongs elsewhere. + * The Attribute type is *not* part of pkcs7 but rather X.501. + * But for now, since PKCS7 is the only customer of attributes, + * we define them here. Once there is a use outside of PKCS7, + * then change the attribute types and functions from internal + * to external naming convention, and move them elsewhere! + */ + +/* + * Look through a set of attributes and find one that matches the + * specified object ID. If "only" is true, then make sure that + * there is not more than one attribute of the same type. Otherwise, + * just return the first one found. (XXX Does anybody really want + * that first-found behavior? It was like that when I found it...) + */ +SEC_PKCS7Attribute * +sec_PKCS7FindAttribute (SEC_PKCS7Attribute **attrs, SECOidTag oidtag, + PRBool only) +{ + SECOidData *oid; + SEC_PKCS7Attribute *attr1, *attr2; + + if (attrs == NULL) + return NULL; + + oid = SECOID_FindOIDByTag(oidtag); + if (oid == NULL) + return NULL; + + while ((attr1 = *attrs++) != NULL) { + if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data, + oid->oid.data, + oid->oid.len) == 0) + break; + } + + if (attr1 == NULL) + return NULL; + + if (!only) + return attr1; + + while ((attr2 = *attrs++) != NULL) { + if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data, + oid->oid.data, + oid->oid.len) == 0) + break; + } + + if (attr2 != NULL) + return NULL; + + return attr1; +} + + +/* + * Return the single attribute value, doing some sanity checking first: + * - Multiple values are *not* expected. + * - Empty values are *not* expected. + */ +SECItem * +sec_PKCS7AttributeValue(SEC_PKCS7Attribute *attr) +{ + SECItem *value; + + if (attr == NULL) + return NULL; + + value = attr->values[0]; + + if (value == NULL || value->data == NULL || value->len == 0) + return NULL; + + if (attr->values[1] != NULL) + return NULL; + + return value; +} + +static const SEC_ASN1Template * +sec_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS7Attribute *attribute; + SECOidData *oiddata; + PRBool encoded; + + PORT_Assert (src_or_dest != NULL); + if (src_or_dest == NULL) + return NULL; + + attribute = (SEC_PKCS7Attribute*)src_or_dest; + + if (encoding && attribute->encoded) + return SEC_AnyTemplate; + + oiddata = attribute->typeTag; + if (oiddata == NULL) { + oiddata = SECOID_FindOID(&attribute->type); + attribute->typeTag = oiddata; + } + + if (oiddata == NULL) { + encoded = PR_TRUE; + theTemplate = SEC_AnyTemplate; + } else { + switch (oiddata->offset) { + default: + encoded = PR_TRUE; + theTemplate = SEC_AnyTemplate; + break; + case SEC_OID_PKCS9_EMAIL_ADDRESS: + case SEC_OID_RFC1274_MAIL: + case SEC_OID_PKCS9_UNSTRUCTURED_NAME: + encoded = PR_FALSE; + theTemplate = SEC_IA5StringTemplate; + break; + case SEC_OID_PKCS9_CONTENT_TYPE: + encoded = PR_FALSE; + theTemplate = SEC_ObjectIDTemplate; + break; + case SEC_OID_PKCS9_MESSAGE_DIGEST: + encoded = PR_FALSE; + theTemplate = SEC_OctetStringTemplate; + break; + case SEC_OID_PKCS9_SIGNING_TIME: + encoded = PR_FALSE; + theTemplate = SEC_UTCTimeTemplate; + break; + /* XXX Want other types here, too */ + } + } + + if (encoding) { + /* + * If we are encoding and we think we have an already-encoded value, + * then the code which initialized this attribute should have set + * the "encoded" property to true (and we would have returned early, + * up above). No devastating error, but that code should be fixed. + * (It could indicate that the resulting encoded bytes are wrong.) + */ + PORT_Assert (!encoded); + } else { + /* + * We are decoding; record whether the resulting value is + * still encoded or not. + */ + attribute->encoded = encoded; + } + return theTemplate; +} + +static SEC_ChooseASN1TemplateFunc sec_attr_chooser + = sec_attr_choose_attr_value_template; + +static const SEC_ASN1Template sec_pkcs7_attribute_template[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SEC_PKCS7Attribute) }, + { SEC_ASN1_OBJECT_ID, + offsetof(SEC_PKCS7Attribute,type) }, + { SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF, + offsetof(SEC_PKCS7Attribute,values), + &sec_attr_chooser }, + { 0 } +}; + +static const SEC_ASN1Template sec_pkcs7_set_of_attribute_template[] = { + { SEC_ASN1_SET_OF, 0, sec_pkcs7_attribute_template }, +}; + +/* + * If you are wondering why this routine does not reorder the attributes + * first, and might be tempted to make it do so, see the comment by the + * call to ReorderAttributes in p7encode.c. (Or, see who else calls this + * and think long and hard about the implications of making it always + * do the reordering.) + */ +SECItem * +sec_PKCS7EncodeAttributes (PRArenaPool *poolp, SECItem *dest, void *src) +{ + return SEC_ASN1EncodeItem (poolp, dest, src, + sec_pkcs7_set_of_attribute_template); +} + +/* + * Make sure that the order of the attributes guarantees valid DER + * (which must be in lexigraphically ascending order for a SET OF); + * if reordering is necessary it will be done in place (in attrs). + */ +SECStatus +sec_PKCS7ReorderAttributes (SEC_PKCS7Attribute **attrs) +{ + PRArenaPool *poolp; + int num_attrs, i, j, pass, besti; + SECItem **enc_attrs; + SEC_PKCS7Attribute **new_attrs; + + /* + * I think we should not be called with NULL. But if we are, + * call it a success anyway, because the order *is* okay. + */ + PORT_Assert (attrs != NULL); + if (attrs == NULL) + return SECSuccess; + + /* + * Count how many attributes we are dealing with here. + */ + num_attrs = 0; + while (attrs[num_attrs] != NULL) + num_attrs++; + + /* + * Again, I think we should have some attributes here. + * But if we do not, or if there is only one, then call it + * a success because it also already has a fine order. + */ + PORT_Assert (num_attrs); + if (num_attrs == 0 || num_attrs == 1) + return SECSuccess; + + /* + * Allocate an arena for us to work with, so it is easy to + * clean up all of the memory (fairly small pieces, really). + */ + poolp = PORT_NewArena (1024); /* XXX what is right value? */ + if (poolp == NULL) + return SECFailure; /* no memory; nothing we can do... */ + + /* + * Allocate arrays to hold the individual encodings which we will use + * for comparisons and the reordered attributes as they are sorted. + */ + enc_attrs=(SECItem**)PORT_ArenaZAlloc(poolp, num_attrs*sizeof(SECItem *)); + new_attrs = (SEC_PKCS7Attribute**)PORT_ArenaZAlloc (poolp, + num_attrs * sizeof(SEC_PKCS7Attribute *)); + if (enc_attrs == NULL || new_attrs == NULL) { + PORT_FreeArena (poolp, PR_FALSE); + return SECFailure; + } + + /* + * DER encode each individual attribute. + */ + for (i = 0; i < num_attrs; i++) { + enc_attrs[i] = SEC_ASN1EncodeItem (poolp, NULL, attrs[i], + sec_pkcs7_attribute_template); + if (enc_attrs[i] == NULL) { + PORT_FreeArena (poolp, PR_FALSE); + return SECFailure; + } + } + + /* + * Now compare and sort them; this is not the most efficient sorting + * method, but it is just fine for the problem at hand, because the + * number of attributes is (always) going to be small. + */ + for (pass = 0; pass < num_attrs; pass++) { + /* + * Find the first not-yet-accepted attribute. (Once one is + * sorted into the other array, it is cleared from enc_attrs.) + */ + for (i = 0; i < num_attrs; i++) { + if (enc_attrs[i] != NULL) + break; + } + PORT_Assert (i < num_attrs); + besti = i; + + /* + * Find the lowest (lexigraphically) encoding. One that is + * shorter than all the rest is known to be "less" because each + * attribute is of the same type (a SEQUENCE) and so thus the + * first octet of each is the same, and the second octet is + * the length (or the length of the length with the high bit + * set, followed by the length, which also works out to always + * order the shorter first). Two (or more) that have the + * same length need to be compared byte by byte until a mismatch + * is found. + */ + for (i = besti + 1; i < num_attrs; i++) { + if (enc_attrs[i] == NULL) /* slot already handled */ + continue; + + if (enc_attrs[i]->len != enc_attrs[besti]->len) { + if (enc_attrs[i]->len < enc_attrs[besti]->len) + besti = i; + continue; + } + + for (j = 0; j < enc_attrs[i]->len; j++) { + if (enc_attrs[i]->data[j] < enc_attrs[besti]->data[j]) { + besti = i; + break; + } + } + + /* + * For this not to be true, we would have to have encountered + * two *identical* attributes, which I think we should not see. + * So assert if it happens, but even if it does, let it go + * through; the ordering of the two does not matter. + */ + PORT_Assert (j < enc_attrs[i]->len); + } + + /* + * Now we have found the next-lowest one; copy it over and + * remove it from enc_attrs. + */ + new_attrs[pass] = attrs[besti]; + enc_attrs[besti] = NULL; + } + + /* + * Now new_attrs has the attributes in the order we want; + * copy them back into the attrs array we started with. + */ + for (i = 0; i < num_attrs; i++) + attrs[i] = new_attrs[i]; + + PORT_FreeArena (poolp, PR_FALSE); + return SECSuccess; +} + +/* + * End of attribute stuff. + * ------------------------------------------------------------------- + */ + + +/* + * Templates and stuff. Keep these at the end of the file. + */ + +/* forward declaration */ +static const SEC_ASN1Template * +sec_pkcs7_choose_content_template(void *src_or_dest, PRBool encoding); + +static SEC_ChooseASN1TemplateFunc sec_pkcs7_chooser + = sec_pkcs7_choose_content_template; + +const SEC_ASN1Template sec_PKCS7ContentInfoTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, + 0, NULL, sizeof(SEC_PKCS7ContentInfo) }, + { SEC_ASN1_OBJECT_ID, + offsetof(SEC_PKCS7ContentInfo,contentType) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM + | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS7ContentInfo,content), + &sec_pkcs7_chooser }, + { 0 } +}; + +/* XXX These names should change from external to internal convention. */ + +static const SEC_ASN1Template SEC_PKCS7SignerInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SEC_PKCS7SignerInfo) }, + { SEC_ASN1_INTEGER, + offsetof(SEC_PKCS7SignerInfo,version) }, + { SEC_ASN1_POINTER, + offsetof(SEC_PKCS7SignerInfo,issuerAndSN), + CERT_IssuerAndSNTemplate }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7SignerInfo,digestAlg), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS7SignerInfo,authAttr), + sec_pkcs7_set_of_attribute_template }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7SignerInfo,digestEncAlg), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_OCTET_STRING, + offsetof(SEC_PKCS7SignerInfo,encDigest) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS7SignerInfo,unAuthAttr), + sec_pkcs7_set_of_attribute_template }, + { 0 } +}; + +static const SEC_ASN1Template SEC_PKCS7SignedDataTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, + 0, NULL, sizeof(SEC_PKCS7SignedData) }, + { SEC_ASN1_INTEGER, + offsetof(SEC_PKCS7SignedData,version) }, + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS7SignedData,digestAlgorithms), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7SignedData,contentInfo), + sec_PKCS7ContentInfoTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS7SignedData,rawCerts), + SEC_SetOfAnyTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS7SignedData,crls), + CERT_SetOfSignedCrlTemplate }, + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS7SignedData,signerInfos), + SEC_PKCS7SignerInfoTemplate }, + { 0 } +}; + +static const SEC_ASN1Template SEC_PointerToPKCS7SignedDataTemplate[] = { + { SEC_ASN1_POINTER, 0, SEC_PKCS7SignedDataTemplate } +}; + +static const SEC_ASN1Template SEC_PKCS7RecipientInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SEC_PKCS7RecipientInfo) }, + { SEC_ASN1_INTEGER, + offsetof(SEC_PKCS7RecipientInfo,version) }, + { SEC_ASN1_POINTER, + offsetof(SEC_PKCS7RecipientInfo,issuerAndSN), + CERT_IssuerAndSNTemplate }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7RecipientInfo,keyEncAlg), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_OCTET_STRING, + offsetof(SEC_PKCS7RecipientInfo,encKey) }, + { 0 } +}; + +static const SEC_ASN1Template SEC_PKCS7EncryptedContentInfoTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, + 0, NULL, sizeof(SEC_PKCS7EncryptedContentInfo) }, + { SEC_ASN1_OBJECT_ID, + offsetof(SEC_PKCS7EncryptedContentInfo,contentType) }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7EncryptedContentInfo,contentEncAlg), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_MAY_STREAM | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS7EncryptedContentInfo,encContent), + SEC_OctetStringTemplate }, + { 0 } +}; + +static const SEC_ASN1Template SEC_PKCS7EnvelopedDataTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, + 0, NULL, sizeof(SEC_PKCS7EnvelopedData) }, + { SEC_ASN1_INTEGER, + offsetof(SEC_PKCS7EnvelopedData,version) }, + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS7EnvelopedData,recipientInfos), + SEC_PKCS7RecipientInfoTemplate }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7EnvelopedData,encContentInfo), + SEC_PKCS7EncryptedContentInfoTemplate }, + { 0 } +}; + +static const SEC_ASN1Template SEC_PointerToPKCS7EnvelopedDataTemplate[] = { + { SEC_ASN1_POINTER, 0, SEC_PKCS7EnvelopedDataTemplate } +}; + +static const SEC_ASN1Template SEC_PKCS7SignedAndEnvelopedDataTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, + 0, NULL, sizeof(SEC_PKCS7SignedAndEnvelopedData) }, + { SEC_ASN1_INTEGER, + offsetof(SEC_PKCS7SignedAndEnvelopedData,version) }, + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS7SignedAndEnvelopedData,recipientInfos), + SEC_PKCS7RecipientInfoTemplate }, + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS7SignedAndEnvelopedData,digestAlgorithms), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7SignedAndEnvelopedData,encContentInfo), + SEC_PKCS7EncryptedContentInfoTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(SEC_PKCS7SignedAndEnvelopedData,rawCerts), + SEC_SetOfAnyTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(SEC_PKCS7SignedAndEnvelopedData,crls), + CERT_SetOfSignedCrlTemplate }, + { SEC_ASN1_SET_OF, + offsetof(SEC_PKCS7SignedAndEnvelopedData,signerInfos), + SEC_PKCS7SignerInfoTemplate }, + { 0 } +}; + +static const SEC_ASN1Template +SEC_PointerToPKCS7SignedAndEnvelopedDataTemplate[] = { + { SEC_ASN1_POINTER, 0, SEC_PKCS7SignedAndEnvelopedDataTemplate } +}; + +static const SEC_ASN1Template SEC_PKCS7DigestedDataTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, + 0, NULL, sizeof(SEC_PKCS7DigestedData) }, + { SEC_ASN1_INTEGER, + offsetof(SEC_PKCS7DigestedData,version) }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7DigestedData,digestAlg), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7DigestedData,contentInfo), + sec_PKCS7ContentInfoTemplate }, + { SEC_ASN1_OCTET_STRING, + offsetof(SEC_PKCS7DigestedData,digest) }, + { 0 } +}; + +static const SEC_ASN1Template SEC_PointerToPKCS7DigestedDataTemplate[] = { + { SEC_ASN1_POINTER, 0, SEC_PKCS7DigestedDataTemplate } +}; + +static const SEC_ASN1Template SEC_PKCS7EncryptedDataTemplate[] = { + { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, + 0, NULL, sizeof(SEC_PKCS7EncryptedData) }, + { SEC_ASN1_INTEGER, + offsetof(SEC_PKCS7EncryptedData,version) }, + { SEC_ASN1_INLINE, + offsetof(SEC_PKCS7EncryptedData,encContentInfo), + SEC_PKCS7EncryptedContentInfoTemplate }, + { 0 } +}; + +static const SEC_ASN1Template SEC_PointerToPKCS7EncryptedDataTemplate[] = { + { SEC_ASN1_POINTER, 0, SEC_PKCS7EncryptedDataTemplate } +}; + +const SEC_ASN1Template SEC_SMIMEKEAParamTemplateSkipjack[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SEC_PKCS7SMIMEKEAParameters) }, + { SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */, + offsetof(SEC_PKCS7SMIMEKEAParameters,originatorKEAKey) }, + { SEC_ASN1_OCTET_STRING, + offsetof(SEC_PKCS7SMIMEKEAParameters,originatorRA) }, + { 0 } +}; + +const SEC_ASN1Template SEC_SMIMEKEAParamTemplateNoSkipjack[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SEC_PKCS7SMIMEKEAParameters) }, + { SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */, + offsetof(SEC_PKCS7SMIMEKEAParameters,originatorKEAKey) }, + { SEC_ASN1_OCTET_STRING, + offsetof(SEC_PKCS7SMIMEKEAParameters,originatorRA) }, + { SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL , + offsetof(SEC_PKCS7SMIMEKEAParameters,nonSkipjackIV) }, + { 0 } +}; + +const SEC_ASN1Template SEC_SMIMEKEAParamTemplateAllParams[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SEC_PKCS7SMIMEKEAParameters) }, + { SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */, + offsetof(SEC_PKCS7SMIMEKEAParameters,originatorKEAKey) }, + { SEC_ASN1_OCTET_STRING, + offsetof(SEC_PKCS7SMIMEKEAParameters,originatorRA) }, + { SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL , + offsetof(SEC_PKCS7SMIMEKEAParameters,nonSkipjackIV) }, + { SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL , + offsetof(SEC_PKCS7SMIMEKEAParameters,bulkKeySize) }, + { 0 } +}; + +const SEC_ASN1Template* +sec_pkcs7_get_kea_template(SECKEATemplateSelector whichTemplate) +{ + const SEC_ASN1Template *returnVal = NULL; + + switch(whichTemplate) + { + case SECKEAUsesNonSkipjack: + returnVal = SEC_SMIMEKEAParamTemplateNoSkipjack; + break; + case SECKEAUsesSkipjack: + returnVal = SEC_SMIMEKEAParamTemplateSkipjack; + break; + case SECKEAUsesNonSkipjackWithPaddedEncKey: + default: + returnVal = SEC_SMIMEKEAParamTemplateAllParams; + break; + } + return returnVal; +} + +static const SEC_ASN1Template * +sec_pkcs7_choose_content_template(void *src_or_dest, PRBool encoding) +{ + const SEC_ASN1Template *theTemplate; + SEC_PKCS7ContentInfo *cinfo; + SECOidTag kind; + + PORT_Assert (src_or_dest != NULL); + if (src_or_dest == NULL) + return NULL; + + cinfo = (SEC_PKCS7ContentInfo*)src_or_dest; + kind = SEC_PKCS7ContentType (cinfo); + switch (kind) { + default: + theTemplate = SEC_PointerToAnyTemplate; + break; + case SEC_OID_PKCS7_DATA: + theTemplate = SEC_PointerToOctetStringTemplate; + break; + case SEC_OID_PKCS7_SIGNED_DATA: + theTemplate = SEC_PointerToPKCS7SignedDataTemplate; + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + theTemplate = SEC_PointerToPKCS7EnvelopedDataTemplate; + break; + case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: + theTemplate = SEC_PointerToPKCS7SignedAndEnvelopedDataTemplate; + break; + case SEC_OID_PKCS7_DIGESTED_DATA: + theTemplate = SEC_PointerToPKCS7DigestedDataTemplate; + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + theTemplate = SEC_PointerToPKCS7EncryptedDataTemplate; + break; + } + return theTemplate; +} + +/* + * End of templates. Do not add stuff after this; put new code + * up above the start of the template definitions. + */ diff --git a/security/nss/lib/pkcs7/p7local.h b/security/nss/lib/pkcs7/p7local.h new file mode 100644 index 000000000..80640c930 --- /dev/null +++ b/security/nss/lib/pkcs7/p7local.h @@ -0,0 +1,176 @@ +/* + * 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. + */ + +/* + * Support routines for PKCS7 implementation, none of which are exported. + * This file should only contain things that are needed by both the + * encoding/creation side *and* the decoding/decryption side. Anything + * else should just be static routines in the appropriate file. + * + * Do not export this file! If something in here is really needed outside + * of pkcs7 code, first try to add a PKCS7 interface which will do it for + * you. If that has a problem, then just move out what you need, changing + * its name as appropriate! + * + * $Id$ + */ + +#ifndef _P7LOCAL_H_ +#define _P7LOCAL_H_ + +#include "secpkcs7.h" +#include "secasn1t.h" + +extern const SEC_ASN1Template sec_PKCS7ContentInfoTemplate[]; + +/* opaque objects */ +typedef struct sec_pkcs7_cipher_object sec_PKCS7CipherObject; + + +/************************************************************************/ +SEC_BEGIN_PROTOS + +/* + * Look through a set of attributes and find one that matches the + * specified object ID. If "only" is true, then make sure that + * there is not more than one attribute of the same type. Otherwise, + * just return the first one found. (XXX Does anybody really want + * that first-found behavior? It was like that when I found it...) + */ +extern SEC_PKCS7Attribute *sec_PKCS7FindAttribute (SEC_PKCS7Attribute **attrs, + SECOidTag oidtag, + PRBool only); +/* + * Return the single attribute value, doing some sanity checking first: + * - Multiple values are *not* expected. + * - Empty values are *not* expected. + */ +extern SECItem *sec_PKCS7AttributeValue (SEC_PKCS7Attribute *attr); + +/* + * Encode a set of attributes (found in "src"). + */ +extern SECItem *sec_PKCS7EncodeAttributes (PRArenaPool *poolp, + SECItem *dest, void *src); + +/* + * Make sure that the order of the attributes guarantees valid DER + * (which must be in lexigraphically ascending order for a SET OF); + * if reordering is necessary it will be done in place (in attrs). + */ +extern SECStatus sec_PKCS7ReorderAttributes (SEC_PKCS7Attribute **attrs); + + +/* + * Create a context for decrypting, based on the given key and algorithm. + */ +extern sec_PKCS7CipherObject * +sec_PKCS7CreateDecryptObject (PK11SymKey *key, SECAlgorithmID *algid); + +/* + * Create a context for encrypting, based on the given key and algorithm, + * and fill in the algorithm id. + */ +extern sec_PKCS7CipherObject * +sec_PKCS7CreateEncryptObject (PRArenaPool *poolp, PK11SymKey *key, + SECOidTag algtag, SECAlgorithmID *algid); + +/* + * Destroy the given decryption or encryption object. + */ +extern void sec_PKCS7DestroyDecryptObject (sec_PKCS7CipherObject *obj); +extern void sec_PKCS7DestroyEncryptObject (sec_PKCS7CipherObject *obj); + +/* + * What will be the output length of the next call to encrypt/decrypt? + * Result can be used to perform memory allocations. Note that the amount + * is exactly accurate only when not doing a block cipher or when final + * is false, otherwise it is an upper bound on the amount because until + * we see the data we do not know how many padding bytes there are + * (always between 1 and the cipher block size). + * + * Note that this can return zero, which does not mean that the cipher + * operation can be skipped! (It simply means that there are not enough + * bytes to make up an entire block; the bytes will be reserved until + * there are enough to encrypt/decrypt at least one block.) However, + * if zero is returned it *does* mean that no output buffer need be + * passed in to the subsequent cipher operation, as no output bytes + * will be stored. + */ +extern unsigned int sec_PKCS7DecryptLength (sec_PKCS7CipherObject *obj, + unsigned int input_len, + PRBool final); +extern unsigned int sec_PKCS7EncryptLength (sec_PKCS7CipherObject *obj, + unsigned int input_len, + PRBool final); + +/* + * Decrypt a given length of input buffer (starting at "input" and + * containing "input_len" bytes), placing the decrypted bytes in + * "output" and storing the output length in "*output_len_p". + * "obj" is the return value from sec_PKCS7CreateDecryptObject. + * When "final" is true, this is the last of the data to be decrypted. + */ +extern SECStatus sec_PKCS7Decrypt (sec_PKCS7CipherObject *obj, + unsigned char *output, + unsigned int *output_len_p, + unsigned int max_output_len, + const unsigned char *input, + unsigned int input_len, + PRBool final); + +/* + * Encrypt a given length of input buffer (starting at "input" and + * containing "input_len" bytes), placing the encrypted bytes in + * "output" and storing the output length in "*output_len_p". + * "obj" is the return value from sec_PKCS7CreateEncryptObject. + * When "final" is true, this is the last of the data to be encrypted. + */ +extern SECStatus sec_PKCS7Encrypt (sec_PKCS7CipherObject *obj, + unsigned char *output, + unsigned int *output_len_p, + unsigned int max_output_len, + const unsigned char *input, + unsigned int input_len, + PRBool final); + +/* return the correct kea template based on the template selector. skipjack + * does not have the extra IV. + */ +const SEC_ASN1Template * +sec_pkcs7_get_kea_template(SECKEATemplateSelector whichTemplate); + +/************************************************************************/ +SEC_END_PROTOS + +#endif /* _P7LOCAL_H_ */ diff --git a/security/nss/lib/pkcs7/pkcs7t.h b/security/nss/lib/pkcs7/pkcs7t.h new file mode 100644 index 000000000..73b23014d --- /dev/null +++ b/security/nss/lib/pkcs7/pkcs7t.h @@ -0,0 +1,292 @@ +/* + * 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. + */ + +/* + * Header for pkcs7 types. + * + * $Id$ + */ + +#ifndef _PKCS7T_H_ +#define _PKCS7T_H_ + +#include "plarena.h" + +#include "seccomon.h" +#include "secoidt.h" +#include "certt.h" +#include "secmodt.h" + +/* Opaque objects */ +typedef struct SEC_PKCS7DecoderContextStr SEC_PKCS7DecoderContext; +typedef struct SEC_PKCS7EncoderContextStr SEC_PKCS7EncoderContext; + +/* Non-opaque objects. NOTE, though: I want them to be treated as + * opaque as much as possible. If I could hide them completely, + * I would. (I tried, but ran into trouble that was taking me too + * much time to get out of.) I still intend to try to do so. + * In fact, the only type that "outsiders" should even *name* is + * SEC_PKCS7ContentInfo, and they should not reference its fields. + */ +/* rjr: PKCS #11 cert handling (pk11cert.c) does use SEC_PKCS7RecipientInfo's. + * This is because when we search the recipient list for the cert and key we + * want, we need to invert the order of the loops we used to have. The old + * loops were: + * + * For each recipient { + * find_cert = PK11_Find_AllCert(recipient->issuerSN); + * [which unrolls to... ] + * For each slot { + * Log into slot; + * search slot for cert; + * } + * } + * + * the new loop searchs all the recipients at once on a slot. this allows + * PKCS #11 to order slots in such a way that logout slots don't get checked + * if we can find the cert on a logged in slot. This eliminates lots of + * spurious password prompts when smart cards are installed... so why this + * comment? If you make SEC_PKCS7RecipientInfo completely opaque, you need + * to provide a non-opaque list of issuerSN's (the only field PKCS#11 needs + * and fix up pk11cert.c first. NOTE: Only S/MIME calls this special PKCS #11 + * function. + */ +typedef struct SEC_PKCS7ContentInfoStr SEC_PKCS7ContentInfo; +typedef struct SEC_PKCS7SignedDataStr SEC_PKCS7SignedData; +typedef struct SEC_PKCS7EncryptedContentInfoStr SEC_PKCS7EncryptedContentInfo; +typedef struct SEC_PKCS7EnvelopedDataStr SEC_PKCS7EnvelopedData; +typedef struct SEC_PKCS7SignedAndEnvelopedDataStr + SEC_PKCS7SignedAndEnvelopedData; +typedef struct SEC_PKCS7SignerInfoStr SEC_PKCS7SignerInfo; +typedef struct SEC_PKCS7RecipientInfoStr SEC_PKCS7RecipientInfo; +typedef struct SEC_PKCS7DigestedDataStr SEC_PKCS7DigestedData; +typedef struct SEC_PKCS7EncryptedDataStr SEC_PKCS7EncryptedData; +typedef struct SEC_PKCS7SMIMEKEAParametersStr SEC_PKCS7SMIMEKEAParameters; +/* + * The following is not actually a PKCS7 type, but for now it is only + * used by PKCS7, so we have adopted it. If someone else *ever* needs + * it, its name should be changed and it should be moved out of here. + * Do not dare to use it without doing so! + */ +typedef struct SEC_PKCS7AttributeStr SEC_PKCS7Attribute; + + +struct SEC_PKCS7ContentInfoStr { + PRArenaPool *poolp; /* local; not part of encoding */ + PRBool created; /* local; not part of encoding */ + int refCount; /* local; not part of encoding */ + SECOidData *contentTypeTag; /* local; not part of encoding */ + SECKEYGetPasswordKey pwfn; /* local; not part of encoding */ + void *pwfn_arg; /* local; not part of encoding */ + SECItem contentType; + union { + SECItem *data; + SEC_PKCS7DigestedData *digestedData; + SEC_PKCS7EncryptedData *encryptedData; + SEC_PKCS7EnvelopedData *envelopedData; + SEC_PKCS7SignedData *signedData; + SEC_PKCS7SignedAndEnvelopedData *signedAndEnvelopedData; + } content; +}; + +struct SEC_PKCS7SignedDataStr { + SECItem version; + SECAlgorithmID **digestAlgorithms; + SEC_PKCS7ContentInfo contentInfo; + SECItem **rawCerts; + CERTSignedCrl **crls; + SEC_PKCS7SignerInfo **signerInfos; + SECItem **digests; /* local; not part of encoding */ + CERTCertificate **certs; /* local; not part of encoding */ + CERTCertificateList **certLists; /* local; not part of encoding */ +}; +#define SEC_PKCS7_SIGNED_DATA_VERSION 1 /* what we *create* */ + +struct SEC_PKCS7EncryptedContentInfoStr { + SECOidData *contentTypeTag; /* local; not part of encoding */ + SECItem contentType; + SECAlgorithmID contentEncAlg; + SECItem encContent; + SECItem plainContent; /* local; not part of encoding */ + /* bytes not encrypted, but encoded */ + int keysize; /* local; not part of encoding */ + /* size of bulk encryption key + * (only used by creation code) */ + SECOidTag encalg; /* local; not part of encoding */ + /* oid tag of encryption algorithm + * (only used by creation code) */ +}; + +struct SEC_PKCS7EnvelopedDataStr { + SECItem version; + SEC_PKCS7RecipientInfo **recipientInfos; + SEC_PKCS7EncryptedContentInfo encContentInfo; +}; +#define SEC_PKCS7_ENVELOPED_DATA_VERSION 0 /* what we *create* */ + +struct SEC_PKCS7SignedAndEnvelopedDataStr { + SECItem version; + SEC_PKCS7RecipientInfo **recipientInfos; + SECAlgorithmID **digestAlgorithms; + SEC_PKCS7EncryptedContentInfo encContentInfo; + SECItem **rawCerts; + CERTSignedCrl **crls; + SEC_PKCS7SignerInfo **signerInfos; + SECItem **digests; /* local; not part of encoding */ + CERTCertificate **certs; /* local; not part of encoding */ + CERTCertificateList **certLists; /* local; not part of encoding */ + PK11SymKey *sigKey; /* local; not part of encoding */ +}; +#define SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION 1 /* what we *create* */ + +struct SEC_PKCS7SignerInfoStr { + SECItem version; + CERTIssuerAndSN *issuerAndSN; + SECAlgorithmID digestAlg; + SEC_PKCS7Attribute **authAttr; + SECAlgorithmID digestEncAlg; + SECItem encDigest; + SEC_PKCS7Attribute **unAuthAttr; + CERTCertificate *cert; /* local; not part of encoding */ + CERTCertificateList *certList; /* local; not part of encoding */ +}; +#define SEC_PKCS7_SIGNER_INFO_VERSION 1 /* what we *create* */ + +struct SEC_PKCS7RecipientInfoStr { + SECItem version; + CERTIssuerAndSN *issuerAndSN; + SECAlgorithmID keyEncAlg; + SECItem encKey; + CERTCertificate *cert; /* local; not part of encoding */ +}; +#define SEC_PKCS7_RECIPIENT_INFO_VERSION 0 /* what we *create* */ + +struct SEC_PKCS7DigestedDataStr { + SECItem version; + SECAlgorithmID digestAlg; + SEC_PKCS7ContentInfo contentInfo; + SECItem digest; +}; +#define SEC_PKCS7_DIGESTED_DATA_VERSION 0 /* what we *create* */ + +struct SEC_PKCS7EncryptedDataStr { + SECItem version; + SEC_PKCS7EncryptedContentInfo encContentInfo; +}; +#define SEC_PKCS7_ENCRYPTED_DATA_VERSION 0 /* what we *create* */ + +/* + * See comment above about this type not really belonging to PKCS7. + */ +struct SEC_PKCS7AttributeStr { + /* The following fields make up an encoded Attribute: */ + SECItem type; + SECItem **values; /* data may or may not be encoded */ + /* The following fields are not part of an encoded Attribute: */ + SECOidData *typeTag; + PRBool encoded; /* when true, values are encoded */ +}; + +/* An enumerated type used to select templates based on the encryption + scenario and data specifics. */ +typedef enum +{ + SECKEAUsesSkipjack, + SECKEAUsesNonSkipjack, + SECKEAUsesNonSkipjackWithPaddedEncKey +} SECKEATemplateSelector; + +/* ### mwelch - S/MIME KEA parameters. These don't really fit here, + but I cannot think of a more appropriate place at this time. */ +struct SEC_PKCS7SMIMEKEAParametersStr { + SECItem originatorKEAKey; /* sender KEA key (encrypted?) */ + SECItem originatorRA; /* random number generated by sender */ + SECItem nonSkipjackIV; /* init'n vector for SkipjackCBC64 + decryption of KEA key if Skipjack + is not the bulk algorithm used on + the message */ + SECItem bulkKeySize; /* if Skipjack is not the bulk + algorithm used on the message, + and the size of the bulk encryption + key is not the same as that of + originatorKEAKey (due to padding + perhaps), this field will contain + the real size of the bulk encryption + key. */ +}; + +/* + * Type of function passed to SEC_PKCS7Decode or SEC_PKCS7DecoderStart. + * If specified, this is where the content bytes (only) will be "sent" + * as they are recovered during the decoding. + * + * XXX Should just combine this with SEC_PKCS7EncoderContentCallback type + * and use a simpler, common name. + */ +typedef void (* SEC_PKCS7DecoderContentCallback)(void *arg, + const char *buf, + unsigned long len); + +/* + * Type of function passed to SEC_PKCS7Encode or SEC_PKCS7EncoderStart. + * This is where the encoded bytes will be "sent". + * + * XXX Should just combine this with SEC_PKCS7DecoderContentCallback type + * and use a simpler, common name. + */ +typedef void (* SEC_PKCS7EncoderOutputCallback)(void *arg, + const char *buf, + unsigned long len); + + +/* + * Type of function passed to SEC_PKCS7Decode or SEC_PKCS7DecoderStart + * to retrieve the decryption key. This function is inteded to be + * used for EncryptedData content info's which do not have a key available + * in a certificate, etc. + */ +typedef PK11SymKey * (* SEC_PKCS7GetDecryptKeyCallback)(void *arg, + SECAlgorithmID *algid); + +/* + * Type of function passed to SEC_PKCS7Decode or SEC_PKCS7DecoderStart. + * This function in intended to be used to verify that decrypting a + * particular crypto algorithm is allowed. Content types which do not + * require decryption will not need the callback. If the callback + * is not specified for content types which require decryption, the + * decryption will be disallowed. + */ +typedef PRBool (* SEC_PKCS7DecryptionAllowedCallback)(SECAlgorithmID *algid, + PK11SymKey *bulkkey); + +#endif /* _PKCS7T_H_ */ diff --git a/security/nss/lib/pkcs7/secmime.c b/security/nss/lib/pkcs7/secmime.c new file mode 100644 index 000000000..017896bd7 --- /dev/null +++ b/security/nss/lib/pkcs7/secmime.c @@ -0,0 +1,901 @@ +/* + * 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. + */ + +/* + * Stuff specific to S/MIME policy and interoperability. + * Depends on PKCS7, but there should be no dependency the other way around. + * + * $Id$ + */ + +#include "secmime.h" +#include "secoid.h" +#include "pk11func.h" +#include "ciferfam.h" /* for CIPHER_FAMILY symbols */ +#include "secasn1.h" +#include "secitem.h" +#include "cert.h" +#include "key.h" +#include "secerr.h" + +typedef struct smime_cipher_map_struct { + unsigned long cipher; + SECOidTag algtag; + SECItem *parms; +} smime_cipher_map; + +/* + * These are macros because I think some subsequent parameters, + * like those for RC5, will want to use them, too, separately. + */ +#define SMIME_DER_INTVAL_16 SEC_ASN1_INTEGER, 0x01, 0x10 +#define SMIME_DER_INTVAL_40 SEC_ASN1_INTEGER, 0x01, 0x28 +#define SMIME_DER_INTVAL_64 SEC_ASN1_INTEGER, 0x01, 0x40 +#define SMIME_DER_INTVAL_128 SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 + +#ifdef SMIME_DOES_RC5 /* will be needed; quiet unused warning for now */ +static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 }; +#endif +static unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 }; +static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 }; +static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 }; + +static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) }; +static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) }; +static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) }; + +static smime_cipher_map smime_cipher_maps[] = { + { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &smime_rc2p40 }, + { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &smime_rc2p64 }, + { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &smime_rc2p128 }, +#ifdef SMIME_DOES_RC5 + { SMIME_RC5PAD_64_16_40, SEC_OID_RC5_CBC_PAD, &smime_rc5p40 }, + { SMIME_RC5PAD_64_16_64, SEC_OID_RC5_CBC_PAD, &smime_rc5p64 }, + { SMIME_RC5PAD_64_16_128, SEC_OID_RC5_CBC_PAD, &smime_rc5p128 }, +#endif + { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL }, + { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL }, + { SMIME_FORTEZZA, SEC_OID_FORTEZZA_SKIPJACK, NULL} +}; + +/* + * Note, the following value really just needs to be an upper bound + * on the ciphers. + */ +static const int smime_symmetric_count = sizeof(smime_cipher_maps) + / sizeof(smime_cipher_map); + +static unsigned long *smime_prefs, *smime_newprefs; +static int smime_current_pref_index = 0; +static PRBool smime_prefs_complete = PR_FALSE; +static PRBool smime_prefs_changed = PR_TRUE; + +static unsigned long smime_policy_bits = 0; + + +static int +smime_mapi_by_cipher (unsigned long cipher) +{ + int i; + + for (i = 0; i < smime_symmetric_count; i++) { + if (smime_cipher_maps[i].cipher == cipher) + break; + } + + if (i == smime_symmetric_count) + return -1; + + return i; +} + + +/* + * this function locally records the user's preference + */ +SECStatus +SECMIME_EnableCipher(long which, int on) +{ + unsigned long mask; + + if (smime_newprefs == NULL || smime_prefs_complete) { + /* + * This is either the very first time, or we are starting over. + */ + smime_newprefs = (unsigned long*)PORT_ZAlloc (smime_symmetric_count + * sizeof(*smime_newprefs)); + if (smime_newprefs == NULL) + return SECFailure; + smime_current_pref_index = 0; + smime_prefs_complete = PR_FALSE; + } + + mask = which & CIPHER_FAMILYID_MASK; + if (mask == CIPHER_FAMILYID_MASK) { + /* + * This call signifies that all preferences have been set. + * Move "newprefs" over, after checking first whether or + * not the new ones are different from the old ones. + */ + if (smime_prefs != NULL) { + if (PORT_Memcmp (smime_prefs, smime_newprefs, + smime_symmetric_count * sizeof(*smime_prefs)) == 0) + smime_prefs_changed = PR_FALSE; + else + smime_prefs_changed = PR_TRUE; + PORT_Free (smime_prefs); + } + + smime_prefs = smime_newprefs; + smime_prefs_complete = PR_TRUE; + return SECSuccess; + } + + PORT_Assert (mask == CIPHER_FAMILYID_SMIME); + if (mask != CIPHER_FAMILYID_SMIME) { + /* XXX set an error! */ + return SECFailure; + } + + if (on) { + PORT_Assert (smime_current_pref_index < smime_symmetric_count); + if (smime_current_pref_index >= smime_symmetric_count) { + /* XXX set an error! */ + return SECFailure; + } + + smime_newprefs[smime_current_pref_index++] = which; + } + + return SECSuccess; +} + + +/* + * this function locally records the export policy + */ +SECStatus +SECMIME_SetPolicy(long which, int on) +{ + unsigned long mask; + + PORT_Assert ((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME); + if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) { + /* XXX set an error! */ + return SECFailure; + } + + which &= ~CIPHER_FAMILYID_MASK; + + PORT_Assert (which < 32); /* bits in the long */ + if (which >= 32) { + /* XXX set an error! */ + return SECFailure; + } + + mask = 1UL << which; + + if (on) { + smime_policy_bits |= mask; + } else { + smime_policy_bits &= ~mask; + } + + return SECSuccess; +} + + +/* + * Based on the given algorithm (including its parameters, in some cases!) + * and the given key (may or may not be inspected, depending on the + * algorithm), find the appropriate policy algorithm specification + * and return it. If no match can be made, -1 is returned. + */ +static long +smime_policy_algorithm (SECAlgorithmID *algid, PK11SymKey *key) +{ + SECOidTag algtag; + + algtag = SECOID_GetAlgorithmTag (algid); + switch (algtag) { + case SEC_OID_RC2_CBC: + { + unsigned int keylen_bits; + + keylen_bits = PK11_GetKeyStrength (key, algid); + switch (keylen_bits) { + case 40: + return SMIME_RC2_CBC_40; + case 64: + return SMIME_RC2_CBC_64; + case 128: + return SMIME_RC2_CBC_128; + default: + break; + } + } + break; + case SEC_OID_DES_CBC: + return SMIME_DES_CBC_56; + case SEC_OID_DES_EDE3_CBC: + return SMIME_DES_EDE3_168; + case SEC_OID_FORTEZZA_SKIPJACK: + return SMIME_FORTEZZA; +#ifdef SMIME_DOES_RC5 + case SEC_OID_RC5_CBC_PAD: + PORT_Assert (0); /* XXX need to pull out parameters and match */ + break; +#endif + default: + break; + } + + return -1; +} + + +static PRBool +smime_cipher_allowed (unsigned long which) +{ + unsigned long mask; + + which &= ~CIPHER_FAMILYID_MASK; + PORT_Assert (which < 32); /* bits per long (min) */ + if (which >= 32) + return PR_FALSE; + + mask = 1UL << which; + if ((mask & smime_policy_bits) == 0) + return PR_FALSE; + + return PR_TRUE; +} + + +PRBool +SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key) +{ + long which; + + which = smime_policy_algorithm (algid, key); + if (which < 0) + return PR_FALSE; + + return smime_cipher_allowed ((unsigned long)which); +} + + +/* + * Does the current policy allow *any* S/MIME encryption (or decryption)? + * + * This tells whether or not *any* S/MIME encryption can be done, + * according to policy. Callers may use this to do nicer user interface + * (say, greying out a checkbox so a user does not even try to encrypt + * a message when they are not allowed to) or for any reason they want + * to check whether S/MIME encryption (or decryption, for that matter) + * may be done. + * + * It takes no arguments. The return value is a simple boolean: + * PR_TRUE means encryption (or decryption) is *possible* + * (but may still fail due to other reasons, like because we cannot + * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee) + * PR_FALSE means encryption (or decryption) is not permitted + * + * There are no errors from this routine. + */ +PRBool +SECMIME_EncryptionPossible (void) +{ + if (smime_policy_bits != 0) + return PR_TRUE; + + return PR_FALSE; +} + + +/* + * XXX Would like the "parameters" field to be a SECItem *, but the + * encoder is having trouble with optional pointers to an ANY. Maybe + * once that is fixed, can change this back... + */ +typedef struct smime_capability_struct { + unsigned long cipher; /* local; not part of encoding */ + SECOidTag capIDTag; /* local; not part of encoding */ + SECItem capabilityID; + SECItem parameters; +} smime_capability; + +static const SEC_ASN1Template smime_capability_template[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(smime_capability) }, + { SEC_ASN1_OBJECT_ID, + offsetof(smime_capability,capabilityID), }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, + offsetof(smime_capability,parameters), }, + { 0, } +}; + +static const SEC_ASN1Template smime_capabilities_template[] = { + { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template } +}; + + + +static void +smime_fill_capability (smime_capability *cap) +{ + unsigned long cipher; + SECOidTag algtag; + int i; + + algtag = SECOID_FindOIDTag (&(cap->capabilityID)); + + for (i = 0; i < smime_symmetric_count; i++) { + if (smime_cipher_maps[i].algtag != algtag) + continue; + /* + * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing + * 2 NULLs as equal and NULL and non-NULL as not equal), we could + * use that here instead of all of the following comparison code. + */ + if (cap->parameters.data != NULL) { + if (smime_cipher_maps[i].parms == NULL) + continue; + if (cap->parameters.len != smime_cipher_maps[i].parms->len) + continue; + if (PORT_Memcmp (cap->parameters.data, + smime_cipher_maps[i].parms->data, + cap->parameters.len) == 0) + break; + } else if (smime_cipher_maps[i].parms == NULL) { + break; + } + } + + if (i == smime_symmetric_count) + cipher = 0; + else + cipher = smime_cipher_maps[i].cipher; + + cap->cipher = cipher; + cap->capIDTag = algtag; +} + + +static long +smime_choose_cipher (CERTCertificate *scert, CERTCertificate **rcerts) +{ + PRArenaPool *poolp; + long chosen_cipher; + int *cipher_abilities; + int *cipher_votes; + int strong_mapi; + int rcount, mapi, max, i; + PRBool isFortezza = PK11_FortezzaHasKEA(scert); + + if (smime_policy_bits == 0) { + PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM); + return -1; + } + + chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */ + + poolp = PORT_NewArena (1024); /* XXX what is right value? */ + if (poolp == NULL) + goto done; + + cipher_abilities = (int*)PORT_ArenaZAlloc (poolp, + smime_symmetric_count * sizeof(int)); + if (cipher_abilities == NULL) + goto done; + + cipher_votes = (int*)PORT_ArenaZAlloc (poolp, + smime_symmetric_count * sizeof(int)); + if (cipher_votes == NULL) + goto done; + + /* + * XXX Should have a #define somewhere which specifies default + * strong cipher. (Or better, a way to configure, which would + * take Fortezza into account as well.) + */ + + /* If the user has the Fortezza preference turned on, make + * that the strong cipher. Otherwise, use triple-DES. */ + strong_mapi = -1; + if (isFortezza) { + for(i=0;i < smime_current_pref_index && strong_mapi < 0;i++) + { + if (smime_prefs[i] == SMIME_FORTEZZA) + strong_mapi = smime_mapi_by_cipher(SMIME_FORTEZZA); + } + } + + if (strong_mapi == -1) + strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168); + + PORT_Assert (strong_mapi >= 0); + + for (rcount = 0; rcerts[rcount] != NULL; rcount++) { + SECItem *profile; + smime_capability **caps; + int capi, pref; + SECStatus dstat; + + pref = smime_symmetric_count; + profile = CERT_FindSMimeProfile (rcerts[rcount]); + if (profile != NULL && profile->data != NULL && profile->len > 0) { + caps = NULL; + dstat = SEC_ASN1DecodeItem (poolp, &caps, + smime_capabilities_template, + profile); + if (dstat == SECSuccess && caps != NULL) { + for (capi = 0; caps[capi] != NULL; capi++) { + smime_fill_capability (caps[capi]); + mapi = smime_mapi_by_cipher (caps[capi]->cipher); + if (mapi >= 0) { + cipher_abilities[mapi]++; + cipher_votes[mapi] += pref; + --pref; + } + } + } + } else { + SECKEYPublicKey *key; + unsigned int pklen_bits; + + /* + * XXX This is probably only good for RSA keys. What I would + * really like is a function to just say; Is the public key in + * this cert an export-length key? Then I would not have to + * know things like the value 512, or the kind of key, or what + * a subjectPublicKeyInfo is, etc. + */ + key = CERT_ExtractPublicKey (rcerts[rcount]); + if (key != NULL) { + pklen_bits = SECKEY_PublicKeyStrength (key) * 8; + SECKEY_DestroyPublicKey (key); + + if (pklen_bits > 512) { + cipher_abilities[strong_mapi]++; + cipher_votes[strong_mapi] += pref; + } + } + } + if (profile != NULL) + SECITEM_FreeItem (profile, PR_TRUE); + } + + max = 0; + for (mapi = 0; mapi < smime_symmetric_count; mapi++) { + if (cipher_abilities[mapi] != rcount) + continue; + if (! smime_cipher_allowed (smime_cipher_maps[mapi].cipher)) + continue; + if (!isFortezza && (smime_cipher_maps[mapi].cipher == SMIME_FORTEZZA)) + continue; + if (cipher_votes[mapi] > max) { + chosen_cipher = smime_cipher_maps[mapi].cipher; + max = cipher_votes[mapi]; + } /* XXX else if a tie, let scert break it? */ + } + +done: + if (poolp != NULL) + PORT_FreeArena (poolp, PR_FALSE); + + return chosen_cipher; +} + + +/* + * XXX This is a hack for now to satisfy our current interface. + * Eventually, with more parameters needing to be specified, just + * looking up the keysize is not going to be sufficient. + */ +static int +smime_keysize_by_cipher (unsigned long which) +{ + int keysize; + + switch (which) { + case SMIME_RC2_CBC_40: + keysize = 40; + break; + case SMIME_RC2_CBC_64: + keysize = 64; + break; + case SMIME_RC2_CBC_128: + keysize = 128; + break; +#ifdef SMIME_DOES_RC5 + case SMIME_RC5PAD_64_16_40: + case SMIME_RC5PAD_64_16_64: + case SMIME_RC5PAD_64_16_128: + /* XXX See comment above; keysize is not enough... */ + PORT_Assert (0); + PORT_SetError (SEC_ERROR_INVALID_ALGORITHM); + keysize = -1; + break; +#endif + case SMIME_DES_CBC_56: + case SMIME_DES_EDE3_168: + case SMIME_FORTEZZA: + /* + * These are special; since the key size is fixed, we actually + * want to *avoid* specifying a key size. + */ + keysize = 0; + break; + default: + keysize = -1; + break; + } + + return keysize; +} + + +/* + * Start an S/MIME encrypting context. + * + * "scert" is the cert for the sender. It will be checked for validity. + * "rcerts" are the certs for the recipients. They will also be checked. + * + * "certdb" is the cert database to use for verifying the certs. + * It can be NULL if a default database is available (like in the client). + * + * This function already does all of the stuff specific to S/MIME protocol + * and local policy; the return value just needs to be passed to + * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, + * and finally to SEC_PKCS7DestroyContentInfo(). + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +SEC_PKCS7ContentInfo * +SECMIME_CreateEncrypted(CERTCertificate *scert, + CERTCertificate **rcerts, + CERTCertDBHandle *certdb, + SECKEYGetPasswordKey pwfn, + void *pwfn_arg) +{ + SEC_PKCS7ContentInfo *cinfo; + long cipher; + SECOidTag encalg; + int keysize; + int mapi, rci; + + cipher = smime_choose_cipher (scert, rcerts); + if (cipher < 0) + return NULL; + + mapi = smime_mapi_by_cipher (cipher); + if (mapi < 0) + return NULL; + + /* + * XXX This is stretching it -- CreateEnvelopedData should probably + * take a cipher itself of some sort, because we cannot know what the + * future will bring in terms of parameters for each type of algorithm. + * For example, just an algorithm and keysize is *not* sufficient to + * fully specify the usage of RC5 (which also needs to know rounds and + * block size). Work this out into a better API! + */ + encalg = smime_cipher_maps[mapi].algtag; + keysize = smime_keysize_by_cipher (cipher); + if (keysize < 0) + return NULL; + + cinfo = SEC_PKCS7CreateEnvelopedData (scert, certUsageEmailRecipient, + certdb, encalg, keysize, + pwfn, pwfn_arg); + if (cinfo == NULL) + return NULL; + + for (rci = 0; rcerts[rci] != NULL; rci++) { + if (rcerts[rci] == scert) + continue; + if (SEC_PKCS7AddRecipient (cinfo, rcerts[rci], certUsageEmailRecipient, + NULL) != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + } + + return cinfo; +} + + +static smime_capability **smime_capabilities; +static SECItem *smime_encoded_caps; +static PRBool lastUsedFortezza; + + +static SECStatus +smime_init_caps (PRBool isFortezza) +{ + smime_capability *cap; + smime_cipher_map *map; + SECOidData *oiddata; + SECStatus rv; + int i, capIndex; + + if (smime_encoded_caps != NULL + && (! smime_prefs_changed) + && lastUsedFortezza == isFortezza) + return SECSuccess; + + if (smime_encoded_caps != NULL) { + SECITEM_FreeItem (smime_encoded_caps, PR_TRUE); + smime_encoded_caps = NULL; + } + + if (smime_capabilities == NULL) { + smime_capabilities = (smime_capability**)PORT_ZAlloc ( + (smime_symmetric_count + 1) + * sizeof(smime_capability *)); + if (smime_capabilities == NULL) + return SECFailure; + } + + rv = SECFailure; + + /* + The process of creating the encoded PKCS7 cipher capability list + involves two basic steps: + + (a) Convert our internal representation of cipher preferences + (smime_prefs) into an array containing cipher OIDs and + parameter data (smime_capabilities). This step is + performed here. + + (b) Encode, using ASN.1, the cipher information in + smime_capabilities, leaving the encoded result in + smime_encoded_caps. + + (In the process of performing (a), Lisa put in some optimizations + which allow us to avoid needlessly re-populating elements in + smime_capabilities as we walk through smime_prefs.) + + We want to use separate loop variables for smime_prefs and + smime_capabilities because in the case where the Skipjack cipher + is turned on in the prefs, but where we don't want to include + Skipjack in the encoded capabilities (presumably due to using a + non-fortezza cert when sending a message), we want to avoid creating + an empty element in smime_capabilities. This would otherwise cause + the encoding step to produce an empty set, since Skipjack happens + to be the first cipher in smime_prefs, if it is turned on. + */ + for (i = 0, capIndex = 0; i < smime_current_pref_index; i++, capIndex++) { + int mapi; + + /* Get the next cipher preference in smime_prefs. */ + mapi = smime_mapi_by_cipher (smime_prefs[i]); + if (mapi < 0) + break; + + /* Find the corresponding entry in the cipher map. */ + PORT_Assert (mapi < smime_symmetric_count); + map = &(smime_cipher_maps[mapi]); + + /* If we're using a non-Fortezza cert, only advertise non-Fortezza + capabilities. (We advertise all capabilities if we have a + Fortezza cert.) */ + if ((!isFortezza) && (map->cipher == SMIME_FORTEZZA)) + { + capIndex--; /* we want to visit the same caps index entry next time */ + continue; + } + + /* + * Convert the next preference found in smime_prefs into an + * smime_capability. + */ + + cap = smime_capabilities[capIndex]; + if (cap == NULL) { + cap = (smime_capability*)PORT_ZAlloc (sizeof(smime_capability)); + if (cap == NULL) + break; + smime_capabilities[capIndex] = cap; + } else if (cap->cipher == smime_prefs[i]) { + continue; /* no change to this one */ + } + + cap->capIDTag = map->algtag; + oiddata = SECOID_FindOIDByTag (map->algtag); + if (oiddata == NULL) + break; + + if (cap->capabilityID.data != NULL) { + SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE); + cap->capabilityID.data = NULL; + cap->capabilityID.len = 0; + } + + rv = SECITEM_CopyItem (NULL, &(cap->capabilityID), &(oiddata->oid)); + if (rv != SECSuccess) + break; + + if (map->parms == NULL) { + cap->parameters.data = NULL; + cap->parameters.len = 0; + } else { + cap->parameters.data = map->parms->data; + cap->parameters.len = map->parms->len; + } + + cap->cipher = smime_prefs[i]; + } + + if (i != smime_current_pref_index) + return rv; + + while (capIndex < smime_symmetric_count) { + cap = smime_capabilities[capIndex]; + if (cap != NULL) { + SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE); + PORT_Free (cap); + } + smime_capabilities[capIndex] = NULL; + capIndex++; + } + smime_capabilities[capIndex] = NULL; + + smime_encoded_caps = SEC_ASN1EncodeItem (NULL, NULL, &smime_capabilities, + smime_capabilities_template); + if (smime_encoded_caps == NULL) + return SECFailure; + + lastUsedFortezza = isFortezza; + + return SECSuccess; +} + + +static SECStatus +smime_add_profile (CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo) +{ + PRBool isFortezza = PR_FALSE; + + PORT_Assert (smime_prefs_complete); + if (! smime_prefs_complete) + return SECFailure; + + /* See if the sender's cert specifies Fortezza key exchange. */ + if (cert != NULL) + isFortezza = PK11_FortezzaHasKEA(cert); + + /* For that matter, if capabilities haven't been initialized yet, + do so now. */ + if (isFortezza != lastUsedFortezza || smime_encoded_caps == NULL || smime_prefs_changed) { + SECStatus rv; + + rv = smime_init_caps(isFortezza); + if (rv != SECSuccess) + return rv; + + PORT_Assert (smime_encoded_caps != NULL); + } + + return SEC_PKCS7AddSignedAttribute (cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES, + smime_encoded_caps); +} + + +/* + * Start an S/MIME signing context. + * + * "scert" is the cert that will be used to sign the data. It will be + * checked for validity. + * + * "ecert" is the signer's encryption cert. If it is different from + * scert, then it will be included in the signed message so that the + * recipient can save it for future encryptions. + * + * "certdb" is the cert database to use for verifying the cert. + * It can be NULL if a default database is available (like in the client). + * + * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). + * XXX There should be SECMIME functions for hashing, or the hashing should + * be built into this interface, which we would like because we would + * support more smartcards that way, and then this argument should go away.) + * + * "digest" is the actual digest of the data. It must be provided in + * the case of detached data or NULL if the content will be included. + * + * This function already does all of the stuff specific to S/MIME protocol + * and local policy; the return value just needs to be passed to + * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, + * and finally to SEC_PKCS7DestroyContentInfo(). + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ + +SEC_PKCS7ContentInfo * +SECMIME_CreateSigned (CERTCertificate *scert, + CERTCertificate *ecert, + CERTCertDBHandle *certdb, + SECOidTag digestalg, + SECItem *digest, + SECKEYGetPasswordKey pwfn, + void *pwfn_arg) +{ + SEC_PKCS7ContentInfo *cinfo; + SECStatus rv; + + /* See note in header comment above about digestalg. */ + PORT_Assert (digestalg == SEC_OID_SHA1); + + cinfo = SEC_PKCS7CreateSignedData (scert, certUsageEmailSigner, + certdb, digestalg, digest, + pwfn, pwfn_arg); + if (cinfo == NULL) + return NULL; + + if (SEC_PKCS7IncludeCertChain (cinfo, NULL) != SECSuccess) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + + /* if the encryption cert and the signing cert differ, then include + * the encryption cert too. + */ + /* it is ok to compare the pointers since we ref count, and the same + * cert will always have the same pointer + */ + if ( ( ecert != NULL ) && ( ecert != scert ) ) { + rv = SEC_PKCS7AddCertificate(cinfo, ecert); + if ( rv != SECSuccess ) { + SEC_PKCS7DestroyContentInfo (cinfo); + return NULL; + } + } + /* + * Add the signing time. But if it fails for some reason, + * may as well not give up altogether -- just assert. + */ + rv = SEC_PKCS7AddSigningTime (cinfo); + PORT_Assert (rv == SECSuccess); + + /* + * Add the email profile. Again, if it fails for some reason, + * may as well not give up altogether -- just assert. + */ + rv = smime_add_profile (ecert, cinfo); + PORT_Assert (rv == SECSuccess); + + return cinfo; +} diff --git a/security/nss/lib/pkcs7/secmime.h b/security/nss/lib/pkcs7/secmime.h new file mode 100644 index 000000000..6950e416f --- /dev/null +++ b/security/nss/lib/pkcs7/secmime.h @@ -0,0 +1,192 @@ +/* + * 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. + */ + +/* + * Header file for routines specific to S/MIME. Keep things that are pure + * pkcs7 out of here; this is for S/MIME policy, S/MIME interoperability, etc. + * + * $Id$ + */ + +#ifndef _SECMIME_H_ +#define _SECMIME_H_ 1 + +#include "secpkcs7.h" + + +/************************************************************************/ +SEC_BEGIN_PROTOS + +/* + * Initialize the local recording of the user S/MIME cipher preferences. + * This function is called once for each cipher, the order being + * important (first call records greatest preference, and so on). + * When finished, it is called with a "which" of CIPHER_FAMILID_MASK. + * If the function is called again after that, it is assumed that + * the preferences are being reset, and the old preferences are + * discarded. + * + * XXX This is for a particular user, and right now the storage is + * XXX local, static. The preference should be stored elsewhere to allow + * XXX for multiple uses of one library? How does SSL handle this; + * XXX it has something similar? + * + * - The "which" values are defined in ciferfam.h (the SMIME_* values, + * for example SMIME_DES_CBC_56). + * - If "on" is non-zero then the named cipher is enabled, otherwise + * it is disabled. (It is not necessary to call the function for + * ciphers that are disabled, however, as that is the default.) + * + * If the cipher preference is successfully recorded, SECSuccess + * is returned. Otherwise SECFailure is returned. The only errors + * are due to failure allocating memory or bad parameters/calls: + * SEC_ERROR_XXX ("which" is not in the S/MIME cipher family) + * SEC_ERROR_XXX (function is being called more times than there + * are known/expected ciphers) + */ +extern SECStatus SECMIME_EnableCipher(long which, int on); + +/* + * Initialize the local recording of the S/MIME policy. + * This function is called to enable/disable a particular cipher. + * (S/MIME encryption or decryption using a particular cipher is only + * allowed if that cipher is currently enabled.) At startup, all S/MIME + * ciphers are disabled. From that point, this function can be called + * to enable a cipher -- it is not necessary to call this to disable + * a cipher unless that cipher was previously, explicitly enabled via + * this function. + * + * XXX This is for a the current module, I think, so local, static storage + * XXX is okay. Is that correct, or could multiple uses of the same + * XXX library expect to operate under different policies? + * + * - The "which" values are defined in ciferfam.h (the SMIME_* values, + * for example SMIME_DES_CBC_56). + * - If "on" is non-zero then the named cipher is enabled, otherwise + * it is disabled. + * + * If the cipher is successfully enabled/disabled, SECSuccess is + * returned. Otherwise SECFailure is returned. The only errors + * are due to bad parameters: + * SEC_ERROR_XXX ("which" is not in the S/MIME cipher family) + * SEC_ERROR_XXX ("which" exceeds expected maximum cipher; this is + * really an internal error) + */ +extern SECStatus SECMIME_SetPolicy(long which, int on); + +/* + * Does the current policy allow S/MIME decryption of this particular + * algorithm and keysize? + */ +extern PRBool SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key); + +/* + * Does the current policy allow *any* S/MIME encryption (or decryption)? + * + * This tells whether or not *any* S/MIME encryption can be done, + * according to policy. Callers may use this to do nicer user interface + * (say, greying out a checkbox so a user does not even try to encrypt + * a message when they are not allowed to) or for any reason they want + * to check whether S/MIME encryption (or decryption, for that matter) + * may be done. + * + * It takes no arguments. The return value is a simple boolean: + * PR_TRUE means encryption (or decryption) is *possible* + * (but may still fail due to other reasons, like because we cannot + * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee) + * PR_FALSE means encryption (or decryption) is not permitted + * + * There are no errors from this routine. + */ +extern PRBool SECMIME_EncryptionPossible(void); + +/* + * Start an S/MIME encrypting context. + * + * "scert" is the cert for the sender. It will be checked for validity. + * "rcerts" are the certs for the recipients. They will also be checked. + * + * "certdb" is the cert database to use for verifying the certs. + * It can be NULL if a default database is available (like in the client). + * + * This function already does all of the stuff specific to S/MIME protocol + * and local policy; the return value just needs to be passed to + * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, + * and finally to SEC_PKCS7DestroyContentInfo(). + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +extern SEC_PKCS7ContentInfo *SECMIME_CreateEncrypted(CERTCertificate *scert, + CERTCertificate **rcerts, + CERTCertDBHandle *certdb, + SECKEYGetPasswordKey pwfn, + void *pwfn_arg); + +/* + * Start an S/MIME signing context. + * + * "scert" is the cert that will be used to sign the data. It will be + * checked for validity. + * + * "certdb" is the cert database to use for verifying the cert. + * It can be NULL if a default database is available (like in the client). + * + * "digestalg" names the digest algorithm. (It should be SEC_OID_SHA1; + * XXX There should be SECMIME functions for hashing, or the hashing should + * be built into this interface, which we would like because we would + * support more smartcards that way, and then this argument should go away.) + * + * "digest" is the actual digest of the data. It must be provided in + * the case of detached data or NULL if the content will be included. + * + * This function already does all of the stuff specific to S/MIME protocol + * and local policy; the return value just needs to be passed to + * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, + * and finally to SEC_PKCS7DestroyContentInfo(). + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +extern SEC_PKCS7ContentInfo *SECMIME_CreateSigned(CERTCertificate *scert, + CERTCertificate *ecert, + CERTCertDBHandle *certdb, + SECOidTag digestalg, + SECItem *digest, + SECKEYGetPasswordKey pwfn, + void *pwfn_arg); + +/************************************************************************/ +SEC_END_PROTOS + +#endif /* _SECMIME_H_ */ diff --git a/security/nss/lib/pkcs7/secpkcs7.h b/security/nss/lib/pkcs7/secpkcs7.h new file mode 100644 index 000000000..7a2b71b24 --- /dev/null +++ b/security/nss/lib/pkcs7/secpkcs7.h @@ -0,0 +1,618 @@ +/* + * 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. + */ + +/* + * Interface to the PKCS7 implementation. + * + * $Id$ + */ + +#ifndef _SECPKCS7_H_ +#define _SECPKCS7_H_ + +#include "seccomon.h" +#include "mcom_db.h" /* needed by certt.h */ + +#include "secoidt.h" +#include "secder.h" /* needed by certt.h; XXX go away when possible */ +#include "certt.h" +#include "keyt.h" +#include "hasht.h" +#include "pkcs7t.h" + +extern const SEC_ASN1Template sec_PKCS7ContentInfoTemplate[]; + +/************************************************************************/ +SEC_BEGIN_PROTOS + +/************************************************************************ + * Miscellaneous + ************************************************************************/ + +/* + * Returns the content type of the given contentInfo. + */ +extern SECOidTag SEC_PKCS7ContentType (SEC_PKCS7ContentInfo *cinfo); + +/* + * Destroy a PKCS7 contentInfo and all of its sub-pieces. + */ +extern void SEC_PKCS7DestroyContentInfo(SEC_PKCS7ContentInfo *contentInfo); + +/* + * Copy a PKCS7 contentInfo. A Destroy is needed on *each* copy. + */ +extern SEC_PKCS7ContentInfo * +SEC_PKCS7CopyContentInfo(SEC_PKCS7ContentInfo *contentInfo); + +/* + * Return a pointer to the actual content. In the case of those types + * which are encrypted, this returns the *plain* content. + */ +extern SECItem *SEC_PKCS7GetContent(SEC_PKCS7ContentInfo *cinfo); + +/************************************************************************ + * PKCS7 Decoding, Verification, etc.. + ************************************************************************/ + +extern SEC_PKCS7DecoderContext * +SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback callback, + void *callback_arg, + SECKEYGetPasswordKey pwfn, void *pwfn_arg, + SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, + void *decrypt_key_cb_arg, + SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb); + +extern SECStatus +SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx, + const char *buf, unsigned long len); + +extern SEC_PKCS7ContentInfo * +SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx); + +extern SEC_PKCS7ContentInfo * +SEC_PKCS7DecodeItem(SECItem *p7item, + SEC_PKCS7DecoderContentCallback cb, void *cb_arg, + SECKEYGetPasswordKey pwfn, void *pwfn_arg, + SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, + void *decrypt_key_cb_arg, + SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb); + +extern PRBool SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo); + +/* checks to see if the contents of the content info is + * empty. it so, PR_TRUE is returned. PR_FALSE, otherwise. + * + * minLen is used to specify a minimum size. if content size <= minLen, + * content is assumed empty. + */ +extern PRBool +SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen); + +extern PRBool SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo); + +/* + * If the PKCS7 content has a signature (not just *could* have a signature) + * return true; false otherwise. This can/should be called before calling + * VerifySignature, which will always indicate failure if no signature is + * present, but that does not mean there even was a signature! + * Note that the content itself can be empty (detached content was sent + * another way); it is the presence of the signature that matters. + */ +extern PRBool SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo); + +/* + * SEC_PKCS7VerifySignature + * Look at a PKCS7 contentInfo and check if the signature is good. + * The verification checks that the signing cert is valid and trusted + * for the purpose specified by "certusage". + * + * In addition, if "keepcerts" is true, add any new certificates found + * into our local database. + */ +extern PRBool SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo, + SECCertUsage certusage, + PRBool keepcerts); + +/* + * SEC_PKCS7VerifyDetachedSignature + * Look at a PKCS7 contentInfo and check if the signature matches + * a passed-in digest (calculated, supposedly, from detached contents). + * The verification checks that the signing cert is valid and trusted + * for the purpose specified by "certusage". + * + * In addition, if "keepcerts" is true, add any new certificates found + * into our local database. + */ +extern PRBool SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo, + SECCertUsage certusage, + SECItem *detached_digest, + HASH_HashType digest_type, + PRBool keepcerts); + +/* + * SEC_PKCS7GetSignerCommonName, SEC_PKCS7GetSignerEmailAddress + * The passed-in contentInfo is espected to be Signed, and these + * functions return the specified portion of the full signer name. + * + * Returns a pointer to allocated memory, which must be freed. + * A NULL return value is an error. + */ +extern char *SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo); +extern char *SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo); + +/* + * Return the the signing time, in UTCTime format, of a PKCS7 contentInfo. + */ +extern SECItem *SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo); + + +/************************************************************************ + * PKCS7 Creation and Encoding. + ************************************************************************/ + +/* + * Start a PKCS7 signing context. + * + * "cert" is the cert that will be used to sign the data. It will be + * checked for validity. + * + * "certusage" describes the signing usage (e.g. certUsageEmailSigner) + * XXX Maybe SECCertUsage should be split so that our caller just says + * "email" and *we* add the "signing" part -- otherwise our caller + * could be lying about the usage; we do not want to allow encryption + * certs for signing or vice versa. + * + * "certdb" is the cert database to use for verifying the cert. + * It can be NULL if a default database is available (like in the client). + * + * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). + * + * "digest" is the actual digest of the data. It must be provided in + * the case of detached data or NULL if the content will be included. + * + * The return value can be passed to functions which add things to + * it like attributes, then eventually to SEC_PKCS7Encode() or to + * SEC_PKCS7EncoderStart() to create the encoded data, and finally to + * SEC_PKCS7DestroyContentInfo(). + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +extern SEC_PKCS7ContentInfo * +SEC_PKCS7CreateSignedData (CERTCertificate *cert, + SECCertUsage certusage, + CERTCertDBHandle *certdb, + SECOidTag digestalg, + SECItem *digest, + SECKEYGetPasswordKey pwfn, void *pwfn_arg); + +/* + * Create a PKCS7 certs-only container. + * + * "cert" is the (first) cert that will be included. + * + * "include_chain" specifies whether the entire chain for "cert" should + * be included. + * + * "certdb" is the cert database to use for finding the chain. + * It can be NULL in when "include_chain" is false, or when meaning + * use the default database. + * + * More certs and chains can be added via AddCertficate and AddCertChain. + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +extern SEC_PKCS7ContentInfo * +SEC_PKCS7CreateCertsOnly (CERTCertificate *cert, + PRBool include_chain, + CERTCertDBHandle *certdb); + +/* + * Start a PKCS7 enveloping context. + * + * "cert" is the cert for the recipient. It will be checked for validity. + * + * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) + * XXX Maybe SECCertUsage should be split so that our caller just says + * "email" and *we* add the "recipient" part -- otherwise our caller + * could be lying about the usage; we do not want to allow encryption + * certs for signing or vice versa. + * + * "certdb" is the cert database to use for verifying the cert. + * It can be NULL if a default database is available (like in the client). + * + * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2). + * + * "keysize" specifies the bulk encryption key size, in bits. + * + * The return value can be passed to functions which add things to + * it like more recipients, then eventually to SEC_PKCS7Encode() or to + * SEC_PKCS7EncoderStart() to create the encoded data, and finally to + * SEC_PKCS7DestroyContentInfo(). + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +extern SEC_PKCS7ContentInfo * +SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert, + SECCertUsage certusage, + CERTCertDBHandle *certdb, + SECOidTag encalg, + int keysize, + SECKEYGetPasswordKey pwfn, void *pwfn_arg); + +/* + * XXX There will be a similar routine for creating signedAndEnvelopedData. + * But its parameters will be different and I have no plans to implement + * it any time soon because we have no current need for it. + */ + +/* + * Create an empty PKCS7 data content info. + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +extern SEC_PKCS7ContentInfo *SEC_PKCS7CreateData (void); + +/* + * Create an empty PKCS7 encrypted content info. + * + * "algorithm" specifies the bulk encryption algorithm to use. + * + * An error results in a return value of NULL and an error set. + * (Retrieve specific errors via PORT_GetError()/XP_GetError().) + */ +extern SEC_PKCS7ContentInfo * +SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize, + SECKEYGetPasswordKey pwfn, void *pwfn_arg); + +/* + * All of the following things return SECStatus to signal success or failure. + * Failure should have a more specific error status available via + * PORT_GetError()/XP_GetError(). + */ + +/* + * Add the specified attribute to the authenticated (i.e. signed) attributes + * of "cinfo" -- "oidtag" describes the attribute and "value" is the + * value to be associated with it. NOTE! "value" must already be encoded; + * no interpretation of "oidtag" is done. Also, it is assumed that this + * signedData has only one signer -- if we ever need to add attributes + * when there is more than one signature, we need a way to specify *which* + * signature should get the attribute. + * + * XXX Technically, a signed attribute can have multiple values; if/when + * we ever need to support an attribute which takes multiple values, we + * either need to change this interface or create an AddSignedAttributeValue + * which can be called subsequently, and would then append a value. + * + * "cinfo" should be of type signedData (the only kind of pkcs7 data + * that is allowed authenticated attributes); SECFailure will be returned + * if it is not. + */ +extern SECStatus SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo, + SECOidTag oidtag, + SECItem *value); + +/* + * Add "cert" and its entire chain to the set of certs included in "cinfo". + * + * "certdb" is the cert database to use for finding the chain. + * It can be NULL, meaning use the default database. + * + * "cinfo" should be of type signedData or signedAndEnvelopedData; + * SECFailure will be returned if it is not. + */ +extern SECStatus SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo, + CERTCertificate *cert, + CERTCertDBHandle *certdb); + +/* + * Add "cert" to the set of certs included in "cinfo". + * + * "cinfo" should be of type signedData or signedAndEnvelopedData; + * SECFailure will be returned if it is not. + */ +extern SECStatus SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, + CERTCertificate *cert); + +/* + * Add another recipient to an encrypted message. + * + * "cinfo" should be of type envelopedData or signedAndEnvelopedData; + * SECFailure will be returned if it is not. + * + * "cert" is the cert for the recipient. It will be checked for validity. + * + * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient) + * XXX Maybe SECCertUsage should be split so that our caller just says + * "email" and *we* add the "recipient" part -- otherwise our caller + * could be lying about the usage; we do not want to allow encryption + * certs for signing or vice versa. + * + * "certdb" is the cert database to use for verifying the cert. + * It can be NULL if a default database is available (like in the client). + */ +extern SECStatus SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo, + CERTCertificate *cert, + SECCertUsage certusage, + CERTCertDBHandle *certdb); + +/* + * Add the signing time to the authenticated (i.e. signed) attributes + * of "cinfo". This is expected to be included in outgoing signed + * messages for email (S/MIME) but is likely useful in other situations. + * + * This should only be added once; a second call will either do + * nothing or replace an old signing time with a newer one. + * + * XXX This will probably just shove the current time into "cinfo" + * but it will not actually get signed until the entire item is + * processed for encoding. Is this (expected to be small) delay okay? + * + * "cinfo" should be of type signedData (the only kind of pkcs7 data + * that is allowed authenticated attributes); SECFailure will be returned + * if it is not. + */ +extern SECStatus SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo); + +/* + * Add the signer's symmetric capabilities to the authenticated + * (i.e. signed) attributes of "cinfo". This is expected to be + * included in outgoing signed messages for email (S/MIME). + * + * This can only be added once; a second call will return SECFailure. + * + * "cinfo" should be of type signedData or signedAndEnvelopedData; + * SECFailure will be returned if it is not. + */ +extern SECStatus SEC_PKCS7AddSymmetricCapabilities(SEC_PKCS7ContentInfo *cinfo); + +/* + * Mark that the signer's certificate and its issuing chain should + * be included in the encoded data. This is expected to be used + * in outgoing signed messages for email (S/MIME). + * + * "certdb" is the cert database to use for finding the chain. + * It can be NULL, meaning use the default database. + * + * "cinfo" should be of type signedData or signedAndEnvelopedData; + * SECFailure will be returned if it is not. + */ +extern SECStatus SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo, + CERTCertDBHandle *certdb); + + +/* + * Set the content; it will be included and also hashed and/or encrypted + * as appropriate. This is for in-memory content (expected to be "small") + * that will be included in the PKCS7 object. All others should stream the + * content through when encoding (see SEC_PKCS7Encoder{Start,Update,Finish}). + * + * "buf" points to data of length "len"; it will be copied. + */ +extern SECStatus SEC_PKCS7SetContent (SEC_PKCS7ContentInfo *cinfo, + const char *buf, unsigned long len); + +/* + * Encode a PKCS7 object, in one shot. All necessary components + * of the object must already be specified. Either the data has + * already been included (via SetContent), or the data is detached, + * or there is no data at all (certs-only). + * + * "cinfo" specifies the object to be encoded. + * + * "outputfn" is where the encoded bytes will be passed. + * + * "outputarg" is an opaque argument to the above callback. + * + * "bulkkey" specifies the bulk encryption key to use. This argument + * can be NULL if no encryption is being done, or if the bulk key should + * be generated internally (usually the case for EnvelopedData but never + * for EncryptedData, which *must* provide a bulk encryption key). + * + * "pwfn" is a callback for getting the password which protects the + * private key of the signer. This argument can be NULL if it is known + * that no signing is going to be done. + * + * "pwfnarg" is an opaque argument to the above callback. + */ +extern SECStatus SEC_PKCS7Encode (SEC_PKCS7ContentInfo *cinfo, + SEC_PKCS7EncoderOutputCallback outputfn, + void *outputarg, + PK11SymKey *bulkkey, + SECKEYGetPasswordKey pwfn, + void *pwfnarg); + +/* + * Encode a PKCS7 object, in one shot. All necessary components + * of the object must already be specified. Either the data has + * already been included (via SetContent), or the data is detached, + * or there is no data at all (certs-only). The output, rather than + * being passed to an output function as is done above, is all put + * into a SECItem. + * + * "pool" specifies a pool from which to allocate the result. + * It can be NULL, in which case memory is allocated generically. + * + * "dest" specifies a SECItem in which to put the result data. + * It can be NULL, in which case the entire item is allocated, too. + * + * "cinfo" specifies the object to be encoded. + * + * "bulkkey" specifies the bulk encryption key to use. This argument + * can be NULL if no encryption is being done, or if the bulk key should + * be generated internally (usually the case for EnvelopedData but never + * for EncryptedData, which *must* provide a bulk encryption key). + * + * "pwfn" is a callback for getting the password which protects the + * private key of the signer. This argument can be NULL if it is known + * that no signing is going to be done. + * + * "pwfnarg" is an opaque argument to the above callback. + */ +extern SECItem *SEC_PKCS7EncodeItem (PRArenaPool *pool, + SECItem *dest, + SEC_PKCS7ContentInfo *cinfo, + PK11SymKey *bulkkey, + SECKEYGetPasswordKey pwfn, + void *pwfnarg); + +/* + * For those who want to simply point to the pkcs7 contentInfo ASN.1 + * template, and *not* call the encoding functions directly, the + * following function can be used -- after it is called, the entire + * PKCS7 contentInfo is ready to be encoded. + */ +extern SECStatus SEC_PKCS7PrepareForEncode (SEC_PKCS7ContentInfo *cinfo, + PK11SymKey *bulkkey, + SECKEYGetPasswordKey pwfn, + void *pwfnarg); + +/* + * Start the process of encoding a PKCS7 object. The first part of + * the encoded object will be passed to the output function right away; + * after that it is expected that SEC_PKCS7EncoderUpdate will be called, + * streaming in the actual content that is getting included as well as + * signed or encrypted (or both). + * + * "cinfo" specifies the object to be encoded. + * + * "outputfn" is where the encoded bytes will be passed. + * + * "outputarg" is an opaque argument to the above callback. + * + * "bulkkey" specifies the bulk encryption key to use. This argument + * can be NULL if no encryption is being done, or if the bulk key should + * be generated internally (usually the case for EnvelopedData but never + * for EncryptedData, which *must* provide a bulk encryption key). + * + * Returns an object to be passed to EncoderUpdate and EncoderFinish. + */ +extern SEC_PKCS7EncoderContext * +SEC_PKCS7EncoderStart (SEC_PKCS7ContentInfo *cinfo, + SEC_PKCS7EncoderOutputCallback outputfn, + void *outputarg, + PK11SymKey *bulkkey); + +/* + * Encode more contents, hashing and/or encrypting along the way. + */ +extern SECStatus SEC_PKCS7EncoderUpdate (SEC_PKCS7EncoderContext *p7ecx, + const char *buf, + unsigned long len); + +/* + * No more contents; finish the signature creation, if appropriate, + * and then the encoding. + * + * "pwfn" is a callback for getting the password which protects the + * signer's private key. This argument can be NULL if it is known + * that no signing is going to be done. + * + * "pwfnarg" is an opaque argument to the above callback. + */ +extern SECStatus SEC_PKCS7EncoderFinish (SEC_PKCS7EncoderContext *p7ecx, + SECKEYGetPasswordKey pwfn, + void *pwfnarg); + +/* retrieve the algorithm ID used to encrypt the content info + * for encrypted and enveloped data. The SECAlgorithmID pointer + * returned needs to be freed as it is a copy of the algorithm + * id in the content info. + */ +extern SECAlgorithmID * +SEC_PKCS7GetEncryptionAlgorithm(SEC_PKCS7ContentInfo *cinfo); + +/* the content of an encrypted data content info is encrypted. + * it is assumed that for encrypted data, that the data has already + * been set and is in the "plainContent" field of the content info. + * + * cinfo is the content info to encrypt + * + * key is the key with which to perform the encryption. if the + * algorithm is a password based encryption algorithm, the + * key is actually a password which will be processed per + * PKCS #5. + * + * in the event of an error, SECFailure is returned. SECSuccess + * indicates a success. + */ +extern SECStatus +SEC_PKCS7EncryptContents(PRArenaPool *poolp, + SEC_PKCS7ContentInfo *cinfo, + SECItem *key, + void *wincx); + +/* the content of an encrypted data content info is decrypted. + * it is assumed that for encrypted data, that the data has already + * been set and is in the "encContent" field of the content info. + * + * cinfo is the content info to decrypt + * + * key is the key with which to perform the decryption. if the + * algorithm is a password based encryption algorithm, the + * key is actually a password which will be processed per + * PKCS #5. + * + * in the event of an error, SECFailure is returned. SECSuccess + * indicates a success. + */ +extern SECStatus +SEC_PKCS7DecryptContents(PRArenaPool *poolp, + SEC_PKCS7ContentInfo *cinfo, + SECItem *key, + void *wincx); + +/* retrieve the certificate list from the content info. the list + * is a pointer to the list in the content info. this should not + * be deleted or freed in any way short of calling + * SEC_PKCS7DestroyContentInfo + */ +extern SECItem ** +SEC_PKCS7GetCertificateList(SEC_PKCS7ContentInfo *cinfo); + +/* Returns the key length (in bits) of the algorithm used to encrypt + this object. Returns 0 if it's not encrypted, or the key length is + irrelevant. */ +extern int +SEC_PKCS7GetKeyLength(SEC_PKCS7ContentInfo *cinfo); + + +/************************************************************************/ +SEC_END_PROTOS + +#endif /* _SECPKCS7_H_ */ diff --git a/security/nss/lib/pki/nsspki.h b/security/nss/lib/pki/nsspki.h new file mode 100644 index 000000000..c8da14050 --- /dev/null +++ b/security/nss/lib/pki/nsspki.h @@ -0,0 +1,3161 @@ +/* + * 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 NSSPKI_H +#define NSSPKI_H + +#ifdef DEBUG +static const char NSSPKI_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +/* + * nsspki.h + * + * This file prototypes the methods of the top-level PKI objects. + */ + +#ifndef NSSPKIT_H +#include "nsspkit.h" +#endif /* NSSPKIT_H */ + +PR_BEGIN_EXTERN_C + +/* + * A note about interfaces + * + * Although these APIs are specified in C, a language which does + * not have fancy support for abstract interfaces, this library + * was designed from an object-oriented perspective. It may be + * useful to consider the standard interfaces which went into + * the writing of these APIs. + * + * Basic operations on all objects: + * Destroy -- free a pointer to an object + * DeleteStoredObject -- delete an object permanently + * + * Public Key cryptographic operations: + * Encrypt + * Verify + * VerifyRecover + * Wrap + * Derive + * + * Private Key cryptographic operations: + * IsStillPresent + * Decrypt + * Sign + * SignRecover + * Unwrap + * Derive + * + * Symmetric Key cryptographic operations: + * IsStillPresent + * Encrypt + * Decrypt + * Sign + * SignRecover + * Verify + * VerifyRecover + * Wrap + * Unwrap + * Derive + * + */ + +/* + * NSSCertificate + * + * These things can do crypto ops like public keys, except that the trust, + * usage, and other constraints are checked. These objects are "high-level," + * so trust, usages, etc. are in the form we throw around (client auth, + * email signing, etc.). Remember that theoretically another implementation + * (think PGP) could be beneath this object. + */ + +/* + * NSSCertificate_Destroy + * + * Free a pointer to a certificate object. + */ + +NSS_EXTERN PRStatus +NSSCertificate_Destroy +( + NSSCertificate *c +); + +/* + * NSSCertificate_DeleteStoredObject + * + * Permanently remove this certificate from storage. If this is the + * only (remaining) certificate corresponding to a private key, + * public key, and/or other object; then that object (those objects) + * are deleted too. + */ + +NSS_EXTERN PRStatus +NSSCertificate_DeleteStoredObject +( + NSSCertificate *c, + NSSCallback *uhh +); + +/* + * NSSCertificate_Validate + * + * Verify that this certificate is trusted, for the specified usage(s), + * at the specified time, {word word} the specified policies. + */ + +NSS_EXTERN PRStatus +NSSCertificate_Validate +( + NSSCertificate *c, + NSSTime *timeOpt, /* NULL for "now" */ + NSSUsage *usage, + NSSPolicies *policiesOpt /* NULL for none */ +); + +/* + * NSSCertificate_ValidateCompletely + * + * Verify that this certificate is trusted. The difference between + * this and the previous call is that NSSCertificate_Validate merely + * returns success or failure with an appropriate error stack. + * However, there may be (and often are) multiple problems with a + * certificate. This routine returns an array of errors, specifying + * every problem. + */ + +/* + * Return value must be an array of objects, each of which has + * an NSSError, and any corresponding certificate (in the chain) + * and/or policy. + */ + +NSS_EXTERN void ** /* void *[] */ +NSSCertificate_ValidateCompletely +( + NSSCertificate *c, + NSSTime *timeOpt, /* NULL for "now" */ + NSSUsage *usage, + NSSPolicies *policiesOpt, /* NULL for none */ + void **rvOpt, /* NULL for allocate */ + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt /* NULL for heap */ +); + +/* + * NSSCertificate_ValidateAndDiscoverUsagesAndPolicies + * + * Returns PR_SUCCESS if the certificate is valid for at least something. + */ + +NSS_EXTERN PRStatus +NSSCertificate_ValidateAndDiscoverUsagesAndPolicies +( + NSSCertificate *c, + NSSTime **notBeforeOutOpt, + NSSTime **notAfterOutOpt, + void *allowedUsages, + void *disallowedUsages, + void *allowedPolicies, + void *disallowedPolicies, + /* more args.. work on this fgmr */ + NSSArena *arenaOpt +); + +/* + * NSSCertificate_Encode + * + */ + +NSS_EXTERN NSSDER * +NSSCertificate_Encode +( + NSSCertificate *c, + NSSDER *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCertificate_BuildChain + * + * This routine returns NSSCertificate *'s for each certificate + * in the "chain" starting from the specified one up to and + * including the root. The zeroth element in the array is the + * specified ("leaf") certificate. + */ + +NSS_EXTERN NSSCertificate ** +NSSCertificate_BuildChain +( + NSSCertificate *c, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCertificate **rvOpt, + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt +); + +/* + * NSSCertificate_GetTrustDomain + * + */ + +NSS_EXTERN NSSTrustDomain * +NSSCertificate_GetTrustDomain +( + NSSCertificate *c +); + +/* + * NSSCertificate_GetToken + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSToken * +NSSCertificate_GetToken +( + NSSCertificate *c, + PRStatus *statusOpt +); + +/* + * NSSCertificate_GetSlot + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSSlot * +NSSCertificate_GetSlot +( + NSSCertificate *c, + PRStatus *statusOpt +); + +/* + * NSSCertificate_GetModule + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSModule * +NSSCertificate_GetModule +( + NSSCertificate *c, + PRStatus *statusOpt +); + +/* + * NSSCertificate_Encrypt + * + * Encrypt a single chunk of data with the public key corresponding to + * this certificate. + */ + +NSS_EXTERN NSSItem * +NSSCertificate_Encrypt +( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCertificate_Verify + * + */ + +NSS_EXTERN PRStatus +NSSCertificate_Verify +( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSItem *signature, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh +); + +/* + * NSSCertificate_VerifyRecover + * + */ + +NSS_EXTERN NSSItem * +NSSCertificate_VerifyRecover +( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSItem *signature, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCertificate_WrapSymmetricKey + * + * This method tries very hard to to succeed, even in situations + * involving sensitive keys and multiple modules. + * { relyea: want to add verbiage? } + */ + +NSS_EXTERN NSSItem * +NSSCertificate_WrapSymmetricKey +( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSSymmetricKey *keyToWrap, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCertificate_CreateCryptoContext + * + * Create a crypto context, in this certificate's trust domain, with this + * as the distinguished certificate. + */ + +NSS_EXTERN NSSCryptoContext * +NSSCertificate_CreateCryptoContext +( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh +); + +/* + * NSSCertificate_GetPublicKey + * + * Returns the public key corresponding to this certificate. + */ + +NSS_EXTERN NSSPublicKey * +NSSCertificate_GetPublicKey +( + NSSCertificate *c +); + +/* + * NSSCertificate_FindPrivateKey + * + * Finds and returns the private key corresponding to this certificate, + * if it is available. + * + * { Should this hang off of NSSUserCertificate? } + */ + +NSS_EXTERN NSSPrivateKey * +NSSCertificate_FindPrivateKey +( + NSSCertificate *c, + NSSCallback *uhh +); + +/* + * NSSCertificate_IsPrivateKeyAvailable + * + * Returns success if the private key corresponding to this certificate + * is available to be used. + * + * { Should *this* hang off of NSSUserCertificate?? } + */ + +NSS_EXTERN PRBool +NSSCertificate_IsPrivateKeyAvailable +( + NSSCertificate *c, + NSSCallback *uhh, + PRStatus *statusOpt +); + +/* + * If we make NSSUserCertificate not a typedef of NSSCertificate, + * then we'll need implementations of the following: + * + * NSSUserCertificate_Destroy + * NSSUserCertificate_DeleteStoredObject + * NSSUserCertificate_Validate + * NSSUserCertificate_ValidateCompletely + * NSSUserCertificate_ValidateAndDiscoverUsagesAndPolicies + * NSSUserCertificate_Encode + * NSSUserCertificate_BuildChain + * NSSUserCertificate_GetTrustDomain + * NSSUserCertificate_GetToken + * NSSUserCertificate_GetSlot + * NSSUserCertificate_GetModule + * NSSUserCertificate_GetCryptoContext + * NSSUserCertificate_GetPublicKey + */ + +/* + * NSSUserCertificate_IsStillPresent + * + * Verify that if this certificate lives on a token, that the token + * is still present and the certificate still exists. This is a + * lightweight call which should be used whenever it should be + * verified that the user hasn't perhaps popped out his or her + * token and strolled away. + */ + +NSS_EXTERN PRBool +NSSUserCertificate_IsStillPresent +( + NSSUserCertificate *uc, + PRStatus *statusOpt +); + +/* + * NSSUserCertificate_Decrypt + * + * Decrypt a single chunk of data with the private key corresponding + * to this certificate. + */ + +NSS_EXTERN NSSItem * +NSSUserCertificate_Decrypt +( + NSSUserCertificate *uc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSUserCertificate_Sign + * + */ + +NSS_EXTERN NSSItem * +NSSUserCertificate_Sign +( + NSSUserCertificate *uc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSUserCertificate_SignRecover + * + */ + +NSS_EXTERN NSSItem * +NSSUserCertificate_SignRecover +( + NSSUserCertificate *uc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSUserCertificate_UnwrapSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSUserCertificate_UnwrapSymmetricKey +( + NSSUserCertificate *uc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *wrappedKey, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSUserCertificate_DeriveSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSUserCertificate_DeriveSymmetricKey +( + NSSUserCertificate *uc, /* provides private key */ + NSSCertificate *c, /* provides public key */ + NSSAlgorithmAndParameters *apOpt, + NSSOID *target, + PRUint32 keySizeOpt, /* zero for best allowed */ + NSSOperations operations, + NSSCallback *uhh +); + +/* filter-certs function(s) */ + +/** + ** fgmr -- trust objects + **/ + +/* + * NSSPrivateKey + * + */ + +/* + * NSSPrivateKey_Destroy + * + * Free a pointer to a private key object. + */ + +NSS_EXTERN PRStatus +NSSPrivateKey_Destroy +( + NSSPrivateKey *vk +); + +/* + * NSSPrivateKey_DeleteStoredObject + * + * Permanently remove this object, and any related objects (such as the + * certificates corresponding to this key). + */ + +NSS_EXTERN PRStatus +NSSPrivateKey_DeleteStoredObject +( + NSSPrivateKey *vk, + NSSCallback *uhh +); + +/* + * NSSPrivateKey_GetSignatureLength + * + */ + +NSS_EXTERN PRUint32 +NSSPrivateKey_GetSignatureLength +( + NSSPrivateKey *vk +); + +/* + * NSSPrivateKey_GetPrivateModulusLength + * + */ + +NSS_EXTERN PRUint32 +NSSPrivateKey_GetPrivateModulusLength +( + NSSPrivateKey *vk +); + +/* + * NSSPrivateKey_IsStillPresent + * + */ + +NSS_EXTERN PRBool +NSSPrivateKey_IsStillPresent +( + NSSPrivateKey *vk, + PRStatus *statusOpt +); + +/* + * NSSPrivateKey_Encode + * + */ + +NSS_EXTERN NSSItem * +NSSPrivateKey_Encode +( + NSSPrivateKey *vk, + NSSAlgorithmAndParameters *ap, + NSSItem *passwordOpt, /* NULL will cause a callback; "" for no password */ + NSSCallback *uhhOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSPrivateKey_GetTrustDomain + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSTrustDomain * +NSSPrivateKey_GetTrustDomain +( + NSSPrivateKey *vk, + PRStatus *statusOpt +); + +/* + * NSSPrivateKey_GetToken + * + */ + +NSS_EXTERN NSSToken * +NSSPrivateKey_GetToken +( + NSSPrivateKey *vk +); + +/* + * NSSPrivateKey_GetSlot + * + */ + +NSS_EXTERN NSSSlot * +NSSPrivateKey_GetSlot +( + NSSPrivateKey *vk +); + +/* + * NSSPrivateKey_GetModule + * + */ + +NSS_EXTERN NSSModule * +NSSPrivateKey_GetModule +( + NSSPrivateKey *vk +); + +/* + * NSSPrivateKey_Decrypt + * + */ + +NSS_EXTERN NSSItem * +NSSPrivateKey_Decrypt +( + NSSPrivateKey *vk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *encryptedData, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSPrivateKey_Sign + * + */ + +NSS_EXTERN NSSItem * +NSSPrivateKey_Sign +( + NSSPrivateKey *vk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSPrivateKey_SignRecover + * + */ + +NSS_EXTERN NSSItem * +NSSPrivateKey_SignRecover +( + NSSPrivateKey *vk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSPrivateKey_UnwrapSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSPrivateKey_UnwrapSymmetricKey +( + NSSPrivateKey *vk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *wrappedKey, + NSSCallback *uhh +); + +/* + * NSSPrivateKey_DeriveSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSPrivateKey_DeriveSymmetricKey +( + NSSPrivateKey *vk, + NSSPublicKey *bk, + NSSAlgorithmAndParameters *apOpt, + NSSOID *target, + PRUint32 keySizeOpt, /* zero for best allowed */ + NSSOperations operations, + NSSCallback *uhh +); + +/* + * NSSPrivateKey_FindPublicKey + * + */ + +NSS_EXTERN NSSPublicKey * +NSSPrivateKey_FindPublicKey +( + NSSPrivateKey *vk + /* { don't need the callback here, right? } */ +); + +/* + * NSSPrivateKey_CreateCryptoContext + * + * Create a crypto context, in this key's trust domain, + * with this as the distinguished private key. + */ + +NSS_EXTERN NSSCryptoContext * +NSSPrivateKey_CreateCryptoContext +( + NSSPrivateKey *vk + NSSAlgorithmAndParameters *apOpt, + NSSCallback *uhh +); + +/* + * NSSPrivateKey_FindCertificates + * + * Note that there may be more than one certificate for this + * private key. { FilterCertificates function to further + * reduce the list. } + */ + +NSS_EXTERN NSSCertificate ** +NSSPrivateKey_FindCertificates +( + NSSPrivateKey *vk, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSPrivateKey_FindBestCertificate + * + * The parameters for this function will depend on what the users + * need. This is just a starting point. + */ + +NSS_EXTERN NSSCertificate * +NSSPrivateKey_FindBestCertificate +( + NSSPrivateKey *vk, + NSSTime *timeOpt, + NSSUsage *usageOpt, + NSSPolicies *policiesOpt +); + +/* + * NSSPublicKey + * + * Once you generate, find, or derive one of these, you can use it + * to perform (simple) cryptographic operations. Though there may + * be certificates associated with these public keys, they are not + * verified. + */ + +/* + * NSSPublicKey_Destroy + * + * Free a pointer to a public key object. + */ + +NSS_EXTERN PRStatus +NSSPublicKey_Destroy +( + NSSPublicKey *bk +); + +/* + * NSSPublicKey_DeleteStoredObject + * + * Permanently remove this object, and any related objects (such as the + * corresponding private keys and certificates). + */ + +NSS_EXTERN PRStatus +NSSPublicKey_DeleteStoredObject +( + NSSPublicKey *bk, + NSSCallback *uhh +); + +/* + * NSSPublicKey_Encode + * + */ + +NSS_EXTERN NSSItem * +NSSPublicKey_Encode +( + NSSPublicKey *bk, + NSSAlgorithmAndParameters *ap, + NSSCallback *uhhOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSPublicKey_GetTrustDomain + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSTrustDomain * +NSSPublicKey_GetTrustDomain +( + NSSPublicKey *bk, + PRStatus *statusOpt +); + +/* + * NSSPublicKey_GetToken + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSToken * +NSSPublicKey_GetToken +( + NSSPublicKey *bk, + PRStatus *statusOpt +); + +/* + * NSSPublicKey_GetSlot + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSSlot * +NSSPublicKey_GetSlot +( + NSSPublicKey *bk, + PRStatus *statusOpt +); + +/* + * NSSPublicKey_GetModule + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSModule * +NSSPublicKey_GetModule +( + NSSPublicKey *bk, + PRStatus *statusOpt +); + +/* + * NSSPublicKey_Encrypt + * + * Encrypt a single chunk of data with the public key corresponding to + * this certificate. + */ + +NSS_EXTERN NSSItem * +NSSPublicKey_Encrypt +( + NSSPublicKey *bk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSPublicKey_Verify + * + */ + +NSS_EXTERN PRStatus +NSSPublicKey_Verify +( + NSSPublicKey *bk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSItem *signature, + NSSCallback *uhh +); + +/* + * NSSPublicKey_VerifyRecover + * + */ + +NSS_EXTERN NSSItem * +NSSPublicKey_VerifyRecover +( + NSSPublicKey *bk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *signature, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSPublicKey_WrapSymmetricKey + * + */ + +NSS_EXTERN NSSItem * +NSSPublicKey_WrapSymmetricKey +( + NSSPublicKey *bk, + NSSAlgorithmAndParameters *apOpt, + NSSSymmetricKey *keyToWrap, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSPublicKey_CreateCryptoContext + * + * Create a crypto context, in this key's trust domain, with this + * as the distinguished public key. + */ + +NSS_EXTERN NSSCryptoContext * +NSSPublicKey_CreateCryptoContext +( + NSSPublicKey *bk + NSSAlgorithmAndParameters *apOpt, + NSSCallback *uhh +); + +/* + * NSSPublicKey_FindCertificates + * + * Note that there may be more than one certificate for this + * public key. The current implementation may not find every + * last certificate available for this public key: that would + * involve trolling e.g. huge ldap databases, which will be + * grossly inefficient and not generally useful. + * { FilterCertificates function to further reduce the list } + */ + +NSS_EXTERN NSSCertificate ** +NSSPublicKey_FindCertificates +( + NSSPublicKey *bk, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSPrivateKey_FindBestCertificate + * + * The parameters for this function will depend on what the users + * need. This is just a starting point. + */ + +NSS_EXTERN NSSCertificate * +NSSPublicKey_FindBestCertificate +( + NSSPublicKey *bk, + NSSTime *timeOpt, + NSSUsage *usageOpt, + NSSPolicies *policiesOpt +); + +/* + * NSSPublicKey_FindPrivateKey + * + */ + +NSS_EXTERN NSSPrivateKey * +NSSPublicKey_FindPrivateKey +( + NSSPublicKey *bk, + NSSCallback *uhh +); + +/* + * NSSSymmetricKey + * + */ + +/* + * NSSSymmetricKey_Destroy + * + * Free a pointer to a symmetric key object. + */ + +NSS_EXTERN PRStatus +NSSSymmetricKey_Destroy +( + NSSSymmetricKey *mk +); + +/* + * NSSSymmetricKey_DeleteStoredObject + * + * Permanently remove this object. + */ + +NSS_EXTERN PRStatus +NSSSymmetricKey_DeleteStoredObject +( + NSSSymmetricKey *mk, + NSSCallback *uhh +); + +/* + * NSSSymmetricKey_GetKeyLength + * + */ + +NSS_EXTERN PRUint32 +NSSSymmetricKey_GetKeyLength +( + NSSSymmetricKey *mk +); + +/* + * NSSSymmetricKey_GetKeyStrength + * + */ + +NSS_EXTERN PRUint32 +NSSSymmetricKey_GetKeyStrength +( + NSSSymmetricKey *mk +); + +/* + * NSSSymmetricKey_IsStillPresent + * + */ + +NSS_EXTERN PRStatus +NSSSymmetricKey_IsStillPresent +( + NSSSymmetricKey *mk +); + +/* + * NSSSymmetricKey_GetTrustDomain + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSTrustDomain * +NSSSymmetricKey_GetTrustDomain +( + NSSSymmetricKey *mk, + PRStatus *statusOpt +); + +/* + * NSSSymmetricKey_GetToken + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSToken * +NSSSymmetricKey_GetToken +( + NSSSymmetricKey *mk, + PRStatus *statusOpt +); + +/* + * NSSSymmetricKey_GetSlot + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSSlot * +NSSSymmetricKey_GetSlot +( + NSSSymmetricKey *mk, + PRStatus *statusOpt +); + +/* + * NSSSymmetricKey_GetModule + * + * There doesn't have to be one. + */ + +NSS_EXTERN NSSModule * +NSSSymmetricKey_GetModule +( + NSSSymmetricKey *mk, + PRStatus *statusOpt +); + +/* + * NSSSymmetricKey_Encrypt + * + */ + +NSS_EXTERN NSSItem * +NSSSymmetricKey_Encrypt +( + NSSSymmetricKey *mk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSSymmetricKey_Decrypt + * + */ + +NSS_EXTERN NSSItem * +NSSSymmetricKey_Decrypt +( + NSSSymmetricKey *mk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *encryptedData, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSSymmetricKey_Sign + * + */ + +NSS_EXTERN NSSItem * +NSSSymmetricKey_Sign +( + NSSSymmetricKey *mk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSSymmetricKey_SignRecover + * + */ + +NSS_EXTERN NSSItem * +NSSSymmetricKey_SignRecover +( + NSSSymmetricKey *mk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSSymmetricKey_Verify + * + */ + +NSS_EXTERN PRStatus +NSSSymmetricKey_Verify +( + NSSSymmetricKey *mk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSItem *signature, + NSSCallback *uhh +); + +/* + * NSSSymmetricKey_VerifyRecover + * + */ + +NSS_EXTERN NSSItem * +NSSSymmetricKey_VerifyRecover +( + NSSSymmetricKey *mk, + NSSAlgorithmAndParameters *apOpt, + NSSItem *signature, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSSymmetricKey_WrapSymmetricKey + * + */ + +NSS_EXTERN NSSItem * +NSSSymmetricKey_WrapSymmetricKey +( + NSSSymmetricKey *wrappingKey, + NSSAlgorithmAndParameters *apOpt, + NSSSymmetricKey *keyToWrap, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSSymmetricKey_WrapPrivateKey + * + */ + +NSS_EXTERN NSSItem * +NSSSymmetricKey_WrapPrivateKey +( + NSSSymmetricKey *wrappingKey, + NSSAlgorithmAndParameters *apOpt, + NSSPrivateKey *keyToWrap, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSSymmetricKey_UnwrapSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSSymmetricKey_UnwrapSymmetricKey +( + NSSSymmetricKey *wrappingKey, + NSSAlgorithmAndParameters *apOpt, + NSSItem *wrappedKey, + NSSOID *target, + PRUint32 keySizeOpt, + NSSOperations operations, + NSSCallback *uhh +); + +/* + * NSSSymmetricKey_UnwrapPrivateKey + * + */ + +NSS_EXTERN NSSPrivateKey * +NSSSymmetricKey_UnwrapPrivateKey +( + NSSSymmetricKey *wrappingKey, + NSSAlgorithmAndParameters *apOpt, + NSSItem *wrappedKey, + NSSUTF8 *labelOpt, + NSSItem *keyIDOpt, + PRBool persistant, + PRBool sensitive, + NSSToken *destinationOpt, + NSSCallback *uhh +); + +/* + * NSSSymmetricKey_DeriveSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSSymmetricKey_DeriveSymmetricKey +( + NSSSymmetricKey *originalKey, + NSSAlgorithmAndParameters *apOpt, + NSSOID *target, + PRUint32 keySizeOpt, + NSSOperations operations, + NSSCallback *uhh +); + +/* + * NSSSymmetricKey_CreateCryptoContext + * + * Create a crypto context, in this key's trust domain, + * with this as the distinguished symmetric key. + */ + +NSS_EXTERN NSSCryptoContext * +NSSSymmetricKey_CreateCryptoContext +( + NSSSymmetricKey *mk, + NSSAlgorithmAndParameters *apOpt, + NSSCallback *uhh +); + +/* + * NSSTrustDomain + * + */ + +/* + * NSSTrustDomain_Create + * + * This creates a trust domain, optionally with an initial cryptoki + * module. If the module name is not null, the module is loaded if + * needed (using the uriOpt argument), and initialized with the + * opaqueOpt argument. If mumble mumble priority settings, then + * module-specification objects in the module can cause the loading + * and initialization of further modules. + * + * The uriOpt is defined to take a URI. At present, we only + * support file: URLs pointing to platform-native shared libraries. + * However, by specifying this as a URI, this keeps open the + * possibility of supporting other, possibly remote, resources. + * + * The "reserved" arguments is held for when we figure out the + * module priority stuff. + */ + +NSS_EXTERN NSSTrustDomain * +NSSTrustDomain_Create +( + NSSUTF8 *moduleOpt, + NSSUTF8 *uriOpt, + NSSUTF8 *opaqueOpt, + void *reserved +); + +/* + * NSSTrustDomain_Destroy + * + */ + +NSS_EXTERN PRStatus +NSSTrustDomain_Destroy +( + NSSTrustDomain *td +); + +/* + * NSSTrustDomain_SetDefaultCallback + * + */ + +NSS_EXTERN PRStatus +NSSTrustDomain_SetDefaultCallback +( + NSSTrustDomain *td, + NSSCallback *newCallback, + NSSCallback **oldCallbackOpt +); + +/* + * NSSTrustDomain_GetDefaultCallback + * + */ + +NSS_EXTERN NSSCallback * +NSSTrustDomain_GetDefaultCallback +( + NSSTrustDomain *td, + PRStatus *statusOpt +); + +/* + * Default policies? + * Default usage? + * Default time, for completeness? + */ + +/* + * NSSTrustDomain_LoadModule + * + */ + +NSS_EXTERN PRStatus +NSSTrustDomain_LoadModule +( + NSSUTF8 *moduleOpt, + NSSUTF8 *uriOpt, + NSSUTF8 *opaqueOpt, + void *reserved +); + +/* + * NSSTrustDomain_AddModule + * NSSTrustDomain_AddSlot + * NSSTrustDomain_UnloadModule + * Managing modules, slots, tokens; priorities; + * Traversing all of the above + * this needs more work + */ + +/* + * NSSTrustDomain_DisableToken + * + */ + +NSS_EXTERN PRStatus +NSSTrustDomain_DisableToken +( + NSSTrustDomain *td, + NSSToken *token, + NSSError why +); + +/* + * NSSTrustDomain_EnableToken + * + */ + +NSS_EXTERN PRStatus +NSSTrustDomain_EnableToken +( + NSSTrustDomain *td, + NSSToken *token +); + +/* + * NSSTrustDomain_IsTokenEnabled + * + * If disabled, "why" is always on the error stack. + * The optional argument is just for convenience. + */ + +NSS_EXTERN PRStatus +NSSTrustDomain_IsTokenEnabled +( + NSSTrustDomain *td, + NSSToken *token, + NSSError *whyOpt +); + +/* + * NSSTrustDomain_FindSlotByName + * + */ + +NSS_EXTERN NSSSlot * +NSSTrustDomain_FindSlotByName +( + NSSTrustDomain *td, + NSSUTF8 *slotName +); + +/* + * NSSTrustDomain_FindTokenByName + * + */ + +NSS_EXTERN NSSToken * +NSSTrustDomain_FindTokenByName +( + NSSTrustDomain *td, + NSSUTF8 *tokenName +); + +/* + * NSSTrustDomain_FindTokenBySlotName + * + */ + +NSS_EXTERN NSSToken * +NSSTrustDomain_FindTokenBySlotName +( + NSSTrustDomain *td, + NSSUTF8 *slotName +); + +/* + * NSSTrustDomain_FindBestTokenForAlgorithm + * + */ + +NSS_EXTERN NSSToken * +NSSTrustDomain_FindTokenForAlgorithm +( + NSSTrustDomain *td, + NSSOID *algorithm +); + +/* + * NSSTrustDomain_FindBestTokenForAlgorithms + * + */ + +NSS_EXTERN NSSToken * +NSSTrustDomain_FindBestTokenForAlgorithms +( + NSSTrustDomain *td, + NSSOID *algorithms[], /* may be null-terminated */ + PRUint32 nAlgorithmsOpt /* limits the array if nonzero */ +); + +/* + * NSSTrustDomain_Login + * + */ + +NSS_EXTERN PRStatus +NSSTrustDomain_Login +( + NSSTrustDomain *td, + NSSCallback *uhhOpt +); + +/* + * NSSTrustDomain_Logout + * + */ + +NSS_EXTERN PRStatus +NSSTrustDomain_Logout +( + NSSTrustDomain *td +); + +/* Importing things */ + +/* + * NSSTrustDomain_ImportCertificate + * + * The implementation will pull some data out of the certificate + * (e.g. e-mail address) for use in pkcs#11 object attributes. + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_ImportCertificate +( + NSSTrustDomain *td, + NSSCertificate *c +); + +/* + * NSSTrustDomain_ImportPKIXCertificate + * + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_ImportPKIXCertificate +( + NSSTrustDomain *td, + /* declared as a struct until these "data types" are defined */ + struct NSSPKIXCertificateStr *pc +); + +/* + * NSSTrustDomain_ImportEncodedCertificate + * + * Imports any type of certificate we support. + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_ImportEncodedCertificate +( + NSSTrustDomain *td, + NSSBER *ber +); + +/* + * NSSTrustDomain_ImportEncodedCertificateChain + * + * If you just want the leaf, pass in a maximum of one. + */ + +NSS_EXTERN NSSCertificate ** +NSSTrustDomain_ImportEncodedCertificateChain +( + NSSTrustDomain *td, + NSSBER *ber, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSTrustDomain_ImportEncodedPrivateKey + * + */ + +NSS_EXTERN NSSPrivateKey * +NSSTrustDomain_ImportEncodedPrivateKey +( + NSSTrustDomain *td, + NSSBER *ber, + NSSItem *passwordOpt, /* NULL will cause a callback */ + NSSCallback *uhhOpt, + NSSToken *destination +); + +/* + * NSSTrustDomain_ImportEncodedPublicKey + * + */ + +NSS_EXTERN NSSPublicKey * +NSSTrustDomain_ImportEncodedPublicKey +( + NSSTrustDomain *td, + NSSBER *ber +); + +/* Other importations: S/MIME capabilities */ + +/* + * NSSTrustDomain_FindBestCertificateByNickname + * + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindBestCertificateByNickname +( + NSSTrustDomain *td, + NSSUTF8 *name, + NSSTime *timeOpt, /* NULL for "now" */ + NSSUsage *usage, + NSSPolicies *policiesOpt /* NULL for none */ +); + +/* + * NSSTrustDomain_FindCertificatesByNickname + * + */ + +NSS_EXTERN NSSCertificate ** +NSSTrustDomain_FindCertificatesByNickname +( + NSSTrustDomain *td, + NSSUTF8 *name, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSTrustDomain_FindCertificateByIssuerAndSerialNumber + * + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindCertificateByIssuerAndSerialNumber +( + NSSTrustDomain *td, + NSSDER *issuer, + NSSDER *serialNumber +); + +/* + * NSSTrustDomain_FindCertificatesByIssuerAndSerialNumber + * + * Theoretically, this should never happen. However, some companies + * we know have issued duplicate certificates with the same issuer + * and serial number. Do we just ignore them? I'm thinking yes. + */ + +/* + * NSSTrustDomain_FindBestCertificateBySubject + * + * This does not search through alternate names hidden in extensions. + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindBestCertificateBySubject +( + NSSTrustDomain *td, + NSSUTF8 *subject, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt +); + +/* + * NSSTrustDomain_FindCertificatesBySubject + * + * This does not search through alternate names hidden in extensions. + */ + +NSS_EXTERN NSSCertificate ** +NSSTrustDomain_FindCertificatesBySubject +( + NSSTrustDomain *td, + NSSUTF8 *subject, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSTrustDomain_FindBestCertificateByNameComponents + * + * This call does try several tricks, including a pseudo pkcs#11 + * attribute for the ldap module to try as a query. Eventually + * this call falls back to a traversal if that's what's required. + * It will search through alternate names hidden in extensions. + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindBestCertificateByNameComponents +( + NSSTrustDomain *td, + NSSUTF8 *nameComponents, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt +); + +/* + * NSSTrustDomain_FindCertificatesByNameComponents + * + * This call, too, tries several tricks. It will stop on the first + * attempt that generates results, so it won't e.g. traverse the + * entire ldap database. + */ + +NSS_EXTERN NSSCertificate ** +NSSTrustDomain_FindCertificatesByNameComponents +( + NSSTrustDomain *td, + NSSUTF8 *nameComponents, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSTrustDomain_FindCertificateByEncodedCertificate + * + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindCertificateByEncodedCertificate +( + NSSTrustDomain *td, + NSSBER *encodedCertificate +); + +/* + * NSSTrustDomain_FindBestCertificateByEmail + * + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindCertificateByEmail +( + NSSTrustDomain *td, + NSSASCII7 *email, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt +); + +/* + * NSSTrustDomain_FindCertificatesByEmail + * + */ + +NSS_EXTERN NSSCertificate ** +NSSTrustDomain_FindCertificateByEmail +( + NSSTrustDomain *td, + NSSASCII7 *email, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSTrustDomain_FindCertificateByOCSPHash + * + * There can be only one. + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindCertificateByOCSPHash +( + NSSTrustDomain *td, + NSSItem *hash +); + +/* + * NSSTrustDomain_TraverseCertificates + * + * This function descends from one in older versions of NSS which + * traverses the certs in the permanent database. That function + * was used to implement selection routines, but was directly + * available too. Trust domains are going to contain a lot more + * certs now (e.g., an ldap server), so we'd really like to + * discourage traversal. Thus for now, this is commented out. + * If it's needed, let's look at the situation more closely to + * find out what the actual requirements are. + * + * + * NSS_EXTERN PRStatus * + * NSSTrustDomain_TraverseCertificates + * ( + * NSSTrustDomain *td, + * PRStatus (*callback)(NSSCertificate *c, void *arg), + * void *arg + * ); + */ + +/* + * NSSTrustDomain_FindBestUserCertificate + * + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindBestUserCertificate +( + NSSTrustDomain *td, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt +); + +/* + * NSSTrustDomain_FindUserCertificates + * + */ + +NSS_EXTERN NSSCertificate ** +NSSTrustDomain_FindUserCertificates +( + NSSTrustDomain *td, + NSSTime *timeOpt, + NSSUsage *usageOpt, + NSSPolicies *policiesOpt, + NSSCertificate **rvOpt, + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt +); + +/* + * NSSTrustDomain_FindBestUserCertificateForSSLClientAuth + * + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindBestUserCertificateForSSLClientAuth +( + NSSTrustDomain *td, + NSSUTF8 *sslHostOpt, + NSSDER *rootCAsOpt[], /* null pointer for none */ + PRUint32 rootCAsMaxOpt, /* zero means list is null-terminated */ + NSSAlgorithmAndParameters *apOpt, + NSSPolicies *policiesOpt +); + +/* + * NSSTrustDomain_FindUserCertificatesForSSLClientAuth + * + */ + +NSS_EXTERN NSSCertificate ** +NSSTrustDomain_FindUserCertificatesForSSLClientAuth +( + NSSTrustDomain *td, + NSSUTF8 *sslHostOpt, + NSSDER *rootCAsOpt[], /* null pointer for none */ + PRUint32 rootCAsMaxOpt, /* zero means list is null-terminated */ + NSSAlgorithmAndParameters *apOpt, + NSSPolicies *policiesOpt, + NSSCertificate **rvOpt, + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt +); + +/* + * NSSTrustDomain_FindBestUserCertificateForEmailSigning + * + */ + +NSS_EXTERN NSSCertificate * +NSSTrustDomain_FindBestUserCertificateForEmailSigning +( + NSSTrustDomain *td, + NSSASCII7 *signerOpt, + NSSASCII7 *recipientOpt, + /* anything more here? */ + NSSAlgorithmAndParameters *apOpt, + NSSPolicies *policiesOpt +); + +/* + * NSSTrustDomain_FindUserCertificatesForEmailSigning + * + */ + +NSS_EXTERN NSSCertificate ** +NSSTrustDomain_FindUserCertificatesForEmailSigning +( + NSSTrustDomain *td, + NSSASCII7 *signerOpt, + NSSASCII7 *recipientOpt, + /* anything more here? */ + NSSAlgorithmAndParameters *apOpt, + NSSPolicies *policiesOpt, + NSSCertificate **rvOpt, + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt +); + +/* + * Here is where we'd add more Find[Best]UserCertificate[s]For<usage> + * routines. + */ + +/* Private Keys */ + +/* + * NSSTrustDomain_GenerateKeyPair + * + * Creates persistant objects. If you want session objects, use + * NSSCryptoContext_GenerateKeyPair. The destination token is where + * the keys are stored. If that token can do the required math, then + * that's where the keys are generated too. Otherwise, the keys are + * generated elsewhere and moved to that token. + */ + +NSS_EXTERN PRStatus +NSSTrustDomain_GenerateKeyPair +( + NSSTrustDomain *td, + NSSAlgorithmAndParameters *ap, + NSSPrivateKey **pvkOpt, + NSSPublicKey **pbkOpt, + PRBool privateKeyIsSensitive, + NSSToken *destination, + NSSCallback *uhhOpt +); + +/* + * NSSTrustDomain_TraversePrivateKeys + * + * + * NSS_EXTERN PRStatus * + * NSSTrustDomain_TraversePrivateKeys + * ( + * NSSTrustDomain *td, + * PRStatus (*callback)(NSSPrivateKey *vk, void *arg), + * void *arg + * ); + */ + +/* Symmetric Keys */ + +/* + * NSSTrustDomain_GenerateSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSTrustDomain_GenerateSymmetricKey +( + NSSTrustDomain *td, + NSSAlgorithmAndParameters *ap, + PRUint32 keysize, + NSSToken *destination, + NSSCallback *uhhOpt +); + +/* + * NSSTrustDomain_GenerateSymmetricKeyFromPassword + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSTrustDomain_GenerateSymmetricKeyFromPassword +( + NSSTrustDomain *td, + NSSAlgorithmAndParameters *ap, + NSSUTF8 *passwordOpt, /* if null, prompt */ + NSSToken *destinationOpt, + NSSCallback *uhhOpt +); + +/* + * NSSTrustDomain_FindSymmetricKeyByAlgorithm + * + * Is this still needed? + * + * NSS_EXTERN NSSSymmetricKey * + * NSSTrustDomain_FindSymmetricKeyByAlgorithm + * ( + * NSSTrustDomain *td, + * NSSOID *algorithm, + * NSSCallback *uhhOpt + * ); + */ + +/* + * NSSTrustDomain_FindSymmetricKeyByAlgorithmAndKeyID + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSTrustDomain_FindSymmetricKeyByAlgorithmAndKeyID +( + NSSTrustDomain *td, + NSSOID *algorithm, + NSSItem *keyID, + NSSCallback *uhhOpt +); + +/* + * NSSTrustDomain_TraverseSymmetricKeys + * + * + * NSS_EXTERN PRStatus * + * NSSTrustDomain_TraverseSymmetricKeys + * ( + * NSSTrustDomain *td, + * PRStatus (*callback)(NSSSymmetricKey *mk, void *arg), + * void *arg + * ); + */ + +/* + * NSSTrustDomain_CreateCryptoContext + * + * If a callback object is specified, it becomes the for the crypto + * context; otherwise, this trust domain's default (if any) is + * inherited. + */ + +NSS_EXTERN NSSCryptoContext * +NSSTrustDomain_CreateCryptoContext +( + NSSTrustDomain *td, + NSSCallback *uhhOpt +); + +/* + * NSSTrustDomain_CreateCryptoContextForAlgorithm + * + */ + +NSS_EXTERN NSSCryptoContext * +NSSTrustDomain_CreateCryptoContextForAlgorithm +( + NSSTrustDomain *td, + NSSOID *algorithm +); + +/* + * NSSTrustDomain_CreateCryptoContextForAlgorithmAndParameters + * + */ + +NSS_EXTERN NSSCryptoContext * +NSSTrustDomain_CreateCryptoContextForAlgorithmAndParameters +( + NSSTrustDomain *td, + NSSAlgorithmAndParameters *ap +); + +/* find/traverse other objects, e.g. s/mime profiles */ + +/* + * NSSCryptoContext + * + * A crypto context is sort of a short-term snapshot of a trust domain, + * used for the life of "one crypto operation." You can also think of + * it as a "temporary database." + * + * Just about all of the things you can do with a trust domain -- importing + * or creating certs, keys, etc. -- can be done with a crypto context. + * The difference is that the objects will be temporary ("session") objects. + * + * Also, if the context was created for a key, cert, and/or algorithm; or + * if such objects have been "associated" with the context, then the context + * can do everything the keys can, like crypto operations. + * + * And finally, because it keeps the state of the crypto operations, it + * can do streaming crypto ops. + */ + +/* + * NSSTrustDomain_Destroy + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_Destroy +( + NSSCryptoContext *td +); + +/* establishing a default callback */ + +/* + * NSSCryptoContext_SetDefaultCallback + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_SetDefaultCallback +( + NSSCryptoContext *td, + NSSCallback *newCallback, + NSSCallback **oldCallbackOpt +); + +/* + * NSSCryptoContext_GetDefaultCallback + * + */ + +NSS_EXTERN NSSCallback * +NSSCryptoContext_GetDefaultCallback +( + NSSCryptoContext *td, + PRStatus *statusOpt +); + +/* + * NSSCryptoContext_GetTrustDomain + * + */ + +NSS_EXTERN NSSTrustDomain * +NSSCryptoContext_GetTrustDomain +( + NSSCryptoContext *td +); + +/* AddModule, etc: should we allow "temporary" changes here? */ +/* DisableToken, etc: ditto */ +/* Ordering of tokens? */ +/* Finding slots+token etc. */ +/* login+logout */ + +/* Importing things */ + +/* + * NSSCryptoContext_ImportCertificate + * + * If there's not a "distinguished certificate" for this context, this + * sets the specified one to be it. + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_ImportCertificate +( + NSSCryptoContext *cc, + NSSCertificate *c +); + +/* + * NSSCryptoContext_ImportPKIXCertificate + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_ImportPKIXCertificate +( + NSSCryptoContext *cc, + struct NSSPKIXCertificateStr *pc +); + +/* + * NSSCryptoContext_ImportEncodedCertificate + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_ImportEncodedCertificate +( + NSSCryptoContext *cc, + NSSBER *ber +); + +/* + * NSSCryptoContext_ImportEncodedPKIXCertificateChain + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_ImportEncodedPKIXCertificateChain +( + NSSCryptoContext *cc, + NSSBER *ber +); + +/* Other importations: S/MIME capabilities + */ + +/* + * NSSCryptoContext_FindBestCertificateByNickname + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindBestCertificateByNickname +( + NSSCryptoContext *cc, + NSSUTF8 *name, + NSSTime *timeOpt, /* NULL for "now" */ + NSSUsage *usage, + NSSPolicies *policiesOpt /* NULL for none */ +); + +/* + * NSSCryptoContext_FindCertificatesByNickname + * + */ + +NSS_EXTERN NSSCertificate ** +NSSCryptoContext_FindCertificatesByNickname +( + NSSCryptoContext *cc, + NSSUTF8 *name, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FindCertificateByIssuerAndSerialNumber + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindCertificateByIssuerAndSerialNumber +( + NSSCryptoContext *cc, + NSSDER *issuer, + NSSDER *serialNumber +); + +/* + * NSSCryptoContext_FindBestCertificateBySubject + * + * This does not search through alternate names hidden in extensions. + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindBestCertificateBySubject +( + NSSCryptoContext *cc, + NSSUTF8 *subject, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt +); + +/* + * NSSCryptoContext_FindCertificatesBySubject + * + * This does not search through alternate names hidden in extensions. + */ + +NSS_EXTERN NSSCertificate ** +NSSCryptoContext_FindCertificatesBySubject +( + NSSCryptoContext *cc, + NSSUTF8 *subject, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FindBestCertificateByNameComponents + * + * This call does try several tricks, including a pseudo pkcs#11 + * attribute for the ldap module to try as a query. Eventually + * this call falls back to a traversal if that's what's required. + * It will search through alternate names hidden in extensions. + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindBestCertificateByNameComponents +( + NSSCryptoContext *cc, + NSSUTF8 *nameComponents, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt +); + +/* + * NSSCryptoContext_FindCertificatesByNameComponents + * + * This call, too, tries several tricks. It will stop on the first + * attempt that generates results, so it won't e.g. traverse the + * entire ldap database. + */ + +NSS_EXTERN NSSCertificate ** +NSSCryptoContext_FindCertificatesByNameComponents +( + NSSCryptoContext *cc, + NSSUTF8 *nameComponents, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FindCertificateByEncodedCertificate + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindCertificateByEncodedCertificate +( + NSSCryptoContext *cc, + NSSBER *encodedCertificate +); + +/* + * NSSCryptoContext_FindBestCertificateByEmail + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindBestCertificateByEmail +( + NSSCryptoContext *cc, + NSSASCII7 *email +); + +/* + * NSSCryptoContext_FindCertificatesByEmail + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindCertificatesByEmail +( + NSSCryptoContext *cc, + NSSASCII7 *email, + NSSCertificate *rvOpt[], + PRUint32 maximumOpt, /* 0 for no max */ + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FindCertificateByOCSPHash + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindCertificateByOCSPHash +( + NSSCryptoContext *cc, + NSSITem *hash +); + +/* + * NSSCryptoContext_TraverseCertificates + * + * + * NSS_EXTERN PRStatus * + * NSSCryptoContext_TraverseCertificates + * ( + * NSSCryptoContext *cc, + * PRStatus (*callback)(NSSCertificate *c, void *arg), + * void *arg + * ); + */ + +/* + * NSSCryptoContext_FindBestUserCertificate + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindBestUserCertificate +( + NSSCryptoContext *cc, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt +); + +/* + * NSSCryptoContext_FindUserCertificates + * + */ + +NSS_EXTERN NSSCertificate ** +NSSCryptoContext_FindUserCertificates +( + NSSCryptoContext *cc, + NSSTime *timeOpt, + NSSUsage *usageOpt, + NSSPolicies *policiesOpt, + NSSCertificate **rvOpt, + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FindBestUserCertificateForSSLClientAuth + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindBestUserCertificateForSSLClientAuth +( + NSSCryptoContext *cc, + NSSUTF8 *sslHostOpt, + NSSDER *rootCAsOpt[], /* null pointer for none */ + PRUint32 rootCAsMaxOpt, /* zero means list is null-terminated */ + NSSAlgorithmAndParameters *apOpt, + NSSPolicies *policiesOpt +); + +/* + * NSSCryptoContext_FindUserCertificatesForSSLClientAuth + * + */ + +NSS_EXTERN NSSCertificate ** +NSSCryptoContext_FindUserCertificatesForSSLClientAuth +( + NSSCryptoContext *cc, + NSSUTF8 *sslHostOpt, + NSSDER *rootCAsOpt[], /* null pointer for none */ + PRUint32 rootCAsMaxOpt, /* zero means list is null-terminated */ + NSSAlgorithmAndParameters *apOpt, + NSSPolicies *policiesOpt, + NSSCertificate **rvOpt, + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FindBestUserCertificateForEmailSigning + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindBestUserCertificateForEmailSigning +( + NSSCryptoContext *cc, + NSSASCII7 *signerOpt, + NSSASCII7 *recipientOpt, + /* anything more here? */ + NSSAlgorithmAndParameters *apOpt, + NSSPolicies *policiesOpt +); + +/* + * NSSCryptoContext_FindUserCertificatesForEmailSigning + * + */ + +NSS_EXTERN NSSCertificate * +NSSCryptoContext_FindUserCertificatesForEmailSigning +( + NSSCryptoContext *cc, + NSSASCII7 *signerOpt, /* fgmr or a more general name? */ + NSSASCII7 *recipientOpt, + /* anything more here? */ + NSSAlgorithmAndParameters *apOpt, + NSSPolicies *policiesOpt, + NSSCertificate **rvOpt, + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt +); + +/* Private Keys */ + +/* + * NSSCryptoContext_GenerateKeyPair + * + * Creates session objects. If you want persistant objects, use + * NSSTrustDomain_GenerateKeyPair. The destination token is where + * the keys are stored. If that token can do the required math, then + * that's where the keys are generated too. Otherwise, the keys are + * generated elsewhere and moved to that token. + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_GenerateKeyPair +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *ap, + NSSPrivateKey **pvkOpt, + NSSPublicKey **pbkOpt, + PRBool privateKeyIsSensitive, + NSSToken *destination, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_TraversePrivateKeys + * + * + * NSS_EXTERN PRStatus * + * NSSCryptoContext_TraversePrivateKeys + * ( + * NSSCryptoContext *cc, + * PRStatus (*callback)(NSSPrivateKey *vk, void *arg), + * void *arg + * ); + */ + +/* Symmetric Keys */ + +/* + * NSSCryptoContext_GenerateSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSCryptoContext_GenerateSymmetricKey +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *ap, + PRUint32 keysize, + NSSToken *destination, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_GenerateSymmetricKeyFromPassword + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSCryptoContext_GenerateSymmetricKeyFromPassword +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *ap, + NSSUTF8 *passwordOpt, /* if null, prompt */ + NSSToken *destinationOpt, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_FindSymmetricKeyByAlgorithm + * + * + * NSS_EXTERN NSSSymmetricKey * + * NSSCryptoContext_FindSymmetricKeyByType + * ( + * NSSCryptoContext *cc, + * NSSOID *type, + * NSSCallback *uhhOpt + * ); + */ + +/* + * NSSCryptoContext_FindSymmetricKeyByAlgorithmAndKeyID + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSCryptoContext_FindSymmetricKeyByAlgorithmAndKeyID +( + NSSCryptoContext *cc, + NSSOID *algorithm, + NSSItem *keyID, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_TraverseSymmetricKeys + * + * + * NSS_EXTERN PRStatus * + * NSSCryptoContext_TraverseSymmetricKeys + * ( + * NSSCryptoContext *cc, + * PRStatus (*callback)(NSSSymmetricKey *mk, void *arg), + * void *arg + * ); + */ + +/* Crypto ops on distinguished keys */ + +/* + * NSSCryptoContext_Decrypt + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_Decrypt +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *encryptedData, + NSSCallback *uhhOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_BeginDecrypt + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_BeginDecrypt +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_ContinueDecrypt + * + */ + +/* + * NSSItem semantics: + * + * If rvOpt is NULL, a new NSSItem and buffer are allocated. + * If rvOpt is not null, but the buffer pointer is null, + * then rvOpt is returned but a new buffer is allocated. + * In this case, if the length value is not zero, then + * no more than that much space will be allocated. + * If rvOpt is not null and the buffer pointer is not null, + * then that buffer is re-used. No more than the buffer + * length value will be used; if it's not enough, an + * error is returned. If less is used, the number is + * adjusted downwards. + * + * Note that although this is short of some ideal "Item" + * definition, we can usually tell how big these buffers + * have to be. + * + * Feedback is requested; and earlier is better than later. + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_ContinueDecrypt +( + NSSCryptoContext *cc, + NSSItem *data, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FinishDecrypt + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_FinishDecrypt +( + NSSCryptoContext *cc, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_Sign + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_Sign +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhhOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_BeginSign + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_BeginSign +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_ContinueSign + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_BeginSign +( + NSSCryptoContext *cc, + NSSItem *data +); + +/* + * NSSCryptoContext_FinishSign + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_FinishSign +( + NSSCryptoContext *cc, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_SignRecover + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_SignRecover +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhhOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_BeginSignRecover + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_BeginSignRecover +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_ContinueSignRecover + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_ContinueSignRecover +( + NSSCryptoContext *cc, + NSSItem *data, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FinishSignRecover + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_FinishSignRecover +( + NSSCryptoContext *cc, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_UnwrapSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSCryptoContext_UnwrapSymmetricKey +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *wrappedKey, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_DeriveSymmetricKey + * + */ + +NSS_EXTERN NSSSymmetricKey * +NSSCryptoContext_DeriveSymmetricKey +( + NSSCryptoContext *cc, + NSSPublicKey *bk, + NSSAlgorithmAndParameters *apOpt, + NSSOID *target, + PRUint32 keySizeOpt, /* zero for best allowed */ + NSSOperations operations, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_Encrypt + * + * Encrypt a single chunk of data with the distinguished public key + * of this crypto context. + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_Encrypt +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhhOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_BeginEncrypt + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_BeginEncrypt +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_ContinueEncrypt + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_ContinueEncrypt +( + NSSCryptoContext *cc, + NSSItem *data, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FinishEncrypt + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_FinishEncrypt +( + NSSCryptoContext *cc, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_Verify + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_Verify +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSItem *signature, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_BeginVerify + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_BeginVerify +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *signature, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_ContinueVerify + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_ContinueVerify +( + NSSCryptoContext *cc, + NSSItem *data +); + +/* + * NSSCryptoContext_FinishVerify + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_FinishVerify +( + NSSCryptoContext *cc +); + +/* + * NSSCryptoContext_VerifyRecover + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_VerifyRecover +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *signature, + NSSCallback *uhhOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_BeginVerifyRecover + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_BeginVerifyRecover +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_ContinueVerifyRecover + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_ContinueVerifyRecover +( + NSSCryptoContext *cc, + NSSItem *data, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_FinishVerifyRecover + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_FinishVerifyRecover +( + NSSCryptoContext *cc, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_WrapSymmetricKey + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_WrapSymmetricKey +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSSymmetricKey *keyToWrap, + NSSCallback *uhhOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_Digest + * + * Digest a single chunk of data with the distinguished digest key + * of this crypto context. + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_Digest +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSCallback *uhhOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * NSSCryptoContext_BeginDigest + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_BeginDigest +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSCallback *uhhOpt +); + +/* + * NSSCryptoContext_ContinueDigest + * + */ + +NSS_EXTERN PRStatus +NSSCryptoContext_ContinueDigest +( + NSSCryptoContext *cc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *item +); + +/* + * NSSCryptoContext_FinishDigest + * + */ + +NSS_EXTERN NSSItem * +NSSCryptoContext_FinishDigest +( + NSSCryptoContext *cc, + NSSItem *rvOpt, + NSSArena *arenaOpt +); + +/* + * tbd: Combination ops + */ + +/* + * NSSCryptoContext_Clone + * + */ + +NSS_EXTERN NSSCryptoContext * +NSSCryptoContext_Clone +( + NSSCryptoContext *cc +); + +/* + * NSSCryptoContext_Save + * NSSCryptoContext_Restore + * + * We need to be able to save and restore the state of contexts. + * Perhaps a mark-and-release mechanism would be better? + */ + +/* + * ..._SignTBSCertificate + * + * This requires feedback from the cert server team. + */ + +/* + * PRBool NSSCertificate_GetIsTrustedFor{xxx}(NSSCertificate *c); + * PRStatus NSSCertificate_SetIsTrustedFor{xxx}(NSSCertificate *c, PRBool trusted); + * + * These will be helper functions which get the trust object for a cert, + * and then call the corresponding function(s) on it. + * + * PKIX trust objects will have methods to manipulate the low-level trust + * bits (which are based on key usage and extended key usage), and also the + * conceptual high-level usages (e.g. ssl client auth, email encryption, etc.) + * + * Other types of trust objects (if any) might have different low-level + * representations, but hopefully high-level concepts would map. + * + * Only these high-level general routines would be promoted to the + * general certificate level here. Hence the {xxx} above would be things + * like "EmailSigning." + * + * + * NSSPKIXTrust *NSSCertificate_GetPKIXTrustObject(NSSCertificate *c); + * PRStatus NSSCertificate_SetPKIXTrustObject(NSSCertificate *c, NSPKIXTrust *t); + * + * I want to hold off on any general trust object until we've investigated + * other models more thoroughly. + */ + +PR_END_EXTERN_C + +#endif /* NSSPKI_H */ diff --git a/security/nss/lib/pki/nsspkit.h b/security/nss/lib/pki/nsspkit.h new file mode 100644 index 000000000..e1b5888fa --- /dev/null +++ b/security/nss/lib/pki/nsspkit.h @@ -0,0 +1,261 @@ +/* + * 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 NSSPKIT_H +#define NSSPKIT_H + +#ifdef DEBUG +static const char NSSPKIT_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +/* + * nsspkit.h + * + * This file defines the types of the top-level PKI objects. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +PR_BEGIN_EXTERN_C + +/* + * NSSCertificate + * + * This is the public representation of a Certificate. The certificate + * may be one found on a smartcard or other token, one decoded from data + * received as part of a protocol, one constructed from constituent + * parts, etc. Usually it is associated with ("in") a trust domain; as + * it can be verified only within a trust domain. The underlying type + * of certificate may be of any supported standard, e.g. PKIX, PGP, etc. + * + * People speak of "verifying (with) the server's, or correspondant's, + * certificate"; for simple operations we support that simplification + * by implementing public-key crypto operations as methods on this type. + */ + +struct NSSCertificateStr; +typedef struct NSSCertificateStr NSSCertificate; + +/* + * NSSUserCertificate + * + * A ``User'' certificate is one for which the private key is available. + * People speak of "using my certificate to sign my email" and "using + * my certificate to authenticate to (or login to) the server"; for + * simple operations, we support that simplification by implementing + * private-key crypto operations as methods on this type. + * + * The current design only weakly distinguishes between certificates + * and user certificates: as far as the compiler goes they're + * interchangable; debug libraries only have one common pointer-tracker; + * etc. However, attempts to do private-key operations on a certificate + * for which the private key is not available will fail. + * + * Open design question: should these types be more firmly separated? + */ + +typedef NSSCertificate NSSUserCertificate; + +/* + * NSSPrivateKey + * + * This is the public representation of a Private Key. In general, + * the actual value of the key is not available, but operations may + * be performed with it. + */ + +struct NSSPrivateKeyStr; +typedef struct NSSPrivateKeyStr NSSPrivateKey; + +/* + * NSSPublicKey + * + */ + +struct NSSPublicKeyStr; +typedef struct NSSPublicKeyStr NSSPublicKey; + +/* + * NSSSymmetricKey + * + */ + +struct NSSSymmetricKeyStr; +typedef struct NSSSymmetricKeyStr NSSSymmetricKey; + +/* + * NSSTrustDomain + * + * A Trust Domain is the field in which certificates may be validated. + * A trust domain will generally have one or more cryptographic modules + * open; these modules perform the cryptographic operations, and + * provide the basic "root" trust information from which the trust in + * a specific certificate or key depends. + * + * A client program, or a simple server, would typically have one + * trust domain. A server supporting multiple "virtual servers" might + * have a separate trust domain for each virtual server. The separate + * trust domains might share some modules (e.g., a hardware crypto + * accelerator) but not others (e.g., the tokens storing the different + * servers' private keys, or the databases with each server's trusted + * root certificates). + * + * This object descends from the "permananet database" in the old code. + */ + +struct NSSTrustDomainStr; +typedef struct NSSTrustDomainStr NSSTrustDomain; + +/* + * NSSCryptoContext + * + * A Crypto Context is a short-term, "helper" object which is used + * for the lifetime of one ongoing "crypto operation." Such an + * operation may be the creation of a signed message, the use of an + * TLS socket connection, etc. Each crypto context is "in" a + * specific trust domain, and it may have associated with it a + * distinguished certificate, public key, private key, and/or + * symmetric key. It can also temporarily hold and use temporary + * data (e.g. intermediate certificates) which is not stored + * permanently in the trust domain. + * + * In OO terms, this interface inherits interfaces from the trust + * domain, the certificates, and the keys. It also provides + * streaming crypto operations. + * + * This object descends from the "temporary database" concept in the + * old code, but it has changed a lot as a result of what we've + * learned. + */ + +struct NSSCryptoContextStr; +typedef struct NSSCryptoContextStr NSSCryptoContext; + +/* + * fgmr others + */ + +/* + * NSSTime + * + * Unfortunately, we need an "exceptional" value to indicate + * an error upon return, or "no value" on input. Note that zero + * is a perfectly valid value for both time_t and PRTime. + * + * If we were to create a "range" object, with two times for + * Not Before and Not After, we would have an obvious place for + * the somewhat arbitrary logic involved in comparing them. + * + * Failing that, let's have an NSSTime_CompareRanges function. + */ + +struct NSSTimeStr; +typedef struct NSSTimeStr NSSTime; + +/* + * NSSUsage + * + * This is trickier than originally planned; I'll write up a + * doc on it. + * + * We'd still like nsspki.h to have a list of common usages, + * e.g.: + * + * extern const NSSUsage *NSSUsage_ClientAuth; + * extern const NSSUsage *NSSUsage_ServerAuth; + * extern const NSSUsage *NSSUsage_SignEmail; + * extern const NSSUsage *NSSUsage_EncryptEmail; + * etc. + */ + +struct NSSUsageStr; +typedef struct NSSUsageStr NSSUsage; + +/* + * NSSPolicies + * + * Placeholder, for now. + */ + +struct NSSPoliciesStr; +typedef struct NSSPoliciesStr NSSPolicies; + +/* + * NSSAlgorithmAndParameters + * + * Algorithm is an OID + * Parameters depend on the algorithm + */ + +struct NSSAlgorithmAndParametersStr; +typedef struct NSSAlgorithmAndParametersStr NSSAlgorithmAndParameters; + +/* + * NSSCallback + * + * At minimum, a "challenge" method and a closure argument. + * Usually the challenge will just be prompting for a password. + * How OO do we want to make it? + */ + +struct NSSCallbackStr; +typedef struct NSSCallbackStr NSSCallback; + +/* + * NSSModule and NSSSlot -- placeholders for the PKCS#11 types + */ + +struct NSSModuleStr; +typedef struct NSSModuleStr NSSModule; + +struct NSSSlotStr; +typedef struct NSSSlotStr NSSSlot; + +typedef PRUint32 NSSOperations; +/* 1) Do we want these to be preprocessor definitions or constants? */ +/* 2) What is the correct and complete list? */ + +#define NSSOperations_ENCRYPT 0x0001 +#define NSSOperations_DECRYPT 0x0002 +#define NSSOperations_WRAP 0x0004 +#define NSSOperations_UNWRAP 0x0008 +#define NSSOperations_SIGN 0x0010 +#define NSSOperations_SIGN_RECOVER 0x0020 +#define NSSOperations_VERIFY 0x0040 +#define NSSOperations_VERIFY_RECOVER 0x0080 + +PR_END_EXTERN_C + +#endif /* NSSPKIT_H */ diff --git a/security/nss/lib/pki1/.cvsignore b/security/nss/lib/pki1/.cvsignore new file mode 100644 index 000000000..12bfbfc7c --- /dev/null +++ b/security/nss/lib/pki1/.cvsignore @@ -0,0 +1,3 @@ +oiddata.c +oiddata.h + diff --git a/security/nss/lib/pki1/Makefile b/security/nss/lib/pki1/Makefile new file mode 100644 index 000000000..03e1fb4c6 --- /dev/null +++ b/security/nss/lib/pki1/Makefile @@ -0,0 +1,38 @@ +# +# 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. +# +MAKEFILE_CVS_ID = "@(#) $RCSfile$ $Revision$ $Date$ $Name$" + +include manifest.mn +include config.mk +include $(CORE_DEPTH)/coreconf/config.mk +include $(CORE_DEPTH)/coreconf/rules.mk diff --git a/security/nss/lib/pki1/atav.c b/security/nss/lib/pki1/atav.c new file mode 100644 index 000000000..299ddfc0d --- /dev/null +++ b/security/nss/lib/pki1/atav.c @@ -0,0 +1,1803 @@ +/* + * 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 */ + +/* + * atav.c + * + * This file contains the implementation of the PKIX part-1 object + * AttributeTypeAndValue. + */ + +#ifndef NSSBASE_H +#include "nssbase.h" +#endif /* NSSBASE_H */ + +#ifndef ASN1_H +#include "asn1.h" +#endif /* ASN1_H */ + +#ifndef PKI1_H +#include "pki1.h" +#endif /* PKI1_H */ + +/* + * AttributeTypeAndValue + * + * From draft-ietf-pkix-ipki-part1-10: + * + * AttributeTypeAndValue ::= SEQUENCE { + * type ATTRIBUTE.&id ({SupportedAttributes}), + * value ATTRIBUTE.&Type ({SupportedAttributes}{@type})} + * + * -- ATTRIBUTE information object class specification + * -- Note: This has been greatly simplified for PKIX !! + * + * ATTRIBUTE ::= CLASS { + * &Type, + * &id OBJECT IDENTIFIER UNIQUE } + * WITH SYNTAX { + * WITH SYNTAX &Type ID &id } + * + * What this means is that the "type" of the value is determined by + * the value of the oid. If we hide the structure, our accessors + * can (at least in debug builds) assert value semantics beyond what + * the compiler can provide. Since these things are only used in + * RelativeDistinguishedNames, and since RDNs always contain a SET + * of these things, we don't lose anything by hiding the structure + * (and its size). + */ + +struct NSSATAVStr { + NSSBER ber; + const NSSOID *oid; + NSSUTF8 *value; + nssStringType stringForm; +}; + +/* + * NSSATAV + * + * The public "methods" regarding this "object" are: + * + * NSSATAV_CreateFromBER -- constructor + * NSSATAV_CreateFromUTF8 -- constructor + * NSSATAV_Create -- constructor + * + * NSSATAV_Destroy + * NSSATAV_GetDEREncoding + * NSSATAV_GetUTF8Encoding + * NSSATAV_GetType + * NSSATAV_GetValue + * NSSATAV_Compare + * NSSATAV_Duplicate + * + * The non-public "methods" regarding this "object" are: + * + * nssATAV_CreateFromBER -- constructor + * nssATAV_CreateFromUTF8 -- constructor + * nssATAV_Create -- constructor + * + * nssATAV_Destroy + * nssATAV_GetDEREncoding + * nssATAV_GetUTF8Encoding + * nssATAV_GetType + * nssATAV_GetValue + * nssATAV_Compare + * nssATAV_Duplicate + * + * In debug builds, the following non-public call is also available: + * + * nssATAV_verifyPointer + */ + +/* + * NSSATAV_CreateFromBER + * + * This routine creates an NSSATAV by decoding a BER- or DER-encoded + * ATAV. If the optional arena argument is non-null, the memory used + * will be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_IMPLEMENT NSSATAV * +NSSATAV_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berATAV +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSATAV *)NULL; + } + } + + /* + * NSSBERs can be created by the user, + * so no pointer-tracking can be checked. + */ + + if( (NSSBER *)NULL == berATAV ) { + nss_SetError(NSS_ERROR_INVALID_BER); + return (NSSATAV *)NULL; + } + + if( (void *)NULL == berATAV->data ) { + nss_SetError(NSS_ERROR_INVALID_BER); + return (NSSATAV *)NULL; + } +#endif /* DEBUG */ + + return nssATAV_CreateFromBER(arenaOpt, berATAV); +} + +/* + * NSSATAV_CreateFromUTF8 + * + * This routine creates an NSSATAV by decoding a UTF8 string in the + * "equals" format, e.g., "c=US." If the optional arena argument is + * non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_UNKNOWN_ATTRIBUTE + * NSS_ERROR_INVALID_STRING + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_IMPLEMENT NSSATAV * +NSSATAV_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringATAV +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSATAV *)NULL; + } + } + + /* + * NSSUTF8s can be created by the user, + * so no pointer-tracking can be checked. + */ + + if( (NSSUTF8 *)NULL == stringATAV ) { + nss_SetError(NSS_ERROR_INVALID_UTF8); + return (NSSATAV *)NULL; + } +#endif /* DEBUG */ + + return nssATAV_CreateFromUTF8(arenaOpt, stringATAV); +} + +/* + * NSSATAV_Create + * + * This routine creates an NSSATAV from the specified NSSOID and the + * specified data. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap.If the specified data length is zero, + * the data is assumed to be terminated by first zero byte; this allows + * UTF8 strings to be easily specified. This routine may return NULL + * upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_IMPLEMENT NSSATAV * +NSSATAV_Create +( + NSSArena *arenaOpt, + const NSSOID *oid, + const void *data, + PRUint32 length +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSATAV *)NULL; + } + } + + if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { + return (NSSATAV *)NULL; + } + + if( (const void *)NULL == data ) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSATAV *)NULL; + } +#endif /* DEBUG */ + + return nssATAV_Create(arenaOpt, oid, data, length); +} + +/* + * NSSATAV_Destroy + * + * This routine will destroy an ATAV object. It should eventually be + * called on all ATAVs created without an arena. While it is not + * necessary to call it on ATAVs created within an arena, it is not an + * error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * create an error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_IMPLEMENT PRStatus +NSSATAV_Destroy +( + NSSATAV *atav +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return PR_FAILURE; + } +#endif /* DEBUG */ + + return nssATAV_Destroy(atav); +} + +/* + * NSSATAV_GetDEREncoding + * + * This routine will DER-encode an ATAV object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSATAV + */ + +NSS_IMPLEMENT NSSDER * +NSSATAV_GetDEREncoding +( + NSSATAV *atav, + NSSArena *arenaOpt +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSDER *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSDER *)NULL; + } + } +#endif /* DEBUG */ + + return nssATAV_GetDEREncoding(atav, arenaOpt); +} + +/* + * NSSATAV_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the ATAV in "equals" notation (e.g., "o=Acme"). + * If the optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return null upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the "equals" encoding of the + * ATAV + */ + +NSS_IMPLEMENT NSSUTF8 * +NSSATAV_GetUTF8Encoding +( + NSSATAV *atav, + NSSArena *arenaOpt +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSUTF8 *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSUTF8 *)NULL; + } + } +#endif /* DEBUG */ + + return nssATAV_GetUTF8Encoding(atav, arenaOpt); +} + +/* + * NSSATAV_GetType + * + * This routine returns the NSSOID corresponding to the attribute type + * in the specified ATAV. This routine may return NSS_OID_UNKNOWN + * upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * NSS_OID_UNKNOWN upon error + * An element of enum NSSOIDenum upon success + */ + +NSS_IMPLEMENT const NSSOID * +NSSATAV_GetType +( + NSSATAV *atav +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSOID *)NULL; + } +#endif /* DEBUG */ + + return nssATAV_GetType(atav); +} + +/* + * NSSATAV_GetValue + * + * This routine returns a string containing the attribute value + * in the specified ATAV. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSItem containing the attribute value. + */ + +NSS_IMPLEMENT NSSUTF8 * +NSSATAV_GetValue +( + NSSATAV *atav, + NSSArena *arenaOpt +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSUTF8 *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSUTF8 *)NULL; + } + } +#endif /* DEBUG */ + + return nssATAV_GetValue(atav, arenaOpt); +} + +/* + * NSSATAV_Compare + * + * This routine compares two ATAVs for equality. For two ATAVs to be + * equal, the attribute types must be the same, and the attribute + * values must have equal length and contents. The result of the + * comparison will be stored at the location pointed to by the "equalp" + * variable, which must point to a valid PRBool. This routine may + * return PR_FAILURE upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_IMPLEMENT PRStatus +NSSATAV_Compare +( + NSSATAV *atav1, + NSSATAV *atav2, + PRBool *equalp +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav1) ) { + return PR_FAILURE; + } + + if( PR_SUCCESS != nssATAV_verifyPointer(atav2) ) { + return PR_FAILURE; + } + + if( (PRBool *)NULL == equalp ) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return PR_FAILURE; + } +#endif /* DEBUG */ + + return nssATAV_Compare(atav1, atav2, equalp); +} + +/* + * NSSATAV_Duplicate + * + * This routine duplicates the specified ATAV. If the optional arena + * argument is non-null, the memory required will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL on error + * A pointer to a new ATAV + */ + +NSS_IMPLEMENT NSSATAV * +NSSATAV_Duplicate +( + NSSATAV *atav, + NSSArena *arenaOpt +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSATAV *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSATAV *)NULL; + } + } +#endif /* DEBUG */ + + return nssATAV_Duplicate(atav, arenaOpt); +} + +/* + * The pointer-tracking code + */ + +#ifdef DEBUG +extern const NSSError NSS_ERROR_INTERNAL_ERROR; + +static nssPointerTracker atav_pointer_tracker; + +static PRStatus +atav_add_pointer +( + const NSSATAV *atav +) +{ + PRStatus rv; + + rv = nssPointerTracker_initialize(&atav_pointer_tracker); + if( PR_SUCCESS != rv ) { + return rv; + } + + rv = nssPointerTracker_add(&atav_pointer_tracker, atav); + if( PR_SUCCESS != rv ) { + NSSError e = NSS_GetError(); + if( NSS_ERROR_NO_MEMORY != e ) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + } + + return rv; + } + + return PR_SUCCESS; +} + +static PRStatus +atav_remove_pointer +( + const NSSATAV *atav +) +{ + PRStatus rv; + + rv = nssPointerTracker_remove(&atav_pointer_tracker, atav); + if( PR_SUCCESS != rv ) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + } + + return rv; +} + +/* + * nssATAV_verifyPointer + * + * This method is only present in debug builds. + * + * If the specified pointer is a valid pointer to an NSSATAV object, + * this routine will return PR_SUCCESS. Otherwise, it will put an + * error on the error stack and return PR_FAILRUE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS if the pointer is valid + * PR_FAILURE if it isn't + */ + +NSS_IMPLEMENT PRStatus +nssATAV_verifyPointer +( + NSSATAV *atav +) +{ + PRStatus rv; + + rv = nssPointerTracker_initialize(&atav_pointer_tracker); + if( PR_SUCCESS != rv ) { + return PR_FAILURE; + } + + rv = nssPointerTracker_verify(&atav_pointer_tracker, atav); + if( PR_SUCCESS != rv ) { + nss_SetError(NSS_ERROR_INVALID_ATAV); + return PR_FAILURE; + } + + return PR_SUCCESS; +} +#endif /* DEBUG */ + +typedef struct { + NSSBER oid; + NSSBER value; +} atav_holder; + +static const nssASN1Template nss_atav_template[] = { + { nssASN1_SEQUENCE, 0, NULL, sizeof(atav_holder) }, + { nssASN1_OBJECT_ID, nsslibc_offsetof(atav_holder, oid), NULL, 0 }, + { nssASN1_ANY, nsslibc_offsetof(atav_holder, value), NULL, 0 }, + { 0, 0, NULL, 0 } +}; + +/* + * There are several common attributes, with well-known type aliases + * and value semantics. This table lists the ones we recognize. + */ + +struct nss_attribute_data_str { + const NSSOID **oid; + nssStringType stringType; + PRUint32 minStringLength; + PRUint32 maxStringLength; /* zero for no limit */ +}; + +static const struct nss_attribute_data_str nss_attribute_data[] = { + { &NSS_OID_X520_NAME, + nssStringType_DirectoryString, 1, 32768 }, + { &NSS_OID_X520_COMMON_NAME, + nssStringType_DirectoryString, 1, 64 }, + { &NSS_OID_X520_SURNAME, + nssStringType_DirectoryString, 1, 40 }, + { &NSS_OID_X520_GIVEN_NAME, + nssStringType_DirectoryString, 1, 16 }, + { &NSS_OID_X520_INITIALS, + nssStringType_DirectoryString, 1, 5 }, + { &NSS_OID_X520_GENERATION_QUALIFIER, + nssStringType_DirectoryString, 1, 3 }, + { &NSS_OID_X520_DN_QUALIFIER, + nssStringType_PrintableString, 1, 0 }, + { &NSS_OID_X520_COUNTRY_NAME, + nssStringType_PrintableString, 2, 2 }, + { &NSS_OID_X520_LOCALITY_NAME, + nssStringType_DirectoryString, 1, 128 }, + { &NSS_OID_X520_STATE_OR_PROVINCE_NAME, + nssStringType_DirectoryString, 1, 128 }, + { &NSS_OID_X520_ORGANIZATION_NAME, + nssStringType_DirectoryString, 1, 64 }, + { &NSS_OID_X520_ORGANIZATIONAL_UNIT_NAME, + nssStringType_DirectoryString, 1, + /* + * Note, draft #11 defines both "32" and "64" for this maximum, + * in two separate places. Until it's settled, "conservative + * in what you send." We're always liberal in what we accept. + */ + 32 }, + { &NSS_OID_X520_TITLE, + nssStringType_DirectoryString, 1, 64 }, + { &NSS_OID_RFC1274_EMAIL, + nssStringType_PHGString, 1, 128 } +}; + +PRUint32 nss_attribute_data_quantity = + (sizeof(nss_attribute_data)/sizeof(nss_attribute_data[0])); + +static nssStringType +nss_attr_underlying_string_form +( + nssStringType type, + void *data +) +{ + if( nssStringType_DirectoryString == type ) { + PRUint8 tag = *(PRUint8 *)data; + switch( tag & nssASN1_TAGNUM_MASK ) { + case 20: + /* + * XXX fgmr-- we have to accept Latin-1 for Teletex; (see + * below) but is T61 a suitable value for "Latin-1"? + */ + return nssStringType_TeletexString; + case 19: + return nssStringType_PrintableString; + case 28: + return nssStringType_UniversalString; + case 30: + return nssStringType_BMPString; + case 12: + return nssStringType_UTF8String; + default: + return nssStringType_Unknown; + } + } + + return type; +} + + +/* + * This routine decodes the attribute value, in a type-specific way. + * + */ + +static NSSUTF8 * +nss_attr_to_utf8 +( + NSSArena *arenaOpt, + const NSSOID *oid, + NSSItem *item, + nssStringType *stringForm +) +{ + NSSUTF8 *rv = (NSSUTF8 *)NULL; + PRUint32 i; + const struct nss_attribute_data_str *which = + (struct nss_attribute_data_str *)NULL; + PRUint32 len = 0; + + for( i = 0; i < nss_attribute_data_quantity; i++ ) { + if( *(nss_attribute_data[ i ].oid) == oid ) { + which = &nss_attribute_data[i]; + break; + } + } + + if( (struct nss_attribute_data_str *)NULL == which ) { + /* Unknown OID. Encode it as hex. */ + PRUint8 *c; + PRUint8 *d = (PRUint8 *)item->data; + PRUint32 amt = item->size; + + if( item->size >= 0x7FFFFFFF ) { + nss_SetError(NSS_ERROR_INVALID_STRING); + return (NSSUTF8 *)NULL; + } + + len = 1 + (item->size * 2) + 1; /* '#' + hex + '\0' */ + rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len); + if( (NSSUTF8 *)NULL == rv ) { + return (NSSUTF8 *)NULL; + } + + c = (PRUint8 *)rv; + *c++ = '#'; /* XXX fgmr check this */ + while( amt > 0 ) { + static char hex[16] = "0123456789ABCDEF"; + *c++ = hex[ ((*d) & 0xf0) >> 4 ]; + *c++ = hex[ ((*d) & 0x0f) ]; + } + + /* *c = '\0'; nss_ZAlloc, remember */ + + *stringForm = nssStringType_Unknown; /* force exact comparison */ + } else { + rv = nssUTF8_CreateFromBER(arenaOpt, which->stringType, + (NSSBER *)item); + + if( (NSSUTF8 *)NULL == rv ) { + return (NSSUTF8 *)NULL; + } + + if( PR_SUCCESS != nssUTF8_Length(rv, &len) ) { + nss_ZFreeIf(rv); + return (NSSUTF8 *)NULL; + } + + *stringForm = nss_attr_underlying_string_form(which->stringType, + item->data); + } + + return rv; +} + +/* + * nssATAV_CreateFromBER + * + * This routine creates an NSSATAV by decoding a BER- or DER-encoded + * ATAV. If the optional arena argument is non-null, the memory used + * will be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_IMPLEMENT NSSATAV * +nssATAV_CreateFromBER +( + NSSArena *arenaOpt, + const NSSBER *berATAV +) +{ + atav_holder holder; + PRStatus status; + NSSATAV *rv; + +#ifdef NSSDEBUG + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSATAV *)NULL; + } + } + + /* + * NSSBERs can be created by the user, + * so no pointer-tracking can be checked. + */ + + if( (NSSBER *)NULL == berATAV ) { + nss_SetError(NSS_ERROR_INVALID_BER); + return (NSSATAV *)NULL; + } + + if( (void *)NULL == berATAV->data ) { + nss_SetError(NSS_ERROR_INVALID_BER); + return (NSSATAV *)NULL; + } +#endif /* NSSDEBUG */ + + status = nssASN1_DecodeBER(arenaOpt, &holder, + nss_atav_template, berATAV); + if( PR_SUCCESS != status ) { + return (NSSATAV *)NULL; + } + + rv = nss_ZNEW(arenaOpt, NSSATAV); + if( (NSSATAV *)NULL == rv ) { + nss_ZFreeIf(holder.oid.data); + nss_ZFreeIf(holder.value.data); + return (NSSATAV *)NULL; + } + + rv->oid = nssOID_CreateFromBER(&holder.oid); + if( (NSSOID *)NULL == rv->oid ) { + nss_ZFreeIf(rv); + nss_ZFreeIf(holder.oid.data); + nss_ZFreeIf(holder.value.data); + return (NSSATAV *)NULL; + } + + nss_ZFreeIf(holder.oid.data); + + rv->ber.data = nss_ZAlloc(arenaOpt, berATAV->size); + if( (void *)NULL == rv->ber.data ) { + nss_ZFreeIf(rv); + nss_ZFreeIf(holder.value.data); + return (NSSATAV *)NULL; + } + + rv->ber.size = berATAV->size; + (void)nsslibc_memcpy(rv->ber.data, berATAV->data, berATAV->size); + + rv->value = nss_attr_to_utf8(arenaOpt, rv->oid, &holder.value, + &rv->stringForm); + if( (NSSUTF8 *)NULL == rv->value ) { + nss_ZFreeIf(rv->ber.data); + nss_ZFreeIf(rv); + nss_ZFreeIf(holder.value.data); + return (NSSATAV *)NULL; + } + + nss_ZFreeIf(holder.value.data); + +#ifdef DEBUG + if( PR_SUCCESS != atav_add_pointer(rv) ) { + nss_ZFreeIf(rv->ber.data); + nss_ZFreeIf(rv->value); + nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } +#endif /* DEBUG */ + + return rv; +} + +static PRBool +nss_atav_utf8_string_is_hex +( + const NSSUTF8 *s +) +{ + /* All hex digits are ASCII, so this works */ + PRUint8 *p = (PRUint8 *)s; + + for( ; (PRUint8)0 != *p; p++ ) { + if( (('0' <= *p) && (*p <= '9')) || + (('A' <= *p) && (*p <= 'F')) || + (('a' <= *p) && (*p <= 'f')) ) { + continue; + } else { + return PR_FALSE; + } + } + + return PR_TRUE; +} + +static PRUint8 +nss_atav_fromhex +( + PRUint8 *d +) +{ + PRUint8 rv; + + if( d[0] <= '9' ) { + rv = (d[0] - '0') * 16; + } else if( d[0] >= 'a' ) { + rv = (d[0] - 'a' + 10) * 16; + } else { + rv = (d[0] - 'A' + 10); + } + + if( d[1] <= '9' ) { + rv += (d[1] - '0'); + } else if( d[1] >= 'a' ) { + rv += (d[1] - 'a' + 10); + } else { + rv += (d[1] - 'A' + 10); + } + + return rv; +} + +/* + * nssATAV_CreateFromUTF8 + * + * This routine creates an NSSATAV by decoding a UTF8 string in the + * "equals" format, e.g., "c=US." If the optional arena argument is + * non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_UNKNOWN_ATTRIBUTE + * NSS_ERROR_INVALID_STRING + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +extern const NSSError NSS_ERROR_INTERNAL_ERROR; + +NSS_IMPLEMENT NSSATAV * +nssATAV_CreateFromUTF8 +( + NSSArena *arenaOpt, + const NSSUTF8 *stringATAV +) +{ + char *c; + NSSUTF8 *type; + NSSUTF8 *value; + PRUint32 i; + const NSSOID *oid = (NSSOID *)NULL; + NSSATAV *rv; + NSSItem xitem; + + xitem.data = (void *)NULL; + + for( c = (char *)stringATAV; '\0' != *c; c++ ) { + if( '=' == *c ) { +#ifdef PEDANTIC + /* + * Theoretically, one could have an '=' in an + * attribute string alias. We don't, yet, though. + */ + if( (char *)stringATAV == c ) { + nss_SetError(NSS_ERROR_INVALID_STRING); + return (NSSATAV *)NULL; + } else { + if( '\\' == c[-1] ) { + continue; + } + } +#endif /* PEDANTIC */ + break; + } + } + + if( '\0' == *c ) { + nss_SetError(NSS_ERROR_INVALID_UTF8); + return (NSSATAV *)NULL; + } else { + c++; + value = (NSSUTF8 *)c; + } + + i = ((NSSUTF8 *)c - stringATAV); + type = (NSSUTF8 *)nss_ZAlloc((NSSArena *)NULL, i); + if( (NSSUTF8 *)NULL == type ) { + return (NSSATAV *)NULL; + } + + (void)nsslibc_memcpy(type, stringATAV, i-1); + + c = (char *)stringATAV; + if( (('0' <= *c) && (*c <= '9')) || ('#' == *c) ) { + oid = nssOID_CreateFromUTF8(type); + if( (NSSOID *)NULL == oid ) { + nss_ZFreeIf(type); + return (NSSATAV *)NULL; + } + } else { + for( i = 0; i < nss_attribute_type_alias_count; i++ ) { + const nssAttributeTypeAliasTable *e = &nss_attribute_type_aliases[i]; + PRBool match = PR_FALSE; + if( PR_SUCCESS != nssUTF8_CaseIgnoreMatch(type, e->alias, + &match) ) { + nss_ZFreeIf(type); + return (NSSATAV *)NULL; + } + if( PR_TRUE == match ) { + oid = *(e->oid); + break; + } + } + + if( (NSSOID *)NULL == oid ) { + nss_ZFreeIf(type); + nss_SetError(NSS_ERROR_UNKNOWN_ATTRIBUTE); + return (NSSATAV *)NULL; + } + } + + nss_ZFreeIf(type); + type = (NSSUTF8 *)NULL; + + rv = nss_ZNEW(arenaOpt, NSSATAV); + if( (NSSATAV *)NULL == rv ) { + return (NSSATAV *)NULL; + } + + rv->oid = oid; + + if( '#' == *value ) { /* XXX fgmr.. was it '#'? or backslash? */ + PRUint32 size; + PRUint32 len; + PRUint8 *c; + PRUint8 *d; + /* It's in hex */ + + value++; + if( PR_TRUE != nss_atav_utf8_string_is_hex(value) ) { + (void)nss_ZFreeIf(rv); + nss_SetError(NSS_ERROR_INVALID_STRING); + return (NSSATAV *)NULL; + } + + if( PR_SUCCESS != nssUTF8_Size(value, &size) ) { + /* + * Only returns an error on bad pointer (nope) or string + * too long. The defined limits for known attributes are + * small enough to fit in PRUint32, and when undefined we + * get to apply our own practical limits. Ergo, I say the + * string is invalid. + */ + (void)nss_ZFreeIf(rv); + nss_SetError(NSS_ERROR_INVALID_STRING); + return (NSSATAV *)NULL; + } + + if( ((size-1) & 1) ) { + /* odd length */ + (void)nss_ZFreeIf(rv); + nss_SetError(NSS_ERROR_INVALID_STRING); + return (NSSATAV *)NULL; + } + + len = (size-1)/2; + + rv->value = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len+1); + if( (NSSUTF8 *)NULL == rv->value ) { + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + xitem.size = len; + xitem.data = (void *)rv->value; + + for( c = rv->value, d = value; len--; c++, d += 2 ) { + *c = nss_atav_fromhex(d); + } + + *c = 0; + } else { + PRUint32 i, len; + PRUint8 *s; + + /* + * XXX fgmr-- okay, this is a little wasteful, and should + * probably be abstracted out a bit. Later. + */ + + rv->value = nssUTF8_Duplicate(value, arenaOpt); + if( (NSSUTF8 *)NULL == rv->value ) { + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + if( PR_SUCCESS != nssUTF8_Size(rv->value, &len) ) { + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + s = (PRUint8 *)rv->value; + for( i = 0; i < len; i++ ) { + if( '\\' == s[i] ) { + (void)nsslibc_memcpy(&s[i], &s[i+1], len-i-1); + } + } + } + + /* Now just BER-encode the baby and we're through.. */ + { + const struct nss_attribute_data_str *which = + (struct nss_attribute_data_str *)NULL; + PRUint32 i; + NSSArena *a; + NSSDER *oidder; + NSSItem *vitem; + atav_holder ah; + NSSDER *status; + + for( i = 0; i < nss_attribute_data_quantity; i++ ) { + if( *(nss_attribute_data[ i ].oid) == rv->oid ) { + which = &nss_attribute_data[i]; + break; + } + } + + a = NSSArena_Create(); + if( (NSSArena *)NULL == a ) { + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + oidder = nssOID_GetDEREncoding(rv->oid, a); + if( (NSSDER *)NULL == oidder ) { + (void)NSSArena_Destroy(a); + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + if( (struct nss_attribute_data_str *)NULL == which ) { + /* + * We'll just have to take the user data as an octet stream. + */ + if( (void *)NULL == xitem.data ) { + /* + * This means that an ATTR entry has been added to oids.txt, + * but no corresponding entry has been added to the array + * ns_attribute_data[] above. + */ + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + (void)NSSArena_Destroy(a); + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + vitem = nssASN1_EncodeDER(a, (NSSDER *)NULL, &xitem, + nssASN1Template_OctetString); + if( (NSSItem *)NULL == vitem ) { + (void)NSSArena_Destroy(a); + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + rv->stringForm = nssStringType_Unknown; + } else { + PRUint32 length = 0; + + if( PR_SUCCESS != nssUTF8_Length(rv->value, &length) ) { + (void)NSSArena_Destroy(a); + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + if( ((0 != which->minStringLength) && + (length < which->minStringLength)) || + ((0 != which->maxStringLength) && + (length > which->maxStringLength)) ) { + nss_SetError(NSS_ERROR_INVALID_STRING); + (void)NSSArena_Destroy(a); + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + vitem = nssUTF8_GetDEREncoding(a, which->stringType, rv->value); + if( (NSSItem *)NULL == vitem ) { + (void)NSSArena_Destroy(a); + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + if( nssStringType_DirectoryString == which->stringType ) { + rv->stringForm = nssStringType_UTF8String; + } else { + rv->stringForm = which->stringType; + } + } + + ah.oid = *oidder; + ah.value = *vitem; + + status = nssASN1_EncodeDER(arenaOpt, &rv->ber, &ah, + nss_atav_template); + + if( (NSSDER *)NULL == status ) { + (void)NSSArena_Destroy(a); + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + (void)NSSArena_Destroy(a); + } + + return rv; +} + +/* + * nssATAV_Create + * + * This routine creates an NSSATAV from the specified NSSOID and the + * specified data. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap.If the specified data length is zero, + * the data is assumed to be terminated by first zero byte; this allows + * UTF8 strings to be easily specified. This routine may return NULL + * upon error, in which case it will have set an error on the error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_IMPLEMENT NSSATAV * +nssATAV_Create +( + NSSArena *arenaOpt, + const NSSOID *oid, + const void *data, + PRUint32 length +) +{ +#ifdef NSSDEBUG + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSATAV *)NULL; + } + } + + if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { + return (NSSATAV *)NULL; + } + + if( (const void *)NULL == data ) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSATAV *)NULL; + } +#endif /* NSSDEBUG */ + + /* XXX fgmr-- oops, forgot this one */ + return (NSSATAV *)NULL; +} + +/* + * nssATAV_Destroy + * + * This routine will destroy an ATAV object. It should eventually be + * called on all ATAVs created without an arena. While it is not + * necessary to call it on ATAVs created within an arena, it is not an + * error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * set an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_IMPLEMENT PRStatus +nssATAV_Destroy +( + NSSATAV *atav +) +{ +#ifdef NSSDEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + (void)nss_ZFreeIf(atav->ber.data); + (void)nss_ZFreeIf(atav->value); + +#ifdef DEBUG + if( PR_SUCCESS != atav_remove_pointer(atav) ) { + return PR_FAILURE; + } +#endif /* DEBUG */ + + return PR_SUCCESS; +} + +/* + * nssATAV_GetDEREncoding + * + * This routine will DER-encode an ATAV object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSATAV + */ + +NSS_IMPLEMENT NSSDER * +nssATAV_GetDEREncoding +( + NSSATAV *atav, + NSSArena *arenaOpt +) +{ + NSSDER *rv; + +#ifdef NSSDEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSDER *)NULL; + } +#endif /* NSSDEBUG */ + + rv = nss_ZNEW(arenaOpt, NSSDER); + if( (NSSDER *)NULL == rv ) { + return (NSSDER *)NULL; + } + + rv->data = nss_ZAlloc(arenaOpt, atav->ber.size); + if( (void *)NULL == rv->data ) { + (void)nss_ZFreeIf(rv); + return (NSSDER *)NULL; + } + + rv->size = atav->ber.size; + if( PR_SUCCESS != nsslibc_memcpy(rv->data, atav->ber.data, + rv->size) ) { + (void)nss_ZFreeIf(rv->data); + (void)nss_ZFreeIf(rv); + return (NSSDER *)NULL; + } + + return rv; +} + +/* + * nssATAV_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the ATAV in "equals" notation (e.g., "o=Acme"). + * If the optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return null upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the "equals" encoding of the + * ATAV + */ + +NSS_IMPLEMENT NSSUTF8 * +nssATAV_GetUTF8Encoding +( + NSSATAV *atav, + NSSArena *arenaOpt +) +{ + NSSUTF8 *rv; + PRUint32 i; + const NSSUTF8 *alias = (NSSUTF8 *)NULL; + NSSUTF8 *oid; + NSSUTF8 *value; + PRUint32 oidlen; + PRUint32 valuelen; + PRUint32 totallen; + +#ifdef NSSDEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSUTF8 *)NULL; + } +#endif /* NSSDEBUG */ + + for( i = 0; i < nss_attribute_type_alias_count; i++ ) { + if( *(nss_attribute_type_aliases[i].oid) == atav->oid ) { + alias = nss_attribute_type_aliases[i].alias; + break; + } + } + + if( (NSSUTF8 *)NULL == alias ) { + oid = nssOID_GetUTF8Encoding(atav->oid, (NSSArena *)NULL); + if( (NSSUTF8 *)NULL == oid ) { + return (NSSUTF8 *)NULL; + } + + if( PR_SUCCESS != nssUTF8_Size(oid, &oidlen) ) { + (void)nss_ZFreeIf(oid); + return (NSSUTF8 *)NULL; + } + } else { + if( PR_SUCCESS != nssUTF8_Size(alias, &oidlen) ) { + return (NSSUTF8 *)NULL; + } + oid = (NSSUTF8 *)NULL; + } + + value = nssATAV_GetValue(atav, (NSSArena *)NULL); + if( (NSSUTF8 *)NULL == value ) { + (void)nss_ZFreeIf(oid); + return (NSSUTF8 *)NULL; + } + + if( PR_SUCCESS != nssUTF8_Size(value, &valuelen) ) { + (void)nss_ZFreeIf(value); + (void)nss_ZFreeIf(oid); + return (NSSUTF8 *)NULL; + } + + totallen = oidlen + valuelen - 1 + 1; + rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, totallen); + if( (NSSUTF8 *)NULL == rv ) { + (void)nss_ZFreeIf(value); + (void)nss_ZFreeIf(oid); + return (NSSUTF8 *)NULL; + } + + if( (NSSUTF8 *)NULL == alias ) { + if( (void *)NULL == nsslibc_memcpy(rv, oid, oidlen-1) ) { + (void)nss_ZFreeIf(rv); + (void)nss_ZFreeIf(value); + (void)nss_ZFreeIf(oid); + return (NSSUTF8 *)NULL; + } + } else { + if( (void *)NULL == nsslibc_memcpy(rv, alias, oidlen-1) ) { + (void)nss_ZFreeIf(rv); + (void)nss_ZFreeIf(value); + return (NSSUTF8 *)NULL; + } + } + + rv[ oidlen-1 ] = '='; + + if( (void *)NULL == nsslibc_memcpy(&rv[oidlen], value, valuelen) ) { + (void)nss_ZFreeIf(rv); + (void)nss_ZFreeIf(value); + (void)nss_ZFreeIf(oid); + return (NSSUTF8 *)NULL; + } + + return rv; +} + +/* + * nssATAV_GetType + * + * This routine returns the NSSOID corresponding to the attribute type + * in the specified ATAV. This routine may return NSS_OID_UNKNOWN + * upon error, in which case it will have set an error on the error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * NSS_OID_UNKNOWN upon error + * A valid NSSOID pointer upon success + */ + +NSS_IMPLEMENT const NSSOID * +nssATAV_GetType +( + NSSATAV *atav +) +{ +#ifdef NSSDEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSOID *)NULL; + } +#endif /* NSSDEBUG */ + + return atav->oid; +} + +/* + * nssATAV_GetValue + * + * This routine returns a NSSUTF8 string containing the attribute value + * in the specified ATAV. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have set an error upon the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSItem containing the attribute value. + */ + +NSS_IMPLEMENT NSSUTF8 * +nssATAV_GetValue +( + NSSATAV *atav, + NSSArena *arenaOpt +) +{ +#ifdef NSSDEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSUTF8 *)NULL; + } +#endif /* NSSDEBUG */ + + return nssUTF8_Duplicate(atav->value, arenaOpt); +} + +/* + * nssATAV_Compare + * + * This routine compares two ATAVs for equality. For two ATAVs to be + * equal, the attribute types must be the same, and the attribute + * values must have equal length and contents. The result of the + * comparison will be stored at the location pointed to by the "equalp" + * variable, which must point to a valid PRBool. This routine may + * return PR_FAILURE upon error, in which case it will have set an + * error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_IMPLEMENT PRStatus +nssATAV_Compare +( + NSSATAV *atav1, + NSSATAV *atav2, + PRBool *equalp +) +{ + nssStringType comparison; + PRUint32 len1; + PRUint32 len2; + +#ifdef DEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav1) ) { + return PR_FAILURE; + } + + if( PR_SUCCESS != nssATAV_verifyPointer(atav2) ) { + return PR_FAILURE; + } + + if( (PRBool *)NULL == equalp ) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return PR_FAILURE; + } +#endif /* DEBUG */ + + if( atav1->oid != atav2->oid ) { + *equalp = PR_FALSE; + return PR_SUCCESS; + } + + if( atav1->stringForm != atav2->stringForm ) { + if( (nssStringType_PrintableString == atav1->stringForm) || + (nssStringType_PrintableString == atav2->stringForm) ) { + comparison = nssStringType_PrintableString; + } else if( (nssStringType_PHGString == atav1->stringForm) || + (nssStringType_PHGString == atav2->stringForm) ) { + comparison = nssStringType_PHGString; + } else { + comparison = atav1->stringForm; + } + } else { + comparison = atav1->stringForm; + } + + switch( comparison ) { + case nssStringType_DirectoryString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + return PR_FAILURE; + case nssStringType_TeletexString: + break; + case nssStringType_PrintableString: + return nssUTF8_PrintableMatch(atav1->value, atav2->value, + equalp); + /* Case-insensitive, with whitespace reduction */ + break; + case nssStringType_UniversalString: + break; + case nssStringType_BMPString: + break; + case nssStringType_UTF8String: + break; + case nssStringType_PHGString: + /* Case-insensitive (XXX fgmr, actually see draft-11 pg. 21) */ + return nssUTF8_CaseIgnoreMatch(atav1->value, atav2->value, + equalp); + case nssStringType_Unknown: + break; + } + + if( PR_SUCCESS != nssUTF8_Size(atav1->value, &len1) ) { + return PR_FAILURE; + } + + if( PR_SUCCESS != nssUTF8_Size(atav2->value, &len2) ) { + return PR_FAILURE; + } + + if( len1 != len2 ) { + *equalp = PR_FALSE; + return PR_SUCCESS; + } + + return nsslibc_compare(atav1->value, atav2->value, len1, equalp); +} + + +/* + * nssATAV_Duplicate + * + * This routine duplicates the specified ATAV. If the optional arena + * argument is non-null, the memory required will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * placed an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL on error + * A pointer to a new ATAV + */ + +NSS_IMPLEMENT NSSATAV * +nssATAV_Duplicate +( + NSSATAV *atav, + NSSArena *arenaOpt +) +{ + NSSATAV *rv; + +#ifdef NSSDEBUG + if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { + return (NSSATAV *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSATAV *)NULL; + } + } +#endif /* NSSDEBUG */ + + rv = nss_ZNEW(arenaOpt, NSSATAV); + if( (NSSATAV *)NULL == rv ) { + return (NSSATAV *)NULL; + } + + rv->oid = atav->oid; + rv->stringForm = atav->stringForm; + rv->value = nssUTF8_Duplicate(atav->value, arenaOpt); + if( (NSSUTF8 *)NULL == rv->value ) { + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + rv->ber.data = nss_ZAlloc(arenaOpt, atav->ber.size); + if( (void *)NULL == rv->ber.data ) { + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + rv->ber.size = atav->ber.size; + if( PR_SUCCESS != nsslibc_memcpy(rv->ber.data, atav->ber.data, + atav->ber.size) ) { + (void)nss_ZFreeIf(rv->ber.data); + (void)nss_ZFreeIf(rv->value); + (void)nss_ZFreeIf(rv); + return (NSSATAV *)NULL; + } + + return rv; +} diff --git a/security/nss/lib/pki1/config.mk b/security/nss/lib/pki1/config.mk new file mode 100644 index 000000000..80b3135f4 --- /dev/null +++ b/security/nss/lib/pki1/config.mk @@ -0,0 +1,37 @@ +# +# 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. +# +CONFIG_CVS_ID = "@(#) $RCSfile$ $Revision$ $Date$ $Name$" + +ifdef BUILD_IDG +DEFINES += -DNSSDEBUG +endif diff --git a/security/nss/lib/pki1/genname.c b/security/nss/lib/pki1/genname.c new file mode 100644 index 000000000..e7b12806a --- /dev/null +++ b/security/nss/lib/pki1/genname.c @@ -0,0 +1,94 @@ +/* + * 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 */ + +/* + * genname.c + * + * This file contains the implementation of the PKIX part-1 object + * GeneralName. + */ + +#ifndef NSSBASE_H +#include "nssbase.h" +#endif /* NSSBASE_H */ + +#ifndef ASN1_H +#include "asn1.h" +#endif /* ASN1_H */ + +#ifndef PKI1_H +#include "pki1.h" +#endif /* PKI1_H */ + +/* + * GeneralName + * + * From draft-ietf-pkix-ipki-part1-10: + * + * GeneralName ::= CHOICE { + * otherName [0] INSTANCE OF OTHER-NAME, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + * + * OTHER-NAME ::= TYPE-IDENTIFIER + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString {ub-name} OPTIONAL, + * partyName [1] DirectoryString {ub-name} } + * + */ + +struct nssGeneralNameStr { + NSSGeneralNameChoice choice; + union { + /* OTHER-NAME otherName */ + NSSUTF8 *rfc822Name; + NSSUTF8 *dNSName; + /* ORAddress x400Address */ + NSSName *directoryName; + /* EDIPartyName ediPartyName */ + NSSUTF8 *uniformResourceIdentifier; + NSSItem *iPAddress; + NSSOID *registeredID; + } u; +}; diff --git a/security/nss/lib/pki1/gnseq.c b/security/nss/lib/pki1/gnseq.c new file mode 100644 index 000000000..e56ec93b9 --- /dev/null +++ b/security/nss/lib/pki1/gnseq.c @@ -0,0 +1,71 @@ +/* + * 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 */ + +/* + * gnseq.c + * + * This file contains the implementation of the PKIX part-1 object + * GeneralNames (note the ending 's'). + */ + +#ifndef NSSBASE_H +#include "nssbase.h" +#endif /* NSSBASE_H */ + +#ifndef ASN1_H +#include "asn1.h" +#endif /* ASN1_H */ + +#ifndef PKI1_H +#include "pki1.h" +#endif /* PKI1_H */ + +/* + * GeneralNames (or, as we call it, GeneralNameSeq) + * + * From draft-ietf-pkix-ipki-part1-10: + * + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * A GeneralNames is simply an (ordered) sequence of General Names. + * The seqSize variable is "helper" kept for simplicity. + */ + +struct nssGeneralNameSeqStr { + PRUint32 seqSize; + NSSGeneralName **generalNames; +}; diff --git a/security/nss/lib/pki1/manifest.mn b/security/nss/lib/pki1/manifest.mn new file mode 100644 index 000000000..8e032d974 --- /dev/null +++ b/security/nss/lib/pki1/manifest.mn @@ -0,0 +1,63 @@ +# +# 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. +# +MANIFEST_CVS_ID = "@(#) $RCSfile$ $Revision$ $Date$ $Name$" + +CORE_DEPTH = ../../.. + +PRIVATE_EXPORTS = \ + pki1.h \ + pki1t.h \ + $(NULL) + +EXPORTS = \ + oiddata.h \ + nsspki1.h \ + nsspki1t.h \ + $(NULL) + +MODULE = security + +CSRCS = \ + atav.c \ + genname.c \ + gnseq.c \ + name.c \ + oid.c \ + oiddata.c \ + rdn.c \ + rdnseq.c \ + $(NULL) + +REQUIRES = security nspr + +LIBRARY_NAME = pki1 diff --git a/security/nss/lib/pki1/name.c b/security/nss/lib/pki1/name.c new file mode 100644 index 000000000..558e1142f --- /dev/null +++ b/security/nss/lib/pki1/name.c @@ -0,0 +1,77 @@ +/* + * 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 */ + +/* + * name.c + * + * This file contains the implementation of the PKIX part-1 object + * Name. + */ + +#ifndef NSSBASE_H +#include "nssbase.h" +#endif /* NSSBASE_H */ + +#ifndef ASN1_H +#include "asn1.h" +#endif /* ASN1_H */ + +#ifndef PKI1_H +#include "pki1.h" +#endif /* PKI1_H */ + +/* + * Name + * + * From draft-ietf-pkix-ipki-part1-10: + * + * -- naming data types -- + * + * Name ::= CHOICE { -- only one possibility for now -- + * rdnSequence RDNSequence } + * + * A name is basically a union of the possible names. At the moment, + * there is only one type of name: an RDNSequence. + */ + +struct nssNameStr { + PRUint32 tagPlaceHolder; + union { + NSSRDNSeq *rdnSequence; + } n; +}; + diff --git a/security/nss/lib/pki1/nsspki1.h b/security/nss/lib/pki1/nsspki1.h new file mode 100644 index 000000000..3498ab520 --- /dev/null +++ b/security/nss/lib/pki1/nsspki1.h @@ -0,0 +1,2869 @@ +/* + * 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 NSSPKI1_H +#define NSSPKI1_H + +#ifdef DEBUG +static const char NSSPKI1_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +/* + * nsspki1.h + * + * This file contains the prototypes of the public NSS routines + * dealing with the PKIX part-1 definitions. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +#ifndef NSSPKI1T_H +#include "nsspki1t.h" +#endif /* NSSPKI1T_H */ + +#ifndef OIDDATA_H +#include "oiddata.h" +#endif /* OIDDATA_H */ + +PR_BEGIN_EXTERN_C + +/* + * NSSOID + * + * The public "methods" regarding this "object" are: + * + * NSSOID_CreateFromBER -- constructor + * NSSOID_CreateFromUTF8 -- constructor + * (there is no explicit destructor) + * + * NSSOID_GetDEREncoding + * NSSOID_GetUTF8Encoding + */ + +extern const NSSOID *NSS_OID_UNKNOWN; + +/* + * NSSOID_CreateFromBER + * + * This routine creates an NSSOID by decoding a BER- or DER-encoded + * OID. It may return NSS_OID_UNKNOWN upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NSS_OID_UNKNOWN upon error + * An NSSOID upon success + */ + +NSS_EXTERN NSSOID * +NSSOID_CreateFromBER +( + NSSBER *berOid +); + +extern const NSSError NSS_ERROR_INVALID_BER; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSOID_CreateFromUTF8 + * + * This routine creates an NSSOID by decoding a UTF8 string + * representation of an OID in dotted-number format. The string may + * optionally begin with an octothorpe. It may return NSS_OID_UNKNOWN + * upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_STRING + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NSS_OID_UNKNOWN upon error + * An NSSOID upon success + */ + +NSS_EXTERN NSSOID * +NSSOID_CreateFromUTF8 +( + NSSUTF8 *stringOid +); + +extern const NSSError NSS_ERROR_INVALID_STRING; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSOID_GetDEREncoding + * + * This routine returns the DER encoding of the specified NSSOID. + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return return null upon error, in + * which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSOID + */ + +NSS_EXTERN NSSDER * +NSSOID_GetDEREncoding +( + const NSSOID *oid, + NSSDER *rvOpt, + NSSArena *arenaOpt +); + +extern const NSSError NSS_ERROR_INVALID_NSSOID; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSOID_GetUTF8Encoding + * + * This routine returns a UTF8 string containing the dotted-number + * encoding of the specified NSSOID. If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return null upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the dotted-digit encoding of + * this NSSOID + */ + +NSS_EXTERN NSSUTF8 * +NSSOID_GetUTF8Encoding +( + const NSSOID *oid, + NSSArena *arenaOpt +); + +extern const NSSError NSS_ERROR_INVALID_NSSOID; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSATAV + * + * The public "methods" regarding this "object" are: + * + * NSSATAV_CreateFromBER -- constructor + * NSSATAV_CreateFromUTF8 -- constructor + * NSSATAV_Create -- constructor + * + * NSSATAV_Destroy + * NSSATAV_GetDEREncoding + * NSSATAV_GetUTF8Encoding + * NSSATAV_GetType + * NSSATAV_GetValue + * NSSATAV_Compare + * NSSATAV_Duplicate + */ + +/* + * NSSATAV_CreateFromBER + * + * This routine creates an NSSATAV by decoding a BER- or DER-encoded + * ATAV. If the optional arena argument is non-null, the memory used + * will be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_EXTERN NSSATAV * +NSSATAV_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *derATAV +); + +extern const NSSError NSS_ERROR_INVALID_BER; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSATAV_CreateFromUTF8 + * + * This routine creates an NSSATAV by decoding a UTF8 string in the + * "equals" format, e.g., "c=US." If the optional arena argument is + * non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_UNKNOWN_ATTRIBUTE + * NSS_ERROR_INVALID_STRING + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_EXTERN NSSATAV * +NSSATAV_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringATAV +); + +extern const NSSError NSS_ERROR_UNKNOWN_ATTRIBUTE; +extern const NSSError NSS_ERROR_INVALID_STRING; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSATAV_Create + * + * This routine creates an NSSATAV from the specified NSSOID and the + * specified data. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap.If the specified data length is zero, + * the data is assumed to be terminated by first zero byte; this allows + * UTF8 strings to be easily specified. This routine may return NULL + * upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_EXTERN NSSATAV * +NSSATAV_Create +( + NSSArena *arenaOpt, + const NSSOID *oid, + const void *data, + PRUint32 length +); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_INVALID_NSSOID; +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSATAV_Destroy + * + * This routine will destroy an ATAV object. It should eventually be + * called on all ATAVs created without an arena. While it is not + * necessary to call it on ATAVs created within an arena, it is not an + * error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * create an error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +NSSATAV_Destroy +( + NSSATAV *atav +); + +extern const NSSError NSS_ERROR_INVALID_ATAV; + +/* + * NSSATAV_GetDEREncoding + * + * This routine will DER-encode an ATAV object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSATAV + */ + +NSS_EXTERN NSSDER * +NSSATAV_GetDEREncoding +( + NSSATAV *atav, + NSSArena *arenaOpt +); + +extern const NSSError NSS_ERROR_INVALID_ATAV; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSATAV_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the ATAV in "equals" notation (e.g., "o=Acme"). + * If the optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return null upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the "equals" encoding of the + * ATAV + */ + +NSS_EXTERN NSSUTF8 * +NSSATAV_GetUTF8Encoding +( + NSSATAV *atav, + NSSArena *arenaOpt +); + +extern const NSSError NSS_ERROR_INVALID_ATAV; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSATAV_GetType + * + * This routine returns the NSSOID corresponding to the attribute type + * in the specified ATAV. This routine may return NSS_OID_UNKNOWN + * upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * NSS_OID_UNKNOWN upon error + * An element of enum NSSOIDenum upon success + */ + +NSS_EXTERN const NSSOID * +NSSATAV_GetType +( + NSSATAV *atav +); + +extern const NSSError NSS_ERROR_INVALID_ATAV; + +/* + * NSSATAV_GetValue + * + * This routine returns an NSSItem containing the attribute value + * in the specified ATAV. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSItem containing the attribute value. + */ + +NSS_EXTERN NSSUTF8 * +NSSATAV_GetValue +( + NSSATAV *atav, + NSSArena *arenaOpt +); + +extern const NSSError NSS_ERROR_INVALID_ATAV; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSATAV_Compare + * + * This routine compares two ATAVs for equality. For two ATAVs to be + * equal, the attribute types must be the same, and the attribute + * values must have equal length and contents. The result of the + * comparison will be stored at the location pointed to by the "equalp" + * variable, which must point to a valid PRBool. This routine may + * return PR_FAILURE upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +NSSATAV_Compare +( + NSSATAV *atav1, + NSSATAV *atav2, + PRBool *equalp +); + +extern const NSSError NSS_ERROR_INVALID_ATAV; +extern const NSSError NSS_ERROR_INVALID_ARGUMENT; + +/* + * NSSATAV_Duplicate + * + * This routine duplicates the specified ATAV. If the optional arena + * argument is non-null, the memory required will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL on error + * A pointer to a new ATAV + */ + +NSS_EXTERN NSSATAV * +NSSATAV_Duplicate +( + NSSATAV *atav, + NSSArena *arenaOpt +); + +extern const NSSError NSS_ERROR_INVALID_ATAV; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSRDN + * + * The public "methods" regarding this "object" are: + * + * NSSRDN_CreateFromBER -- constructor + * NSSRDN_CreateFromUTF8 -- constructor + * NSSRDN_Create -- constructor + * NSSRDN_CreateSimple -- constructor + * + * NSSRDN_Destroy + * NSSRDN_GetDEREncoding + * NSSRDN_GetUTF8Encoding + * NSSRDN_AddATAV + * NSSRDN_GetATAVCount + * NSSRDN_GetATAV + * NSSRDN_GetSimpleATAV + * NSSRDN_Compare + * NSSRDN_Duplicate + */ + +/* + * NSSRDN_CreateFromBER + * + * This routine creates an NSSRDN by decoding a BER- or DER-encoded + * RDN. If the optional arena argument is non-null, the memory used + * will be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSRDN upon success + */ + +NSS_EXTERN NSSRDN * +NSSRDN_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berRDN +); + +/* + * NSSRDN_CreateFromUTF8 + * + * This routine creates an NSSRDN by decoding an UTF8 string + * consisting of either a single ATAV in the "equals" format, e.g., + * "uid=smith," or one or more such ATAVs in parentheses, e.g., + * "(sn=Smith,ou=Sales)." If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_UNKNOWN_ATTRIBUTE + * NSS_ERROR_INVALID_STRING + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSRDN upon success + */ + +NSS_EXTERN NSSRDN * +NSSRDN_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringRDN +); + +/* + * NSSRDN_Create + * + * This routine creates an NSSRDN from one or more NSSATAVs. The + * final argument to this routine must be NULL. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * NULL upon error + * A pointer to an NSSRDN upon success + */ + +NSS_EXTERN NSSRDN * +NSSRDN_Create +( + NSSArena *arenaOpt, + NSSATAV *atav1, + ... +); + +/* + * NSSRDN_CreateSimple + * + * This routine creates a simple NSSRDN from a single NSSATAV. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * NULL upon error + * A pointer to an NSSRDN upon success + */ + +NSS_EXTERN NSSRDN * +NSSRDN_CreateSimple +( + NSSArena *arenaOpt, + NSSATAV *atav +); + +/* + * NSSRDN_Destroy + * + * This routine will destroy an RDN object. It should eventually be + * called on all RDNs created without an arena. While it is not + * necessary to call it on RDNs created within an arena, it is not an + * error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * create an error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * + * Return value: + * PR_FAILURE upon failure + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +NSSRDN_Destroy +( + NSSRDN *rdn +); + +/* + * NSSRDN_GetDEREncoding + * + * This routine will DER-encode an RDN object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSRDN + */ + +NSS_EXTERN NSSDER * +NSSRDN_GetDEREncoding +( + NSSRDN *rdn, + NSSArena *arenaOpt +); + +/* + * NSSRDN_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the RDN. A simple (one-ATAV) RDN will be simply + * the string representation of that ATAV; a non-simple RDN will be in + * parenthesised form. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * null upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string + */ + +NSS_EXTERN NSSUTF8 * +NSSRDN_GetUTF8Encoding +( + NSSRDN *rdn, + NSSArena *arenaOpt +); + +/* + * NSSRDN_AddATAV + * + * This routine adds an ATAV to the set of ATAVs in the specified RDN. + * Remember that RDNs consist of an unordered set of ATAVs. If the + * RDN was created with a non-null arena argument, that same arena + * will be used for any additional required memory. If the RDN was + * created with a NULL arena argument, any additional memory will + * be obtained from the heap. This routine returns a PRStatus value; + * it will return PR_SUCCESS upon success, and upon failure it will + * create an error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure + */ + +NSS_EXTERN PRStatus +NSSRDN_AddATAV +( + NSSRDN *rdn, + NSSATAV *atav +); + +/* + * NSSRDN_GetATAVCount + * + * This routine returns the cardinality of the set of ATAVs within + * the specified RDN. This routine may return 0 upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * + * Return value: + * 0 upon error + * A positive number upon success + */ + +NSS_EXTERN PRUint32 +NSSRDN_GetATAVCount +( + NSSRDN *rdn +); + +/* + * NSSRDN_GetATAV + * + * This routine returns a pointer to an ATAV that is a member of + * the set of ATAVs within the specified RDN. While the set of + * ATAVs within an RDN is unordered, this routine will return + * distinct values for distinct values of 'i' as long as the RDN + * is not changed in any way. The RDN may be changed by calling + * NSSRDN_AddATAV. The value of the variable 'i' is on the range + * [0,c) where c is the cardinality returned from NSSRDN_GetATAVCount. + * The caller owns the ATAV the pointer to which is returned. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_VALUE_OUT_OF_RANGE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSATAV + */ + +NSS_EXTERN NSSATAV * +NSSRDN_GetATAV +( + NSSRDN *rdn, + NSSArena *arenaOpt, + PRUint32 i +); + +/* + * NSSRDN_GetSimpleATAV + * + * Most RDNs are actually very simple, with a single ATAV. This + * routine will return the single ATAV from such an RDN. The caller + * owns the ATAV the pointer to which is returned. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, including the case where + * the set of ATAVs in the RDN is nonsingular. Upon error, this + * routine will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_RDN_NOT_SIMPLE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSATAV + */ + +NSS_EXTERN NSSATAV * +NSSRDN_GetSimpleATAV +( + NSSRDN *rdn, + NSSArena *arenaOpt +); + +/* + * NSSRDN_Compare + * + * This routine compares two RDNs for equality. For two RDNs to be + * equal, they must have the same number of ATAVs, and every ATAV in + * one must be equal to an ATAV in the other. (Note that the sets + * of ATAVs are unordered.) The result of the comparison will be + * stored at the location pointed to by the "equalp" variable, which + * must point to a valid PRBool. This routine may return PR_FAILURE + * upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +NSSRDN_Compare +( + NSSRDN *rdn1, + NSSRDN *rdn2, + PRBool *equalp +); + +/* + * NSSRDN_Duplicate + * + * This routine duplicates the specified RDN. If the optional arena + * argument is non-null, the memory required will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL on error + * A pointer to a new RDN + */ + +NSS_EXTERN NSSRDN * +NSSRDN_Duplicate +( + NSSRDN *rdn, + NSSArena *arenaOpt +); + +/* + * NSSRDNSeq + * + * The public "methods" regarding this "object" are: + * + * NSSRDNSeq_CreateFromBER -- constructor + * NSSRDNSeq_CreateFromUTF8 -- constructor + * NSSRDNSeq_Create -- constructor + * + * NSSRDNSeq_Destroy + * NSSRDNSeq_GetDEREncoding + * NSSRDNSeq_GetUTF8Encoding + * NSSRDNSeq_AppendRDN + * NSSRDNSeq_GetRDNCount + * NSSRDNSeq_GetRDN + * NSSRDNSeq_Compare + * NSSRDNSeq_Duplicate + */ + +/* + * NSSRDNSeq_CreateFromBER + * + * This routine creates an NSSRDNSeq by decoding a BER- or DER-encoded + * sequence of RDNs. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSRDNSeq upon success + */ + +NSS_EXTERN NSSRDNSeq * +NSSRDNSeq_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berRDNSeq +); + +/* + * NSSRDNSeq_CreateFromUTF8 + * + * This routine creates an NSSRDNSeq by decoding a UTF8 string + * consisting of a comma-separated sequence of RDNs, such as + * "(sn=Smith,ou=Sales),o=Acme,c=US." If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_UNKNOWN_ATTRIBUTE + * NSS_ERROR_INVALID_STRING + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSRDNSeq upon success + */ + +NSS_EXTERN NSSRDNSeq * +NSSRDNSeq_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringRDNSeq +); + +/* + * NSSRDNSeq_Create + * + * This routine creates an NSSRDNSeq from one or more NSSRDNs. The + * final argument to this routine must be NULL. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_INVALID_RDN + * + * Return value: + * NULL upon error + * A pointero to an NSSRDNSeq upon success + */ + +NSS_EXTERN NSSRDNSeq * +NSSRDNSeq_Create +( + NSSArena *arenaOpt, + NSSRDN *rdn1, + ... +); + +/* + * NSSRDNSeq_Destroy + * + * This routine will destroy an RDNSeq object. It should eventually + * be called on all RDNSeqs created without an arena. While it is not + * necessary to call it on RDNSeqs created within an arena, it is not + * an error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * create an error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +NSSRDNSeq_Destroy +( + NSSRDNSeq *rdnseq +); + +/* + * NSSRDNSeq_GetDEREncoding + * + * This routine will DER-encode an RDNSeq object. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return null upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSRDNSeq + */ + +NSS_EXTERN NSSDER * +NSSRDNSeq_GetDEREncoding +( + NSSRDNSeq *rdnseq, + NSSArena *arenaOpt +); + +/* + * NSSRDNSeq_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the RDNSeq as a comma-separated sequence of RDNs. + * If the optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return null upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the UTF8 string + */ + +NSS_EXTERN NSSUTF8 * +NSSRDNSeq_GetUTF8Encoding +( + NSSRDNSeq *rdnseq, + NSSArena *arenaOpt +); + +/* + * NSSRDNSeq_AppendRDN + * + * This routine appends an RDN to the end of the existing RDN + * sequence. If the RDNSeq was created with a non-null arena + * argument, that same arena will be used for any additional required + * memory. If the RDNSeq was created with a NULL arena argument, any + * additional memory will be obtained from the heap. This routine + * returns a PRStatus value; it will return PR_SUCCESS upon success, + * and upon failure it will create an error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure + */ + +NSS_EXTERN PRStatus +NSSRDNSeq_AppendRDN +( + NSSRDNSeq *rdnseq, + NSSRDN *rdn +); + +/* + * NSSRDNSeq_GetRDNCount + * + * This routine returns the cardinality of the sequence of RDNs within + * the specified RDNSeq. This routine may return 0 upon error, in + * which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * + * Return value: + * 0 upon error + * A positive number upon success + */ + +NSS_EXTERN PRUint32 +NSSRDNSeq_GetRDNCount +( + NSSRDNSeq *rdnseq +); + +/* + * NSSRDNSeq_GetRDN + * + * This routine returns a pointer to the i'th RDN in the sequence of + * RDNs that make up the specified RDNSeq. The sequence begins with + * the top-level (e.g., "c=US") RDN. The value of the variable 'i' + * is on the range [0,c) where c is the cardinality returned from + * NSSRDNSeq_GetRDNCount. The caller owns the RDN the pointer to which + * is returned. If the optional arena argument is non-null, the memory + * used will be obtained from that areana; otherwise, the memory will + * be obtained from the heap. This routine may return NULL upon error, + * in which case it will have created an error stack. Note that the + * usual string representation of RDN Sequences is from last to first. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_VALUE_OUT_OF_RANGE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSRDN + */ + +NSS_EXTERN NSSRDN * +NSSRDNSeq_GetRDN +( + NSSRDNSeq *rdnseq, + NSSArena *arenaOpt, + PRUint32 i +); + +/* + * NSSRDNSeq_Compare + * + * This routine compares two RDNSeqs for equality. For two RDNSeqs to + * be equal, they must have the same number of RDNs, and each RDN in + * one sequence must be equal to the corresponding RDN in the other + * sequence. The result of the comparison will be stored at the + * location pointed to by the "equalp" variable, which must point to a + * valid PRBool. This routine may return PR_FAILURE upon error, in + * which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +NSSRDNSeq_Compare +( + NSSRDNSeq *rdnseq1, + NSSRDNSeq *rdnseq2, + PRBool *equalp +); + +/* + * NSSRDNSeq_Duplicate + * + * This routine duplicates the specified RDNSeq. If the optional arena + * argument is non-null, the memory required will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a new RDNSeq + */ + +NSS_EXTERN NSSRDNSeq * +NSSRDNSeq_Duplicate +( + NSSRDNSeq *rdnseq, + NSSArena *arenaOpt +); + +/* + * NSSName + * + * The public "methods" regarding this "object" are: + * + * NSSName_CreateFromBER -- constructor + * NSSName_CreateFromUTF8 -- constructor + * NSSName_Create -- constructor + * + * NSSName_Destroy + * NSSName_GetDEREncoding + * NSSName_GetUTF8Encoding + * NSSName_GetChoice + * NSSName_GetRDNSequence + * NSSName_GetSpecifiedChoice + * NSSName_Compare + * NSSName_Duplicate + * + * NSSName_GetUID + * NSSName_GetEmail + * NSSName_GetCommonName + * NSSName_GetOrganization + * NSSName_GetOrganizationalUnits + * NSSName_GetStateOrProvince + * NSSName_GetLocality + * NSSName_GetCountry + * NSSName_GetAttribute + */ + +/* + * NSSName_CreateFromBER + * + * This routine creates an NSSName by decoding a BER- or DER-encoded + * (directory) Name. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, + * the memory will be obtained from the heap. This routine may + * return NULL upon error, in which case it will have created an error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSName upon success + */ + +NSS_EXTERN NSSName * +NSSName_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berName +); + +/* + * NSSName_CreateFromUTF8 + * + * This routine creates an NSSName by decoding a UTF8 string + * consisting of the string representation of one of the choices of + * (directory) names. Currently the only choice is an RDNSeq. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. The routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_STRING + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSName upon success + */ + +NSS_EXTERN NSSName * +NSSName_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringName +); + +/* + * NSSName_Create + * + * This routine creates an NSSName with the specified choice of + * underlying name types. The value of the choice variable must be + * one of the values of the NSSNameChoice enumeration, and the type + * of the arg variable must be as specified in the following table: + * + * Choice Type + * ======================== =========== + * NSSNameChoiceRdnSequence NSSRDNSeq * + * + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_CHOICE + * NSS_ERROR_INVALID_ARGUMENT + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSName upon success + */ + +NSS_EXTERN NSSName * +NSSName_Create +( + NSSArena *arenaOpt, + NSSNameChoice choice, + void *arg +); + +/* + * NSSName_Destroy + * + * This routine will destroy a Name object. It should eventually be + * called on all Names created without an arena. While it is not + * necessary to call it on Names created within an arena, it is not + * an error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * create an error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +NSSName_Destroy +( + NSSName *name +); + +/* + * NSSName_GetDEREncoding + * + * This routine will DER-encode a name object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSName + */ + +NSS_EXTERN NSSDER * +NSSName_GetDEREncoding +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the Name in the format specified by the + * underlying name choice. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the UTF8 string + */ + +NSS_EXTERN NSSUTF8 * +NSSName_GetUTF8Encoding +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetChoice + * + * This routine returns the type of the choice underlying the specified + * name. The return value will be a member of the NSSNameChoice + * enumeration. This routine may return NSSNameChoiceInvalid upon + * error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * + * Return value: + * NSSNameChoiceInvalid upon error + * An other member of the NSSNameChoice enumeration upon success + */ + +NSS_EXTERN NSSNameChoice +NSSName_GetChoice +( + NSSName *name +); + +/* + * NSSName_GetRDNSequence + * + * If the choice underlying the specified NSSName is that of an + * RDNSequence, this routine will return a pointer to that RDN + * sequence. Otherwise, this routine will place an error on the + * error stack, and return NULL. If the optional arena argument is + * non-null, the memory required will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. The + * caller owns the returned pointer. This routine may return NULL + * upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSRDNSeq + */ + +NSS_EXTERN NSSRDNSeq * +NSSName_GetRDNSequence +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetSpecifiedChoice + * + * If the choice underlying the specified NSSName matches the specified + * choice, a caller-owned pointer to that underlying object will be + * returned. Otherwise, an error will be placed on the error stack and + * NULL will be returned. If the optional arena argument is non-null, + * the memory required will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer, which must be typecast + */ + +NSS_EXTERN void * +NSSName_GetSpecifiedChoice +( + NSSName *name, + NSSNameChoice choice, + NSSArena *arenaOpt +); + +/* + * NSSName_Compare + * + * This routine compares two Names for equality. For two Names to be + * equal, they must have the same choice of underlying types, and the + * underlying values must be equal. The result of the comparison will + * be stored at the location pointed to by the "equalp" variable, which + * must point to a valid PRBool. This routine may return PR_FAILURE + * upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +NSSName_Compare +( + NSSName *name1, + NSSName *name2, + PRBool *equalp +); + +/* + * NSSName_Duplicate + * + * This routine duplicates the specified nssname. If the optional + * arena argument is non-null, the memory required will be obtained + * from that arena; otherwise, the memory will be obtained from the + * heap. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a new NSSName + */ + +NSS_EXTERN NSSName * +NSSName_Duplicate +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetUID + * + * This routine will attempt to derive a user identifier from the + * specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished + * Names containing a UID attribute, the UID will be the value of + * that attribute. Note that no UID attribute is defined in either + * PKIX or PKCS#9; rather, this seems to derive from RFC 1274, which + * defines the type as a caseIgnoreString. We'll return a Directory + * String. If the optional arena argument is non-null, the memory + * used will be obtained from that arena; otherwise, the memory will + * be obtained from the heap. This routine may return NULL upon error, + * in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_UID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String. + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSName_GetUID +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetEmail + * + * This routine will attempt to derive an email address from the + * specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished + * Names containing either a PKIX email address or a PKCS#9 email + * address, the result will be the value of that attribute. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_EMAIL + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr IA5 String */ +NSSName_GetEmail +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetCommonName + * + * This routine will attempt to derive a common name from the + * specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished Names + * containing a PKIX Common Name, the result will be that name. If + * the optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_COMMON_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSName_GetCommonName +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetOrganization + * + * This routine will attempt to derive an organisation name from the + * specified name, if the choices and content of the name permit. + * If Name consists of a Sequence of Relative Distinguished names + * containing a PKIX Organization, the result will be the value of + * that attribute. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. This routine may return NULL upon + * error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_ORGANIZATION + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSName_GetOrganization +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetOrganizationalUnits + * + * This routine will attempt to derive a sequence of organisational + * unit names from the specified name, if the choices and content of + * the name permit. If the Name consists of a Sequence of Relative + * Distinguished Names containing one or more organisational units, + * the result will be the values of those attributes. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_ORGANIZATIONAL_UNITS + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a null-terminated array of UTF8 Strings + */ + +NSS_EXTERN NSSUTF8 ** /* XXX fgmr DirectoryString */ +NSSName_GetOrganizationalUnits +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetStateOrProvince + * + * This routine will attempt to derive a state or province name from + * the specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished Names + * containing a state or province, the result will be the value of + * that attribute. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. This routine may return NULL upon + * error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_STATE_OR_PROVINCE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSName_GetStateOrProvince +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetLocality + * + * This routine will attempt to derive a locality name from the + * specified name, if the choices and content of the name permit. If + * the Name consists of a Sequence of Relative Distinguished names + * containing a Locality, the result will be the value of that + * attribute. If the optional arena argument is non-null, the memory + * used will be obtained from that arena; otherwise, the memory will + * be obtained from the heap. This routine may return NULL upon error, + * in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_LOCALITY + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSName_GetLocality +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetCountry + * + * This routine will attempt to derive a country name from the + * specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished + * Names containing a Country, the result will be the value of + * that attribute.. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, + * the memory will be obtained from the heap. This routine may + * return NULL upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_COUNTRY + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr PrintableString */ +NSSName_GetCountry +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * NSSName_GetAttribute + * + * If the specified name consists of a Sequence of Relative + * Distinguished Names containing an attribute with the specified + * type, and the actual value of that attribute may be expressed + * with a Directory String, then the value of that attribute will + * be returned as a Directory String. If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_ATTRIBUTE + * NSS_ERROR_ATTRIBUTE_VALUE_NOT_STRING + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSName_GetAttribute +( + NSSName *name, + NSSOID *attribute, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName + * + * The public "methods" regarding this "object" are: + * + * NSSGeneralName_CreateFromBER -- constructor + * NSSGeneralName_CreateFromUTF8 -- constructor + * NSSGeneralName_Create -- constructor + * + * NSSGeneralName_Destroy + * NSSGeneralName_GetDEREncoding + * NSSGeneralName_GetUTF8Encoding + * NSSGeneralName_GetChoice + * NSSGeneralName_GetOtherName + * NSSGeneralName_GetRfc822Name + * NSSGeneralName_GetDNSName + * NSSGeneralName_GetX400Address + * NSSGeneralName_GetDirectoryName + * NSSGeneralName_GetEdiPartyName + * NSSGeneralName_GetUniformResourceIdentifier + * NSSGeneralName_GetIPAddress + * NSSGeneralName_GetRegisteredID + * NSSGeneralName_GetSpecifiedChoice + * NSSGeneralName_Compare + * NSSGeneralName_Duplicate + * + * NSSGeneralName_GetUID + * NSSGeneralName_GetEmail + * NSSGeneralName_GetCommonName + * NSSGeneralName_GetOrganization + * NSSGeneralName_GetOrganizationalUnits + * NSSGeneralName_GetStateOrProvince + * NSSGeneralName_GetLocality + * NSSGeneralName_GetCountry + * NSSGeneralName_GetAttribute + */ + +/* + * NSSGeneralName_CreateFromBER + * + * This routine creates an NSSGeneralName by decoding a BER- or DER- + * encoded general name. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralName upon success + */ + +NSS_EXTERN NSSGeneralName * +NSSGeneralName_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berGeneralName +); + +/* + * NSSGeneralName_CreateFromUTF8 + * + * This routine creates an NSSGeneralName by decoding a UTF8 string + * consisting of the string representation of one of the choices of + * general names. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The routine may return NULL upon + * error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_STRING + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralName upon success + */ + +NSS_EXTERN NSSGeneralName * +NSSGeneralName_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringGeneralName +); + +/* + * NSSGeneralName_Create + * + * This routine creates an NSSGeneralName with the specified choice of + * underlying name types. The value of the choice variable must be one + * of the values of the NSSGeneralNameChoice enumeration, and the type + * of the arg variable must be as specified in the following table: + * + * Choice Type + * ============================================ ========= + * NSSGeneralNameChoiceOtherName + * NSSGeneralNameChoiceRfc822Name + * NSSGeneralNameChoiceDNSName + * NSSGeneralNameChoiceX400Address + * NSSGeneralNameChoiceDirectoryName NSSName * + * NSSGeneralNameChoiceEdiPartyName + * NSSGeneralNameChoiceUniformResourceIdentifier + * NSSGeneralNameChoiceIPAddress + * NSSGeneralNameChoiceRegisteredID + * + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have created an error stack. + * + * The error may be one fo the following values: + * NSS_ERROR_INVALID_CHOICE + * NSS_ERROR_INVALID_ARGUMENT + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralName upon success + */ + +NSS_EXTERN NSSGeneralName * +NSSGeneralName_Create +( + NSSGeneralNameChoice choice, + void *arg +); + +/* + * NSSGeneralName_Destroy + * + * This routine will destroy a General Name object. It should + * eventually be called on all General Names created without an arena. + * While it is not necessary to call it on General Names created within + * an arena, it is not an error to do so. This routine returns a + * PRStatus value; if successful, it will return PR_SUCCESS. If + * usuccessful, it will create an error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * + * Return value: + * PR_FAILURE upon failure + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +NSSGeneralName_Destroy +( + NSSGeneralName *generalName +); + +/* + * NSSGeneralName_GetDEREncoding + * + * This routine will DER-encode a name object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSGeneralName + */ + +NSS_EXTERN NSSDER * +NSSGeneralName_GetDEREncoding +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the General Name in the format specified by the + * underlying name choice. If the optional arena argument is + * non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string + */ + +NSS_EXTERN NSSUTF8 * +NSSGeneralName_GetUTF8Encoding +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetChoice + * + * This routine returns the type of choice underlying the specified + * general name. The return value will be a member of the + * NSSGeneralNameChoice enumeration. This routine may return + * NSSGeneralNameChoiceInvalid upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * + * Return value: + * NSSGeneralNameChoiceInvalid upon error + * An other member of the NSSGeneralNameChoice enumeration + */ + +NSS_EXTERN NSSGeneralNameChoice +NSSGeneralName_GetChoice +( + NSSGeneralName *generalName +); + +/* + * NSSGeneralName_GetOtherName + * + * If the choice underlying the specified NSSGeneralName is that of an + * Other Name, this routine will return a pointer to that Other name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSOtherName + */ + +NSS_EXTERN NSSOtherName * +NSSGeneralName_GetOtherName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetRfc822Name + * + * If the choice underlying the specified NSSGeneralName is that of an + * RFC 822 Name, this routine will return a pointer to that name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSRFC822Name + */ + +NSS_EXTERN NSSRFC822Name * +NSSGeneralName_GetRfc822Name +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetDNSName + * + * If the choice underlying the specified NSSGeneralName is that of a + * DNS Name, this routine will return a pointer to that DNS name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSDNSName + */ + +NSS_EXTERN NSSDNSName * +NSSGeneralName_GetDNSName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetX400Address + * + * If the choice underlying the specified NSSGeneralName is that of an + * X.400 Address, this routine will return a pointer to that Address. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSX400Address + */ + +NSS_EXTERN NSSX400Address * +NSSGeneralName_GetX400Address +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetDirectoryName + * + * If the choice underlying the specified NSSGeneralName is that of a + * (directory) Name, this routine will return a pointer to that name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSName + */ + +NSS_EXTERN NSSName * +NSSGeneralName_GetName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetEdiPartyName + * + * If the choice underlying the specified NSSGeneralName is that of an + * EDI Party Name, this routine will return a pointer to that name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSEdiPartyName + */ + +NSS_EXTERN NSSEdiPartyName * +NSSGeneralName_GetEdiPartyName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetUniformResourceIdentifier + * + * If the choice underlying the specified NSSGeneralName is that of a + * URI, this routine will return a pointer to that URI. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSURI + */ + +NSS_EXTERN NSSURI * +NSSGeneralName_GetUniformResourceIdentifier +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetIPAddress + * + * If the choice underlying the specified NSSGeneralName is that of an + * IP Address , this routine will return a pointer to that address. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSIPAddress + */ + +NSS_EXTERN NSSIPAddress * +NSSGeneralName_GetIPAddress +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetRegisteredID + * + * If the choice underlying the specified NSSGeneralName is that of a + * Registered ID, this routine will return a pointer to that ID. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSRegisteredID + */ + +NSS_EXTERN NSSRegisteredID * +NSSGeneralName_GetRegisteredID +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetSpecifiedChoice + * + * If the choice underlying the specified NSSGeneralName matches the + * specified choice, a caller-owned pointer to that underlying object + * will be returned. Otherwise, an error will be placed on the error + * stack and NULL will be returned. If the optional arena argument + * is non-null, the memory required will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. The caller + * owns the returned pointer. This routine may return NULL upon + * error, in which caes it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer, which must be typecast + */ + +NSS_EXTERN void * +NSSGeneralName_GetSpecifiedChoice +( + NSSGeneralName *generalName, + NSSGeneralNameChoice choice, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_Compare + * + * This routine compares two General Names for equality. For two + * General Names to be equal, they must have the same choice of + * underlying types, and the underlying values must be equal. The + * result of the comparison will be stored at the location pointed + * to by the "equalp" variable, which must point to a valid PRBool. + * This routine may return PR_FAILURE upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following value: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +NSSGeneralName_Compare +( + NSSGeneralName *generalName1, + NSSGeneralName *generalName2, + PRBool *equalp +); + +/* + * NSSGeneralName_Duplicate + * + * This routine duplicates the specified General Name. If the optional + * arena argument is non-null, the memory required will be obtained + * from that arena; otherwise, the memory will be obtained from the + * heap. This routine may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a new NSSGeneralName + */ + +NSS_EXTERN NSSGeneralName * +NSSGeneralName_Duplicate +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetUID + * + * This routine will attempt to derive a user identifier from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished Names containing a UID + * attribute, the UID will be the value of that attribute. Note + * that no UID attribute is defined in either PKIX or PKCS#9; + * rather, this seems to derive from RFC 1274, which defines the + * type as a caseIgnoreString. We'll return a Directory String. + * If the optional arena argument is non-null, the memory used + * will be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_UID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String. + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSGeneralName_GetUID +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetEmail + * + * This routine will attempt to derive an email address from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing either + * a PKIX email address or a PKCS#9 email address, the result will + * be the value of that attribute. If the General Name is an RFC 822 + * Name, the result will be the string form of that name. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_EMAIL + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr IA5String */ +NSSGeneralName_GetEmail +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetCommonName + * + * This routine will attempt to derive a common name from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing a PKIX + * Common Name, the result will be that name. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_COMMON_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSGeneralName_GetCommonName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetOrganization + * + * This routine will attempt to derive an organisation name from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing an + * Organization, the result will be the value of that attribute. + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_ORGANIZATION + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSGeneralName_GetOrganization +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetOrganizationalUnits + * + * This routine will attempt to derive a sequence of organisational + * unit names from the specified general name, if the choices and + * content of the name permit. If the General Name is a (directory) + * Name consisting of a Sequence of Relative Distinguished names + * containing one or more organisational units, the result will + * consist of those units. If the optional arena argument is non- + * null, the memory used will be obtained from that arena; otherwise, + * the memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_ORGANIZATIONAL_UNITS + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a null-terminated array of UTF8 Strings + */ + +NSS_EXTERN NSSUTF8 ** /* XXX fgmr DirectoryString */ +NSSGeneralName_GetOrganizationalUnits +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetStateOrProvince + * + * This routine will attempt to derive a state or province name from + * the specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing a state or + * province, the result will be the value of that attribute. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_STATE_OR_PROVINCE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSGeneralName_GetStateOrProvince +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetLocality + * + * This routine will attempt to derive a locality name from + * the specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing a Locality, + * the result will be the value of that attribute. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_LOCALITY + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSGeneralName_GetLocality +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetCountry + * + * This routine will attempt to derive a country name from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting of a + * Sequence of Relative Distinguished names containing a Country, the + * result will be the value of that attribute. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_COUNTRY + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr PrintableString */ +NSSGeneralName_GetCountry +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * NSSGeneralName_GetAttribute + * + * If the specified general name is a (directory) name consisting + * of a Sequence of Relative Distinguished Names containing an + * attribute with the specified type, and the actual value of that + * attribute may be expressed with a Directory String, then the + * value of that attribute will be returned as a Directory String. + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_ATTRIBUTE + * NSS_ERROR_ATTRIBUTE_VALUE_NOT_STRING + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +NSSGeneralName_GetAttribute +( + NSSGeneralName *generalName, + NSSOID *attribute, + NSSArena *arenaOpt +); + +/* + * NSSGeneralNameSeq + * + * The public "methods" regarding this "object" are: + * + * NSSGeneralNameSeq_CreateFromBER -- constructor + * NSSGeneralNameSeq_Create -- constructor + * + * NSSGeneralNameSeq_Destroy + * NSSGeneralNameSeq_GetDEREncoding + * NSSGeneralNameSeq_AppendGeneralName + * NSSGeneralNameSeq_GetGeneralNameCount + * NSSGeneralNameSeq_GetGeneralName + * NSSGeneralNameSeq_Compare + * NSSGeneralnameSeq_Duplicate + */ + +/* + * NSSGeneralNameSeq_CreateFromBER + * + * This routine creates a general name sequence by decoding a BER- + * or DER-encoded GeneralNames. If the optional arena argument is + * non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralNameSeq upon success + */ + +NSS_EXTERN NSSGeneralNameSeq * +NSSGeneralNameSeq_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berGeneralNameSeq +); + +/* + * NSSGeneralNameSeq_Create + * + * This routine creates an NSSGeneralNameSeq from one or more General + * Names. The final argument to this routine must be NULL. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_INVALID_GENERAL_NAME + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralNameSeq upon success + */ + +NSS_EXTERN NSSGeneralNameSeq * +NSSGeneralNameSeq_Create +( + NSSArena *arenaOpt, + NSSGeneralName *generalName1, + ... +); + +/* + * NSSGeneralNameSeq_Destroy + * + * This routine will destroy an NSSGeneralNameSeq object. It should + * eventually be called on all NSSGeneralNameSeqs created without an + * arena. While it is not necessary to call it on NSSGeneralNameSeq's + * created within an arena, it is not an error to do so. This routine + * returns a PRStatus value; if successful, it will return PR_SUCCESS. + * If unsuccessful, it will create an error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +NSSGeneralNameSeq_Destroy +( + NSSGeneralNameSeq *generalNameSeq +); + +/* + * NSSGeneralNameSeq_GetDEREncoding + * + * This routine will DER-encode an NSSGeneralNameSeq object. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return null upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSGeneralNameSeq + */ + +NSS_EXTERN NSSDER * +NSSGeneralNameSeq_GetDEREncoding +( + NSSGeneralNameSeq *generalNameSeq, + NSSArena *arenaOpt +); + +/* + * NSSGeneralNameSeq_AppendGeneralName + * + * This routine appends a General Name to the end of the existing + * General Name Sequence. If the sequence was created with a non-null + * arena argument, that same arena will be used for any additional + * required memory. If the sequence was created with a NULL arena + * argument, any additional memory will be obtained from the heap. + * This routine returns a PRStatus value; it will return PR_SUCCESS + * upon success, and upon failure it will create an error stack and + * return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure. + */ + +NSS_EXTERN PRStatus +NSSGeneralNameSeq_AppendGeneralName +( + NSSGeneralNameSeq *generalNameSeq, + NSSGeneralName *generalName +); + +/* + * NSSGeneralNameSeq_GetGeneralNameCount + * + * This routine returns the cardinality of the specified General name + * Sequence. This routine may return 0 upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * + * Return value; + * 0 upon error + * A positive number upon success + */ + +NSS_EXTERN PRUint32 +NSSGeneralNameSeq_GetGeneralNameCount +( + NSSGeneralNameSeq *generalNameSeq +); + +/* + * NSSGeneralNameSeq_GetGeneralName + * + * This routine returns a pointer to the i'th General Name in the + * specified General Name Sequence. The value of the variable 'i' is + * on the range [0,c) where c is the cardinality returned from + * NSSGeneralNameSeq_GetGeneralNameCount. The caller owns the General + * Name the pointer to which is returned. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_VALUE_OUT_OF_RANGE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to a General Name. + */ + +NSS_EXTERN NSSGeneralName * +NSSGeneralNameSeq_GetGeneralName +( + NSSGeneralNameSeq *generalNameSeq, + NSSArena *arenaOpt, + PRUint32 i +); + +/* + * NSSGeneralNameSeq_Compare + * + * This routine compares two General Name Sequences for equality. For + * two General Name Sequences to be equal, they must have the same + * cardinality, and each General Name in one sequence must be equal to + * the corresponding General Name in the other. The result of the + * comparison will be stored at the location pointed to by the "equalp" + * variable, which must point to a valid PRBool. This routine may + * return PR_FAILURE upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +NSSGeneralNameSeq_Compare +( + NSSGeneralNameSeq *generalNameSeq1, + NSSGeneralNameSeq *generalNameSeq2, + PRBool *equalp +); + +/* + * NSSGeneralNameSeq_Duplicate + * + * This routine duplicates the specified sequence of general names. If + * the optional arena argument is non-null, the memory required will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a new General Name Sequence. + */ + +NSS_EXTERN NSSGeneralNameSeq * +NSSGeneralNameSeq_Duplicate +( + NSSGeneralNameSeq *generalNameSeq, + NSSArena *arenaOpt +); + +PR_END_EXTERN_C + +#endif /* NSSPT1M_H */ diff --git a/security/nss/lib/pki1/nsspki1t.h b/security/nss/lib/pki1/nsspki1t.h new file mode 100644 index 000000000..8765a3ad3 --- /dev/null +++ b/security/nss/lib/pki1/nsspki1t.h @@ -0,0 +1,202 @@ +/* + * 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 NSSPKI1T_H +#define NSSPKI1T_H + +#ifdef DEBUG +static const char NSSPKI1T_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +/* + * nsspki1t.h + * + * This file contains the public type definitions for the PKIX part-1 + * objects. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +PR_BEGIN_EXTERN_C + +/* + * OBJECT IDENTIFIER + * + * This is the basic OID that crops up everywhere. + */ + +struct NSSOIDStr; +typedef struct NSSOIDStr NSSOID; + +/* + * AttributeTypeAndValue + * + * This structure contains an attribute type (indicated by an OID), + * and the type-specific value. RelativeDistinguishedNamess consist + * of a set of these. These are distinct from Attributes (which have + * SET of values), from AttributeDescriptions (which have qualifiers + * on the types), and from AttributeValueAssertions (which assert a + * a value comparison under some matching rule). + */ + +struct NSSATAVStr; +typedef struct NSSATAVStr NSSATAV; + +/* + * RelativeDistinguishedName + * + * This structure contains an unordered set of AttributeTypeAndValue + * objects. RDNs are used to distinguish a set of objects underneath + * a common object. + * + * Often, a single ATAV is sufficient to make a unique distinction. + * For example, if a company assigns its people unique uid values, + * then in the Name "uid=smith,ou=People,o=Acme,c=US" the "uid=smith" + * ATAV by itself forms an RDN. However, sometimes a set of ATAVs is + * needed. For example, if a company needed to distinguish between + * two Smiths by specifying their corporate divisions, then in the + * Name "(cn=Smith,ou=Sales),ou=People,o=Acme,c=US" the parenthesised + * set of ATAVs forms the RDN. + */ + +struct NSSRDNStr; +typedef struct NSSRDNStr NSSRDN; + +/* + * RDNSequence + * + * This structure contains a sequence of RelativeDistinguishedName + * objects. + */ + +struct NSSRDNSeqStr; +typedef struct NSSRDNSeqStr NSSRDNSeq; + +/* + * Name + * + * This structure contains a union of the possible name formats, + * which at the moment is limited to an RDNSequence. + */ + +struct NSSNameStr; +typedef struct NSSNameStr NSSName; + +/* + * NameChoice + * + * This enumeration is used to specify choice within a name. + */ + +enum NSSNameChoiceEnum { + NSSNameChoiceInvalid = -1, + NSSNameChoiceRdnSequence +}; +typedef enum NSSNameChoiceEnum NSSNameChoice; + +/* + * GeneralName + * + * This structure contains a union of the possible general names, + * of which there are several. + */ + +struct NSSGeneralNameStr; +typedef struct NSSGeneralNameStr NSSGeneralName; + +/* + * GeneralNameChoice + * + * This enumerates the possible general name types. + */ + +enum NSSGeneralNameChoiceEnum { + NSSGeneralNameChoiceInvalid = -1, + NSSGeneralNameChoiceOtherName = 0, + NSSGeneralNameChoiceRfc822Name = 1, + NSSGeneralNameChoiceDNSName = 2, + NSSGeneralNameChoiceX400Address = 3, + NSSGeneralNameChoiceDirectoryName = 4, + NSSGeneralNameChoiceEdiPartyName = 5, + NSSGeneralNameChoiceUniformResourceIdentifier = 6, + NSSGeneralNameChoiceIPAddress = 7, + NSSGeneralNameChoiceRegisteredID = 8 +}; +typedef enum NSSGeneralNameChoiceEnum NSSGeneralNameChoice; + +/* + * The "other" types of general names. + */ + +struct NSSOtherNameStr; +typedef struct NSSOtherNameStr NSSOtherName; + +struct NSSRFC822NameStr; +typedef struct NSSRFC822NameStr NSSRFC822Name; + +struct NSSDNSNameStr; +typedef struct NSSDNSNameStr NSSDNSName; + +struct NSSX400AddressStr; +typedef struct NSSX400AddressStr NSSX400Address; + +struct NSSEdiPartyNameStr; +typedef struct NSSEdiPartyNameStr NSSEdiPartyName; + +struct NSSURIStr; +typedef struct NSSURIStr NSSURI; + +struct NSSIPAddressStr; +typedef struct NSSIPAddressStr NSSIPAddress; + +struct NSSRegisteredIDStr; +typedef struct NSSRegisteredIDStr NSSRegisteredID; + +/* + * GeneralNameSeq + * + * This structure contains a sequence of GeneralName objects. + * Note that the PKIX documents refer to this as "GeneralNames," + * which differs from "GeneralName" by only one letter. To + * try to reduce confusion, we expand the name slightly to + * "GeneralNameSeq." + */ + +struct NSSGeneralNameSeqStr; +typedef struct NSSGeneralNameSeqStr NSSGeneralNameSeq; + +PR_END_EXTERN_C + +#endif /* NSSPKI1T_H */ diff --git a/security/nss/lib/pki1/oid.c b/security/nss/lib/pki1/oid.c new file mode 100644 index 000000000..c4028a803 --- /dev/null +++ b/security/nss/lib/pki1/oid.c @@ -0,0 +1,1615 @@ +/* + * 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 */ + +/* + * oid.c + * + * This file contains the implementation of the basic OID routines. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#ifndef PKI1_H +#include "pki1.h" +#endif /* PKI1_H */ + +#include "plhash.h" +#include "plstr.h" + +/* + * NSSOID + * + * The public "methods" regarding this "object" are: + * + * NSSOID_CreateFromBER -- constructor + * NSSOID_CreateFromUTF8 -- constructor + * (there is no explicit destructor) + * + * NSSOID_GetDEREncoding + * NSSOID_GetUTF8Encoding + + * The non-public "methods" regarding this "object" are: + * + * nssOID_CreateFromBER -- constructor + * nssOID_CreateFromUTF8 -- constructor + * (there is no explicit destructor) + * + * nssOID_GetDEREncoding + * nssOID_GetUTF8Encoding + * + * In debug builds, the following non-public calls are also available: + * + * nssOID_verifyPointer + * nssOID_getExplanation + * nssOID_getTaggedUTF8 + */ + +const NSSOID *NSS_OID_UNKNOWN = (NSSOID *)NULL; + +/* + * First, the public "wrappers" + */ + +/* + * NSSOID_CreateFromBER + * + * This routine creates an NSSOID by decoding a BER- or DER-encoded + * OID. It may return NULL upon error, in which case it + * will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * An NSSOID upon success + */ + +NSS_EXTERN NSSOID * +NSSOID_CreateFromBER +( + NSSBER *berOid +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + /* + * NSSBERs can be created by the user, + * so no pointer-tracking can be checked. + */ + + if( (NSSBER *)NULL == berOid ) { + nss_SetError(NSS_ERROR_INVALID_BER); + return (NSSOID *)NULL; + } + + if( (void *)NULL == berOid->data ) { + nss_SetError(NSS_ERROR_INVALID_BER); + return (NSSOID *)NULL; + } +#endif /* DEBUG */ + + return nssOID_CreateFromBER(berOid); +} + +/* + * NSSOID_CreateFromUTF8 + * + * This routine creates an NSSOID by decoding a UTF8 string + * representation of an OID in dotted-number format. The string may + * optionally begin with an octothorpe. It may return NULL + * upon error, in which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_UTF8 + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * An NSSOID upon success + */ + +NSS_EXTERN NSSOID * +NSSOID_CreateFromUTF8 +( + NSSUTF8 *stringOid +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + /* + * NSSUTF8s can be created by the user, + * so no pointer-tracking can be checked. + */ + + if( (NSSUTF8 *)NULL == stringOid ) { + nss_SetError(NSS_ERROR_INVALID_UTF8); + return (NSSOID *)NULL; + } +#endif /* DEBUG */ + + return nssOID_CreateFromUTF8(stringOid); +} + +/* + * NSSOID_GetDEREncoding + * + * This routine returns the DER encoding of the specified NSSOID. + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return return null upon error, in + * which case it will have created an error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSOID + */ + +NSS_EXTERN NSSDER * +NSSOID_GetDEREncoding +( + const NSSOID *oid, + NSSDER *rvOpt, + NSSArena *arenaOpt +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { + return (NSSDER *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSDER *)NULL; + } + } +#endif /* DEBUG */ + + return nssOID_GetDEREncoding(oid, rvOpt, arenaOpt); +} + +/* + * NSSOID_GetUTF8Encoding + * + * This routine returns a UTF8 string containing the dotted-number + * encoding of the specified NSSOID. If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return null upon error, in which case it will have created an + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the dotted-digit encoding of + * this NSSOID + */ + +NSS_EXTERN NSSUTF8 * +NSSOID_GetUTF8Encoding +( + const NSSOID *oid, + NSSArena *arenaOpt +) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { + return (NSSUTF8 *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSUTF8 *)NULL; + } + } +#endif /* DEBUG */ + + return nssOID_GetUTF8Encoding(oid, arenaOpt); +} + +/* + * Next, some internal bookkeeping; including the OID "tag" table + * and the debug-version pointer tracker. + */ + +/* + * For implementation reasons (so NSSOIDs can be compared with ==), + * we hash all NSSOIDs. This is the hash table. + */ + +static PLHashTable *oid_hash_table; + +/* + * And this is its lock. + */ + +static PRLock *oid_hash_lock; + +/* + * This is the hash function. We simply XOR the encoded form with + * itself in sizeof(PLHashNumber)-byte chunks. Improving this + * routine is left as an excercise for the more mathematically + * inclined student. + */ + +static PR_CALLBACK PLHashNumber +oid_hash +( + const void *key +) +{ + const NSSItem *item = (const NSSItem *)key; + PLHashNumber rv = 0; + + PRUint8 *data = (PRUint8 *)item->data; + PRUint32 i; + PRUint8 *rvc = (PRUint8 *)&rv; + + for( i = 0; i < item->size; i++ ) { + rvc[ i % sizeof(rv) ] ^= *data; + data++; + } + + return rv; +} + +/* + * This is the key-compare function. It simply does a lexical + * comparison on the encoded OID form. This does not result in + * quite the same ordering as the "sequence of numbers" order, + * but heck it's only used internally by the hash table anyway. + */ + +static PR_CALLBACK PRIntn +oid_hash_compare +( + const void *k1, + const void *k2 +) +{ + PRIntn rv; + + const NSSItem *i1 = (const NSSItem *)k1; + const NSSItem *i2 = (const NSSItem *)k2; + + PRUint32 size = (i1->size < i2->size) ? i1->size : i2->size; + + rv = (PRIntn)nsslibc_memcmp(i1->data, i2->data, size, (PRStatus *)NULL); + if( 0 == rv ) { + rv = i1->size - i2->size; + } + + return !rv; +} + +/* + * The pointer-tracking code + */ + +#ifdef DEBUG +extern const NSSError NSS_ERROR_INTERNAL_ERROR; + +static nssPointerTracker oid_pointer_tracker; + +static PRStatus +oid_add_pointer +( + const NSSOID *oid +) +{ + PRStatus rv; + + rv = nssPointerTracker_initialize(&oid_pointer_tracker); + if( PR_SUCCESS != rv ) { + return rv; + } + + rv = nssPointerTracker_add(&oid_pointer_tracker, oid); + if( PR_SUCCESS != rv ) { + NSSError e = NSS_GetError(); + if( NSS_ERROR_NO_MEMORY != e ) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + } + + return rv; + } + + return PR_SUCCESS; +} + +#if defined(CAN_DELETE_OIDS) +/* + * We actually don't define NSSOID deletion, since we keep OIDs + * in a hash table for easy comparison. Were we to, this is + * what the pointer-removal function would look like. + */ + +static PRStatus +oid_remove_pointer +( + const NSSOID *oid +) +{ + PRStatus rv; + + rv = nssPointerTracker_remove(&oid_pointer_tracker, oid); + if( PR_SUCCESS != rv ) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + } + + return rv; +} +#endif /* CAN_DELETE_OIDS */ + +#endif /* DEBUG */ + +/* + * All dynamically-added OIDs get their memory from one statically- + * declared arena here, merely so that any cleanup code will have + * an easier time of it. + */ + +static NSSArena *oid_arena; + +/* + * This is the call-once function which initializes the hashtable. + * It creates it, then prepopulates it with all of the builtin OIDs. + * It also creates the aforementioned NSSArena. + */ + +static PR_CALLBACK PRStatus +oid_once_func +( + void +) +{ + PRUint32 i; + + /* Initialize the arena */ + oid_arena = nssArena_Create(); + if( (NSSArena *)NULL == oid_arena ) { + goto loser; + } + + /* Create the hash table lock */ + oid_hash_lock = PR_NewLock(); + if( (PRLock *)NULL == oid_hash_lock ) { + nss_SetError(NSS_ERROR_NO_MEMORY); + goto loser; + } + + /* Create the hash table */ + oid_hash_table = PL_NewHashTable(0, oid_hash, oid_hash_compare, + PL_CompareValues, + (PLHashAllocOps *)0, + (void *)0); + if( (PLHashTable *)NULL == oid_hash_table ) { + nss_SetError(NSS_ERROR_NO_MEMORY); + goto loser; + } + + /* And populate it with all the builtins */ + for( i = 0; i < nss_builtin_oid_count; i++ ) { + NSSOID *oid = (NSSOID *)&nss_builtin_oids[i]; + PLHashEntry *e = PL_HashTableAdd(oid_hash_table, &oid->data, oid); + if( (PLHashEntry *)NULL == e ) { + nss_SetError(NSS_ERROR_NO_MEMORY); + goto loser; + } + +#ifdef DEBUG + if( PR_SUCCESS != oid_add_pointer(oid) ) { + goto loser; + } +#endif /* DEBUG */ + } + + return PR_SUCCESS; + + loser: + if( (PLHashTable *)NULL != oid_hash_table ) { + PL_HashTableDestroy(oid_hash_table); + oid_hash_table = (PLHashTable *)NULL; + } + + if( (PRLock *)NULL != oid_hash_lock ) { + PR_DestroyLock(oid_hash_lock); + oid_hash_lock = (PRLock *)NULL; + } + + if( (NSSArena *)NULL != oid_arena ) { + (void)nssArena_Destroy(oid_arena); + oid_arena = (NSSArena *)NULL; + } + + return PR_FAILURE; +} + +/* + * This is NSPR's once-block. + */ + +static PRCallOnceType oid_call_once; + +/* + * And this is our multiply-callable internal init routine, which + * will call-once our call-once function. + */ + +static PRStatus +oid_init +( + void +) +{ + return PR_CallOnce(&oid_call_once, oid_once_func); +} + +#ifdef DEBUG + +/* + * nssOID_verifyPointer + * + * This method is only present in debug builds. + * + * If the specified pointer is a valid pointer to an NSSOID object, + * this routine will return PR_SUCCESS. Otherwise, it will put an + * error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS if the pointer is valid + * PR_FAILURE if it isn't + */ + +NSS_EXTERN PRStatus +nssOID_verifyPointer +( + const NSSOID *oid +) +{ + PRStatus rv; + + rv = oid_init(); + if( PR_SUCCESS != rv ) { + return PR_FAILURE; + } + + rv = nssPointerTracker_initialize(&oid_pointer_tracker); + if( PR_SUCCESS != rv ) { + return PR_FAILURE; + } + + rv = nssPointerTracker_verify(&oid_pointer_tracker, oid); + if( PR_SUCCESS != rv ) { + nss_SetError(NSS_ERROR_INVALID_NSSOID); + return PR_FAILURE; + } + + return PR_SUCCESS; +} +#endif /* DEBUG */ + +/* + * oid_sanity_check_ber + * + * This routine merely applies some sanity-checking to the BER-encoded + * OID. + */ + +static PRStatus +oid_sanity_check_ber +( + NSSBER *berOid +) +{ + PRUint32 i; + PRUint8 *data = (PRUint8 *)berOid->data; + + /* + * The size must be longer than zero bytes. + */ + + if( berOid->size <= 0 ) { + return PR_FAILURE; + } + + /* + * In general, we can't preclude any number from showing up + * someday. We could probably guess that top-level numbers + * won't get very big (beyond the current ccitt(0), iso(1), + * or joint-ccitt-iso(2)). However, keep in mind that the + * encoding rules wrap the first two numbers together, as + * + * (first * 40) + second + * + * Also, it is noted in the specs that this implies that the + * second number won't go above forty. + * + * 128 encodes 3.8, which seems pretty safe for now. Let's + * check that the first byte is less than that. + * + * XXX This is a "soft check" -- we may want to exclude it. + */ + + if( data[0] >= 0x80 ) { + return PR_FAILURE; + } + + /* + * In a normalised format, leading 0x80s will never show up. + * This means that no 0x80 will be preceeded by the final + * byte of a sequence, which would naturaly be less than 0x80. + * Our internal encoding for the single-digit OIDs uses 0x80, + * but the only places we use them (loading the builtin table, + * and adding a UTF8-encoded OID) bypass this check. + */ + + for( i = 1; i < berOid->size; i++ ) { + if( (0x80 == data[i]) && (data[i-1] < 0x80) ) { + return PR_FAILURE; + } + } + + /* + * The high bit of each octet indicates that following octets + * are included in the current number. Thus the last byte can't + * have the high bit set. + */ + + if( data[ berOid->size-1 ] >= 0x80 ) { + return PR_FAILURE; + } + + /* + * Other than that, any byte sequence is legit. + */ + return PR_SUCCESS; +} + +/* + * nssOID_CreateFromBER + * + * This routine creates an NSSOID by decoding a BER- or DER-encoded + * OID. It may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * An NSSOID upon success + */ + +NSS_EXTERN NSSOID * +nssOID_CreateFromBER +( + NSSBER *berOid +) +{ + NSSOID *rv; + PLHashEntry *e; + + if( PR_SUCCESS != oid_init() ) { + return (NSSOID *)NULL; + } + + if( PR_SUCCESS != oid_sanity_check_ber(berOid) ) { + nss_SetError(NSS_ERROR_INVALID_BER); + return (NSSOID *)NULL; + } + + /* + * Does it exist? + */ + PR_Lock(oid_hash_lock); + rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, berOid); + (void)PR_Unlock(oid_hash_lock); + if( (NSSOID *)NULL != rv ) { + /* Found it! */ + return rv; + } + + /* + * Doesn't exist-- create it. + */ + rv = nss_ZNEW(oid_arena, NSSOID); + if( (NSSOID *)NULL == rv ) { + return (NSSOID *)NULL; + } + + rv->data.data = nss_ZAlloc(oid_arena, berOid->size); + if( (void *)NULL == rv->data.data ) { + return (NSSOID *)NULL; + } + + rv->data.size = berOid->size; + nsslibc_memcpy(rv->data.data, berOid->data, berOid->size); + +#ifdef DEBUG + rv->tag = "<runtime>"; + rv->expl = "(OID registered at runtime)"; +#endif /* DEBUG */ + + PR_Lock(oid_hash_lock); + e = PL_HashTableAdd(oid_hash_table, &rv->data, rv); + (void)PR_Unlock(oid_hash_lock); + if( (PLHashEntry *)NULL == e ) { + nss_ZFreeIf(rv->data.data); + nss_ZFreeIf(rv); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSOID *)NULL; + } + +#ifdef DEBUG + { + PRStatus st; + st = oid_add_pointer(rv); + if( PR_SUCCESS != st ) { + PR_Lock(oid_hash_lock); + (void)PL_HashTableRemove(oid_hash_table, &rv->data); + (void)PR_Unlock(oid_hash_lock); + (void)nss_ZFreeIf(rv->data.data); + (void)nss_ZFreeIf(rv); + return (NSSOID *)NULL; + } + } +#endif /* DEBUG */ + + return rv; +} + +/* + * oid_sanity_check_utf8 + * + * This routine merely applies some sanity-checking to the + * UTF8-encoded OID. + */ + +static PRStatus +oid_sanity_check_utf8 +( + NSSUTF8 *s +) +{ + /* + * It may begin with an octothorpe, which we skip. + */ + + if( '#' == *s ) { + s++; + } + + /* + * It begins with a number + */ + + if( (*s < '0') || (*s > '9') ) { + return PR_FAILURE; + } + + /* + * First number is only one digit long + * + * XXX This is a "soft check" -- we may want to exclude it + */ + + if( (s[1] != '.') && (s[1] != '\0') ) { + return PR_FAILURE; + } + + /* + * Every character is either a digit or a period + */ + + for( ; '\0' != *s; s++ ) { + if( ('.' != *s) && ((*s < '0') || (*s > '9')) ) { + return PR_FAILURE; + } + + /* No two consecutive periods */ + if( ('.' == *s) && ('.' == s[1]) ) { + return PR_FAILURE; + } + } + + /* + * The last character isn't a period + */ + + if( '.' == *--s ) { + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +static PRUint32 +oid_encode_number +( + PRUint32 n, + PRUint8 *dp, + PRUint32 nb +) +{ + PRUint32 a[5]; + PRUint32 i; + PRUint32 rv; + + a[0] = (n >> 28) & 0x7f; + a[1] = (n >> 21) & 0x7f; + a[2] = (n >> 14) & 0x7f; + a[3] = (n >> 7) & 0x7f; + a[4] = n & 0x7f; + + for( i = 0; i < 5; i++ ) { + if( 0 != a[i] ) { + break; + } + } + + if( 5 == i ) { + i--; + } + + rv = 5-i; + if( rv > nb ) { + return rv; + } + + for( ; i < 4; i++ ) { + *dp = 0x80 | a[i]; + dp++; + } + + *dp = a[4]; + + return rv; +} + +/* + * oid_encode_huge + * + * This routine will convert a huge decimal number into the DER + * encoding for oid numbers. It is not limited to numbers that will + * fit into some wordsize, like oid_encode_number. But it's not + * necessarily very fast, either. This is here in case some joker + * throws us an ASCII oid like 1.2.3.99999999999999999999999999. + */ + +static PRUint32 +oid_encode_huge +( + NSSUTF8 *s, + NSSUTF8 *e, + PRUint8 *dp, + PRUint32 nb +) +{ + PRUint32 slen = (e-s); + PRUint32 blen = (slen+1)/2; + PRUint8 *st = (PRUint8 *)NULL; + PRUint8 *bd = (PRUint8 *)NULL; + PRUint32 i; + PRUint32 bitno; + PRUint8 *last; + PRUint8 *first; + PRUint32 byteno; + PRUint8 mask; + + /* We'll be munging the data, so duplicate it */ + st = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, slen); + if( (PRUint8 *)NULL == st ) { + return 0; + } + + /* Don't know ahead of time exactly how long we'll need */ + bd = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, blen); + if( (PRUint8 *)NULL == bd ) { + (void)nss_ZFreeIf(st); + return 0; + } + + /* Copy the original, and convert ASCII to numbers */ + for( i = 0; i < slen; i++ ) { + st[i] = (PRUint8)(s[i] - '0'); + } + + last = &st[slen-1]; + first = &st[0]; + + /* + * The way we create the binary version is by looking at it + * bit by bit. Start with the least significant bit. If the + * number is odd, set that bit. Halve the number (with integer + * division), and go to the next least significant bit. Keep + * going until the number goes to zero. + */ + for( bitno = 0; ; bitno++ ) { + PRUint8 *d; + + byteno = bitno/7; + mask = (PRUint8)(1 << (bitno%7)); + + /* Skip leading zeroes */ + for( ; first < last; first ++ ) { + if( 0 != *first ) { + break; + } + } + + /* Down to one number and it's a zero? Done. */ + if( (first == last) && (0 == *last) ) { + break; + } + + /* Last digit is odd? Set the bit */ + if( *last & 1 ) { + bd[ byteno ] |= mask; + } + + + /* + * Divide the number in half. This is just a matter + * of going from the least significant digit upwards, + * halving each one. If any digit is odd (other than + * the last, which has already been handled), add five + * to the digit to its right. + */ + *last /= 2; + + for( d = &last[-1]; d >= first; d-- ) { + if( *d & 1 ) { + d[1] += 5; + } + + *d /= 2; + } + } + + /* Is there room to write the encoded data? */ + if( (byteno+1) > nb ) { + return (byteno+1); + } + + /* Trim any leading zero that crept in there */ + for( ; byteno > 0; byteno-- ) { + if( 0 != bd[ byteno ] ) { + break; + } + } + + /* Copy all but the last, marking the "continue" bit */ + for( i = 0; i < byteno; i++ ) { + dp[i] = bd[ byteno-i ] | 0x80; + } + /* And the last with the "continue" bit clear */ + dp[byteno] = bd[0]; + + (void)nss_ZFreeIf(bd); + (void)nss_ZFreeIf(st); + return (byteno+1); +} + +/* + * oid_encode_string + * + * This routine converts a dotted-number OID into a DER-encoded + * one. It assumes we've already sanity-checked the string. + */ + +extern const NSSError NSS_ERROR_INTERNAL_ERROR; + +static NSSOID * +oid_encode_string +( + NSSUTF8 *s +) +{ + PRUint32 nn = 0; /* number of numbers */ + PRUint32 nb = 0; /* number of bytes (estimated) */ + NSSUTF8 *t; + PRUint32 nd = 0; /* number of digits */ + NSSOID *rv; + PRUint8 *dp; + PRUint32 a, b; + PRUint32 inc; + + /* Dump any octothorpe */ + if( '#' == *s ) { + s++; + } + + /* Count up the bytes needed */ + for( t = s; '\0' != *t; t++ ) { + if( '.' == *t ) { + nb += (nd+1)/2; /* errs on the big side */ + nd = 0; + nn++; + } else { + nd++; + } + } + nb += (nd+1)/2; + nn++; + + if( 1 == nn ) { + /* + * We have our own "denormalised" encoding for these, + * which is only used internally. + */ + nb++; + } + + /* + * Allocate. Note that we don't use the oid_arena here.. this is + * because there really isn't a "free()" for stuff allocated out of + * arenas (at least with the current implementation), so this would + * keep using up memory each time a UTF8-encoded OID were added. + * If need be (if this is the first time this oid has been seen), + * we'll copy it. + */ + rv = nss_ZNEW((NSSArena *)NULL, NSSOID); + if( (NSSOID *)NULL == rv ) { + return (NSSOID *)NULL; + } + + rv->data.data = nss_ZAlloc((NSSArena *)NULL, nb); + if( (void *)NULL == rv->data.data ) { + (void)nss_ZFreeIf(rv); + return (NSSOID *)NULL; + } + + dp = (PRUint8 *)rv->data.data; + + a = atoi(s); + + if( 1 == nn ) { + dp[0] = '\x80'; + inc = oid_encode_number(a, &dp[1], nb-1); + if( inc >= nb ) { + goto loser; + } + } else { + for( t = s; '.' != *t; t++ ) { + ; + } + + t++; + b = atoi(t); + inc = oid_encode_number(a*40+b, dp, nb); + if( inc > nb ) { + goto loser; + } + dp += inc; + nb -= inc; + nn -= 2; + + while( nn-- > 0 ) { + NSSUTF8 *u; + + for( ; '.' != *t; t++ ) { + ; + } + + t++; + + for( u = t; ('\0' != *u) && ('.' != *u); u++ ) { + ; + } + + if( (u-t > 9) ) { + /* In the billions. Rats. */ + inc = oid_encode_huge(t, u, dp, nb); + } else { + b = atoi(t); + inc = oid_encode_number(b, dp, nb); + } + + if( inc > nb ) { + goto loser; + } + dp += inc; + nb -= inc; + } + } + + return rv; + + loser: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + return (NSSOID *)NULL; +} + +/* + * nssOID_CreateFromUTF8 + * + * This routine creates an NSSOID by decoding a UTF8 string + * representation of an OID in dotted-number format. The string may + * optionally begin with an octothorpe. It may return NULL + * upon error, in which case it will have set an error on the error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_STRING + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * An NSSOID upon success + */ + +NSS_EXTERN NSSOID * +nssOID_CreateFromUTF8 +( + NSSUTF8 *stringOid +) +{ + NSSOID *rv = (NSSOID *)NULL; + NSSOID *candidate = (NSSOID *)NULL; + PLHashEntry *e; + + if( PR_SUCCESS != oid_init() ) { + return (NSSOID *)NULL; + } + + if( PR_SUCCESS != oid_sanity_check_utf8(stringOid) ) { + nss_SetError(NSS_ERROR_INVALID_STRING); + return (NSSOID *)NULL; + } + + candidate = oid_encode_string(stringOid); + if( (NSSOID *)NULL == candidate ) { + /* Internal error only */ + return rv; + } + + /* + * Does it exist? + */ + PR_Lock(oid_hash_lock); + rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, &candidate->data); + (void)PR_Unlock(oid_hash_lock); + if( (NSSOID *)NULL != rv ) { + /* Already exists. Delete my copy and return the original. */ + (void)nss_ZFreeIf(candidate->data.data); + (void)nss_ZFreeIf(candidate); + return rv; + } + + /* + * Nope. Add it. Remember to allocate it out of the oid arena. + */ + + rv = nss_ZNEW(oid_arena, NSSOID); + if( (NSSOID *)NULL == rv ) { + goto loser; + } + + rv->data.data = nss_ZAlloc(oid_arena, candidate->data.size); + if( (void *)NULL == rv->data.data ) { + goto loser; + } + + rv->data.size = candidate->data.size; + nsslibc_memcpy(rv->data.data, candidate->data.data, rv->data.size); + + (void)nss_ZFreeIf(candidate->data.data); + (void)nss_ZFreeIf(candidate); + +#ifdef DEBUG + rv->tag = "<runtime>"; + rv->expl = "(OID registered at runtime)"; +#endif /* DEBUG */ + + PR_Lock(oid_hash_lock); + e = PL_HashTableAdd(oid_hash_table, &rv->data, rv); + (void)PR_Unlock(oid_hash_lock); + if( (PLHashEntry *)NULL == e ) { + nss_SetError(NSS_ERROR_NO_MEMORY); + goto loser; + } + +#ifdef DEBUG + { + PRStatus st; + st = oid_add_pointer(rv); + if( PR_SUCCESS != st ) { + PR_Lock(oid_hash_lock); + (void)PL_HashTableRemove(oid_hash_table, &rv->data); + (void)PR_Unlock(oid_hash_lock); + goto loser; + } + } +#endif /* DEBUG */ + + return rv; + + loser: + if( (NSSOID *)NULL != candidate ) { + (void)nss_ZFreeIf(candidate->data.data); + } + (void)nss_ZFreeIf(candidate); + + if( (NSSOID *)NULL != rv ) { + (void)nss_ZFreeIf(rv->data.data); + } + (void)nss_ZFreeIf(rv); + + return (NSSOID *)NULL; +} + +/* + * nssOID_GetDEREncoding + * + * This routine returns the DER encoding of the specified NSSOID. + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return return null upon error, in + * which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_OID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSOID + */ + +NSS_EXTERN NSSDER * +nssOID_GetDEREncoding +( + const NSSOID *oid, + NSSDER *rvOpt, + NSSArena *arenaOpt +) +{ + const NSSItem *it; + NSSDER *rv; + + if( PR_SUCCESS != oid_init() ) { + return (NSSDER *)NULL; + } + +#ifdef NSSDEBUG + if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { + return (NSSDER *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSDER *)NULL; + } + } +#endif /* NSSDEBUG */ + + it = &oid->data; + + if( (NSSDER *)NULL == rvOpt ) { + rv = nss_ZNEW(arenaOpt, NSSDER); + if( (NSSDER *)NULL == rv ) { + return (NSSDER *)NULL; + } + } else { + rv = rvOpt; + } + + rv->data = nss_ZAlloc(arenaOpt, it->size); + if( (void *)NULL == rv->data ) { + if( rv != rvOpt ) { + (void)nss_ZFreeIf(rv); + } + return (NSSDER *)NULL; + } + + rv->size = it->size; + nsslibc_memcpy(rv->data, it->data, it->size); + + return rv; +} + +/* + * nssOID_GetUTF8Encoding + * + * This routine returns a UTF8 string containing the dotted-number + * encoding of the specified NSSOID. If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return null upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_OID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the dotted-digit encoding of + * this NSSOID + */ + +NSS_EXTERN NSSUTF8 * +nssOID_GetUTF8Encoding +( + const NSSOID *oid, + NSSArena *arenaOpt +) +{ + NSSUTF8 *rv; + PRUint8 *end; + PRUint8 *d; + PRUint8 *e; + char *a; + char *b; + PRUint32 len; + + if( PR_SUCCESS != oid_init() ) { + return (NSSUTF8 *)NULL; + } + +#ifdef NSSDEBUG + if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { + return (NSSUTF8 *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSUTF8 *)NULL; + } + } +#endif /* NSSDEBUG */ + + a = (char *)NULL; + + /* d will point to the next sequence of bytes to decode */ + d = (PRUint8 *)oid->data.data; + /* end points to one past the legitimate data */ + end = &d[ oid->data.size ]; + +#ifdef NSSDEBUG + /* + * Guarantee that the for(e=d;e<end;e++) loop below will + * terminate. Our BER sanity-checking code above will prevent + * such a BER from being registered, so the only other way one + * might show up is if our dotted-decimal encoder above screws + * up or our generated list is wrong. So I'll wrap it with + * #ifdef NSSDEBUG and #endif. + */ + if( end[-1] & 0x80 ) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + return (NSSUTF8 *)NULL; + } +#endif /* NSSDEBUG */ + + /* + * Check for our pseudo-encoded single-digit OIDs + */ + if( (*d == 0x80) && (2 == oid->data.size) ) { + /* Funky encoding. The second byte is the number */ + a = PR_smprintf("%lu", (PRUint32)d[1]); + if( (char *)NULL == a ) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSUTF8 *)NULL; + } + goto done; + } + + for( ; d < end; d = &e[1] ) { + + for( e = d; e < end; e++ ) { + if( 0 == (*e & 0x80) ) { + break; + } + } + + if( ((e-d) > 4) || (((e-d) == 4) && (*d & 0x70)) ) { + /* More than a 32-bit number */ + } else { + PRUint32 n = 0; + + switch( e-d ) { + case 4: + n |= ((PRUint32)(e[-4] & 0x0f)) << 28; + case 3: + n |= ((PRUint32)(e[-3] & 0x7f)) << 21; + case 2: + n |= ((PRUint32)(e[-2] & 0x7f)) << 14; + case 1: + n |= ((PRUint32)(e[-1] & 0x7f)) << 7; + case 0: + n |= ((PRUint32)(e[-0] & 0x7f)) ; + } + + if( (char *)NULL == a ) { + /* This is the first number.. decompose it */ + PRUint32 one = (n/40), two = (n%40); + + a = PR_smprintf("%lu.%lu", one, two); + if( (char *)NULL == a ) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSUTF8 *)NULL; + } + } else { + b = PR_smprintf("%s.%lu", a, n); + if( (char *)NULL == b ) { + PR_smprintf_free(a); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSUTF8 *)NULL; + } + + PR_smprintf_free(a); + a = b; + } + } + } + + done: + /* + * Even if arenaOpt is NULL, we have to copy the data so that + * it'll be freed with the right version of free: ours, not + * PR_smprintf_free's. + */ + len = PL_strlen(a); + rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len); + if( (NSSUTF8 *)NULL == rv ) { + PR_smprintf_free(a); + return (NSSUTF8 *)NULL; + } + + nsslibc_memcpy(rv, a, len); + PR_smprintf_free(a); + + return rv; +} + +/* + * nssOID_getExplanation + * + * This method is only present in debug builds. + * + * This routine will return a static pointer to a UTF8-encoded string + * describing (in English) the specified OID. The memory pointed to + * by the return value is not owned by the caller, and should not be + * freed or modified. Note that explanations are only provided for + * the OIDs built into the NSS library; there is no way to specify an + * explanation for dynamically created OIDs. This routine is intended + * only for use in debugging tools such as "derdump." This routine + * may return null upon error, in which case it will have placed an + * error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * + * Return value: + * NULL upon error + * A static pointer to a readonly, non-caller-owned UTF8-encoded + * string explaining the specified OID. + */ + +#ifdef DEBUG +NSS_EXTERN const NSSUTF8 * +nssOID_getExplanation +( + NSSOID *oid +) +{ + if( PR_SUCCESS != oid_init() ) { + return (const NSSUTF8 *)NULL; + } + +#ifdef NSSDEBUG + if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { + return (NSSUTF8 *)NULL; + } +#endif /* NSSDEBUG */ + + return oid->expl; +} + +extern const NSSError NSS_ERROR_INVALID_NSSOID; +#endif /* DEBUG */ + +/* + * nssOID_getTaggedUTF8 + * + * This method is only present in debug builds. + * + * This routine will return a pointer to a caller-owned UTF8-encoded + * string containing a tagged encoding of the specified OID. Note + * that OID (component) tags are only provided for the OIDs built + * into the NSS library; there is no way to specify tags for + * dynamically created OIDs. This routine is intended for use in + * debugging tools such as "derdump." If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return return null upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the tagged encoding of + * this NSSOID + */ + +#ifdef DEBUG +NSS_EXTERN NSSUTF8 * +nssOID_getTaggedUTF8 +( + NSSOID *oid, + NSSArena *arenaOpt +) +{ + NSSUTF8 *rv; + char *raw; + char *c; + char *a = (char *)NULL; + char *b; + PRBool done = PR_FALSE; + PRUint32 len; + + if( PR_SUCCESS != oid_init() ) { + return (NSSUTF8 *)NULL; + } + +#ifdef NSSDEBUG + if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { + return (NSSUTF8 *)NULL; + } + + if( (NSSArena *)NULL != arenaOpt ) { + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { + return (NSSUTF8 *)NULL; + } + } +#endif /* NSSDEBUG */ + + a = PR_smprintf("{"); + if( (char *)NULL == a ) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSUTF8 *)NULL; + } + + /* + * What I'm doing here is getting the text version of the OID, + * e.g. 1.2.12.92, then looking up each set of leading numbers + * as oids.. e.g. "1," then "1.2," then "1.2.12," etc. Each of + * those will have the leaf tag, and I just build up the string. + * I never said this was the most efficient way of doing it, + * but hey it's a debug-build thing, and I'm getting really tired + * of writing this stupid low-level PKI code. + */ + + /* I know it's all ASCII, so I can use char */ + raw = (char *)nssOID_GetUTF8Encoding(oid, (NSSArena *)NULL); + if( (char *)NULL == raw ) { + return (NSSUTF8 *)NULL; + } + + for( c = raw; !done; c++ ) { + NSSOID *lead; + char *lastdot; + + for( ; '.' != *c; c++ ) { + if( '\0' == *c ) { + done = PR_TRUE; + break; + } + } + + *c = '\0'; + lead = nssOID_CreateFromUTF8((NSSUTF8 *)raw); + if( (NSSOID *)NULL == lead ) { + PR_smprintf_free(a); + nss_ZFreeIf(raw); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSUTF8 *)NULL; + } + + lastdot = PL_strrchr(raw, '.'); + if( (char *)NULL == lastdot ) { + lastdot = raw; + } + + b = PR_smprintf("%s %s(%s) ", a, lead->tag, &lastdot[1]); + if( (char *)NULL == b ) { + PR_smprintf_free(a); + nss_ZFreeIf(raw); + /* drop the OID reference on the floor */ + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSUTF8 *)NULL; + } + + PR_smprintf_free(a); + a = b; + + if( !done ) { + *c = '.'; + } + } + + nss_ZFreeIf(raw); + + b = PR_smprintf("%s }", a); + if( (char *)NULL == b ) { + PR_smprintf_free(a); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSUTF8 *)NULL; + } + + len = PL_strlen(b); + + rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len+1); + if( (NSSUTF8 *)NULL == rv ) { + PR_smprintf_free(b); + return (NSSUTF8 *)NULL; + } + + nsslibc_memcpy(rv, b, len); + PR_smprintf_free(b); + + return rv; +} + +extern const NSSError NSS_ERROR_INVALID_NSSOID; +extern const NSSError NSS_ERROR_NO_MEMORY; +#endif /* DEBUG */ diff --git a/security/nss/lib/pki1/oidgen.perl b/security/nss/lib/pki1/oidgen.perl new file mode 100755 index 000000000..959e3cbaf --- /dev/null +++ b/security/nss/lib/pki1/oidgen.perl @@ -0,0 +1,311 @@ +#!perl +# +# 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. +# +$cvs_id = '@(#) $RCSfile$ $Revision$ $Date$ $Name$'; + +$count = -1; +while(<>) { + s/^((?:[^"#]+|"[^"]*")*)(\s*#.*$)/$1/; + next if (/^\s*$/); + + /^([\S]+)\s+([^"][\S]*|"[^"]*")/; + $name = $1; + $value = $2; + # This is certainly not the best way to dequote the data. + $value =~ s/"//g; + + if( $name =~ "OID" ) { + $count++; + $x[$count]{$name} = $value; + $enc = encodeoid($value); + $x[$count]{" encoding"} = escapeoid($enc); + $x[$count]{" encoding length"} = length($enc); + } else { + if( $count < 0 ) { + $g{$name} = $value; + } else { + $x[$count]{$name} = $value; + } + } +} + +# dodump(); +doprint(); + +sub dodump { +for( $i = 0; $i <= $count; $i++ ) { + print "number $i:\n"; + %y = %{$x[$i]}; + while(($n,$v) = each(%y)) { + print "\t$n ==> $v\n"; + } +} +} + +sub doprint { +open(CFILE, ">oiddata.c") || die "Can't open oiddata.c: $!"; +open(HFILE, ">oiddata.h") || die "Can't open oiddata.h: $!"; + +print CFILE <<EOD +/* THIS IS A GENERATED FILE */ +/* + * 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[] = "$g{CVS_ID} ; $cvs_id"; +#endif /* DEBUG */ + +#ifndef PKI1T_H +#include "pki1t.h" +#endif /* PKI1T_H */ + +const NSSOID nss_builtin_oids[] = { +EOD + ; + +for( $i = 0; $i <= $count; $i++ ) { + %y = %{$x[$i]}; + print CFILE " {\n"; + print CFILE "#ifdef DEBUG\n"; + print CFILE " \"$y{TAG}\",\n"; + print CFILE " \"$y{EXPL}\",\n"; + print CFILE "#endif /* DEBUG */\n"; + print CFILE " { \"", $y{" encoding"}; + print CFILE "\", ", $y{" encoding length"}, " }\n"; + + if( $i == $count ) { + print CFILE " }\n"; + } else { + print CFILE " },\n"; + } +} + +print CFILE "};\n\n"; + +print CFILE "const PRUint32 nss_builtin_oid_count = ", ($count+1), ";\n\n"; + +for( $i = 0; $i <= $count; $i++ ) { + %y = %{$x[$i]}; + if( defined($y{NAME}) ) { + print CFILE "const NSSOID *$y{NAME} = (NSSOID *)&nss_builtin_oids[$i];\n"; + } +} + +print CFILE "\n"; + +$attrcount = -1; +for( $i = 0; $i <= $count; $i++ ) { + %y = %{$x[$i]}; + if( defined($y{ATTR}) ) { + if( defined($y{NAME}) ) { + $attrcount++; + $attr[$attrcount]{ATTR} = $y{ATTR}; + $attr[$attrcount]{NAME} = $y{NAME}; + } else { + warn "Attribute $y{ATTR} has no name, and will be omitted!"; + } + } +} + +print CFILE "const nssAttributeTypeAliasTable nss_attribute_type_aliases[] = {\n"; + +for( $i = 0; $i <= $attrcount; $i++ ) { + %y = %{$attr[$i]}; + print CFILE " {\n"; + print CFILE " \"$y{ATTR}\",\n"; + print CFILE " &$y{NAME}\n"; + + if( $i == $attrcount ) { + print CFILE " }\n"; + } else { + print CFILE " },\n"; + } +} + +print CFILE "};\n\n"; + +print CFILE "const PRUint32 nss_attribute_type_alias_count = ", ($attrcount+1), ";\n\n"; + +print HFILE <<EOD +/* THIS IS A GENERATED FILE */ +/* + * 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 OIDDATA_H +#define OIDDATA_H + +#ifdef DEBUG +static const char OIDDATA_CVS_ID[] = "$g{CVS_ID} ; $cvs_id"; +#endif /* DEBUG */ + +#ifndef NSSPKI1T_H +#include "nsspki1t.h" +#endif /* NSSPKI1T_H */ + +extern const NSSOID nss_builtin_oids[]; +extern const PRUint32 nss_builtin_oid_count; + +/*extern const nssAttributeTypeAliasTable nss_attribute_type_aliases[];*/ +/*extern const PRUint32 nss_attribute_type_alias_count;*/ + +EOD + ; + +for( $i = 0; $i <= $count; $i++ ) { + %y = %{$x[$i]}; + if( defined($y{NAME}) ) { + print HFILE "extern const NSSOID *$y{NAME};\n"; + } +} + +print HFILE <<EOD + +#endif /* OIDDATA_H */ +EOD + ; + +close CFILE; +close HFILE; +} + +sub encodenum { + my $v = $_[0]; + my @d; + my $i; + my $rv = ""; + + while( $v > 128 ) { + push @d, ($v % 128); + $v /= 128; + }; + push @d, ($v%128); + + for( $i = @d-1; $i > 0; $i-- ) { + $rv = $rv . chr(128 + $d[$i]); + } + + $rv = $rv . chr($d[0]); + + return $rv; +} + +sub encodeoid { + my @o = split(/\./, $_[0]); + my $rv = ""; + my $i; + + if( @o < 2 ) { + # NSS's special "illegal" encoding + return chr(128) . encodenum($o[0]); + } + + $rv = encodenum($o[0] * 40 + $o[1]); + shift @o; shift @o; + + foreach $i (@o) { + $rv = $rv . encodenum($i); + } + + return $rv; +} + +sub escapeoid { + my @v = unpack("C*", $_[0]); + my $a; + my $rv = ""; + + foreach $a (@v) { + $rv = $rv . sprintf("\\x%02x", $a); + } + + return $rv; +} diff --git a/security/nss/lib/pki1/oids.txt b/security/nss/lib/pki1/oids.txt new file mode 100644 index 000000000..df7b3ab28 --- /dev/null +++ b/security/nss/lib/pki1/oids.txt @@ -0,0 +1,2115 @@ +# +# 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. +# +CVS_ID "@(#) $RCSfile$ $Revision$ $Date$ $Name$" + +# Fields +# OID -- the OID data itself, in dotted-number format +# TAG -- the unofficial but common name. e.g., when written like +# { joint-iso-ccitt(2) country(16) US(840) company(1) netscape(113730) } +# those words ("joint-iso-ccitt," "country," etc.) are the tags. +# EXPL -- a textual explanation, that should stand by itself +# NAME -- the name we use in our code. If no NAME is given, it won't be included +# +# Additionally, some sets of OIDs map to some other spaces.. +# additional fields capture that data: +# +# CKM -- the corresponding Cryptoki Mechanism, if any +# CKK -- the corresponding Cryptoki Key type, if any +# ATTR -- the UTF-8 attribute type encoding, if applicable +# it should be in the standard presentation style (e.g., lowercase), +# already-escaped a la RFC 2253 if necessary (which would only +# be necessary if some idiot defined an attribute with, say, +# an equals sign in it) +# CERT_EXTENSION -- SUPPORTED or UNSUPPORTED certificate extension, if applicable + +# Top of the OID tree -- see http://www.alvestrand.no/objectid/top.html +# +OID 0 +TAG ccitt # ITU-T used to be called CCITT +EXPL "ITU-T" + +# See X.208 Annex C for an explanation of the OIDs below ccitt/itu-t + +OID 0.0 +TAG recommendation +EXPL "ITU-T Recommendation" + +OID 0.1 +TAG question +EXPL "ITU-T Question" + +OID 0.2 +TAG administration +EXPL "ITU-T Administration" + +OID 0.3 +TAG network-operator +EXPL "ITU-T Network Operator" + +OID 0.4 +TAG identified-organization +EXPL "ITU-T Identified Organization" + +OID 0.9 # used in some RFCs (unofficial!) +TAG data +EXPL "RFC Data" + +OID 0.9.2342 +TAG pss +EXPL "PSS British Telecom X.25 Network" + +OID 0.9.2342.19200300 +TAG ucl +EXPL "RFC 1274 UCL Data networks" + +OID 0.9.2342.19200300.100 +TAG pilot +EXPL "RFC 1274 pilot" + +OID 0.9.2342.19200300.100.1 +TAG attributeType +EXPL "RFC 1274 Attribute Type" + +OID 0.9.2342.19200300.100.1.1 +TAG uid +EXPL "RFC 1274 User Id" +NAME NSS_OID_RFC1274_UID +ATTR "uid" + +OID 0.9.2342.19200300.100.1.3 +TAG mail +EXPL "RFC 1274 E-mail Addres" +NAME NSS_OID_RFC1274_EMAIL +ATTR "mail" # XXX fgmr + +OID 0.9.2342.19200300.100.1.25 +TAG dc +EXPL "RFC 2247 Domain Component" +NAME NSS_OID_RFC2247_DC +ATTR "dc" + +OID 0.9.2342.19200300.100.3 +TAG attributeSyntax +EXPL "RFC 1274 Attribute Syntax" + +OID 0.9.2342.19200300.100.3.4 +TAG iA5StringSyntax +EXPL "RFC 1274 IA5 String Attribute Syntax" + +OID 0.9.2342.19200300.100.3.5 +TAG caseIgnoreIA5StringSyntax +EXPL "RFC 1274 Case-Ignore IA5 String Attribute Syntax" + +OID 0.9.2342.19200300.100.4 +TAG objectClass +EXPL "RFC 1274 Object Class" + +OID 0.9.2342.19200300.100.10 +TAG groups +EXPL "RFC 1274 Groups" + +OID 0.9.2342.234219200300 +TAG ucl +EXPL "RFC 1327 ucl" + +OID 1 +TAG iso +EXPL "ISO" + +# See X.208 Annex B for an explanation of the OIDs below iso + +OID 1.0 +TAG standard +EXPL "ISO Standard" + +OID 1.1 +TAG registration-authority +EXPL "ISO Registration Authority" + +OID 1.2 +TAG member-body +EXPL "ISO Member Body" + +OID 1.2.36 +TAG australia +EXPL "Australia (ISO)" + +OID 1.2.158 +TAG taiwan +EXPL "Taiwan (ISO)" + +OID 1.2.372 +TAG ireland +EXPL "Ireland (ISO)" + +OID 1.2.578 +TAG norway +EXPL "Norway (ISO)" + +OID 1.2.752 +TAG sweden +EXPL "Sweden (ISO)" + +OID 1.2.826 +TAG great-britain +EXPL "Great Britain (ISO)" + +OID 1.2.840 +TAG us +EXPL "United States (ISO)" + +OID 1.2.840.1 +TAG organization +EXPL "US (ISO) organization" + +OID 1.2.840.10003 +TAG ansi-z30-50 +EXPL "ANSI Z39.50" + +OID 1.2.840.10008 +TAG dicom +EXPL "DICOM" + +OID 1.2.840.10017 +TAG ieee-1224 +EXPL "IEEE 1224" + +OID 1.2.840.10022 +TAG ieee-802-10 +EXPL "IEEE 802.10" + +OID 1.2.840.10036 +TAG ieee-802-11 +EXPL "IEEE 802.11" + +OID 1.2.840.10040 +TAG x9-57 +EXPL "ANSI X9.57" + +# RFC 2459: +# +# holdInstruction OBJECT IDENTIFIER ::= +# {iso(1) member-body(2) us(840) x9cm(10040) 2} +# +# Note that the appendices of RFC 2459 define the (wrong) value +# of 2.2.840.10040.2 for this oid. +OID 1.2.840.10040.2 +TAG holdInstruction +EXPL "ANSI X9.57 Hold Instruction" + +# RFC 2459: +# +# id-holdinstruction-none OBJECT IDENTIFIER ::= +# {holdInstruction 1} -- deprecated +OID 1.2.840.10040.2.1 +TAG id-holdinstruction-none +EXPL "ANSI X9.57 Hold Instruction: None" + +# RFC 2459: +# +# id-holdinstruction-callissuer OBJECT IDENTIFIER ::= +# {holdInstruction 2} +OID 1.2.840.10040.2.2 +TAG id-holdinstruction-callissuer +EXPL "ANSI X9.57 Hold Instruction: Call Issuer" + +# RFC 2459: +# +# id-holdinstruction-reject OBJECT IDENTIFIER ::= +# {holdInstruction 3} +OID 1.2.840.10040.2.3 +TAG id-holdinstruction-reject +EXPL "ANSI X9.57 Hold Instruction: Reject" + +OID 1.2.840.10040.4 +TAG x9algorithm +EXPL "ANSI X9.57 Algorithm" + +# RFC 2459: +# +# id-dsa OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 1 } +OID 1.2.840.10040.4.1 +TAG id-dsa +EXPL "ANSI X9.57 DSA Signature" +NAME NSS_OID_ANSIX9_DSA_SIGNATURE +CKM CKM_DSA +CKK CKK_DSA + +# RFC 2459: +# +# id-dsa-with-sha1 OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) x9-57 (10040) x9algorithm(4) 3 } +OID 1.2.840.10040.4.3 +TAG id-dsa-with-sha1 +EXPL "ANSI X9.57 Algorithm DSA Signature with SHA-1 Digest" +NAME NSS_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST +CKM CKM_DSA_SHA1 + +OID 1.2.840.10046 +TAG x942 +EXPL "ANSI X9.42" + +OID 1.2.840.10046.2 +TAG algorithm +EXPL "ANSI X9.42 Algorithm" + +# RFC 2459: +# +# dhpublicnumber OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) ansi-x942(10046) number-type(2) 1 } +OID 1.2.840.10046.2.1 +TAG dhpublicnumber +EXPL "Diffie-Hellman Public Key Algorithm" +NAME NSS_OID_X942_DIFFIE_HELMAN_KEY +CKM CKM_DH_PKCS_DERIVE +CKK CKK_DH + +OID 1.2.840.113533 +TAG entrust +EXPL "Entrust Technologies" + +OID 1.2.840.113549 +TAG rsadsi +EXPL "RSA Data Security Inc." + +OID 1.2.840.113549.1 +# http://www.rsa.com/rsalabs/pubs/PKCS/ +TAG pkcs +EXPL "PKCS" + +# RFC 2459: +# +# pkcs-1 OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } +OID 1.2.840.113549.1.1 +# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-1.asc +TAG pkcs-1 +EXPL "PKCS #1" + +# RFC 2459: +# +# rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } +OID 1.2.840.113549.1.1.1 +TAG rsaEncryption +EXPL "PKCS #1 RSA Encryption" +NAME NSS_OID_PKCS1_RSA_ENCRYPTION +CKM CKM_RSA_PKCS +CKK CKK_RSA + +# RFC 2459: +# +# md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } +OID 1.2.840.113549.1.1.2 +TAG md2WithRSAEncryption +EXPL "PKCS #1 MD2 With RSA Encryption" +NAME NSS_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION +CKM CKM_MD2_RSA_PKCS + +OID 1.2.840.113549.1.1.3 +TAG md4WithRSAEncryption +EXPL "PKCS #1 MD4 With RSA Encryption" +NAME NSS_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION +# No CKM! + +# RFC 2459: +# +# md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } +OID 1.2.840.113549.1.1.4 +TAG md5WithRSAEncryption +EXPL "PKCS #1 MD5 With RSA Encryption" +NAME NSS_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION +CKM CKM_MD5_RSA_PKCS + +# RFC 2459: +# +# sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } +OID 1.2.840.113549.1.1.5 +TAG sha1WithRSAEncryption +EXPL "PKCS #1 SHA-1 With RSA Encryption" +NAME NSS_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION +CKM CKM_SHA1_RSA_PKCS + +OID 1.2.840.113549.1.5 +# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-5.asc +TAG pkcs-5 +EXPL "PKCS #5" + +OID 1.2.840.113549.1.5.1 +TAG pbeWithMD2AndDES-CBC +EXPL "PKCS #5 Password Based Encryption With MD2 and DES-CBC" +NAME NSS_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC +CKM CKM_PBE_MD2_DES_CBC + +OID 1.2.840.113549.1.5.3 +TAG pbeWithMD5AndDES-CBC +EXPL "PKCS #5 Password Based Encryption With MD5 and DES-CBC" +NAME NSS_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC +CKM CKM_PBE_MD5_DES_CBC + +OID 1.2.840.113549.1.5.10 +TAG pbeWithSha1AndDES-CBC +EXPL "PKCS #5 Password Based Encryption With SHA-1 and DES-CBC" +NAME NSS_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC +CKM CKM_NETSCAPE_PBE_SHA1_DES_CBC + +OID 1.2.840.113549.1.7 +# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-7.asc +TAG pkcs-7 +EXPL "PKCS #7" +NAME NSS_OID_PKCS7 + +OID 1.2.840.113549.1.7.1 +TAG data +EXPL "PKCS #7 Data" +NAME NSS_OID_PKCS7_DATA + +OID 1.2.840.113549.1.7.2 +TAG signedData +EXPL "PKCS #7 Signed Data" +NAME NSS_OID_PKCS7_SIGNED_DATA + +OID 1.2.840.113549.1.7.3 +TAG envelopedData +EXPL "PKCS #7 Enveloped Data" +NAME NSS_OID_PKCS7_ENVELOPED_DATA + +OID 1.2.840.113549.1.7.4 +TAG signedAndEnvelopedData +EXPL "PKCS #7 Signed and Enveloped Data" +NAME NSS_OID_PKCS7_SIGNED_ENVELOPED_DATA + +OID 1.2.840.113549.1.7.5 +TAG digestedData +EXPL "PKCS #7 Digested Data" +NAME NSS_OID_PKCS7_DIGESTED_DATA + +OID 1.2.840.113549.1.7.6 +TAG encryptedData +EXPL "PKCS #7 Encrypted Data" +NAME NSS_OID_PKCS7_ENCRYPTED_DATA + +# RFC 2459: +# +# pkcs-9 OBJECT IDENTIFIER ::= +# { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } +OID 1.2.840.113549.1.9 +# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-9.asc +TAG pkcs-9 +EXPL "PKCS #9" + +# RFC 2459: +# +# emailAddress AttributeType ::= { pkcs-9 1 } +OID 1.2.840.113549.1.9.1 +TAG emailAddress +EXPL "PKCS #9 Email Address" +NAME NSS_OID_PKCS9_EMAIL_ADDRESS + +OID 1.2.840.113549.1.9.2 +TAG unstructuredName +EXPL "PKCS #9 Unstructured Name" +NAME NSS_OID_PKCS9_UNSTRUCTURED_NAME + +OID 1.2.840.113549.1.9.3 +TAG contentType +EXPL "PKCS #9 Content Type" +NAME NSS_OID_PKCS9_CONTENT_TYPE + +OID 1.2.840.113549.1.9.4 +TAG messageDigest +EXPL "PKCS #9 Message Digest" +NAME NSS_OID_PKCS9_MESSAGE_DIGEST + +OID 1.2.840.113549.1.9.5 +TAG signingTime +EXPL "PKCS #9 Signing Time" +NAME NSS_OID_PKCS9_SIGNING_TIME + +OID 1.2.840.113549.1.9.6 +TAG counterSignature +EXPL "PKCS #9 Counter Signature" +NAME NSS_OID_PKCS9_COUNTER_SIGNATURE + +OID 1.2.840.113549.1.9.7 +TAG challengePassword +EXPL "PKCS #9 Challenge Password" +NAME NSS_OID_PKCS9_CHALLENGE_PASSWORD + +OID 1.2.840.113549.1.9.8 +TAG unstructuredAddress +EXPL "PKCS #9 Unstructured Address" +NAME NSS_OID_PKCS9_UNSTRUCTURED_ADDRESS + +OID 1.2.840.113549.1.9.9 +TAG extendedCertificateAttributes +EXPL "PKCS #9 Extended Certificate Attributes" +NAME NSS_OID_PKCS9_EXTENDED_CERTIFICATE_ATTRIBUTES + +OID 1.2.840.113549.1.9.15 +TAG sMIMECapabilities +EXPL "PKCS #9 S/MIME Capabilities" +NAME NSS_OID_PKCS9_SMIME_CAPABILITIES + +OID 1.2.840.113549.1.9.20 +TAG friendlyName +EXPL "PKCS #9 Friendly Name" +NAME NSS_OID_PKCS9_FRIENDLY_NAME + +OID 1.2.840.113549.1.9.21 +TAG localKeyID +EXPL "PKCS #9 Local Key ID" +NAME NSS_OID_PKCS9_LOCAL_KEY_ID + +OID 1.2.840.113549.1.9.22 +TAG certTypes +EXPL "PKCS #9 Certificate Types" + +OID 1.2.840.113549.1.9.22.1 +TAG x509Certificate +EXPL "PKCS #9 Certificate Type = X.509" +NAME NSS_OID_PKCS9_X509_CERT + +OID 1.2.840.113549.1.9.22.2 +TAG sdsiCertificate +EXPL "PKCS #9 Certificate Type = SDSI" +NAME NSS_OID_PKCS9_SDSI_CERT + +OID 1.2.840.113549.1.9.23 +TAG crlTypes +EXPL "PKCS #9 Certificate Revocation List Types" + +OID 1.2.840.113549.1.9.23.1 +TAG x509Crl +EXPL "PKCS #9 CRL Type = X.509" +NAME NSS_OID_PKCS9_X509_CRL + +OID 1.2.840.113549.1.12 +# http://www.rsa.com/rsalabs/pubs/PKCS/html/pkcs-12.html +# NOTE -- PKCS #12 multiple contradictory +# documents exist. Somebody go figure out the canonical +# OID list, keeping in mind backwards-compatability.. +TAG pkcs-12 +EXPL "PKCS #12" +NAME NSS_OID_PKCS12 + +OID 1.2.840.113549.1.12.1 +TAG pkcs-12PbeIds +EXPL "PKCS #12 Password Based Encryption IDs" +NAME NSS_OID_PKCS12_PBE_IDS +# We called it SEC_OID_PKCS12_MODE_IDS + +OID 1.2.840.113549.1.12.1.1 +TAG pbeWithSHA1And128BitRC4 +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 128-bit RC4" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4 +CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4 +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4 + +OID 1.2.840.113549.1.12.1.2 +TAG pbeWithSHA1And40BitRC4 +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 40-bit RC4" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4 +CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4 +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4 + +OID 1.2.840.113549.1.12.1.3 +TAG pbeWithSHA1And3-KeyTripleDES-CBC +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 3-key Triple DES-CBC" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_3_KEY_TRIPLE_DES_CBC +CKM CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC + +OID 1.2.840.113549.1.12.1.4 +TAG pbeWithSHA1And2-KeyTripleDES-CBC +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 2-key Triple DES-CBC" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_2_KEY_TRIPLE_DES_CBC +CKM CKM_PBE_SHA1_DES2_EDE_CBC +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC + +OID 1.2.840.113549.1.12.1.5 +TAG pbeWithSHA1And128BitRC2-CBC +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 128-bit RC2-CBC" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC +CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC + +OID 1.2.840.113549.1.12.1.6 +TAG pbeWithSHA1And40BitRC2-CBC +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 40-bit RC2-CBC" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC +CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC + +OID 1.2.840.113549.1.12.2 +TAG pkcs-12EspvkIds +EXPL "PKCS #12 ESPVK IDs" +# We called it SEC_OID_PKCS12_ESPVK_IDS + +OID 1.2.840.113549.1.12.2.1 +TAG pkcs8-key-shrouding +EXPL "PKCS #12 Key Shrouding" +# We called it SEC_OID_PKCS12_PKCS8_KEY_SHROUDING + +OID 1.2.840.113549.1.12.3 +TAG draft1Pkcs-12Bag-ids +EXPL "Draft 1.0 PKCS #12 Bag IDs" +# We called it SEC_OID_PKCS12_BAG_IDS + +OID 1.2.840.113549.1.12.3.1 +TAG keyBag +EXPL "Draft 1.0 PKCS #12 Key Bag" +# We called it SEC_OID_PKCS12_KEY_BAG_ID + +OID 1.2.840.113549.1.12.3.2 +TAG certAndCRLBagId +EXPL "Draft 1.0 PKCS #12 Cert and CRL Bag ID" +# We called it SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID + +OID 1.2.840.113549.1.12.3.3 +TAG secretBagId +EXPL "Draft 1.0 PKCS #12 Secret Bag ID" +# We called it SEC_OID_PKCS12_SECRET_BAG_ID + +OID 1.2.840.113549.1.12.3.4 +TAG safeContentsId +EXPL "Draft 1.0 PKCS #12 Safe Contents Bag ID" +# We called it SEC_OID_PKCS12_SAFE_CONTENTS_ID + +OID 1.2.840.113549.1.12.3.5 +TAG pkcs-8ShroudedKeyBagId +EXPL "Draft 1.0 PKCS #12 PKCS #8-shrouded Key Bag ID" +# We called it SEC_OID_PKCS12_PKCS8_SHROUDED_KEY_BAG_ID + +OID 1.2.840.113549.1.12.4 +TAG pkcs-12CertBagIds +EXPL "PKCS #12 Certificate Bag IDs" +# We called it SEC_OID_PKCS12_CERT_BAG_IDS + +OID 1.2.840.113549.1.12.4.1 +TAG x509CertCRLBagId +EXPL "PKCS #12 X.509 Certificate and CRL Bag" +# We called it SEC_OID_PKCS12_X509_CERT_CRL_BAG + +OID 1.2.840.113549.1.12.4.2 +TAG SDSICertBagID +EXPL "PKCS #12 SDSI Certificate Bag" +# We called it SEC_OID_PKCS12_SDSI_CERT_BAG + +OID 1.2.840.113549.1.12.5 +TAG pkcs-12Oids +EXPL "PKCS #12 OIDs (XXX)" +# We called it SEC_OID_PKCS12_OIDS + +OID 1.2.840.113549.1.12.5.1 +TAG pkcs-12PbeIds +EXPL "PKCS #12 OIDs PBE IDs (XXX)" +# We called it SEC_OID_PKCS12_PBE_IDS + +OID 1.2.840.113549.1.12.5.1.1 +TAG pbeWithSha1And128BitRC4 +EXPL "PKCS #12 OIDs PBE with SHA-1 and 128-bit RC4 (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4 +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4 + +OID 1.2.840.113549.1.12.5.1.2 +TAG pbeWithSha1And40BitRC4 +EXPL "PKCS #12 OIDs PBE with SHA-1 and 40-bit RC4 (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4 +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4 + +OID 1.2.840.113549.1.12.5.1.3 +TAG pbeWithSha1AndTripleDES-CBC +EXPL "PKCS #12 OIDs PBE with SHA-1 and Triple DES-CBC (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC + +OID 1.2.840.113549.1.12.5.1.4 +TAG pbeWithSha1And128BitRC2-CBC +EXPL "PKCS #12 OIDs PBE with SHA-1 and 128-bit RC2-CBC (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC + +OID 1.2.840.113549.1.12.5.1.5 +TAG pbeWithSha1And40BitRC2-CBC +EXPL "PKCS #12 OIDs PBE with SHA-1 and 40-bit RC2-CBC (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC + +OID 1.2.840.113549.1.12.5.2 +TAG pkcs-12EnvelopingIds +EXPL "PKCS #12 OIDs Enveloping IDs (XXX)" +# We called it SEC_OID_PKCS12_ENVELOPING_IDS + +OID 1.2.840.113549.1.12.5.2.1 +TAG rsaEncryptionWith128BitRC4 +EXPL "PKCS #12 OIDs Enveloping RSA Encryption with 128-bit RC4" +# We called it SEC_OID_PKCS12_RSA_ENCRYPTION_WITH_128_BIT_RC4 + +OID 1.2.840.113549.1.12.5.2.2 +TAG rsaEncryptionWith40BitRC4 +EXPL "PKCS #12 OIDs Enveloping RSA Encryption with 40-bit RC4" +# We called it SEC_OID_PKCS12_RSA_ENCRYPTION_WITH_40_BIT_RC4 + +OID 1.2.840.113549.1.12.5.2.3 +TAG rsaEncryptionWithTripleDES +EXPL "PKCS #12 OIDs Enveloping RSA Encryption with Triple DES" +# We called it SEC_OID_PKCS12_RSA_ENCRYPTION_WITH_TRIPLE_DES + +OID 1.2.840.113549.1.12.5.3 +TAG pkcs-12SignatureIds +EXPL "PKCS #12 OIDs Signature IDs (XXX)" +# We called it SEC_OID_PKCS12_SIGNATURE_IDS + +OID 1.2.840.113549.1.12.5.3.1 +TAG rsaSignatureWithSHA1Digest +EXPL "PKCS #12 OIDs RSA Signature with SHA-1 Digest" +# We called it SEC_OID_PKCS12_RSA_SIGNATURE_WITH_SHA1_DIGEST + +OID 1.2.840.113549.1.12.10 +TAG pkcs-12Version1 +EXPL "PKCS #12 Version 1" + +OID 1.2.840.113549.1.12.10.1 +TAG pkcs-12BagIds +EXPL "PKCS #12 Bag IDs" + +OID 1.2.840.113549.1.12.10.1.1 +TAG keyBag +EXPL "PKCS #12 Key Bag" +NAME NSS_OID_PKCS12_KEY_BAG +# We called it SEC_OID_PKCS12_V1_KEY_BAG_ID + +OID 1.2.840.113549.1.12.10.1.2 +TAG pkcs-8ShroudedKeyBag +EXPL "PKCS #12 PKCS #8-shrouded Key Bag" +NAME NSS_OID_PKCS12_PKCS8_SHROUDED_KEY_BAG +# We called it SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID + +OID 1.2.840.113549.1.12.10.1.3 +TAG certBag +EXPL "PKCS #12 Certificate Bag" +NAME NSS_OID_PKCS12_CERT_BAG +# We called it SEC_OID_PKCS12_V1_CERT_BAG_ID + +OID 1.2.840.113549.1.12.10.1.4 +TAG crlBag +EXPL "PKCS #12 CRL Bag" +NAME NSS_OID_PKCS12_CRL_BAG +# We called it SEC_OID_PKCS12_V1_CRL_BAG_ID + +OID 1.2.840.113549.1.12.10.1.5 +TAG secretBag +EXPL "PKCS #12 Secret Bag" +NAME NSS_OID_PKCS12_SECRET_BAG +# We called it SEC_OID_PKCS12_V1_SECRET_BAG_ID + +OID 1.2.840.113549.1.12.10.1.6 +TAG safeContentsBag +EXPL "PKCS #12 Safe Contents Bag" +NAME NSS_OID_PKCS12_SAFE_CONTENTS_BAG +# We called it SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID + +OID 1.2.840.113549.2 +TAG digest +EXPL "RSA digest algorithm" + +OID 1.2.840.113549.2.2 +TAG md2 +EXPL "MD2" +NAME NSS_OID_MD2 +CKM CKM_MD2 + +OID 1.2.840.113549.2.4 +TAG md4 +EXPL "MD4" +NAME NSS_OID_MD4 +# No CKM + +OID 1.2.840.113549.2.5 +TAG md5 +EXPL "MD5" +NAME NSS_OID_MD5 +CKM CKM_MD5 + +OID 1.2.840.113549.3 +TAG cipher +EXPL "RSA cipher algorithm" + +OID 1.2.840.113549.3.2 +TAG rc2cbc +EXPL "RC2-CBC" +NAME NSS_OID_RC2_CBC +CKM_RC2_CBC + +OID 1.2.840.113549.3.4 +TAG rc4 +EXPL "RC4" +NAME NSS_OID_RC4 +CKM CKM_RC4 + +OID 1.2.840.113549.3.7 +TAG desede3cbc +EXPL "DES-EDE3-CBC" +NAME NSS_OID_DES_EDE3_CBC +CKM CKM_DES3_CBC + +OID 1.2.840.113549.3.9 +TAG rc5cbcpad +EXPL "RC5-CBCPad" +NAME NSS_OID_RC5_CBC_PAD +CKM CKM_RC5_CBC + +OID 1.2.840.113556 +TAG microsoft +EXPL "Microsoft" + +OID 1.2.840.113560 +TAG columbia-university +EXPL "Columbia University" + +OID 1.2.840.113572 +TAG unisys +EXPL "Unisys" + +OID 1.2.840.113658 +TAG xapia +EXPL "XAPIA" + +OID 1.2.840.113699 +TAG wordperfect +EXPL "WordPerfect" + +OID 1.3 +TAG identified-organization +EXPL "ISO identified organizations" + +OID 1.3.6 +TAG us-dod +EXPL "United States Department of Defense" + +OID 1.3.6.1 +TAG internet # See RFC 1065 +EXPL "The Internet" + +OID 1.3.6.1.1 +TAG directory +EXPL "Internet: Directory" + +OID 1.3.6.1.2 +TAG management +EXPL "Internet: Management" + +OID 1.3.6.1.3 +TAG experimental +EXPL "Internet: Experimental" + +OID 1.3.6.1.4 +TAG private +EXPL "Internet: Private" + +OID 1.3.6.1.5 +TAG security +EXPL "Internet: Security" + +OID 1.3.6.1.5.5 + +# RFC 2459: +# +# id-pkix OBJECT IDENTIFIER ::= +# { iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) } +OID 1.3.6.1.5.5.7 +TAG id-pkix +EXPL "Public Key Infrastructure" + +# RFC 2459: +# +# PKIX1Explicit88 {iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit-88(1)} +OID 1.3.6.1.5.5.7.0.1 +TAG PKIX1Explicit88 +EXPL "RFC 2459 Explicitly Tagged Module, 1988 Syntax" + +# RFC 2459: +# +# PKIX1Implicit88 {iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit-88(2)} +OID 1.3.6.1.5.5.7.0.2 +TAG PKIXImplicit88 +EXPL "RFC 2459 Implicitly Tagged Module, 1988 Syntax" + +# RFC 2459: +# +# PKIX1Explicit93 {iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit-93(3)} +OID 1.3.6.1.5.5.7.0.3 +TAG PKIXExplicit93 +EXPL "RFC 2459 Explicitly Tagged Module, 1993 Syntax" + +# RFC 2459: +# +# id-pe OBJECT IDENTIFIER ::= { id-pkix 1 } +# -- arc for private certificate extensions +OID 1.3.6.1.5.5.7.1 +TAG id-pe +EXPL "PKIX Private Certificate Extensions" + +# RFC 2459: +# +# id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } +OID 1.3.6.1.5.5.7.1.1 +TAG id-pe-authorityInfoAccess +EXPL "Certificate Authority Information Access" +NAME NSS_OID_X509_AUTH_INFO_ACCESS +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } +# -- arc for policy qualifier types +OID 1.3.6.1.5.5.7.2 +TAG id-qt +EXPL "PKIX Policy Qualifier Types" + +# RFC 2459: +# +# id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } +# -- OID for CPS qualifier +OID 1.3.6.1.5.5.7.2.1 +TAG id-qt-cps +EXPL "PKIX CPS Pointer Qualifier" +NAME NSS_OID_PKIX_CPS_POINTER_QUALIFIER + +# RFC 2459: +# +# id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } +# -- OID for user notice qualifier +OID 1.3.6.1.5.5.7.2.2 +TAG id-qt-unotice +EXPL "PKIX User Notice Qualifier" +NAME NSS_OID_PKIX_USER_NOTICE_QUALIFIER + +# RFC 2459: +# +# id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } +# -- arc for extended key purpose OIDS +OID 1.3.6.1.5.5.7.3 +TAG id-kp +EXPL "PKIX Key Purpose" + +# RFC 2459: +# +# id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } +OID 1.3.6.1.5.5.7.3.1 +TAG id-kp-serverAuth +EXPL "TLS Web Server Authentication Certificate" +NAME NSS_OID_EXT_KEY_USAGE_SERVER_AUTH + +# RFC 2459: +# +# id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } +OID 1.3.6.1.5.5.7.3.2 +TAG id-kp-clientAuth +EXPL "TLS Web Client Authentication Certificate" +NAME NSS_OID_EXT_KEY_USAGE_CLIENT_AUTH + +# RFC 2459: +# +# id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } +OID 1.3.6.1.5.5.7.3.3 +TAG id-kp-codeSigning +EXPL "Code Signing Certificate" +NAME NSS_OID_EXT_KEY_USAGE_CODE_SIGN + +# RFC 2459: +# +# id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } +OID 1.3.6.1.5.5.7.3.4 +TAG id-kp-emailProtection +EXPL "E-Mail Protection Certificate" +NAME NSS_OID_EXT_KEY_USAGE_EMAIL_PROTECTION + +# RFC 2459: +# +# id-kp-ipsecEndSystem OBJECT IDENTIFIER ::= { id-kp 5 } +OID 1.3.6.1.5.5.7.3.5 +TAG id-kp-ipsecEndSystem +EXPL "IPSEC End System Certificate" +NAME NSS_OID_EXT_KEY_USAGE_IPSEC_END_SYSTEM + +# RFC 2459: +# +# id-kp-ipsecTunnel OBJECT IDENTIFIER ::= { id-kp 6 } +OID 1.3.6.1.5.5.7.3.6 +TAG id-kp-ipsecTunnel +EXPL "IPSEC Tunnel Certificate" +NAME NSS_OID_EXT_KEY_USAGE_IPSEC_TUNNEL + +# RFC 2459: +# +# id-kp-ipsecUser OBJECT IDENTIFIER ::= { id-kp 7 } +OID 1.3.6.1.5.5.7.3.7 +TAG id-kp-ipsecUser +EXPL "IPSEC User Certificate" +NAME NSS_OID_EXT_KEY_USAGE_IPSEC_USER + +# RFC 2459: +# +# id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } +OID 1.3.6.1.5.5.7.3.8 +TAG id-kp-timeStamping +EXPL "Time Stamping Certificate" +NAME NSS_OID_EXT_KEY_USAGE_TIME_STAMP + +OID 1.3.6.1.5.5.7.3.9 +TAG ocsp-responder +EXPL "OCSP Responder Certificate" +NAME NSS_OID_OCSP_RESPONDER + +OID 1.3.6.1.5.5.7.7 +TAG pkix-id-pkix + +OID 1.3.6.1.5.5.7.7.5 +TAG pkix-id-pkip + +OID 1.3.6.1.5.5.7.7.5.1 +TAG pkix-id-regctrl +EXPL "CRMF Registration Control" + +OID 1.3.6.1.5.5.7.7.5.1.1 +TAG regtoken +EXPL "CRMF Registration Control, Registration Token" +NAME NSS_OID_PKIX_REGCTRL_REGTOKEN + +OID 1.3.6.1.5.5.7.7.5.1.2 +TAG authenticator +EXPL "CRMF Registration Control, Registration Authenticator" +NAME NSS_OID_PKIX_REGCTRL_AUTHENTICATOR + +OID 1.3.6.1.5.5.7.7.5.1.3 +TAG pkipubinfo +EXPL "CRMF Registration Control, PKI Publication Info" +NAME NSS_OID_PKIX_REGCTRL_PKIPUBINFO + +OID 1.3.6.1.5.5.7.7.5.1.4 +TAG pki-arch-options +EXPL "CRMF Registration Control, PKI Archive Options" +NAME NSS_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS + +OID 1.3.6.1.5.5.7.7.5.1.5 +TAG old-cert-id +EXPL "CRMF Registration Control, Old Certificate ID" +NAME NSS_OID_PKIX_REGCTRL_OLD_CERT_ID + +OID 1.3.6.1.5.5.7.7.5.1.6 +TAG protocol-encryption-key +EXPL "CRMF Registration Control, Protocol Encryption Key" +NAME NSS_OID_PKIX_REGCTRL_PROTOCOL_ENC_KEY + +OID 1.3.6.1.5.5.7.7.5.2 +TAG pkix-id-reginfo +EXPL "CRMF Registration Info" + +OID 1.3.6.1.5.5.7.7.5.2.1 +TAG utf8-pairs +EXPL "CRMF Registration Info, UTF8 Pairs" +NAME NSS_OID_PKIX_REGINFO_UTF8_PAIRS + +OID 1.3.6.1.5.5.7.7.5.2.2 +TAG cert-request +EXPL "CRMF Registration Info, Certificate Request" +NAME NSS_OID_PKIX_REGINFO_CERT_REQUEST + +# RFC 2549: +# +# id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } +# -- arc for access descriptors +OID 1.3.6.1.5.5.7.48 +TAG id-ad +EXPL "PKIX Access Descriptors" + +# RFC 2549: +# +# id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } +OID 1.3.6.1.5.5.7.48.1 +TAG id-ad-ocsp +EXPL "PKIX Online Certificate Status Protocol" +NAME NSS_OID_OID_PKIX_OCSP + +OID 1.3.6.1.5.5.7.48.1.1 +TAG basic-response +EXPL "OCSP Basic Response" +NAME NSS_OID_PKIX_OCSP_BASIC_RESPONSE + +OID 1.3.6.1.5.5.7.48.1.2 +TAG nonce-extension +EXPL "OCSP Nonce Extension" +NAME NSS_OID_PKIX_OCSP_NONCE + +OID 1.3.6.1.5.5.7.48.1.3 +TAG response +EXPL "OCSP Response Types Extension" +NAME NSS_OID_PKIX_OCSP_RESPONSE + +OID 1.3.6.1.5.5.7.48.1.4 +TAG crl +EXPL "OCSP CRL Reference Extension" +NAME NSS_OID_PKIX_OCSP_CRL + +OID 1.3.6.1.5.5.7.48.1.5 +TAG no-check +EXPL "OCSP No Check Extension" +NAME NSS_OID_X509_OCSP_NO_CHECK # X509_... ? + +OID 1.3.6.1.5.5.7.48.1.6 +TAG archive-cutoff +EXPL "OCSP Archive Cutoff Extension" +NAME NSS_OID_PKIX_OCSP_ARCHIVE_CUTOFF + +OID 1.3.6.1.5.5.7.48.1.7 +TAG service-locator +EXPL "OCSP Service Locator Extension" +NAME NSS_OID_PKIX_OCSP_SERVICE_LOCATOR + +# RFC 2549: +# +# id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } +OID 1.3.6.1.5.5.7.48.2 +TAG id-ad-caIssuers +EXPL "Certificate Authority Issuers" + +OID 1.3.6.1.6 +TAG snmpv2 +EXPL "Internet: SNMPv2" + +OID 1.3.6.1.7 +TAG mail +EXPL "Internet: mail" + +OID 1.3.6.1.7.1 +TAG mime-mhs +EXPL "Internet: mail MIME mhs" + +OID 1.3.12 +TAG ecma +EXPL "European Computers Manufacturing Association" + +OID 1.3.14 +TAG oiw +EXPL "Open Systems Implementors Workshop" + +OID 1.3.14.3 secsig +TAG secsig +EXPL "Open Systems Implementors Workshop Security Special Interest Group" + +OID 1.3.14.3.1 +TAG oIWSECSIGAlgorithmObjectIdentifiers +EXPL "OIW SECSIG Algorithm OIDs" + +OID 1.3.14.3.2 +TAG algorithm +EXPL "OIW SECSIG Algorithm" + +OID 1.3.14.3.2.6 +TAG desecb +EXPL "DES-ECB" +NAME NSS_OID_DES_ECB +CKM CKM_DES_ECB + +OID 1.3.14.3.2.7 +TAG descbc +EXPL "DES-CBC" +NAME NSS_OID_DES_CBC +CKM CKM_DES_CBC + +OID 1.3.14.3.2.8 +TAG desofb +EXPL "DES-OFB" +NAME NSS_OID_DES_OFB +# No CKM.. + +OID 1.3.14.3.2.9 +TAG descfb +EXPL "DES-CFB" +NAME NSS_OID_DES_CFB +# No CKM.. + +OID 1.3.14.3.2.10 +TAG desmac +EXPL "DES-MAC" +NAME NSS_OID_DES_MAC +CKM CKM_DES_MAC + +OID 1.3.14.3.2.15 +TAG isoSHAWithRSASignature +EXPL "ISO SHA with RSA Signature" +NAME NSS_OID_ISO_SHA_WITH_RSA_SIGNATURE +# No CKM.. + +OID 1.3.14.3.2.17 +TAG desede +EXPL "DES-EDE" +NAME NSS_OID_DES_EDE +# No CKM.. + +OID 1.3.14.3.2.26 +TAG sha1 +EXPL "SHA-1" +NAME NSS_OID_SHA1 +CKM CKM_SHA_1 + +OID 1.3.14.3.2.27 +TAG bogusDSASignatureWithSHA1Digest +EXPL "Forgezza DSA Signature with SHA-1 Digest" +NAME NSS_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST +CKM CKM_DSA_SHA1 + +OID 1.3.14.3.3 +TAG authentication-mechanism +EXPL "OIW SECSIG Authentication Mechanisms" + +OID 1.3.14.3.4 +TAG security-attribute +EXPL "OIW SECSIG Security Attributes" + +OID 1.3.14.3.5 +TAG document-definition +EXPL "OIW SECSIG Document Definitions used in security" + +OID 1.3.14.7 +TAG directory-services-sig +EXPL "OIW directory services sig" + +OID 1.3.16 +TAG ewos +EXPL "European Workshop on Open Systems" + +OID 1.3.22 +TAG osf +EXPL "Open Software Foundation" + +OID 1.3.23 +TAG nordunet +EXPL "Nordunet" + +OID 1.3.26 +TAG nato-id-org +EXPL "NATO identified organisation" + +OID 1.3.36 +TAG teletrust +EXPL "Teletrust" + +OID 1.3.52 +TAG smpte +EXPL "Society of Motion Picture and Television Engineers" + +OID 1.3.69 +TAG sita +EXPL "Societe Internationale de Telecommunications Aeronautiques" + +OID 1.3.90 +TAG iana +EXPL "Internet Assigned Numbers Authority" + +OID 1.3.101 +TAG thawte +EXPL "Thawte" + +OID 2 +TAG joint-iso-ccitt +EXPL "Joint ISO/ITU-T assignment" + +OID 2.0 +TAG presentation +EXPL "Joint ISO/ITU-T Presentation" + +OID 2.1 +TAG asn-1 +EXPL "Abstract Syntax Notation One" + +OID 2.2 +TAG acse +EXPL "Association Control" + +OID 2.3 +TAG rtse +EXPL "Reliable Transfer" + +OID 2.4 +TAG rose +EXPL "Remote Operations" + +OID 2.5 +TAG x500 +EXPL "Directory" + +OID 2.5.1 +TAG modules +EXPL "X.500 modules" + +OID 2.5.2 +TAG service-environment +EXPL "X.500 service environment" + +OID 2.5.3 +TAG application-context +EXPL "X.500 application context" + +# RFC 2459: +# +# id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4} +OID 2.5.4 +TAG id-at +EXPL "X.520 attribute types" + +# RFC 2459: +# +# id-at-commonName AttributeType ::= {id-at 3} +OID 2.5.4.3 +TAG id-at-commonName +EXPL "X.520 Common Name" +NAME NSS_OID_X520_COMMON_NAME +ATTR "cn" + +# RFC 2459: +# +# id-at-surname AttributeType ::= {id-at 4} +OID 2.5.4.4 +TAG id-at-surname +EXPL "X.520 Surname" +NAME NSS_OID_X520_SURNAME +ATTR "sn" + +# RFC 2459: +# +# id-at-countryName AttributeType ::= {id-at 6} +OID 2.5.4.6 +TAG id-at-countryName +EXPL "X.520 Country Name" +NAME NSS_OID_X520_COUNTRY_NAME +ATTR "c" + +# RFC 2459: +# +# id-at-localityName AttributeType ::= {id-at 7} +OID 2.5.4.7 +TAG id-at-localityName +EXPL "X.520 Locality Name" +NAME NSS_OID_X520_LOCALITY_NAME +ATTR "l" + +# RFC 2459: +# +# id-at-stateOrProvinceName AttributeType ::= {id-at 8} +OID 2.5.4.8 +TAG id-at-stateOrProvinceName +EXPL "X.520 State or Province Name" +NAME NSS_OID_X520_STATE_OR_PROVINCE_NAME +ATTR "s" + +# RFC 2459: +# +# id-at-organizationName AttributeType ::= {id-at 10} +OID 2.5.4.10 +TAG id-at-organizationName +EXPL "X.520 Organization Name" +NAME NSS_OID_X520_ORGANIZATION_NAME +ATTR "o" + +# RFC 2459: +# +# id-at-organizationalUnitName AttributeType ::= {id-at 11} +OID 2.5.4.11 +TAG id-at-organizationalUnitName +EXPL "X.520 Organizational Unit Name" +NAME NSS_OID_X520_ORGANIZATIONAL_UNIT_NAME +ATTR "ou" + +# RFC 2459: +# +# id-at-title AttributeType ::= {id-at 12} +OID 2.5.4.12 +TAG id-at-title +EXPL "X.520 Title" +NAME NSS_OID_X520_TITLE +ATTR "title" + +# RFC 2459: +# +# id-at-name AttributeType ::= {id-at 41} +OID 2.5.4.41 +TAG id-at-name +EXPL "X.520 Name" +NAME NSS_OID_X520_NAME +ATTR "name" + +# RFC 2459: +# +# id-at-givenName AttributeType ::= {id-at 42} +OID 2.5.4.42 +TAG id-at-givenName +EXPL "X.520 Given Name" +NAME NSS_OID_X520_GIVEN_NAME +ATTR "givenName" + +# RFC 2459: +# +# id-at-initials AttributeType ::= {id-at 43} +OID 2.5.4.43 +TAG id-at-initials +EXPL "X.520 Initials" +NAME NSS_OID_X520_INITIALS +ATTR "initials" + +# RFC 2459: +# +# id-at-generationQualifier AttributeType ::= {id-at 44} +OID 2.5.4.44 +TAG id-at-generationQualifier +EXPL "X.520 Generation Qualifier" +NAME NSS_OID_X520_GENERATION_QUALIFIER +ATTR "generationQualifier" + +# RFC 2459: +# +# id-at-dnQualifier AttributeType ::= {id-at 46} +OID 2.5.4.46 +TAG id-at-dnQualifier +EXPL "X.520 DN Qualifier" +NAME NSS_OID_X520_DN_QUALIFIER +ATTR "dnQualifier" + +OID 2.5.5 +TAG attribute-syntax +EXPL "X.500 attribute syntaxes" + +OID 2.5.6 +TAG object-classes +EXPL "X.500 standard object classes" + +OID 2.5.7 +TAG attribute-set +EXPL "X.500 attribute sets" + +OID 2.5.8 +TAG algorithms +EXPL "X.500-defined algorithms" + +OID 2.5.8.1 +TAG encryption +EXPL "X.500-defined encryption algorithms" + +OID 2.5.8.1.1 +TAG rsa +EXPL "RSA Encryption Algorithm" +NAME NSS_OID_X500_RSA_ENCRYPTION +CKM CKM_RSA_X_509 + +OID 2.5.9 +TAG abstract-syntax +EXPL "X.500 abstract syntaxes" + +OID 2.5.12 +TAG operational-attribute +EXPL "DSA Operational Attributes" + +OID 2.5.13 +TAG matching-rule +EXPL "Matching Rule" + +OID 2.5.14 +TAG knowledge-matching-rule +EXPL "X.500 knowledge Matching Rules" + +OID 2.5.15 +TAG name-form +EXPL "X.500 name forms" + +OID 2.5.16 +TAG group +EXPL "X.500 groups" + +OID 2.5.17 +TAG subentry +EXPL "X.500 subentry" + +OID 2.5.18 +TAG operational-attribute-type +EXPL "X.500 operational attribute type" + +OID 2.5.19 +TAG operational-binding +EXPL "X.500 operational binding" + +OID 2.5.20 +TAG schema-object-class +EXPL "X.500 schema Object class" + +OID 2.5.21 +TAG schema-operational-attribute +EXPL "X.500 schema operational attributes" + +OID 2.5.23 +TAG administrative-role +EXPL "X.500 administrative roles" + +OID 2.5.24 +TAG access-control-attribute +EXPL "X.500 access control attribute" + +OID 2.5.25 +TAG ros +EXPL "X.500 ros object" + +OID 2.5.26 +TAG contract +EXPL "X.500 contract" + +OID 2.5.27 +TAG package +EXPL "X.500 package" + +OID 2.5.28 +TAG access-control-schema +EXPL "X.500 access control schema" + +# RFC 2459: +# +# id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} +OID 2.5.29 +TAG id-ce +EXPL "X.500 Certificate Extension" + +OID 2.5.29.5 +TAG subject-directory-attributes +EXPL "Certificate Subject Directory Attributes" +NAME NSS_OID_X509_SUBJECT_DIRECTORY_ATTR +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } +OID 2.5.29.9 +TAG id-ce-subjectDirectoryAttributes +EXPL "Certificate Subject Directory Attributes" +NAME NSS_OID_X509_SUBJECT_DIRECTORY_ATTRIBUTES +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } +OID 2.5.29.14 +TAG id-ce-subjectKeyIdentifier +EXPL "Certificate Subject Key ID" +NAME NSS_OID_X509_SUBJECT_KEY_ID +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } +OID 2.5.29.15 +TAG id-ce-keyUsage +EXPL "Certificate Key Usage" +NAME NSS_OID_X509_KEY_USAGE +CERT_EXTENSION SUPPORTED +# We called it PKCS12_KEY_USAGE + +# RFC 2459: +# +# id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 } +OID 2.5.29.16 +TAG id-ce-privateKeyUsagePeriod +EXPL "Certificate Private Key Usage Period" +NAME NSS_OID_X509_PRIVATE_KEY_USAGE_PERIOD +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } +OID 2.5.29.17 +TAG id-ce-subjectAltName +EXPL "Certificate Subject Alternate Name" +NAME NSS_OID_X509_SUBJECT_ALT_NAME +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } +OID 2.5.29.18 +TAG id-ce-issuerAltName +EXPL "Certificate Issuer Alternate Name" +NAME NSS_OID_X509_ISSUER_ALT_NAME +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } +OID 2.5.29.19 +TAG id-ce-basicConstraints +EXPL "Certificate Basic Constraints" +NAME NSS_OID_X509_BASIC_CONSTRAINTS +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } +OID 2.5.29.20 +TAG id-ce-cRLNumber +EXPL "CRL Number" +NAME NSS_OID_X509_CRL_NUMBER +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 } +OID 2.5.29.21 +TAG id-ce-cRLReasons +EXPL "CRL Reason Code" +NAME NSS_OID_X509_REASON_CODE +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-ce 23 } +OID 2.5.29.23 +TAG id-ce-holdInstructionCode +EXPL "Hold Instruction Code" +NAME NSS_OID_X509_HOLD_INSTRUCTION_CODE +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 } +OID 2.5.29.24 +TAG id-ce-invalidityDate +EXPL "Invalid Date" +NAME NSS_OID_X509_INVALID_DATE +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } +OID 2.5.29.27 +TAG id-ce-deltaCRLIndicator +EXPL "Delta CRL Indicator" +NAME NSS_OID_X509_DELTA_CRL_INDICATOR +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } +OID 2.5.29.28 +TAG id-ce-issuingDistributionPoint +EXPL "Issuing Distribution Point" +NAME NSS_OID_X509_ISSUING_DISTRIBUTION_POINT +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } +OID 2.5.29.29 +TAG id-ce-certificateIssuer +EXPL "Certificate Issuer" +NAME NSS_OID_X509_CERTIFICATE_ISSUER +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } +OID 2.5.29.30 +TAG id-ce-nameConstraints +EXPL "Certificate Name Constraints" +NAME NSS_OID_X509_NAME_CONSTRAINTS +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31} +OID 2.5.29.31 +TAG id-ce-cRLDistributionPoints +EXPL "CRL Distribution Points" +NAME NSS_OID_X509_CRL_DIST_POINTS +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } +OID 2.5.29.32 +TAG id-ce-certificatePolicies +EXPL "Certificate Policies" +NAME NSS_OID_X509_CERTIFICATE_POLICIES +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } +OID 2.5.29.33 +TAG id-ce-policyMappings +EXPL "Certificate Policy Mappings" +NAME NSS_OID_X509_POLICY_MAPPINGS +CERT_EXTENSION UNSUPPORTED + +OID 2.5.29.34 +TAG policy-constraints +EXPL "Certificate Policy Constraints (old)" +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } +OID 2.5.29.35 +TAG id-ce-authorityKeyIdentifier +EXPL "Certificate Authority Key Identifier" +NAME NSS_OID_X509_AUTH_KEY_ID +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } +OID 2.5.29.36 +TAG id-ce-policyConstraints +EXPL "Certificate Policy Constraints" +NAME NSS_OID_X509_POLICY_CONSTRAINTS +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37} +OID 2.5.29.37 +TAG id-ce-extKeyUsage +EXPL "Extended Key Usage" +NAME NSS_OID_X509_EXT_KEY_USAGE +CERT_EXTENSION SUPPORTED + +OID 2.5.30 +TAG id-mgt +EXPL "X.500 Management Object" + +OID 2.6 +TAG x400 +EXPL "X.400 MHS" + +OID 2.7 +TAG ccr +EXPL "Committment, Concurrency and Recovery" + +OID 2.8 +TAG oda +EXPL "Office Document Architecture" + +OID 2.9 +TAG osi-management +EXPL "OSI management" + +OID 2.10 +TAG tp +EXPL "Transaction Processing" + +OID 2.11 +TAG dor +EXPL "Distinguished Object Reference" + +OID 2.12 +TAG rdt +EXPL "Referenced Data Transfer" + +OID 2.13 +TAG nlm +EXPL "Network Layer Management" + +OID 2.14 +TAG tlm +EXPL "Transport Layer Management" + +OID 2.15 +TAG llm +EXPL "Link Layer Management" + +OID 2.16 +TAG country +EXPL "Country Assignments" + +OID 2.16.124 +TAG canada +EXPL "Canada" + +OID 2.16.158 +TAG taiwan +EXPL "Taiwan" + +OID 2.16.578 +TAG norway +EXPL "Norway" + +OID 2.16.756 +TAG switzerland +EXPL "Switzerland" + +OID 2.16.840 +TAG us +EXPL "United States" + +OID 2.16.840.1 +TAG us-company +EXPL "United States Company" + +OID 2.16.840.1.101 +TAG us-government +EXPL "United States Government (1.101)" + +OID 2.16.840.1.101.2 +TAG us-dod +EXPL "United States Department of Defense" + +OID 2.16.840.1.101.2.1 +TAG id-infosec +EXPL "US DOD Infosec" + +OID 2.16.840.1.101.2.1.0 +TAG id-modules +EXPL "US DOD Infosec modules" + +OID 2.16.840.1.101.2.1.1 +TAG id-algorithms +EXPL "US DOD Infosec algorithms (MISSI)" + +OID 2.16.840.1.101.2.1.1.2 +TAG old-dss +EXPL "MISSI DSS Algorithm (Old)" +NAME NSS_OID_MISSI_DSS_OLD + +# This is labeled as "### mwelch temporary" +# Is it official?? XXX fgmr +OID 2.16.840.1.101.2.1.1.4 +TAG skipjack-cbc-64 +EXPL "Skipjack CBC64" +NAME NSS_OID_FORTEZZA_SKIPJACK +CKM CKM_SKIPJACK_CBC64 + +OID 2.16.840.1.101.2.1.1.10 +TAG kea +EXPL "MISSI KEA Algorithm" +NAME NSS_OID_MISSI_KEA + +OID 2.16.840.1.101.2.1.1.12 +TAG old-kea-dss +EXPL "MISSI KEA and DSS Algorithm (Old)" +NAME NSS_OID_MISSI_KEA_DSS_OLD + +OID 2.16.840.1.101.2.1.1.19 +TAG dss +EXPL "MISSI DSS Algorithm" +NAME NSS_OID_MISSI_DSS + +OID 2.16.840.1.101.2.1.1.20 +TAG kea-dss +EXPL "MISSI KEA and DSS Algorithm" +NAME NSS_OID_MISSI_KEA_DSS + +OID 2.16.840.1.101.2.1.1.22 +TAG alt-kea +EXPL "MISSI Alternate KEA Algorithm" +NAME NSS_OID_MISSI_ALT_KEY + +OID 2.16.840.1.101.2.1.2 +TAG id-formats +EXPL "US DOD Infosec formats" + +OID 2.16.840.1.101.2.1.3 +TAG id-policy +EXPL "US DOD Infosec policy" + +OID 2.16.840.1.101.2.1.4 +TAG id-object-classes +EXPL "US DOD Infosec object classes" + +OID 2.16.840.1.101.2.1.5 +TAG id-attributes +EXPL "US DOD Infosec attributes" + +OID 2.16.840.1.101.2.1.6 +TAG id-attribute-syntax +EXPL "US DOD Infosec attribute syntax" + +OID 2.16.840.1.113730 +# The Netscape OID space +TAG netscape +EXPL "Netscape Communications Corp." + +OID 2.16.840.1.113730.1 +TAG cert-ext +EXPL "Netscape Cert Extensions" + +OID 2.16.840.1.113730.1.1 +TAG cert-type +EXPL "Certificate Type" +NAME NSS_OID_NS_CERT_EXT_CERT_TYPE +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.2 +TAG base-url +EXPL "Certificate Extension Base URL" +NAME NSS_OID_NS_CERT_EXT_BASE_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.3 +TAG revocation-url +EXPL "Certificate Revocation URL" +NAME NSS_OID_NS_CERT_EXT_REVOCATION_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.4 +TAG ca-revocation-url +EXPL "Certificate Authority Revocation URL" +NAME NSS_OID_NS_CERT_EXT_CA_REVOCATION_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.5 +TAG ca-crl-download-url +EXPL "Certificate Authority CRL Download URL" +NAME NSS_OID_NS_CERT_EXT_CA_CRL_URL +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.6 +TAG ca-cert-url +EXPL "Certificate Authority Certificate Download URL" +NAME NSS_OID_NS_CERT_EXT_CA_CERT_URL +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.7 +TAG renewal-url +EXPL "Certificate Renewal URL" +NAME NSS_OID_NS_CERT_EXT_CERT_RENEWAL_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.8 +TAG ca-policy-url +EXPL "Certificate Authority Policy URL" +NAME NSS_OID_NS_CERT_EXT_CA_POLICY_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.9 +TAG homepage-url +EXPL "Certificate Homepage URL" +NAME NSS_OID_NS_CERT_EXT_HOMEPAGE_URL +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.10 +TAG entity-logo +EXPL "Certificate Entity Logo" +NAME NSS_OID_NS_CERT_EXT_ENTITY_LOGO +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.11 +TAG user-picture +EXPL "Certificate User Picture" +NAME NSS_OID_NS_CERT_EXT_USER_PICTURE +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.12 +TAG ssl-server-name +EXPL "Certificate SSL Server Name" +NAME NSS_OID_NS_CERT_EXT_SSL_SERVER_NAME +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.13 +TAG comment +EXPL "Certificate Comment" +NAME NSS_OID_NS_CERT_EXT_COMMENT +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.14 +TAG thayes +EXPL "" +NAME NSS_OID_NS_CERT_EXT_THAYES +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.2 +TAG data-type +EXPL "Netscape Data Types" + +OID 2.16.840.1.113730.2.1 +TAG gif +EXPL "image/gif" +NAME NSS_OID_NS_TYPE_GIF + +OID 2.16.840.1.113730.2.2 +TAG jpeg +EXPL "image/jpeg" +NAME NSS_OID_NS_TYPE_JPEG + +OID 2.16.840.1.113730.2.3 +TAG url +EXPL "URL" +NAME NSS_OID_NS_TYPE_URL + +OID 2.16.840.1.113730.2.4 +TAG html +EXPL "text/html" +NAME NSS_OID_NS_TYPE_HTML + +OID 2.16.840.1.113730.2.5 +TAG cert-sequence +EXPL "Certificate Sequence" +NAME NSS_OID_NS_TYPE_CERT_SEQUENCE + +OID 2.16.840.1.113730.3 +# The Netscape Directory OID space +TAG directory +EXPL "Netscape Directory" + +OID 2.16.840.1.113730.4 +TAG policy +EXPL "Netscape Policy Type OIDs" + +OID 2.16.840.1.113730.4.1 +TAG export-approved +EXPL "Strong Crypto Export Approved" +NAME NSS_OID_NS_KEY_USAGE_GOVT_APPROVED +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.5 +TAG cert-server +EXPL "Netscape Certificate Server" + +OID 2.16.840.1.113730.5.1 + +OID 2.16.840.1.113730.5.1.1 +TAG recovery-request +EXPL "Netscape Cert Server Recovery Request" +NAME NSS_OID_NETSCAPE_RECOVERY_REQUEST + +OID 2.16.840.1.113730.6 +TAG algs +EXPL "Netscape algorithm OIDs" + +OID 2.16.840.1.113730.6.1 +TAG smime-kea +EXPL "Netscape S/MIME KEA" +NAME NSS_OID_NETSCAPE_SMIME_KEA + +OID 2.16.840.1.113730.7 +TAG name-components +EXPL "Netscape Name Components" + +OID 2.16.840.1.113730.7.1 +TAG nickname +EXPL "Netscape Nickname" +NAME NSS_OID_NETSCAPE_NICKNAME + +OID 2.16.840.1.113733 +TAG verisign +EXPL "Verisign" + +OID 2.16.840.1.113733.1 + +OID 2.16.840.1.113733.1.7 + +OID 2.16.840.1.113733.1.7.1 + +OID 2.16.840.1.113733.1.7.1.1 +TAG verisign-user-notices +EXPL "Verisign User Notices" +NAME NSS_OID_VERISIGN_USER_NOTICES + +OID 2.16.840.101 +TAG us-government +EXPL "US Government (101)" + +OID 2.16.840.102 +TAG us-government2 +EXPL "US Government (102)" + +OID 2.16.840.11370 +TAG old-netscape +EXPL "Netscape Communications Corp. (Old)" + +OID 2.16.840.11370.1 +TAG ns-cert-ext +EXPL "Netscape Cert Extensions (Old NS)" + +OID 2.16.840.11370.1.1 +TAG netscape-ok +EXPL "Netscape says this cert is ok (Old NS)" +NAME NSS_OID_NS_CERT_EXT_NETSCAPE_OK +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.11370.1.2 +TAG issuer-logo +EXPL "Certificate Issuer Logo (Old NS)" +NAME NSS_OID_NS_CERT_EXT_ISSUER_LOGO +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.11370.1.3 +TAG subject-logo +EXPL "Certificate Subject Logo (Old NS)" +NAME NSS_OID_NS_CERT_EXT_SUBJECT_LOGO +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.11370.2 +TAG ns-file-type +EXPL "Netscape File Type" + +OID 2.16.840.11370.3 +TAG ns-image-type +EXPL "Netscape Image Type" + +OID 2.17 +TAG registration-procedures +EXPL "Registration procedures" + +OID 2.18 +TAG physical-layer-management +EXPL "Physical layer Management" + +OID 2.19 +TAG mheg +EXPL "MHEG" + +OID 2.20 +TAG guls +EXPL "Generic Upper Layer Security" + +OID 2.21 +TAG tls +EXPL "Transport Layer Security Protocol" + +OID 2.22 +TAG nls +EXPL "Network Layer Security Protocol" + +OID 2.23 +TAG organization +EXPL "International organizations" diff --git a/security/nss/lib/pki1/pki1.h b/security/nss/lib/pki1/pki1.h new file mode 100644 index 000000000..62049efcc --- /dev/null +++ b/security/nss/lib/pki1/pki1.h @@ -0,0 +1,3032 @@ +/* + * 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 PKI1_H +#define PKI1_H + +#ifdef DEBUG +static const char PKI1_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +/* + * pki1.h + * + * This file contains the prototypes to the non-public NSS routines + * relating to the PKIX part-1 objects. + */ + +#ifndef PKI1T_H +#include "pki1t.h" +#endif /* PKI1T_H */ + +#ifndef NSSPKI1_H +#include "nsspki1.h" +#endif /* NSSPKI1_H */ + +PR_BEGIN_EXTERN_C + +/* fgmr 19990505 moved these here from oiddata.h */ +extern const nssAttributeTypeAliasTable nss_attribute_type_aliases[]; +extern const PRUint32 nss_attribute_type_alias_count; + +/* + * NSSOID + * + * The non-public "methods" regarding this "object" are: + * + * nssOID_CreateFromBER -- constructor + * nssOID_CreateFromUTF8 -- constructor + * (there is no explicit destructor) + * + * nssOID_GetDEREncoding + * nssOID_GetUTF8Encoding + * + * In debug builds, the following non-public calls are also available: + * + * nssOID_verifyPointer + * nssOID_getExplanation + * nssOID_getTaggedUTF8 + */ + +/* + * nssOID_CreateFromBER + * + * This routine creates an NSSOID by decoding a BER- or DER-encoded + * OID. It may return NSS_OID_UNKNOWN upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NSS_OID_UNKNOWN upon error + * An NSSOID upon success + */ + +NSS_EXTERN NSSOID * +nssOID_CreateFromBER +( + NSSBER *berOid +); + +extern const NSSError NSS_ERROR_INVALID_BER; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * nssOID_CreateFromUTF8 + * + * This routine creates an NSSOID by decoding a UTF8 string + * representation of an OID in dotted-number format. The string may + * optionally begin with an octothorpe. It may return NSS_OID_UNKNOWN + * upon error, in which case it will have set an error on the error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_UTF8 + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NSS_OID_UNKNOWN upon error + * An NSSOID upon success + */ + +NSS_EXTERN NSSOID * +nssOID_CreateFromUTF8 +( + NSSUTF8 *stringOid +); + +extern const NSSError NSS_ERROR_INVALID_UTF8; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * nssOID_GetDEREncoding + * + * This routine returns the DER encoding of the specified NSSOID. + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return return null upon error, in + * which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSOID + */ + +NSS_EXTERN NSSDER * +nssOID_GetDEREncoding +( + const NSSOID *oid, + NSSDER *rvOpt, + NSSArena *arenaOpt +); + +/* + * nssOID_GetUTF8Encoding + * + * This routine returns a UTF8 string containing the dotted-number + * encoding of the specified NSSOID. If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return null upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the dotted-digit encoding of + * this NSSOID + */ + +NSS_EXTERN NSSUTF8 * +nssOID_GetUTF8Encoding +( + const NSSOID *oid, + NSSArena *arenaOpt +); + +/* + * nssOID_verifyPointer + * + * This method is only present in debug builds. + * + * If the specified pointer is a valid poitner to an NSSOID object, + * this routine will return PR_SUCCESS. Otherwise, it will put an + * error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * + * Return value: + * PR_SUCCESS if the pointer is valid + * PR_FAILURE if it isn't + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus +nssOID_verifyPointer +( + const NSSOID *oid +); + +extern const NSSError NSS_ERROR_INVALID_NSSOID; +#endif /* DEBUG */ + +/* + * nssOID_getExplanation + * + * This method is only present in debug builds. + * + * This routine will return a static pointer to a UTF8-encoded string + * describing (in English) the specified OID. The memory pointed to + * by the return value is not owned by the caller, and should not be + * freed or modified. Note that explanations are only provided for + * the OIDs built into the NSS library; there is no way to specify an + * explanation for dynamically created OIDs. This routine is intended + * only for use in debugging tools such as "derdump." This routine + * may return null upon error, in which case it will have placed an + * error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSOID + * + * Return value: + * NULL upon error + * A static pointer to a readonly, non-caller-owned UTF8-encoded + * string explaining the specified OID. + */ + +#ifdef DEBUG +NSS_EXTERN const char * +nssOID_getExplanation +( + NSSOID *oid +); + +extern const NSSError NSS_ERROR_INVALID_NSSOID; +#endif /* DEBUG */ + +/* + * nssOID_getTaggedUTF8 + * + * This method is only present in debug builds. + * + * This routine will return a pointer to a caller-owned UTF8-encoded + * string containing a tagged encoding of the specified OID. Note + * that OID (component) tags are only provided for the OIDs built + * into the NSS library; there is no way to specify tags for + * dynamically created OIDs. This routine is intended for use in + * debugging tools such as "derdump." If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return return null upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the tagged encoding of + * this NSSOID + */ + +#ifdef DEBUG +NSS_EXTERN NSSUTF8 * +nssOID_getTaggedUTF8 +( + NSSOID *oid, + NSSArena *arenaOpt +); + +extern const NSSError NSS_ERROR_INVALID_NSSOID; +extern const NSSError NSS_ERROR_NO_MEMORY; +#endif /* DEBUG */ + +/* + * NSSATAV + * + * The non-public "methods" regarding this "object" are: + * + * nssATAV_CreateFromBER -- constructor + * nssATAV_CreateFromUTF8 -- constructor + * nssATAV_Create -- constructor + * + * nssATAV_Destroy + * nssATAV_GetDEREncoding + * nssATAV_GetUTF8Encoding + * nssATAV_GetType + * nssATAV_GetValue + * nssATAV_Compare + * nssATAV_Duplicate + * + * In debug builds, the following non-public call is also available: + * + * nssATAV_verifyPointer + */ + +/* + * nssATAV_CreateFromBER + * + * This routine creates an NSSATAV by decoding a BER- or DER-encoded + * ATAV. If the optional arena argument is non-null, the memory used + * will be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_EXTERN NSSATAV * +nssATAV_CreateFromBER +( + NSSArena *arenaOpt, + const NSSBER *berATAV +); + +/* + * nssATAV_CreateFromUTF8 + * + * This routine creates an NSSATAV by decoding a UTF8 string in the + * "equals" format, e.g., "c=US." If the optional arena argument is + * non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_UNKNOWN_ATTRIBUTE + * NSS_ERROR_INVALID_UTF8 + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_EXTERN NSSATAV * +nssATAV_CreateFromUTF8 +( + NSSArena *arenaOpt, + const NSSUTF8 *stringATAV +); + +/* + * nssATAV_Create + * + * This routine creates an NSSATAV from the specified NSSOID and the + * specified data. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap.If the specified data length is zero, + * the data is assumed to be terminated by first zero byte; this allows + * UTF8 strings to be easily specified. This routine may return NULL + * upon error, in which case it will have set an error on the error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_NSSOID + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSATAV upon success + */ + +NSS_EXTERN NSSATAV * +nssATAV_Create +( + NSSArena *arenaOpt, + const NSSOID *oid, + const void *data, + PRUint32 length +); + +/* + * nssATAV_Destroy + * + * This routine will destroy an ATAV object. It should eventually be + * called on all ATAVs created without an arena. While it is not + * necessary to call it on ATAVs created within an arena, it is not an + * error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * set an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +nssATAV_Destroy +( + NSSATAV *atav +); + +/* + * nssATAV_GetDEREncoding + * + * This routine will DER-encode an ATAV object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSATAV + */ + +NSS_EXTERN NSSDER * +nssATAV_GetDEREncoding +( + NSSATAV *atav, + NSSArena *arenaOpt +); + +/* + * nssATAV_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the ATAV in "equals" notation (e.g., "o=Acme"). + * If the optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return null upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string containing the "equals" encoding of the + * ATAV + */ + +NSS_EXTERN NSSUTF8 * +nssATAV_GetUTF8Encoding +( + NSSATAV *atav, + NSSArena *arenaOpt +); + +/* + * nssATAV_GetType + * + * This routine returns the NSSOID corresponding to the attribute type + * in the specified ATAV. This routine may return NSS_OID_UNKNOWN + * upon error, in which case it will have set an error on the error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * NSS_OID_UNKNOWN upon error + * An element of enum NSSOIDenum upon success + */ + +NSS_EXTERN const NSSOID * +nssATAV_GetType +( + NSSATAV *atav +); + +/* + * nssATAV_GetValue + * + * This routine returns a string containing the attribute value + * in the specified ATAV. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have set an error upon the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSItem containing the attribute value. + */ + +NSS_EXTERN NSSUTF8 * +nssATAV_GetValue +( + NSSATAV *atav, + NSSArena *arenaOpt +); + +/* + * nssATAV_Compare + * + * This routine compares two ATAVs for equality. For two ATAVs to be + * equal, the attribute types must be the same, and the attribute + * values must have equal length and contents. The result of the + * comparison will be stored at the location pointed to by the "equalp" + * variable, which must point to a valid PRBool. This routine may + * return PR_FAILURE upon error, in which case it will have set an + * error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +nssATAV_Compare +( + NSSATAV *atav1, + NSSATAV *atav2, + PRBool *equalp +); + +/* + * nssATAV_Duplicate + * + * This routine duplicates the specified ATAV. If the optional arena + * argument is non-null, the memory required will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * placed an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL on error + * A pointer to a new ATAV + */ + +NSS_EXTERN NSSATAV * +nssATAV_Duplicate +( + NSSATAV *atav, + NSSArena *arenaOpt +); + +/* + * nssATAV_verifyPointer + * + * This method is only present in debug builds. + * + * If the specified pointer is a valid pointer to an NSSATAV object, + * this routine will return PR_SUCCESS. Otherwise, it will put an + * error on the error stack and return PR_FAILRUE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NSSATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS if the pointer is valid + * PR_FAILURE if it isn't + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus +nssATAV_verifyPointer +( + NSSATAV *atav +); +#endif /* DEBUG */ + +/* + * NSSRDN + * + * The non-public "methods" regarding this "object" are: + * + * nssRDN_CreateFromBER -- constructor + * nssRDN_CreateFromUTF8 -- constructor + * nssRDN_Create -- constructor + * nssRDN_CreateSimple -- constructor + * + * nssRDN_Destroy + * nssRDN_GetDEREncoding + * nssRDN_GetUTF8Encoding + * nssRDN_AddATAV + * nssRDN_GetATAVCount + * nssRDN_GetATAV + * nssRDN_GetSimpleATAV + * nssRDN_Compare + * nssRDN_Duplicate + */ + +/* + * nssRDN_CreateFromBER + * + * This routine creates an NSSRDN by decoding a BER- or DER-encoded + * RDN. If the optional arena argument is non-null, the memory used + * will be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSRDN upon success + */ + +NSS_EXTERN NSSRDN * +nssRDN_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berRDN +); + +/* + * nssRDN_CreateFromUTF8 + * + * This routine creates an NSSRDN by decoding an UTF8 string + * consisting of either a single ATAV in the "equals" format, e.g., + * "uid=smith," or one or more such ATAVs in parentheses, e.g., + * "(sn=Smith,ou=Sales)." If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have set an error on the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_UNKNOWN_ATTRIBUTE + * NSS_ERROR_INVALID_UTF8 + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSRDN upon success + */ + +NSS_EXTERN NSSRDN * +nssRDN_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringRDN +); + +/* + * nssRDN_Create + * + * This routine creates an NSSRDN from one or more NSSATAVs. The + * final argument to this routine must be NULL. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * NULL upon error + * A pointer to an NSSRDN upon success + */ + +NSS_EXTERN NSSRDN * +nssRDN_Create +( + NSSArena *arenaOpt, + NSSATAV *atav1, + ... +); + +/* + * nssRDN_CreateSimple + * + * This routine creates a simple NSSRDN from a single NSSATAV. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_INVALID_ATAV + * + * Return value: + * NULL upon error + * A pointer to an NSSRDN upon success + */ + +NSS_EXTERN NSSRDN * +nssRDN_CreateSimple +( + NSSArena *arenaOpt, + NSSATAV *atav +); + +/* + * nssRDN_Destroy + * + * This routine will destroy an RDN object. It should eventually be + * called on all RDNs created without an arena. While it is not + * necessary to call it on RDNs created within an arena, it is not an + * error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * set an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * + * Return value: + * PR_FAILURE upon failure + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +nssRDN_Destroy +( + NSSRDN *rdn +); + +/* + * nssRDN_GetDEREncoding + * + * This routine will DER-encode an RDN object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSRDN + */ + +NSS_EXTERN NSSDER * +nssRDN_GetDEREncoding +( + NSSRDN *rdn, + NSSArena *arenaOpt +); + +/* + * nssRDN_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the RDN. A simple (one-ATAV) RDN will be simply + * the string representation of that ATAV; a non-simple RDN will be in + * parenthesised form. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * null upon error, in which case it will have set an error on the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string + */ + +NSS_EXTERN NSSUTF8 * +nssRDN_GetUTF8Encoding +( + NSSRDN *rdn, + NSSArena *arenaOpt +); + +/* + * nssRDN_AddATAV + * + * This routine adds an ATAV to the set of ATAVs in the specified RDN. + * Remember that RDNs consist of an unordered set of ATAVs. If the + * RDN was created with a non-null arena argument, that same arena + * will be used for any additional required memory. If the RDN was + * created with a NULL arena argument, any additional memory will + * be obtained from the heap. This routine returns a PRStatus value; + * it will return PR_SUCCESS upon success, and upon failure it will + * set an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_INVALID_ATAV + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure + */ + +NSS_EXTERN PRStatus +nssRDN_AddATAV +( + NSSRDN *rdn, + NSSATAV *atav +); + +/* + * nssRDN_GetATAVCount + * + * This routine returns the cardinality of the set of ATAVs within + * the specified RDN. This routine may return 0 upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * + * Return value: + * 0 upon error + * A positive number upon success + */ + +NSS_EXTERN PRUint32 +nssRDN_GetATAVCount +( + NSSRDN *rdn +); + +/* + * nssRDN_GetATAV + * + * This routine returns a pointer to an ATAV that is a member of + * the set of ATAVs within the specified RDN. While the set of + * ATAVs within an RDN is unordered, this routine will return + * distinct values for distinct values of 'i' as long as the RDN + * is not changed in any way. The RDN may be changed by calling + * NSSRDN_AddATAV. The value of the variable 'i' is on the range + * [0,c) where c is the cardinality returned from NSSRDN_GetATAVCount. + * The caller owns the ATAV the pointer to which is returned. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have set an error upon the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_VALUE_OUT_OF_RANGE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSATAV + */ + +NSS_EXTERN NSSATAV * +nssRDN_GetATAV +( + NSSRDN *rdn, + NSSArena *arenaOpt, + PRUint32 i +); + +/* + * nssRDN_GetSimpleATAV + * + * Most RDNs are actually very simple, with a single ATAV. This + * routine will return the single ATAV from such an RDN. The caller + * owns the ATAV the pointer to which is returned. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, including the case where + * the set of ATAVs in the RDN is nonsingular. Upon error, this + * routine will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_RDN_NOT_SIMPLE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSATAV + */ + +NSS_EXTERN NSSATAV * +nssRDN_GetSimpleATAV +( + NSSRDN *rdn, + NSSArena *arenaOpt +); + +/* + * nssRDN_Compare + * + * This routine compares two RDNs for equality. For two RDNs to be + * equal, they must have the same number of ATAVs, and every ATAV in + * one must be equal to an ATAV in the other. (Note that the sets + * of ATAVs are unordered.) The result of the comparison will be + * stored at the location pointed to by the "equalp" variable, which + * must point to a valid PRBool. This routine may return PR_FAILURE + * upon error, in which case it will have set an error on the error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +nssRDN_Compare +( + NSSRDN *rdn1, + NSSRDN *rdn2, + PRBool *equalp +); + +/* + * nssRDN_Duplicate + * + * This routine duplicates the specified RDN. If the optional arena + * argument is non-null, the memory required will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * placed an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL on error + * A pointer to a new RDN + */ + +NSS_EXTERN NSSRDN * +nssRDN_Duplicate +( + NSSRDN *rdn, + NSSArena *arenaOpt +); + +/* + * NSSRDNSeq + * + * The non-public "methods" regarding this "object" are: + * + * nssRDNSeq_CreateFromBER -- constructor + * nssRDNSeq_CreateFromUTF8 -- constructor + * nssRDNSeq_Create -- constructor + * + * nssRDNSeq_Destroy + * nssRDNSeq_GetDEREncoding + * nssRDNSeq_GetUTF8Encoding + * nssRDNSeq_AppendRDN + * nssRDNSeq_GetRDNCount + * nssRDNSeq_GetRDN + * nssRDNSeq_Compare + * nssRDNSeq_Duplicate + * + * nssRDNSeq_EvaluateUTF8 -- not an object method + */ + +/* + * nssRDNSeq_CreateFromBER + * + * This routine creates an NSSRDNSeq by decoding a BER- or DER-encoded + * sequence of RDNs. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have set an error on the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSRDNSeq upon success + */ + +NSS_EXTERN NSSRDNSeq * +nssRDNSeq_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berRDNSeq +); + +/* + * nssRDNSeq_CreateFromUTF8 + * + * This routine creates an NSSRDNSeq by decoding a UTF8 string + * consisting of a comma-separated sequence of RDNs, such as + * "(sn=Smith,ou=Sales),o=Acme,c=US." If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_UNKNOWN_ATTRIBUTE + * NSS_ERROR_INVALID_UTF8 + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSRDNSeq upon success + */ + +NSS_EXTERN NSSRDNSeq * +nssRDNSeq_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringRDNSeq +); + +/* + * nssRDNSeq_Create + * + * This routine creates an NSSRDNSeq from one or more NSSRDNs. The + * final argument to this routine must be NULL. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_INVALID_RDN + * + * Return value: + * NULL upon error + * A pointero to an NSSRDNSeq upon success + */ + +NSS_EXTERN NSSRDNSeq * +nssRDNSeq_Create +( + NSSArena *arenaOpt, + NSSRDN *rdn1, + ... +); + +/* + * nssRDNSeq_Destroy + * + * This routine will destroy an RDNSeq object. It should eventually + * be called on all RDNSeqs created without an arena. While it is not + * necessary to call it on RDNSeqs created within an arena, it is not + * an error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * set an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +nssRDNSeq_Destroy +( + NSSRDNSeq *rdnseq +); + +/* + * nssRDNSeq_GetDEREncoding + * + * This routine will DER-encode an RDNSeq object. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return null upon error, in which case it will have + * set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSRDNSeq + */ + +NSS_EXTERN NSSDER * +nssRDNSeq_GetDEREncoding +( + NSSRDNSeq *rdnseq, + NSSArena *arenaOpt +); + +/* + * nssRDNSeq_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the RDNSeq as a comma-separated sequence of RDNs. + * If the optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return null upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the UTF8 string + */ + +NSS_EXTERN NSSUTF8 * +nssRDNSeq_GetUTF8Encoding +( + NSSRDNSeq *rdnseq, + NSSArena *arenaOpt +); + +/* + * nssRDNSeq_AppendRDN + * + * This routine appends an RDN to the end of the existing RDN + * sequence. If the RDNSeq was created with a non-null arena + * argument, that same arena will be used for any additional required + * memory. If the RDNSeq was created with a NULL arena argument, any + * additional memory will be obtained from the heap. This routine + * returns a PRStatus value; it will return PR_SUCCESS upon success, + * and upon failure it will set an error on the error stack and return + * PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_INVALID_RDN + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure + */ + +NSS_EXTERN PRStatus +nssRDNSeq_AppendRDN +( + NSSRDNSeq *rdnseq, + NSSRDN *rdn +); + +/* + * nssRDNSeq_GetRDNCount + * + * This routine returns the cardinality of the sequence of RDNs within + * the specified RDNSeq. This routine may return 0 upon error, in + * which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * + * Return value: + * 0 upon error + * A positive number upon success + */ + +NSS_EXTERN PRUint32 +nssRDNSeq_GetRDNCount +( + NSSRDNSeq *rdnseq +); + +/* + * nssRDNSeq_GetRDN + * + * This routine returns a pointer to the i'th RDN in the sequence of + * RDNs that make up the specified RDNSeq. The sequence begins with + * the top-level (e.g., "c=US") RDN. The value of the variable 'i' + * is on the range [0,c) where c is the cardinality returned from + * NSSRDNSeq_GetRDNCount. The caller owns the RDN the pointer to which + * is returned. If the optional arena argument is non-null, the memory + * used will be obtained from that areana; otherwise, the memory will + * be obtained from the heap. This routine may return NULL upon error, + * in which case it will have set an error upon the error stack. Note + * that the usual UTF8 representation of RDN Sequences is from last + * to first. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_VALUE_OUT_OF_RANGE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSRDN + */ + +NSS_EXTERN NSSRDN * +nssRDNSeq_GetRDN +( + NSSRDNSeq *rdnseq, + NSSArena *arenaOpt, + PRUint32 i +); + +/* + * nssRDNSeq_Compare + * + * This routine compares two RDNSeqs for equality. For two RDNSeqs to + * be equal, they must have the same number of RDNs, and each RDN in + * one sequence must be equal to the corresponding RDN in the other + * sequence. The result of the comparison will be stored at the + * location pointed to by the "equalp" variable, which must point to a + * valid PRBool. This routine may return PR_FAILURE upon error, in + * which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +nssRDNSeq_Compare +( + NSSRDNSeq *rdnseq1, + NSSRDNSeq *rdnseq2, + PRBool *equalp +); + +/* + * nssRDNSeq_Duplicate + * + * This routine duplicates the specified RDNSeq. If the optional arena + * argument is non-null, the memory required will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * placed an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_RDNSEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a new RDNSeq + */ + +NSS_EXTERN NSSRDNSeq * +nssRDNSeq_Duplicate +( + NSSRDNSeq *rdnseq, + NSSArena *arenaOpt +); + +/* + * nssRDNSeq_EvaluateUTF8 + * + * This routine evaluates a UTF8 string, and returns PR_TRUE if the + * string contains the string representation of an RDNSeq. This + * routine is used by the (directory) Name routines + * nssName_CreateFromUTF8 and nssName_EvaluateUTF8 to determine which + * choice of directory name the string may encode. This routine may + * return PR_FALSE upon error, but it subsumes that condition under the + * general "string does not evaluate as an RDNSeq" state, and does not + * set an error on the error stack. + * + * Return value: + * PR_TRUE if the string represents an RDNSeq + * PR_FALSE if otherwise + */ + +NSS_EXTERN PRBool +nssRDNSeq_EvaluateUTF8 +( + NSSUTF8 *str +); + +/* + * NSSName + * + * The non-public "methods" regarding this "object" are: + * + * nssName_CreateFromBER -- constructor + * nssName_CreateFromUTF8 -- constructor + * nssName_Create -- constructor + * + * nssName_Destroy + * nssName_GetDEREncoding + * nssName_GetUTF8Encoding + * nssName_GetChoice + * nssName_GetRDNSequence + * nssName_GetSpecifiedChoice + * nssName_Compare + * nssName_Duplicate + * + * nssName_GetUID + * nssName_GetEmail + * nssName_GetCommonName + * nssName_GetOrganization + * nssName_GetOrganizationalUnits + * nssName_GetStateOrProvince + * nssName_GetLocality + * nssName_GetCountry + * nssName_GetAttribute + * + * nssName_EvaluateUTF8 -- not an object method + */ + +/* + * nssName_CreateFromBER + * + * This routine creates an NSSName by decoding a BER- or DER-encoded + * (directory) Name. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, + * the memory will be obtained from the heap. This routine may + * return NULL upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSName upon success + */ + +NSS_EXTERN NSSName * +nssName_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berName +); + +/* + * nssName_CreateFromUTF8 + * + * This routine creates an NSSName by decoding a UTF8 string + * consisting of the string representation of one of the choices of + * (directory) names. Currently the only choice is an RDNSeq. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. The routine may return NULL upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_UTF8 + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSName upon success + */ + +NSS_EXTERN NSSName * +nssName_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringName +); + +/* + * nssName_Create + * + * This routine creates an NSSName with the specified choice of + * underlying name types. The value of the choice variable must be + * one of the values of the NSSNameChoice enumeration, and the type + * of the arg variable must be as specified in the following table: + * + * Choice Type + * ======================== =========== + * NSSNameChoiceRdnSequence NSSRDNSeq * + * + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_CHOICE + * NSS_ERROR_INVALID_ARGUMENT + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSName upon success + */ + +NSS_EXTERN NSSName * +nssName_Create +( + NSSArena *arenaOpt, + NSSNameChoice choice, + void *arg +); + +/* + * nssName_Destroy + * + * This routine will destroy a Name object. It should eventually be + * called on all Names created without an arena. While it is not + * necessary to call it on Names created within an arena, it is not + * an error to do so. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * set an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +nssName_Destroy +( + NSSName *name +); + +/* + * nssName_GetDEREncoding + * + * This routine will DER-encode a name object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSName + */ + +NSS_EXTERN NSSDER * +nssName_GetDEREncoding +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the Name in the format specified by the + * underlying name choice. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have set an error on the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the UTF8 string + */ + +NSS_EXTERN NSSUTF8 * +nssName_GetUTF8Encoding +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetChoice + * + * This routine returns the type of the choice underlying the specified + * name. The return value will be a member of the NSSNameChoice + * enumeration. This routine may return NSSNameChoiceInvalid upon + * error, in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * + * Return value: + * NSSNameChoiceInvalid upon error + * An other member of the NSSNameChoice enumeration upon success + */ + +NSS_EXTERN NSSNameChoice +nssName_GetChoice +( + NSSName *name +); + +/* + * nssName_GetRDNSequence + * + * If the choice underlying the specified NSSName is that of an + * RDNSequence, this routine will return a pointer to that RDN + * sequence. Otherwise, this routine will place an error on the + * error stack, and return NULL. If the optional arena argument is + * non-null, the memory required will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. The + * caller owns the returned pointer. This routine may return NULL + * upon error, in which case it will have set an error on the error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSRDNSeq + */ + +NSS_EXTERN NSSRDNSeq * +nssName_GetRDNSequence +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetSpecifiedChoice + * + * If the choice underlying the specified NSSName matches the specified + * choice, a caller-owned pointer to that underlying object will be + * returned. Otherwise, an error will be placed on the error stack and + * NULL will be returned. If the optional arena argument is non-null, + * the memory required will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer, which must be typecast + */ + +NSS_EXTERN void * +nssName_GetSpecifiedChoice +( + NSSName *name, + NSSNameChoice choice, + NSSArena *arenaOpt +); + +/* + * nssName_Compare + * + * This routine compares two Names for equality. For two Names to be + * equal, they must have the same choice of underlying types, and the + * underlying values must be equal. The result of the comparison will + * be stored at the location pointed to by the "equalp" variable, which + * must point to a valid PRBool. This routine may return PR_FAILURE + * upon error, in which case it will have set an error on the error + * stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE on error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +nssName_Compare +( + NSSName *name1, + NSSName *name2, + PRBool *equalp +); + +/* + * nssName_Duplicate + * + * This routine duplicates the specified nssname. If the optional + * arena argument is non-null, the memory required will be obtained + * from that arena; otherwise, the memory will be obtained from the + * heap. This routine may return NULL upon error, in which case it + * will have placed an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a new NSSName + */ + +NSS_EXTERN NSSName * +nssName_Duplicate +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetUID + * + * This routine will attempt to derive a user identifier from the + * specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished + * Names containing a UID attribute, the UID will be the value of + * that attribute. Note that no UID attribute is defined in either + * PKIX or PKCS#9; rather, this seems to derive from RFC 1274, which + * defines the type as a caseIgnoreString. We'll return a Directory + * String. If the optional arena argument is non-null, the memory + * used will be obtained from that arena; otherwise, the memory will + * be obtained from the heap. This routine may return NULL upon error, + * in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_UID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String. + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssName_GetUID +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetEmail + * + * This routine will attempt to derive an email address from the + * specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished + * Names containing either a PKIX email address or a PKCS#9 email + * address, the result will be the value of that attribute. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_EMAIL + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr IA5 String */ +nssName_GetEmail +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetCommonName + * + * This routine will attempt to derive a common name from the + * specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished Names + * containing a PKIX Common Name, the result will be that name. If + * the optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_COMMON_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssName_GetCommonName +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetOrganization + * + * This routine will attempt to derive an organisation name from the + * specified name, if the choices and content of the name permit. + * If Name consists of a Sequence of Relative Distinguished names + * containing a PKIX Organization, the result will be the value of + * that attribute. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. This routine may return NULL upon + * error, in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_ORGANIZATION + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssName_GetOrganization +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetOrganizationalUnits + * + * This routine will attempt to derive a sequence of organisational + * unit names from the specified name, if the choices and content of + * the name permit. If the Name consists of a Sequence of Relative + * Distinguished Names containing one or more organisational units, + * the result will be the values of those attributes. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_ORGANIZATIONAL_UNITS + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a null-terminated array of UTF8 Strings + */ + +NSS_EXTERN NSSUTF8 ** /* XXX fgmr DirectoryString */ +nssName_GetOrganizationalUnits +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetStateOrProvince + * + * This routine will attempt to derive a state or province name from + * the specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished Names + * containing a state or province, the result will be the value of + * that attribute. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. This routine may return NULL upon + * error, in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_STATE_OR_PROVINCE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssName_GetStateOrProvince +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetLocality + * + * This routine will attempt to derive a locality name from the + * specified name, if the choices and content of the name permit. If + * the Name consists of a Sequence of Relative Distinguished names + * containing a Locality, the result will be the value of that + * attribute. If the optional arena argument is non-null, the memory + * used will be obtained from that arena; otherwise, the memory will + * be obtained from the heap. This routine may return NULL upon error, + * in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_LOCALITY + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssName_GetLocality +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetCountry + * + * This routine will attempt to derive a country name from the + * specified name, if the choices and content of the name permit. + * If the Name consists of a Sequence of Relative Distinguished + * Names containing a Country, the result will be the value of + * that attribute.. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, + * the memory will be obtained from the heap. This routine may + * return NULL upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_COUNTRY + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr PrintableString */ +nssName_GetCountry +( + NSSName *name, + NSSArena *arenaOpt +); + +/* + * nssName_GetAttribute + * + * If the specified name consists of a Sequence of Relative + * Distinguished Names containing an attribute with the specified + * type, and the actual value of that attribute may be expressed + * with a Directory String, then the value of that attribute will + * be returned as a Directory String. If the optional arena argument + * is non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_NAME + * NSS_ERROR_NO_ATTRIBUTE + * NSS_ERROR_ATTRIBUTE_VALUE_NOT_STRING + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssName_GetAttribute +( + NSSName *name, + NSSOID *attribute, + NSSArena *arenaOpt +); + +/* + * nssName_EvaluateUTF8 + * + * This routine evaluates a UTF8 string, and returns PR_TRUE if the + * string contains the string representation of an NSSName. This + * routine is used by the GeneralName routine + * nssGeneralName_CreateFromUTF8 to determine which choice of + * general name the string may encode. This routine may return + * PR_FALSE upon error, but it subsumes that condition under the + * general "string does not evaluate as a Name" state, and does not + * set an error on the error stack. + * + * Return value: + * PR_TRUE if the string represents a Name + * PR_FALSE otherwise + */ + +NSS_EXTERN PRBool +nssName_EvaluateUTF8 +( + NSSUTF8 *str +); + +/* + * NSSGeneralName + * + * The non-public "methods" regarding this "object" are: + * + * nssGeneralName_CreateFromBER -- constructor + * nssGeneralName_CreateFromUTF8 -- constructor + * nssGeneralName_Create -- constructor + * + * nssGeneralName_Destroy + * nssGeneralName_GetDEREncoding + * nssGeneralName_GetUTF8Encoding + * nssGeneralName_GetChoice + * nssGeneralName_GetOtherName + * nssGeneralName_GetRfc822Name + * nssGeneralName_GetDNSName + * nssGeneralName_GetX400Address + * nssGeneralName_GetDirectoryName + * nssGeneralName_GetEdiPartyName + * nssGeneralName_GetUniformResourceIdentifier + * nssGeneralName_GetIPAddress + * nssGeneralName_GetRegisteredID + * nssGeneralName_GetSpecifiedChoice + * nssGeneralName_Compare + * nssGeneralName_Duplicate + * + * nssGeneralName_GetUID + * nssGeneralName_GetEmail + * nssGeneralName_GetCommonName + * nssGeneralName_GetOrganization + * nssGeneralName_GetOrganizationalUnits + * nssGeneralName_GetStateOrProvince + * nssGeneralName_GetLocality + * nssGeneralName_GetCountry + * nssGeneralName_GetAttribute + */ + +/* + * nssGeneralName_CreateFromBER + * + * This routine creates an NSSGeneralName by decoding a BER- or DER- + * encoded general name. If the optional arena argument is non-null, + * the memory used will be obtained from that arena; otherwise, the + * memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have set an error on the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralName upon success + */ + +NSS_EXTERN NSSGeneralName * +nssGeneralName_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berGeneralName +); + +/* + * nssGeneralName_CreateFromUTF8 + * + * This routine creates an NSSGeneralName by decoding a UTF8 string + * consisting of the string representation of one of the choices of + * general names. If the optional arena argument is non-null, the + * memory used will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The routine may return NULL upon + * error, in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_UTF8 + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralName upon success + */ + +NSS_EXTERN NSSGeneralName * +nssGeneralName_CreateFromUTF8 +( + NSSArena *arenaOpt, + NSSUTF8 *stringGeneralName +); + +/* + * nssGeneralName_Create + * + * This routine creates an NSSGeneralName with the specified choice of + * underlying name types. The value of the choice variable must be one + * of the values of the NSSGeneralNameChoice enumeration, and the type + * of the arg variable must be as specified in the following table: + * + * Choice Type + * ============================================ ========= + * NSSGeneralNameChoiceOtherName + * NSSGeneralNameChoiceRfc822Name + * NSSGeneralNameChoiceDNSName + * NSSGeneralNameChoiceX400Address + * NSSGeneralNameChoiceDirectoryName NSSName * + * NSSGeneralNameChoiceEdiPartyName + * NSSGeneralNameChoiceUniformResourceIdentifier + * NSSGeneralNameChoiceIPAddress + * NSSGeneralNameChoiceRegisteredID + * + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have set an error on the error stack. + * + * The error may be one fo the following values: + * NSS_ERROR_INVALID_CHOICE + * NSS_ERROR_INVALID_ARGUMENT + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralName upon success + */ + +NSS_EXTERN NSSGeneralName * +nssGeneralName_Create +( + NSSGeneralNameChoice choice, + void *arg +); + +/* + * nssGeneralName_Destroy + * + * This routine will destroy a General Name object. It should + * eventually be called on all General Names created without an arena. + * While it is not necessary to call it on General Names created within + * an arena, it is not an error to do so. This routine returns a + * PRStatus value; if successful, it will return PR_SUCCESS. If + * usuccessful, it will set an error on the error stack and return + * PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * + * Return value: + * PR_FAILURE upon failure + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +nssGeneralName_Destroy +( + NSSGeneralName *generalName +); + +/* + * nssGeneralName_GetDEREncoding + * + * This routine will DER-encode a name object. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return null upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSGeneralName + */ + +NSS_EXTERN NSSDER * +nssGeneralName_GetDEREncoding +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetUTF8Encoding + * + * This routine returns a UTF8 string containing a string + * representation of the General Name in the format specified by the + * underlying name choice. If the optional arena argument is + * non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 string + */ + +NSS_EXTERN NSSUTF8 * +nssGeneralName_GetUTF8Encoding +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetChoice + * + * This routine returns the type of choice underlying the specified + * general name. The return value will be a member of the + * NSSGeneralNameChoice enumeration. This routine may return + * NSSGeneralNameChoiceInvalid upon error, in which case it will have + * set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * + * Return value: + * NSSGeneralNameChoiceInvalid upon error + * An other member of the NSSGeneralNameChoice enumeration + */ + +NSS_EXTERN NSSGeneralNameChoice +nssGeneralName_GetChoice +( + NSSGeneralName *generalName +); + +/* + * nssGeneralName_GetOtherName + * + * If the choice underlying the specified NSSGeneralName is that of an + * Other Name, this routine will return a pointer to that Other name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSOtherName + */ + +NSS_EXTERN NSSOtherName * +nssGeneralName_GetOtherName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetRfc822Name + * + * If the choice underlying the specified NSSGeneralName is that of an + * RFC 822 Name, this routine will return a pointer to that name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSRFC822Name + */ + +NSS_EXTERN NSSRFC822Name * +nssGeneralName_GetRfc822Name +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetDNSName + * + * If the choice underlying the specified NSSGeneralName is that of a + * DNS Name, this routine will return a pointer to that DNS name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSDNSName + */ + +NSS_EXTERN NSSDNSName * +nssGeneralName_GetDNSName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetX400Address + * + * If the choice underlying the specified NSSGeneralName is that of an + * X.400 Address, this routine will return a pointer to that Address. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSX400Address + */ + +NSS_EXTERN NSSX400Address * +nssGeneralName_GetX400Address +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetDirectoryName + * + * If the choice underlying the specified NSSGeneralName is that of a + * (directory) Name, this routine will return a pointer to that name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSName + */ + +NSS_EXTERN NSSName * +nssGeneralName_GetName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetEdiPartyName + * + * If the choice underlying the specified NSSGeneralName is that of an + * EDI Party Name, this routine will return a pointer to that name. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSEdiPartyName + */ + +NSS_EXTERN NSSEdiPartyName * +nssGeneralName_GetEdiPartyName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetUniformResourceIdentifier + * + * If the choice underlying the specified NSSGeneralName is that of a + * URI, this routine will return a pointer to that URI. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSURI + */ + +NSS_EXTERN NSSURI * +nssGeneralName_GetUniformResourceIdentifier +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetIPAddress + * + * If the choice underlying the specified NSSGeneralName is that of an + * IP Address , this routine will return a pointer to that address. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSIPAddress + */ + +NSS_EXTERN NSSIPAddress * +nssGeneralName_GetIPAddress +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetRegisteredID + * + * If the choice underlying the specified NSSGeneralName is that of a + * Registered ID, this routine will return a pointer to that ID. + * Otherwise, this routine will place an error on the error stack, and + * return NULL. If the optional arena argument is non-null, the memory + * required will be obtained from that arena; otherwise, the memory + * will be obtained from the heap. The caller owns the returned + * pointer. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to an NSSRegisteredID + */ + +NSS_EXTERN NSSRegisteredID * +nssGeneralName_GetRegisteredID +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetSpecifiedChoice + * + * If the choice underlying the specified NSSGeneralName matches the + * specified choice, a caller-owned pointer to that underlying object + * will be returned. Otherwise, an error will be placed on the error + * stack and NULL will be returned. If the optional arena argument + * is non-null, the memory required will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. The caller + * owns the returned pointer. This routine may return NULL upon + * error, in which caes it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_WRONG_CHOICE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer, which must be typecast + */ + +NSS_EXTERN void * +nssGeneralName_GetSpecifiedChoice +( + NSSGeneralName *generalName, + NSSGeneralNameChoice choice, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_Compare + * + * This routine compares two General Names for equality. For two + * General Names to be equal, they must have the same choice of + * underlying types, and the underlying values must be equal. The + * result of the comparison will be stored at the location pointed + * to by the "equalp" variable, which must point to a valid PRBool. + * This routine may return PR_FAILURE upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following value: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +nssGeneralName_Compare +( + NSSGeneralName *generalName1, + NSSGeneralName *generalName2, + PRBool *equalp +); + +/* + * nssGeneralName_Duplicate + * + * This routine duplicates the specified General Name. If the optional + * arena argument is non-null, the memory required will be obtained + * from that arena; otherwise, the memory will be obtained from the + * heap. This routine may return NULL upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a new NSSGeneralName + */ + +NSS_EXTERN NSSGeneralName * +nssGeneralName_Duplicate +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetUID + * + * This routine will attempt to derive a user identifier from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished Names containing a UID + * attribute, the UID will be the value of that attribute. Note + * that no UID attribute is defined in either PKIX or PKCS#9; + * rather, this seems to derive from RFC 1274, which defines the + * type as a caseIgnoreString. We'll return a Directory String. + * If the optional arena argument is non-null, the memory used + * will be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon error, + * in which case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_UID + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String. + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssGeneralName_GetUID +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetEmail + * + * This routine will attempt to derive an email address from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing either + * a PKIX email address or a PKCS#9 email address, the result will + * be the value of that attribute. If the General Name is an RFC 822 + * Name, the result will be the string form of that name. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_EMAIL + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr IA5String */ +nssGeneralName_GetEmail +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetCommonName + * + * This routine will attempt to derive a common name from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing a PKIX + * Common Name, the result will be that name. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have set + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_COMMON_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssGeneralName_GetCommonName +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetOrganization + * + * This routine will attempt to derive an organisation name from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing an + * Organization, the result will be the value of that attribute. + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_ORGANIZATION + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssGeneralName_GetOrganization +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetOrganizationalUnits + * + * This routine will attempt to derive a sequence of organisational + * unit names from the specified general name, if the choices and + * content of the name permit. If the General Name is a (directory) + * Name consisting of a Sequence of Relative Distinguished names + * containing one or more organisational units, the result will + * consist of those units. If the optional arena argument is non- + * null, the memory used will be obtained from that arena; otherwise, + * the memory will be obtained from the heap. This routine may return + * NULL upon error, in which case it will have set an error on the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_ORGANIZATIONAL_UNITS + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a null-terminated array of UTF8 Strings + */ + +NSS_EXTERN NSSUTF8 ** /* XXX fgmr DirectoryString */ +nssGeneralName_GetOrganizationalUnits +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetStateOrProvince + * + * This routine will attempt to derive a state or province name from + * the specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing a state or + * province, the result will be the value of that attribute. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_STATE_OR_PROVINCE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssGeneralName_GetStateOrProvince +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetLocality + * + * This routine will attempt to derive a locality name from + * the specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting + * of a Sequence of Relative Distinguished names containing a Locality, + * the result will be the value of that attribute. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_LOCALITY + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssGeneralName_GetLocality +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetCountry + * + * This routine will attempt to derive a country name from the + * specified general name, if the choices and content of the name + * permit. If the General Name is a (directory) Name consisting of a + * Sequence of Relative Distinguished names containing a Country, the + * result will be the value of that attribute. If the optional + * arena argument is non-null, the memory used will be obtained from + * that arena; otherwise, the memory will be obtained from the heap. + * This routine may return NULL upon error, in which case it will have + * set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_COUNTRY + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr PrintableString */ +nssGeneralName_GetCountry +( + NSSGeneralName *generalName, + NSSArena *arenaOpt +); + +/* + * nssGeneralName_GetAttribute + * + * If the specified general name is a (directory) name consisting + * of a Sequence of Relative Distinguished Names containing an + * attribute with the specified type, and the actual value of that + * attribute may be expressed with a Directory String, then the + * value of that attribute will be returned as a Directory String. + * If the optional arena argument is non-null, the memory used will + * be obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_ATTRIBUTE + * NSS_ERROR_ATTRIBUTE_VALUE_NOT_STRING + * + * Return value: + * NULL upon error + * A pointer to a UTF8 String + */ + +NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */ +nssGeneralName_GetAttribute +( + NSSGeneralName *generalName, + NSSOID *attribute, + NSSArena *arenaOpt +); + +/* + * NSSGeneralNameSeq + * + * The public "methods" regarding this "object" are: + * + * nssGeneralNameSeq_CreateFromBER -- constructor + * nssGeneralNameSeq_Create -- constructor + * + * nssGeneralNameSeq_Destroy + * nssGeneralNameSeq_GetDEREncoding + * nssGeneralNameSeq_AppendGeneralName + * nssGeneralNameSeq_GetGeneralNameCount + * nssGeneralNameSeq_GetGeneralName + * nssGeneralNameSeq_Compare + * nssGeneralnameSeq_Duplicate + */ + +/* + * nssGeneralNameSeq_CreateFromBER + * + * This routine creates a general name sequence by decoding a BER- + * or DER-encoded GeneralNames. If the optional arena argument is + * non-null, the memory used will be obtained from that arena; + * otherwise, the memory will be obtained from the heap. This routine + * may return NULL upon error, in which case it will have set an error + * on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_BER + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralNameSeq upon success + */ + +NSS_EXTERN NSSGeneralNameSeq * +nssGeneralNameSeq_CreateFromBER +( + NSSArena *arenaOpt, + NSSBER *berGeneralNameSeq +); + +/* + * nssGeneralNameSeq_Create + * + * This routine creates an NSSGeneralNameSeq from one or more General + * Names. The final argument to this routine must be NULL. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_INVALID_GENERAL_NAME + * + * Return value: + * NULL upon error + * A pointer to an NSSGeneralNameSeq upon success + */ + +NSS_EXTERN NSSGeneralNameSeq * +nssGeneralNameSeq_Create +( + NSSArena *arenaOpt, + NSSGeneralName *generalName1, + ... +); + +/* + * nssGeneralNameSeq_Destroy + * + * This routine will destroy an NSSGeneralNameSeq object. It should + * eventually be called on all NSSGeneralNameSeqs created without an + * arena. While it is not necessary to call it on NSSGeneralNameSeq's + * created within an arena, it is not an error to do so. This routine + * returns a PRStatus value; if successful, it will return PR_SUCCESS. + * If unsuccessful, it will set an error on the error stack and return + * PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon success + */ + +NSS_EXTERN PRStatus +nssGeneralNameSeq_Destroy +( + NSSGeneralNameSeq *generalNameSeq +); + +/* + * nssGeneralNameSeq_GetDEREncoding + * + * This routine will DER-encode an NSSGeneralNameSeq object. If the + * optional arena argument is non-null, the memory used will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return null upon error, in which + * case it will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * The DER encoding of this NSSGeneralNameSeq + */ + +NSS_EXTERN NSSDER * +nssGeneralNameSeq_GetDEREncoding +( + NSSGeneralNameSeq *generalNameSeq, + NSSArena *arenaOpt +); + +/* + * nssGeneralNameSeq_AppendGeneralName + * + * This routine appends a General Name to the end of the existing + * General Name Sequence. If the sequence was created with a non-null + * arena argument, that same arena will be used for any additional + * required memory. If the sequence was created with a NULL arena + * argument, any additional memory will be obtained from the heap. + * This routine returns a PRStatus value; it will return PR_SUCCESS + * upon success, and upon failure it will set an error on the error + * stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_INVALID_GENERAL_NAME + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure. + */ + +NSS_EXTERN PRStatus +nssGeneralNameSeq_AppendGeneralName +( + NSSGeneralNameSeq *generalNameSeq, + NSSGeneralName *generalName +); + +/* + * nssGeneralNameSeq_GetGeneralNameCount + * + * This routine returns the cardinality of the specified General name + * Sequence. This routine may return 0 upon error, in which case it + * will have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * + * Return value; + * 0 upon error + * A positive number upon success + */ + +NSS_EXTERN PRUint32 +nssGeneralNameSeq_GetGeneralNameCount +( + NSSGeneralNameSeq *generalNameSeq +); + +/* + * nssGeneralNameSeq_GetGeneralName + * + * This routine returns a pointer to the i'th General Name in the + * specified General Name Sequence. The value of the variable 'i' is + * on the range [0,c) where c is the cardinality returned from + * NSSGeneralNameSeq_GetGeneralNameCount. The caller owns the General + * Name the pointer to which is returned. If the optional arena + * argument is non-null, the memory used will be obtained from that + * arena; otherwise, the memory will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have set + * an error upon the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_VALUE_OUT_OF_RANGE + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A caller-owned pointer to a General Name. + */ + +NSS_EXTERN NSSGeneralName * +nssGeneralNameSeq_GetGeneralName +( + NSSGeneralNameSeq *generalNameSeq, + NSSArena *arenaOpt, + PRUint32 i +); + +/* + * nssGeneralNameSeq_Compare + * + * This routine compares two General Name Sequences for equality. For + * two General Name Sequences to be equal, they must have the same + * cardinality, and each General Name in one sequence must be equal to + * the corresponding General Name in the other. The result of the + * comparison will be stored at the location pointed to by the "equalp" + * variable, which must point to a valid PRBool. This routine may + * return PR_FAILURE upon error, in which case it will have set an + * error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_INVALID_ARGUMENT + * + * Return value: + * PR_FAILURE upon error + * PR_SUCCESS upon a successful comparison (equal or not) + */ + +NSS_EXTERN PRStatus +nssGeneralNameSeq_Compare +( + NSSGeneralNameSeq *generalNameSeq1, + NSSGeneralNameSeq *generalNameSeq2, + PRBool *equalp +); + +/* + * nssGeneralNameSeq_Duplicate + * + * This routine duplicates the specified sequence of general names. If + * the optional arena argument is non-null, the memory required will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in which + * case it will have placed an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_GENERAL_NAME_SEQ + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to a new General Name Sequence. + */ + +NSS_EXTERN NSSGeneralNameSeq * +nssGeneralNameSeq_Duplicate +( + NSSGeneralNameSeq *generalNameSeq, + NSSArena *arenaOpt +); + +PR_END_EXTERN_C + +#endif /* PKI1_H */ diff --git a/security/nss/lib/pki1/pki1t.h b/security/nss/lib/pki1/pki1t.h new file mode 100644 index 000000000..de6e5c835 --- /dev/null +++ b/security/nss/lib/pki1/pki1t.h @@ -0,0 +1,104 @@ +/* + * 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 PKI1T_H +#define PKI1T_H + +#ifdef DEBUG +static const char PKI1T_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; +#endif /* DEBUG */ + +/* + * pki1t.h + * + * This file contains definitions for the types used in the PKIX part-1 + * code, but not available publicly. + */ + +#ifndef BASET_H +#include "baset.h" +#endif /* BASET_H */ + +#ifndef NSSPKI1T_H +#include "nsspki1t.h" +#endif /* NSSPKI1T_H */ + +PR_BEGIN_EXTERN_C + +/* + * NSSOID + * + * This structure is used to hold our internal table of built-in OID + * data. The fields are as follows: + * + * NSSItem data -- this is the actual DER-encoded multinumber oid + * const char *expl -- this explains the derivation, and is checked + * in a unit test. While the field always exists, + * it is only populated or used in debug builds. + * + */ + +struct NSSOIDStr { +#ifdef DEBUG + const NSSUTF8 *tag; + const NSSUTF8 *expl; +#endif /* DEBUG */ + NSSItem data; +}; + +/* + * nssAttributeTypeAliasTable + * + * Attribute types are passed around as oids (at least in the X.500 + * and PKI worlds, as opposed to ldap). However, when written as + * strings they usually have well-known aliases, e.g., "ou" or "c." + * + * This type defines a table, populated in the generated oiddata.c + * file, of the aliases we recognize. + * + * The fields are as follows: + * + * NSSUTF8 *alias -- a well-known string alias for an oid + * NSSOID *oid -- the oid to which the alias corresponds + * + */ + +struct nssAttributeTypeAliasTableStr { + const NSSUTF8 *alias; + const NSSOID **oid; +}; +typedef struct nssAttributeTypeAliasTableStr nssAttributeTypeAliasTable; + +PR_END_EXTERN_C + +#endif /* PKI1T_H */ diff --git a/security/nss/lib/pki1/rdn.c b/security/nss/lib/pki1/rdn.c new file mode 100644 index 000000000..f778de22c --- /dev/null +++ b/security/nss/lib/pki1/rdn.c @@ -0,0 +1,73 @@ +/* + * 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 */ + +/* + * rdn.c + * + * This file contains the implementation of the PKIX part-1 object + * RelativeDistinguishedName. + */ + +#ifndef NSSBASE_H +#include "nssbase.h" +#endif /* NSSBASE_H */ + +#ifndef ASN1_H +#include "asn1.h" +#endif /* ASN1_H */ + +#ifndef PKI1_H +#include "pki1.h" +#endif /* PKI1_H */ + +/* + * RelativeDistinguishedName + * + * From draft-ietf-pkix-ipki-part1-10: + * + * RelativeDistinguishedName ::= + * SET SIZE (1 .. MAX) OF AttributeTypeAndValue + * + * An RDN is merely an (unordered) set of ATAV's. The setSize (that's + * a noun, not a verb) variable is a "helper" variable kept for + * convenience. + */ + +struct nssRDNStr { + PRUint32 setSize; + NSSATAV **atavs; +}; diff --git a/security/nss/lib/pki1/rdnseq.c b/security/nss/lib/pki1/rdnseq.c new file mode 100644 index 000000000..bbd4dfe4b --- /dev/null +++ b/security/nss/lib/pki1/rdnseq.c @@ -0,0 +1,71 @@ +/* + * 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 */ + +/* + * rdnseq.c + * + * This file contains the implementation of the PKIX part-1 object + * RDNSequence. + */ + +#ifndef NSSBASE_H +#include "nssbase.h" +#endif /* NSSBASE_H */ + +#ifndef ASN1_H +#include "asn1.h" +#endif /* ASN1_H */ + +#ifndef PKI1_H +#include "pki1.h" +#endif /* PKI1_H */ + +/* + * RDNSequence + * + * From draft-ietf-pkix-ipki-part1-10: + * + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * + * An RDNSequence is simply an (ordered) sequence of RDN's. The + * seqSize variable is a "helper" kept for simplicity. + */ + +struct nssRDNSeqStr { + PRUint32 seqSize; + NSSRDN **rdns; +}; |