UniqueSECKEYPublicKey CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr; } SECItem spkiItem = { siBuffer, nullptr, 0 }; if (!aKeyData.ToSECItem(arena.get(), &spkiItem)) { return nullptr; } UniqueCERTSubjectPublicKeyInfo spki( SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem)); if (!spki) { return nullptr; } bool isECDHAlgorithm = SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH, &spki->algorithm.algorithm); bool isDHAlgorithm = SECITEM_ItemsAreEqual(&SEC_OID_DATA_DH_KEY_AGREEMENT, &spki->algorithm.algorithm); // Check for |id-ecDH| and |dhKeyAgreement|. Per the WebCrypto spec we must // support these OIDs but NSS does unfortunately not know about them. Let's // change the algorithm to |id-ecPublicKey| or |dhPublicKey| to make NSS happy. if (isECDHAlgorithm || isDHAlgorithm) { SECOidTag oid = SEC_OID_UNKNOWN; if (isECDHAlgorithm) { oid = SEC_OID_ANSIX962_EC_PUBLIC_KEY; } else if (isDHAlgorithm) { oid = SEC_OID_X942_DIFFIE_HELMAN_KEY; } else { MOZ_ASSERT(false); } SECOidData* oidData = SECOID_FindOIDByTag(oid); if (!oidData) { return nullptr; } SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm, &oidData->oid); if (rv != SECSuccess) { return nullptr; } } UniqueSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get())); if (!tmp.get() || !PublicKeyValid(tmp.get())) { return nullptr; } return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(tmp.get())); }
// The code that executes in the inner loop of BuildForward static Result BuildForwardInner(TrustDomain& trustDomain, BackCert& subject, PRTime time, KeyPurposeId requiredEKUIfPresent, const CertPolicyId& requiredPolicy, const SECItem& potentialIssuerDER, unsigned int subCACount, ScopedCERTCertList& results) { BackCert potentialIssuer(&subject, BackCert::IncludeCN::No); Result rv = potentialIssuer.Init(potentialIssuerDER); if (rv != Success) { return rv; } // RFC5280 4.2.1.1. Authority Key Identifier // RFC5280 4.2.1.2. Subject Key Identifier // Loop prevention, done as recommended by RFC4158 Section 5.2 // TODO: this doesn't account for subjectAltNames! // TODO(perf): This probably can and should be optimized in some way. bool loopDetected = false; for (BackCert* prev = potentialIssuer.childCert; !loopDetected && prev != nullptr; prev = prev->childCert) { if (SECITEM_ItemsAreEqual(&potentialIssuer.GetSubjectPublicKeyInfo(), &prev->GetSubjectPublicKeyInfo()) && SECITEM_ItemsAreEqual(&potentialIssuer.GetSubject(), &prev->GetSubject())) { return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER); // XXX: error code } } rv = CheckNameConstraints(potentialIssuer); if (rv != Success) { return rv; } rv = BuildForward(trustDomain, potentialIssuer, time, EndEntityOrCA::MustBeCA, KU_KEY_CERT_SIGN, requiredEKUIfPresent, requiredPolicy, nullptr, subCACount, results); if (rv != Success) { return rv; } return subject.VerifyOwnSignatureWithKey( trustDomain, potentialIssuer.GetSubjectPublicKeyInfo()); }
CERTCertificate * CERT_FindCertByKeyID(CERTCertDBHandle *handle, SECItem *name, SECItem *keyID) { CERTCertList *list; CERTCertificate *cert = NULL; CERTCertListNode *node, *head; list = CERT_CreateSubjectCertList(NULL, handle, name, 0, PR_FALSE); if (list == NULL) return NULL; node = head = CERT_LIST_HEAD(list); if (head) { do { if (node->cert && SECITEM_ItemsAreEqual(&node->cert->subjectKeyID, keyID)) { cert = CERT_DupCertificate(node->cert); goto done; } node = CERT_LIST_NEXT(node); } while (node && head != node); } PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); done: if (list) { CERT_DestroyCertList(list); } return cert; }
/* * SecCmsAlgArrayGetIndexByAlgTag - find a specific algorithm in an array of * algorithms. * * algorithmArray - array of algorithm IDs * algtag - algorithm tag of algorithm to pick * * Returns: * An integer containing the index of the algorithm in the array or -1 if * algorithm was not found. */ int SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray, SECOidTag algtag) { SECOidData *algid; int i = -1; if (algorithmArray == NULL || algorithmArray[0] == NULL) return i; #ifdef ORDER_N_SQUARED for (i = 0; algorithmArray[i] != NULL; i++) { algid = SECOID_FindOID(&(algorithmArray[i]->algorithm)); if (algid->offset == algtag) break; /* bingo */ } #else algid = SECOID_FindOIDByTag(algtag); if (!algid) return i; for (i = 0; algorithmArray[i] != NULL; i++) { if (SECITEM_ItemsAreEqual(&algorithmArray[i]->algorithm, &algid->oid)) break; /* bingo */ } #endif if (algorithmArray[i] == NULL) return -1; /* not found */ return i; }
SECKEYPublicKey* CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { ScopedSECItem spkiItem(aKeyData.ToSECItem()); if (!spkiItem) { return nullptr; } ScopedCERTSubjectPublicKeyInfo spki(SECKEY_DecodeDERSubjectPublicKeyInfo(spkiItem.get())); if (!spki) { return nullptr; } // Check for id-ecDH. Per the WebCrypto spec we must support it but NSS // does unfortunately not know about it. Let's change the algorithm to // id-ecPublicKey to make NSS happy. if (SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH, &spki->algorithm.algorithm)) { // Retrieve OID data for id-ecPublicKey (1.2.840.10045.2.1). SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PUBLIC_KEY); if (!oidData) { return nullptr; } SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm, &oidData->oid); if (rv != SECSuccess) { return nullptr; } } return SECKEY_ExtractPublicKey(spki.get()); }
/* * This is the key-compare function. It simply does a lexical * comparison on the item data. 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. */ PRIntn PR_CALLBACK SECITEM_HashCompare ( const void *k1, const void *k2) { const SECItem *i1 = (const SECItem *)k1; const SECItem *i2 = (const SECItem *)k2; return SECITEM_ItemsAreEqual(i1,i2); }
static nssCertIDMatch nss3certificate_matchIdentifier(nssDecodedCert *dc, void *id) { CERTCertificate *c = (CERTCertificate *)dc->data; CERTAuthKeyID *authKeyID = (CERTAuthKeyID *)id; SECItem skid; nssCertIDMatch match = nssCertIDMatch_Unknown; /* keyIdentifier */ if (authKeyID->keyID.len > 0 && CERT_FindSubjectKeyIDExtension(c, &skid) == SECSuccess) { PRBool skiEqual; skiEqual = SECITEM_ItemsAreEqual(&authKeyID->keyID, &skid); PORT_Free(skid.data); if (skiEqual) { /* change the state to positive match, but keep going */ match = nssCertIDMatch_Yes; } else { /* exit immediately on failure */ return nssCertIDMatch_No; } } /* issuer/serial (treated as pair) */ if (authKeyID->authCertIssuer) { SECItem *caName = NULL; SECItem *caSN = &authKeyID->authCertSerialNumber; caName = (SECItem *)CERT_GetGeneralNameByType( authKeyID->authCertIssuer, certDirectoryName, PR_TRUE); if (caName != NULL && SECITEM_ItemsAreEqual(&c->derIssuer, caName) && SECITEM_ItemsAreEqual(&c->serialNumber, caSN)) { match = nssCertIDMatch_Yes; } else { match = nssCertIDMatch_Unknown; } } return match; }
PRBool SEC_CertNicknameConflict(const char *nickname, const SECItem *derSubject, CERTCertDBHandle *handle) { CERTCertificate *cert; PRBool conflict = PR_FALSE; cert = CERT_FindCertByNickname(handle, nickname); if (!cert) { return conflict; } conflict = !SECITEM_ItemsAreEqual(derSubject, &cert->derSubject); CERT_DestroyCertificate(cert); return conflict; }
static CERTSignedCrl *get_issuer_crl(CERTCertDBHandle *handle, CERTCertificate *cert) { if (handle == NULL || cert == NULL) return NULL; DBG(DBG_X509, DBG_log("%s : looking for a CRL issued by %s", __FUNCTION__, cert->issuerName)); /* * Use SEC_LookupCrls method instead of SEC_FindCrlByName. * For some reason, SEC_FindCrlByName was giving out bad pointers! * * crl = (CERTSignedCrl *)SEC_FindCrlByName(handle, &searchName, SEC_CRL_TYPE); */ CERTCrlHeadNode *crl_list = NULL; if (SEC_LookupCrls(handle, &crl_list, SEC_CRL_TYPE) != SECSuccess) { return NULL; } CERTCrlNode *crl_node = crl_list->first; CERTSignedCrl *crl = NULL; while (crl_node != NULL) { if (crl_node->crl != NULL && SECITEM_ItemsAreEqual(&cert->derIssuer, &crl_node->crl->crl.derName)) { crl = crl_node->crl; DBG(DBG_X509, DBG_log("%s : CRL found", __FUNCTION__)); break; } crl_node = crl_node->next; } if (crl == NULL) { PORT_FreeArena(crl_list->arena, PR_FALSE); } return crl; }
// CertID ::= SEQUENCE { // hashAlgorithm AlgorithmIdentifier, // issuerNameHash OCTET STRING, -- Hash of issuer's DN // issuerKeyHash OCTET STRING, -- Hash of issuer's public key // serialNumber CertificateSerialNumber } static inline der::Result CertID(der::Input& input, const Context& context, /*out*/ bool& match) { match = false; SECAlgorithmID hashAlgorithm; if (der::Nested(input, der::SEQUENCE, bind(der::AlgorithmIdentifier, _1, ref(hashAlgorithm))) != der::Success) { return der::Failure; } SECItem issuerNameHash; if (der::Skip(input, der::OCTET_STRING, issuerNameHash) != der::Success) { return der::Failure; } SECItem issuerKeyHash; if (der::Skip(input, der::OCTET_STRING, issuerKeyHash) != der::Success) { return der::Failure; } SECItem serialNumber; if (der::CertificateSerialNumber(input, serialNumber) != der::Success) { return der::Failure; } const CERTCertificate& cert = context.cert; const CERTCertificate& issuerCert = context.issuerCert; if (!SECITEM_ItemsAreEqual(&serialNumber, &cert.serialNumber)) { // This does not reference the certificate we're interested in. // Consume the rest of the input and return successfully to // potentially continue processing other responses. input.SkipToEnd(); return der::Success; } // TODO: support SHA-2 hashes. SECOidTag hashAlg = SECOID_GetAlgorithmTag(&hashAlgorithm); if (hashAlg != SEC_OID_SHA1) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return der::Success; } if (issuerNameHash.len != SHA1_LENGTH) { return der::Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE); } // From http://tools.ietf.org/html/rfc6960#section-4.1.1: // "The hash shall be calculated over the DER encoding of the // issuer's name field in the certificate being checked." uint8_t hashBuf[SHA1_LENGTH]; if (PK11_HashBuf(SEC_OID_SHA1, hashBuf, cert.derIssuer.data, cert.derIssuer.len) != SECSuccess) { return der::Failure; } if (memcmp(hashBuf, issuerNameHash.data, issuerNameHash.len)) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return der::Success; } return MatchIssuerKey(issuerKeyHash, issuerCert, match); }
/* * return the certificate associated with a derCert */ SECItem * PK11_FindSMimeProfile(PK11SlotInfo **slot, char *emailAddr, SECItem *name, SECItem **profileTime) { CK_OBJECT_CLASS smimeClass = CKO_NETSCAPE_SMIME; CK_ATTRIBUTE theTemplate[] = { { CKA_SUBJECT, NULL, 0 }, { CKA_CLASS, NULL, 0 }, { CKA_NETSCAPE_EMAIL, NULL, 0 }, }; CK_ATTRIBUTE smimeData[] = { { CKA_SUBJECT, NULL, 0 }, { CKA_VALUE, NULL, 0 }, }; /* if you change the array, change the variable below as well */ int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); CK_OBJECT_HANDLE smimeh = CK_INVALID_HANDLE; CK_ATTRIBUTE *attrs = theTemplate; CK_RV crv; SECItem *emailProfile = NULL; if (!emailAddr || !emailAddr[0]) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } PK11_SETATTRS(attrs, CKA_SUBJECT, name->data, name->len); attrs++; PK11_SETATTRS(attrs, CKA_CLASS, &smimeClass, sizeof(smimeClass)); attrs++; PK11_SETATTRS(attrs, CKA_NETSCAPE_EMAIL, emailAddr, strlen(emailAddr)); attrs++; if (*slot) { smimeh = pk11_FindObjectByTemplate(*slot,theTemplate,tsize); } else { PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE,PR_TRUE,NULL); PK11SlotListElement *le; if (!list) { return NULL; } /* loop through all the slots */ for (le = list->head; le; le = le->next) { smimeh = pk11_FindObjectByTemplate(le->slot,theTemplate,tsize); if (smimeh != CK_INVALID_HANDLE) { *slot = PK11_ReferenceSlot(le->slot); break; } } PK11_FreeSlotList(list); } if (smimeh == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_NO_KRL); return NULL; } if (profileTime) { PK11_SETATTRS(smimeData, CKA_NETSCAPE_SMIME_TIMESTAMP, NULL, 0); } crv = PK11_GetAttributes(NULL,*slot,smimeh,smimeData,2); if (crv != CKR_OK) { PORT_SetError(PK11_MapError (crv)); goto loser; } if (!profileTime) { SECItem profileSubject; profileSubject.data = (unsigned char*) smimeData[0].pValue; profileSubject.len = smimeData[0].ulValueLen; if (!SECITEM_ItemsAreEqual(&profileSubject,name)) { goto loser; } } emailProfile = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); if (emailProfile == NULL) { goto loser; } emailProfile->data = (unsigned char*) smimeData[1].pValue; emailProfile->len = smimeData[1].ulValueLen; if (profileTime) { *profileTime = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); if (*profileTime) { (*profileTime)->data = (unsigned char*) smimeData[0].pValue; (*profileTime)->len = smimeData[0].ulValueLen; } } loser: if (emailProfile == NULL) { if (smimeData[1].pValue) { PORT_Free(smimeData[1].pValue); } } if (profileTime == NULL || *profileTime == NULL) { if (smimeData[0].pValue) { PORT_Free(smimeData[0].pValue); } } return emailProfile; }
// The code that executes in the inner loop of BuildForward static Result BuildForwardInner(TrustDomain& trustDomain, BackCert& subject, PRTime time, EndEntityOrCA endEntityOrCA, SECOidTag requiredEKUIfPresent, SECOidTag requiredPolicy, CERTCertificate* potentialIssuerCertToDup, /*optional*/ const SECItem* stapledOCSPResponse, unsigned int subCACount, ScopedCERTCertList& results) { PORT_Assert(potentialIssuerCertToDup); BackCert potentialIssuer(potentialIssuerCertToDup, &subject, BackCert::ExcludeCN); Result rv = potentialIssuer.Init(); if (rv != Success) { return rv; } // RFC5280 4.2.1.1. Authority Key Identifier // RFC5280 4.2.1.2. Subject Key Identifier // Loop prevention, done as recommended by RFC4158 Section 5.2 // TODO: this doesn't account for subjectAltNames! // TODO(perf): This probably can and should be optimized in some way. bool loopDetected = false; for (BackCert* prev = potentialIssuer.childCert; !loopDetected && prev != nullptr; prev = prev->childCert) { if (SECITEM_ItemsAreEqual(&potentialIssuer.GetNSSCert()->derPublicKey, &prev->GetNSSCert()->derPublicKey) && SECITEM_ItemsAreEqual(&potentialIssuer.GetNSSCert()->derSubject, &prev->GetNSSCert()->derSubject)) { return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER); // XXX: error code } } rv = CheckNameConstraints(potentialIssuer); if (rv != Success) { return rv; } unsigned int newSubCACount = subCACount; if (endEntityOrCA == MustBeCA) { newSubCACount = subCACount + 1; } else { PR_ASSERT(newSubCACount == 0); } rv = BuildForward(trustDomain, potentialIssuer, time, MustBeCA, KU_KEY_CERT_SIGN, requiredEKUIfPresent, requiredPolicy, nullptr, newSubCACount, results); if (rv != Success) { return rv; } if (trustDomain.VerifySignedData(&subject.GetNSSCert()->signatureWrap, potentialIssuer.GetNSSCert()) != SECSuccess) { return MapSECStatus(SECFailure); } return Success; }
// The code that executes in the inner loop of BuildForward SECStatus PathBuildingStep::Check(const SECItem& potentialIssuerDER, /*optional*/ const SECItem* additionalNameConstraints, /*out*/ bool& keepGoing) { BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA, &subject); Result rv = potentialIssuer.Init(); if (rv != Success) { return RecordResult(PR_GetError(), keepGoing); } // RFC5280 4.2.1.1. Authority Key Identifier // RFC5280 4.2.1.2. Subject Key Identifier // Loop prevention, done as recommended by RFC4158 Section 5.2 // TODO: this doesn't account for subjectAltNames! // TODO(perf): This probably can and should be optimized in some way. bool loopDetected = false; for (const BackCert* prev = potentialIssuer.childCert; !loopDetected && prev != nullptr; prev = prev->childCert) { if (SECITEM_ItemsAreEqual(&potentialIssuer.GetSubjectPublicKeyInfo(), &prev->GetSubjectPublicKeyInfo()) && SECITEM_ItemsAreEqual(&potentialIssuer.GetSubject(), &prev->GetSubject())) { // XXX: error code return RecordResult(SEC_ERROR_UNKNOWN_ISSUER, keepGoing); } } if (potentialIssuer.GetNameConstraints()) { rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(), subject, requiredEKUIfPresent); if (rv != Success) { return RecordResult(PR_GetError(), keepGoing); } } if (additionalNameConstraints) { rv = CheckNameConstraints(*additionalNameConstraints, subject, requiredEKUIfPresent); if (rv != Success) { return RecordResult(PR_GetError(), keepGoing); } } // RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the // subject public key MUST NOT be used to verify signatures on certificates // or CRLs unless the corresponding keyCertSign or cRLSign bit is set." rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign, requiredEKUIfPresent, requiredPolicy, nullptr, subCACount); if (rv != Success) { return RecordResult(PR_GetError(), keepGoing); } SECStatus srv = trustDomain.VerifySignedData( subject.GetSignedData(), potentialIssuer.GetSubjectPublicKeyInfo()); if (srv != SECSuccess) { return RecordResult(PR_GetError(), keepGoing); } CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(), subject.GetSerialNumber()); srv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time, stapledOCSPResponse, subject.GetAuthorityInfoAccess()); if (srv != SECSuccess) { return RecordResult(PR_GetError(), keepGoing); } return RecordResult(0/*PRErrorCode::success*/, keepGoing); }
// CertID ::= SEQUENCE { // hashAlgorithm AlgorithmIdentifier, // issuerNameHash OCTET STRING, -- Hash of issuer's DN // issuerKeyHash OCTET STRING, -- Hash of issuer's public key // serialNumber CertificateSerialNumber } static inline Result CertID(Input& input, const Context& context, /*out*/ bool& match) { match = false; DigestAlgorithm hashAlgorithm; Result rv = der::DigestAlgorithmIdentifier(input, hashAlgorithm); if (rv != Success) { if (PR_GetError() == SEC_ERROR_INVALID_ALGORITHM) { // Skip entries that are hashed with algorithms we don't support. input.SkipToEnd(); return Success; } return rv; } SECItem issuerNameHash; rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerNameHash); if (rv != Success) { return rv; } SECItem issuerKeyHash; rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerKeyHash); if (rv != Success) { return rv; } SECItem serialNumber; rv = der::CertificateSerialNumber(input, serialNumber); if (rv != Success) { return rv; } if (!SECITEM_ItemsAreEqual(&serialNumber, &context.certID.serialNumber)) { // This does not reference the certificate we're interested in. // Consume the rest of the input and return successfully to // potentially continue processing other responses. input.SkipToEnd(); return Success; } // TODO: support SHA-2 hashes. if (hashAlgorithm != DigestAlgorithm::sha1) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return Success; } if (issuerNameHash.len != TrustDomain::DIGEST_LENGTH) { return Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE); } // From http://tools.ietf.org/html/rfc6960#section-4.1.1: // "The hash shall be calculated over the DER encoding of the // issuer's name field in the certificate being checked." uint8_t hashBuf[TrustDomain::DIGEST_LENGTH]; if (context.trustDomain.DigestBuf(context.certID.issuer, hashBuf, sizeof(hashBuf)) != SECSuccess) { return MapSECStatus(SECFailure); } if (memcmp(hashBuf, issuerNameHash.data, issuerNameHash.len)) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return Success; } return MatchKeyHash(context.trustDomain, issuerKeyHash, context.certID.issuerSubjectPublicKeyInfo, match); }