Bug 798413 - Export signature from MAR files implementation. r=bsmith
authorBrian R. Bondy <netzen@gmail.com>
Wed, 17 Oct 2012 09:39:44 -0400
changeset 110558 b3d74f835aa9fe439a35727b1dc2ddcf4ab6455a
parent 110557 29ee4f3f346b5b8789e6c07178fe2286aa8ae471
child 110559 53560434a2361a7d86133bee62bcd776685a9643
push id16609
push usereakhgari@mozilla.com
push dateWed, 17 Oct 2012 17:03:21 +0000
treeherdermozilla-inbound@44265699e0a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmith
bugs798413
milestone19.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 798413 - Export signature from MAR files implementation. r=bsmith
modules/libmar/sign/mar_sign.c
modules/libmar/src/mar.h
modules/libmar/src/mar_cmdline.h
modules/libmar/tool/mar.c
--- a/modules/libmar/sign/mar_sign.c
+++ b/modules/libmar/sign/mar_sign.c
@@ -17,16 +17,17 @@
 #include "mar_cmdline.h"
 #include "mar.h"
 #include "cryptox.h"
 #ifndef XP_WIN
 #include <unistd.h>
 #endif
 
 #include "nss_secutil.h"
+#include "base64.h"
 
 /**
  * Initializes the NSS context.
  * 
  * @param NSSConfigDir The config dir containing the private key to use
  * @return 0 on success
  *         -1 on error
 */
@@ -473,16 +474,129 @@ failure:
 
   if (rv) {
     remove(dest);
   }
   return rv;
 }
 
 /**
+ * Extracts a signature from a MAR file, base64 encodes it, and writes it out
+ *
+ * @param  src       The path of the source MAR file
+ * @param  sigIndex  The index of the signature to extract
+ * @param  dest      The path of file to write the signature to
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+extract_signature(const char *src, uint32_t sigIndex, const char * dest)
+{
+  FILE *fpSrc = NULL, *fpDest = NULL;
+  uint32_t i;
+  uint32_t signatureCount;
+  uint32_t signatureLen;
+  uint8_t *extractedSignature = NULL;
+  char *base64Encoded = NULL;
+  int rv = -1;
+  if (!src || !dest) {
+    fprintf(stderr, "ERROR: Invalid parameter passed in.\n");
+    goto failure;
+  }
+
+  fpSrc = fopen(src, "rb");
+  if (!fpSrc) {
+    fprintf(stderr, "ERROR: could not open source file: %s\n", src);
+    goto failure;
+  }
+
+  fpDest = fopen(dest, "wb");
+  if (!fpDest) {
+    fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
+    goto failure;
+  }
+
+  /* Skip to the start of the signature block */
+  if (fseeko(fpSrc, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
+    fprintf(stderr, "ERROR: could not seek to signature block\n");
+    goto failure;
+  }
+
+  /* Get the number of signatures */
+  if (fread(&signatureCount, sizeof(signatureCount), 1, fpSrc) != 1) {
+    fprintf(stderr, "ERROR: could not read signature count\n");
+    goto failure;
+  }
+  signatureCount = ntohl(signatureCount);
+  if (sigIndex >= signatureCount) {
+    fprintf(stderr, "ERROR: Signature index was out of range\n");
+    goto failure;
+  }
+
+  /* Skip to the correct signature */
+  for (i = 0; i <= sigIndex; i++) {
+    /* skip past the signature algorithm ID */
+    if (fseeko(fpSrc, sizeof(uint32_t), SEEK_CUR)) {
+      fprintf(stderr, "ERROR: Could not seek past sig algorithm ID.\n");
+      goto failure;
+    }
+
+    /* Get the signature length */
+    if (fread(&signatureLen, sizeof(signatureLen), 1, fpSrc) != 1) {
+      fprintf(stderr, "ERROR: could not read signature length\n");
+      goto failure;
+    }
+    signatureLen = ntohl(signatureLen);
+
+    /* Get the signature */
+    extractedSignature = malloc(signatureLen);
+    if (fread(extractedSignature, signatureLen, 1, fpSrc) != 1) {
+      fprintf(stderr, "ERROR: could not read signature\n");
+      goto failure;
+    }
+  }
+
+  base64Encoded = BTOA_DataToAscii(extractedSignature, signatureLen);
+  if (!base64Encoded) {
+    fprintf(stderr, "ERROR: could not obtain base64 encoded data\n");
+    goto failure;
+  }
+
+  if (fwrite(base64Encoded, strlen(base64Encoded), 1, fpDest) != 1) {
+    fprintf(stderr, "ERROR: Could not write base64 encoded string\n");
+    goto failure;
+  }
+
+  rv = 0;
+failure:
+  if (base64Encoded) {
+    PORT_Free(base64Encoded);
+  }
+
+  if (extractedSignature) {
+    free(extractedSignature);
+  }
+
+  if (fpSrc) {
+    fclose(fpSrc);
+  }
+
+  if (fpDest) {
+    fclose(fpDest);
+  }
+
+  if (rv) {
+    remove(dest);
+  }
+
+  return rv;
+}
+
+
+/**
  * Writes out a copy of the MAR at src but with embedded signatures.
  * The passed in MAR file must not already be signed or an error will 
  * be returned.
  *
  * @param  NSSConfigDir  The NSS directory containing the private key for signing
  * @param  certNames     The nicknames of the certificate to use for signing
  * @param  certCount     The number of certificate names contained in certNames.
  *                       One signature will be produced for each certificate.
--- a/modules/libmar/src/mar.h
+++ b/modules/libmar/src/mar.h
@@ -11,18 +11,21 @@
 #include "prtypes.h"
 #include "mozilla/StandardInteger.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /* We have a MAX_SIGNATURES limit so that an invalid MAR will never
-   waste too much of either updater's or signmar's time.
-   It is also used at various places internally and will affect memory usage. */
+ * waste too much of either updater's or signmar's time.
+ * It is also used at various places internally and will affect memory usage.
+ * If you want to increase this value above 9 then you need to adjust parsing
+ * code in tool/mar.c.
+*/
 #define MAX_SIGNATURES 8
 PR_STATIC_ASSERT(MAX_SIGNATURES <= 9);
 
 struct ProductInformationBlock {
   const char *MARChannelID;
   const char *productVersion;
 };
 
