summaryrefslogtreecommitdiffstats
path: root/modules/libmar/verify
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /modules/libmar/verify
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--modules/libmar/verify/MacVerifyCrypto.cpp218
-rw-r--r--modules/libmar/verify/cryptox.c239
-rw-r--r--modules/libmar/verify/cryptox.h165
-rw-r--r--modules/libmar/verify/mar_verify.c416
-rw-r--r--modules/libmar/verify/moz.build49
5 files changed, 1087 insertions, 0 deletions
diff --git a/modules/libmar/verify/MacVerifyCrypto.cpp b/modules/libmar/verify/MacVerifyCrypto.cpp
new file mode 100644
index 0000000000..d1d1200fef
--- /dev/null
+++ b/modules/libmar/verify/MacVerifyCrypto.cpp
@@ -0,0 +1,218 @@
+/* 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 <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#include <dlfcn.h>
+
+#include "cryptox.h"
+
+// We declare the necessary parts of the Security Transforms API here since
+// we're building with the 10.6 SDK, which doesn't know about Security
+// Transforms.
+#if defined(__cplusplus)
+extern "C" {
+#endif
+const CFStringRef kSecTransformInputAttributeName = CFSTR("INPUT");
+typedef CFTypeRef SecTransformRef;
+typedef OpaqueSecKeyRef* SecKeyRef;
+
+typedef SecTransformRef (*SecTransformCreateReadTransformWithReadStreamFunc)(
+ CFReadStreamRef inputStream);
+SecTransformCreateReadTransformWithReadStreamFunc
+ SecTransformCreateReadTransformWithReadStreamPtr = NULL;
+typedef CFTypeRef (*SecTransformExecuteFunc)(SecTransformRef transform,
+ CFErrorRef* error);
+SecTransformExecuteFunc SecTransformExecutePtr = NULL;
+typedef SecTransformRef (*SecVerifyTransformCreateFunc)(SecKeyRef key,
+ CFDataRef signature,
+ CFErrorRef* error);
+SecVerifyTransformCreateFunc SecVerifyTransformCreatePtr = NULL;
+typedef Boolean (*SecTransformSetAttributeFunc)(SecTransformRef transform,
+ CFStringRef key,
+ CFTypeRef value,
+ CFErrorRef* error);
+SecTransformSetAttributeFunc SecTransformSetAttributePtr = NULL;
+#if defined(__cplusplus)
+}
+#endif
+
+CryptoX_Result CryptoMac_InitCryptoProvider() {
+ if (!SecTransformCreateReadTransformWithReadStreamPtr) {
+ SecTransformCreateReadTransformWithReadStreamPtr =
+ (SecTransformCreateReadTransformWithReadStreamFunc)dlsym(
+ RTLD_DEFAULT, "SecTransformCreateReadTransformWithReadStream");
+ }
+ if (!SecTransformExecutePtr) {
+ SecTransformExecutePtr =
+ (SecTransformExecuteFunc)dlsym(RTLD_DEFAULT, "SecTransformExecute");
+ }
+ if (!SecVerifyTransformCreatePtr) {
+ SecVerifyTransformCreatePtr = (SecVerifyTransformCreateFunc)dlsym(
+ RTLD_DEFAULT, "SecVerifyTransformCreate");
+ }
+ if (!SecTransformSetAttributePtr) {
+ SecTransformSetAttributePtr = (SecTransformSetAttributeFunc)dlsym(
+ RTLD_DEFAULT, "SecTransformSetAttribute");
+ }
+ if (!SecTransformCreateReadTransformWithReadStreamPtr ||
+ !SecTransformExecutePtr || !SecVerifyTransformCreatePtr ||
+ !SecTransformSetAttributePtr) {
+ return CryptoX_Error;
+ }
+ return CryptoX_Success;
+}
+
+CryptoX_Result CryptoMac_VerifyBegin(CryptoX_SignatureHandle* aInputData) {
+ if (!aInputData) {
+ return CryptoX_Error;
+ }
+
+ void* inputData = CFDataCreateMutable(kCFAllocatorDefault, 0);
+ if (!inputData) {
+ return CryptoX_Error;
+ }
+
+ *aInputData = inputData;
+ return CryptoX_Success;
+}
+
+CryptoX_Result CryptoMac_VerifyUpdate(CryptoX_SignatureHandle* aInputData,
+ void* aBuf, unsigned int aLen) {
+ if (aLen == 0) {
+ return CryptoX_Success;
+ }
+ if (!aInputData || !*aInputData) {
+ return CryptoX_Error;
+ }
+
+ CFMutableDataRef inputData = (CFMutableDataRef)*aInputData;
+
+ CFDataAppendBytes(inputData, (const uint8*)aBuf, aLen);
+ return CryptoX_Success;
+}
+
+CryptoX_Result CryptoMac_LoadPublicKey(const unsigned char* aCertData,
+ unsigned int aDataSize,
+ CryptoX_PublicKey* aPublicKey) {
+ if (!aCertData || aDataSize == 0 || !aPublicKey) {
+ return CryptoX_Error;
+ }
+ *aPublicKey = NULL;
+ CFDataRef certData = CFDataCreate(kCFAllocatorDefault, aCertData, aDataSize);
+ if (!certData) {
+ return CryptoX_Error;
+ }
+
+ SecCertificateRef cert =
+ SecCertificateCreateWithData(kCFAllocatorDefault, certData);
+ CFRelease(certData);
+ if (!cert) {
+ return CryptoX_Error;
+ }
+
+ OSStatus status = SecCertificateCopyPublicKey(cert, (SecKeyRef*)aPublicKey);
+ CFRelease(cert);
+ if (status != 0) {
+ return CryptoX_Error;
+ }
+
+ return CryptoX_Success;
+}
+
+CryptoX_Result CryptoMac_VerifySignature(CryptoX_SignatureHandle* aInputData,
+ CryptoX_PublicKey* aPublicKey,
+ const unsigned char* aSignature,
+ unsigned int aSignatureLen) {
+ if (!aInputData || !*aInputData || !aPublicKey || !*aPublicKey ||
+ !aSignature || aSignatureLen == 0) {
+ return CryptoX_Error;
+ }
+
+ CFDataRef signatureData =
+ CFDataCreate(kCFAllocatorDefault, aSignature, aSignatureLen);
+ if (!signatureData) {
+ return CryptoX_Error;
+ }
+
+ CFErrorRef error;
+ SecTransformRef verifier = SecVerifyTransformCreatePtr((SecKeyRef)*aPublicKey,
+ signatureData, &error);
+ if (!verifier || error) {
+ if (error) {
+ CFRelease(error);
+ }
+ CFRelease(signatureData);
+ return CryptoX_Error;
+ }
+
+ SecTransformSetAttributePtr(verifier, kSecDigestTypeAttribute, kSecDigestSHA2,
+ &error);
+ if (error) {
+ CFRelease(error);
+ CFRelease(signatureData);
+ CFRelease(verifier);
+ return CryptoX_Error;
+ }
+
+ int digestLength = 384;
+ CFNumberRef dLen =
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &digestLength);
+ SecTransformSetAttributePtr(verifier, kSecDigestLengthAttribute, dLen,
+ &error);
+ CFRelease(dLen);
+ if (error) {
+ CFRelease(error);
+ CFRelease(signatureData);
+ CFRelease(verifier);
+ return CryptoX_Error;
+ }
+
+ SecTransformSetAttributePtr(verifier, kSecTransformInputAttributeName,
+ (CFDataRef)*aInputData, &error);
+ if (error) {
+ CFRelease(error);
+ CFRelease(signatureData);
+ CFRelease(verifier);
+ return CryptoX_Error;
+ }
+
+ CryptoX_Result result = CryptoX_Error;
+ CFTypeRef rv = SecTransformExecutePtr(verifier, &error);
+ if (error) {
+ CFRelease(error);
+ CFRelease(signatureData);
+ CFRelease(verifier);
+ return CryptoX_Error;
+ }
+
+ if (CFGetTypeID(rv) == CFBooleanGetTypeID() &&
+ CFBooleanGetValue((CFBooleanRef)rv) == true) {
+ result = CryptoX_Success;
+ }
+
+ CFRelease(signatureData);
+ CFRelease(verifier);
+
+ return result;
+}
+
+void CryptoMac_FreeSignatureHandle(CryptoX_SignatureHandle* aInputData) {
+ if (!aInputData || !*aInputData) {
+ return;
+ }
+
+ CFMutableDataRef inputData = NULL;
+ inputData = (CFMutableDataRef)*aInputData;
+
+ CFRelease(inputData);
+}
+
+void CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey) {
+ if (!aPublicKey || !*aPublicKey) {
+ return;
+ }
+
+ CFRelease((SecKeyRef)*aPublicKey);
+}
diff --git a/modules/libmar/verify/cryptox.c b/modules/libmar/verify/cryptox.c
new file mode 100644
index 0000000000..8afc13e5e9
--- /dev/null
+++ b/modules/libmar/verify/cryptox.c
@@ -0,0 +1,239 @@
+/* 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/. */
+
+#ifdef XP_WIN
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "cryptox.h"
+
+#if defined(MAR_NSS)
+
+/**
+ * Loads the public key for the specified cert name from the NSS store.
+ *
+ * @param certData The DER-encoded X509 certificate to extract the key from.
+ * @param certDataSize The size of certData.
+ * @param publicKey Out parameter for the public key to use.
+ * @return CryptoX_Success on success, CryptoX_Error on error.
+ */
+CryptoX_Result NSS_LoadPublicKey(const unsigned char* certData,
+ unsigned int certDataSize,
+ SECKEYPublicKey** publicKey) {
+ CERTCertificate* cert;
+ SECItem certDataItem = {siBuffer, (unsigned char*)certData, certDataSize};
+
+ if (!certData || !publicKey) {
+ return CryptoX_Error;
+ }
+
+ cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certDataItem, NULL,
+ PR_FALSE, PR_TRUE);
+ /* Get the cert and embedded public key out of the database */
+ if (!cert) {
+ return CryptoX_Error;
+ }
+ *publicKey = CERT_ExtractPublicKey(cert);
+ CERT_DestroyCertificate(cert);
+
+ if (!*publicKey) {
+ return CryptoX_Error;
+ }
+ return CryptoX_Success;
+}
+
+CryptoX_Result NSS_VerifyBegin(VFYContext** ctx,
+ SECKEYPublicKey* const* publicKey) {
+ SECStatus status;
+ if (!ctx || !publicKey || !*publicKey) {
+ return CryptoX_Error;
+ }
+
+ /* Check that the key length is large enough for our requirements */
+ if ((SECKEY_PublicKeyStrength(*publicKey) * 8) <
+ XP_MIN_SIGNATURE_LEN_IN_BYTES) {
+ fprintf(stderr, "ERROR: Key length must be >= %d bytes\n",
+ XP_MIN_SIGNATURE_LEN_IN_BYTES);
+ return CryptoX_Error;
+ }
+
+ *ctx = VFY_CreateContext(*publicKey, NULL,
+ SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION, NULL);
+ if (*ctx == NULL) {
+ return CryptoX_Error;
+ }
+
+ status = VFY_Begin(*ctx);
+ return SECSuccess == status ? CryptoX_Success : CryptoX_Error;
+}
+
+/**
+ * Verifies if a verify context matches the passed in signature.
+ *
+ * @param ctx The verify context that the signature should match.
+ * @param signature The signature to match.
+ * @param signatureLen The length of the signature.
+ * @return CryptoX_Success on success, CryptoX_Error on error.
+ */
+CryptoX_Result NSS_VerifySignature(VFYContext* const* ctx,
+ const unsigned char* signature,
+ unsigned int signatureLen) {
+ SECItem signedItem;
+ SECStatus status;
+ if (!ctx || !signature || !*ctx) {
+ return CryptoX_Error;
+ }
+
+ signedItem.len = signatureLen;
+ signedItem.data = (unsigned char*)signature;
+ status = VFY_EndWithSignature(*ctx, &signedItem);
+ return SECSuccess == status ? CryptoX_Success : CryptoX_Error;
+}
+
+#elif defined(XP_WIN)
+/**
+ * Verifies if a signature + public key matches a hash context.
+ *
+ * @param hash The hash context that the signature should match.
+ * @param pubKey The public key to use on the signature.
+ * @param signature The signature to check.
+ * @param signatureLen The length of the signature.
+ * @return CryptoX_Success on success, CryptoX_Error on error.
+ */
+CryptoX_Result CryptoAPI_VerifySignature(HCRYPTHASH* hash, HCRYPTKEY* pubKey,
+ const BYTE* signature,
+ DWORD signatureLen) {
+ DWORD i;
+ BOOL result;
+ /* Windows APIs expect the bytes in the signature to be in little-endian
+ * order, but we write the signature in big-endian order. Other APIs like
+ * NSS and OpenSSL expect big-endian order.
+ */
+ BYTE* signatureReversed;
+ if (!hash || !pubKey || !signature || signatureLen < 1) {
+ return CryptoX_Error;
+ }
+
+ signatureReversed = malloc(signatureLen);
+ if (!signatureReversed) {
+ return CryptoX_Error;
+ }
+
+ for (i = 0; i < signatureLen; i++) {
+ signatureReversed[i] = signature[signatureLen - 1 - i];
+ }
+ result = CryptVerifySignature(*hash, signatureReversed, signatureLen, *pubKey,
+ NULL, 0);
+ free(signatureReversed);
+ return result ? CryptoX_Success : CryptoX_Error;
+}
+
+/**
+ * Obtains the public key for the passed in cert data
+ *
+ * @param provider The cyrto provider
+ * @param certData Data of the certificate to extract the public key from
+ * @param sizeOfCertData The size of the certData buffer
+ * @param certStore Pointer to the handle of the certificate store to use
+ * @param CryptoX_Success on success
+ */
+CryptoX_Result CryptoAPI_LoadPublicKey(HCRYPTPROV provider, BYTE* certData,
+ DWORD sizeOfCertData,
+ HCRYPTKEY* publicKey) {
+ CRYPT_DATA_BLOB blob;
+ CERT_CONTEXT* context;
+ if (!provider || !certData || !publicKey) {
+ return CryptoX_Error;
+ }
+
+ blob.cbData = sizeOfCertData;
+ blob.pbData = certData;
+ if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob,
+ CERT_QUERY_CONTENT_FLAG_CERT,
+ CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
+ NULL, NULL, (const void**)&context)) {
+ return CryptoX_Error;
+ }
+
+ if (!CryptImportPublicKeyInfo(
+ provider, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ &context->pCertInfo->SubjectPublicKeyInfo, publicKey)) {
+ CertFreeCertificateContext(context);
+ return CryptoX_Error;
+ }
+
+ CertFreeCertificateContext(context);
+ return CryptoX_Success;
+}
+
+/* Try to acquire context in this way:
+ * 1. Enhanced provider without creating a new key set
+ * 2. Enhanced provider with creating a new key set
+ * 3. Default provider without creating a new key set
+ * 4. Default provider without creating a new key set
+ * #2 and #4 should not be needed because of the CRYPT_VERIFYCONTEXT,
+ * but we add it just in case.
+ *
+ * @param provider Out parameter containing the provider handle.
+ * @return CryptoX_Success on success, CryptoX_Error on error.
+ */
+CryptoX_Result CryptoAPI_InitCryptoContext(HCRYPTPROV* provider) {
+ if (!CryptAcquireContext(provider, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES,
+ CRYPT_VERIFYCONTEXT)) {
+ if (!CryptAcquireContext(provider, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES,
+ CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) {
+ if (!CryptAcquireContext(provider, NULL, NULL, PROV_RSA_AES,
+ CRYPT_VERIFYCONTEXT)) {
+ if (!CryptAcquireContext(provider, NULL, NULL, PROV_RSA_AES,
+ CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) {
+ *provider = CryptoX_InvalidHandleValue;
+ return CryptoX_Error;
+ }
+ }
+ }
+ }
+ return CryptoX_Success;
+}
+
+/**
+ * Begins a signature verification hash context
+ *
+ * @param provider The crypt provider to use
+ * @param hash Out parameter for a handle to the hash context
+ * @return CryptoX_Success on success, CryptoX_Error on error.
+ */
+CryptoX_Result CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash) {
+ BOOL result;
+ if (!provider || !hash) {
+ return CryptoX_Error;
+ }
+
+ *hash = (HCRYPTHASH)NULL;
+ result = CryptCreateHash(provider, CALG_SHA_384, 0, 0, hash);
+ return result ? CryptoX_Success : CryptoX_Error;
+}
+
+/**
+ * Updates a signature verification hash context
+ *
+ * @param hash The hash context to udpate
+ * @param buf The buffer to update the hash context with
+ * @param len The size of the passed in buffer
+ * @return CryptoX_Success on success, CryptoX_Error on error.
+ */
+CryptoX_Result CryptoAPI_VerifyUpdate(HCRYPTHASH* hash, BYTE* buf, DWORD len) {
+ BOOL result;
+ if (!hash || !buf) {
+ return CryptoX_Error;
+ }
+
+ result = CryptHashData(*hash, buf, len, 0);
+ return result ? CryptoX_Success : CryptoX_Error;
+}
+
+#endif
diff --git a/modules/libmar/verify/cryptox.h b/modules/libmar/verify/cryptox.h
new file mode 100644
index 0000000000..9d7b1f04bc
--- /dev/null
+++ b/modules/libmar/verify/cryptox.h
@@ -0,0 +1,165 @@
+/* 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/. */
+
+#ifndef CRYPTOX_H
+#define CRYPTOX_H
+
+#define XP_MIN_SIGNATURE_LEN_IN_BYTES 256
+
+#define CryptoX_Result int
+#define CryptoX_Success 0
+#define CryptoX_Error (-1)
+#define CryptoX_Succeeded(X) ((X) == CryptoX_Success)
+#define CryptoX_Failed(X) ((X) != CryptoX_Success)
+
+#if defined(MAR_NSS)
+
+# include "cert.h"
+# include "keyhi.h"
+# include "cryptohi.h"
+
+# define CryptoX_InvalidHandleValue NULL
+# define CryptoX_ProviderHandle void*
+# define CryptoX_SignatureHandle VFYContext*
+# define CryptoX_PublicKey SECKEYPublicKey*
+# define CryptoX_Certificate CERTCertificate*
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+CryptoX_Result NSS_LoadPublicKey(const unsigned char* certData,
+ unsigned int certDataSize,
+ SECKEYPublicKey** publicKey);
+CryptoX_Result NSS_VerifyBegin(VFYContext** ctx,
+ SECKEYPublicKey* const* publicKey);
+CryptoX_Result NSS_VerifySignature(VFYContext* const* ctx,
+ const unsigned char* signature,
+ unsigned int signatureLen);
+# ifdef __cplusplus
+} // extern "C"
+# endif
+
+# define CryptoX_InitCryptoProvider(CryptoHandle) CryptoX_Success
+# define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
+ NSS_VerifyBegin(SignatureHandle, PublicKey)
+# define CryptoX_FreeSignatureHandle(SignatureHandle) \
+ VFY_DestroyContext(*SignatureHandle, PR_TRUE)
+# define CryptoX_VerifyUpdate(SignatureHandle, buf, len) \
+ VFY_Update(*SignatureHandle, (const unsigned char*)(buf), len)
+# define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, publicKey) \
+ NSS_LoadPublicKey(certData, dataSize, publicKey)
+# define CryptoX_VerifySignature(hash, publicKey, signedData, len) \
+ NSS_VerifySignature(hash, (const unsigned char*)(signedData), len)
+# define CryptoX_FreePublicKey(key) SECKEY_DestroyPublicKey(*key)
+# define CryptoX_FreeCertificate(cert) CERT_DestroyCertificate(*cert)
+
+#elif XP_MACOSX
+
+# define CryptoX_InvalidHandleValue NULL
+# define CryptoX_ProviderHandle void*
+# define CryptoX_SignatureHandle void*
+# define CryptoX_PublicKey void*
+# define CryptoX_Certificate void*
+
+// Forward-declare Objective-C functions implemented in MacVerifyCrypto.mm.
+# ifdef __cplusplus
+extern "C" {
+# endif
+CryptoX_Result CryptoMac_InitCryptoProvider();
+CryptoX_Result CryptoMac_VerifyBegin(CryptoX_SignatureHandle* aInputData);
+CryptoX_Result CryptoMac_VerifyUpdate(CryptoX_SignatureHandle* aInputData,
+ void* aBuf, unsigned int aLen);
+CryptoX_Result CryptoMac_LoadPublicKey(const unsigned char* aCertData,
+ unsigned int aDataSize,
+ CryptoX_PublicKey* aPublicKey);
+CryptoX_Result CryptoMac_VerifySignature(CryptoX_SignatureHandle* aInputData,
+ CryptoX_PublicKey* aPublicKey,
+ const unsigned char* aSignature,
+ unsigned int aSignatureLen);
+void CryptoMac_FreeSignatureHandle(CryptoX_SignatureHandle* aInputData);
+void CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey);
+# ifdef __cplusplus
+} // extern "C"
+# endif
+
+# define CryptoX_InitCryptoProvider(aProviderHandle) \
+ CryptoMac_InitCryptoProvider()
+# define CryptoX_VerifyBegin(aCryptoHandle, aInputData, aPublicKey) \
+ CryptoMac_VerifyBegin(aInputData)
+# define CryptoX_VerifyUpdate(aInputData, aBuf, aLen) \
+ CryptoMac_VerifyUpdate(aInputData, aBuf, aLen)
+# define CryptoX_LoadPublicKey(aProviderHandle, aCertData, aDataSize, \
+ aPublicKey) \
+ CryptoMac_LoadPublicKey(aCertData, aDataSize, aPublicKey)
+# define CryptoX_VerifySignature(aInputData, aPublicKey, aSignature, \
+ aSignatureLen) \
+ CryptoMac_VerifySignature(aInputData, aPublicKey, aSignature, aSignatureLen)
+# define CryptoX_FreeSignatureHandle(aInputData) \
+ CryptoMac_FreeSignatureHandle(aInputData)
+# define CryptoX_FreePublicKey(aPublicKey) CryptoMac_FreePublicKey(aPublicKey)
+# define CryptoX_FreeCertificate(aCertificate)
+
+#elif defined(XP_WIN)
+
+# include <windows.h>
+# include <wincrypt.h>
+
+CryptoX_Result CryptoAPI_InitCryptoContext(HCRYPTPROV* provider);
+CryptoX_Result CryptoAPI_LoadPublicKey(HCRYPTPROV hProv, BYTE* certData,
+ DWORD sizeOfCertData,
+ HCRYPTKEY* publicKey);
+CryptoX_Result CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash);
+CryptoX_Result CryptoAPI_VerifyUpdate(HCRYPTHASH* hash, BYTE* buf, DWORD len);
+CryptoX_Result CryptoAPI_VerifySignature(HCRYPTHASH* hash, HCRYPTKEY* pubKey,
+ const BYTE* signature,
+ DWORD signatureLen);
+
+# define CryptoX_InvalidHandleValue ((ULONG_PTR)NULL)
+# define CryptoX_ProviderHandle HCRYPTPROV
+# define CryptoX_SignatureHandle HCRYPTHASH
+# define CryptoX_PublicKey HCRYPTKEY
+# define CryptoX_Certificate HCERTSTORE
+# define CryptoX_InitCryptoProvider(CryptoHandle) \
+ CryptoAPI_InitCryptoContext(CryptoHandle)
+# define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
+ CryptoAPI_VerifyBegin(CryptoHandle, SignatureHandle)
+# define CryptoX_FreeSignatureHandle(SignatureHandle)
+# define CryptoX_VerifyUpdate(SignatureHandle, buf, len) \
+ CryptoAPI_VerifyUpdate(SignatureHandle, (BYTE*)(buf), len)
+# define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, publicKey) \
+ CryptoAPI_LoadPublicKey(CryptoHandle, (BYTE*)(certData), dataSize, \
+ publicKey)
+# define CryptoX_VerifySignature(hash, publicKey, signedData, len) \
+ CryptoAPI_VerifySignature(hash, publicKey, signedData, len)
+# define CryptoX_FreePublicKey(key) CryptDestroyKey(*(key))
+# define CryptoX_FreeCertificate(cert) \
+ CertCloseStore(*(cert), CERT_CLOSE_STORE_FORCE_FLAG);
+
+#else
+
+/* This default implementation is necessary because we don't want to
+ * link to NSS from updater code on non Windows platforms. On Windows
+ * we use CyrptoAPI instead of NSS. We don't call any function as they
+ * would just fail, but this simplifies linking.
+ */
+
+# define CryptoX_InvalidHandleValue NULL
+# define CryptoX_ProviderHandle void*
+# define CryptoX_SignatureHandle void*
+# define CryptoX_PublicKey void*
+# define CryptoX_Certificate void*
+# define CryptoX_InitCryptoProvider(CryptoHandle) CryptoX_Error
+# define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
+ CryptoX_Error
+# define CryptoX_FreeSignatureHandle(SignatureHandle)
+# define CryptoX_VerifyUpdate(SignatureHandle, buf, len) CryptoX_Error
+# define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, publicKey) \
+ CryptoX_Error
+# define CryptoX_VerifySignature(hash, publicKey, signedData, len) \
+ CryptoX_Error
+# define CryptoX_FreePublicKey(key) CryptoX_Error
+
+#endif
+
+#endif
diff --git a/modules/libmar/verify/mar_verify.c b/modules/libmar/verify/mar_verify.c
new file mode 100644
index 0000000000..5272bb585b
--- /dev/null
+++ b/modules/libmar/verify/mar_verify.c
@@ -0,0 +1,416 @@
+/* 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/. */
+
+#ifdef XP_WIN
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mar_private.h"
+#include "mar.h"
+#include "cryptox.h"
+
+int mar_read_entire_file(const char* filePath, uint32_t maxSize,
+ /*out*/ const uint8_t** data,
+ /*out*/ uint32_t* size) {
+ int result;
+ FILE* f;
+
+ if (!filePath || !data || !size) {
+ return -1;
+ }
+
+ f = fopen(filePath, "rb");
+ if (!f) {
+ return -1;
+ }
+
+ result = -1;
+ if (!fseeko(f, 0, SEEK_END)) {
+ int64_t fileSize = ftello(f);
+ if (fileSize > 0 && fileSize <= maxSize && !fseeko(f, 0, SEEK_SET)) {
+ unsigned char* fileData;
+
+ *size = (unsigned int)fileSize;
+ fileData = malloc(*size);
+ if (fileData) {
+ if (fread(fileData, *size, 1, f) == 1) {
+ *data = fileData;
+ result = 0;
+ } else {
+ free(fileData);
+ }
+ }
+ }
+ }
+
+ fclose(f);
+
+ return result;
+}
+
+int mar_extract_and_verify_signatures(MarFile* mar,
+ CryptoX_ProviderHandle provider,
+ CryptoX_PublicKey* keys,
+ uint32_t keyCount);
+int mar_verify_extracted_signatures(MarFile* mar,
+ CryptoX_ProviderHandle provider,
+ CryptoX_PublicKey* keys,
+ const uint8_t* const* extractedSignatures,
+ uint32_t keyCount, uint32_t* numVerified);
+
+/**
+ * Reads the specified number of bytes from the MAR buffer and
+ * stores them in the passed buffer.
+ *
+ * @param mar An opened MAR
+ * @param mar_position
+ * Our current position within the MAR file buffer.
+ * @param buffer The buffer to store the read results.
+ * @param size The number of bytes to read, buffer must be
+ * at least of this size.
+ * @param ctxs Pointer to the first element in an array of verify context.
+ * @param count The number of elements in ctxs
+ * @param err The name of what is being written to in case of error.
+ * @return CryptoX_Success on success
+ * CryptoX_Error on error
+ */
+CryptoX_Result ReadAndUpdateVerifyContext(MarFile* mar, size_t* mar_position,
+ void* buffer, uint32_t size,
+ CryptoX_SignatureHandle* ctxs,
+ uint32_t count, const char* err) {
+ uint32_t k;
+ if (!mar || !mar_position || !buffer || !ctxs || count == 0 || !err) {
+ fprintf(stderr, "ERROR: Invalid parameter specified.\n");
+ return CryptoX_Error;
+ }
+
+ if (!size) {
+ return CryptoX_Success;
+ }
+
+ if (mar_read_buffer(mar, buffer, mar_position, size) != 0) {
+ fprintf(stderr, "ERROR: Could not read %s\n", err);
+ return CryptoX_Error;
+ }
+
+ for (k = 0; k < count; k++) {
+ if (CryptoX_Failed(CryptoX_VerifyUpdate(&ctxs[k], buffer, size))) {
+ fprintf(stderr, "ERROR: Could not update verify context for %s\n", err);
+ return CryptoX_Error;
+ }
+ }
+ return CryptoX_Success;
+}
+
+/**
+ * Verifies a MAR file by verifying each signature with the corresponding
+ * certificate. That is, the first signature will be verified using the first
+ * certificate given, the second signature will be verified using the second
+ * certificate given, etc. The signature count must exactly match the number of
+ * certificates given, and all signature verifications must succeed.
+ *
+ * @param mar The file who's signature should be calculated
+ * @param certData Pointer to the first element in an array of
+ * certificate data
+ * @param certDataSizes Pointer to the first element in an array for size of
+ * the data stored
+ * @param certCount The number of elements in certData and certDataSizes
+ * @return 0 on success
+ */
+int mar_verify_signatures(MarFile* mar, const uint8_t* const* certData,
+ const uint32_t* certDataSizes, uint32_t certCount) {
+ int rv = -1;
+ CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue;
+ CryptoX_PublicKey keys[MAX_SIGNATURES];
+ uint32_t k;
+
+ memset(keys, 0, sizeof(keys));
+
+ if (!mar || !certData || !certDataSizes || certCount == 0) {
+ fprintf(stderr, "ERROR: Invalid parameter specified.\n");
+ goto failure;
+ }
+
+ if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) {
+ fprintf(stderr, "ERROR: Could not init crytpo library.\n");
+ goto failure;
+ }
+
+ for (k = 0; k < certCount; ++k) {
+ if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData[k],
+ certDataSizes[k], &keys[k]))) {
+ fprintf(stderr, "ERROR: Could not load public key.\n");
+ goto failure;
+ }
+ }
+
+ rv = mar_extract_and_verify_signatures(mar, provider, keys, certCount);
+
+failure:
+
+ for (k = 0; k < certCount; ++k) {
+ if (keys[k]) {
+ CryptoX_FreePublicKey(&keys[k]);
+ }
+ }
+
+ return rv;
+}
+
+/**
+ * Extracts each signature from the specified MAR file,
+ * then calls mar_verify_extracted_signatures to verify each signature.
+ *
+ * @param mar An opened MAR
+ * @param provider A library provider
+ * @param keys The public keys to use to verify the MAR
+ * @param keyCount The number of keys pointed to by keys
+ * @return 0 on success
+ */
+int mar_extract_and_verify_signatures(MarFile* mar,
+ CryptoX_ProviderHandle provider,
+ CryptoX_PublicKey* keys,
+ uint32_t keyCount) {
+ uint32_t signatureCount, signatureLen, numVerified = 0;
+ uint32_t signatureAlgorithmIDs[MAX_SIGNATURES];
+ uint8_t* extractedSignatures[MAX_SIGNATURES];
+ uint32_t i;
+ size_t mar_position = 0;
+
+ memset(signatureAlgorithmIDs, 0, sizeof(signatureAlgorithmIDs));
+ memset(extractedSignatures, 0, sizeof(extractedSignatures));
+
+ if (!mar) {
+ fprintf(stderr, "ERROR: Invalid file pointer passed.\n");
+ return CryptoX_Error;
+ }
+
+ /* Skip to the start of the signature block */
+ if (mar_buffer_seek(mar, &mar_position, SIGNATURE_BLOCK_OFFSET) != 0) {
+ fprintf(stderr, "ERROR: Could not seek to the signature block.\n");
+ return CryptoX_Error;
+ }
+
+ /* Get the number of signatures */
+ if (mar_read_buffer(mar, &signatureCount, &mar_position,
+ sizeof(signatureCount)) != 0) {
+ fprintf(stderr, "ERROR: Could not read number of signatures.\n");
+ return CryptoX_Error;
+ }
+ signatureCount = ntohl(signatureCount);
+
+ /* Check that we have less than the max amount of signatures so we don't
+ waste too much of either updater's or signmar's time. */
+ if (signatureCount > MAX_SIGNATURES) {
+ fprintf(stderr, "ERROR: At most %d signatures can be specified.\n",
+ MAX_SIGNATURES);
+ return CryptoX_Error;
+ }
+
+ for (i = 0; i < signatureCount; i++) {
+ /* Get the signature algorithm ID */
+ if (mar_read_buffer(mar, &signatureAlgorithmIDs[i], &mar_position,
+ sizeof(uint32_t)) != 0) {
+ fprintf(stderr, "ERROR: Could not read signatures algorithm ID.\n");
+ return CryptoX_Error;
+ }
+ signatureAlgorithmIDs[i] = ntohl(signatureAlgorithmIDs[i]);
+
+ if (mar_read_buffer(mar, &signatureLen, &mar_position, sizeof(uint32_t)) !=
+ 0) {
+ fprintf(stderr, "ERROR: Could not read signatures length.\n");
+ return CryptoX_Error;
+ }
+ signatureLen = ntohl(signatureLen);
+
+ /* To protect against invalid input make sure the signature length
+ isn't too big. */
+ if (signatureLen > MAX_SIGNATURE_LENGTH) {
+ fprintf(stderr, "ERROR: Signature length is too large to verify.\n");
+ return CryptoX_Error;
+ }
+
+ extractedSignatures[i] = malloc(signatureLen);
+ if (!extractedSignatures[i]) {
+ fprintf(stderr, "ERROR: Could not allocate buffer for signature.\n");
+ return CryptoX_Error;
+ }
+ if (mar_read_buffer(mar, extractedSignatures[i], &mar_position,
+ signatureLen) != 0) {
+ fprintf(stderr, "ERROR: Could not read extracted signature.\n");
+ for (i = 0; i < signatureCount; ++i) {
+ free(extractedSignatures[i]);
+ }
+ return CryptoX_Error;
+ }
+
+ /* We don't try to verify signatures we don't know about */
+ if (signatureAlgorithmIDs[i] != 2) {
+ fprintf(stderr, "ERROR: Unknown signature algorithm ID.\n");
+ for (i = 0; i < signatureCount; ++i) {
+ free(extractedSignatures[i]);
+ }
+ return CryptoX_Error;
+ }
+ }
+
+ if (mar_verify_extracted_signatures(
+ mar, provider, keys, (const uint8_t* const*)extractedSignatures,
+ signatureCount, &numVerified) == CryptoX_Error) {
+ return CryptoX_Error;
+ }
+ for (i = 0; i < signatureCount; ++i) {
+ free(extractedSignatures[i]);
+ }
+
+ /* If we reached here and we verified every
+ signature, return success. */
+ if (numVerified == signatureCount && keyCount == numVerified) {
+ return CryptoX_Success;
+ }
+
+ if (numVerified == 0) {
+ fprintf(stderr, "ERROR: Not all signatures were verified.\n");
+ } else {
+ fprintf(stderr, "ERROR: Only %d of %d signatures were verified.\n",
+ numVerified, signatureCount);
+ }
+ return CryptoX_Error;
+}
+
+/**
+ * Verifies a MAR file by verifying each signature with the corresponding
+ * certificate. That is, the first signature will be verified using the first
+ * certificate given, the second signature will be verified using the second
+ * certificate given, etc. The signature count must exactly match the number of
+ * certificates given, and all signature verifications must succeed.
+ *
+ * @param mar An opened MAR
+ * @param provider A library provider
+ * @param keys A pointer to the first element in an
+ * array of keys.
+ * @param extractedSignatures Pointer to the first element in an array
+ * of extracted signatures.
+ * @param signatureCount The number of signatures in the MAR file
+ * @param numVerified Out parameter which will be filled with
+ * the number of verified signatures.
+ * This information can be useful for printing
+ * error messages.
+ * @return CryptoX_Success on success, *numVerified == signatureCount.
+ */
+CryptoX_Result mar_verify_extracted_signatures(
+ MarFile* mar, CryptoX_ProviderHandle provider, CryptoX_PublicKey* keys,
+ const uint8_t* const* extractedSignatures, uint32_t signatureCount,
+ uint32_t* numVerified) {
+ CryptoX_SignatureHandle signatureHandles[MAX_SIGNATURES];
+ char buf[BLOCKSIZE];
+ uint32_t signatureLengths[MAX_SIGNATURES];
+ uint32_t i;
+ int rv = CryptoX_Error;
+ size_t mar_position = 0;
+
+ memset(signatureHandles, 0, sizeof(signatureHandles));
+ memset(signatureLengths, 0, sizeof(signatureLengths));
+
+ if (!extractedSignatures || !numVerified) {
+ fprintf(stderr, "ERROR: Invalid parameter specified.\n");
+ goto failure;
+ }
+
+ *numVerified = 0;
+
+ /* This function is only called when we have at least one signature,
+ but to protected against future people who call this function we
+ make sure a non zero value is passed in.
+ */
+ if (!signatureCount) {
+ fprintf(stderr, "ERROR: There must be at least one signature.\n");
+ goto failure;
+ }
+
+ for (i = 0; i < signatureCount; i++) {
+ if (CryptoX_Failed(
+ CryptoX_VerifyBegin(provider, &signatureHandles[i], &keys[i]))) {
+ fprintf(stderr, "ERROR: Could not initialize signature handle.\n");
+ goto failure;
+ }
+ }
+
+ /* Bytes 0-3: MAR1
+ Bytes 4-7: index offset
+ Bytes 8-15: size of entire MAR
+ */
+ if (CryptoX_Failed(ReadAndUpdateVerifyContext(
+ mar, &mar_position, buf, SIGNATURE_BLOCK_OFFSET + sizeof(uint32_t),
+ signatureHandles, signatureCount, "signature block"))) {
+ goto failure;
+ }
+
+ /* Read the signature block */
+ for (i = 0; i < signatureCount; i++) {
+ /* Get the signature algorithm ID */
+ if (CryptoX_Failed(ReadAndUpdateVerifyContext(
+ mar, &mar_position, &buf, sizeof(uint32_t), signatureHandles,
+ signatureCount, "signature algorithm ID"))) {
+ goto failure;
+ }
+
+ if (CryptoX_Failed(ReadAndUpdateVerifyContext(
+ mar, &mar_position, &signatureLengths[i], sizeof(uint32_t),
+ signatureHandles, signatureCount, "signature length"))) {
+ goto failure;
+ }
+ signatureLengths[i] = ntohl(signatureLengths[i]);
+ if (signatureLengths[i] > MAX_SIGNATURE_LENGTH) {
+ fprintf(stderr, "ERROR: Embedded signature length is too large.\n");
+ goto failure;
+ }
+
+ /* Skip past the signature itself as those are not included */
+ if (mar_buffer_seek(mar, &mar_position, signatureLengths[i]) != 0) {
+ fprintf(stderr, "ERROR: Could not seek past signature.\n");
+ goto failure;
+ }
+ }
+
+ /* Read the rest of the file after the signature block */
+ while (mar_position < mar->data_len) {
+ int numRead = mar_read_buffer_max(mar, buf, &mar_position, BLOCKSIZE);
+ for (i = 0; i < signatureCount; i++) {
+ if (CryptoX_Failed(
+ CryptoX_VerifyUpdate(&signatureHandles[i], buf, numRead))) {
+ fprintf(stderr,
+ "ERROR: Error updating verify context with"
+ " data block.\n");
+ goto failure;
+ }
+ }
+ }
+
+ /* Verify the signatures */
+ for (i = 0; i < signatureCount; i++) {
+ if (CryptoX_Failed(CryptoX_VerifySignature(&signatureHandles[i], &keys[i],
+ extractedSignatures[i],
+ signatureLengths[i]))) {
+ fprintf(stderr, "ERROR: Error verifying signature.\n");
+ goto failure;
+ }
+ ++*numVerified;
+ }
+
+ rv = CryptoX_Success;
+failure:
+ for (i = 0; i < signatureCount; i++) {
+ CryptoX_FreeSignatureHandle(&signatureHandles[i]);
+ }
+
+ return rv;
+}
diff --git a/modules/libmar/verify/moz.build b/modules/libmar/verify/moz.build
new file mode 100644
index 0000000000..b07475655f
--- /dev/null
+++ b/modules/libmar/verify/moz.build
@@ -0,0 +1,49 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Library("verifymar")
+
+UNIFIED_SOURCES += [
+ "cryptox.c",
+ "mar_verify.c",
+]
+
+FORCE_STATIC_LIB = True
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ USE_STATIC_LIBS = True
+elif CONFIG["OS_ARCH"] == "Darwin":
+ UNIFIED_SOURCES += [
+ "MacVerifyCrypto.cpp",
+ ]
+ OS_LIBS += [
+ "-framework Security",
+ ]
+else:
+ DEFINES["MAR_NSS"] = True
+ LOCAL_INCLUDES += ["../sign"]
+ USE_LIBS += [
+ "nspr",
+ "nss",
+ "signmar",
+ ]
+ # Ideally, this would be '-Wl,-rpath=$ORIGIN', but the build system
+ # doesn't do the right escaping yet. Even more ideally, this would
+ # be LDFLAGS, but the build system doesn't propagate those like USE_LIBS
+ # and OS_LIBS. Bug #1041943.
+ OS_LIBS += [
+ "-Wl,-rpath=\\$$ORIGIN",
+ ]
+
+LOCAL_INCLUDES += [
+ "../src",
+]
+
+# C11 for static_assert
+c11_flags = ["-std=gnu11"]
+if CONFIG["CC_TYPE"] == "clang-cl":
+ c11_flags.insert(0, "-Xclang")
+CFLAGS += c11_flags