Bug 798415 - Import signature to MAR files implementation. r=bsmith
authorBrian R. Bondy <netzen@gmail.com>
Wed, 17 Oct 2012 09:39:44 -0400
changeset 110572 bd12f4180358f5ef14e1d4a71c7c38fab3b1df8f
parent 110571 31534cf5dd450f5a9243673edd4a0dbc4e99e5db
child 110573 e64299ca74390d6810e6bdf87f9fe01d59d61574
child 110703 44265699e0a98fb5e9063b73c2015ae50efe5af4
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersbsmith
bugs798415
milestone19.0a1
Bug 798415 - Import signature to MAR files implementation. r=bsmith
modules/libmar/sign/mar_sign.c
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
@@ -267,17 +267,17 @@ strip_signature_block(const char *src, c
 
   if (!src || !dest) {
     fprintf(stderr, "ERROR: Invalid parameter passed in.\n");
     return -1;
   }
 
   fpSrc = fopen(src, "rb");
   if (!fpSrc) {
-    fprintf(stderr, "ERROR: could not open source file: %s\n", dest);
+    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;
   }
@@ -585,16 +585,222 @@ failure:
 
   if (rv) {
     remove(dest);
   }
 
   return rv;
 }
 
+/**
+ * Imports a base64 encoded signature into a MAR file
+ *
+ * @param  src           The path of the source MAR file
+ * @param  sigIndex      The index of the signature to import
+ * @param  base64SigFile A file which contains the signature to import
+ * @param  dest          The path of the destination MAR file with replaced signature
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+import_signature(const char *src, uint32_t sigIndex,
+                 const char *base64SigFile, const char *dest)
+{
+  int rv = -1;
+  FILE *fpSrc, *fpDest, *fpSigFile;
+  uint32_t i;
+  uint32_t signatureCount, signatureLen, signatureAlgorithmID,
+           numChunks, leftOver;
+  char buf[BLOCKSIZE];
+  uint64_t sizeOfSrcMAR, sizeOfBase64EncodedFile;
+  char *passedInSignatureB64 = NULL;
+  uint8_t *passedInSignatureRaw = NULL;
+  uint8_t *extractedMARSignature = NULL;
+  unsigned int passedInSignatureLenRaw;
+
+  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 open dest file: %s\n", dest);
+    goto failure;
+  }
+
+  fpSigFile = fopen(base64SigFile , "rb");
+  if (!fpSigFile) {
+    fprintf(stderr, "ERROR: could not open sig file: %s\n", base64SigFile);
+    goto failure;
+  }
+
+  /* Get the src file size */
+  if (fseeko(fpSrc, 0, SEEK_END)) {
+    fprintf(stderr, "ERROR: Could not seek to end of src file.\n");
+    goto failure;
+  }
+  sizeOfSrcMAR = ftello(fpSrc);
+  if (fseeko(fpSrc, 0, SEEK_SET)) {
+    fprintf(stderr, "ERROR: Could not seek to start of src file.\n");
+    goto failure;
+  }
+
+  /* Get the sig file size */
+  if (fseeko(fpSigFile, 0, SEEK_END)) {
+    fprintf(stderr, "ERROR: Could not seek to end of sig file.\n");
+    goto failure;
+  }
+  sizeOfBase64EncodedFile= ftello(fpSigFile);
+  if (fseeko(fpSigFile, 0, SEEK_SET)) {
+    fprintf(stderr, "ERROR: Could not seek to start of sig file.\n");
+    goto failure;
+  }
+
+  /* Read in the base64 encoded signature to import */
+  passedInSignatureB64 = malloc(sizeOfBase64EncodedFile + 1);
+  passedInSignatureB64[sizeOfBase64EncodedFile] = '\0';
+  if (fread(passedInSignatureB64, sizeOfBase64EncodedFile, 1, fpSigFile) != 1) {
+    fprintf(stderr, "ERROR: Could read b64 sig file.\n");
+    goto failure;
+  }
+
+  /* Decode the base64 encoded data */
+  passedInSignatureRaw = ATOB_AsciiToData(passedInSignatureB64, &passedInSignatureLenRaw);
+  if (!passedInSignatureRaw) {
+    fprintf(stderr, "ERROR: could not obtain base64 decoded data\n");
+    goto failure;
+  }
+
+  /* Read everything up until the signature block offset and write it out */
+  if (ReadAndWrite(fpSrc, fpDest, buf,
+                   SIGNATURE_BLOCK_OFFSET, "signature block offset")) {
+    goto failure;
+  }
+
+  /* Get the number of signatures */
+  if (ReadAndWrite(fpSrc, fpDest, &signatureCount,
+                   sizeof(signatureCount), "signature count")) {
+    goto failure;
+  }
+  signatureCount = ntohl(signatureCount);
+  if (signatureCount > MAX_SIGNATURES) {
+    fprintf(stderr, "ERROR: Signature count was out of range\n");
+    goto failure;
+  }
+
+  if (sigIndex >= signatureCount) {
+    fprintf(stderr, "ERROR: Signature index was out of range\n");
+    goto failure;
+  }
+
+  /* Read and write the whole signature block, but if we reach the
+     signature offset, then we should replace it with the specified
+     base64 decoded signature */
+  for (i = 0; i < signatureCount; i++) {
+    /* Read/Write the signature algorithm ID */
+    if (ReadAndWrite(fpSrc, fpDest,
+                     &signatureAlgorithmID,
+                     sizeof(signatureAlgorithmID), "sig algorithm ID")) {
+      goto failure;
+    }
+
+    /* Read/Write the signature length */
+    if (ReadAndWrite(fpSrc, fpDest,
+                     &signatureLen, sizeof(signatureLen), "sig length")) {
+      goto failure;
+    }
+    signatureLen = ntohl(signatureLen);
+
+    /* Get the signature */
+    if (extractedMARSignature) {
+      free(extractedMARSignature);
+    }
+    extractedMARSignature = malloc(signatureLen);
+
+    if (sigIndex == i) {
+      if (passedInSignatureLenRaw != signatureLen) {
+        fprintf(stderr, "ERROR: Signature length must be the same\n");
+        goto failure;
+      }
+
+      if (fread(extractedMARSignature, signatureLen, 1, fpSrc) != 1) {
+        fprintf(stderr, "ERROR: Could not read signature\n");
+        goto failure;
+      }
+
+      if (fwrite(passedInSignatureRaw, passedInSignatureLenRaw,
+                 1, fpDest) != 1) {
+        fprintf(stderr, "ERROR: Could not write signature\n");
+        goto failure;
+      }
+    } else {
+      if (ReadAndWrite(fpSrc, fpDest,
+                       extractedMARSignature, signatureLen, "signature")) {
+        goto failure;
+      }
+    }
+  }
+
+  /* We replaced the signature so let's just skip past the rest o the
+     file. */
+  numChunks = (sizeOfSrcMAR - ftello(fpSrc)) / BLOCKSIZE;
+  leftOver = (sizeOfSrcMAR - ftello(fpSrc)) % BLOCKSIZE;
+
+  /* Read each file and write it to the MAR file */
+  for (i = 0; i < numChunks; ++i) {
+    if (ReadAndWrite(fpSrc, fpDest, buf, BLOCKSIZE, "content block")) {
+      goto failure;
+    }
+  }
+
+  if (ReadAndWrite(fpSrc, fpDest, buf, leftOver, "left over content block")) {
+    goto failure;
+  }
+
+  rv = 0;
+
+failure:
+
+  if (fpSrc) {
+    fclose(fpSrc);
+  }
+
+  if (fpDest) {
+    fclose(fpDest);
+  }
+
+  if (fpSigFile) {
+    fclose(fpSigFile);
+  }
+
+  if (rv) {
+    remove(dest);
+  }
+
+  if (extractedMARSignature) {
+    free(extractedMARSignature);
+  }
+
+  if (passedInSignatureB64) {
+    free(passedInSignatureB64);
+  }
+
+  if (passedInSignatureRaw) {
+    PORT_Free(passedInSignatureRaw);
+  }
+
+  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
@@ -644,17 +850,17 @@ mar_repackage_and_sign(const char *NSSCo
     fprintf(stderr, "ERROR: Could not init config dir: %s\n", NSSConfigDir);
     goto failure;
   }
 
   PK11_SetPasswordFunc(SECU_GetModulePassword);
 
   fpSrc = fopen(src, "rb");
   if (!fpSrc) {
-    fprintf(stderr, "ERROR: could not open source file: %s\n", dest);
+    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;
   }
