import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

6
modules/libmar/README Normal file
View file

@ -0,0 +1,6 @@
This directory contains code for a simple archive file format, which
is documented at http://wiki.mozilla.org/Software_Update:MAR
The src directory builds a small static library used to create, read, and
extract an archive file. The tool directory builds a command line utility
around the library.

17
modules/libmar/moz.build Normal file
View file

@ -0,0 +1,17 @@
# -*- 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/.
DIRS += ['src']
if CONFIG['MOZ_ENABLE_SIGNMAR']:
DIRS += ['sign', 'verify']
TEST_DIRS += ['tests']
elif CONFIG['MOZ_VERIFY_MAR_SIGNATURE']:
DIRS += ['verify']
# If we are building ./sign and ./verify then ./tool must come after it
DIRS += ['tool']

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,24 @@
# -*- 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('signmar')
UNIFIED_SOURCES += [
'mar_sign.c',
'nss_secutil.c',
]
FORCE_STATIC_LIB = True
LOCAL_INCLUDES += [
'../src',
'../verify',
]
DEFINES['MAR_NSS'] = True
if CONFIG['OS_ARCH'] == 'WINNT':
USE_STATIC_LIBS = True

View file

@ -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/. */
/* With the exception of GetPasswordString, this file was
copied from NSS's cmd/lib/secutil.c hg revision 8f011395145e */
#include "nss_secutil.h"
#include "prprf.h"
#ifdef XP_WIN
#include <io.h>
#else
#include <unistd.h>
#endif
static char consoleName[] = {
#ifdef XP_UNIX
"/dev/tty"
#else
"CON:"
#endif
};
#if defined(_WINDOWS)
static char * quiet_fgets (char *buf, int length, FILE *input)
{
int c;
char *end = buf;
/* fflush (input); */
memset (buf, 0, length);
if (!isatty(fileno(input))) {
return fgets(buf,length,input);
}
while (1)
{
#if defined (_WIN32_WCE)
c = getchar(); /* gets a character from stdin */
#else
c = getch(); /* getch gets a character from the console */
#endif
if (c == '\b')
{
if (end > buf)
end--;
}
else if (--length > 0)
*end++ = c;
if (!c || c == '\n' || c == '\r')
break;
}
return buf;
}
#endif
char *
GetPasswordString(void *arg, char *prompt)
{
FILE *input = stdin;
char phrase[200] = {'\0'};
int isInputTerminal = isatty(fileno(stdin));
#ifndef _WINDOWS
if (isInputTerminal) {
input = fopen(consoleName, "r");
if (input == NULL) {
fprintf(stderr, "Error opening input terminal for read\n");
return NULL;
}
}
#endif
if (isInputTerminal) {
fprintf(stdout, "Please enter your password:\n");
fflush(stdout);
}
if (!QUIET_FGETS(phrase, sizeof(phrase), input)) {
fprintf(stderr, "QUIET_FGETS failed\n");
return NULL;
}
if (isInputTerminal) {
fprintf(stdout, "\n");
}
#ifndef _WINDOWS
if (isInputTerminal) {
fclose(input);
}
#endif
/* Strip off the newlines if present */
if (phrase[PORT_Strlen(phrase)-1] == '\n' ||
phrase[PORT_Strlen(phrase)-1] == '\r') {
phrase[PORT_Strlen(phrase)-1] = 0;
}
return (char*) PORT_Strdup(phrase);
}
char *
SECU_FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg)
{
char* phrases, *phrase;
PRFileDesc *fd;
int32_t nb;
char *pwFile = arg;
int i;
const long maxPwdFileSize = 4096;
char* tokenName = NULL;
int tokenLen = 0;
if (!pwFile)
return 0;
if (retry) {
return 0; /* no good retrying - the files contents will be the same */
}
phrases = PORT_ZAlloc(maxPwdFileSize);
if (!phrases) {
return 0; /* out of memory */
}
fd = PR_Open(pwFile, PR_RDONLY, 0);
if (!fd) {
fprintf(stderr, "No password file \"%s\" exists.\n", pwFile);
PORT_Free(phrases);
return NULL;
}
nb = PR_Read(fd, phrases, maxPwdFileSize);
PR_Close(fd);
if (nb == 0) {
fprintf(stderr,"password file contains no data\n");
PORT_Free(phrases);
return NULL;
}
if (slot) {
tokenName = PK11_GetTokenName(slot);
if (tokenName) {
tokenLen = PORT_Strlen(tokenName);
}
}
i = 0;
do
{
int startphrase = i;
int phraseLen;
/* handle the Windows EOL case */
while (phrases[i] != '\r' && phrases[i] != '\n' && i < nb) i++;
/* terminate passphrase */
phrases[i++] = '\0';
/* clean up any EOL before the start of the next passphrase */
while ( (i<nb) && (phrases[i] == '\r' || phrases[i] == '\n')) {
phrases[i++] = '\0';
}
/* now analyze the current passphrase */
phrase = &phrases[startphrase];
if (!tokenName)
break;
if (PORT_Strncmp(phrase, tokenName, tokenLen)) continue;
phraseLen = PORT_Strlen(phrase);
if (phraseLen < (tokenLen+1)) continue;
if (phrase[tokenLen] != ':') continue;
phrase = &phrase[tokenLen+1];
break;
} while (i<nb);
phrase = PORT_Strdup((char*)phrase);
PORT_Free(phrases);
return phrase;
}
char *
SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg)
{
char prompt[255];
secuPWData *pwdata = (secuPWData *)arg;
secuPWData pwnull = { PW_NONE, 0 };
secuPWData pwxtrn = { PW_EXTERNAL, "external" };
char *pw;
if (pwdata == NULL)
pwdata = &pwnull;
if (PK11_ProtectedAuthenticationPath(slot)) {
pwdata = &pwxtrn;
}
if (retry && pwdata->source != PW_NONE) {
PR_fprintf(PR_STDERR, "Incorrect password/PIN entered.\n");
return NULL;
}
switch (pwdata->source) {
case PW_NONE:
sprintf(prompt, "Enter Password or Pin for \"%s\":",
PK11_GetTokenName(slot));
return GetPasswordString(NULL, prompt);
case PW_FROMFILE:
/* Instead of opening and closing the file every time, get the pw
* once, then keep it in memory (duh).
*/
pw = SECU_FilePasswd(slot, retry, pwdata->data);
pwdata->source = PW_PLAINTEXT;
pwdata->data = PL_strdup(pw);
/* it's already been dup'ed */
return pw;
case PW_EXTERNAL:
sprintf(prompt,
"Press Enter, then enter PIN for \"%s\" on external device.\n",
PK11_GetTokenName(slot));
pw = GetPasswordString(NULL, prompt);
if (pw) {
memset(pw, 0, PORT_Strlen(pw));
PORT_Free(pw);
}
/* Fall Through */
case PW_PLAINTEXT:
return PL_strdup(pwdata->data);
default:
break;
}
PR_fprintf(PR_STDERR, "Password check failed: No password found.\n");
return NULL;
}

View file

@ -0,0 +1,41 @@
/* 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/. */
/* With the exception of GetPasswordString, this file was
copied from NSS's cmd/lib/secutil.h hg revision 8f011395145e */
#ifndef NSS_SECUTIL_H_
#define NSS_SECUTIL_H_
#include "nss.h"
#include "pk11pub.h"
#include "cryptohi.h"
#include "hasht.h"
#include "cert.h"
#include "key.h"
#include <stdint.h>
typedef struct {
enum {
PW_NONE = 0,
PW_FROMFILE = 1,
PW_PLAINTEXT = 2,
PW_EXTERNAL = 3
} source;
char *data;
} secuPWData;
#if( defined(_WINDOWS) && !defined(_WIN32_WCE))
#include <conio.h>
#include <io.h>
#define QUIET_FGETS quiet_fgets
static char * quiet_fgets (char *buf, int length, FILE *input);
#else
#define QUIET_FGETS fgets
#endif
char *
SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg);
#endif

198
modules/libmar/src/mar.h Normal file
View file

@ -0,0 +1,198 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 MAR_H__
#define MAR_H__
#include "mozilla/Assertions.h"
#include <stdint.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.
* If you want to increase this value above 9 then you need to adjust parsing
* code in tool/mar.c.
*/
#define MAX_SIGNATURES 8
#ifdef __cplusplus
static_assert(MAX_SIGNATURES <= 9, "too many signatures");
#else
MOZ_STATIC_ASSERT(MAX_SIGNATURES <= 9, "too many signatures");
#endif
struct ProductInformationBlock {
const char *MARChannelID;
const char *productVersion;
};
/**
* The MAR item data structure.
*/
typedef struct MarItem_ {
struct MarItem_ *next; /* private field */
uint32_t offset; /* offset into archive */
uint32_t length; /* length of data in bytes */
uint32_t flags; /* contains file mode bits */
char name[1]; /* file path */
} MarItem;
#define TABLESIZE 256
struct MarFile_ {
FILE *fp;
MarItem *item_table[TABLESIZE];
};
typedef struct MarFile_ MarFile;
/**
* Signature of callback function passed to mar_enum_items.
* @param mar The MAR file being visited.
* @param item The MAR item being visited.
* @param data The data parameter passed by the caller of mar_enum_items.
* @return A non-zero value to stop enumerating.
*/
typedef int (* MarItemCallback)(MarFile *mar, const MarItem *item, void *data);
/**
* Open a MAR file for reading.
* @param path Specifies the path to the MAR file to open. This path must
* be compatible with fopen.
* @return NULL if an error occurs.
*/
MarFile *mar_open(const char *path);
#ifdef XP_WIN
MarFile *mar_wopen(const wchar_t *path);
#endif
/**
* Close a MAR file that was opened using mar_open.
* @param mar The MarFile object to close.
*/
void mar_close(MarFile *mar);
/**
* Find an item in the MAR file by name.
* @param mar The MarFile object to query.
* @param item The name of the item to query.
* @return A const reference to a MAR item or NULL if not found.
*/
const MarItem *mar_find_item(MarFile *mar, const char *item);
/**
* Enumerate all MAR items via callback function.
* @param mar The MAR file to enumerate.
* @param callback The function to call for each MAR item.
* @param data A caller specified value that is passed along to the
* callback function.
* @return 0 if the enumeration ran to completion. Otherwise, any
* non-zero return value from the callback is returned.
*/
int mar_enum_items(MarFile *mar, MarItemCallback callback, void *data);
/**
* Read from MAR item at given offset up to bufsize bytes.
* @param mar The MAR file to read.
* @param item The MAR item to read.
* @param offset The byte offset relative to the start of the item.
* @param buf A pointer to a buffer to copy the data into.
* @param bufsize The length of the buffer to copy the data into.
* @return The number of bytes written or a negative value if an
* error occurs.
*/
int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
int bufsize);
/**
* Create a MAR file from a set of files.
* @param dest The path to the file to create. This path must be
* compatible with fopen.
* @param numfiles The number of files to store in the archive.
* @param files The list of null-terminated file paths. Each file
* path must be compatible with fopen.
* @param infoBlock The information to store in the product information block.
* @return A non-zero value if an error occurs.
*/
int mar_create(const char *dest,
int numfiles,
char **files,
struct ProductInformationBlock *infoBlock);
/**
* Extract a MAR file to the current working directory.
* @param path The path to the MAR file to extract. This path must be
* compatible with fopen.
* @return A non-zero value if an error occurs.
*/
int mar_extract(const char *path);
#define MAR_MAX_CERT_SIZE (16*1024) // Way larger than necessary
/* Read the entire file (not a MAR file) into a newly-allocated buffer.
* This function does not write to stderr. Instead, the caller should
* write whatever error messages it sees fit. The caller must free the returned
* buffer using free().
*
* @param filePath The path to the file that should be read.
* @param maxSize The maximum valid file size.
* @param data On success, *data will point to a newly-allocated buffer
* with the file's contents in it.
* @param size On success, *size will be the size of the created buffer.
*
* @return 0 on success, -1 on error
*/
int mar_read_entire_file(const char * filePath,
uint32_t maxSize,
/*out*/ const uint8_t * *data,
/*out*/ uint32_t *size);
/**
* 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.
* We do not check that the certificate was issued by any trusted authority.
* We assume it to be self-signed. We do not check whether the certificate
* is valid for this usage.
*
* @param mar The already opened MAR file.
* @param certData Pointer to the first element in an array of certificate
* file data.
* @param certDataSizes Pointer to the first element in an array for size of
* the cert data.
* @param certCount The number of elements in certData and certDataSizes
* @return 0 on success
* a negative number if there was an error
* a positive number if the signature does not verify
*/
int mar_verify_signatures(MarFile *mar,
const uint8_t * const *certData,
const uint32_t *certDataSizes,
uint32_t certCount);
/**
* Reads the product info block from the MAR file's additional block section.
* The caller is responsible for freeing the fields in infoBlock
* if the return is successful.
*
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
mar_read_product_info_block(MarFile *mar,
struct ProductInformationBlock *infoBlock);
#ifdef __cplusplus
}
#endif
#endif /* MAR_H__ */

