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 110568 b3d74f835aa9fe439a35727b1dc2ddcf4ab6455a
parent 110567 29ee4f3f346b5b8789e6c07178fe2286aa8ae471
child 110569 53560434a2361a7d86133bee62bcd776685a9643
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersbsmith
bugs798413
milestone19.0a1
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;
     }