mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
81d61fa324
- 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)
332 lines
10 KiB
C++
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;
|
|
}
|