Files
palemoon27/security/manager/ssl/nsDataSignatureVerifier.cpp
T
roytam1 81d61fa324 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1265036 - Use NS_ABORT_OOM() if try_realloc() fails. r=billm (c30f4f83d5)
- Bug 1263292 - Handle calling realloc(0) (r=jld) (f292859ee9)
- Bug 1256366 - Remove linear and exponential stats collection from histogram.cc. r=gfritzsche (f9a1c869a1)
- Bug 1263953 - Reduce the growth rate of Pickle. r=wmccloskey (6eb5228490)
- Bug 1233275 - Copy environment for IPC using NSPR. r=jld (2004db748e)
- Bug 1261094 - Improve how MessageChannel::mInterruptStack is used in IPC code, r=jld (56e2c114a4)
- Bug 1246931: Include dbus.h in DBus IPC headers, r=shuang (43e797c2d8)
- Bug 1264887: Make DBus helpers available on desktop builds, r=shuang (58bff1f640)
- Bug 1268130, part 1 - Reimplement ByteLengthIsValid using CheckedInt. r=froydnj (6018e22ae0)
- Bug 1268130, part 2 - Make ByteLengthIsValid failures fatal in release builds. r=froydnj (f9d934a498)
- Bug 1269365, part 1 - Swap fallible and infallible TArray ParamTraits. r=froydnj (ad423bc04d)
- Bug 1269365, part 2 - Make ParamTraits<nsTArray<E>>::Read use infallible allocation. r=froydnj (9b902a5bc4)
- Bug 1269365, part 3 - Use infallible array allocation in implementSpecialArrayPickling. r=froydnj (592fe648d3)
- Bug 1264820 - Measure IPC reply size in telemetry (r=mccr8) (62c54d3141)
- Bug 1268938 - Use the name of the original message in Send for reply telemetry. r=billm (a2de5c6a91)
- Bug 1266954: Remove temporary |ScopedClose| from PDU receive code, r=jacheng (cb06315c33)
- Bug 1142109 - Fix IPDL tests (r=dvander) (df3f0cda32)
- Bug 1177013 - Fix IPDL tests for not allowing CPOWs during sync (r=dvander) (5da0a8a4c9)
- Bug 1261307: Convert RIL sockets to |UniquePtr|, r=nfroyd (08609783b3)
- Bug 1253622 - Move the mozilla-trace.h generation into moz.build; r=ted (f01dc418bc)
- Bug 1267318 ignore cert expiration for mozilla-signed packages, r=dkeeler (7a1ddd6090)
- Bug 1029173 - Clean up nsDataSignatureVerifier. r=keeler (f9602341ea)
- bug 1267463 - add a more nuanced subject common name fallback option for prerelease channels r=Cykesiopka,jcj (9b55320c9b)
- Bug 1253108 - Enable ESLint "strict" rule for PSM. r=keeler (54802bdc38)
- Bug 1255425 - part 1 - clearly delineate steps when outputting HSTS preload list; r=keeler (79f73189c8)
- Bug 1251801 - Fully implement nsNSSShutDownObject and obviate manual NSS resource management. r=keeler (af32315d3f)
- Bug 1251801 - Improve handling of PK11_* function error codes. r=keeler (9f2c8ac64b)
- Fix unified-build bustage from bug 1264706. r=bustage (11bc0417c7)
- Bug 1265164 - Always use nsCOMPtrs with getNSSDialogs(). r=keeler (ce5a703972)
2024-09-03 10:19:02 +08:00

332 lines
10 KiB
C++

