Bug 903135 - Updates to libmar needed to support B2G MAR signature verification. r=bbondy

This commit is contained in:
Brian Smith 2014-10-22 21:00:15 -04:00
Родитель dc4ba8bd44
Коммит 6a474042e5
6 изменённых файлов: 161 добавлений и 220 удалений

Просмотреть файл

@ -134,6 +134,26 @@ int mar_create(const char *dest,
*/
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
@ -154,12 +174,10 @@ int mar_extract(const char *path);
* a negative number if there was an error
* a positive number if the signature does not verify
*/
#ifdef XP_WIN
int mar_verify_signaturesW(MarFile *mar,
const uint8_t * const *certData,
const uint32_t *certDataSizes,
uint32_t certCount);
#endif
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.

Просмотреть файл

@ -38,38 +38,6 @@ int get_mar_file_info(const char *path,
uint32_t *offsetAdditionalBlocks,
uint32_t *numAdditionalBlocks);
/**
* 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.
* This is only used by the signmar program when used with arguments to verify
* a MAR. This should not be used to verify a MAR that will be extracted in the
* same operation by updater code. This function prints the error message if
* verification fails.
*
* @param pathToMAR The path of the MAR file whose signature should be
* checked
* @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 certNames Pointer to the first element in an array of certificate
* names.
* Used only if compiled with NSS support
* @param certCount The number of elements in certData, certDataSizes,
* and certNames
* @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(const char *pathToMAR,
const uint8_t * const *certData,
const uint32_t *certDataSizes,
const char * const *certNames,
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

Просмотреть файл

@ -19,6 +19,8 @@
#endif
#if !defined(NO_SIGN_VERIFY) && (!defined(XP_WIN) || defined(MAR_NSS))
#include "cert.h"
#include "pk11pub.h"
int NSSInitCryptoContext(const char *NSSConfigDir);
#endif
@ -120,15 +122,13 @@ int main(int argc, char **argv) {
uint32_t certCount = 0;
int32_t sigIndex = -1;
#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
HANDLE certFile;
uint8_t *certBuffers[MAX_SIGNATURES];
#endif
#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \
defined(XP_MACOSX))
char* DERFilePaths[MAX_SIGNATURES];
#if !defined(NO_SIGN_VERIFY)
uint32_t fileSizes[MAX_SIGNATURES];
uint32_t read;
uint8_t* certBuffers[MAX_SIGNATURES];
char* DERFilePaths[MAX_SIGNATURES];
#if (!defined(XP_WIN) && !defined(XP_MACOSX)) || defined(MAR_NSS)
CERTCertificate* certs[MAX_SIGNATURES];
#endif
#endif
memset(certNames, 0, sizeof(certNames));
@ -319,43 +319,68 @@ int main(int argc, char **argv) {
return import_signature(argv[2], sigIndex, argv[3], argv[4]);
case 'v':
#if defined(XP_WIN) && !defined(MAR_NSS)
if (certCount == 0) {
print_usage();
return -1;
}
for (k = 0; k < certCount; ++k) {
/* If the mar program was built using CryptoAPI, then read in the buffer
containing the cert from disk. */
certFile = CreateFileA(DERFilePaths[k], GENERIC_READ,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
0, NULL);
if (INVALID_HANDLE_VALUE == certFile) {
return -1;
}
fileSizes[k] = GetFileSize(certFile, NULL);
certBuffers[k] = malloc(fileSizes[k]);
if (!ReadFile(certFile, certBuffers[k], fileSizes[k], &read, NULL) ||
fileSizes[k] != read) {
CloseHandle(certFile);
for (i = 0; i <= k; i++) {
free(certBuffers[i]);
}
return -1;
}
CloseHandle(certFile);
#if (!defined(XP_WIN) && !defined(XP_MACOSX)) || defined(MAR_NSS)
if (!NSSConfigDir || certCount == 0) {
print_usage();
return -1;
}
rv = mar_verify_signatures(argv[2], certBuffers, fileSizes,
NULL, certCount);
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]);
#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;
}
#endif
if (rv) {
fprintf(stderr, "ERROR: could not read file %s", DERFilePaths[k]);
break;
}
}
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(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 */
@ -369,26 +394,8 @@ int main(int argc, char **argv) {
}
return -1;
}
return 0;
#elif defined(XP_MACOSX)
return mar_verify_signatures(argv[2], (const uint8_t* const*)DERFilePaths,
0, NULL, certCount);
#else
if (!NSSConfigDir || certCount == 0) {
print_usage();
return -1;
}
if (NSSInitCryptoContext(NSSConfigDir)) {
fprintf(stderr, "ERROR: Could not initialize crypto library.\n");
return -1;
}
return mar_verify_signatures(argv[2], NULL, 0, certNames, certCount);
#endif /* defined(XP_WIN) && !defined(MAR_NSS) */
case 's':
if (!NSSConfigDir || certCount == 0 || argc < 4) {
print_usage();

Просмотреть файл

@ -16,29 +16,32 @@
/**
* Loads the public key for the specified cert name from the NSS store.
*
* @param certName The cert name to find.
* @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.
* @param cert Out parameter for the certificate to use.
* @return CryptoX_Success on success, CryptoX_Error on error.
*/
CryptoX_Result
NSS_LoadPublicKey(const char *certNickname,
SECKEYPublicKey **publicKey,
CERTCertificate **cert)
NSS_LoadPublicKey(const unsigned char *certData, unsigned int certDataSize,
SECKEYPublicKey **publicKey)
{
secuPWData pwdata = { PW_NONE, 0 };
if (!cert || !publicKey || !cert) {
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 */
*cert = PK11_FindCertFromNickname(certNickname, &pwdata);
if (!*cert) {
if (!cert) {
return CryptoX_Error;
}
*publicKey = CERT_ExtractPublicKey(*cert);
*publicKey = CERT_ExtractPublicKey(cert);
CERT_DestroyCertificate(cert);
if (!*publicKey) {
CERT_DestroyCertificate(*cert);
return CryptoX_Error;
}
return CryptoX_Success;
@ -150,12 +153,11 @@ CryptoX_Result
CryptoAPI_LoadPublicKey(HCRYPTPROV provider,
BYTE *certData,
DWORD sizeOfCertData,
HCRYPTKEY *publicKey,
HCERTSTORE *certStore)
HCRYPTKEY *publicKey)
{
CRYPT_DATA_BLOB blob;
CERT_CONTEXT *context;
if (!provider || !certData || !publicKey || !certStore) {
if (!provider || !certData || !publicKey) {
return CryptoX_Error;
}
@ -165,7 +167,7 @@ CryptoAPI_LoadPublicKey(HCRYPTPROV provider,
CERT_QUERY_CONTENT_FLAG_CERT,
CERT_QUERY_FORMAT_FLAG_BINARY,
0, NULL, NULL, NULL,
certStore, NULL, (const void **)&context)) {
NULL, NULL, (const void **)&context)) {
return CryptoX_Error;
}

Просмотреть файл

@ -15,7 +15,9 @@
#if defined(MAR_NSS)
#include "nss_secutil.h"
#include "cert.h"
#include "keyhi.h"
#include "cryptohi.h"
#define CryptoX_InvalidHandleValue NULL
#define CryptoX_ProviderHandle void*
@ -26,9 +28,9 @@
#ifdef __cplusplus
extern "C" {
#endif
CryptoX_Result NSS_LoadPublicKey(const char *certNickname,
SECKEYPublicKey **publicKey,
CERTCertificate **cert);
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 ,
@ -46,9 +48,8 @@ CryptoX_Result NSS_VerifySignature(VFYContext * const *ctx ,
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, certName, cert) \
NSS_LoadPublicKey(certName, publicKey, cert)
#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) \
@ -91,7 +92,7 @@ void CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey);
#define CryptoX_VerifyUpdate(aInputData, aBuf, aLen) \
CryptoMac_VerifyUpdate(aInputData, aBuf, aLen)
#define CryptoX_LoadPublicKey(aProviderHandle, aCertData, aDataSize, \
aPublicKey, aCertName, aCert) \
aPublicKey) \
CryptoMac_LoadPublicKey(aCertData, aPublicKey)
#define CryptoX_VerifySignature(aInputData, aPublicKey, aSignature, \
aSignatureLen) \
@ -111,8 +112,7 @@ CryptoX_Result CryptoAPI_InitCryptoContext(HCRYPTPROV *provider);
CryptoX_Result CryptoAPI_LoadPublicKey(HCRYPTPROV hProv,
BYTE *certData,
DWORD sizeOfCertData,
HCRYPTKEY *publicKey,
HCERTSTORE *cert);
HCRYPTKEY *publicKey);
CryptoX_Result CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash);
CryptoX_Result CryptoAPI_VerifyUpdate(HCRYPTHASH* hash,
BYTE *buf, DWORD len);
@ -133,10 +133,8 @@ CryptoX_Result CyprtoAPI_VerifySignature(HCRYPTHASH *hash,
#define CryptoX_FreeSignatureHandle(SignatureHandle)
#define CryptoX_VerifyUpdate(SignatureHandle, buf, len) \
CryptoAPI_VerifyUpdate(SignatureHandle, (BYTE *)(buf), len)
#define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, \
publicKey, certName, cert) \
CryptoAPI_LoadPublicKey(CryptoHandle, (BYTE*)(certData), \
dataSize, publicKey, cert)
#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) \
@ -163,8 +161,7 @@ CryptoX_Result CyprtoAPI_VerifySignature(HCRYPTHASH *hash,
CryptoX_Error
#define CryptoX_FreeSignatureHandle(SignatureHandle)
#define CryptoX_VerifyUpdate(SignatureHandle, buf, len) CryptoX_Error
#define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, \
publicKey, certName, cert) \
#define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, publicKey) \
CryptoX_Error
#define CryptoX_VerifySignature(hash, publicKey, signedData, len) CryptoX_Error
#define CryptoX_FreePublicKey(key) CryptoX_Error

