Bug 978596: Implement native MAR verification for OS X 10.7+. r=bbondy, rstrong, smichaud
authorStephen Pohl <spohl.mozilla.bugs@gmail.com>
Tue, 08 Apr 2014 10:06:56 -0400
changeset 177578 a502db5137ab95d9b3093aa31b0df517ce78b5f2
parent 177577 8920c42b19309e22a71db54ae5992c19386248f2
child 177579 704fd93fb58dcc80bb28e8be7e7b4cacbe1b2389
push id26556
push userryanvm@gmail.com
push dateTue, 08 Apr 2014 22:16:57 +0000
treeherdermozilla-central@5811efc11011 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbondy, rstrong, smichaud
bugs978596
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 978596: Implement native MAR verification for OS X 10.7+. r=bbondy, rstrong, smichaud
modules/libmar/moz.build
modules/libmar/tests/unit/test_sign_verify.js
modules/libmar/tool/mar.c
modules/libmar/verify/MacVerifyCrypto.cpp
modules/libmar/verify/cryptox.h
modules/libmar/verify/moz.build
--- a/modules/libmar/moz.build
+++ b/modules/libmar/moz.build
@@ -7,12 +7,15 @@
 DIRS += ['src']
 
 if CONFIG['MOZ_ENABLE_SIGNMAR']:
     DIRS += ['sign', 'verify']
     TEST_DIRS += ['tests']
 elif CONFIG['OS_ARCH'] == 'WINNT':
     # On Windows we don't verify with NSS and updater needs to link to it
     DIRS += ['verify']
+elif CONFIG['OS_ARCH'] == 'Darwin':
+    # On OSX 10.7+ we don't verify with NSS and updater needs to link to it
+    DIRS += ['verify']
 
 # If we are building ./sign and ./verify then ./tool must come after it
 DIRS += ['tool']
 