View file

@ -0,0 +1,110 @@
/* 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 MAR_CMDLINE_H__
#define MAR_CMDLINE_H__
/* We use NSPR here just to import the definition of uint32_t */
#ifdef __cplusplus
extern "C" {
#endif
struct ProductInformationBlock;
/**
* Determines MAR file information.
*
* @param path The path of the MAR file to check.
* @param hasSignatureBlock Optional out parameter specifying if the MAR
* file has a signature block or not.
* @param numSignatures Optional out parameter for storing the number
* of signatures in the MAR file.
* @param hasAdditionalBlocks Optional out parameter specifying if the MAR
* file has additional blocks or not.
* @param offsetAdditionalBlocks Optional out parameter for the offset to the
* first additional block. Value is only valid if
* hasAdditionalBlocks is not equal to 0.
* @param numAdditionalBlocks Optional out parameter for the number of
* additional blocks. Value is only valid if
* has_additional_blocks is not equal to 0.
* @return 0 on success and non-zero on failure.
*/
int get_mar_file_info(const char *path,
int *hasSignatureBlock,
uint32_t *numSignatures,
int *hasAdditionalBlocks,
uint32_t *offsetAdditionalBlocks,
uint32_t *numAdditionalBlocks);
/**
* Reads the product info block from the MAR file's additional block section.
* The caller is responsible for freeing the fields in infoBlock
* if the return is successful.
*
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
read_product_info_block(char *path,
struct ProductInformationBlock *infoBlock);
/**
* Refreshes the product information block with the new information.
* The input MAR must not be signed or the function call will fail.
*
* @param path The path to the MAR file whose product info block
* should be refreshed.
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
refresh_product_info_block(const char *path,
struct ProductInformationBlock *infoBlock);
/**
* Writes out a copy of the MAR at src but with the signature block stripped.
*
* @param src The path of the source MAR file
* @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);
/**
* 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__ */

View file

@ -0,0 +1,399 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "mar_private.h"
#include "mar_cmdline.h"
#include "mar.h"
#ifdef XP_WIN
#include <winsock2.h>
#else
#include <netinet/in.h>
#include <unistd.h>
#endif
struct MarItemStack {
void *head;
uint32_t size_used;
uint32_t size_allocated;
uint32_t last_offset;
};
/**
* Push a new item onto the stack of items. The stack is a single block
* of memory.
*/
static int mar_push(struct MarItemStack *stack, uint32_t length, uint32_t flags,
const char *name) {
int namelen;
uint32_t n_offset, n_length, n_flags;
uint32_t size;
char *data;
namelen = strlen(name);
size = MAR_ITEM_SIZE(namelen);
if (stack->size_allocated - stack->size_used < size) {
/* increase size of stack */
size_t size_needed = ROUND_UP(stack->size_used + size, BLOCKSIZE);
stack->head = realloc(stack->head, size_needed);
if (!stack->head)
return -1;
stack->size_allocated = size_needed;
}
data = (((char *) stack->head) + stack->size_used);
n_offset = htonl(stack->last_offset);
n_length = htonl(length);
n_flags = htonl(flags);
memcpy(data, &n_offset, sizeof(n_offset));
data += sizeof(n_offset);
memcpy(data, &n_length, sizeof(n_length));
data += sizeof(n_length);
memcpy(data, &n_flags, sizeof(n_flags));
data += sizeof(n_flags);
memcpy(data, name, namelen + 1);
stack->size_used += size;
stack->last_offset += length;
return 0;
}
static int mar_concat_file(FILE *fp, const char *path) {
FILE *in;
char buf[BLOCKSIZE];
size_t len;
int rv = 0;
in = fopen(path, "rb");
if (!in) {
fprintf(stderr, "ERROR: could not open file in mar_concat_file()\n");
perror(path);
return -1;
}
while ((len = fread(buf, 1, BLOCKSIZE, in)) > 0) {
if (fwrite(buf, len, 1, fp) != 1) {
rv = -1;
break;
}
}
fclose(in);
return rv;
}
/**
* Writes out the product information block to the specified file.
*
* @param fp The opened MAR file being created.
* @param stack A pointer to the MAR item stack being used to create
* the MAR
* @param infoBlock The product info block to store in the file.
* @return 0 on success.
*/
static int
mar_concat_product_info_block(FILE *fp,
struct MarItemStack *stack,
struct ProductInformationBlock *infoBlock)
{
char buf[PIB_MAX_MAR_CHANNEL_ID_SIZE + PIB_MAX_PRODUCT_VERSION_SIZE];
uint32_t additionalBlockID = 1, infoBlockSize, unused;
if (!fp || !infoBlock ||
!infoBlock->MARChannelID ||
!infoBlock->productVersion) {
return -1;
}
/* The MAR channel name must be < 64 bytes per the spec */
if (strlen(infoBlock->MARChannelID) > PIB_MAX_MAR_CHANNEL_ID_SIZE) {
return -1;
}
/* The product version must be < 32 bytes per the spec */
if (strlen(infoBlock->productVersion) > PIB_MAX_PRODUCT_VERSION_SIZE) {
return -1;
}
/* Although we don't need the product information block size to include the
maximum MAR channel name and product version, we allocate the maximum
amount to make it easier to modify the MAR file for repurposing MAR files
to different MAR channels. + 2 is for the NULL terminators. */
infoBlockSize = sizeof(infoBlockSize) +
sizeof(additionalBlockID) +
PIB_MAX_MAR_CHANNEL_ID_SIZE +
PIB_MAX_PRODUCT_VERSION_SIZE + 2;
if (stack) {
stack->last_offset += infoBlockSize;
}
/* Write out the product info block size */
infoBlockSize = htonl(infoBlockSize);
if (fwrite(&infoBlockSize,
sizeof(infoBlockSize), 1, fp) != 1) {
return -1;
}
infoBlockSize = ntohl(infoBlockSize);
/* Write out the product info block ID */
additionalBlockID = htonl(additionalBlockID);
if (fwrite(&additionalBlockID,
sizeof(additionalBlockID), 1, fp) != 1) {
return -1;
}
additionalBlockID = ntohl(additionalBlockID);
/* Write out the channel name and NULL terminator */
if (fwrite(infoBlock->MARChannelID,
strlen(infoBlock->MARChannelID) + 1, 1, fp) != 1) {
return -1;
}
/* Write out the product version string and NULL terminator */
if (fwrite(infoBlock->productVersion,
strlen(infoBlock->productVersion) + 1, 1, fp) != 1) {
return -1;
}
/* Write out the rest of the block that is unused */
unused = infoBlockSize - (sizeof(infoBlockSize) +
sizeof(additionalBlockID) +
strlen(infoBlock->MARChannelID) +
strlen(infoBlock->productVersion) + 2);
memset(buf, 0, sizeof(buf));
if (fwrite(buf, unused, 1, fp) != 1) {
return -1;
}
return 0;
}
/**
* Refreshes the product information block with the new information.
* The input MAR must not be signed or the function call will fail.
*
* @param path The path to the MAR file whose product info block
* should be refreshed.
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
refresh_product_info_block(const char *path,
struct ProductInformationBlock *infoBlock)
{
FILE *fp ;
int rv;
uint32_t numSignatures, additionalBlockSize, additionalBlockID,
offsetAdditionalBlocks, numAdditionalBlocks, i;
int additionalBlocks, hasSignatureBlock;
int64_t oldPos;
rv = get_mar_file_info(path,
&hasSignatureBlock,
&numSignatures,
&additionalBlocks,
&offsetAdditionalBlocks,
&numAdditionalBlocks);
if (rv) {
fprintf(stderr, "ERROR: Could not obtain MAR information.\n");
return -1;
}
if (hasSignatureBlock && numSignatures) {
fprintf(stderr, "ERROR: Cannot refresh a signed MAR\n");
return -1;
}
fp = fopen(path, "r+b");
if (!fp) {
fprintf(stderr, "ERROR: could not open target file: %s\n", path);
return -1;
}
if (fseeko(fp, offsetAdditionalBlocks, SEEK_SET)) {
fprintf(stderr, "ERROR: could not seek to additional blocks\n");
fclose(fp);
return -1;
}
for (i = 0; i < numAdditionalBlocks; ++i) {
/* Get the position of the start of this block */
oldPos = ftello(fp);
/* Read the additional block size */
if (fread(&additionalBlockSize,
sizeof(additionalBlockSize),
1, fp) != 1) {
fclose(fp);
return -1;
}
additionalBlockSize = ntohl(additionalBlockSize);
/* Read the additional block ID */
if (fread(&additionalBlockID,
sizeof(additionalBlockID),
1, fp) != 1) {
fclose(fp);
return -1;
}
additionalBlockID = ntohl(additionalBlockID);
if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
if (fseeko(fp, oldPos, SEEK_SET)) {
fprintf(stderr, "Could not seek back to Product Information Block\n");
fclose(fp);
return -1;
}
if (mar_concat_product_info_block(fp, NULL, infoBlock)) {
fprintf(stderr, "Could not concat Product Information Block\n");
fclose(fp);
return -1;
}
fclose(fp);
return 0;
} else {
/* This is not the additional block you're looking for. Move along. */
if (fseek(fp, additionalBlockSize, SEEK_CUR)) {
fprintf(stderr, "ERROR: Could not seek past current block.\n");
fclose(fp);
return -1;
}
}
}
/* If we had a product info block we would have already returned */
fclose(fp);
fprintf(stderr, "ERROR: Could not refresh because block does not exist\n");
return -1;
}
/**
* Create a MAR file from a set of files.
* @param dest The path to the file to create. This path must be
* compatible with fopen.
* @param numfiles The number of files to store in the archive.
* @param files The list of null-terminated file paths. Each file
* path must be compatible with fopen.
* @param infoBlock The information to store in the product information block.
* @return A non-zero value if an error occurs.
*/
int mar_create(const char *dest, int
num_files, char **files,
struct ProductInformationBlock *infoBlock) {
struct MarItemStack stack;
uint32_t offset_to_index = 0, size_of_index,
numSignatures, numAdditionalSections;
uint64_t sizeOfEntireMAR = 0;
struct stat st;
FILE *fp;
int i, rv = -1;
memset(&stack, 0, sizeof(stack));
fp = fopen(dest, "wb");
if (!fp) {
fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
return -1;
}
if (fwrite(MAR_ID, MAR_ID_SIZE, 1, fp) != 1)
goto failure;
if (fwrite(&offset_to_index, sizeof(uint32_t), 1, fp) != 1)
goto failure;
stack.last_offset = MAR_ID_SIZE +
sizeof(offset_to_index) +
sizeof(numSignatures) +
sizeof(numAdditionalSections) +
sizeof(sizeOfEntireMAR);
/* We will circle back on this at the end of the MAR creation to fill it */
if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) {
goto failure;
}
/* Write out the number of signatures, for now only at most 1 is supported */
numSignatures = 0;
if (fwrite(&numSignatures, sizeof(numSignatures), 1, fp) != 1) {
goto failure;
}
/* Write out the number of additional sections, for now just 1
for the product info block */
numAdditionalSections = htonl(1);
if (fwrite(&numAdditionalSections,
sizeof(numAdditionalSections), 1, fp) != 1) {
goto failure;
}
numAdditionalSections = ntohl(numAdditionalSections);
if (mar_concat_product_info_block(fp, &stack, infoBlock)) {
goto failure;
}
for (i = 0; i < num_files; ++i) {
if (stat(files[i], &st)) {
fprintf(stderr, "ERROR: file not found: %s\n", files[i]);
goto failure;
}
if (mar_push(&stack, st.st_size, st.st_mode & 0777, files[i]))
goto failure;
/* concatenate input file to archive */
if (mar_concat_file(fp, files[i]))
goto failure;
}
/* write out the index (prefixed with length of index) */
size_of_index = htonl(stack.size_used);
if (fwrite(&size_of_index, sizeof(size_of_index), 1, fp) != 1)
goto failure;
if (fwrite(stack.head, stack.size_used, 1, fp) != 1)
goto failure;
/* 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 (ftell(fp) > MAX_SIZE_OF_MAR_FILE) {
goto failure;
}
/* write out offset to index file in network byte order */
offset_to_index = htonl(stack.last_offset);
if (fseek(fp, MAR_ID_SIZE, SEEK_SET))
goto failure;
if (fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1)
goto failure;
offset_to_index = ntohl(stack.last_offset);
sizeOfEntireMAR = ((uint64_t)stack.last_offset) +
stack.size_used +
sizeof(size_of_index);
sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1)
goto failure;
sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
rv = 0;
failure:
if (stack.head)
free(stack.head);
fclose(fp);
if (rv)
remove(dest);
return rv;
}

