summaryrefslogtreecommitdiffstats
path: root/security/pkix/lib/pkixnss.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/pkix/lib/pkixnss.cpp')
-rw-r--r--security/pkix/lib/pkixnss.cpp228
1 files changed, 228 insertions, 0 deletions
diff --git a/security/pkix/lib/pkixnss.cpp b/security/pkix/lib/pkixnss.cpp
new file mode 100644
index 000000000..798a19265
--- /dev/null
+++ b/security/pkix/lib/pkixnss.cpp
@@ -0,0 +1,228 @@
+/*- *- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+/* Copyright 2013 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pkix/pkixnss.h"
+
+#include <limits>
+
+#include "cryptohi.h"
+#include "keyhi.h"
+#include "pk11pub.h"
+#include "pkix/pkix.h"
+#include "pkixutil.h"
+#include "ScopedPtr.h"
+#include "secerr.h"
+#include "sslerr.h"
+
+namespace mozilla { namespace pkix {
+
+namespace {
+
+Result
+VerifySignedDigest(const SignedDigest& sd,
+ Input subjectPublicKeyInfo,
+ SECOidTag pubKeyAlg,
+ void* pkcs11PinArg)
+{
+ SECOidTag digestAlg;
+ switch (sd.digestAlgorithm) {
+ case DigestAlgorithm::sha512: digestAlg = SEC_OID_SHA512; break;
+ case DigestAlgorithm::sha384: digestAlg = SEC_OID_SHA384; break;
+ case DigestAlgorithm::sha256: digestAlg = SEC_OID_SHA256; break;
+ case DigestAlgorithm::sha1: digestAlg = SEC_OID_SHA1; break;
+ MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
+ }
+
+ SECItem subjectPublicKeyInfoSECItem =
+ UnsafeMapInputToSECItem(subjectPublicKeyInfo);
+ ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
+ spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem));
+ if (!spki) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+ ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
+ pubKey(SECKEY_ExtractPublicKey(spki.get()));
+ if (!pubKey) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+
+ SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest));
+ SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature));
+ SECStatus srv = VFY_VerifyDigestDirect(&digestSECItem, pubKey.get(),
+ &signatureSECItem, pubKeyAlg,
+ digestAlg, pkcs11PinArg);
+ if (srv != SECSuccess) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+
+ return Success;
+}
+
+} // namespace
+
+Result
+VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd,
+ Input subjectPublicKeyInfo,
+ void* pkcs11PinArg)
+{
+ return VerifySignedDigest(sd, subjectPublicKeyInfo,
+ SEC_OID_PKCS1_RSA_ENCRYPTION, pkcs11PinArg);
+}
+
+Result
+VerifyECDSASignedDigestNSS(const SignedDigest& sd,
+ Input subjectPublicKeyInfo,
+ void* pkcs11PinArg)
+{
+ return VerifySignedDigest(sd, subjectPublicKeyInfo,
+ SEC_OID_ANSIX962_EC_PUBLIC_KEY, pkcs11PinArg);
+}
+
+Result
+DigestBufNSS(Input item,
+ DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf,
+ size_t digestBufLen)
+{
+ SECOidTag oid;
+ size_t bits;
+ switch (digestAlg) {
+ case DigestAlgorithm::sha512: oid = SEC_OID_SHA512; bits = 512; break;
+ case DigestAlgorithm::sha384: oid = SEC_OID_SHA384; bits = 384; break;
+ case DigestAlgorithm::sha256: oid = SEC_OID_SHA256; bits = 256; break;
+ case DigestAlgorithm::sha1: oid = SEC_OID_SHA1; bits = 160; break;
+ MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
+ }
+ if (digestBufLen != bits / 8) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ SECItem itemSECItem = UnsafeMapInputToSECItem(item);
+ if (itemSECItem.len >
+ static_cast<decltype(itemSECItem.len)>(
+ std::numeric_limits<int32_t>::max())) {
+ PR_NOT_REACHED("large items should not be possible here");
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ SECStatus srv = PK11_HashBuf(oid, digestBuf, itemSECItem.data,
+ static_cast<int32_t>(itemSECItem.len));
+ if (srv != SECSuccess) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+ return Success;
+}
+
+Result
+MapPRErrorCodeToResult(PRErrorCode error)
+{
+ switch (error)
+ {
+#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \
+ case nss_result: return Result::mozilla_pkix_result;
+
+ MOZILLA_PKIX_MAP_LIST
+
+#undef MOZILLA_PKIX_MAP
+
+ default:
+ return Result::ERROR_UNKNOWN_ERROR;
+ }
+}
+
+PRErrorCode
+MapResultToPRErrorCode(Result result)
+{
+ switch (result)
+ {
+#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \
+ case Result::mozilla_pkix_result: return nss_result;
+
+ MOZILLA_PKIX_MAP_LIST
+
+#undef MOZILLA_PKIX_MAP
+
+ MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
+ }
+}
+
+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." },
+ { "MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME",
+ "The server presented a certificate with an empty issuer distinguished "
+ "name." },
+ };
+ // 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);
+}
+
+} } // namespace mozilla::pkix