mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 20:07:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
6
modules/libmar/README
Normal file
6
modules/libmar/README
Normal 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
17
modules/libmar/moz.build
Normal 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']
|
||||
|
||||
1163
modules/libmar/sign/mar_sign.c
Normal file
1163
modules/libmar/sign/mar_sign.c
Normal file
File diff suppressed because it is too large
Load diff
24
modules/libmar/sign/moz.build
Normal file
24
modules/libmar/sign/moz.build
Normal 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
|
||||
239
modules/libmar/sign/nss_secutil.c
Normal file
239
modules/libmar/sign/nss_secutil.c
Normal 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;
|
||||
}
|
||||
41
modules/libmar/sign/nss_secutil.h
Normal file
41
modules/libmar/sign/nss_secutil.h
Normal 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
198
modules/libmar/src/mar.h
Normal 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__ */
|
||||
110
modules/libmar/src/mar_cmdline.h
Normal file
110
modules/libmar/src/mar_cmdline.h
Normal 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__ */
|
||||
399
modules/libmar/src/mar_create.c
Normal file
399
modules/libmar/src/mar_create.c
Normal 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;
|
||||
}
|
||||
83
modules/libmar/src/mar_extract.c
Normal file
83
modules/libmar/src/mar_extract.c
Normal 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;
|
||||
}
|
||||
79
modules/libmar/src/mar_private.h
Normal file
79
modules/libmar/src/mar_private.h
Normal 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__ */
|
||||
577
modules/libmar/src/mar_read.c
Normal file
577
modules/libmar/src/mar_read.c
Normal 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;
|
||||
}
|
||||
30
modules/libmar/src/moz.build
Normal file
30
modules/libmar/src/moz.build
Normal 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
|
||||
12
modules/libmar/tests/moz.build
Normal file
12
modules/libmar/tests/moz.build
Normal 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'],
|
||||
]
|
||||
0
modules/libmar/tests/unit/data/0_sized_file
Normal file
0
modules/libmar/tests/unit/data/0_sized_file
Normal file
BIN
modules/libmar/tests/unit/data/0_sized_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/0_sized_mar.mar
Normal file
Binary file not shown.
1
modules/libmar/tests/unit/data/1_byte_file
Normal file
1
modules/libmar/tests/unit/data/1_byte_file
Normal file
|
|
@ -0,0 +1 @@
|
|||
1
|
||||
BIN
modules/libmar/tests/unit/data/1_byte_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/1_byte_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/binary_data_file
Normal file
BIN
modules/libmar/tests/unit/data/binary_data_file
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 512 B |
BIN
modules/libmar/tests/unit/data/binary_data_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/binary_data_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/cert8.db
Normal file
BIN
modules/libmar/tests/unit/data/cert8.db
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/key3.db
Normal file
BIN
modules/libmar/tests/unit/data/key3.db
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/manipulated_signed_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/manipulated_signed_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/multiple_file_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/multiple_file_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/multiple_signed_no_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/multiple_signed_no_pib_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/multiple_signed_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/multiple_signed_pib_mar.mar
Normal file
Binary file not shown.
|
|
@ -0,0 +1,6 @@
|
|||
myuujl0MBwyLCrp8I84HrDbGLe+T5yfAR869QWvhcet/CglmuEsQEJczAoK8PtOR
|
||||
HcqczCRFtxNRKDPOUC0i+CS7RAQG4XJd9uprqHtB28s4SR/9nXKfWDnH2UYq9hkt
|
||||
X6qTab9M9ySrugOugomDb3ej/qYoNfJN9RnkLP4GP+cl96bWPU33rL0Uu7sUKizu
|
||||
QoYzxKeZ0r9hGMpOP2l/Jn+pydoEWGVB1mzvIFLPqD9cShUvV80xs8teV0G9IncC
|
||||
ZRuBwwNkfMTgJDCnxbrw3gIqNXMN1zjssztyJIpT2q8JGs+F6H0wz515xm32dCdQ
|
||||
b3Oo8a9Dx28NKKq83DJDQA==
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
xSnuhHyf8iEdPnRgNw9w0Tu6dJimNv+etdZagvbftkGuBlQArhPn7unCOEQ+jS0n
|
||||
ummJHp1yv64Q7Tte4te8OpRaR6eE333bHfFp++cKKJ2jWFeZ1SwRn59cWX0w4z9u
|
||||
I4VJmqzOKDUgZq24m6MfGr9iRKbrDjDgvfapzRkZNNU/I0jv20+G+vIUysQGLSN7
|
||||
fMAxxeurZNbinIiFQyudSpLU2n5PJDh/FIX2lt8H8nX5/yNyznbz0Gm+/hGMZj7+
|
||||
EfPxLxyOuSRVxI0ebAcRBQJLiyEh1iIluvjuBKohCxkWkEZG3weCz29JxdWOzobj
|
||||
3/6D+xJW5M1V8aE7EEjt4w==
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
VQmSlk8q2tmSd+C/d3ADde0lQoJw611sWboc7GOnFTRRsO0D61fIl3tlXLn/N4vN
|
||||
fCHQxwrszCizC1ddt9Bf5ujBqcAx+ZrN+iT2NlK2j6TN6K1W8LENJgCE7IXN5h1G
|
||||
VVryo5OkJzWd50DLX/qL9EAg3wx+P3b2BWXkhMuCDGvtAL3C4Ffnm7dw0hjErsEV
|
||||
X0cB5O5ozM0dOih+GNNX++wxT8E1NqNJOGaJR1KYeY17agz+QlSvFt/fL/a64Fsw
|
||||
DtOeGZ79nQZ6qkbmUxLXDQ630y3AQ8ceMJFIlI+T3Tk1DUuAWUpMXOICaqzDCdh+
|
||||
QC5nuQ7OK8Ycbm5fkIFfNQ==
|
||||
BIN
modules/libmar/tests/unit/data/multiple_signed_pib_mar_2.mar
Normal file
BIN
modules/libmar/tests/unit/data/multiple_signed_pib_mar_2.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/mycert.der
Normal file
BIN
modules/libmar/tests/unit/data/mycert.der
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/mycert2.der
Normal file
BIN
modules/libmar/tests/unit/data/mycert2.der
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/mycert3.der
Normal file
BIN
modules/libmar/tests/unit/data/mycert3.der
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/no_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/no_pib_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/secmod.db
Normal file
BIN
modules/libmar/tests/unit/data/secmod.db
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/signed_no_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/signed_no_pib_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/signed_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/signed_pib_mar.mar
Normal file
Binary file not shown.
|
|
@ -0,0 +1,6 @@
|
|||
VTw+yunNUglvAxNevIP1A+1aWNgD479tbZy4t8uDuC9AJ0nLeLXYBfklGxaKCzo4
|
||||
/UdWFfv1gJmqbnCAlZrZ9UJLUVZOUAwKb+V90bC7pBnGy7xplKBDm40SIs4fwWZl
|
||||
EGRt8GCPjYKgsYp+jScNMD8B4J3bPSR7m0c1TjXV4pZMhT7LJ+iLfHiy5+WiGBgZ
|
||||
9qor7plYxfZFgg4moAA3iIXIJbNORUEWfz9b7rsMmiwZO4XmMSDNUutkj9Jl+9gB
|
||||
XRwrwL0QLvAuYwIzB0HDdl/LPCC+UDEMKigcPhjwFnpN17qUks0fRxId8e4P8m2H
|
||||
rumgMHGhwx3uagGTTufQSw==
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
wYPEMA2nfrMfkL5+//r9Of8JicdmG5KxAzYUhSR3d/vr075LhGkc6nQc5orDIRnz
|
||||
vuMBRIqoAsK3IdtCEbZ6rRKN9d+N7HfvmLdDXGpK3lr9NKKTnk1n/0o7ziRi3Fm8
|
||||
hOkJpdit7OHV0RH5GBSM8tQziXGN+qe51W2otMA4d8+oa4tp2D7W4SSUxxJwBPiu
|
||||
5CJAA68qaxzhWd5iVtU8mcjbYOKZAciIPgqBxhgmukqNrShQpnwcop/WHggL7lxI
|
||||
QWZYpuU6MMxVmLSiAAEAwLMwL2UqHxcGQjIuepu9ikbJ251SwxYiH3xRMkYpQNNv
|
||||
YFEb9pm2HJq9oNgZUGakDQ==
|
||||
BIN
modules/libmar/tests/unit/data/signed_pib_mar_with_mycert2.mar
Normal file
BIN
modules/libmar/tests/unit/data/signed_pib_mar_with_mycert2.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/win_0_sized_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/win_0_sized_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/win_1_byte_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/win_1_byte_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/win_binary_data_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/win_binary_data_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/win_multiple_file_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/win_multiple_file_mar.mar
Normal file
Binary file not shown.
Binary file not shown.
BIN
modules/libmar/tests/unit/data/win_multiple_signed_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/win_multiple_signed_pib_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/win_signed_no_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/win_signed_no_pib_mar.mar
Normal file
Binary file not shown.
BIN
modules/libmar/tests/unit/data/win_signed_pib_mar.mar
Normal file
BIN
modules/libmar/tests/unit/data/win_signed_pib_mar.mar
Normal file
Binary file not shown.
157
modules/libmar/tests/unit/head_libmar.js
Normal file
157
modules/libmar/tests/unit/head_libmar.js
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
78
modules/libmar/tests/unit/test_create.js
Normal file
78
modules/libmar/tests/unit/test_create.js
Normal 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);
|
||||
}
|
||||
93
modules/libmar/tests/unit/test_extract.js
Normal file
93
modules/libmar/tests/unit/test_extract.js
Normal 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);
|
||||
}
|
||||
575
modules/libmar/tests/unit/test_sign_verify.js
Normal file
575
modules/libmar/tests/unit/test_sign_verify.js
Normal 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);
|
||||
}
|
||||
8
modules/libmar/tests/unit/xpcshell.ini
Normal file
8
modules/libmar/tests/unit/xpcshell.ini
Normal 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
426
modules/libmar/tool/mar.c
Normal 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;
|
||||
}
|
||||
}
|
||||
61
modules/libmar/tool/moz.build
Normal file
61
modules/libmar/tool/moz.build
Normal 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
|
||||
213
modules/libmar/verify/MacVerifyCrypto.cpp
Normal file
213
modules/libmar/verify/MacVerifyCrypto.cpp
Normal 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);
|
||||
}
|
||||
273
modules/libmar/verify/cryptox.c
Normal file
273
modules/libmar/verify/cryptox.c
Normal 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
|
||||
|
||||
|
||||
|
||||
172
modules/libmar/verify/cryptox.h
Normal file
172
modules/libmar/verify/cryptox.h
Normal 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
|
||||
466
modules/libmar/verify/mar_verify.c
Normal file
466
modules/libmar/verify/mar_verify.c
Normal 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;
|
||||
}
|
||||
31
modules/libmar/verify/moz.build
Normal file
31
modules/libmar/verify/moz.build
Normal 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',
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue