/* Initialize the extension data block. */ void ssl3_InitExtensionData(TLSExtensionData *xtnData, const sslSocket *ss) { unsigned int advertisedMax; PRCList *cursor; /* Set things up to the right starting state. */ PORT_Memset(xtnData, 0, sizeof(*xtnData)); xtnData->peerSupportsFfdheGroups = PR_FALSE; PR_INIT_CLIST(&xtnData->remoteKeyShares); /* Allocate enough to allow for native extensions, plus any custom ones. */ if (ss->sec.isServer) { advertisedMax = PR_MAX(PR_ARRAY_SIZE(certificateRequestHandlers), PR_ARRAY_SIZE(tls13_cert_req_senders)); } else { advertisedMax = PR_MAX(PR_ARRAY_SIZE(clientHelloHandlers), PR_ARRAY_SIZE(clientHelloSendersTLS)); ++advertisedMax; /* For the RI SCSV, which we also track. */ } for (cursor = PR_NEXT_LINK(&ss->extensionHooks); cursor != &ss->extensionHooks; cursor = PR_NEXT_LINK(cursor)) { ++advertisedMax; } xtnData->advertised = PORT_ZNewArray(PRUint16, advertisedMax); }
bool TransportLayerDtls::SetupCipherSuites(PRFileDesc* ssl_fd) const { SECStatus rv; // Set the SRTP ciphers if (!srtp_ciphers_.empty()) { // Note: std::vector is guaranteed to contiguous rv = SSL_SetSRTPCiphers(ssl_fd, &srtp_ciphers_[0], srtp_ciphers_.size()); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set SRTP cipher suite"); return false; } } for (size_t i = 0; i < PR_ARRAY_SIZE(EnabledCiphers); ++i) { MOZ_MTLOG(ML_INFO, LAYER_INFO << "Enabling: " << EnabledCiphers[i]); rv = SSL_CipherPrefSet(ssl_fd, EnabledCiphers[i], PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Unable to enable suite: " << EnabledCiphers[i]); return false; } } // Don't remove suites; TODO([email protected]) restore; bug 1052610 #if 0 for (size_t i = 0; i < PR_ARRAY_SIZE(DisabledCiphers); ++i) { MOZ_MTLOG(ML_INFO, LAYER_INFO << "Disabling: " << DisabledCiphers[i]); PRBool enabled = false; rv = SSL_CipherPrefGet(ssl_fd, DisabledCiphers[i], &enabled); if (rv != SECSuccess) { MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Unable to check if suite is enabled: " << DisabledCiphers[i]); return false; } if (enabled) { rv = SSL_CipherPrefSet(ssl_fd, DisabledCiphers[i], PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Unable to disable suite: " << DisabledCiphers[i]); return false; } } } #endif return true; }
void RegisterErrorTable() { static const struct PRErrorMessage ErrorTableText[] = { { "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE", "The server uses key pinning (HPKP) but no trusted certificate chain " "could be constructed that matches the pinset. Key pinning violations " "cannot be overridden." }, { "MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY", "The server uses a certificate with a basic constraints extension " "identifying it as a certificate authority. For a properly-issued " "certificate, this should not be the case." }, { "MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE", "The server presented a certificate with a key size that is too small " "to establish a secure connection." } }; static const struct PRErrorTable ErrorTable = { ErrorTableText, "pkixerrors", ERROR_BASE, PR_ARRAY_SIZE(ErrorTableText) }; (void) PR_ErrorInstallTable(&ErrorTable); }
const ssl3MACDef * ssl_GetMacDefByAlg(SSL3MACAlgorithm mac) { /* Cast here for clang: https://bugs.llvm.org/show_bug.cgi?id=16154 */ PORT_Assert((size_t)mac < PR_ARRAY_SIZE(ssl_mac_defs)); PORT_Assert(ssl_mac_defs[mac].mac == mac); return &ssl_mac_defs[mac]; }
const ssl3BulkCipherDef * ssl_GetBulkCipherDef(const ssl3CipherSuiteDef *suiteDef) { SSL3BulkCipher bulkCipher = suiteDef->bulk_cipher_alg; PORT_Assert(bulkCipher < PR_ARRAY_SIZE(ssl_bulk_cipher_defs)); PORT_Assert(ssl_bulk_cipher_defs[bulkCipher].cipher == bulkCipher); return &ssl_bulk_cipher_defs[bulkCipher]; }
// Returns the preload list entry for the given host, if it exists. // Only does exact host matching - the user must decide how to use the returned // data. May return null. const nsSTSPreload * nsStrictTransportSecurityService::GetPreloadListEntry(const char *aHost) { return (const nsSTSPreload *) bsearch(aHost, kSTSPreloadList, PR_ARRAY_SIZE(kSTSPreloadList), sizeof(nsSTSPreload), STSPreloadCompare); }
void RegisterErrorTable() { // Note that these error strings are not localizable. // When these strings change, update the localization information too. static const PRErrorMessage ErrorTableText[] = { { "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE", "The server uses key pinning (HPKP) but no trusted certificate chain " "could be constructed that matches the pinset. Key pinning violations " "cannot be overridden." }, { "MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY", "The server uses a certificate with a basic constraints extension " "identifying it as a certificate authority. For a properly-issued " "certificate, this should not be the case." }, { "MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE", "The server presented a certificate with a key size that is too small " "to establish a secure connection." }, { "MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA", "An X.509 version 1 certificate that is not a trust anchor was used to " "issue the server's certificate. X.509 version 1 certificates are " "deprecated and should not be used to sign other certificates." }, { "MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH", "The certificate is not valid for the given email address." }, { "MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE", "The server presented a certificate that is not yet valid." }, { "MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE", "A certificate that is not yet valid was used to issue the server's " "certificate." }, { "MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH", "The signature algorithm in the signature field of the certificate does " "not match the algorithm in its signatureAlgorithm field." }, { "MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING", "The OCSP response does not include a status for the certificate being " "verified." }, { "MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG", "The server presented a certificate that is valid for too long." }, { "MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING", "A required TLS feature is missing." }, { "MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING", "The server presented a certificate that contains an invalid encoding of " "an integer. Common causes include negative serial numbers, negative RSA " "moduli, and encodings that are longer than necessary." }, }; // Note that these error strings are not localizable. // When these strings change, update the localization information too. static const PRErrorTable ErrorTable = { ErrorTableText, "pkixerrors", ERROR_BASE, PR_ARRAY_SIZE(ErrorTableText) }; (void) PR_ErrorInstallTable(&ErrorTable); }
static SSLExtensionSupport ssl_GetExtensionSupport(PRUint16 type) { unsigned int i; for (i = 0; i < PR_ARRAY_SIZE(ssl_supported_extensions); ++i) { if (type == ssl_supported_extensions[i].type) { return ssl_supported_extensions[i].support; } } return ssl_ext_none; }
static PRStatus ssl_ECRegister(void) { SECStatus rv; PORT_Assert(PR_ARRAY_SIZE(gECDHEKeyPairs) == ssl_named_group_count); rv = NSS_RegisterShutdown(ssl_ShutdownECDHECurves, gECDHEKeyPairs); if (rv != SECSuccess) { gECDHEInitError = PORT_GetError(); } return (PRStatus)rv; }
static bool isEVPolicy(SECOidTag policyOIDTag) { for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) { nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV]; if (policyOIDTag == entry.oid_tag) { return true; } } return false; }
// BasicOCSPResponse ::= SEQUENCE { // tbsResponseData ResponseData, // signatureAlgorithm AlgorithmIdentifier, // signature BIT STRING, // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } Result BasicResponse(Input& input, Context& context) { Input tbsResponseData; SignedDataWithSignature signedData; Result rv = der::SignedData(input, tbsResponseData, signedData); if (rv != Success) { if (PR_GetError() == SEC_ERROR_BAD_SIGNATURE) { return Fail(RecoverableError, SEC_ERROR_OCSP_BAD_SIGNATURE); } return rv; } // Parse certificates, if any SECItem certs[8]; size_t numCerts = 0; if (!input.AtEnd()) { // We ignore the lengths of the wrappers because we'll detect bad lengths // during parsing--too short and we'll run out of input for parsing a cert, // and too long and we'll have leftover data that won't parse as a cert. // [0] wrapper rv = der::ExpectTagAndSkipLength( input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0); if (rv != Success) { return rv; } // SEQUENCE wrapper rv = der::ExpectTagAndSkipLength(input, der::SEQUENCE); if (rv != Success) { return rv; } // sequence of certificates while (!input.AtEnd()) { if (numCerts == PR_ARRAY_SIZE(certs)) { return Fail(SEC_ERROR_BAD_DER); } rv = der::ExpectTagAndGetTLV(input, der::SEQUENCE, certs[numCerts]); if (rv != Success) { return rv; } ++numCerts; } } return ResponseData(tbsResponseData, context, signedData, certs, numCerts); }
void CleanupIdentityInfo() { for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) { nsMyTrustedEVInfo &entry = myTrustedEVInfos[iEV]; if (entry.cert) { CERT_DestroyCertificate(entry.cert); entry.cert = nullptr; } } memset(&sIdentityInfoCallOnce, 0, sizeof(PRCallOnceType)); }
/* function to clear out the lists */ static SECStatus ssl_ShutdownECDHECurves(void *appData, void *nssData) { int i; for (i = 0; i < PR_ARRAY_SIZE(gECDHEKeyPairs); i++) { if (gECDHEKeyPairs[i].pair) { ssl_FreeEphemeralKeyPair(gECDHEKeyPairs[i].pair); } } memset(gECDHEKeyPairs, 0, sizeof(gECDHEKeyPairs)); return SECSuccess; }
CERTCertList* GetRootsForOid(SECOidTag oid_tag) { CERTCertList* certList = CERT_NewCertList(); if (!certList) return nullptr; for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) { nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV]; if (entry.oid_tag == oid_tag) { addToCertListIfTrusted(certList, entry.cert); } } return certList; }
void RegisterErrorTable() { static const struct PRErrorMessage ErrorTableText[] = { { "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE", "The server uses key pinning (HPKP) but no trusted certificate chain " "could be constructed that matches the pinset. Key pinning violations " "cannot be overridden." } }; static const struct PRErrorTable ErrorTable = { ErrorTableText, "pkixerrors", ERROR_BASE, PR_ARRAY_SIZE(ErrorTableText) }; (void) PR_ErrorInstallTable(&ErrorTable); }
// Returns the preload list entry for the given host, if it exists. // Only does exact host matching - the user must decide how to use the returned // data. May return null. const nsSTSPreload * nsStrictTransportSecurityService::GetPreloadListEntry(const char *aHost) { PRTime currentTime = PR_Now(); int32_t timeOffset = 0; nsresult rv = mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", &timeOffset); if (NS_SUCCEEDED(rv)) { currentTime += (PRTime(timeOffset) * PR_USEC_PER_SEC); } if (mUsePreloadList && currentTime < gPreloadListExpirationTime) { return (const nsSTSPreload *) bsearch(aHost, kSTSPreloadList, PR_ARRAY_SIZE(kSTSPreloadList), sizeof(nsSTSPreload), STSPreloadCompare); } return nullptr; }
bool CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert, const mozilla::pkix::CertPolicyId& policy) { PR_ASSERT(cert); if (!cert) { return false; } for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) { nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV]; if (entry.cert && CERT_CompareCerts(cert, entry.cert)) { const SECOidData* oidData = SECOID_FindOIDByTag(entry.oid_tag); if (oidData && oidData->oid.len == policy.numBytes && !memcmp(oidData->oid.data, policy.bytes, policy.numBytes)) { return true; } } } return false; }
void _PR_DumpThreads(PRFileDesc *fd) { PRThread *t; PRIntn i; _PR_DumpPrintf(fd, "Current Thread:\n"); t = _PR_MD_CURRENT_THREAD(); _PR_DumpThread(fd, t); _PR_DumpPrintf(fd, "Runnable Threads:\n"); for (i = 0; i < PR_ARRAY_SIZE(_PR_RUNQ(t->cpu)); i++) { DumpThreadQueue(fd, &_PR_RUNQ(t->cpu)[i]); } _PR_DumpPrintf(fd, "CondVar timed wait Threads:\n"); DumpThreadQueue(fd, &_PR_SLEEPQ(t->cpu)); _PR_DumpPrintf(fd, "CondVar wait Threads:\n"); DumpThreadQueue(fd, &_PR_PAUSEQ(t->cpu)); _PR_DumpPrintf(fd, "Suspended Threads:\n"); DumpThreadQueue(fd, &_PR_SUSPENDQ(t->cpu)); }
SECKEYPrivateKey* CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY; CK_BBOOL falseValue = CK_FALSE; if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) { // Verify that all of the required parameters are present CryptoBuffer x, y, d; if (!aJwk.mCrv.WasPassed() || !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) || !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value())) || !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value()))) { return nullptr; } nsString namedCurve; if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) { return nullptr; } ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr; } // Create parameters. SECItem* params = CreateECParamsForCurve(namedCurve, arena.get()); if (!params) { return nullptr; } SECItem* ecPoint = CreateECPointForCoordinates(x, y, arena.get()); if (!ecPoint) { return nullptr; } // Compute the ID for this key // This is generated with a SHA-1 hash, so unlikely to collide ScopedSECItem objID(PK11_MakeIDFromPubKey(ecPoint)); if (!objID.get()) { return nullptr; } // Populate template from parameters CK_KEY_TYPE ecValue = CKK_EC; CK_ATTRIBUTE keyTemplate[9] = { { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) }, { CKA_KEY_TYPE, &ecValue, sizeof(ecValue) }, { CKA_TOKEN, &falseValue, sizeof(falseValue) }, { CKA_SENSITIVE, &falseValue, sizeof(falseValue) }, { CKA_PRIVATE, &falseValue, sizeof(falseValue) }, { CKA_ID, objID->data, objID->len }, { CKA_EC_PARAMS, params->data, params->len }, { CKA_EC_POINT, ecPoint->data, ecPoint->len }, { CKA_VALUE, (void*) d.Elements(), d.Length() }, }; return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate, PR_ARRAY_SIZE(keyTemplate)); } if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) { // Verify that all of the required parameters are present CryptoBuffer n, e, d, p, q, dp, dq, qi; if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) || !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) || !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) || !aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) || !aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) || !aJwk.mDp.WasPassed() || NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) || !aJwk.mDq.WasPassed() || NS_FAILED(dq.FromJwkBase64(aJwk.mDq.Value())) || !aJwk.mQi.WasPassed() || NS_FAILED(qi.FromJwkBase64(aJwk.mQi.Value()))) { return nullptr; } ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr; } // Compute the ID for this key // This is generated with a SHA-1 hash, so unlikely to collide SECItem nItem = { siBuffer, nullptr, 0 }; if (!n.ToSECItem(arena, &nItem)) { return nullptr; } ScopedSECItem objID(PK11_MakeIDFromPubKey(&nItem)); if (!objID.get()) { return nullptr; } // Populate template from parameters CK_KEY_TYPE rsaValue = CKK_RSA; CK_ATTRIBUTE keyTemplate[14] = { { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) }, { CKA_KEY_TYPE, &rsaValue, sizeof(rsaValue) }, { CKA_TOKEN, &falseValue, sizeof(falseValue) }, { CKA_SENSITIVE, &falseValue, sizeof(falseValue) }, { CKA_PRIVATE, &falseValue, sizeof(falseValue) }, { CKA_ID, objID->data, objID->len }, { CKA_MODULUS, (void*) n.Elements(), n.Length() }, { CKA_PUBLIC_EXPONENT, (void*) e.Elements(), e.Length() }, { CKA_PRIVATE_EXPONENT, (void*) d.Elements(), d.Length() }, { CKA_PRIME_1, (void*) p.Elements(), p.Length() }, { CKA_PRIME_2, (void*) q.Elements(), q.Length() }, { CKA_EXPONENT_1, (void*) dp.Elements(), dp.Length() }, { CKA_EXPONENT_2, (void*) dq.Elements(), dq.Length() }, { CKA_COEFFICIENT, (void*) qi.Elements(), qi.Length() }, }; return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate, PR_ARRAY_SIZE(keyTemplate)); } return nullptr; }
SECItem* CreateEncodedOCSPRequest(PLArenaPool* arena, const CERTCertificate* cert, const CERTCertificate* issuerCert) { if (!arena || !cert || !issuerCert) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return nullptr; } // We do not add any extensions to the request. // RFC 6960 says "An OCSP client MAY wish to specify the kinds of response // types it understands. To do so, it SHOULD use an extension with the OID // id-pkix-ocsp-response." This use of MAY and SHOULD is unclear. MSIE11 // on Windows 8.1 does not include any extensions, whereas NSS has always // included the id-pkix-ocsp-response extension. Avoiding the sending the // extension is better for OCSP GET because it makes the request smaller, // and thus more likely to fit within the 255 byte limit for OCSP GET that // is specified in RFC 5019 Section 5. // Bug 966856: Add the id-pkix-ocsp-pref-sig-algs extension. // Since we don't know whether the OCSP responder supports anything other // than SHA-1, we have no choice but to use SHA-1 for issuerNameHash and // issuerKeyHash. static const uint8_t hashAlgorithm[11] = { 0x30, 0x09, // SEQUENCE 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJECT IDENTIFIER id-sha1 0x05, 0x00, // NULL }; static const uint8_t hashLen = SHA1_LENGTH; static const unsigned int totalLenWithoutSerialNumberData = 2 // OCSPRequest + 2 // tbsRequest + 2 // requestList + 2 // Request + 2 // reqCert (CertID) + PR_ARRAY_SIZE(hashAlgorithm) // hashAlgorithm + 2 + hashLen // issuerNameHash + 2 + hashLen // issuerKeyHash + 2; // serialNumber (header) // The only way we could have a request this large is if the serialNumber was // ridiculously and unreasonably large. RFC 5280 says "Conforming CAs MUST // NOT use serialNumber values longer than 20 octets." With this restriction, // we allow for some amount of non-conformance with that requirement while // still ensuring we can encode the length values in the ASN.1 TLV structures // in a single byte. if (issuerCert->serialNumber.len > 127u - totalLenWithoutSerialNumberData) { PR_SetError(SEC_ERROR_BAD_DATA, 0); return nullptr; } uint8_t totalLen = static_cast<uint8_t>(totalLenWithoutSerialNumberData + cert->serialNumber.len); SECItem* encodedRequest = SECITEM_AllocItem(arena, nullptr, totalLen); if (!encodedRequest) { return nullptr; } uint8_t* d = encodedRequest->data; *d++ = 0x30; *d++ = totalLen - 2; // OCSPRequest (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 4; // tbsRequest (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 6; // requestList (SEQUENCE OF) *d++ = 0x30; *d++ = totalLen - 8; // Request (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 10; // reqCert (CertID SEQUENCE) // reqCert.hashAlgorithm for (size_t i = 0; i < PR_ARRAY_SIZE(hashAlgorithm); ++i) { *d++ = hashAlgorithm[i]; } // reqCert.issuerNameHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen; if (PK11_HashBuf(SEC_OID_SHA1, d, issuerCert->derSubject.data, issuerCert->derSubject.len) != SECSuccess) { return nullptr; } d += hashLen; // reqCert.issuerKeyHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen; SECItem key = issuerCert->subjectPublicKeyInfo.subjectPublicKey; DER_ConvertBitString(&key); if (PK11_HashBuf(SEC_OID_SHA1, d, key.data, key.len) != SECSuccess) { return nullptr; } d += hashLen; // reqCert.serialNumber (INTEGER) *d++ = 0x02; // INTEGER *d++ = static_cast<uint8_t>(cert->serialNumber.len); for (size_t i = 0; i < cert->serialNumber.len; ++i) { *d++ = cert->serialNumber.data[i]; } PR_ASSERT(d == encodedRequest->data + totalLen); return encodedRequest; }
// BasicOCSPResponse ::= SEQUENCE { // tbsResponseData ResponseData, // signatureAlgorithm AlgorithmIdentifier, // signature BIT STRING, // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } der::Result BasicResponse(der::Input& input, Context& context) { der::Input::Mark mark(input.GetMark()); uint16_t length; if (der::ExpectTagAndGetLength(input, der::SEQUENCE, length) != der::Success) { return der::Failure; } // The signature covers the entire DER encoding of tbsResponseData, including // the beginning tag and length. However, when we're parsing tbsResponseData, // we want to strip off the tag and length because we don't need it after // we've confirmed it's there and figured out what length it is. der::Input tbsResponseData; if (input.Skip(length, tbsResponseData) != der::Success) { return der::Failure; } CERTSignedData signedData; input.GetSECItem(siBuffer, mark, signedData.data); if (der::Nested(input, der::SEQUENCE, bind(der::AlgorithmIdentifier, _1, ref(signedData.signatureAlgorithm))) != der::Success) { return der::Failure; } if (der::Skip(input, der::BIT_STRING, signedData.signature) != der::Success) { return der::Failure; } if (signedData.signature.len == 0) { return der::Fail(SEC_ERROR_OCSP_BAD_SIGNATURE); } unsigned int unusedBitsAtEnd = signedData.signature.data[0]; // XXX: Really the constraint should be that unusedBitsAtEnd must be less // than 7. But, we suspect there are no valid OCSP response signatures with // non-zero unused bits. It seems like NSS assumes this in various places, so // we enforce it. If we find compatibility issues, we'll know we're wrong. if (unusedBitsAtEnd != 0) { return der::Fail(SEC_ERROR_OCSP_BAD_SIGNATURE); } ++signedData.signature.data; --signedData.signature.len; signedData.signature.len = (signedData.signature.len << 3); // Bytes to bits // Parse certificates, if any SECItem certs[8]; size_t numCerts = 0; if (!input.AtEnd()) { // We ignore the lengths of the wrappers because we'll detect bad lengths // during parsing--too short and we'll run out of input for parsing a cert, // and too long and we'll have leftover data that won't parse as a cert. // [0] wrapper if (der::ExpectTagAndIgnoreLength( input, der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0) != der::Success) { return der::Failure; } // SEQUENCE wrapper if (der::ExpectTagAndIgnoreLength(input, der::SEQUENCE) != der::Success) { return der::Failure; } // sequence of certificates while (!input.AtEnd()) { if (numCerts == PR_ARRAY_SIZE(certs)) { return der::Fail(SEC_ERROR_BAD_DER); } // Unwrap the SEQUENCE that contains the certificate, which is itself a // SEQUENCE. der::Input::Mark mark(input.GetMark()); if (der::Skip(input, der::SEQUENCE) != der::Success) { return der::Failure; } input.GetSECItem(siBuffer, mark, certs[numCerts]); ++numCerts; } } return ResponseData(tbsResponseData, context, signedData, certs, numCerts); }
/* * point validation is not necessary in general. But this checks a point (px) * against some known bad values. */ SECStatus ec_Curve25519_pt_validate(const SECItem *px) { PRUint8 *p; int i; PRUint8 forbiddenValues[12][32] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00 }, { 0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57 }, { 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }, { 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }, { 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }, { 0xcd, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x80 }, { 0x4c, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0xd7 }, { 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, { 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, { 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, }; if (px->len == 32) { p = px->data; } else { return SECFailure; } for (i = 0; i < PR_ARRAY_SIZE(forbiddenValues); ++i) { if (NSS_SecureMemcmp(p, forbiddenValues[i], px->len) == 0) { return SECFailure; } } return SECSuccess; }
SECKEYPrivateKey* CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { if (!aJwk.mKty.WasPassed() || !aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) { return nullptr; } // Verify that all of the required parameters are present CryptoBuffer n, e, d, p, q, dp, dq, qi; if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) || !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) || !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) || !aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) || !aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) || !aJwk.mDp.WasPassed() || NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) || !aJwk.mDq.WasPassed() || NS_FAILED(dq.FromJwkBase64(aJwk.mDq.Value())) || !aJwk.mQi.WasPassed() || NS_FAILED(qi.FromJwkBase64(aJwk.mQi.Value()))) { return nullptr; } // Compute the ID for this key // This is generated with a SHA-1 hash, so unlikely to collide ScopedSECItem nItem(n.ToSECItem()); ScopedSECItem objID(PK11_MakeIDFromPubKey(nItem.get())); if (!nItem.get() || !objID.get()) { return nullptr; } // Populate template from parameters CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY; CK_KEY_TYPE rsaValue = CKK_RSA; CK_BBOOL falseValue = CK_FALSE; CK_ATTRIBUTE keyTemplate[14] = { { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) }, { CKA_KEY_TYPE, &rsaValue, sizeof(rsaValue) }, { CKA_TOKEN, &falseValue, sizeof(falseValue) }, { CKA_SENSITIVE, &falseValue, sizeof(falseValue) }, { CKA_PRIVATE, &falseValue, sizeof(falseValue) }, { CKA_ID, objID->data, objID->len }, { CKA_MODULUS, (void*) n.Elements(), n.Length() }, { CKA_PUBLIC_EXPONENT, (void*) e.Elements(), e.Length() }, { CKA_PRIVATE_EXPONENT, (void*) d.Elements(), d.Length() }, { CKA_PRIME_1, (void*) p.Elements(), p.Length() }, { CKA_PRIME_2, (void*) q.Elements(), q.Length() }, { CKA_EXPONENT_1, (void*) dp.Elements(), dp.Length() }, { CKA_EXPONENT_2, (void*) dq.Elements(), dq.Length() }, { CKA_COEFFICIENT, (void*) qi.Elements(), qi.Length() }, }; // Create a generic object with the contents of the key ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot.get()) { return nullptr; } ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot.get(), keyTemplate, PR_ARRAY_SIZE(keyTemplate), PR_FALSE)); if (!obj.get()) { return nullptr; } // Have NSS translate the object to a private key by inspection // and make a copy we can own ScopedSECKEYPrivateKey privKey(PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr)); if (!privKey.get()) { return nullptr; } return SECKEY_CopyPrivateKey(privKey.get()); }
static SECStatus ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data) { PRInt32 list_len; unsigned int i; const sslNamedGroupDef *enabled[SSL_NAMED_GROUP_COUNT] = { 0 }; PORT_Assert(SSL_NAMED_GROUP_COUNT == PR_ARRAY_SIZE(enabled)); if (!data->data || data->len < 4) { (void)ssl3_DecodeError(ss); return SECFailure; } /* get the length of elliptic_curve_list */ list_len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); if (list_len < 0 || data->len != list_len || (data->len % 2) != 0) { (void)ssl3_DecodeError(ss); return SECFailure; } /* disable all groups and remember the enabled groups */ for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { enabled[i] = ss->namedGroupPreferences[i]; ss->namedGroupPreferences[i] = NULL; } /* Read groups from data and enable if in |enabled| */ while (data->len) { const sslNamedGroupDef *group; PRInt32 curve_name = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); if (curve_name < 0) { return SECFailure; /* fatal alert already sent */ } group = ssl_LookupNamedGroup(curve_name); if (group) { for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { if (enabled[i] && group == enabled[i]) { ss->namedGroupPreferences[i] = enabled[i]; break; } } } /* "Codepoints in the NamedCurve registry with a high byte of 0x01 (that * is, between 256 and 511 inclusive) are set aside for FFDHE groups," * -- https://tools.ietf.org/html/draft-ietf-tls-negotiated-ff-dhe-10 */ if ((curve_name & 0xff00) == 0x0100) { ss->ssl3.hs.peerSupportsFfdheGroups = PR_TRUE; } } /* Note: if ss->opt.requireDHENamedGroups is set, we disable DHE cipher * suites, but we do that in ssl3_config_match(). */ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 && !ss->opt.requireDHENamedGroups && !ss->ssl3.hs.peerSupportsFfdheGroups) { /* If we don't require that DHE use named groups, and no FFDHE was * included, we pretend that they support all the FFDHE groups we do. */ for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { if (enabled[i] && enabled[i]->keaType == ssl_kea_dh) { ss->namedGroupPreferences[i] = enabled[i]; } } } return SECSuccess; }
// We assume ext has been zero-initialized by its constructor and otherwise // not modified. // // TODO(perf): This sorting of extensions should be be moved into the // certificate decoder so that the results are cached with the certificate, so // that the decoding doesn't have to happen more than once per cert. Result BackCert::Init(const SECItem& certDER) { // XXX: Currently-known uses of mozilla::pkix create CERTCertificate objects // for all certs anyway, so the overhead of CERT_NewTempCertificate will be // reduced to a lookup in NSS's SECItem* -> CERTCertificate cache and // a CERT_DupCertificate. Eventually, we should parse the certificate using // mozilla::pkix::der and avoid the need to create a CERTCertificate at all. nssCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), const_cast<SECItem*>(&certDER), nullptr, false, true); if (!nssCert) { return MapSECStatus(SECFailure); } const CERTCertExtension* const* exts = nssCert->extensions; if (!exts) { return Success; } // We only decode v3 extensions for v3 certificates for two reasons. // 1. They make no sense in non-v3 certs // 2. An invalid cert can embed a basic constraints extension and the // check basic constrains will asume that this is valid. Making it // posible to create chains with v1 and v2 intermediates with is // not desirable. if (! (nssCert->version.len == 1 && nssCert->version.data[0] == mozilla::pkix::der::Version::v3)) { return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID); } const SECItem* dummyEncodedSubjectKeyIdentifier = nullptr; const SECItem* dummyEncodedAuthorityKeyIdentifier = nullptr; const SECItem* dummyEncodedAuthorityInfoAccess = nullptr; const SECItem* dummyEncodedSubjectAltName = nullptr; for (const CERTCertExtension* ext = *exts; ext; ext = *++exts) { const SECItem** out = nullptr; // python DottedOIDToCode.py id-ce 2.5.29 static const uint8_t id_ce[] = { 0x55, 0x1d }; // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1 static const uint8_t id_pe_authorityInfoAccess[] = { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }; if (ext->id.len == PR_ARRAY_SIZE(id_ce) + 1 && !memcmp(ext->id.data, id_ce, PR_ARRAY_SIZE(id_ce))) { switch (ext->id.data[ext->id.len - 1]) { case 14: out = &dummyEncodedSubjectKeyIdentifier; break; // bug 965136 case 15: out = &encodedKeyUsage; break; case 17: out = &dummyEncodedSubjectAltName; break; // bug 970542 case 19: out = &encodedBasicConstraints; break; case 30: out = &encodedNameConstraints; break; case 32: out = &encodedCertificatePolicies; break; case 35: out = &dummyEncodedAuthorityKeyIdentifier; break; // bug 965136 case 37: out = &encodedExtendedKeyUsage; break; case 54: out = &encodedInhibitAnyPolicy; break; // Bug 989051 } } else if (ext->id.len == PR_ARRAY_SIZE(id_pe_authorityInfoAccess) && !memcmp(ext->id.data, id_pe_authorityInfoAccess, PR_ARRAY_SIZE(id_pe_authorityInfoAccess))) { // We should remember the value of the encoded AIA extension here, but // since our TrustDomain implementations get the OCSP URI using // CERT_GetOCSPAuthorityInfoAccessLocation, we currently don't need to. out = &dummyEncodedAuthorityInfoAccess; } // If this is an extension we don't understand and it's marked critical, // we must reject this certificate. // (The only valid explicit value of the critical flag is TRUE because // it is defined as BOOLEAN DEFAULT FALSE, so we just assume it is true.) if (!out && ext->critical.data && ext->critical.len > 0) { return Fail(RecoverableError, SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); } if (out) { // This is an extension we understand. Save it in results unless we've // already found the extension previously. if (*out) { // Duplicate extension return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID); } *out = &ext->value; } } return Success; }
static PRStatus IdentityInfoInit() { for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) { nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV]; SECStatus rv; CERTIssuerAndSN ias; rv = ATOB_ConvertAsciiToItem(&ias.derIssuer, const_cast<char*>(entry.issuer_base64)); PR_ASSERT(rv == SECSuccess); if (rv != SECSuccess) { return PR_FAILURE; } rv = ATOB_ConvertAsciiToItem(&ias.serialNumber, const_cast<char*>(entry.serial_base64)); PR_ASSERT(rv == SECSuccess); if (rv != SECSuccess) { SECITEM_FreeItem(&ias.derIssuer, false); return PR_FAILURE; } ias.serialNumber.type = siUnsignedInteger; entry.cert = CERT_FindCertByIssuerAndSN(nullptr, &ias); SECITEM_FreeItem(&ias.derIssuer, false); SECITEM_FreeItem(&ias.serialNumber, false); // If an entry is missing in the NSS root database, it may be because the // root database is out of sync with what we expect (e.g. a different // version of system NSS is installed). We will just silently avoid // treating that root cert as EV. if (!entry.cert) { #ifdef DEBUG // The debug CA info is at position 0, and is NOT on the NSS root db if (iEV == 0) { continue; } #endif PR_NOT_REACHED("Could not find EV root in NSS storage"); continue; } unsigned char certFingerprint[20]; rv = PK11_HashBuf(SEC_OID_SHA1, certFingerprint, entry.cert->derCert.data, entry.cert->derCert.len); PR_ASSERT(rv == SECSuccess); if (rv == SECSuccess) { bool same = !memcmp(certFingerprint, entry.ev_root_sha1_fingerprint, 20); PR_ASSERT(same); if (same) { SECItem ev_oid_item; ev_oid_item.data = nullptr; ev_oid_item.len = 0; rv = SEC_StringToOID(nullptr, &ev_oid_item, entry.dotted_oid, 0); PR_ASSERT(rv == SECSuccess); if (rv == SECSuccess) { entry.oid_tag = register_oid(&ev_oid_item, entry.oid_name); if (entry.oid_tag == SEC_OID_UNKNOWN) { rv = SECFailure; } SECITEM_FreeItem(&ev_oid_item, false); } } else { PR_SetError(SEC_ERROR_BAD_DATA, 0); rv = SECFailure; } } if (rv != SECSuccess) { CERT_DestroyCertificate(entry.cert); entry.cert = nullptr; entry.oid_tag = SEC_OID_UNKNOWN; return PR_FAILURE; } } return PR_SUCCESS; }
// NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the // public value. To properly export the private key to JWK or PKCS #8 we need // the public key data though and so we use this method to augment a private // key with data from the given public key. nsresult CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey) { // This should be a private key. MOZ_ASSERT(GetKeyType() == PRIVATE); // There should be a private NSS key with type 'EC'. MOZ_ASSERT(mPrivateKey && mPrivateKey->keyType == ecKey); // The given public key should have the same key type. MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType); nsNSSShutDownPreventionLock locker; ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) { return NS_ERROR_DOM_OPERATION_ERR; } // Generate a random 160-bit object ID. ScopedSECItem objID(::SECITEM_AllocItem(nullptr, nullptr, 20)); SECStatus rv = PK11_GenerateRandomOnSlot(slot, objID->data, objID->len); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } // Read EC params. ScopedSECItem params(::SECITEM_AllocItem(nullptr, nullptr, 0)); rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_EC_PARAMS, params); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } // Read private value. ScopedSECItem value(::SECITEM_AllocItem(nullptr, nullptr, 0)); rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_VALUE, value); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } SECItem* point = &aPublicKey->u.ec.publicValue; CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY; CK_BBOOL falseValue = CK_FALSE; CK_KEY_TYPE ecValue = CKK_EC; CK_ATTRIBUTE keyTemplate[9] = { { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) }, { CKA_KEY_TYPE, &ecValue, sizeof(ecValue) }, { CKA_TOKEN, &falseValue, sizeof(falseValue) }, { CKA_SENSITIVE, &falseValue, sizeof(falseValue) }, { CKA_PRIVATE, &falseValue, sizeof(falseValue) }, { CKA_ID, objID->data, objID->len }, { CKA_EC_PARAMS, params->data, params->len }, { CKA_EC_POINT, point->data, point->len }, { CKA_VALUE, value->data, value->len }, }; mPrivateKey = PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate, PR_ARRAY_SIZE(keyTemplate)); NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR); return NS_OK; }
static SECStatus applyCryptoPolicy(char *policy) { char *s, *sp, *p; unsigned i; SECStatus rv; unsigned unknown; if (policy == NULL || policy[0] == 0) { return SECSuccess; /* do nothing */ } p = policy; /* disable all options by default */ for (i = 0; i < PR_ARRAY_SIZE(algOptList); i++) { NSS_SetAlgorithmPolicy(algOptList[i].oid, 0, algOptList[i].val); } NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL, 0); do { s = strtok_r(p, ":", &sp); p = NULL; if (s != NULL) { unknown = 1; for (i = 0; i < PR_ARRAY_SIZE(algOptList); i++) { if (strcasecmp(algOptList[i].name, s) == 0) { rv = NSS_SetAlgorithmPolicy(algOptList[i].oid, algOptList[i].val, 0); if (rv != SECSuccess) { /* could not enable option */ rv = SECFailure; goto cleanup; } unknown = 0; break; } } if (unknown != 0) { for (i = 0; i < PR_ARRAY_SIZE(freeOptList); i++) { if (strncasecmp(freeOptList[i].name, s, freeOptList[i].name_size) == 0 && s[freeOptList[i].name_size] == '=') { PRInt32 val = atoi(&s[freeOptList[i].name_size+1]); assert(val != 0); rv = NSS_OptionSet(freeOptList[i].option, val); if (rv != SECSuccess) { /* could not enable option */ rv = SECFailure; goto cleanup; } unknown = 0; break; } } } if (unknown != 0) { fprintf(stderr, "error in term '%s'\n", s); rv = SECFailure; goto cleanup; } } } while (s != NULL); cleanup: /*NSS cannot recover*/ rv = SECSuccess; return rv; }