View file

@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include "mar_private.h"
#include "mar.h"
#ifdef XP_WIN
#include <io.h>
#include <direct.h>
#endif
/* Ensure that the directory containing this file exists */
static int mar_ensure_parent_dir(const char *path)
{
char *slash = strrchr(path, '/');
if (slash)
{
*slash = '\0';
mar_ensure_parent_dir(path);
#ifdef XP_WIN
_mkdir(path);
#else
mkdir(path, 0755);
#endif
*slash = '/';
}
return 0;
}
static int mar_test_callback(MarFile *mar, const MarItem *item, void *unused) {
FILE *fp;
char buf[BLOCKSIZE];
int fd, len, offset = 0;
if (mar_ensure_parent_dir(item->name))
return -1;
#ifdef XP_WIN
fd = _open(item->name, _O_BINARY|_O_CREAT|_O_TRUNC|_O_WRONLY, item->flags);
#else
fd = creat(item->name, item->flags);
#endif
if (fd == -1) {
fprintf(stderr, "ERROR: could not create file in mar_test_callback()\n");
perror(item->name);
return -1;
}
fp = fdopen(fd, "wb");
if (!fp)
return -1;
while ((len = mar_read(mar, item, offset, buf, sizeof(buf))) > 0) {
if (fwrite(buf, len, 1, fp) != 1)
break;
offset += len;
}
fclose(fp);
return len == 0 ? 0 : -1;
}
int mar_extract(const char *path) {
MarFile *mar;
int rv;
mar = mar_open(path);
if (!mar)
return -1;
rv = mar_enum_items(mar, mar_test_callback, NULL);
mar_close(mar);
return rv;
}

View file

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 MAR_PRIVATE_H__
#define MAR_PRIVATE_H__
#include "limits.h"
#include "mozilla/Assertions.h"
#include <stdint.h>
#define BLOCKSIZE 4096
#define ROUND_UP(n, incr) (((n) / (incr) + 1) * (incr))
#define MAR_ID "MAR1"
#define MAR_ID_SIZE 4
/* The signature block comes directly after the header block
which is 16 bytes */
#define SIGNATURE_BLOCK_OFFSET 16
/* Make sure the file is less than 500MB. We do this to protect against
invalid MAR files. */
#define MAX_SIZE_OF_MAR_FILE ((int64_t)524288000)
/* Existing code makes assumptions that the file size is
smaller than LONG_MAX. */
MOZ_STATIC_ASSERT(MAX_SIZE_OF_MAR_FILE < ((int64_t)LONG_MAX),
"max mar file size is too big");
/* We store at most the size up to the signature block + 4
bytes per BLOCKSIZE bytes */
MOZ_STATIC_ASSERT(sizeof(BLOCKSIZE) < \
(SIGNATURE_BLOCK_OFFSET + sizeof(uint32_t)),
"BLOCKSIZE is too big");
/* The maximum size of any signature supported by current and future
implementations of the signmar program. */
#define MAX_SIGNATURE_LENGTH 2048
/* Each additional block has a unique ID.
The product information block has an ID of 1. */
#define PRODUCT_INFO_BLOCK_ID 1
#define MAR_ITEM_SIZE(namelen) (3*sizeof(uint32_t) + (namelen) + 1)
/* Product Information Block (PIB) constants */
#define PIB_MAX_MAR_CHANNEL_ID_SIZE 63
#define PIB_MAX_PRODUCT_VERSION_SIZE 31
/* The mar program is compiled as a host bin so we don't have access to NSPR at
runtime. For that reason we use ntohl, htonl, and define HOST_TO_NETWORK64
instead of the NSPR equivalents. */
#ifdef XP_WIN
#include <winsock2.h>
#define ftello _ftelli64
#define fseeko _fseeki64
#else
#define _FILE_OFFSET_BITS 64
#include <netinet/in.h>
#include <unistd.h>
#endif
#include <stdio.h>
#define HOST_TO_NETWORK64(x) ( \
((((uint64_t) x) & 0xFF) << 56) | \
((((uint64_t) x) >> 8) & 0xFF) << 48) | \
(((((uint64_t) x) >> 16) & 0xFF) << 40) | \
(((((uint64_t) x) >> 24) & 0xFF) << 32) | \
(((((uint64_t) x) >> 32) & 0xFF) << 24) | \
(((((uint64_t) x) >> 40) & 0xFF) << 16) | \
(((((uint64_t) x) >> 48) & 0xFF) << 8) | \
(((uint64_t) x) >> 56)
#define NETWORK_TO_HOST64 HOST_TO_NETWORK64
#endif /* MAR_PRIVATE_H__ */

View file