--- a/modules/libmar/src/mar_cmdline.h
+++ b/modules/libmar/src/mar_cmdline.h
@@ -103,13 +103,25 @@ refresh_product_info_block(const char *p
  * @param  dest The path of the MAR file to write out that 
                 has no signature block
  * @return 0 on success
  *         -1 on error
 */
 int
 strip_signature_block(const char *src, const char * dest);
 
+/**
+ * Extracts a signature from a MAR file, base64 encodes it, and writes it out
+ *
+ * @param  src       The path of the source MAR file
+ * @param  sigIndex  The index of the signature to extract
+ * @param  dest      The path of file to write the signature to
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+extract_signature(const char *src, uint32_t sigIndex, const char * dest);
+
 #ifdef __cplusplus
 }
 #endif
 
 #endif  /* MAR_CMDLINE_H__ */
--- a/modules/libmar/tool/mar.c
+++ b/modules/libmar/tool/mar.c
@@ -25,36 +25,46 @@ int NSSInitCryptoContext(const char *NSS
 int mar_repackage_and_sign(const char *NSSConfigDir,
                            const char * const *certNames,
                            uint32_t certCount,
                            const char *src, 
                            const char * dest);
 
 static void print_usage() {
   printf("usage:\n");
+  printf("Create a MAR file:\n");
   printf("  mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
          "{-c|-x|-t|-T} archive.mar [files...]\n");
 #ifndef NO_SIGN_VERIFY
+  printf("Sign a MAR file:\n");
   printf("  mar [-C workingDir] -d NSSConfigDir -n certname -s "
          "archive.mar out_signed_archive.mar\n");
+  printf("Strip a MAR signature:\n");
   printf("  mar [-C workingDir] -r "
          "signed_input_archive.mar output_archive.mar\n");
+  printf("Extract a MAR signature:\n");
+  printf("  mar [-C workingDir] -n(i) -X "
+         "signed_input_archive.mar base_64_encoded_signature_file\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 
+  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] "
          "-i unsigned_archive_to_refresh.mar\n");
   printf("This program does not handle unicode file paths properly\n");
 }
 
 static int mar_test_callback(MarFile *mar, 
                              const MarItem *item, 
                              void *unused) {
@@ -79,16 +89,17 @@ 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;
+  int32_t sigIndex = -1;
 #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];
@@ -107,17 +118,17 @@ int main(int argc, char **argv) {
     return -1;
   }
 
   while (argc > 0) {
     if (argv[1][0] == '-' && (argv[1][1] == 'c' || 
         argv[1][1] == 't' || argv[1][1] == 'x' || 
         argv[1][1] == 'v' || argv[1][1] == 's' ||
         argv[1][1] == 'i' || argv[1][1] == 'T' ||
-        argv[1][1] == 'r')) {
+        argv[1][1] == 'r' || argv[1][1] == 'X')) {
       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)
@@ -141,24 +152,34 @@ int main(int argc, char **argv) {
       NSSConfigDir = argv[2];
       argv += 2;
       argc -= 2;
      /* -n certName, also matches -n[index] certName
         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] == 'n' &&
-               (argv[1][2] == '0' + certCount || argv[1][2] == '\0')) {
+               (argv[1][2] == '0' + certCount ||
+                argv[1][2] == '\0' ||
+                !strcmp(argv[2], "-X"))) {
       if (certCount >= MAX_SIGNATURES) {
         print_usage();
         return -1;
       }
       certNames[certCount++] = argv[2];
-      argv += 2;
-      argc -= 2;
+      if (strlen(argv[1]) > 2 &&
+        !strcmp(argv[2], "-X") &&
+        argv[1][2] >= '0' && argv[1][2] <= '9') {
+        sigIndex = argv[1][2] - '0';
+        argv++;
+        argc--;
+      } else {
+        argv += 2;
+        argc -= 2;
+      }
     /* MAR channel ID */
     } else if (argv[1][0] == '-' && argv[1][1] == 'H') {
       MARChannelID = argv[2];
       argv += 2;
       argc -= 2;
     /* Product Version */
     } else if (argv[1][0] == '-' && argv[1][1] == 'V') {
       productVersion = argv[2];
@@ -221,20 +242,34 @@ int main(int argc, char **argv) {
       }
      }
     printf("\n");
     /* The fall through from 'T' to 't' is intentional */
   }
   case 't':
     return mar_test(argv[2]);
 
+  /* Extract a MAR file */
   case 'x':
     return mar_extract(argv[2]);
 
 #ifndef NO_SIGN_VERIFY
+  /* Extract a MAR signature */
+  case 'X':
+    if (sigIndex == -1) {
+      fprintf(stderr, "ERROR: Signature index was not passed.\n");
+      return -1;
+    }
+    if (sigIndex >= MAX_SIGNATURES || sigIndex < -1) {
+      fprintf(stderr, "ERROR: Signature index is out of range: %d.\n",
+              sigIndex);
+      return -1;
+    }
+    return extract_signature(argv[2], sigIndex, argv[3]);
+
   case 'v':
 
 #if defined(XP_WIN) && !defined(MAR_NSS)
     if (certCount == 0) {
       print_usage();
       return -1;
     }