diff options
author | wtc%netscape.com <devnull@localhost> | 2002-12-17 05:35:07 +0000 |
---|---|---|
committer | wtc%netscape.com <devnull@localhost> | 2002-12-17 05:35:07 +0000 |
commit | 8fdcddee79fdc74043e47296b76a8f6a2e1303f7 (patch) | |
tree | cf18422ea2a1289d399df64782525838f3195b88 | |
parent | 8703ea0ca7d05542047e4fc5e4743c30a86e7b89 (diff) | |
download | nss-hg-8fdcddee79fdc74043e47296b76a8f6a2e1303f7.tar.gz |
Bug 183612: added support for looking up a cert by subject key ID and
creating a CMS recipient info from a subject key ID. The patch was
contributed by Javi Delgadillo <javi@netscape.com>. r=relyea, wtc.
Modified Files:
certdb/cert.h certdb/certdb.c certdb/certdb.h certdb/certv3.c
certdb/stanpcertdb.c nss/nss.def nss/nssinit.c
pk11wrap/pk11cert.c pk11wrap/pk11func.h pk11wrap/secmod.h
pki/pki3hack.c smime/cms.h smime/cmslocal.h smime/cmspubkey.c
smime/cmsrecinfo.c smime/cmssiginfo.c smime/cmst.h
smime/smime.def
Tag: NSS_3_7_BRANCH
-rw-r--r-- | security/nss/lib/certdb/cert.h | 5 | ||||
-rw-r--r-- | security/nss/lib/certdb/certdb.c | 160 | ||||
-rw-r--r-- | security/nss/lib/certdb/certdb.h | 22 | ||||
-rw-r--r-- | security/nss/lib/certdb/certv3.c | 2 | ||||
-rw-r--r-- | security/nss/lib/nss/nss.def | 2 | ||||
-rw-r--r-- | security/nss/lib/nss/nssinit.c | 5 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/pk11cert.c | 137 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/pk11func.h | 3 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/secmod.h | 16 | ||||
-rw-r--r-- | security/nss/lib/pki/pki3hack.c | 2 | ||||
-rw-r--r-- | security/nss/lib/smime/cms.h | 9 | ||||
-rw-r--r-- | security/nss/lib/smime/cmslocal.h | 10 | ||||
-rw-r--r-- | security/nss/lib/smime/cmspubkey.c | 39 | ||||
-rw-r--r-- | security/nss/lib/smime/cmsrecinfo.c | 190 | ||||
-rw-r--r-- | security/nss/lib/smime/cmssiginfo.c | 12 | ||||
-rw-r--r-- | security/nss/lib/smime/cmst.h | 35 | ||||
-rw-r--r-- | security/nss/lib/smime/smime.def | 7 |
17 files changed, 571 insertions, 85 deletions
diff --git a/security/nss/lib/certdb/cert.h b/security/nss/lib/certdb/cert.h index da8c53777..39ab8e4a8 100644 --- a/security/nss/lib/certdb/cert.h +++ b/security/nss/lib/certdb/cert.h @@ -478,6 +478,9 @@ CERT_FindCertByKeyID (CERTCertDBHandle *handle, SECItem *name, SECItem *keyID); extern CERTCertificate * CERT_FindCertByIssuerAndSN (CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN); +extern CERTCertificate * +CERT_FindCertBySubjKeyID (CERTCertDBHandle *handle, SECItem *subjKeyID); + /* ** Find a certificate in the database by a nickname ** "nickname" is the ascii string nickname to look for @@ -922,7 +925,7 @@ extern SECStatus CERT_FindKeyUsageExtension (CERTCertificate *cert, /* Return the decoded value of the subjectKeyID extension. The caller should ** free up the storage allocated in retItem->data. */ -extern SECStatus CERT_FindSubjectKeyIDExten (CERTCertificate *cert, +extern SECStatus CERT_FindSubjectKeyIDExtension (CERTCertificate *cert, SECItem *retItem); /* diff --git a/security/nss/lib/certdb/certdb.c b/security/nss/lib/certdb/certdb.c index 8bfe1a58c..270e6a05d 100644 --- a/security/nss/lib/certdb/certdb.c +++ b/security/nss/lib/certdb/certdb.c @@ -670,7 +670,7 @@ cert_GetKeyID(CERTCertificate *cert) cert->subjectKeyID.len = 0; /* see of the cert has a key identifier extension */ - rv = CERT_FindSubjectKeyIDExten(cert, &tmpitem); + rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem); if ( rv == SECSuccess ) { cert->subjectKeyID.data = (unsigned char*) PORT_ArenaAlloc(cert->arena, tmpitem.len); if ( cert->subjectKeyID.data != NULL ) { @@ -747,7 +747,7 @@ cert_IsRootCert(CERTCertificate *cert) /* authority key identifier is present */ if (cert->authKeyID->keyID.len > 0) { /* the keyIdentifier field is set, look for subjectKeyID */ - rv = CERT_FindSubjectKeyIDExten(cert, &tmpitem); + rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem); if (rv == SECSuccess) { PRBool match; /* also present, they MUST match for it to be a root */ @@ -2737,3 +2737,159 @@ CERT_SetStatusConfig(CERTCertDBHandle *handle, CERTStatusConfig *statusConfig) PORT_Assert(handle->statusConfig == NULL); handle->statusConfig = statusConfig; } + +/* + * Code for dealing with subjKeyID to cert mappings. + */ + +static PLHashTable *gSubjKeyIDHash = NULL; +static PRLock *gSubjKeyIDLock = NULL; + +static void *cert_AllocTable(void *pool, PRSize size) +{ + return PORT_Alloc(size); +} + +static void cert_FreeTable(void *pool, void *item) +{ + PORT_Free(item); +} + +static PLHashEntry* cert_AllocEntry(void *pool, const void *key) +{ + return PORT_New(PLHashEntry); +} + +static void cert_FreeEntry(void *pool, PLHashEntry *he, PRUintn flag) +{ + SECITEM_FreeItem((SECItem*)(he->value), PR_TRUE); + if (flag == HT_FREE_ENTRY) { + SECITEM_FreeItem((SECItem*)(he->key), PR_TRUE); + PORT_Free(he); + } +} + +static PLHashAllocOps cert_AllocOps = { + cert_AllocTable, cert_FreeTable, cert_AllocEntry, cert_FreeEntry +}; + +SECStatus +CERT_CreateSubjKeyIDHashTable(void) +{ + gSubjKeyIDHash = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, + SECITEM_HashCompare, + &cert_AllocOps, NULL); + if (!gSubjKeyIDHash) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + gSubjKeyIDLock = PR_NewLock(); + if (!gSubjKeyIDLock) { + PL_HashTableDestroy(gSubjKeyIDHash); + gSubjKeyIDHash = NULL; + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + return SECSuccess; + +} + +SECStatus +CERT_AddSubjKeyIDMapping(SECItem *subjKeyID, CERTCertificate *cert) +{ + SECItem *newKeyID, *oldVal, *newVal; + SECStatus rv = SECFailure; + + if (!gSubjKeyIDLock) { + /* If one is created, then both are there. So only check for one. */ + return SECFailure; + } + + newVal = SECITEM_DupItem(&cert->derCert); + if (!newVal) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto done; + } + newKeyID = SECITEM_DupItem(subjKeyID); + if (!newKeyID) { + SECITEM_FreeItem(newVal, PR_TRUE); + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto done; + } + + PR_Lock(gSubjKeyIDLock); + /* The hash table implementation does not free up the memory + * associated with the key of an already existing entry if we add a + * duplicate, so we would wind up leaking the previously allocated + * key if we don't remove before adding. + */ + oldVal = (SECItem*)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID); + if (oldVal) { + PL_HashTableRemove(gSubjKeyIDHash, subjKeyID); + } + + rv = (PL_HashTableAdd(gSubjKeyIDHash, newKeyID, newVal)) ? SECSuccess : + SECFailure; + PR_Unlock(gSubjKeyIDLock); +done: + return rv; +} + +SECStatus +CERT_RemoveSubjKeyIDMapping(SECItem *subjKeyID) +{ + SECStatus rv; + if (!gSubjKeyIDLock) + return SECFailure; + + PR_Lock(gSubjKeyIDLock); + rv = (PL_HashTableRemove(gSubjKeyIDHash, subjKeyID)) ? SECSuccess : + SECFailure; + PR_Unlock(gSubjKeyIDLock); + return rv; +} + +SECStatus +CERT_DestroySubjKeyIDHashTable(void) +{ + if (gSubjKeyIDHash) { + PR_Lock(gSubjKeyIDLock); + PL_HashTableDestroy(gSubjKeyIDHash); + gSubjKeyIDHash = NULL; + PR_Unlock(gSubjKeyIDLock); + PR_DestroyLock(gSubjKeyIDLock); + gSubjKeyIDLock = NULL; + } + return SECSuccess; +} + +SECItem* +CERT_FindDERCertBySubjKeyID(SECItem *subjKeyID) +{ + SECItem *val; + + if (!gSubjKeyIDLock) + return NULL; + + PR_Lock(gSubjKeyIDLock); + val = (SECItem*)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID); + if (val) { + val = SECITEM_DupItem(val); + } + PR_Unlock(gSubjKeyIDLock); + return val; +} + +CERTCertificate* +CERT_FindCertBySubjKeyID(CERTCertDBHandle *handle, SECItem *subjKeyID) +{ + CERTCertificate *cert = NULL; + SECItem *derCert; + + derCert = CERT_FindDERCertBySubjKeyID(subjKeyID); + if (derCert) { + cert = CERT_FindCertByDERCert(handle, derCert); + SECITEM_FreeItem(derCert, PR_TRUE); + } + return cert; +} diff --git a/security/nss/lib/certdb/certdb.h b/security/nss/lib/certdb/certdb.h index 7340961c2..2c6cdd9aa 100644 --- a/security/nss/lib/certdb/certdb.h +++ b/security/nss/lib/certdb/certdb.h @@ -154,6 +154,28 @@ SECStatus SEC_CrlReplaceUrl(PCERTSignedCrl *crl,char *url); #endif +/* + * These functions are used to map subjectKeyID extension values to certs. + */ +SECStatus +CERT_CreateSubjKeyIDHashTable(void); + +SECStatus +CERT_AddSubjKeyIDMapping(SECItem *subjKeyID, CERTCertificate *cert); + + +/* + * Call this function to remove an entry from the mapping table. + */ +SECStatus +CERT_RemoveSubjKeyIDMapping(SECItem *subjKeyID); + +SECStatus +CERT_DestroySubjKeyIDHashTable(void); + +SECItem* +CERT_FindDERCertBySubjKeyID(SECItem *subjKeyID); + SEC_END_PROTOS #endif /* _CERTDB_H_ */ diff --git a/security/nss/lib/certdb/certv3.c b/security/nss/lib/certdb/certv3.c index 9b5979364..e50c66279 100644 --- a/security/nss/lib/certdb/certv3.c +++ b/security/nss/lib/certdb/certv3.c @@ -291,7 +291,7 @@ CERT_FindKeyUsageExtension(CERTCertificate *cert, SECItem *retItem) * get the value of the X.509 v3 Key Usage Extension */ SECStatus -CERT_FindSubjectKeyIDExten(CERTCertificate *cert, SECItem *retItem) +CERT_FindSubjectKeyIDExtension(CERTCertificate *cert, SECItem *retItem) { SECItem encodedValue; diff --git a/security/nss/lib/nss/nss.def b/security/nss/lib/nss/nss.def index 0bc28e972..473a45228 100644 --- a/security/nss/lib/nss/nss.def +++ b/security/nss/lib/nss/nss.def @@ -718,6 +718,8 @@ SECKEY_CopyPublicKey; ;+ global: CERT_CRLCacheRefreshIssuer; CERT_EncodeAltNameExtension; +CERT_FindCertBySubjKeyID; +CERT_FindSubjectKeyIDExtension; CERT_GetFirstEmailAddress; CERT_GetNextEmailAddress; CERT_VerifySignedDataWithPubKeyInfo; diff --git a/security/nss/lib/nss/nssinit.c b/security/nss/lib/nss/nssinit.c index 3d8b2d4c2..13bf7d702 100644 --- a/security/nss/lib/nss/nssinit.c +++ b/security/nss/lib/nss/nssinit.c @@ -41,6 +41,7 @@ #include "prprf.h" #include "prmem.h" #include "cert.h" +#include "certdb.h" #include "key.h" #include "ssl.h" #include "sslproto.h" @@ -476,6 +477,8 @@ loser: } #endif pk11sdr_Init(); + CERT_CreateSubjKeyIDHashTable(); + SECMOD_InitCallOnce(); nss_IsInitted = PR_TRUE; } return rv; @@ -545,6 +548,8 @@ NSS_Shutdown(void) ShutdownCRLCache(); SECOID_Shutdown(); STAN_Shutdown(); + CERT_DestroySubjKeyIDHashTable(); + SECMOD_CleanupCallOnce(); rv = SECMOD_Shutdown(); pk11sdr_Shutdown(); nss_IsInitted = PR_FALSE; diff --git a/security/nss/lib/pk11wrap/pk11cert.c b/security/nss/lib/pk11wrap/pk11cert.c index abc8e55df..d4a192e1f 100644 --- a/security/nss/lib/pk11wrap/pk11cert.c +++ b/security/nss/lib/pk11wrap/pk11cert.c @@ -2153,11 +2153,16 @@ pk11_FindCertObjectByRecipientNew(PK11SlotInfo *slot, NSSCMSRecipient **recipien for (i=0; (ri = recipientlist[i]) != NULL; i++) { CERTCertificate *cert = NULL; - /* XXXXX fixme - not yet implemented! */ - if (ri->kind == RLSubjKeyID) - continue; - cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->id.issuerAndSN, - pwarg); + if (ri->kind == RLSubjKeyID) { + SECItem *derCert = CERT_FindDERCertBySubjKeyID(ri->id.subjectKeyID); + if (derCert) { + cert = PK11_FindCertFromDERCertItem(slot, derCert, pwarg); + SECITEM_FreeItem(derCert, PR_TRUE); + } + } else { + cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->id.issuerAndSN, + pwarg); + } if (cert) { /* this isn't our cert */ if ((cert->trust == NULL) || @@ -2169,7 +2174,6 @@ pk11_FindCertObjectByRecipientNew(PK11SlotInfo *slot, NSSCMSRecipient **recipien *rlIndex = i; return cert; } - } *rlIndex = -1; return NULL; @@ -2335,6 +2339,34 @@ loser: return NULL; } +static SECMODCallOnceType keyIDHashCallOnce; + +static SECStatus PR_CALLBACK +pk11_keyIDHash_populate(void *wincx) +{ + CERTCertList *certList; + CERTCertListNode *node = NULL; + SECItem subjKeyID = {siBuffer, NULL, 0}; + + certList = PK11_ListCerts(PK11CertListUser, wincx); + if (!certList) { + return SECFailure; + } + + for (node = CERT_LIST_HEAD(certList); + !CERT_LIST_END(node, certList); + node = CERT_LIST_NEXT(node)) { + if (CERT_FindSubjectKeyIDExtension(node->cert, + &subjKeyID) == SECSuccess && + subjKeyID.data != NULL) { + CERT_AddSubjKeyIDMapping(&subjKeyID, node->cert); + SECITEM_FreeItem(&subjKeyID, PR_FALSE); + } + } + CERT_DestroyCertList(certList); + return SECSuccess; +} + /* * This is the new version of the above function for NSS SMIME code * this stuff should REALLY be in the SMIME code, but some things in here are not public @@ -2345,8 +2377,13 @@ PK11_FindCertAndKeyByRecipientListNew(NSSCMSRecipient **recipientlist, void *win { CERTCertificate *cert; NSSCMSRecipient *rl; + SECStatus srv; int rlIndex; + srv = SECMOD_CallOnce(&keyIDHashCallOnce, pk11_keyIDHash_populate, wincx); + if (srv != SECSuccess) + return -1; + cert = pk11_AllFindCertObjectByRecipientNew(recipientlist, wincx, &rlIndex); if (!cert) { return -1; @@ -2888,43 +2925,21 @@ CERTCertificate * PK11_FindCertFromDERCert(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) { -#ifdef NSS_CLASSIC - 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)); + return PK11_FindCertFromDERCertItem(slot, &cert->derCert, wincx); +} - /* - * issue the find - */ - if ( !PK11_IsFriendly(slot)) { - rv = PK11_Authenticate(slot, PR_TRUE, wincx); - if (rv != SECSuccess) return NULL; - } +CERTCertificate * +PK11_FindCertFromDERCertItem(PK11SlotInfo *slot, SECItem *inDerCert, + void *wincx) - certh = pk11_getcerthandle(slot,cert,theTemplate,tsize); - if (certh == CK_INVALID_HANDLE) { - return NULL; - } - return PK11_MakeCertFromHandle(slot, certh, NULL); -#else +{ CERTCertificate *rvCert = NULL; NSSCertificate *c; NSSDER derCert; NSSToken *tok; NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); tok = PK11Slot_GetNSSToken(slot); - NSSITEM_FROM_SECITEM(&derCert, &cert->derCert); + NSSITEM_FROM_SECITEM(&derCert, inDerCert); if (!PK11_IsFriendly(slot)) { if (PK11_Authenticate(slot, PR_TRUE, wincx) != SECSuccess) { PK11_FreeSlot(slot); @@ -2954,7 +2969,6 @@ PK11_FindCertFromDERCert(PK11SlotInfo *slot, CERTCertificate *cert, rvCert = STAN_GetCERTCertificate(c); } return rvCert; -#endif } /* mcgreer 3.4 -- nobody uses this, ignoring */ @@ -4137,3 +4151,54 @@ CERTSignedCrl* PK11_ImportCRL(PK11SlotInfo * slot, SECItem *derCRL, char *url, } return (crl); } + +/* + * This code takes the NSPR CallOnce functionality and modifies it so + * that we can pass an argument to our function + */ +static struct { + PRLock *ml; + PRCondVar *cv; +} mod_init; + +void SECMOD_InitCallOnce(void) { + mod_init.ml = PR_NewLock(); + PORT_Assert(NULL != mod_init.ml); + mod_init.cv = PR_NewCondVar(mod_init.ml); + PORT_Assert(NULL != mod_init.cv); +} + +void SECMOD_CleanupCallOnce() +{ + if (mod_init.ml) { + PR_DestroyLock(mod_init.ml); + mod_init.ml = NULL; + } + if (mod_init.cv) { + PR_DestroyCondVar(mod_init.cv); + mod_init.cv = NULL; + } +} + +SECStatus SECMOD_CallOnce(SECMODCallOnceType *once, + SECMODCallOnceFN func, + void *arg) +{ + + if (!once->initialized) { + if (PR_AtomicSet(&once->inProgress, 1) == 0) { + once->status = (PRStatus)(*func)(arg); + PR_Lock(mod_init.ml); + once->initialized = 1; + PR_NotifyAllCondVar(mod_init.cv); + PR_Unlock(mod_init.ml); + } else { + PR_Lock(mod_init.ml); + while (!once->initialized) { + PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(mod_init.ml); + } + } + return once->status; +} diff --git a/security/nss/lib/pk11wrap/pk11func.h b/security/nss/lib/pk11wrap/pk11func.h index 0a450659c..9f91085cf 100644 --- a/security/nss/lib/pk11wrap/pk11func.h +++ b/security/nss/lib/pk11wrap/pk11func.h @@ -66,7 +66,6 @@ PK11SlotListElement *PK11_FindSlotElement(PK11SlotList *list, * 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); @@ -461,6 +460,8 @@ SECStatus PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert, void *arg); CERTCertificate *PK11_FindCertFromDERCert(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx); +CERTCertificate *PK11_FindCertFromDERCertItem(PK11SlotInfo *slot, + SECItem *derCert, void *wincx); CERTCertificate *PK11_FindCertFromDERSubjectAndNickname( PK11SlotInfo *slot, CERTCertificate *cert, char *nickname, diff --git a/security/nss/lib/pk11wrap/secmod.h b/security/nss/lib/pk11wrap/secmod.h index fd8037e65..51fabec75 100644 --- a/security/nss/lib/pk11wrap/secmod.h +++ b/security/nss/lib/pk11wrap/secmod.h @@ -148,6 +148,22 @@ extern unsigned long SECMOD_InternaltoPubMechFlags(unsigned long internalFlags); extern unsigned long SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags); extern unsigned long SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags); +typedef struct SECMODCallOnceType { + PRIntn initialized; + PRInt32 inProgress; + SECStatus status; +} SECMODCallOnceType; + +typedef SECStatus (PR_CALLBACK *SECMODCallOnceFN)(void *arg); + +extern void SECMOD_InitCallOnce(); + +extern SECStatus SECMOD_CallOnce(SECMODCallOnceType *once, + SECMODCallOnceFN func, + void *arg); + +extern void SECMOD_CleanupCallOnce(); + SEC_END_PROTOS #endif diff --git a/security/nss/lib/pki/pki3hack.c b/security/nss/lib/pki/pki3hack.c index 1d06b7f34..a828f98c8 100644 --- a/security/nss/lib/pki/pki3hack.c +++ b/security/nss/lib/pki/pki3hack.c @@ -248,7 +248,7 @@ nss3certificate_matchIdentifier(nssDecodedCert *dc, void *id) /* keyIdentifier */ if (authKeyID->keyID.len > 0) { - if (CERT_FindSubjectKeyIDExten(c, &skid) == SECSuccess) { + if (CERT_FindSubjectKeyIDExtension(c, &skid) == SECSuccess) { PRBool skiEqual; skiEqual = SECITEM_ItemsAreEqual(&authKeyID->keyID, &skid); PORT_Free(skid.data); diff --git a/security/nss/lib/smime/cms.h b/security/nss/lib/smime/cms.h index 7035a3265..e5ce768c4 100644 --- a/security/nss/lib/smime/cms.h +++ b/security/nss/lib/smime/cms.h @@ -859,6 +859,15 @@ NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd); extern NSSCMSRecipientInfo * NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert); +extern NSSCMSRecipientInfo * +NSS_CMSRecipientInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, + SECItem *subjKeyID, + SECKEYPublicKey *pubKey); + +extern NSSCMSRecipientInfo * +NSS_CMSRecipientInfo_CreateWithSubjKeyIDFromCert(NSSCMSMessage *cmsg, + CERTCertificate *cert); + extern void NSS_CMSRecipientInfo_Destroy(NSSCMSRecipientInfo *ri); diff --git a/security/nss/lib/smime/cmslocal.h b/security/nss/lib/smime/cmslocal.h index e7f15c4e1..962871804 100644 --- a/security/nss/lib/smime/cmslocal.h +++ b/security/nss/lib/smime/cmslocal.h @@ -161,8 +161,14 @@ NSS_CMSCipherContext_Encrypt(NSSCMSCipherContext *cc, unsigned char *output, * according to PKCS#1 and RFC2633 (S/MIME) */ extern SECStatus -NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key, - SECItem *encKey); +NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, + PK11SymKey *key, + SECItem *encKey); + +extern SECStatus +NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp, + SECKEYPublicKey *publickey, + PK11SymKey *bulkkey, SECItem *encKey); /* * NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key diff --git a/security/nss/lib/smime/cmspubkey.c b/security/nss/lib/smime/cmspubkey.c index 3e06da556..1cf0336e4 100644 --- a/security/nss/lib/smime/cmspubkey.c +++ b/security/nss/lib/smime/cmspubkey.c @@ -56,29 +56,43 @@ * according to PKCS#1 and RFC2633 (S/MIME) */ SECStatus -NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *bulkkey, - SECItem *encKey) +NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, + PK11SymKey *bulkkey, + SECItem *encKey) { - SECOidTag certalgtag; /* the certificate's encryption algorithm */ - SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */ SECStatus rv; SECKEYPublicKey *publickey; + + publickey = CERT_ExtractPublicKey(cert); + if (publickey == NULL) + return SECFailure; + + rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, publickey, bulkkey, encKey); + SECKEY_DestroyPublicKey(publickey); + return rv; +} + +SECStatus +NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp, + SECKEYPublicKey *publickey, + PK11SymKey *bulkkey, SECItem *encKey) +{ + SECStatus rv; int data_len; + KeyType keyType; void *mark = NULL; - /* sanity check */ - certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); - PORT_Assert(certalgtag == SEC_OID_PKCS1_RSA_ENCRYPTION); - encalgtag = SEC_OID_PKCS1_RSA_ENCRYPTION; - publickey = CERT_ExtractPublicKey(cert); - if (publickey == NULL) - goto loser; - mark = PORT_ArenaMark(poolp); if (!mark) goto loser; + /* sanity check */ + keyType = SECKEY_GetPublicKeyType(publickey); + PORT_Assert(keyType == rsaKey); + if (keyType != rsaKey) { + goto loser; + } /* allocate memory for the encrypted key */ data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */ encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len); @@ -90,7 +104,6 @@ NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, PK11Sym rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION), publickey, bulkkey, encKey); - SECKEY_DestroyPublicKey(publickey); if (rv != SECSuccess) goto loser; diff --git a/security/nss/lib/smime/cmsrecinfo.c b/security/nss/lib/smime/cmsrecinfo.c index dfe05a07a..d6270da25 100644 --- a/security/nss/lib/smime/cmsrecinfo.c +++ b/security/nss/lib/smime/cmsrecinfo.c @@ -47,14 +47,24 @@ #include "pk11func.h" #include "secerr.h" -/* - * NSS_CMSRecipientInfo_Create - create a recipientinfo - * - * we currently do not create KeyAgreement recipientinfos with multiple recipientEncryptedKeys - * the certificate is supposed to have been verified by the caller - */ +PRBool +nss_cmsrecipientinfo_usessubjectkeyid(NSSCMSRecipientInfo *ri) +{ + if (ri->recipientInfoType == NSSCMSRecipientInfoID_KeyTrans) { + NSSCMSRecipientIdentifier *rid; + rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier; + if (rid->identifierType == NSSCMSRecipientID_SubjectKeyID) { + return PR_TRUE; + } + } + return PR_FALSE; +} + + NSSCMSRecipientInfo * -NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert) +nss_cmsrecipientinfo_create(NSSCMSMessage *cmsg, NSSCMSRecipientIDSelector type, + CERTCertificate *cert, SECKEYPublicKey *pubKey, + SECItem *subjKeyID) { NSSCMSRecipientInfo *ri; void *mark; @@ -65,6 +75,8 @@ NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert) unsigned long version; SECItem *dummy; PLArenaPool *poolp; + CERTSubjectPublicKeyInfo *spki, *freeSpki = NULL; + NSSCMSRecipientIdentifier *rid; poolp = cmsg->poolp; @@ -75,26 +87,64 @@ NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert) goto loser; ri->cmsg = cmsg; - ri->cert = CERT_DupCertificate(cert); - if (ri->cert == NULL) - goto loser; + if (type == NSSCMSRecipientID_IssuerSN) { + ri->cert = CERT_DupCertificate(cert); + if (ri->cert == NULL) + goto loser; + spki = &(cert->subjectPublicKeyInfo); + } else { + PORT_Assert(pubKey); + spki = freeSpki = SECKEY_CreateSubjectPublicKeyInfo(pubKey); + } - certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); + certalgtag = SECOID_GetAlgorithmTag(&(spki->algorithm)); + rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier; switch (certalgtag) { case SEC_OID_PKCS1_RSA_ENCRYPTION: ri->recipientInfoType = NSSCMSRecipientInfoID_KeyTrans; - /* hardcoded issuerSN choice for now */ - ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType = NSSCMSRecipientID_IssuerSN; - ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert); - if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) { + rid->identifierType = type; + if (type == NSSCMSRecipientID_IssuerSN) { + rid->id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert); + if (rid->id.issuerAndSN == NULL) { + break; + } + } else if (type == NSSCMSRecipientID_SubjectKeyID){ + NSSCMSKeyTransRecipientInfoEx *riExtra; + + rid->id.subjectKeyID = PORT_ArenaNew(poolp, SECItem); + if (rid->id.subjectKeyID == NULL) { + rv = SECFailure; + PORT_SetError(SEC_ERROR_NO_MEMORY); + break; + } + SECITEM_CopyItem(poolp, rid->id.subjectKeyID, subjKeyID); + if (rid->id.subjectKeyID->data == NULL) { + rv = SECFailure; + PORT_SetError(SEC_ERROR_NO_MEMORY); + break; + } + riExtra = &ri->ri.keyTransRecipientInfoEx; + riExtra->version = 0; + riExtra->pubKey = SECKEY_CopyPublicKey(pubKey); + if (riExtra->pubKey == NULL) { + rv = SECFailure; + PORT_SetError(SEC_ERROR_NO_MEMORY); + break; + } + } else { + PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; - break; } break; case SEC_OID_MISSI_KEA_DSS_OLD: case SEC_OID_MISSI_KEA_DSS: case SEC_OID_MISSI_KEA: + PORT_Assert(type != NSSCMSRecipientID_SubjectKeyID); + if (type == NSSCMSRecipientID_SubjectKeyID) { + rv = SECFailure; + break; + } /* backward compatibility - this is not really a keytrans operation */ ri->recipientInfoType = NSSCMSRecipientInfoID_KeyTrans; /* hardcoded issuerSN choice for now */ @@ -106,6 +156,11 @@ NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert) } break; case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */ + PORT_Assert(type != NSSCMSRecipientID_SubjectKeyID); + if (type == NSSCMSRecipientID_SubjectKeyID) { + rv = SECFailure; + break; + } /* a key agreement op */ ri->recipientInfoType = NSSCMSRecipientInfoID_KeyAgree; @@ -184,13 +239,70 @@ NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert) } PORT_ArenaUnmark (poolp, mark); + if (freeSpki) + SECKEY_DestroySubjectPublicKeyInfo(freeSpki); return ri; loser: + if (freeSpki) + SECKEY_DestroySubjectPublicKeyInfo(freeSpki); PORT_ArenaRelease (poolp, mark); return NULL; } +/* + * NSS_CMSRecipientInfo_Create - create a recipientinfo + * + * we currently do not create KeyAgreement recipientinfos with multiple + * recipientEncryptedKeys the certificate is supposed to have been + * verified by the caller + */ +NSSCMSRecipientInfo * +NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert) +{ + return nss_cmsrecipientinfo_create(cmsg, NSSCMSRecipientID_IssuerSN, cert, + NULL, NULL); +} + +NSSCMSRecipientInfo * +NSS_CMSRecipientInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, + SECItem *subjKeyID, + SECKEYPublicKey *pubKey) +{ + return nss_cmsrecipientinfo_create(cmsg, NSSCMSRecipientID_SubjectKeyID, + NULL, pubKey, subjKeyID); +} + +NSSCMSRecipientInfo * +NSS_CMSRecipientInfo_CreateWithSubjKeyIDFromCert(NSSCMSMessage *cmsg, + CERTCertificate *cert) +{ + SECKEYPublicKey *pubKey = NULL; + SECItem subjKeyID = {siBuffer, NULL, 0}; + NSSCMSRecipientInfo *retVal = NULL; + + if (!cmsg || !cert) { + return NULL; + } + pubKey = CERT_ExtractPublicKey(cert); + if (!pubKey) { + goto done; + } + if (CERT_FindSubjectKeyIDExtension(cert, &subjKeyID) != SECSuccess || + subjKeyID.data == NULL) { + goto done; + } + retVal = NSS_CMSRecipientInfo_CreateWithSubjKeyID(cmsg, &subjKeyID, pubKey); +done: + if (pubKey) + SECKEY_DestroyPublicKey(pubKey); + + if (subjKeyID.data) + SECITEM_FreeItem(&subjKeyID, PR_FALSE); + + return retVal; +} + void NSS_CMSRecipientInfo_Destroy(NSSCMSRecipientInfo *ri) { @@ -198,6 +310,14 @@ NSS_CMSRecipientInfo_Destroy(NSSCMSRecipientInfo *ri) /* issuerAndSN was allocated on the pool, so no need to destroy it */ if (ri->cert != NULL) CERT_DestroyCertificate(ri->cert); + + if (nss_cmsrecipientinfo_usessubjectkeyid(ri)) { + NSSCMSKeyTransRecipientInfoEx *extra; + extra = &ri->ri.keyTransRecipientInfoEx; + if (extra->pubKey) + SECKEY_DestroyPublicKey(extra->pubKey); + } + /* recipientInfo structure itself was allocated on the pool, so no need to destroy it */ /* we're done. */ } @@ -275,7 +395,8 @@ NSS_CMSRecipientInfo_GetKeyEncryptionAlgorithmTag(NSSCMSRecipientInfo *ri) } SECStatus -NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey, SECOidTag bulkalgtag) +NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey, + SECOidTag bulkalgtag) { CERTCertificate *cert; SECOidTag certalgtag; @@ -283,22 +404,46 @@ NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey, S SECItem *params = NULL; NSSCMSRecipientEncryptedKey *rek; NSSCMSOriginatorIdentifierOrKey *oiok; + CERTSubjectPublicKeyInfo *spki, *freeSpki = NULL; PLArenaPool *poolp; + NSSCMSKeyTransRecipientInfoEx *extra; + PRBool usesSubjKeyID; poolp = ri->cmsg->poolp; cert = ri->cert; - PORT_Assert (cert != NULL); - if (cert == NULL) + usesSubjKeyID = nss_cmsrecipientinfo_usessubjectkeyid(ri); + if (cert) { + spki = &cert->subjectPublicKeyInfo; + certalgtag = SECOID_GetAlgorithmTag(&(spki->algorithm)); + } else if (usesSubjKeyID) { + extra = &ri->ri.keyTransRecipientInfoEx; + /* sanity check */ + PORT_Assert(extra->pubKey); + if (!extra->pubKey) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + spki = freeSpki = SECKEY_CreateSubjectPublicKeyInfo(extra->pubKey); + certalgtag = SECOID_GetAlgorithmTag(&spki->algorithm); + } else { + PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; + } /* XXX set ri->recipientInfoType to the proper value here */ /* or should we look if it's been set already ? */ - certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); + certalgtag = SECOID_GetAlgorithmTag(&spki->algorithm); switch (certalgtag) { case SEC_OID_PKCS1_RSA_ENCRYPTION: /* wrap the symkey */ - if (NSS_CMSUtil_EncryptSymKey_RSA(poolp, cert, bulkkey, &ri->ri.keyTransRecipientInfo.encKey) != SECSuccess) { + if (usesSubjKeyID) { + rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, extra->pubKey, + bulkkey, &ri->ri.keyTransRecipientInfo.encKey); + if (rv != SECSuccess) + break; + } else if (NSS_CMSUtil_EncryptSymKey_RSA(poolp, cert, bulkkey, + &ri->ri.keyTransRecipientInfo.encKey) != SECSuccess) { rv = SECFailure; break; } @@ -353,6 +498,9 @@ NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey, S rv = SECFailure; break; } + if (freeSpki) + SECKEY_DestroySubjectPublicKeyInfo(freeSpki); + return rv; } diff --git a/security/nss/lib/smime/cmssiginfo.c b/security/nss/lib/smime/cmssiginfo.c index a9c46d07e..a858f9ae6 100644 --- a/security/nss/lib/smime/cmssiginfo.c +++ b/security/nss/lib/smime/cmssiginfo.c @@ -566,6 +566,7 @@ CERTCertificate * NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb) { CERTCertificate *cert; + NSSCMSSignerIdentifier *sid; if (signerinfo->cert != NULL) return signerinfo->cert; @@ -580,16 +581,13 @@ NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDB * we leave this function -- we let the clean-up of the entire * cinfo structure later do the destroy of this cert. */ - switch (signerinfo->signerIdentifier.identifierType) { + sid = &signerinfo->signerIdentifier; + switch (sid->identifierType) { case NSSCMSSignerID_IssuerSN: - cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->signerIdentifier.id.issuerAndSN); + cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN); break; case NSSCMSSignerID_SubjectKeyID: -#if 0 /* not yet implemented */ - cert = CERT_FindCertBySubjectKeyID(certdb, signerinfo->signerIdentifier.id.subjectKeyID); -#else - cert = NULL; -#endif + cert = CERT_FindCertBySubjKeyID(certdb, sid->id.subjectKeyID); break; default: cert = NULL; diff --git a/security/nss/lib/smime/cmst.h b/security/nss/lib/smime/cmst.h index 105ade583..2fa63a3b8 100644 --- a/security/nss/lib/smime/cmst.h +++ b/security/nss/lib/smime/cmst.h @@ -303,6 +303,18 @@ struct NSSCMSKeyTransRecipientInfoStr { }; typedef struct NSSCMSKeyTransRecipientInfoStr NSSCMSKeyTransRecipientInfo; +/* + * View comments before NSSCMSRecipientInfoStr for purpose of this + * structure. + */ +struct NSSCMSKeyTransRecipientInfoExStr { + NSSCMSKeyTransRecipientInfo recipientInfo; + int version; + SECKEYPublicKey *pubKey; +}; + +typedef struct NSSCMSKeyTransRecipientInfoExStr NSSCMSKeyTransRecipientInfoEx; + #define NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN 0 /* what we *create* */ #define NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY 2 /* what we *create* */ @@ -399,12 +411,35 @@ typedef enum { NSSCMSRecipientInfoID_KEK = 2 } NSSCMSRecipientInfoIDSelector; +/* + * In order to preserve backwards binary compatibility when implementing + * creation of Recipient Info's that uses subjectKeyID in the + * keyTransRecipientInfo we need to stash a public key pointer in this + * structure somewhere. We figured out that NSSCMSKeyTransRecipientInfo + * is the smallest member of the ri union. We're in luck since that's + * the very structure that would need to use the public key. So we created + * a new structure NSSCMSKeyTransRecipientInfoEx which has a member + * NSSCMSKeyTransRecipientInfo as the first member followed by a version + * and a public key pointer. This way we can keep backwards compatibility + * without changing the size of this structure. + * + * BTW, size of structure: + * NSSCMSKeyTransRecipientInfo: 9 ints, 4 pointers + * NSSCMSKeyAgreeRecipientInfo: 12 ints, 8 pointers + * NSSCMSKEKRecipientInfo: 10 ints, 7 pointers + * + * The new structure: + * NSSCMSKeyTransRecipientInfoEx: sizeof(NSSCMSKeyTransRecipientInfo) + + * 1 int, 1 pointer + */ + struct NSSCMSRecipientInfoStr { NSSCMSRecipientInfoIDSelector recipientInfoType; union { NSSCMSKeyTransRecipientInfo keyTransRecipientInfo; NSSCMSKeyAgreeRecipientInfo keyAgreeRecipientInfo; NSSCMSKEKRecipientInfo kekRecipientInfo; + NSSCMSKeyTransRecipientInfoEx keyTransRecipientInfoEx; } ri; /* --------- local; not part of encoding --------- */ NSSCMSMessage * cmsg; /* back pointer to message */ diff --git a/security/nss/lib/smime/smime.def b/security/nss/lib/smime/smime.def index dde59aaad..87ba37f6d 100644 --- a/security/nss/lib/smime/smime.def +++ b/security/nss/lib/smime/smime.def @@ -216,3 +216,10 @@ NSS_CMSSignerInfo_CreateWithSubjKeyID; ;+ local: ;+ *; ;+}; +;+NSS_3.7 { # NSS 3.7 release +;+ global: +NSS_CMSRecipientInfo_CreateWithSubjKeyID; +NSS_CMSRecipientInfo_CreateWithSubjKeyIDFromCert; +;+ local: +;+ *; +;+}; |