@ -0,0 +1,577 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "mar_private.h"
#include "mar.h"
#ifdef XP_WIN
#include <winsock2.h>
#else
#include <netinet/in.h>
#endif
/* this is the same hash algorithm used by nsZipArchive.cpp */
static uint32_t mar_hash_name(const char *name) {
uint32_t val = 0;
unsigned char* c;
for (c = (unsigned char *) name; *c; ++c)
val = val*37 + *c;
return val % TABLESIZE;
}
static int mar_insert_item(MarFile *mar, const char *name, int namelen,
uint32_t offset, uint32_t length, uint32_t flags) {
MarItem *item, *root;
uint32_t hash;
item = (MarItem *) malloc(sizeof(MarItem) + namelen);
if (!item)
return -1;
item->next = NULL;
item->offset = offset;
item->length = length;
item->flags = flags;
memcpy(item->name, name, namelen + 1);
hash = mar_hash_name(name);
root = mar->item_table[hash];
if (!root) {
mar->item_table[hash] = item;
} else {
/* append item */
while (root->next)
root = root->next;
root->next = item;
}
return 0;
}
static int mar_consume_index(MarFile *mar, char **buf, const char *buf_end) {
/*
* Each item has the following structure:
* uint32_t offset (network byte order)
* uint32_t length (network byte order)
* uint32_t flags (network byte order)
* char name[N] (where N >= 1)
* char null_byte;
*/
uint32_t offset;
uint32_t length;
uint32_t flags;
const char *name;
int namelen;
if ((buf_end - *buf) < (int)(3*sizeof(uint32_t) + 2))
return -1;
memcpy(&offset, *buf, sizeof(offset));
*buf += sizeof(offset);
memcpy(&length, *buf, sizeof(length));
*buf += sizeof(length);
memcpy(&flags, *buf, sizeof(flags));
*buf += sizeof(flags);
offset = ntohl(offset);
length = ntohl(length);
flags = ntohl(flags);
name = *buf;
/* find namelen; must take care not to read beyond buf_end */
while (**buf) {
/* buf_end points one byte past the end of buf's allocation */
if (*buf == (buf_end - 1))
return -1;
++(*buf);
}
namelen = (*buf - name);
/* must ensure that namelen is valid */
if (namelen < 0) {
return -1;
}
/* consume null byte */
if (*buf == buf_end)
return -1;
++(*buf);
return mar_insert_item(mar, name, namelen, offset, length, flags);
}
static int mar_read_index(MarFile *mar) {
char id[MAR_ID_SIZE], *buf, *bufptr, *bufend;
uint32_t offset_to_index, size_of_index;
/* verify MAR ID */
if (fread(id, MAR_ID_SIZE, 1, mar->fp) != 1)
return -1;
if (memcmp(id, MAR_ID, MAR_ID_SIZE) != 0)
return -1;
if (fread(&offset_to_index, sizeof(uint32_t), 1, mar->fp) != 1)
return -1;
offset_to_index = ntohl(offset_to_index);
if (fseek(mar->fp, offset_to_index, SEEK_SET))
return -1;
if (fread(&size_of_index, sizeof(uint32_t), 1, mar->fp) != 1)
return -1;
size_of_index = ntohl(size_of_index);
buf = (char *) malloc(size_of_index);
if (!buf)
return -1;
if (fread(buf, size_of_index, 1, mar->fp) != 1) {
free(buf);
return -1;
}
bufptr = buf;
bufend = buf + size_of_index;
while (bufptr < bufend && mar_consume_index(mar, &bufptr, bufend) == 0);
free(buf);
return (bufptr == bufend) ? 0 : -1;
}
/**
* Internal shared code for mar_open and mar_wopen.
* On failure, will fclose(fp).
*/
static MarFile *mar_fpopen(FILE *fp)
{
MarFile *mar;
mar = (MarFile *) malloc(sizeof(*mar));
if (!mar) {
fclose(fp);
return NULL;
}
mar->fp = fp;
memset(mar->item_table, 0, sizeof(mar->item_table));
if (mar_read_index(mar)) {
mar_close(mar);
return NULL;
}
return mar;
}
MarFile *mar_open(const char *path) {
FILE *fp;
fp = fopen(path, "rb");
if (!fp) {
fprintf(stderr, "ERROR: could not open file in mar_open()\n");
perror(path);
return NULL;
}
return mar_fpopen(fp);
}
#ifdef XP_WIN
MarFile *mar_wopen(const wchar_t *path) {
FILE *fp;
_wfopen_s(&fp, path, L"rb");
if (!fp) {
fprintf(stderr, "ERROR: could not open file in mar_wopen()\n");
_wperror(path);
return NULL;
}
return mar_fpopen(fp);
}
#endif
void mar_close(MarFile *mar) {
MarItem *item;
int i;
fclose(mar->fp);
for (i = 0; i < TABLESIZE; ++i) {
item = mar->item_table[i];
while (item) {
MarItem *temp = item;
item = item->next;
free(temp);
}
}
free(mar);
}
/**
* Determines the MAR file information.
*
* @param fp An opened MAR file in read mode.
* @param hasSignatureBlock Optional out parameter specifying if the MAR
* file has a signature block or not.
* @param numSignatures Optional out parameter for storing the number
* of signatures in the MAR file.
* @param hasAdditionalBlocks Optional out parameter specifying if the MAR
* file has additional blocks or not.
* @param offsetAdditionalBlocks Optional out parameter for the offset to the
* first additional block. Value is only valid if
* hasAdditionalBlocks is not equal to 0.
* @param numAdditionalBlocks Optional out parameter for the number of
* additional blocks. Value is only valid if
* hasAdditionalBlocks is not equal to 0.
* @return 0 on success and non-zero on failure.
*/
int get_mar_file_info_fp(FILE *fp,
int *hasSignatureBlock,
uint32_t *numSignatures,
int *hasAdditionalBlocks,
uint32_t *offsetAdditionalBlocks,
uint32_t *numAdditionalBlocks)
{
uint32_t offsetToIndex, offsetToContent, signatureCount, signatureLen, i;
/* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
if (!hasSignatureBlock && !hasAdditionalBlocks) {
return -1;
}
/* Skip to the start of the offset index */
if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) {
return -1;
}
/* Read the offset to the index. */
if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fp) != 1) {
return -1;
}
offsetToIndex = ntohl(offsetToIndex);
if (numSignatures) {
/* Skip past the MAR file size field */
if (fseek(fp, sizeof(uint64_t), SEEK_CUR)) {
return -1;
}
/* Read the number of signatures field */
if (fread(numSignatures, sizeof(*numSignatures), 1, fp) != 1) {
return -1;
}
*numSignatures = ntohl(*numSignatures);
}
/* Skip to the first index entry past the index size field
We do it in 2 calls because offsetToIndex + sizeof(uint32_t)
could oerflow in theory. */
if (fseek(fp, offsetToIndex, SEEK_SET)) {
return -1;
}
if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) {
return -1;
}
/* Read the first offset to content field. */
if (fread(&offsetToContent, sizeof(offsetToContent), 1, fp) != 1) {
return -1;
}
offsetToContent = ntohl(offsetToContent);
/* Check if we have a new or old MAR file */
if (hasSignatureBlock) {
if (offsetToContent == MAR_ID_SIZE + sizeof(uint32_t)) {
*hasSignatureBlock = 0;
} else {
*hasSignatureBlock = 1;
}
}
/* If the caller doesn't care about the product info block
value, then just return */
if (!hasAdditionalBlocks) {
return 0;
}
/* Skip to the start of the signature block */
if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
return -1;
}
/* Get the number of signatures */
if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
return -1;
}
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) {
return -1;
}
/* Skip past the whole signature block */
for (i = 0; i < signatureCount; i++) {
/* Skip past the signature algorithm ID */
if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) {
return -1;
}
/* Read the signature length and skip past the signature */
if (fread(&signatureLen, sizeof(uint32_t), 1, fp) != 1) {
return -1;
}
signatureLen = ntohl(signatureLen);
if (fseek(fp, signatureLen, SEEK_CUR)) {
return -1;
}
}
if ((int64_t)ftell(fp) == (int64_t)offsetToContent) {
*hasAdditionalBlocks = 0;
} else {
if (numAdditionalBlocks) {
/* We have an additional block, so read in the number of additional blocks
and set the offset. */
*hasAdditionalBlocks = 1;
if (fread(numAdditionalBlocks, sizeof(uint32_t), 1, fp) != 1) {
return -1;
}
*numAdditionalBlocks = ntohl(*numAdditionalBlocks);
if (offsetAdditionalBlocks) {
*offsetAdditionalBlocks = ftell(fp);
}
} else if (offsetAdditionalBlocks) {
/* numAdditionalBlocks is not specified but offsetAdditionalBlocks
is, so fill it! */
*offsetAdditionalBlocks = ftell(fp) + sizeof(uint32_t);
}
}
return 0;
}
/**
* Reads the product info block from the MAR file's additional block section.
* The caller is responsible for freeing the fields in infoBlock
* if the return is successful.
*
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
read_product_info_block(char *path,
struct ProductInformationBlock *infoBlock)
{
int rv;
MarFile mar;
mar.fp = fopen(path, "rb");
if (!mar.fp) {
fprintf(stderr, "ERROR: could not open file in read_product_info_block()\n");
perror(path);
return -1;
}
rv = mar_read_product_info_block(&mar, infoBlock);
fclose(mar.fp);
return rv;
}
/**
* Reads the product info block from the MAR file's additional block section.
* The caller is responsible for freeing the fields in infoBlock
* if the return is successful.
*
* @param infoBlock Out parameter for where to store the result to
* @return 0 on success, -1 on failure
*/
int
mar_read_product_info_block(MarFile *mar,
struct ProductInformationBlock *infoBlock)
{
uint32_t i, offsetAdditionalBlocks, numAdditionalBlocks,
additionalBlockSize, additionalBlockID;
int hasAdditionalBlocks;
/* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and
product version < 32 bytes + 3 NULL terminator bytes. */
char buf[97] = { '\0' };
if (get_mar_file_info_fp(mar->fp, NULL, NULL,
&hasAdditionalBlocks,
&offsetAdditionalBlocks,
&numAdditionalBlocks) != 0) {
return -1;
}
for (i = 0; i < numAdditionalBlocks; ++i) {
/* Read the additional block size */
if (fread(&additionalBlockSize,
sizeof(additionalBlockSize),
1, mar->fp) != 1) {
return -1;
}
additionalBlockSize = ntohl(additionalBlockSize) -
sizeof(additionalBlockSize) -
sizeof(additionalBlockID);
/* Read the additional block ID */
if (fread(&additionalBlockID,
sizeof(additionalBlockID),
1, mar->fp) != 1) {
return -1;
}
additionalBlockID = ntohl(additionalBlockID);
if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
const char *location;
int len;
/* This block must be at most 104 bytes.
MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL
terminator bytes. We only check for 96 though because we remove 8
bytes above from the additionalBlockSize: We subtract
sizeof(additionalBlockSize) and sizeof(additionalBlockID) */
if (additionalBlockSize > 96) {
return -1;
}
if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) {
return -1;
}
/* Extract the MAR channel name from the buffer. For now we
point to the stack allocated buffer but we strdup this
if we are within bounds of each field's max length. */
location = buf;
len = strlen(location);
infoBlock->MARChannelID = location;
location += len + 1;
if (len >= 64) {
infoBlock->MARChannelID = NULL;
return -1;
}
/* Extract the version from the buffer */
len = strlen(location);
infoBlock->productVersion = location;
location += len + 1;
if (len >= 32) {
infoBlock->MARChannelID = NULL;
infoBlock->productVersion = NULL;
return -1;
}
infoBlock->MARChannelID =
strdup(infoBlock->MARChannelID);
infoBlock->productVersion =
strdup(infoBlock->productVersion);
return 0;
} else {
/* This is not the additional block you're looking for. Move along. */
if (fseek(mar->fp, additionalBlockSize, SEEK_CUR)) {
return -1;
}
}
}
/* If we had a product info block we would have already returned */
return -1;
}
const MarItem *mar_find_item(MarFile *mar, const char *name) {
uint32_t hash;
const MarItem *item;
hash = mar_hash_name(name);
item = mar->item_table[hash];
while (item && strcmp(item->name, name) != 0)
item = item->next;
return item;
}
int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) {
MarItem *item;
int i;
for (i = 0; i < TABLESIZE; ++i) {
item = mar->item_table[i];
while (item) {
int rv = callback(mar, item, closure);
if (rv)
return rv;
item = item->next;
}
}
return 0;
}
int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
int bufsize) {
int nr;
if (offset == (int) item->length)
return 0;
if (offset > (int) item->length)
return -1;
nr = item->length - offset;
if (nr > bufsize)
nr = bufsize;
if (fseek(mar->fp, item->offset + offset, SEEK_SET))
return -1;
return fread(buf, 1, nr, mar->fp);
}
/**
* Determines the MAR file information.
*
* @param path The path of the MAR file to check.
* @param hasSignatureBlock Optional out parameter specifying if the MAR
* file has a signature block or not.
* @param numSignatures Optional out parameter for storing the number
* of signatures in the MAR file.
* @param hasAdditionalBlocks Optional out parameter specifying if the MAR
* file has additional blocks or not.
* @param offsetAdditionalBlocks Optional out parameter for the offset to the
* first additional block. Value is only valid if
* hasAdditionalBlocks is not equal to 0.
* @param numAdditionalBlocks Optional out parameter for the number of
* additional blocks. Value is only valid if
* has_additional_blocks is not equal to 0.
* @return 0 on success and non-zero on failure.
*/
int get_mar_file_info(const char *path,
int *hasSignatureBlock,
uint32_t *numSignatures,
int *hasAdditionalBlocks,
uint32_t *offsetAdditionalBlocks,
uint32_t *numAdditionalBlocks)
{
int rv;
FILE *fp = fopen(path, "rb");
if (!fp) {
fprintf(stderr, "ERROR: could not open file in get_mar_file_info()\n");
perror(path);
return -1;
}
rv = get_mar_file_info_fp(fp, hasSignatureBlock,
numSignatures, hasAdditionalBlocks,
offsetAdditionalBlocks, numAdditionalBlocks);
fclose(fp);
return rv;
}

View file