/* 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/. */
#include "nsDataSignatureVerifier.h"
#include "cms.h"
#include "cryptohi.h"
#include "keyhi.h"
#include "mozilla/unused.h"
#include "nsCOMPtr.h"
#include "nsNSSComponent.h"
#include "nssb64.h"
#include "pkix/pkixtypes.h"
#include "ScopedNSSTypes.h"
#include "secerr.h"
#include "SharedCertVerifier.h"
using namespace mozilla;
using namespace mozilla::pkix;
using namespace mozilla::psm;
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
NS_IMPL_ISUPPORTS(nsDataSignatureVerifier, nsIDataSignatureVerifier)
const SEC_ASN1Template CERT_SignatureDataTemplate[] =
{
{ SEC_ASN1_SEQUENCE,
0, nullptr, sizeof(CERTSignedData) },
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
offsetof(CERTSignedData,signatureAlgorithm),
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate), },
{ SEC_ASN1_BIT_STRING,
offsetof(CERTSignedData,signature), },
{ 0, }
};
nsDataSignatureVerifier::~nsDataSignatureVerifier()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
shutdown(calledFromObject);
}
NS_IMETHODIMP
nsDataSignatureVerifier::VerifyData(const nsACString& aData,
const nsACString& aSignature,
const nsACString& aPublicKey,
bool* _retval)
{
NS_ENSURE_ARG_POINTER(_retval);
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
// Allocate an arena to handle the majority of the allocations
UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
if (!arena) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Base 64 decode the key
SECItem keyItem;
PORT_Memset(&keyItem, 0, sizeof(SECItem));
if (!NSSBase64_DecodeBuffer(arena.get(), &keyItem,
PromiseFlatCString(aPublicKey).get(),
aPublicKey.Length())) {
return NS_ERROR_FAILURE;
}
// Extract the public key from the data
UniqueCERTSubjectPublicKeyInfo pki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&keyItem));
if (!pki) {
return NS_ERROR_FAILURE;
}
UniqueSECKEYPublicKey publicKey(SECKEY_ExtractPublicKey(pki.get()));
if (!publicKey) {
return NS_ERROR_FAILURE;
}
// Base 64 decode the signature
SECItem signatureItem;
PORT_Memset(&signatureItem, 0, sizeof(SECItem));
if (!NSSBase64_DecodeBuffer(arena.get(), &signatureItem,
PromiseFlatCString(aSignature).get(),
aSignature.Length())) {
return NS_ERROR_FAILURE;
}
// Decode the signature and algorithm
CERTSignedData sigData;
PORT_Memset(&sigData, 0, sizeof(CERTSignedData));
SECStatus srv = SEC_QuickDERDecodeItem(arena.get(), &sigData,
CERT_SignatureDataTemplate,
&signatureItem);
if (srv != SECSuccess) {
return NS_ERROR_FAILURE;
}
// Perform the final verification
DER_ConvertBitString(&(sigData.signature));
srv = VFY_VerifyDataWithAlgorithmID(
reinterpret_cast<const unsigned char*>(PromiseFlatCString(aData).get()),
aData.Length(), publicKey.get(), &(sigData.signature),
&(sigData.signatureAlgorithm), nullptr, nullptr);
*_retval = (srv == SECSuccess);
return NS_OK;
}
namespace mozilla {
nsresult
VerifyCMSDetachedSignatureIncludingCertificate(
const SECItem& buffer, const SECItem& detachedDigest,
nsresult (*verifyCertificate)(CERTCertificate* cert, void* context,
void* pinArg),
void* verifyCertificateContext, void* pinArg,
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
{
// XXX: missing pinArg is tolerated.
if (NS_WARN_IF(!buffer.data && buffer.len > 0) ||
NS_WARN_IF(!detachedDigest.data && detachedDigest.len > 0) ||
(!verifyCertificate) ||
NS_WARN_IF(!verifyCertificateContext)) {
return NS_ERROR_INVALID_ARG;
}
UniqueNSSCMSMessage
cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr,
nullptr, nullptr, nullptr, nullptr,
nullptr));
if (!cmsMsg) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) {
return NS_ERROR_CMS_VERIFY_NOT_SIGNED;
}
NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(cmsMsg.get(), 0);
if (!cinfo) {
return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
}
// We're expecting this to be a PKCS#7 signedData content info.
if (NSS_CMSContentInfo_GetContentTypeTag(cinfo)
!= SEC_OID_PKCS7_SIGNED_DATA) {
return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
}
// signedData is non-owning
NSSCMSSignedData* signedData =
reinterpret_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(cinfo));
if (!signedData) {
return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
}
// We're expecting this to be a PKCS#7 signedData content info.
if (NSS_CMSContentInfo_GetContentTypeTag(cinfo)
!= SEC_OID_PKCS7_SIGNED_DATA) {
return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
}
// Set digest value.
if (NSS_CMSSignedData_SetDigestValue(signedData, SEC_OID_SHA1,
const_cast<SECItem*>(&detachedDigest))) {
return NS_ERROR_CMS_VERIFY_BAD_DIGEST;
}
// Parse the certificates into CERTCertificate objects held in memory so
// verifyCertificate will be able to find them during path building.
UniqueCERTCertList certs(CERT_NewCertList());
if (!certs) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (signedData->rawCerts) {
for (size_t i = 0; signedData->rawCerts[i]; ++i) {
UniqueCERTCertificate
cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
signedData->rawCerts[i], nullptr, false,
true));
// Skip certificates that fail to parse
if (!cert) {
continue;
}
if (CERT_AddCertToListTail(certs.get(), cert.get()) != SECSuccess) {
return NS_ERROR_OUT_OF_MEMORY;
}
Unused << cert.release(); // Ownership transferred to the cert list.
}
}
// Get the end-entity certificate.
int numSigners = NSS_CMSSignedData_SignerInfoCount(signedData);
if (NS_WARN_IF(numSigners != 1)) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
// signer is non-owning.
NSSCMSSignerInfo* signer = NSS_CMSSignedData_GetSignerInfo(signedData, 0);
if (NS_WARN_IF(!signer)) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
CERTCertificate* signerCert =
NSS_CMSSignerInfo_GetSigningCertificate(signer, CERT_GetDefaultCertDB());
if (!signerCert) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
nsresult rv = verifyCertificate(signerCert, verifyCertificateContext, pinArg);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
SECOidData* contentTypeOidData =
SECOID_FindOID(&signedData->contentInfo.contentType);
if (!contentTypeOidData) {
return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
}
return MapSECStatus(NSS_CMSSignerInfo_Verify(signer,
const_cast<SECItem*>(&detachedDigest),
&contentTypeOidData->oid));
}
} // namespace mozilla
namespace {
struct VerifyCertificateContext
{
nsCOMPtr<nsIX509Cert> signingCert;
ScopedCERTCertList builtChain;
};
static nsresult
VerifyCertificate(CERTCertificate* cert, void* voidContext, void* pinArg)
{
// XXX: missing pinArg is tolerated
if (NS_WARN_IF(!cert) || NS_WARN_IF(!voidContext)) {
return NS_ERROR_INVALID_ARG;
}
VerifyCertificateContext* context =
reinterpret_cast<VerifyCertificateContext*>(voidContext);
nsCOMPtr<nsIX509Cert> xpcomCert(nsNSSCertificate::Create(cert));
if (!xpcomCert) {
return NS_ERROR_OUT_OF_MEMORY;
}
context->signingCert = xpcomCert;
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
return MapSECStatus(certVerifier->VerifyCert(cert,
certificateUsageObjectSigner,
Now(), pinArg,
nullptr, // hostname
context->builtChain));
}
} // namespace
NS_IMETHODIMP
nsDataSignatureVerifier::VerifySignature(const char* aRSABuf,
uint32_t aRSABufLen,
const char* aPlaintext,
uint32_t aPlaintextLen,
int32_t* aErrorCode,
nsIX509Cert** aSigningCert)
{
if (!aRSABuf || !aPlaintext || !aErrorCode || !aSigningCert) {
return NS_ERROR_INVALID_ARG;
}
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
*aErrorCode = VERIFY_ERROR_OTHER;
*aSigningCert = nullptr;
Digest digest;
nsresult rv = digest.DigestBuf(SEC_OID_SHA1, uint8_t_ptr_cast(aPlaintext),
aPlaintextLen);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
SECItem buffer = {
siBuffer,
reinterpret_cast<uint8_t*>(const_cast<char*>(aRSABuf)),
aRSABufLen
};
VerifyCertificateContext context;
// XXX: pinArg is missing
rv = VerifyCMSDetachedSignatureIncludingCertificate(buffer, digest.get(),
VerifyCertificate,
&context, nullptr, locker);
if (NS_SUCCEEDED(rv)) {
*aErrorCode = VERIFY_OK;
} else if (NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_SECURITY) {
if (rv == GetXPCOMFromNSSError(SEC_ERROR_UNKNOWN_ISSUER)) {
*aErrorCode = VERIFY_ERROR_UNKNOWN_ISSUER;
} else {
*aErrorCode = VERIFY_ERROR_OTHER;
}
rv = NS_OK;
}
if (rv == NS_OK) {
context.signingCert.forget(aSigningCert);
}
return rv;
}