diff options
Diffstat (limited to 'modules/libmar/verify')
-rw-r--r-- | modules/libmar/verify/MacVerifyCrypto.cpp | 218 | ||||
-rw-r--r-- | modules/libmar/verify/cryptox.c | 239 | ||||
-rw-r--r-- | modules/libmar/verify/cryptox.h | 165 | ||||
-rw-r--r-- | modules/libmar/verify/mar_verify.c | 438 | ||||
-rw-r--r-- | modules/libmar/verify/moz.build | 51 |
5 files changed, 1111 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..2ec17bbf7f --- /dev/null +++ b/modules/libmar/verify/mar_verify.c @@ -0,0 +1,438 @@ +/* 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_fp(FILE* fp, + CryptoX_ProviderHandle provider, + CryptoX_PublicKey* keys, + uint32_t keyCount); +int mar_verify_signatures_for_fp(FILE* fp, 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 file pointer and + * stores them in the passed buffer. + * + * @param fp The file pointer to read from. + * @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 0 on success + * -1 on read error + * -2 on verify update error + */ +int ReadAndUpdateVerifyContext(FILE* fp, void* buffer, uint32_t size, + CryptoX_SignatureHandle* ctxs, uint32_t count, + const char* err) { + uint32_t k; + if (!fp || !buffer || !ctxs || count == 0 || !err) { + fprintf(stderr, "ERROR: Invalid parameter specified.\n"); + return CryptoX_Error; + } + + if (!size) { + return CryptoX_Success; + } + + if (fread(buffer, size, 1, fp) != 1) { + 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 -2; + } + } + 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 (!mar->fp) { + fprintf(stderr, "ERROR: MAR file is not open.\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_fp(mar->fp, 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_signatures_for_fp to verify each signature. + * + * @param fp An opened MAR file handle + * @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_fp(FILE* fp, + 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; + + memset(signatureAlgorithmIDs, 0, sizeof(signatureAlgorithmIDs)); + memset(extractedSignatures, 0, sizeof(extractedSignatures)); + + if (!fp) { + fprintf(stderr, "ERROR: Invalid file pointer passed.\n"); + return CryptoX_Error; + } + + /* To protect against invalid MAR files, we assumes that the MAR file + size is less than or equal to MAX_SIZE_OF_MAR_FILE. */ + if (fseeko(fp, 0, SEEK_END)) { + fprintf(stderr, "ERROR: Could not seek to the end of the MAR file.\n"); + return CryptoX_Error; + } + if (ftello(fp) > MAX_SIZE_OF_MAR_FILE) { + fprintf(stderr, "ERROR: MAR file is too large to be verified.\n"); + return CryptoX_Error; + } + + /* Skip to the start of the signature block */ + if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) { + fprintf(stderr, "ERROR: Could not seek to the signature block.\n"); + return CryptoX_Error; + } + + /* Get the number of signatures */ + if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) { + 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 (fread(&signatureAlgorithmIDs[i], sizeof(uint32_t), 1, fp) != 1) { + fprintf(stderr, "ERROR: Could not read signatures algorithm ID.\n"); + return CryptoX_Error; + } + signatureAlgorithmIDs[i] = ntohl(signatureAlgorithmIDs[i]); + + if (fread(&signatureLen, sizeof(uint32_t), 1, fp) != 1) { + fprintf(stderr, "ERROR: Could not read signatures length.\n"); + return CryptoX_Error; + } + signatureLen = ntohl(signatureLen); + + /* To protected 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 allocate buffer for signature.\n"); + return CryptoX_Error; + } + if (fread(extractedSignatures[i], signatureLen, 1, fp) != 1) { + 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 (ftello(fp) == -1) { + return CryptoX_Error; + } + if (mar_verify_signatures_for_fp( + fp, 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 fp An opened MAR file handle + * @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 0 on success, *numVerified == signatureCount. + */ +int mar_verify_signatures_for_fp(FILE* fp, 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; + + 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; + } + } + + /* Skip to the start of the file */ + if (fseeko(fp, 0, SEEK_SET)) { + fprintf(stderr, "ERROR: Could not seek to start of the file\n"); + goto failure; + } + + /* Bytes 0-3: MAR1 + Bytes 4-7: index offset + Bytes 8-15: size of entire MAR + */ + if (CryptoX_Failed(ReadAndUpdateVerifyContext( + fp, 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( + fp, &buf, sizeof(uint32_t), signatureHandles, signatureCount, + "signature algorithm ID"))) { + goto failure; + } + + if (CryptoX_Failed(ReadAndUpdateVerifyContext( + fp, &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 (fseeko(fp, signatureLengths[i], SEEK_CUR)) { + fprintf(stderr, "ERROR: Could not seek past signature.\n"); + goto failure; + } + } + + /* Read the rest of the file after the signature block */ + while (!feof(fp)) { + int numRead = fread(buf, 1, BLOCKSIZE, fp); + if (ferror(fp)) { + fprintf(stderr, "ERROR: Error reading data block.\n"); + goto failure; + } + + 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..17bf7b8387 --- /dev/null +++ b/modules/libmar/verify/moz.build @@ -0,0 +1,51 @@ +# -*- 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 + +REQUIRES_UNIFIED_BUILD = True |