@ -0,0 +1,30 @@
# -*- 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/.
EXPORTS += [
'mar.h',
'mar_cmdline.h',
]
HOST_SOURCES += [
'mar_create.c',
'mar_extract.c',
'mar_read.c',
]
HostLibrary('hostmar')
Library('mar')
UNIFIED_SOURCES += [
'mar_create.c',
'mar_extract.c',
'mar_read.c',
]
FORCE_STATIC_LIB = True
if CONFIG['OS_ARCH'] == 'WINNT':
USE_STATIC_LIBS = True

View file

@ -0,0 +1,12 @@
# -*- 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/.
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
if CONFIG['OS_TARGET'] != 'Android':
TEST_HARNESS_FILES.xpcshell.modules.libmar.tests.unit += [
'!../tool/signmar%s' % CONFIG['BIN_SUFFIX'],
]

Binary file not shown.

View file

@ -0,0 +1 @@
1

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,6 @@
myuujl0MBwyLCrp8I84HrDbGLe+T5yfAR869QWvhcet/CglmuEsQEJczAoK8PtOR
HcqczCRFtxNRKDPOUC0i+CS7RAQG4XJd9uprqHtB28s4SR/9nXKfWDnH2UYq9hkt
X6qTab9M9ySrugOugomDb3ej/qYoNfJN9RnkLP4GP+cl96bWPU33rL0Uu7sUKizu
QoYzxKeZ0r9hGMpOP2l/Jn+pydoEWGVB1mzvIFLPqD9cShUvV80xs8teV0G9IncC
ZRuBwwNkfMTgJDCnxbrw3gIqNXMN1zjssztyJIpT2q8JGs+F6H0wz515xm32dCdQ
b3Oo8a9Dx28NKKq83DJDQA==

View file

@ -0,0 +1,6 @@
xSnuhHyf8iEdPnRgNw9w0Tu6dJimNv+etdZagvbftkGuBlQArhPn7unCOEQ+jS0n
ummJHp1yv64Q7Tte4te8OpRaR6eE333bHfFp++cKKJ2jWFeZ1SwRn59cWX0w4z9u
I4VJmqzOKDUgZq24m6MfGr9iRKbrDjDgvfapzRkZNNU/I0jv20+G+vIUysQGLSN7
fMAxxeurZNbinIiFQyudSpLU2n5PJDh/FIX2lt8H8nX5/yNyznbz0Gm+/hGMZj7+
EfPxLxyOuSRVxI0ebAcRBQJLiyEh1iIluvjuBKohCxkWkEZG3weCz29JxdWOzobj
3/6D+xJW5M1V8aE7EEjt4w==

View file

@ -0,0 +1,6 @@
VQmSlk8q2tmSd+C/d3ADde0lQoJw611sWboc7GOnFTRRsO0D61fIl3tlXLn/N4vN
fCHQxwrszCizC1ddt9Bf5ujBqcAx+ZrN+iT2NlK2j6TN6K1W8LENJgCE7IXN5h1G
VVryo5OkJzWd50DLX/qL9EAg3wx+P3b2BWXkhMuCDGvtAL3C4Ffnm7dw0hjErsEV
X0cB5O5ozM0dOih+GNNX++wxT8E1NqNJOGaJR1KYeY17agz+QlSvFt/fL/a64Fsw
DtOeGZ79nQZ6qkbmUxLXDQ630y3AQ8ceMJFIlI+T3Tk1DUuAWUpMXOICaqzDCdh+
QC5nuQ7OK8Ycbm5fkIFfNQ==

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,6 @@
VTw+yunNUglvAxNevIP1A+1aWNgD479tbZy4t8uDuC9AJ0nLeLXYBfklGxaKCzo4
/UdWFfv1gJmqbnCAlZrZ9UJLUVZOUAwKb+V90bC7pBnGy7xplKBDm40SIs4fwWZl
EGRt8GCPjYKgsYp+jScNMD8B4J3bPSR7m0c1TjXV4pZMhT7LJ+iLfHiy5+WiGBgZ
9qor7plYxfZFgg4moAA3iIXIJbNORUEWfz9b7rsMmiwZO4XmMSDNUutkj9Jl+9gB
XRwrwL0QLvAuYwIzB0HDdl/LPCC+UDEMKigcPhjwFnpN17qUks0fRxId8e4P8m2H
rumgMHGhwx3uagGTTufQSw==

View file

@ -0,0 +1,6 @@
wYPEMA2nfrMfkL5+//r9Of8JicdmG5KxAzYUhSR3d/vr075LhGkc6nQc5orDIRnz
vuMBRIqoAsK3IdtCEbZ6rRKN9d+N7HfvmLdDXGpK3lr9NKKTnk1n/0o7ziRi3Fm8
hOkJpdit7OHV0RH5GBSM8tQziXGN+qe51W2otMA4d8+oa4tp2D7W4SSUxxJwBPiu
5CJAA68qaxzhWd5iVtU8mcjbYOKZAciIPgqBxhgmukqNrShQpnwcop/WHggL7lxI
QWZYpuU6MMxVmLSiAAEAwLMwL2UqHxcGQjIuepu9ikbJ251SwxYiH3xRMkYpQNNv
YFEb9pm2HJq9oNgZUGakDQ==

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,157 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var Cc = Components.classes;
var Ci = Components.interfaces;
const refMARPrefix = (mozinfo.os == "win" ? "win_" : "");
const BIN_SUFFIX = mozinfo.bin_suffix;
var tempDir = do_get_tempdir();
/**
* Compares binary data of 2 arrays and throws if they aren't the same.
* Throws on mismatch, does nothing on match.
*
* @param arr1 The first array to compare
* @param arr2 The second array to compare
*/
function compareBinaryData(arr1, arr2) {
do_check_eq(arr1.length, arr2.length);
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
throw "Data differs at index " + i +
", arr1: " + arr1[i] + ", arr2: " + arr2[i];
}
}
}
/**
* Reads a file's data and returns it
*
* @param file The file to read the data from
* @return a byte array for the data in the file.
*/
function getBinaryFileData(file) {
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
// Open as RD_ONLY with default permissions.
fileStream.init(file, -1, -1, null);
// Check the returned size versus the expected size.
let stream = Cc["@mozilla.org/binaryinputstream;1"].
createInstance(Ci.nsIBinaryInputStream);
stream.setInputStream(fileStream);
let bytes = stream.readByteArray(stream.available());
fileStream.close();
return bytes;
}
/**
* Runs each method in the passed in object
* Every method of the passed in object that starts with test_ will be ran
* The cleanup_per_test method of the object will be run right away, it will be
* registered to be the cleanup function, and it will be run between each test.
*
* @return The number of tests ran
*/
function run_tests(obj) {
let cleanup_per_test = obj.cleanup_per_test;
if (cleanup_per_test === undefined) {
cleanup_per_test = function() {};
}
do_register_cleanup(cleanup_per_test);
// Make sure there's nothing left over from a preious failed test
cleanup_per_test();
let ranCount = 0;
// hasOwnProperty ensures we only see direct properties and not all
for (let f in obj) {
if (typeof obj[f] === "function" &&
obj.hasOwnProperty(f) &&
f.toString().indexOf("test_") === 0) {
obj[f]();
cleanup_per_test();
ranCount++;
}
}
return ranCount;
}
/**
* Creates a MAR file with the content of files.
*
* @param outMAR The file where the MAR should be created to
* @param dataDir The directory where the relative file paths exist
* @param files The relative file paths of the files to include in the MAR
*/
function createMAR(outMAR, dataDir, files) {
// You cannot create an empy MAR.
do_check_true(files.length > 0);
// Get an nsIProcess to the signmar binary.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Ensure on non Windows platforms we encode the same permissions
// as the refernence MARs contain. On Windows this is also safe.
// The reference MAR files have permissions of 0o664, so in case
// someone is running these tests locally with another permission
// (perhaps 0o777), make sure that we encode them as 0o664.
for (filePath of files) {
let f = dataDir.clone();
f.append(filePath);
f.permissions = 0o664;
}
// Setup the command line arguments to create the MAR.
let args = ["-C", dataDir.path, "-H", "\@MAR_CHANNEL_ID\@",
"-V", "13.0a1", "-c", outMAR.path];
args = args.concat(files);
do_print('Running: ' + signmarBin.path);
process.init(signmarBin);
process.run(true, args, args.length);
// Verify signmar returned 0 for success.
do_check_eq(process.exitValue, 0);
// Verify the out MAR file actually exists.
do_check_true(outMAR.exists());
}
/**
* Extracts a MAR file to the specified output directory.
*
* @param mar The MAR file that should be matched
* @param dataDir The directory to extract to
*/
function extractMAR(mar, dataDir) {
// Get an nsIProcess to the signmar binary.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Setup the command line arguments to create the MAR.
let args = ["-C", dataDir.path, "-x", mar.path];
do_print('Running: ' + signmarBin.path);
process.init(signmarBin);
process.run(true, args, args.length);
// Verify signmar returned 0 for success.
do_check_eq(process.exitValue, 0);
}

View file

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
/**
* Creates MAR from the passed files, compares it to the reference MAR.
*
* @param refMARFileName The name of the MAR file that should match
* @param files The files that should go in the created MAR
* @param checkNoMAR If true return an error if a file already exists
*/
function run_one_test(refMARFileName, files, checkNoMAR) {
if (checkNoMAR === undefined) {
checkNoMAR = true;
}
// Ensure the MAR we will create doesn't already exist.
let outMAR = tempDir.clone();
outMAR.append("out.mar");
if (checkNoMAR) {
do_check_false(outMAR.exists());
}
// Create the actual MAR file.
createMAR(outMAR, do_get_file("data"), files);
// Get the reference MAR data.
let refMAR = do_get_file("data/" + refMARFileName);
let refMARData = getBinaryFileData(refMAR);
// Verify the data of the MAR is what it should be.
let outMARData = getBinaryFileData(outMAR);
compareBinaryData(outMARData, refMARData);
}
// Define the unit tests to run.
let tests = {
// Test creating a MAR file with a 0 byte file.
test_zero_sized: function() {
return run_one_test(refMARPrefix + "0_sized_mar.mar", ["0_sized_file"]);
},
// Test creating a MAR file with a 1 byte file.
test_one_byte: function() {
return run_one_test(refMARPrefix + "1_byte_mar.mar", ["1_byte_file"]);
},
// Test creating a MAR file with binary data.
test_binary_data: function() {
return run_one_test(refMARPrefix + "binary_data_mar.mar",
["binary_data_file"]);
},
// Test creating a MAR file with multiple files inside of it.
test_multiple_file: function() {
return run_one_test(refMARPrefix + "multiple_file_mar.mar",
["0_sized_file", "1_byte_file", "binary_data_file"]);
},
// Test creating a MAR file on top of a different one that already exists
// at the location the new one will be created at.
test_overwrite_already_exists: function() {
let differentFile = do_get_file("data/1_byte_mar.mar");
let outMARDir = tempDir.clone();
differentFile.copyTo(outMARDir, "out.mar");
return run_one_test(refMARPrefix + "binary_data_mar.mar",
["binary_data_file"], false);
},
// Between each test make sure the out MAR does not exist.
cleanup_per_test: function() {
let outMAR = tempDir.clone();
outMAR.append("out.mar");
if (outMAR.exists()) {
outMAR.remove(false);
}
}
};
// Run all the tests
do_check_eq(run_tests(tests), Object.keys(tests).length - 1);
}

View file