--- a/modules/libmar/src/mar_cmdline.h
+++ b/modules/libmar/src/mar_cmdline.h
@@ -115,13 +115,29 @@ strip_signature_block(const char *src, c
  * @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);
 
+/**
+ * Imports a base64 encoded signature into a MAR file
+ *
+ * @param  src           The path of the source MAR file
+ * @param  sigIndex      The index of the signature to import
+ * @param  base64SigFile A file which contains the signature to import
+ * @param  dest          The path of the destination MAR file with replaced signature
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+import_signature(const char *src,
+                 uint32_t sigIndex,
+                 const char * base64SigFile,
+                 const char *dest);
+
 #ifdef __cplusplus
 }
 #endif
 
 #endif  /* MAR_CMDLINE_H__ */
--- a/modules/libmar/tool/mar.c
+++ b/modules/libmar/tool/mar.c
@@ -38,16 +38,20 @@ static void print_usage() {
   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("Import a MAR signature:\n");
+  printf("  mar [-C workingDir] -n(i) -I "
+         "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 
   printf("Verify a MAR file:\n");
@@ -118,17 +122,18 @@ 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] == 'X')) {
+        argv[1][1] == 'r' || argv[1][1] == 'X' ||
+        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)
@@ -154,25 +159,26 @@ int main(int argc, char **argv) {
       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' ||
-                !strcmp(argv[2], "-X"))) {
+                !strcmp(argv[2], "-X") ||
+                !strcmp(argv[2], "-I"))) {
       if (certCount >= MAX_SIGNATURES) {
         print_usage();
         return -1;
       }
       certNames[certCount++] = argv[2];
       if (strlen(argv[1]) > 2 &&
-        !strcmp(argv[2], "-X") &&
-        argv[1][2] >= '0' && argv[1][2] <= '9') {
+          (!strcmp(argv[2], "-X") || !strcmp(argv[2], "-I")) &&
+          argv[1][2] >= '0' && argv[1][2] <= '9') {
         sigIndex = argv[1][2] - '0';
         argv++;
         argc--;
       } else {
         argv += 2;
         argc -= 2;
       }
     /* MAR channel ID */
@@ -260,16 +266,33 @@ int main(int argc, char **argv) {
     }
     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]);
 
+  /* Import a MAR signature */
+  case 'I':
+    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;
+    }
+    if (argc < 5) {
+      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) {
       print_usage();
       return -1;
     }