Просмотреть файл

@ -17,6 +17,46 @@
#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,
@ -81,92 +121,8 @@ ReadAndUpdateVerifyContext(FILE *fp,
* 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.
* This is only used by the signmar program when used with arguments to verify
* a MAR. This should not be used to verify a MAR that will be extracted in the
* same operation by updater code. This function prints the error message if
* verification fails.
*
* @param pathToMARFile The path of the MAR file to verify.
* @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 certNames Pointer to the first element in an array of certificate names.
* Used only if compiled as NSS, specifies the certificate names
* @param certCount The number of elements in certData, certDataSizes, and certNames
* @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(const char *pathToMARFile,
const uint8_t * const *certData,
const uint32_t *certDataSizes,
const char * const *certNames,
uint32_t certCount) {
int rv;
CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue;
CryptoX_Certificate certs[MAX_SIGNATURES];
CryptoX_PublicKey keys[MAX_SIGNATURES];
FILE *fp;
uint32_t k;
memset(certs, 0, sizeof(certs));
memset(keys, 0, sizeof(keys));
if (!pathToMARFile || certCount == 0) {
fprintf(stderr, "ERROR: Invalid parameter specified.\n");
return CryptoX_Error;
}
fp = fopen(pathToMARFile, "rb");
if (!fp) {
fprintf(stderr, "ERROR: Could not open MAR file.\n");
return CryptoX_Error;
}
if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) {
fclose(fp);
fprintf(stderr, "ERROR: Could not init crytpo library.\n");
return CryptoX_Error;
}
/* Load the certs and keys */
for (k = 0; k < certCount; k++) {
if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData[k], certDataSizes[k],
&keys[k], certNames[k], &certs[k]))) {
fclose(fp);
fprintf(stderr, "ERROR: Could not load public key.\n");
return CryptoX_Error;
}
}
rv = mar_extract_and_verify_signatures_fp(fp, provider, keys, certCount);
fclose(fp);
/* Cleanup the allocated keys and certs */
for (k = 0; k < certCount; k++) {
if (keys[k]) {
CryptoX_FreePublicKey(&keys[k]);
}
if (certs[k]) {
CryptoX_FreeCertificate(&certs[k]);
}
}
return rv;
}
#ifdef XP_WIN
/**
* 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 pathToMARFile The path of the MAR file who's signature
* should be calculated
* @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
@ -175,17 +131,15 @@ mar_verify_signatures(const char *pathToMARFile,
* @return 0 on success
*/
int
mar_verify_signaturesW(MarFile *mar,
const uint8_t * const *certData,
const uint32_t *certDataSizes,
uint32_t certCount) {
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_Certificate certs[MAX_SIGNATURES];
CryptoX_PublicKey keys[MAX_SIGNATURES];
uint32_t k;
memset(certs, 0, sizeof(certs));
memset(keys, 0, sizeof(keys));
if (!mar || !certData || !certDataSizes || certCount == 0) {
@ -205,7 +159,7 @@ mar_verify_signaturesW(MarFile *mar,
for (k = 0; k < certCount; ++k) {
if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData[k], certDataSizes[k],
&keys[k], "", &certs[k]))) {
&keys[k]))) {
fprintf(stderr, "ERROR: Could not load public key.\n");
goto failure;
}
@ -219,15 +173,10 @@ failure:
if (keys[k]) {
CryptoX_FreePublicKey(&keys[k]);
}
if (certs[k]) {
CryptoX_FreeCertificate(&certs[k]);
}
}
return rv;
}
#endif
/**
* Extracts each signature from the specified MAR file,