@ -0,0 +1,93 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
/**
* Extracts a MAR and makes sure each file matches the reference files.
*
* @param marFileName The name of the MAR file to extract
* @param files The files that the extracted MAR should contain
*/
function run_one_test(marFileName, files) {
// Get the MAR file that we will be extracting
let mar = do_get_file("data/" + marFileName);
// Get the path that we will extract to
let outDir = tempDir.clone();
outDir.append("out");
do_check_false(outDir.exists());
outDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o777);
// Get the ref files and the files that will be extracted.
let outFiles = [];
let refFiles = [];
for (let i = 0; i < files.length; i++) {
let outFile = outDir.clone();
outFile.append(files[i]);
do_check_false(outFile.exists());
outFiles.push(outFile);
refFiles.push(do_get_file("data/" + files[i]));
}
// Extract the MAR contents into the ./out dir.
extractMAR(mar, outDir);
// Compare to make sure the extracted files are the same.
for (let i = 0; i < files.length; i++) {
do_check_true(outFiles[i].exists());
let refFileData = getBinaryFileData(refFiles[i]);
let outFileData = getBinaryFileData(outFiles[i]);
compareBinaryData(refFileData, outFileData);
}
}
// Define the unit tests to run.
let tests = {
// Test extracting a MAR file with a 0 byte file.
test_zero_sized: function() {
return run_one_test("0_sized_mar.mar", ["0_sized_file"]);
},
// Test extracting a MAR file with a 1 byte file.
test_one_byte: function() {
return run_one_test("1_byte_mar.mar", ["1_byte_file"]);
},
// Test extracting a MAR file with binary data.
test_binary_data: function() {
return run_one_test("binary_data_mar.mar", ["binary_data_file"]);
},
// Test extracting a MAR without a product information block (PIB) which
// contains binary data.
test_no_pib: function() {
return run_one_test("no_pib_mar.mar", ["binary_data_file"]);
},
// Test extracting a MAR without a product information block (PIB) that is
// signed and which contains binary data.
test_no_pib_signed: function() {
return run_one_test("signed_no_pib_mar.mar", ["binary_data_file"]);
},
// Test extracting a MAR with a product information block (PIB) that is
// signed and which contains binary data.
test_pib_signed: function() {
return run_one_test("signed_pib_mar.mar", ["binary_data_file"]);
},
// Test extracting a MAR file with multiple files inside of it.
test_multiple_file: function() {
return run_one_test("multiple_file_mar.mar",
["0_sized_file", "1_byte_file", "binary_data_file"]);
},
// Between each test make sure the out directory and its subfiles do
// not exist.
cleanup_per_test: function() {
let outDir = tempDir.clone();
outDir.append("out");
if (outDir.exists()) {
outDir.remove(true);
}
}
};
// Run all the tests
do_check_eq(run_tests(tests), Object.keys(tests).length - 1);
}

View file

@ -0,0 +1,575 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
/**
* Signs a MAR file.
*
* @param inMAR The MAR file that should be signed
* @param outMAR The MAR file to create
*/
function signMAR(inMAR, outMAR, certs, wantSuccess, useShortHandCmdLine) {
// Get a process to the signmar binary from the dist/bin directory.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Setup the command line arguments to sign the MAR.
let NSSConfigDir = do_get_file("data");
let args = ["-d", NSSConfigDir.path];
if (certs.length == 1 && useShortHandCmdLine) {
args.push("-n", certs[0]);
} else {
for (var i = 0; i < certs.length; i++) {
args.push("-n" + i, certs[i]);
}
}
args.push("-s", inMAR.path, outMAR.path);
process.init(signmarBin);
try {
process.run(true, args, args.length);
} catch(e) {
// On Windows negative return value throws an exception
process.exitValue = -1;
}
// Verify signmar returned 0 for success.
if (wantSuccess) {
do_check_eq(process.exitValue, 0);
} else {
do_check_neq(process.exitValue, 0);
}
}
/**
* Extract a MAR signature.
*
* @param inMAR The MAR file who's signature should be extracted
* @param sigIndex The index of the signature to extract
* @param extractedSig The file where the extracted signature will be stored
* @param wantSuccess True if a successful signmar return code is desired
*/
function extractMARSignature(inMAR, sigIndex, extractedSig, wantSuccess) {
// Get a process to the signmar binary from the dist/bin directory.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Setup the command line arguments to extract the signature in the MAR.
let args = ["-n" + sigIndex, "-X", inMAR.path, extractedSig.path];
process.init(signmarBin);
try {
process.run(true, args, args.length);
} catch(e) {
// On Windows negative return value throws an exception
process.exitValue = -1;
}
// Verify signmar returned 0 for success.
if (wantSuccess) {
do_check_eq(process.exitValue, 0);
} else {
do_check_neq(process.exitValue, 0);
}
}
/**
* Import a MAR signature.
*
* @param inMAR The MAR file who's signature should be imported to
* @param sigIndex The index of the signature to import to
* @param sigFile The file where the base64 signature exists
* @param outMAR The same as inMAR but with the specified signature
* swapped at the specified index.
* @param wantSuccess True if a successful signmar return code is desired
*/
function importMARSignature(inMAR, sigIndex, sigFile, outMAR, wantSuccess) {
// Get a process to the signmar binary from the dist/bin directory.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Setup the command line arguments to import the signature in the MAR.
let args = ["-n" + sigIndex, "-I", inMAR.path, sigFile.path, outMAR.path];
process.init(signmarBin);
try {
process.run(true, args, args.length);
} catch(e) {
// On Windows negative return value throws an exception
process.exitValue = -1;
}
// Verify signmar returned 0 for success.
if (wantSuccess) {
do_check_eq(process.exitValue, 0);
} else {
do_check_neq(process.exitValue, 0);
}
}
/**
* Verifies a MAR file.
*
* @param signedMAR Verifies a MAR file
*/
function verifyMAR(signedMAR, wantSuccess, certs, useShortHandCmdLine) {
// Get a process to the signmar binary from the dist/bin directory.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// 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 = [];
// Setup the command line arguments to create the MAR.
// Windows & Mac vs. Linux/... have different command line for verification
// since on Windows we verify with CryptoAPI, on Mac with Security
// Transforms or CDSA/CSSM 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 (mozinfo.os == "win" || mozinfo.os == "mac") {
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");
}
}
} else {
let NSSConfigDir = do_get_file("data");
args = ["-d", NSSConfigDir.path];
if (certs.length == 1 && useShortHandCmdLine) {
args.push("-n", certs[0]);
} else {
for (var i = 0; i < certs.length; i++) {
args.push("-n" + i, certs[i]);
}
}
}
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;
}
// Verify signmar returned 0 for success.
if (wantSuccess) {
do_check_eq(process.exitValue, 0);
} else {
do_check_neq(process.exitValue, 0);
}
}
/**
* Strips a MAR signature.
*
* @param signedMAR The MAR file that should be signed
* @param outMAR The MAR file to write to with signature stripped
*/
function stripMARSignature(signedMAR, outMAR, wantSuccess) {
// Get a process to the signmar binary from the dist/bin directory.
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
// Make sure the signmar binary exists and is an executable.
do_check_true(signmarBin.exists());
do_check_true(signmarBin.isExecutable());
// Setup the command line arguments to create the MAR.
let args = ["-r", signedMAR.path, outMAR.path];
process.init(signmarBin);
try {
process.run(true, args, args.length);
} catch (e) {
// On Windows negative return value throws an exception
process.exitValue = -1;
}
// Verify signmar returned 0 for success.
if (wantSuccess) {
do_check_eq(process.exitValue, 0);
} else {
do_check_neq(process.exitValue, 0);
}
}
function cleanup() {
let outMAR = tempDir.clone();
outMAR.append("signed_out.mar");
if (outMAR.exists()) {
outMAR.remove(false);
}
outMAR = tempDir.clone();
outMAR.append("multiple_signed_out.mar");
if (outMAR.exists()) {
outMAR.remove(false);
}
outMAR = tempDir.clone();
outMAR.append("out.mar");
if (outMAR.exists()) {
outMAR.remove(false);
}
let outDir = tempDir.clone();
outDir.append("out");
if (outDir.exists()) {
outDir.remove(true);
}
}
const wantFailure = false;
const wantSuccess = true;
// Define the unit tests to run.
let tests = {
// Test signing a MAR file with a single signature
test_sign_single: function() {
let inMAR = do_get_file("data/" + refMARPrefix + "binary_data_mar.mar");
let outMAR = tempDir.clone();
outMAR.append("signed_out.mar");
if (outMAR.exists()) {
outMAR.remove(false);
}
signMAR(inMAR, outMAR, ["mycert"], wantSuccess, true);
do_check_true(outMAR.exists());
let outMARData = getBinaryFileData(outMAR);
let refMAR = do_get_file("data/" + refMARPrefix + "signed_pib_mar.mar");
let refMARData = getBinaryFileData(refMAR);
compareBinaryData(outMARData, refMARData);
},
// Test signing a MAR file with multiple signatures
test_sign_multiple: function() {
let inMAR = do_get_file("data/" + refMARPrefix + "binary_data_mar.mar");
let outMAR = tempDir.clone();
outMAR.append("multiple_signed_out.mar");
if (outMAR.exists()) {
outMAR.remove(false);
}
do_check_false(outMAR.exists());
signMAR(inMAR, outMAR, ["mycert", "mycert2", "mycert3"],
wantSuccess, true);
do_check_true(outMAR.exists());
let outMARData = getBinaryFileData(outMAR);
let refMAR = do_get_file("data/" + refMARPrefix + "multiple_signed_pib_mar.mar");
let refMARData = getBinaryFileData(refMAR);
compareBinaryData(outMARData, refMARData);
},
// Test verifying a signed MAR file
test_verify_single: function() {
let signedMAR = do_get_file("data/signed_pib_mar.mar");
verifyMAR(signedMAR, wantSuccess, ["mycert"], true);
verifyMAR(signedMAR, wantSuccess, ["mycert"], false);
},
// Test verifying a signed MAR file with too many certs fails.
// Or if you want to look at it another way, One mycert signature
// is missing.
test_verify_single_too_many_certs: function() {
let signedMAR = do_get_file("data/signed_pib_mar.mar");
verifyMAR(signedMAR, wantFailure, ["mycert", "mycert"], true);
verifyMAR(signedMAR, wantFailure, ["mycert", "mycert"], false);
},
// Test verifying a signed MAR file fails when using a wrong cert
test_verify_single_wrong_cert: function() {
let signedMAR = do_get_file("data/signed_pib_mar.mar");
verifyMAR(signedMAR, wantFailure, ["mycert2"], true);
verifyMAR(signedMAR, wantFailure, ["mycert2"], false);
},
// Test verifying a signed MAR file with multiple signatures
test_verify_multiple: function() {
let signedMAR = do_get_file("data/multiple_signed_pib_mar.mar");
verifyMAR(signedMAR, wantSuccess, ["mycert", "mycert2", "mycert3"]);
},
// Test verifying an unsigned MAR file fails
test_verify_unsigned_mar_file_fails: function() {
let unsignedMAR = do_get_file("data/binary_data_mar.mar");
verifyMAR(unsignedMAR, wantFailure, ["mycert", "mycert2", "mycert3"]);
},
// Test verifying a signed MAR file with the same signature multiple
// times fails. The input MAR has: mycert, mycert2, mycert3.
// we're checking to make sure the number of verified signatures
// is only 1 and not 3. Each signature should be verified once.
test_verify_multiple_same_cert: function() {
let signedMAR = do_get_file("data/multiple_signed_pib_mar.mar");
verifyMAR(signedMAR, wantFailure, ["mycert", "mycert", "mycert"]);
},
// Test verifying a signed MAR file with the correct signatures but in
// a different order fails
test_verify_multiple_wrong_order: function() {
let signedMAR = do_get_file("data/multiple_signed_pib_mar.mar");
verifyMAR(signedMAR, wantSuccess, ["mycert", "mycert2", "mycert3"]);
verifyMAR(signedMAR, wantFailure, ["mycert", "mycert3", "mycert2"]);
verifyMAR(signedMAR, wantFailure, ["mycert2", "mycert", "mycert3"]);
verifyMAR(signedMAR, wantFailure, ["mycert2", "mycert3", "mycert"]);
verifyMAR(signedMAR, wantFailure, ["mycert3", "mycert", "mycert2"]);
verifyMAR(signedMAR, wantFailure, ["mycert3", "mycert2", "mycert"]);
},
// Test verifying a signed MAR file without a PIB
test_verify_no_pib: function() {
let signedMAR = do_get_file("data/signed_no_pib_mar.mar");
verifyMAR(signedMAR, wantSuccess, ["mycert"], true);
verifyMAR(signedMAR, wantSuccess, ["mycert"], false);
},
// Test verifying a signed MAR file with multiple signatures without a PIB
test_verify_no_pib_multiple: function() {
let signedMAR = do_get_file("data/multiple_signed_no_pib_mar.mar");
verifyMAR(signedMAR, wantSuccess, ["mycert", "mycert2", "mycert3"]);
},
// Test verifying a crafted MAR file where the attacker tried to adjust
// the version number manually.
test_crafted_mar: function() {
let signedBadMAR = do_get_file("data/manipulated_signed_mar.mar");
verifyMAR(signedBadMAR, wantFailure, ["mycert"], true);
verifyMAR(signedBadMAR, wantFailure, ["mycert"], false);
},
// Test verifying a file that doesn't exist fails
test_bad_path_verify_fails: function() {
let noMAR = do_get_file("data/does_not_exist_.mar", true);
do_check_false(noMAR.exists());
verifyMAR(noMAR, wantFailure, ["mycert"], true);
},
// Test to make sure a stripped MAR is the same as the original MAR
test_strip_signature: function() {
let originalMAR = do_get_file("data/" +
refMARPrefix +
"binary_data_mar.mar");
let signedMAR = tempDir.clone();
signedMAR.append("signed_out.mar");
let outMAR = tempDir.clone();
outMAR.append("out.mar", true);
stripMARSignature(signedMAR, outMAR, wantSuccess);
// Verify that the stripped MAR matches the original data MAR exactly
let outMARData = getBinaryFileData(outMAR);
let originalMARData = getBinaryFileData(originalMAR);
compareBinaryData(outMARData, originalMARData);
},
// Test to make sure a stripped multi-signature-MAR is the same as the original MAR
test_strip_multiple_signatures: function() {
let originalMAR = do_get_file("data/" +
refMARPrefix +
"binary_data_mar.mar");
let signedMAR = tempDir.clone();
signedMAR.append("multiple_signed_out.mar");
let outMAR = tempDir.clone();
outMAR.append("out.mar");
stripMARSignature(signedMAR, outMAR, wantSuccess);
// Verify that the stripped MAR matches the original data MAR exactly
let outMARData = getBinaryFileData(outMAR);
let originalMARData = getBinaryFileData(originalMAR);
compareBinaryData(outMARData, originalMARData);
},
// Test extracting the first signature in a MAR that has only a single signature
test_extract_sig_single: function() {
let inMAR = do_get_file("data/signed_pib_mar.mar");
let extractedSig = do_get_file("extracted_signature", true);
if (extractedSig.exists()) {
extractedSig.remove(false);
}
extractMARSignature(inMAR, 0, extractedSig, wantSuccess);
do_check_true(extractedSig.exists());
let referenceSig = do_get_file("data/signed_pib_mar.signature.0"); +
compareBinaryData(extractedSig, referenceSig);
},
// Test extracting the all signatures in a multi signature MAR
// The input MAR has 3 signatures.
test_extract_sig_multi: function() {
for (let i = 0; i < 3; i++) {
let inMAR = do_get_file("data/multiple_signed_pib_mar.mar");
let extractedSig = do_get_file("extracted_signature", true);
if (extractedSig.exists()) {
extractedSig.remove(false);
}
extractMARSignature(inMAR, i, extractedSig, wantSuccess);
do_check_true(extractedSig.exists());
let referenceSig = do_get_file("data/multiple_signed_pib_mar.sig." + i); +
compareBinaryData(extractedSig, referenceSig);
}
},
// Test extracting a signature that is out of range fails
test_extract_sig_out_of_range: function() {
let inMAR = do_get_file("data/signed_pib_mar.mar");
let extractedSig = do_get_file("extracted_signature", true);
if (extractedSig.exists()) {
extractedSig.remove(false);
}
const outOfBoundsIndex = 5;
extractMARSignature(inMAR, outOfBoundsIndex, extractedSig, wantFailure);
do_check_false(extractedSig.exists());
},
// Test signing a file that doesn't exist fails
test_bad_path_sign_fails: function() {
let inMAR = do_get_file("data/does_not_exist_.mar", true);
let outMAR = tempDir.clone();
outMAR.append("signed_out.mar");
do_check_false(inMAR.exists());
signMAR(inMAR, outMAR, ["mycert"], wantFailure, true);
do_check_false(outMAR.exists());
},
// Test verifying only a subset of the signatures fails.
// The input MAR has: mycert, mycert2, mycert3.
// We're only verifying 2 of the 3 signatures and that should fail.
test_verify_multiple_subset: function() {
let signedMAR = do_get_file("data/multiple_signed_pib_mar.mar");
verifyMAR(signedMAR, wantFailure, ["mycert", "mycert2"]);
},
// Test importing the first signature in a MAR that has only
// a single signature
test_import_sig_single: function() {
// Make sure the input MAR was signed with mycert only
let inMAR = do_get_file("data/signed_pib_mar.mar");
verifyMAR(inMAR, wantSuccess, ["mycert"], false);
verifyMAR(inMAR, wantFailure, ["mycert2"], false);
verifyMAR(inMAR, wantFailure, ["mycert3"], false);
// Get the signature file for this MAR signed with the key from mycert2
let sigFile = do_get_file("data/signed_pib_mar.signature.mycert2");
do_check_true(sigFile.exists());
let outMAR = tempDir.clone();
outMAR.append("sigchanged_signed_pib_mar.mar");
if (outMAR.exists()) {
outMAR.remove(false);
}
//Run the import operation
importMARSignature(inMAR, 0, sigFile, outMAR, wantSuccess);
// Verify we have a new MAR file and that mycert no longer verifies
// and that mycert2 does verify
do_check_true(outMAR.exists());
verifyMAR(outMAR, wantFailure, ["mycert"], false);
verifyMAR(outMAR, wantSuccess, ["mycert2"], false);
verifyMAR(outMAR, wantFailure, ["mycert3"], false);
// Compare the binary data to something that was signed originally
// with the private key from mycert2
let refMAR = do_get_file("data/signed_pib_mar_with_mycert2.mar");
do_check_true(refMAR.exists());
let refMARData = getBinaryFileData(refMAR);
let outMARData = getBinaryFileData(outMAR);
compareBinaryData(outMARData, refMARData);
},
// Test importing a signature that doesn't belong to the file
// fails to verify.
test_import_wrong_sig: function() {
// Make sure the input MAR was signed with mycert only
let inMAR = do_get_file("data/signed_pib_mar.mar");
verifyMAR(inMAR, wantSuccess, ["mycert"], false);
verifyMAR(inMAR, wantFailure, ["mycert2"], false);
verifyMAR(inMAR, wantFailure, ["mycert3"], false);
// Get the signature file for this MAR signed with the key from mycert2
let sigFile = do_get_file("data/multiple_signed_pib_mar.sig.0");
do_check_true(sigFile.exists());
let outMAR = tempDir.clone();
outMAR.append("sigchanged_signed_pib_mar.mar");
if (outMAR.exists()) {
outMAR.remove(false);
}
//Run the import operation
importMARSignature(inMAR, 0, sigFile, outMAR, wantSuccess);
// Verify we have a new MAR file and that mycert no longer verifies
// and that mycert2 does verify
do_check_true(outMAR.exists());
verifyMAR(outMAR, wantFailure, ["mycert"], false);
verifyMAR(outMAR, wantFailure, ["mycert2"], false);
verifyMAR(outMAR, wantFailure, ["mycert3"], false);
},
// Test importing to the second signature in a MAR that has multiple
// signature
test_import_sig_multiple: function() {
// Make sure the input MAR was signed with mycert only
let inMAR = do_get_file("data/multiple_signed_pib_mar.mar");
verifyMAR(inMAR, wantSuccess, ["mycert", "mycert2", "mycert3"], false);
verifyMAR(inMAR, wantFailure, ["mycert", "mycert", "mycert3"], false);
// Get the signature file for this MAR signed with the key from mycert
let sigFile = do_get_file("data/multiple_signed_pib_mar.sig.0");
do_check_true(sigFile.exists());
let outMAR = tempDir.clone();
outMAR.append("sigchanged_signed_pib_mar.mar");
if (outMAR.exists()) {
outMAR.remove(false);
}
//Run the import operation
const secondSigPos = 1;
importMARSignature(inMAR, secondSigPos, sigFile, outMAR, wantSuccess);
// Verify we have a new MAR file and that mycert no longer verifies
// and that mycert2 does verify
do_check_true(outMAR.exists());
verifyMAR(outMAR, wantSuccess, ["mycert", "mycert", "mycert3"], false);
verifyMAR(outMAR, wantFailure, ["mycert", "mycert2", "mycert3"], false);
// Compare the binary data to something that was signed originally
// with the private keys from mycert, mycert, mycert3
let refMAR = do_get_file("data/multiple_signed_pib_mar_2.mar");
do_check_true(refMAR.exists());
let refMARData = getBinaryFileData(refMAR);
let outMARData = getBinaryFileData(outMAR);
compareBinaryData(outMARData, refMARData);
},
// Test stripping a MAR that doesn't exist fails
test_bad_path_strip_fails: function() {
let noMAR = do_get_file("data/does_not_exist_mar", true);
do_check_false(noMAR.exists());
let outMAR = tempDir.clone();
outMAR.append("out.mar");
stripMARSignature(noMAR, outMAR, wantFailure);
},
// Test extracting from a bad path fails
test_extract_bad_path: function() {
let noMAR = do_get_file("data/does_not_exist.mar", true);
let extractedSig = do_get_file("extracted_signature", true);
do_check_false(noMAR.exists());
if (extractedSig.exists()) {
extractedSig.remove(false);
}
extractMARSignature(noMAR, 0, extractedSig, wantFailure);
do_check_false(extractedSig.exists());
},
// Between each test make sure the out MAR does not exist.
cleanup_per_test: function() {
}
};
cleanup();
// Run all the tests
do_check_eq(run_tests(tests), Object.keys(tests).length - 1);
do_register_cleanup(cleanup);
}