--- a/modules/libmar/tests/unit/test_sign_verify.js
+++ b/modules/libmar/tests/unit/test_sign_verify.js
@@ -137,46 +137,48 @@ function run_test() {
 
     // Make sure the signmar binary exists and is an executable.
     do_check_true(signmarBin.exists());
     do_check_true(signmarBin.isExecutable());
 
     // Will reference the arguments to use for verification in signmar
     let args = [];
 
-    // The XPCShell test wiki indicates this is the preferred way for 
-    // Windows detection.
+    // The XPCShell test wiki indicates this is the preferred way for
+    // Windows and OSX detection.
     var isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
+    var isOSX = ("nsILocalFileMac" in Components.interfaces);
 
     // Setup the command line arguments to create the MAR.
-    // Windows vs. Linux/Mac/... have different command line for verification 
-    // since  on Windows we verify with CryptoAPI and on all other platforms 
-    // we verify with NSS. So on Windows we use an exported DER file and on 
-    // other platforms we use the NSS config db.
-    if (isWindows) {
-      if (certs.length == 1 && useShortHandCmdLine) {
-        args.push("-D", "data/" + certs[0] + ".der");
-      } else {
-        for (i = 0; i < certs.length; i++) {
-          args.push("-D" + i, "data/" + certs[i] + ".der");
-        }
-      }
-      args.push("-v", signedMAR.path);
-    } else {
+    // Windows & Mac vs. Linux/... have different command line for verification
+    // since on Windows we verify with CryptoAPI, on Mac with Security
+    // Transforms or NSS and on all other platforms we verify with NSS. So on
+    // Windows and Mac we use an exported DER file and on other platforms we use
+    // the NSS config db.
+    if (!isWindows) {
       let NSSConfigDir = do_get_file("data");
       args = ["-d", NSSConfigDir.path];
       if (certs.length == 1 && useShortHandCmdLine) {
         args.push("-n", certs[0]);
       } else {
-        for (i = 0; i < certs.length; i++) {
+        for (var i = 0; i < certs.length; i++) {
           args.push("-n" + i, certs[i]);
         }
       }
-      args.push("-v", signedMAR.path);
     }
+    if (isWindows || isOSX) {
+      if (certs.length == 1 && useShortHandCmdLine) {
+        args.push("-D", "data/" + certs[0] + ".der");
+      } else {
+        for (var i = 0; i < certs.length; i++) {
+          args.push("-D" + i, "data/" + certs[i] + ".der");
+        }
+      }
+    }
+    args.push("-v", signedMAR.path);
 
     process.init(signmarBin);
     try {
       // We put this in a try block because nsIProcess doesn't like -1 returns
       process.run(true, args, args.length);
     } catch (e) {
       // On Windows negative return value throws an exception
       process.exitValue = -1;
--- a/modules/libmar/tool/mar.c
+++ b/modules/libmar/tool/mar.c
@@ -53,20 +53,28 @@ static void print_usage() {
          "signed_input_archive.mar base_64_encoded_signature_file "
          "changed_signed_output.mar\n");
   printf("(i) is the index of the certificate to extract\n");
 #if defined(XP_WIN) && !defined(MAR_NSS)
   printf("Verify a MAR file:\n");
   printf("  mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n");
   printf("At most %d signature certificate DER files are specified by "
          "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES);
-#else 
+#elif defined(XP_MACOSX)
   printf("Verify a MAR file:\n");
   printf("  mar [-C workingDir] -d NSSConfigDir -n certname "
-    "-v signed_archive.mar\n");
+         "-v signed_archive.mar -D DERFilePath\n");
+  printf("At most %d signature certificate names are specified by "
+         "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES);
+  printf("At most %d signature certificate DER files are specified by "
+         "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES);
+#else
+  printf("Verify a MAR file:\n");
+  printf("  mar [-C workingDir] -d NSSConfigDir -n certname "
+         "-v signed_archive.mar\n");
   printf("At most %d signature certificate names are specified by "
          "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES);
 #endif
   printf("At most %d verification certificate names are specified by "
          "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES);
 #endif
   printf("Print information on a MAR file:\n");
   printf("  mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
@@ -100,34 +108,39 @@ static int mar_test(const char *path) {
 int main(int argc, char **argv) {
   char *NSSConfigDir = NULL;
   const char *certNames[MAX_SIGNATURES];
   char *MARChannelID = MAR_CHANNEL_ID;
   char *productVersion = MOZ_APP_VERSION;
   uint32_t i, k;
   int rv = -1;
   uint32_t certCount = 0;
+  uint32_t derCount = 0;
   int32_t sigIndex = -1;
+  char* DERFilePaths[MAX_SIGNATURES];
+
 #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
   HANDLE certFile;
-  /* We use DWORD here instead of uint64_t because it simplifies code with
-     the Win32 API ReadFile which takes a DWORD.  DER files will not be too
-     large anyway. */
-  DWORD fileSizes[MAX_SIGNATURES];
-  DWORD read;
   uint8_t *certBuffers[MAX_SIGNATURES];
-  char *DERFilePaths[MAX_SIGNATURES];
+#endif
+#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \
+                                 defined(XP_MACOSX))
+  uint32_t fileSizes[MAX_SIGNATURES];
+  uint32_t read;
 #endif
 
   memset(certNames, 0, sizeof(certNames));
 #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
+  memset(certBuffers, 0, sizeof(certBuffers));
+#endif
+#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \
+                                 defined(XP_MACOSX))
   memset(fileSizes, 0, sizeof(fileSizes));
-  memset(certBuffers, 0, sizeof(certBuffers));
+#endif
   memset(DERFilePaths, 0, sizeof(DERFilePaths));
-#endif
 
   if (argc > 1 && 0 == strcmp(argv[1], "--version")) {
     print_version();
     return 0;
   }
 
   if (argc < 3) {
     print_usage();
@@ -143,28 +156,29 @@ int main(int argc, char **argv) {
         argv[1][1] == 'I')) {
       break;
     /* -C workingdirectory */
     } else if (argv[1][0] == '-' && argv[1][1] == 'C') {
       chdir(argv[2]);
       argv += 2;
       argc -= 2;
     } 
-#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
+#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \
+                                 defined(XP_MACOSX))
     /* -D DERFilePath, also matches -D[index] DERFilePath
        We allow an index for verifying to be symmetric
        with the import and export command line arguments. */
     else if (argv[1][0] == '-' &&
              argv[1][1] == 'D' &&
-             (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) {
-      if (certCount >= MAX_SIGNATURES) {
+             (argv[1][2] == (char)('0' + derCount) || argv[1][2] == '\0')) {
+      if (derCount >= MAX_SIGNATURES) {
         print_usage();
         return -1;
       }
-      DERFilePaths[certCount++] = argv[2];
+      DERFilePaths[derCount++] = argv[2];
       argv += 2;
       argc -= 2;
     }
 #endif
     /* -d NSSConfigdir */
     else if (argv[1][0] == '-' && argv[1][1] == 'd') {
       NSSConfigDir = argv[2];
       argv += 2;
@@ -298,22 +312,22 @@ int main(int argc, char **argv) {
       print_usage();
       return -1;
     }
     return import_signature(argv[2], sigIndex, argv[3], argv[4]);
 
   case 'v':
 
 #if defined(XP_WIN) && !defined(MAR_NSS)
-    if (certCount == 0) {
+    if (derCount == 0) {
       print_usage();
       return -1;
     }
 
-    for (k = 0; k < certCount; ++k) {
+    for (k = 0; k < derCount; ++k) {
       /* If the mar program was built using CryptoAPI, then read in the buffer
         containing the cert from disk. */
       certFile = CreateFileA(DERFilePaths[k], GENERIC_READ,
                              FILE_SHARE_READ |
                              FILE_SHARE_WRITE |
                              FILE_SHARE_DELETE,
                              NULL,
                              OPEN_EXISTING,
@@ -330,47 +344,48 @@ int main(int argc, char **argv) {
           free(certBuffers[i]);
         }
         return -1;
       }
       CloseHandle(certFile);
     }
 
     rv = mar_verify_signatures(argv[2], certBuffers, fileSizes,
-                               NULL, certCount);
-    for (k = 0; k < certCount; ++k) {
+                               NULL, derCount);
+    for (k = 0; k < derCount; ++k) {
       free(certBuffers[k]);
     }
     if (rv) {
       /* Determine if the source MAR file has the new fields for signing */
       int hasSignatureBlock;
       if (get_mar_file_info(argv[2], &hasSignatureBlock, 
                             NULL, NULL, NULL, NULL)) {
         fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n");
       } else if (!hasSignatureBlock) {
         fprintf(stderr, "ERROR: The MAR file is in the old format so has"
                         " no signature to verify.\n");
       }
       return -1;
     }
 
     return 0;
+
 #else
     if (!NSSConfigDir || certCount == 0) {
       print_usage();
       return -1;
     }
 
     if (NSSInitCryptoContext(NSSConfigDir)) {
       fprintf(stderr, "ERROR: Could not initialize crypto library.\n");
       return -1;
     }
 
-    return mar_verify_signatures(argv[2], NULL, 0,
-                                 certNames, certCount);
+    return mar_verify_signatures(argv[2], (const uint8_t* const*)DERFilePaths,
+                                 0, certNames, certCount);
 
 #endif /* defined(XP_WIN) && !defined(MAR_NSS) */
   case 's':
     if (!NSSConfigDir || certCount == 0 || argc < 4) {
       print_usage();
       return -1;
     }
     return mar_repackage_and_sign(NSSConfigDir, certNames, certCount,
new file mode 100644
--- /dev/null
+++ b/modules/libmar/verify/MacVerifyCrypto.cpp
@@ -0,0 +1,367 @@
+/* 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.
+#ifdef __cplusplus
+extern "C" {
+#endif
+  const CFStringRef kSecTransformInputAttributeName = CFSTR("INPUT");
+  typedef CFTypeRef SecTransformRef;
+  typedef struct 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;
+  typedef SecCertificateRef (*SecCertificateCreateWithDataFunc)
+                              (CFAllocatorRef allocator,
+                               CFDataRef data);
+  SecCertificateCreateWithDataFunc SecCertificateCreateWithDataPtr = NULL;
+  typedef OSStatus (*SecCertificateCopyPublicKeyFunc)
+                     (SecCertificateRef certificate,
+                      SecKeyRef* key);
+  SecCertificateCopyPublicKeyFunc SecCertificateCopyPublicKeyPtr = NULL;
+#ifdef __cplusplus
+}
+#endif
+
+#define MAC_OS_X_VERSION_10_7_HEX 0x00001070
+
+static int sOnLionOrLater = -1;
+
+static bool OnLionOrLater()
+{
+  if (sOnLionOrLater < 0) {
+    SInt32 major = 0, minor = 0;
+
+    CFURLRef url =
+      CFURLCreateWithString(kCFAllocatorDefault,
+                            CFSTR("file:///System/Library/CoreServices/SystemVersion.plist"),
+                            NULL);
+    CFReadStreamRef stream =
+      CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+    CFReadStreamOpen(stream);
+    CFDictionaryRef sysVersionPlist = (CFDictionaryRef)
+      CFPropertyListCreateWithStream(kCFAllocatorDefault,
+                                     stream, 0, kCFPropertyListImmutable,
+                                     NULL, NULL);
+    CFReadStreamClose(stream);
+    CFRelease(stream);
+    CFRelease(url);
+
+    CFStringRef versionString = (CFStringRef)
+      CFDictionaryGetValue(sysVersionPlist, CFSTR("ProductVersion"));
+    CFArrayRef versions =
+      CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault,
+                                             versionString, CFSTR("."));
+    CFIndex count = CFArrayGetCount(versions);
+    if (count > 0) {
+      CFStringRef component = (CFStringRef) CFArrayGetValueAtIndex(versions, 0);
+      major = CFStringGetIntValue(component);
+      if (count > 1) {
+        component = (CFStringRef) CFArrayGetValueAtIndex(versions, 1);
+        minor = CFStringGetIntValue(component);
+      }
+    }
+    CFRelease(sysVersionPlist);
+    CFRelease(versions);
+
+    if (major < 10) {
+      sOnLionOrLater = 0;
+    } else {
+      int version = 0x1000 + (minor << 4);
+      sOnLionOrLater = version >= MAC_OS_X_VERSION_10_7_HEX ? 1 : 0;
+    }
+  }
+
+  return sOnLionOrLater > 0 ? true : false;
+}
+
+CryptoX_Result
+CryptoMac_InitCryptoProvider()
+{
+  if (!OnLionOrLater()) {
+    return CryptoX_Success;
+  }
+
+  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 (!SecCertificateCreateWithDataPtr) {
+    SecCertificateCreateWithDataPtr = (SecCertificateCreateWithDataFunc)
+      dlsym(RTLD_DEFAULT, "SecCertificateCreateWithData");
+  }
+  if (!SecCertificateCopyPublicKeyPtr) {
+    SecCertificateCopyPublicKeyPtr = (SecCertificateCopyPublicKeyFunc)
+      dlsym(RTLD_DEFAULT, "SecCertificateCopyPublicKey");
+  }
+  if (!SecTransformCreateReadTransformWithReadStreamPtr ||
+      !SecTransformExecutePtr ||
+      !SecVerifyTransformCreatePtr ||
+      !SecTransformSetAttributePtr ||
+      !SecCertificateCreateWithDataPtr ||
+      !SecCertificateCopyPublicKeyPtr) {
+    return CryptoX_Error;
+  }
+  return CryptoX_Success;
+}
+
+CryptoX_Result
+CryptoMac_VerifyBegin(CryptoX_SignatureHandle* aInputData,
+                      CryptoX_PublicKey* aPublicKey)
+{
+  if (!OnLionOrLater()) {
+    return NSS_VerifyBegin((VFYContext**)aInputData,
+                           (SECKEYPublicKey* const*)aPublicKey);
+  }
+
+  (void)aPublicKey;
+  if (!aInputData) {
+    return CryptoX_Error;
+  }
+
+  CryptoX_Result result = CryptoX_Error;
+  *aInputData = CFDataCreateMutable(kCFAllocatorDefault, 0);
+  if (*aInputData) {
+    result = CryptoX_Success;
+  }
+
+  return result;
+}
+
+CryptoX_Result
+CryptoMac_VerifyUpdate(CryptoX_SignatureHandle* aInputData, void* aBuf,
+                       unsigned int aLen)
+{
+  if (!OnLionOrLater()) {
+    return VFY_Update((VFYContext*)*aInputData,
+                      (const unsigned char*)aBuf, aLen);
+  }
+
+  if (aLen == 0) {
+    return CryptoX_Success;
+  }
+  if (!aInputData || !*aInputData) {
+    return CryptoX_Error;
+  }
+
+  CryptoX_Result result = CryptoX_Error;
+  CFDataAppendBytes((CFMutableDataRef)*aInputData, (const UInt8 *) aBuf, aLen);
+  if (*aInputData) {
+    result = CryptoX_Success;
+  }
+
+  return result;
+}
+
+CryptoX_Result
+CryptoMac_LoadPublicKey(const unsigned char* aCertData,
+                        CryptoX_PublicKey* aPublicKey,
+                        const char* aCertName,
+                        CryptoX_Certificate* aCert)
+{
+  if (!aPublicKey ||
+      (OnLionOrLater() && !aCertData) ||
+      (!OnLionOrLater() && !aCertName)) {
+    return CryptoX_Error;
+  }
+
+  if (!OnLionOrLater()) {
+    return NSS_LoadPublicKey(aCertName,
+                             (SECKEYPublicKey**)aPublicKey,
+                             (CERTCertificate**)aCert);
+  }
+
+  CFURLRef url =
+    CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+                                            aCertData,
+                                            strlen((char*)aCertData),
+                                            false);
+  if (!url) {
+    return CryptoX_Error;
+  }
+
+  CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+  if (!stream) {
+    CFRelease(url);
+    return CryptoX_Error;
+  }
+
+  SecTransformRef readTransform =
+    SecTransformCreateReadTransformWithReadStreamPtr(stream);
+  if (!readTransform) {
+    CFRelease(url);
+    CFRelease(stream);
+    return CryptoX_Error;
+  }
+
+  CFErrorRef error;
+  CFDataRef tempCertData = (CFDataRef)SecTransformExecutePtr(readTransform,
+                                                             &error);
+  if (!tempCertData || error) {
+    CFRelease(url);
+    CFRelease(stream);
+    CFRelease(readTransform);
+    return CryptoX_Error;
+  }
+
+  SecCertificateRef cert = SecCertificateCreateWithDataPtr(kCFAllocatorDefault,
+                                                           tempCertData);
+  if (!cert) {
+    CFRelease(url);
+    CFRelease(stream);
+    CFRelease(readTransform);
+    CFRelease(tempCertData);
+    return CryptoX_Error;
+  }
+
+  CryptoX_Result result = CryptoX_Error;
+  OSStatus status = SecCertificateCopyPublicKeyPtr(cert,
+                                                   (SecKeyRef*)aPublicKey);
+  if (status == 0) {
+    result = CryptoX_Success;
+  }
+
+  CFRelease(url);
+  CFRelease(stream);
+  CFRelease(readTransform);
+  CFRelease(tempCertData);
+  CFRelease(cert);
+
+  return result;
+}
+
+CryptoX_Result
+CryptoMac_VerifySignature(CryptoX_SignatureHandle* aInputData,
+                          CryptoX_PublicKey* aPublicKey,
+                          const unsigned char* aSignature,
+                          unsigned int aSignatureLen)
+{
+  if (!OnLionOrLater()) {
+    return NSS_VerifySignature((VFYContext* const*)aInputData, aSignature,
+                               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) {
+    CFRelease(signatureData);
+    return CryptoX_Error;
+  }
+
+  SecTransformSetAttributePtr(verifier,
+                              kSecTransformInputAttributeName,
+                              (CFDataRef)*aInputData,
+                              &error);
+  if (error) {
+    CFRelease(signatureData);
+    CFRelease(verifier);
+    return CryptoX_Error;
+  }
+
+  CryptoX_Result result = CryptoX_Error;
+  CFTypeRef rv = SecTransformExecutePtr(verifier, &error);
+  if (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 (!OnLionOrLater()) {
+    return VFY_DestroyContext((VFYContext*)aInputData, PR_TRUE);
+  }
+
+  if (!aInputData || !*aInputData) {
+    return;
+  }
+  CFRelease((CFMutableDataRef)*aInputData);
+}
+
+void
+CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey)
+{
+  if (!OnLionOrLater()) {
+    return SECKEY_DestroyPublicKey((SECKEYPublicKey*)*aPublicKey);
+  }
+
+  if (!aPublicKey || !*aPublicKey) {
+    return;
+  }
+  CFRelease((SecKeyRef)*aPublicKey);
+}
+
+void
+CryptoMac_FreeCertificate(CryptoX_Certificate* aCertificate)
+{
+  if (!OnLionOrLater()) {
+    return CERT_DestroyCertificate((CERTCertificate*)*aCertificate);
+  }
+
+  if (!aCertificate || !*aCertificate) {
+    return;
+  }
+  CFRelease((SecKeyRef)*aCertificate);
+}
--- a/modules/libmar/verify/cryptox.h
+++ b/modules/libmar/verify/cryptox.h
@@ -12,27 +12,85 @@
 #define CryptoX_Error (-1)
 #define CryptoX_Succeeded(X) ((X) == CryptoX_Success)
 #define CryptoX_Failed(X) ((X) != CryptoX_Success)
 
 #if defined(MAR_NSS)
 
 #include "nss_secutil.h"
 
+#define CryptoX_InvalidHandleValue NULL
+#define CryptoX_ProviderHandle void*
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 CryptoX_Result NSS_LoadPublicKey(const char *certNickname, 
                                  SECKEYPublicKey **publicKey, 
                                  CERTCertificate **cert);
 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_InvalidHandleValue NULL
-#define CryptoX_ProviderHandle void*
+#ifdef XP_MACOSX
+
+#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_PublicKey* aPublicKey);
+CryptoX_Result CryptoMac_VerifyUpdate(CryptoX_SignatureHandle* aInputData,
+                                      void* aBuf, unsigned int aLen);
+CryptoX_Result CryptoMac_LoadPublicKey(const unsigned char* aCertData,
+                                       CryptoX_PublicKey* aPublicKey,
+                                       const char* aCertName,
+                                       CryptoX_Certificate* aCert);
+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);
+void CryptoMac_FreeCertificate(CryptoX_Certificate* aCertificate);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#define CryptoX_InitCryptoProvider(aCryptoHandle) \
+  CryptoMac_InitCryptoProvider()
+#define CryptoX_VerifyBegin(aCryptoHandle, aInputData, aPublicKey) \
+  CryptoMac_VerifyBegin(aInputData, aPublicKey)
+#define CryptoX_VerifyUpdate(aInputData, aBuf, aLen) \
+  CryptoMac_VerifyUpdate(aInputData, aBuf, aLen)
+#define CryptoX_LoadPublicKey(aCryptoHandle, aCertData, aDataSize, \
+                              aPublicKey, aCertName, aCert) \
+  CryptoMac_LoadPublicKey(aCertData, aPublicKey, aCertName, aCert)
+#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) \
+  CryptoMac_FreeCertificate(aCertificate)
+
+#else
+
 #define CryptoX_SignatureHandle VFYContext *
 #define CryptoX_PublicKey SECKEYPublicKey *
 #define CryptoX_Certificate CERTCertificate *
 #define CryptoX_InitCryptoProvider(CryptoHandle) \
   CryptoX_Success
 #define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
   NSS_VerifyBegin(SignatureHandle, PublicKey)
 #define CryptoX_FreeSignatureHandle(SignatureHandle) \
@@ -44,16 +102,18 @@ CryptoX_Result NSS_VerifySignature(VFYCo
   NSS_LoadPublicKey(certName, publicKey, cert)
 #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)
 
+#endif
+
 #elif defined(XP_WIN) 
 
 #include <windows.h>
 #include <wincrypt.h>
 
 CryptoX_Result CryptoAPI_InitCryptoContext(HCRYPTPROV *provider);
 CryptoX_Result CryptoAPI_LoadPublicKey(HCRYPTPROV hProv, 
                                        BYTE *certData,
--- a/modules/libmar/verify/moz.build
+++ b/modules/libmar/verify/moz.build
@@ -14,12 +14,17 @@ UNIFIED_SOURCES += [
 FORCE_STATIC_LIB = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     USE_STATIC_LIBS = True
 else:
     DEFINES['MAR_NSS'] = True
     LOCAL_INCLUDES += ['../sign']
 
+if CONFIG['OS_ARCH'] == 'Darwin':
+    UNIFIED_SOURCES += [
+      'MacVerifyCrypto.cpp',
+    ]
+
 LOCAL_INCLUDES += [
     '../src',
 ]