View file

@ -0,0 +1,8 @@
[DEFAULT]
head = head_libmar.js
tail =
support-files = data/**
[test_create.js]
[test_extract.js]
[test_sign_verify.js]

426
modules/libmar/tool/mar.c Normal file
View file

@ -0,0 +1,426 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mar.h"
#include "mar_cmdline.h"
#ifdef XP_WIN
#include <windows.h>
#include <direct.h>
#define chdir _chdir
#else
#include <unistd.h>
#endif
#if !defined(NO_SIGN_VERIFY) && (!defined(XP_WIN) || defined(MAR_NSS))
#include "cert.h"
#include "nss.h"
#include "pk11pub.h"
int NSSInitCryptoContext(const char *NSSConfigDir);
#endif
int mar_repackage_and_sign(const char *NSSConfigDir,
const char * const *certNames,
uint32_t certCount,
const char *src,
const char * dest);
static void print_version() {
printf("Version: %s\n", MOZ_APP_VERSION);
printf("Default Channel ID: %s\n", MAR_CHANNEL_ID);
}
static void print_usage() {
printf("usage:\n");
printf("Create a MAR file:\n");
printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
"-c archive.mar [files...]\n");
printf("Extract a MAR file:\n");
printf(" mar [-C workingDir] -x archive.mar\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("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_MACOSX) || (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 -t archive.mar\n");
printf("Print detailed information on a MAR file including signatures:\n");
printf(" mar -T archive.mar\n");
printf("Refresh the product information block of a MAR file:\n");
printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
"-i unsigned_archive_to_refresh.mar\n");
printf("Print executable version:\n");
printf(" mar --version\n");
printf("This program does not handle unicode file paths properly\n");
}
static int mar_test_callback(MarFile *mar,
const MarItem *item,
void *unused) {
printf("%u\t0%o\t%s\n", item->length, item->flags, item->name);
return 0;
}
static int mar_test(const char *path) {
MarFile *mar;
mar = mar_open(path);
if (!mar)
return -1;
printf("SIZE\tMODE\tNAME\n");
mar_enum_items(mar, mar_test_callback, NULL);
mar_close(mar);
return 0;
}
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 k;
int rv = -1;
uint32_t certCount = 0;
int32_t sigIndex = -1;
#if !defined(NO_SIGN_VERIFY)
uint32_t fileSizes[MAX_SIGNATURES];
const uint8_t* certBuffers[MAX_SIGNATURES];
#if ((!defined(MAR_NSS) && defined(XP_WIN)) || defined(XP_MACOSX)) || \
((defined(XP_WIN) || defined(XP_MACOSX)) && !defined(MAR_NSS))
char* DERFilePaths[MAX_SIGNATURES];
#endif
#if (!defined(XP_WIN) && !defined(XP_MACOSX)) || defined(MAR_NSS)
CERTCertificate* certs[MAX_SIGNATURES];
#endif
#endif
memset((void*)certNames, 0, sizeof(certNames));
#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
memset((void*)certBuffers, 0, sizeof(certBuffers));
#endif
#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \
defined(XP_MACOSX))
memset(DERFilePaths, 0, sizeof(DERFilePaths));
memset(fileSizes, 0, sizeof(fileSizes));
#endif
if (argc > 1 && 0 == strcmp(argv[1], "--version")) {
print_version();
return 0;
}
if (argc < 3) {
print_usage();
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] == 'I')) {
break;
/* -C workingdirectory */
} else if (argv[1][0] == '-' && argv[1][1] == 'C') {
if (chdir(argv[2]) != 0) {
return -1;
}
argv += 2;
argc -= 2;
}
#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) {
print_usage();
return -1;
}
DERFilePaths[certCount++] = argv[2];
argv += 2;
argc -= 2;
}
#endif
/* -d NSSConfigdir */
else if (argv[1][0] == '-' && argv[1][1] == 'd') {
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] == (char)('0' + certCount) ||
argv[1][2] == '\0' ||
!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") || !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 */
} 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];
argv += 2;
argc -= 2;
}
else {
print_usage();
return -1;
}
}
if (argv[1][0] != '-') {
print_usage();
return -1;
}
switch (argv[1][1]) {
case 'c': {
struct ProductInformationBlock infoBlock;
infoBlock.MARChannelID = MARChannelID;
infoBlock.productVersion = productVersion;
return mar_create(argv[2], argc - 3, argv + 3, &infoBlock);
}
case 'i': {
struct ProductInformationBlock infoBlock;
infoBlock.MARChannelID = MARChannelID;
infoBlock.productVersion = productVersion;
return refresh_product_info_block(argv[2], &infoBlock);
}
case 'T': {
struct ProductInformationBlock infoBlock;
uint32_t numSignatures, numAdditionalBlocks;
int hasSignatureBlock, hasAdditionalBlock;
if (!get_mar_file_info(argv[2],
&hasSignatureBlock,
&numSignatures,
&hasAdditionalBlock,
NULL, &numAdditionalBlocks)) {
if (hasSignatureBlock) {
printf("Signature block found with %d signature%s\n",
numSignatures,
numSignatures != 1 ? "s" : "");
}
if (hasAdditionalBlock) {
printf("%d additional block%s found:\n",
numAdditionalBlocks,
numAdditionalBlocks != 1 ? "s" : "");
}
rv = read_product_info_block(argv[2], &infoBlock);
if (!rv) {
printf(" - Product Information Block:\n");
printf(" - MAR channel name: %s\n"
" - Product version: %s\n",
infoBlock.MARChannelID,
infoBlock.productVersion);
free((void *)infoBlock.MARChannelID);
free((void *)infoBlock.productVersion);
}
}
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]);
/* 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 (certCount == 0) {
print_usage();
return -1;
}
#if (!defined(XP_WIN) && !defined(XP_MACOSX)) || defined(MAR_NSS)
if (!NSSConfigDir || certCount == 0) {
print_usage();
return -1;
}
if (NSSInitCryptoContext(NSSConfigDir)) {
fprintf(stderr, "ERROR: Could not initialize crypto library.\n");
return -1;
}
#endif
rv = 0;
for (k = 0; k < certCount; ++k) {
#if (defined(XP_WIN) || defined(XP_MACOSX)) && !defined(MAR_NSS)
rv = mar_read_entire_file(DERFilePaths[k], MAR_MAX_CERT_SIZE,
&certBuffers[k], &fileSizes[k]);
if (rv) {
fprintf(stderr, "ERROR: could not read file %s", DERFilePaths[k]);
break;
}
#else
/* It is somewhat circuitous to look up a CERTCertificate and then pass
* in its DER encoding just so we can later re-create that
* CERTCertificate to extract the public key out of it. However, by doing
* things this way, we maximize the reuse of the mar_verify_signatures
* function and also we keep the control flow as similar as possible
* between programs and operating systems, at least for the functions
* that are critically important to security.
*/
certs[k] = PK11_FindCertFromNickname(certNames[k], NULL);
if (certs[k]) {
certBuffers[k] = certs[k]->derCert.data;
fileSizes[k] = certs[k]->derCert.len;
} else {
rv = -1;
fprintf(stderr, "ERROR: could not find cert from nickname %s", certNames[k]);
break;
}
#endif
}
if (!rv) {
MarFile *mar = mar_open(argv[2]);
if (mar) {
rv = mar_verify_signatures(mar, certBuffers, fileSizes, certCount);
mar_close(mar);
} else {
fprintf(stderr, "ERROR: Could not open MAR file.\n");
rv = -1;
}
}
for (k = 0; k < certCount; ++k) {
#if (defined(XP_WIN) || defined(XP_MACOSX)) && !defined(MAR_NSS)
free((void*)certBuffers[k]);
#else
/* certBuffers[k] is owned by certs[k] so don't free it */
CERT_DestroyCertificate(certs[k]);
#endif
}
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");
}
}
#if (!defined(XP_WIN) && !defined(XP_MACOSX)) || defined(MAR_NSS)
(void) NSS_Shutdown();
#endif
return rv ? -1 : 0;
case 's':
if (!NSSConfigDir || certCount == 0 || argc < 4) {
print_usage();
return -1;
}
return mar_repackage_and_sign(NSSConfigDir, certNames, certCount,
argv[2], argv[3]);
case 'r':
return strip_signature_block(argv[2], argv[3]);
#endif /* endif NO_SIGN_VERIFY disabled */
default:
print_usage();
return -1;
}
}

View file

@ -0,0 +1,61 @@
# -*- 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/.
HOST_SOURCES += [
'mar.c',
]
HostProgram('mar')
HOST_USE_LIBS += [
'hostmar',
]
if CONFIG['MOZ_ENABLE_SIGNMAR']:
Program('signmar')
SOURCES += HOST_SOURCES
USE_LIBS += [
'mar',
'signmar',
'verifymar',
]
for var in ('MAR_CHANNEL_ID', 'MOZ_APP_VERSION'):
DEFINES[var] = '"%s"' % CONFIG[var]
HOST_DEFINES[var] = DEFINES[var]
if CONFIG['MOZ_ENABLE_SIGNMAR']:
USE_LIBS += [
'nspr',
'nss',
]
else:
DEFINES['NO_SIGN_VERIFY'] = True
if CONFIG['OS_ARCH'] == 'WINNT':
USE_STATIC_LIBS = True
OS_LIBS += [
'ws2_32',
]
if CONFIG['MOZ_ENABLE_SIGNMAR']:
OS_LIBS += [
'crypt32',
'advapi32',
]
elif CONFIG['OS_ARCH'] == 'Darwin':
OS_LIBS += [
'-framework Security',
]
if CONFIG['HOST_OS_ARCH'] == 'WINNT':
HOST_OS_LIBS += [
'ws2_32',
]
HOST_DEFINES['NO_SIGN_VERIFY'] = True

View file

@ -0,0 +1,213 @@
/* 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;
#ifdef __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) {
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 (!aInputData || !*aInputData) {
return;
}
CFMutableDataRef inputData = NULL;
inputData = (CFMutableDataRef)*aInputData;
CFRelease(inputData);
}
void
CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey)
{
if (!aPublicKey || !*aPublicKey) {
return;
}
CFRelease((SecKeyRef)*aPublicKey);
}

View file

@ -0,0 +1,273 @@
/* 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 "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_ISO_SHA1_WITH_RSA_SIGNATURE, 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
CyprtoAPI_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_ENHANCED_PROV,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
if (!CryptAcquireContext(provider,
NULL,
MS_ENHANCED_PROV,
PROV_RSA_FULL,
CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) {
if (!CryptAcquireContext(provider,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
if (!CryptAcquireContext(provider,
NULL,
NULL,
PROV_RSA_FULL,
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_SHA1,
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

View file

@ -0,0 +1,172 @@
/* 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 CyprtoAPI_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) \
CyprtoAPI_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

View file

@ -0,0 +1,466 @@
/* 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] != 1) {
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;
}

View file

@ -0,0 +1,31 @@
# -*- 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']
LOCAL_INCLUDES += [
'../src',
]