mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 20:30:41 +00:00
Merge mozilla-central into mozilla-inbound
This commit is contained in:
commit
16f73f960a
@ -30,7 +30,7 @@ class nsDOMTokenList;
|
||||
class nsIControllers;
|
||||
class nsICSSDeclaration;
|
||||
class nsIDocument;
|
||||
class nsIDOMDOMStringMap;
|
||||
class nsDOMStringMap;
|
||||
class nsIDOMNamedNodeMap;
|
||||
class nsINodeInfo;
|
||||
class nsIURI;
|
||||
@ -348,7 +348,7 @@ public:
|
||||
* The .dataset attribute.
|
||||
* @see nsGenericHTMLElement::GetDataset
|
||||
*/
|
||||
nsIDOMDOMStringMap* mDataset; // [Weak]
|
||||
nsDOMStringMap* mDataset; // [Weak]
|
||||
|
||||
/**
|
||||
* SMIL Overridde style rules (for SMIL animation of CSS properties)
|
||||
|
@ -1237,7 +1237,7 @@ FragmentOrElement::MarkNodeChildren(nsINode* aNode)
|
||||
|
||||
nsEventListenerManager* elm = aNode->GetListenerManager(false);
|
||||
if (elm) {
|
||||
elm->UnmarkGrayJSListeners();
|
||||
elm->MarkForCC();
|
||||
}
|
||||
|
||||
if (aNode->HasProperties()) {
|
||||
|
@ -585,7 +585,7 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(WebSocket)
|
||||
bool isBlack = tmp->IsBlack();
|
||||
if (isBlack|| tmp->mKeepingAlive) {
|
||||
if (tmp->mListenerManager) {
|
||||
tmp->mListenerManager->UnmarkGrayJSListeners();
|
||||
tmp->mListenerManager->MarkForCC();
|
||||
}
|
||||
if (!isBlack && tmp->PreservingWrapper()) {
|
||||
xpc_UnmarkGrayObject(tmp->GetWrapperPreserveColor());
|
||||
|
@ -92,11 +92,13 @@ MarkUserDataHandler(void* aNode, nsIAtom* aKey, void* aValue, void* aData)
|
||||
static void
|
||||
MarkMessageManagers()
|
||||
{
|
||||
nsCOMPtr<nsIMessageBroadcaster> globalMM =
|
||||
nsCOMPtr<nsIMessageBroadcaster> strongGlobalMM =
|
||||
do_GetService("@mozilla.org/globalmessagemanager;1");
|
||||
if (!globalMM) {
|
||||
if (!strongGlobalMM) {
|
||||
return;
|
||||
}
|
||||
nsIMessageBroadcaster* globalMM = strongGlobalMM;
|
||||
strongGlobalMM = nullptr;
|
||||
|
||||
globalMM->MarkForCC();
|
||||
uint32_t childCount = 0;
|
||||
@ -107,7 +109,10 @@ MarkMessageManagers()
|
||||
if (!childMM) {
|
||||
continue;
|
||||
}
|
||||
nsCOMPtr<nsIMessageBroadcaster> windowMM = do_QueryInterface(childMM);
|
||||
nsCOMPtr<nsIMessageBroadcaster> strongWindowMM = do_QueryInterface(childMM);
|
||||
nsIMessageBroadcaster* windowMM = strongWindowMM;
|
||||
childMM = nullptr;
|
||||
strongWindowMM = nullptr;
|
||||
windowMM->MarkForCC();
|
||||
uint32_t tabChildCount = 0;
|
||||
windowMM->GetChildCount(&tabChildCount);
|
||||
@ -117,12 +122,15 @@ MarkMessageManagers()
|
||||
if (!childMM) {
|
||||
continue;
|
||||
}
|
||||
nsCOMPtr<nsIMessageSender> tabMM = do_QueryInterface(childMM);
|
||||
nsCOMPtr<nsIMessageSender> strongTabMM = do_QueryInterface(childMM);
|
||||
nsIMessageSender* tabMM = strongTabMM;
|
||||
childMM = nullptr;
|
||||
strongTabMM = nullptr;
|
||||
tabMM->MarkForCC();
|
||||
//XXX hack warning, but works, since we know that
|
||||
// callback is frameloader.
|
||||
mozilla::dom::ipc::MessageManagerCallback* cb =
|
||||
static_cast<nsFrameMessageManager*>(tabMM.get())->GetCallback();
|
||||
static_cast<nsFrameMessageManager*>(tabMM)->GetCallback();
|
||||
if (cb) {
|
||||
nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
|
||||
nsIDOMEventTarget* et = fl->GetTabChildGlobalAsEventTarget();
|
||||
@ -132,11 +140,33 @@ MarkMessageManagers()
|
||||
static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC();
|
||||
nsEventListenerManager* elm = et->GetListenerManager(false);
|
||||
if (elm) {
|
||||
elm->UnmarkGrayJSListeners();
|
||||
elm->MarkForCC();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nsFrameMessageManager::sParentProcessManager) {
|
||||
nsFrameMessageManager::sParentProcessManager->MarkForCC();
|
||||
uint32_t childCount = 0;
|
||||
nsFrameMessageManager::sParentProcessManager->GetChildCount(&childCount);
|
||||
for (uint32_t i = 0; i < childCount; ++i) {
|
||||
nsCOMPtr<nsIMessageListenerManager> childMM;
|
||||
nsFrameMessageManager::sParentProcessManager->
|
||||
GetChildAt(i, getter_AddRefs(childMM));
|
||||
if (!childMM) {
|
||||
continue;
|
||||
}
|
||||
nsIMessageListenerManager* child = childMM;
|
||||
childMM = nullptr;
|
||||
child->MarkForCC();
|
||||
}
|
||||
}
|
||||
if (nsFrameMessageManager::sSameProcessParentManager) {
|
||||
nsFrameMessageManager::sSameProcessParentManager->MarkForCC();
|
||||
}
|
||||
if (nsFrameMessageManager::sChildProcessManager) {
|
||||
nsFrameMessageManager::sChildProcessManager->MarkForCC();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -154,13 +184,13 @@ MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS,
|
||||
if (aCleanupJS) {
|
||||
nsEventListenerManager* elm = doc->GetListenerManager(false);
|
||||
if (elm) {
|
||||
elm->UnmarkGrayJSListeners();
|
||||
elm->MarkForCC();
|
||||
}
|
||||
nsCOMPtr<nsIDOMEventTarget> win = do_QueryInterface(doc->GetInnerWindow());
|
||||
if (win) {
|
||||
elm = win->GetListenerManager(false);
|
||||
if (elm) {
|
||||
elm->UnmarkGrayJSListeners();
|
||||
elm->MarkForCC();
|
||||
}
|
||||
static_cast<nsGlobalWindow*>(win.get())->UnmarkGrayTimers();
|
||||
}
|
||||
|
@ -3918,7 +3918,7 @@ ListenerEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aEntry,
|
||||
nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
|
||||
if (n && n->IsInDoc() &&
|
||||
nsCCUncollectableMarker::InGeneration(n->OwnerDoc()->GetMarkedCCGeneration())) {
|
||||
entry->mListenerManager->UnmarkGrayJSListeners();
|
||||
entry->mListenerManager->MarkForCC();
|
||||
}
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
|
@ -1718,7 +1718,7 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
|
||||
if (nsGenericElement::CanSkip(tmp, aRemovingAllowed)) {
|
||||
nsEventListenerManager* elm = tmp->GetListenerManager(false);
|
||||
if (elm) {
|
||||
elm->UnmarkGrayJSListeners();
|
||||
elm->MarkForCC();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsEventSource)
|
||||
bool isBlack = tmp->IsBlack();
|
||||
if (isBlack || tmp->mWaitingForOnStopRequest) {
|
||||
if (tmp->mListenerManager) {
|
||||
tmp->mListenerManager->UnmarkGrayJSListeners();
|
||||
tmp->mListenerManager->MarkForCC();
|
||||
}
|
||||
if (!isBlack && tmp->PreservingWrapper()) {
|
||||
xpc_UnmarkGrayObject(tmp->GetWrapperPreserveColor());
|
||||
|
@ -1326,5 +1326,8 @@ nsFrameMessageManager::MarkForCC()
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
xpc_TryUnmarkWrappedGrayObject(mListeners[i].mListener);
|
||||
}
|
||||
if (mRefCnt.IsPurple()) {
|
||||
mRefCnt.RemovePurple();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -583,7 +583,7 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
|
||||
bool isBlack = tmp->IsBlack();
|
||||
if (isBlack || tmp->mWaitingForOnStopRequest) {
|
||||
if (tmp->mListenerManager) {
|
||||
tmp->mListenerManager->UnmarkGrayJSListeners();
|
||||
tmp->mListenerManager->MarkForCC();
|
||||
}
|
||||
if (!isBlack && tmp->PreservingWrapper()) {
|
||||
xpc_UnmarkGrayObject(tmp->GetWrapperPreserveColor());
|
||||
|
@ -47,7 +47,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMEventTargetHelper)
|
||||
if (tmp->IsBlack()) {
|
||||
if (tmp->mListenerManager) {
|
||||
tmp->mListenerManager->UnmarkGrayJSListeners();
|
||||
tmp->mListenerManager->MarkForCC();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1179,7 +1179,7 @@ nsEventListenerManager::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
|
||||
}
|
||||
|
||||
void
|
||||
nsEventListenerManager::UnmarkGrayJSListeners()
|
||||
nsEventListenerManager::MarkForCC()
|
||||
{
|
||||
uint32_t count = mListeners.Length();
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
@ -1192,4 +1192,7 @@ nsEventListenerManager::UnmarkGrayJSListeners()
|
||||
xpc_TryUnmarkWrappedGrayObject(ls.mListener);
|
||||
}
|
||||
}
|
||||
if (mRefCnt.IsPurple()) {
|
||||
mRefCnt.RemovePurple();
|
||||
}
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ public:
|
||||
|
||||
size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
|
||||
|
||||
void UnmarkGrayJSListeners();
|
||||
void MarkForCC();
|
||||
|
||||
nsISupports* GetTarget() { return mTarget; }
|
||||
protected:
|
||||
|
@ -15,9 +15,11 @@ DOMCI_DATA(DOMStringMap, nsDOMStringMap)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStringMap)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStringMap)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mElement)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStringMap)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
// Check that mElement exists in case the unlink code is run more than once.
|
||||
if (tmp->mElement) {
|
||||
// Call back to element to null out weak reference to this object.
|
||||
@ -26,7 +28,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStringMap)
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMStringMap)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStringMap)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMDOMStringMap)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMStringMap)
|
||||
|
@ -13,16 +13,42 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
|
||||
class nsGenericHTMLElement;
|
||||
|
||||
class nsDOMStringMap : public nsIDOMDOMStringMap
|
||||
class nsDOMStringMap : public nsIDOMDOMStringMap,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIDOMDOMSTRINGMAP
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMStringMap)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMStringMap)
|
||||
|
||||
nsINode* GetParentObject()
|
||||
{
|
||||
return mElement;
|
||||
}
|
||||
|
||||
static nsDOMStringMap* FromSupports(nsISupports* aSupports)
|
||||
{
|
||||
nsIDOMDOMStringMap* map =
|
||||
static_cast<nsDOMStringMap*>(aSupports);
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsCOMPtr<nsIDOMDOMStringMap> map_qi =
|
||||
do_QueryInterface(aSupports);
|
||||
|
||||
// If this assertion fires the QI implementation for the object in
|
||||
// question doesn't use the nsIDOMDOMStringMap pointer as the
|
||||
// nsISupports pointer. That must be fixed, or we'll crash...
|
||||
NS_ASSERTION(map_qi == map, "Uh, fix QI!");
|
||||
}
|
||||
#endif
|
||||
|
||||
return static_cast<nsDOMStringMap*>(map);
|
||||
}
|
||||
|
||||
|
||||
nsDOMStringMap(nsGenericHTMLElement* aElement);
|
||||
|
||||
// GetDataPropList is not defined in IDL due to difficulty
|
||||
|
@ -344,18 +344,25 @@ nsGenericHTMLElement::SetAttribute(const nsAString& aName,
|
||||
aValue, true);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLElement::GetDataset(nsIDOMDOMStringMap** aDataset)
|
||||
already_AddRefed<nsDOMStringMap>
|
||||
nsGenericHTMLElement::Dataset()
|
||||
{
|
||||
nsDOMSlots *slots = DOMSlots();
|
||||
|
||||
if (!slots->mDataset) {
|
||||
// mDataset is a weak reference so assignment will not AddRef.
|
||||
// AddRef is called before assigning to out parameter.
|
||||
// AddRef is called before returning the pointer.
|
||||
slots->mDataset = new nsDOMStringMap(this);
|
||||
}
|
||||
|
||||
NS_ADDREF(*aDataset = slots->mDataset);
|
||||
NS_ADDREF(slots->mDataset);
|
||||
return slots->mDataset;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLElement::GetDataset(nsIDOMDOMStringMap** aDataset)
|
||||
{
|
||||
*aDataset = Dataset().get();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -219,6 +219,7 @@ public:
|
||||
nsresult GetIsContentEditable(bool* aContentEditable);
|
||||
nsresult SetContentEditable(const nsAString &aContentEditable);
|
||||
nsresult GetDataset(nsIDOMDOMStringMap** aDataset);
|
||||
already_AddRefed<nsDOMStringMap> Dataset();
|
||||
// Callback for destructor of of dataset to ensure to null out weak pointer.
|
||||
nsresult ClearDataset();
|
||||
nsresult GetContextMenu(nsIDOMHTMLMenuElement** aContextMenu);
|
||||
|
@ -8482,13 +8482,13 @@ NS_IMETHODIMP
|
||||
nsDOMStringMapSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
|
||||
JSObject *globalObj, JSObject **parentObj)
|
||||
{
|
||||
*parentObj = globalObj;
|
||||
nsDOMStringMap* map = nsDOMStringMap::FromSupports(nativeObj);
|
||||
nsINode* native_parent = map->GetParentObject();
|
||||
if (!native_parent) {
|
||||
return nsDOMClassInfo::PreCreate(nativeObj, cx, globalObj, parentObj);
|
||||
}
|
||||
|
||||
nsDOMStringMap* dataset = static_cast<nsDOMStringMap*>(nativeObj);
|
||||
|
||||
// Parent the string map to its element.
|
||||
nsINode* element = dataset->GetElement();
|
||||
return WrapNativeParent(cx, globalObj, element, element, parentObj);
|
||||
return WrapNativeParent(cx, globalObj, native_parent, parentObj);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1269,7 +1269,7 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
|
||||
}
|
||||
nsEventListenerManager* elm = tmp->GetListenerManager(false);
|
||||
if (elm) {
|
||||
elm->UnmarkGrayJSListeners();
|
||||
elm->MarkForCC();
|
||||
}
|
||||
tmp->UnmarkGrayTimers();
|
||||
return true;
|
||||
|
@ -62,6 +62,7 @@ x-imap4-modified-utf7.notForOutgoing = true
|
||||
us-ascii.notForOutgoing = true
|
||||
iso-8859-6-e.notForOutgoing = true
|
||||
iso-8859-6-i.notForOutgoing = true
|
||||
ibm864.notForOutgoing = true
|
||||
ibm869.notForOutgoing = true
|
||||
ibm1125.notForOutgoing = true
|
||||
ibm1131.notForOutgoing = true
|
||||
|
@ -485,6 +485,7 @@ customIncludes = [
|
||||
'nsSVGStylableElement.h',
|
||||
'nsHTMLDocument.h',
|
||||
'nsDOMQS.h',
|
||||
'nsDOMStringMap.h',
|
||||
'mozilla/dom/ImageData.h'
|
||||
]
|
||||
|
||||
@ -849,6 +850,11 @@ customMethodCalls = {
|
||||
' self->SetInnerHTML(arg0, error);\n'
|
||||
' rv = error.ErrorCode();'
|
||||
},
|
||||
'nsIDOMHTMLElement_GetDataset': {
|
||||
'thisType' : 'nsGenericHTMLElement',
|
||||
'code': ' nsRefPtr<nsDOMStringMap> result = self->Dataset();',
|
||||
'canFail': False
|
||||
},
|
||||
'nsIDOMElementCSSInlineStyle_GetStyle': {
|
||||
'thisType': 'nsStyledElement',
|
||||
'code': ' /* XXXbz MathML elements inherit from nsStyledElement but\n'
|
||||
|
@ -22,6 +22,7 @@
|
||||
#endif
|
||||
|
||||
#include "nss_secutil.h"
|
||||
#include "base64.h"
|
||||
|
||||
/**
|
||||
* Initializes the NSS context.
|
||||
@ -109,22 +110,26 @@ NSSSignBegin(const char *certName,
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the passed buffer to the file fp and updates the signature context.
|
||||
* Writes the passed buffer to the file fp and updates the signature contexts.
|
||||
*
|
||||
* @param fpDest The file pointer to write to.
|
||||
* @param buffer The buffer to write.
|
||||
* @param size The size of the buffer to write.
|
||||
* @param ctx The signature context.
|
||||
* @param fpDest The file pointer to write to.
|
||||
* @param buffer The buffer to write.
|
||||
* @param size The size of the buffer to write.
|
||||
* @param ctxs Pointer to the first element in an array of signature
|
||||
* contexts to update.
|
||||
* @param ctxCount The number of signature contexts pointed to by ctxs
|
||||
* @param err The name of what is being written to in case of error.
|
||||
* @return 0 on success
|
||||
* -2 on write error
|
||||
* -3 on signature update error
|
||||
*/
|
||||
int
|
||||
WriteAndUpdateSignature(FILE *fpDest, void *buffer,
|
||||
uint32_t size, SGNContext *ctx,
|
||||
const char *err)
|
||||
WriteAndUpdateSignatures(FILE *fpDest, void *buffer,
|
||||
uint32_t size, SGNContext **ctxs,
|
||||
uint32_t ctxCount,
|
||||
const char *err)
|
||||
{
|
||||
uint32_t k;
|
||||
if (!size) {
|
||||
return 0;
|
||||
}
|
||||
@ -133,9 +138,12 @@ WriteAndUpdateSignature(FILE *fpDest, void *buffer,
|
||||
fprintf(stderr, "ERROR: Could not write %s\n", err);
|
||||
return -2;
|
||||
}
|
||||
if (SGN_Update(ctx, (const unsigned char *)buffer, size) != SECSuccess) {
|
||||
fprintf(stderr, "ERROR: Could not update signature context for %s\n", err);
|
||||
return -3;
|
||||
|
||||
for (k = 0; k < ctxCount; ++k) {
|
||||
if (SGN_Update(ctxs[k], buffer, size) != SECSuccess) {
|
||||
fprintf(stderr, "ERROR: Could not update signature context for %s\n", err);
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -168,13 +176,15 @@ AdjustIndexContentOffsets(char *indexBuf, uint32_t indexLength, uint32_t offsetA
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from fpSrc, writes it to fpDest, and updates the signature context.
|
||||
* Reads from fpSrc, writes it to fpDest, and updates the signature contexts.
|
||||
*
|
||||
* @param fpSrc The file pointer to read from.
|
||||
* @param fpDest The file pointer to write to.
|
||||
* @param buffer The buffer to write.
|
||||
* @param size The size of the buffer to write.
|
||||
* @param ctx The signature context.
|
||||
* @param fpSrc The file pointer to read from.
|
||||
* @param fpDest The file pointer to write to.
|
||||
* @param buffer The buffer to write.
|
||||
* @param size The size of the buffer to write.
|
||||
* @param ctxs Pointer to the first element in an array of signature
|
||||
* contexts to update.
|
||||
* @param ctxCount The number of signature contexts pointed to by ctxs
|
||||
* @param err The name of what is being written to in case of error.
|
||||
* @return 0 on success
|
||||
* -1 on read error
|
||||
@ -182,9 +192,10 @@ AdjustIndexContentOffsets(char *indexBuf, uint32_t indexLength, uint32_t offsetA
|
||||
* -3 on signature update error
|
||||
*/
|
||||
int
|
||||
ReadWriteAndUpdateSignature(FILE *fpSrc, FILE *fpDest, void *buffer,
|
||||
uint32_t size, SGNContext *ctx,
|
||||
const char *err)
|
||||
ReadWriteAndUpdateSignatures(FILE *fpSrc, FILE *fpDest, void *buffer,
|
||||
uint32_t size, SGNContext **ctxs,
|
||||
uint32_t ctxCount,
|
||||
const char *err)
|
||||
{
|
||||
if (!size) {
|
||||
return 0;
|
||||
@ -195,7 +206,7 @@ ReadWriteAndUpdateSignature(FILE *fpSrc, FILE *fpDest, void *buffer,
|
||||
return -1;
|
||||
}
|
||||
|
||||
return WriteAndUpdateSignature(fpDest, buffer, size, ctx, err);
|
||||
return WriteAndUpdateSignatures(fpDest, buffer, size, ctxs, ctxCount, err);
|
||||
}
|
||||
|
||||
|
||||
@ -261,7 +272,7 @@ strip_signature_block(const char *src, const char * dest)
|
||||
|
||||
fpSrc = fopen(src, "rb");
|
||||
if (!fpSrc) {
|
||||
fprintf(stderr, "ERROR: could not open source file: %s\n", dest);
|
||||
fprintf(stderr, "ERROR: could not open source file: %s\n", src);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
@ -468,39 +479,369 @@ failure:
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out a copy of the MAR at src but with an embedded signature.
|
||||
* Extracts a signature from a MAR file, base64 encodes it, and writes it out
|
||||
*
|
||||
* @param src The path of the source MAR file
|
||||
* @param sigIndex The index of the signature to extract
|
||||
* @param dest The path of file to write the signature to
|
||||
* @return 0 on success
|
||||
* -1 on error
|
||||
*/
|
||||
int
|
||||
extract_signature(const char *src, uint32_t sigIndex, const char * dest)
|
||||
{
|
||||
FILE *fpSrc = NULL, *fpDest = NULL;
|
||||
uint32_t i;
|
||||
uint32_t signatureCount;
|
||||
uint32_t signatureLen;
|
||||
uint8_t *extractedSignature = NULL;
|
||||
char *base64Encoded = NULL;
|
||||
int rv = -1;
|
||||
if (!src || !dest) {
|
||||
fprintf(stderr, "ERROR: Invalid parameter passed in.\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
fpSrc = fopen(src, "rb");
|
||||
if (!fpSrc) {
|
||||
fprintf(stderr, "ERROR: could not open source file: %s\n", src);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
fpDest = fopen(dest, "wb");
|
||||
if (!fpDest) {
|
||||
fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Skip to the start of the signature block */
|
||||
if (fseeko(fpSrc, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
|
||||
fprintf(stderr, "ERROR: could not seek to signature block\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Get the number of signatures */
|
||||
if (fread(&signatureCount, sizeof(signatureCount), 1, fpSrc) != 1) {
|
||||
fprintf(stderr, "ERROR: could not read signature count\n");
|
||||
goto failure;
|
||||
}
|
||||
signatureCount = ntohl(signatureCount);
|
||||
if (sigIndex >= signatureCount) {
|
||||
fprintf(stderr, "ERROR: Signature index was out of range\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Skip to the correct signature */
|
||||
for (i = 0; i <= sigIndex; i++) {
|
||||
/* skip past the signature algorithm ID */
|
||||
if (fseeko(fpSrc, sizeof(uint32_t), SEEK_CUR)) {
|
||||
fprintf(stderr, "ERROR: Could not seek past sig algorithm ID.\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Get the signature length */
|
||||
if (fread(&signatureLen, sizeof(signatureLen), 1, fpSrc) != 1) {
|
||||
fprintf(stderr, "ERROR: could not read signature length\n");
|
||||
goto failure;
|
||||
}
|
||||
signatureLen = ntohl(signatureLen);
|
||||
|
||||
/* Get the signature */
|
||||
extractedSignature = malloc(signatureLen);
|
||||
if (fread(extractedSignature, signatureLen, 1, fpSrc) != 1) {
|
||||
fprintf(stderr, "ERROR: could not read signature\n");
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
base64Encoded = BTOA_DataToAscii(extractedSignature, signatureLen);
|
||||
if (!base64Encoded) {
|
||||
fprintf(stderr, "ERROR: could not obtain base64 encoded data\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (fwrite(base64Encoded, strlen(base64Encoded), 1, fpDest) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not write base64 encoded string\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
failure:
|
||||
if (base64Encoded) {
|
||||
PORT_Free(base64Encoded);
|
||||
}
|
||||
|
||||
if (extractedSignature) {
|
||||
free(extractedSignature);
|
||||
}
|
||||
|
||||
if (fpSrc) {
|
||||
fclose(fpSrc);
|
||||
}
|
||||
|
||||
if (fpDest) {
|
||||
fclose(fpDest);
|
||||
}
|
||||
|
||||
if (rv) {
|
||||
remove(dest);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a base64 encoded signature into a MAR file
|
||||
*
|
||||
* @param src The path of the source MAR file
|
||||
* @param sigIndex The index of the signature to import
|
||||
* @param base64SigFile A file which contains the signature to import
|
||||
* @param dest The path of the destination MAR file with replaced signature
|
||||
* @return 0 on success
|
||||
* -1 on error
|
||||
*/
|
||||
int
|
||||
import_signature(const char *src, uint32_t sigIndex,
|
||||
const char *base64SigFile, const char *dest)
|
||||
{
|
||||
int rv = -1;
|
||||
FILE *fpSrc, *fpDest, *fpSigFile;
|
||||
uint32_t i;
|
||||
uint32_t signatureCount, signatureLen, signatureAlgorithmID,
|
||||
numChunks, leftOver;
|
||||
char buf[BLOCKSIZE];
|
||||
uint64_t sizeOfSrcMAR, sizeOfBase64EncodedFile;
|
||||
char *passedInSignatureB64 = NULL;
|
||||
uint8_t *passedInSignatureRaw = NULL;
|
||||
uint8_t *extractedMARSignature = NULL;
|
||||
unsigned int passedInSignatureLenRaw;
|
||||
|
||||
if (!src || !dest) {
|
||||
fprintf(stderr, "ERROR: Invalid parameter passed in.\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
fpSrc = fopen(src, "rb");
|
||||
if (!fpSrc) {
|
||||
fprintf(stderr, "ERROR: could not open source file: %s\n", src);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
fpDest = fopen(dest, "wb");
|
||||
if (!fpDest) {
|
||||
fprintf(stderr, "ERROR: could not open dest file: %s\n", dest);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
fpSigFile = fopen(base64SigFile , "rb");
|
||||
if (!fpSigFile) {
|
||||
fprintf(stderr, "ERROR: could not open sig file: %s\n", base64SigFile);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Get the src file size */
|
||||
if (fseeko(fpSrc, 0, SEEK_END)) {
|
||||
fprintf(stderr, "ERROR: Could not seek to end of src file.\n");
|
||||
goto failure;
|
||||
}
|
||||
sizeOfSrcMAR = ftello(fpSrc);
|
||||
if (fseeko(fpSrc, 0, SEEK_SET)) {
|
||||
fprintf(stderr, "ERROR: Could not seek to start of src file.\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Get the sig file size */
|
||||
if (fseeko(fpSigFile, 0, SEEK_END)) {
|
||||
fprintf(stderr, "ERROR: Could not seek to end of sig file.\n");
|
||||
goto failure;
|
||||
}
|
||||
sizeOfBase64EncodedFile= ftello(fpSigFile);
|
||||
if (fseeko(fpSigFile, 0, SEEK_SET)) {
|
||||
fprintf(stderr, "ERROR: Could not seek to start of sig file.\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Read in the base64 encoded signature to import */
|
||||
passedInSignatureB64 = malloc(sizeOfBase64EncodedFile + 1);
|
||||
passedInSignatureB64[sizeOfBase64EncodedFile] = '\0';
|
||||
if (fread(passedInSignatureB64, sizeOfBase64EncodedFile, 1, fpSigFile) != 1) {
|
||||
fprintf(stderr, "ERROR: Could read b64 sig file.\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Decode the base64 encoded data */
|
||||
passedInSignatureRaw = ATOB_AsciiToData(passedInSignatureB64, &passedInSignatureLenRaw);
|
||||
if (!passedInSignatureRaw) {
|
||||
fprintf(stderr, "ERROR: could not obtain base64 decoded data\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Read everything up until the signature block offset and write it out */
|
||||
if (ReadAndWrite(fpSrc, fpDest, buf,
|
||||
SIGNATURE_BLOCK_OFFSET, "signature block offset")) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Get the number of signatures */
|
||||
if (ReadAndWrite(fpSrc, fpDest, &signatureCount,
|
||||
sizeof(signatureCount), "signature count")) {
|
||||
goto failure;
|
||||
}
|
||||
signatureCount = ntohl(signatureCount);
|
||||
if (signatureCount > MAX_SIGNATURES) {
|
||||
fprintf(stderr, "ERROR: Signature count was out of range\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (sigIndex >= signatureCount) {
|
||||
fprintf(stderr, "ERROR: Signature index was out of range\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Read and write the whole signature block, but if we reach the
|
||||
signature offset, then we should replace it with the specified
|
||||
base64 decoded signature */
|
||||
for (i = 0; i < signatureCount; i++) {
|
||||
/* Read/Write the signature algorithm ID */
|
||||
if (ReadAndWrite(fpSrc, fpDest,
|
||||
&signatureAlgorithmID,
|
||||
sizeof(signatureAlgorithmID), "sig algorithm ID")) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Read/Write the signature length */
|
||||
if (ReadAndWrite(fpSrc, fpDest,
|
||||
&signatureLen, sizeof(signatureLen), "sig length")) {
|
||||
goto failure;
|
||||
}
|
||||
signatureLen = ntohl(signatureLen);
|
||||
|
||||
/* Get the signature */
|
||||
if (extractedMARSignature) {
|
||||
free(extractedMARSignature);
|
||||
}
|
||||
extractedMARSignature = malloc(signatureLen);
|
||||
|
||||
if (sigIndex == i) {
|
||||
if (passedInSignatureLenRaw != signatureLen) {
|
||||
fprintf(stderr, "ERROR: Signature length must be the same\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (fread(extractedMARSignature, signatureLen, 1, fpSrc) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not read signature\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (fwrite(passedInSignatureRaw, passedInSignatureLenRaw,
|
||||
1, fpDest) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not write signature\n");
|
||||
goto failure;
|
||||
}
|
||||
} else {
|
||||
if (ReadAndWrite(fpSrc, fpDest,
|
||||
extractedMARSignature, signatureLen, "signature")) {
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We replaced the signature so let's just skip past the rest o the
|
||||
file. */
|
||||
numChunks = (sizeOfSrcMAR - ftello(fpSrc)) / BLOCKSIZE;
|
||||
leftOver = (sizeOfSrcMAR - ftello(fpSrc)) % BLOCKSIZE;
|
||||
|
||||
/* Read each file and write it to the MAR file */
|
||||
for (i = 0; i < numChunks; ++i) {
|
||||
if (ReadAndWrite(fpSrc, fpDest, buf, BLOCKSIZE, "content block")) {
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
if (ReadAndWrite(fpSrc, fpDest, buf, leftOver, "left over content block")) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
|
||||
failure:
|
||||
|
||||
if (fpSrc) {
|
||||
fclose(fpSrc);
|
||||
}
|
||||
|
||||
if (fpDest) {
|
||||
fclose(fpDest);
|
||||
}
|
||||
|
||||
if (fpSigFile) {
|
||||
fclose(fpSigFile);
|
||||
}
|
||||
|
||||
if (rv) {
|
||||
remove(dest);
|
||||
}
|
||||
|
||||
if (extractedMARSignature) {
|
||||
free(extractedMARSignature);
|
||||
}
|
||||
|
||||
if (passedInSignatureB64) {
|
||||
free(passedInSignatureB64);
|
||||
}
|
||||
|
||||
if (passedInSignatureRaw) {
|
||||
PORT_Free(passedInSignatureRaw);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out a copy of the MAR at src but with embedded signatures.
|
||||
* The passed in MAR file must not already be signed or an error will
|
||||
* be returned.
|
||||
*
|
||||
* @param NSSConfigDir The NSS directory containing the private key for signing
|
||||
* @param certName The nickname of the certificate to use for signing
|
||||
* @param src The path of the source MAR file to sign
|
||||
* @param dest The path of the MAR file to write out that is signed
|
||||
* @param NSSConfigDir The NSS directory containing the private key for signing
|
||||
* @param certNames The nicknames of the certificate to use for signing
|
||||
* @param certCount The number of certificate names contained in certNames.
|
||||
* One signature will be produced for each certificate.
|
||||
* @param src The path of the source MAR file to sign
|
||||
* @param dest The path of the MAR file to write out that is signed
|
||||
* @return 0 on success
|
||||
* -1 on error
|
||||
*/
|
||||
int
|
||||
mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
const char *certName,
|
||||
const char * const *certNames,
|
||||
uint32_t certCount,
|
||||
const char *src,
|
||||
const char *dest)
|
||||
{
|
||||
uint32_t offsetToIndex, dstOffsetToIndex, indexLength,
|
||||
numSignatures = 0, signatureLength, leftOver,
|
||||
signatureAlgorithmID, signatureSectionLength;
|
||||
numSignatures = 0, leftOver,
|
||||
signatureAlgorithmID, signatureSectionLength = 0;
|
||||
uint32_t signatureLengths[MAX_SIGNATURES];
|
||||
int64_t oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR,
|
||||
signaturePlaceholderOffset, numBytesToCopy,
|
||||
numChunks, i;
|
||||
FILE *fpSrc = NULL, *fpDest = NULL;
|
||||
int rv = -1, hasSignatureBlock;
|
||||
SGNContext *ctx = NULL;
|
||||
SECItem secItem;
|
||||
SGNContext *ctxs[MAX_SIGNATURES];
|
||||
SECItem secItems[MAX_SIGNATURES];
|
||||
char buf[BLOCKSIZE];
|
||||
SECKEYPrivateKey *privKey = NULL;
|
||||
CERTCertificate *cert = NULL;
|
||||
SECKEYPrivateKey *privKeys[MAX_SIGNATURES];
|
||||
CERTCertificate *certs[MAX_SIGNATURES];
|
||||
char *indexBuf = NULL, *indexBufLoc;
|
||||
uint32_t k;
|
||||
|
||||
if (!NSSConfigDir || !certName || !src || !dest) {
|
||||
memset(signatureLengths, 0, sizeof(signatureLengths));
|
||||
memset(ctxs, 0, sizeof(ctxs));
|
||||
memset(secItems, 0, sizeof(secItems));
|
||||
memset(privKeys, 0, sizeof(privKeys));
|
||||
memset(certs, 0, sizeof(certs));
|
||||
|
||||
if (!NSSConfigDir || !certNames || certCount == 0 || !src || !dest) {
|
||||
fprintf(stderr, "ERROR: Invalid parameter passed in.\n");
|
||||
return -1;
|
||||
}
|
||||
@ -512,14 +853,9 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
|
||||
PK11_SetPasswordFunc(SECU_GetModulePassword);
|
||||
|
||||
if (NSSSignBegin(certName, &ctx, &privKey, &cert, &signatureLength)) {
|
||||
fprintf(stderr, "ERROR: NSSSignBegin failed\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
fpSrc = fopen(src, "rb");
|
||||
if (!fpSrc) {
|
||||
fprintf(stderr, "ERROR: could not open source file: %s\n", dest);
|
||||
fprintf(stderr, "ERROR: could not open source file: %s\n", src);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
@ -535,10 +871,18 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
goto failure;
|
||||
}
|
||||
|
||||
for (k = 0; k < certCount; k++) {
|
||||
if (NSSSignBegin(certNames[k], &ctxs[k], &privKeys[k],
|
||||
&certs[k], &signatureLengths[k])) {
|
||||
fprintf(stderr, "ERROR: NSSSignBegin failed\n");
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
/* MAR ID */
|
||||
if (ReadWriteAndUpdateSignature(fpSrc, fpDest,
|
||||
buf, MAR_ID_SIZE,
|
||||
ctx, "MAR ID")) {
|
||||
if (ReadWriteAndUpdateSignatures(fpSrc, fpDest,
|
||||
buf, MAR_ID_SIZE,
|
||||
ctxs, certCount, "MAR ID")) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
@ -574,14 +918,15 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Get the num signatures in the source file so we know what to skip over */
|
||||
/* Get the num signatures in the source file */
|
||||
if (fread(&numSignatures, sizeof(numSignatures), 1, fpSrc) != 1) {
|
||||
fprintf(stderr, "ERROR: Could read num signatures\n");
|
||||
goto failure;
|
||||
}
|
||||
numSignatures = ntohl(numSignatures);
|
||||
|
||||
/* We do not support resigning */
|
||||
/* We do not support resigning, if you have multiple signatures,
|
||||
you must add them all at the same time. */
|
||||
if (numSignatures) {
|
||||
fprintf(stderr, "ERROR: MAR is already signed\n");
|
||||
goto failure;
|
||||
@ -595,10 +940,12 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Write out the new offset to the index */
|
||||
signatureSectionLength = sizeof(signatureAlgorithmID) +
|
||||
sizeof(signatureLength) +
|
||||
signatureLength;
|
||||
/* Calculate the total signature block length */
|
||||
for (k = 0; k < certCount; k++) {
|
||||
signatureSectionLength += sizeof(signatureAlgorithmID) +
|
||||
sizeof(signatureLengths[k]) +
|
||||
signatureLengths[k];
|
||||
}
|
||||
dstOffsetToIndex = offsetToIndex;
|
||||
if (!hasSignatureBlock) {
|
||||
dstOffsetToIndex += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
|
||||
@ -607,8 +954,9 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
|
||||
/* Write out the index offset */
|
||||
dstOffsetToIndex = htonl(dstOffsetToIndex);
|
||||
if (WriteAndUpdateSignature(fpDest, &dstOffsetToIndex,
|
||||
sizeof(dstOffsetToIndex), ctx, "index offset")) {
|
||||
if (WriteAndUpdateSignatures(fpDest, &dstOffsetToIndex,
|
||||
sizeof(dstOffsetToIndex), ctxs, certCount,
|
||||
"index offset")) {
|
||||
goto failure;
|
||||
}
|
||||
dstOffsetToIndex = ntohl(dstOffsetToIndex);
|
||||
@ -621,47 +969,52 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
|
||||
/* Write out the MAR size */
|
||||
sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
|
||||
if (WriteAndUpdateSignature(fpDest, &sizeOfEntireMAR,
|
||||
sizeof(sizeOfEntireMAR), ctx, "size of MAR")) {
|
||||
if (WriteAndUpdateSignatures(fpDest, &sizeOfEntireMAR,
|
||||
sizeof(sizeOfEntireMAR), ctxs, certCount,
|
||||
"size of MAR")) {
|
||||
goto failure;
|
||||
}
|
||||
sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
|
||||
|
||||
/* Write out the number of signatures, for now only 1 is supported */
|
||||
numSignatures = 1;
|
||||
/* Write out the number of signatures */
|
||||
numSignatures = certCount;
|
||||
numSignatures = htonl(numSignatures);
|
||||
if (WriteAndUpdateSignature(fpDest, &numSignatures,
|
||||
sizeof(numSignatures), ctx, "num signatures")) {
|
||||
if (WriteAndUpdateSignatures(fpDest, &numSignatures,
|
||||
sizeof(numSignatures), ctxs, certCount,
|
||||
"num signatures")) {
|
||||
goto failure;
|
||||
}
|
||||
numSignatures = ntohl(numSignatures);
|
||||
|
||||
/* Write out the signature ID, for now only an ID of 1 is supported */
|
||||
signatureAlgorithmID = htonl(1);
|
||||
if (WriteAndUpdateSignature(fpDest, &signatureAlgorithmID,
|
||||
sizeof(signatureAlgorithmID),
|
||||
ctx, "num signatures")) {
|
||||
goto failure;
|
||||
}
|
||||
signatureAlgorithmID = ntohl(signatureAlgorithmID);
|
||||
|
||||
/* Write out the signature length */
|
||||
signatureLength = htonl(signatureLength);
|
||||
if (WriteAndUpdateSignature(fpDest, &signatureLength,
|
||||
sizeof(signatureLength),
|
||||
ctx, "signature length")) {
|
||||
goto failure;
|
||||
}
|
||||
signatureLength = ntohl(signatureLength);
|
||||
|
||||
/* Write out a placeholder for the signature, we'll come back to this later
|
||||
*** THIS IS NOT SIGNED because it is a placeholder that will be replaced
|
||||
below, plus it is going to be the signature itself. *** */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
signaturePlaceholderOffset = ftello(fpDest);
|
||||
if (fwrite(buf, signatureLength, 1, fpDest) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not write signature length\n");
|
||||
goto failure;
|
||||
|
||||
for (k = 0; k < certCount; k++) {
|
||||
/* Write out the signature algorithm ID, Only an ID of 1 is supported */
|
||||
signatureAlgorithmID = htonl(1);
|
||||
if (WriteAndUpdateSignatures(fpDest, &signatureAlgorithmID,
|
||||
sizeof(signatureAlgorithmID),
|
||||
ctxs, certCount, "num signatures")) {
|
||||
goto failure;
|
||||
}
|
||||
signatureAlgorithmID = ntohl(signatureAlgorithmID);
|
||||
|
||||
/* Write out the signature length */
|
||||
signatureLengths[k] = htonl(signatureLengths[k]);
|
||||
if (WriteAndUpdateSignatures(fpDest, &signatureLengths[k],
|
||||
sizeof(signatureLengths[k]),
|
||||
ctxs, certCount, "signature length")) {
|
||||
goto failure;
|
||||
}
|
||||
signatureLengths[k] = ntohl(signatureLengths[k]);
|
||||
|
||||
/* Write out a placeholder for the signature, we'll come back to this later
|
||||
*** THIS IS NOT SIGNED because it is a placeholder that will be replaced
|
||||
below, plus it is going to be the signature itself. *** */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (fwrite(buf, signatureLengths[k], 1, fpDest) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not write signature length\n");
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write out the rest of the MAR excluding the index header and index
|
||||
@ -677,21 +1030,24 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
|
||||
/* Read each file and write it to the MAR file */
|
||||
for (i = 0; i < numChunks; ++i) {
|
||||
if (ReadWriteAndUpdateSignature(fpSrc, fpDest, buf,
|
||||
BLOCKSIZE, ctx, "content block")) {
|
||||
if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, buf,
|
||||
BLOCKSIZE, ctxs, certCount,
|
||||
"content block")) {
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write out the left over */
|
||||
if (ReadWriteAndUpdateSignature(fpSrc, fpDest, buf,
|
||||
leftOver, ctx, "left over content block")) {
|
||||
if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, buf,
|
||||
leftOver, ctxs, certCount,
|
||||
"left over content block")) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Length of the index */
|
||||
if (ReadWriteAndUpdateSignature(fpSrc, fpDest, &indexLength,
|
||||
sizeof(indexLength), ctx, "index length")) {
|
||||
if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, &indexLength,
|
||||
sizeof(indexLength), ctxs, certCount,
|
||||
"index length")) {
|
||||
goto failure;
|
||||
}
|
||||
indexLength = ntohl(indexLength);
|
||||
@ -714,8 +1070,8 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
signatureSectionLength);
|
||||
}
|
||||
|
||||
if (WriteAndUpdateSignature(fpDest, indexBuf,
|
||||
indexLength, ctx, "index")) {
|
||||
if (WriteAndUpdateSignatures(fpDest, indexBuf,
|
||||
indexLength, ctxs, certCount, "index")) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
@ -725,14 +1081,16 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Get the signature */
|
||||
if (SGN_End(ctx, &secItem) != SECSuccess) {
|
||||
fprintf(stderr, "ERROR: Could not end signature context\n");
|
||||
goto failure;
|
||||
}
|
||||
if (signatureLength != secItem.len) {
|
||||
fprintf(stderr, "ERROR: Signature is not the expected length\n");
|
||||
goto failure;
|
||||
for (k = 0; k < certCount; k++) {
|
||||
/* Get the signature */
|
||||
if (SGN_End(ctxs[k], &secItems[k]) != SECSuccess) {
|
||||
fprintf(stderr, "ERROR: Could not end signature context\n");
|
||||
goto failure;
|
||||
}
|
||||
if (signatureLengths[k] != secItems[k].len) {
|
||||
fprintf(stderr, "ERROR: Signature is not the expected length\n");
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get back to the location of the signature placeholder */
|
||||
@ -741,11 +1099,20 @@ mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Write out the calculated signature.
|
||||
*** THIS IS NOT SIGNED because it is the signature itself. *** */
|
||||
if (fwrite(secItem.data, secItem.len, 1, fpDest) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not write signature\n");
|
||||
goto failure;
|
||||
for (k = 0; k < certCount; k++) {
|
||||
/* Skip to the position of the next signature */
|
||||
if (fseeko(fpDest, sizeof(signatureAlgorithmID) +
|
||||
sizeof(signatureLengths[k]), SEEK_CUR)) {
|
||||
fprintf(stderr, "ERROR: Could not seek to signature offset\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Write out the calculated signature.
|
||||
*** THIS IS NOT SIGNED because it is the signature itself. *** */
|
||||
if (fwrite(secItems[k].data, secItems[k].len, 1, fpDest) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not write signature\n");
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
@ -766,20 +1133,26 @@ failure:
|
||||
free(indexBuf);
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
SGN_DestroyContext(ctx, PR_TRUE);
|
||||
}
|
||||
/* Cleanup */
|
||||
for (k = 0; k < certCount; k++) {
|
||||
if (ctxs[k]) {
|
||||
SGN_DestroyContext(ctxs[k], PR_TRUE);
|
||||
}
|
||||
|
||||
if (cert) {
|
||||
CERT_DestroyCertificate(cert);
|
||||
}
|
||||
if (certs[k]) {
|
||||
CERT_DestroyCertificate(certs[k]);
|
||||
}
|
||||
|
||||
if (privKey) {
|
||||
SECKEY_DestroyPrivateKey(privKey);
|
||||
if (privKeys[k]) {
|
||||
SECKEY_DestroyPrivateKey(privKeys[k]);
|
||||
}
|
||||
|
||||
SECITEM_FreeItem(&secItems[k], PR_FALSE);
|
||||
}
|
||||
|
||||
if (rv) {
|
||||
remove(dest);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -15,6 +15,15 @@
|
||||
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
|
||||
PR_STATIC_ASSERT(MAX_SIGNATURES <= 9);
|
||||
|
||||
struct ProductInformationBlock {
|
||||
const char *MARChannelID;
|
||||
const char *productVersion;
|
||||
@ -123,22 +132,30 @@ int mar_create(const char *dest,
|
||||
int mar_extract(const char *path);
|
||||
|
||||
/**
|
||||
* Verifies the embedded signature for the specified mar file.
|
||||
* 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 The certificate file data.
|
||||
* @param sizeOfCertData The size of the cert data.
|
||||
* @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
|
||||
*/
|
||||
#ifdef XP_WIN
|
||||
int mar_verify_signatureW(MarFile *mar,
|
||||
const char *certData,
|
||||
uint32_t sizeOfCertData);
|
||||
int mar_verify_signaturesW(MarFile *mar,
|
||||
const uint8_t * const *certData,
|
||||
const uint32_t *certDataSizes,
|
||||
uint32_t certCount);
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -40,24 +40,36 @@ int get_mar_file_info(const char *path,
|
||||
uint32_t *numAdditionalBlocks);
|
||||
|
||||
/**
|
||||
* Verifies the embedded signature of the specified file path.
|
||||
* 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 The certificate file data.
|
||||
* @param sizeOfCertData The size of the cert data.
|
||||
* @param certName Used only if compiled as NSS, specifies the certName
|
||||
* @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_signature(const char *pathToMAR,
|
||||
const char *certData,
|
||||
uint32_t sizeOfCertData,
|
||||
const char *certName);
|
||||
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.
|
||||
@ -96,6 +108,34 @@ refresh_product_info_block(const char *path,
|
||||
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
|
||||
|
@ -26,10 +26,6 @@ PR_STATIC_ASSERT(sizeof(uint64_t) == 8);
|
||||
which is 16 bytes */
|
||||
#define SIGNATURE_BLOCK_OFFSET 16
|
||||
|
||||
/* We have a MAX_SIGNATURES limit so that an invalid MAR will never
|
||||
waste too much of either updater's or signmar's time. */
|
||||
#define MAX_SIGNATURES 8
|
||||
|
||||
/* 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)
|
||||
|
@ -254,7 +254,7 @@ int get_mar_file_info_fp(FILE *fp,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read the offset to the index. */
|
||||
/* Read the number of signatures field */
|
||||
if (fread(numSignatures, sizeof(*numSignatures), 1, fp) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
Binary file not shown.
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/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.
@ -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.
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.
@ -71,6 +71,6 @@ function run_test() {
|
||||
}
|
||||
};
|
||||
|
||||
// Run all the tests, there should be 5.
|
||||
do_check_eq(run_tests(tests), 5);
|
||||
// Run all the tests
|
||||
do_check_eq(run_tests(tests), Object.keys(tests).length - 1);
|
||||
}
|
||||
|
@ -85,6 +85,6 @@ function run_test() {
|
||||
}
|
||||
};
|
||||
|
||||
// Run all the tests, there should be 7.
|
||||
do_check_eq(run_tests(tests), 7);
|
||||
// Run all the tests
|
||||
do_check_eq(run_tests(tests), Object.keys(tests).length - 1);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ function run_test() {
|
||||
* @param inMAR The MAR file that should be signed
|
||||
* @param outMAR The MAR file to create
|
||||
*/
|
||||
function signMAR(inMAR, outMAR) {
|
||||
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);
|
||||
@ -19,17 +19,109 @@ function run_test() {
|
||||
do_check_true(signmarBin.exists());
|
||||
do_check_true(signmarBin.isExecutable());
|
||||
|
||||
// Setup the command line arguments to create the MAR.
|
||||
// Setup the command line arguments to sign the MAR.
|
||||
let NSSConfigDir = do_get_file("data");
|
||||
let args = ["-d", NSSConfigDir.path, "-n", "mycert", "-s",
|
||||
inMAR.path, outMAR.path];
|
||||
let args = ["-d", NSSConfigDir.path];
|
||||
if (certs.length == 1 && useShortHandCmdLine) {
|
||||
args.push("-n", certs[0]);
|
||||
} else {
|
||||
for (i = 0; i < certs.length; i++) {
|
||||
args.push("-n" + i, certs[i]);
|
||||
}
|
||||
}
|
||||
args.push("-s", inMAR.path, outMAR.path);
|
||||
|
||||
do_print('Running sign operation: ' + signmarBin.path);
|
||||
process.init(signmarBin);
|
||||
process.run(true, args, args.length);
|
||||
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.
|
||||
do_check_eq(process.exitValue, 0);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,10 +129,7 @@ function run_test() {
|
||||
*
|
||||
* @param signedMAR Verifies a MAR file
|
||||
*/
|
||||
function verifyMAR(signedMAR, wantSuccess) {
|
||||
if (wantSuccess === undefined) {
|
||||
wantSuccess = true;
|
||||
}
|
||||
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);
|
||||
@ -50,10 +139,8 @@ function run_test() {
|
||||
do_check_true(signmarBin.exists());
|
||||
do_check_true(signmarBin.isExecutable());
|
||||
|
||||
let DERFile = do_get_file("data/mycert.der");
|
||||
|
||||
// Will reference the arguments to use for verification in signmar
|
||||
let args;
|
||||
let args = [];
|
||||
|
||||
// The XPCShell test wiki indicates this is the preferred way for
|
||||
// Windows detection.
|
||||
@ -65,18 +152,33 @@ function run_test() {
|
||||
// we verify with NSS. So on Windows we use an exported DER file and on
|
||||
// other platforms we use the NSS config db.
|
||||
if (isWindows) {
|
||||
args = ["-D", DERFile.path, "-v", signedMAR.path];
|
||||
if (certs.length == 1 && useShortHandCmdLine) {
|
||||
args.push("-D", "data/" + certs[0] + ".der");
|
||||
} else {
|
||||
for (i = 0; i < certs.length; i++) {
|
||||
args.push("-D" + i, "data/" + certs[i] + ".der");
|
||||
}
|
||||
}
|
||||
args.push("-v", signedMAR.path);
|
||||
} else {
|
||||
let NSSConfigDir = do_get_file("data");
|
||||
args = ["-d", NSSConfigDir.path, "-n", "mycert", "-v", signedMAR.path];
|
||||
args = ["-d", NSSConfigDir.path];
|
||||
if (certs.length == 1 && useShortHandCmdLine) {
|
||||
args.push("-n", certs[0]);
|
||||
} else {
|
||||
for (i = 0; i < certs.length; i++) {
|
||||
args.push("-n" + i, certs[i]);
|
||||
}
|
||||
}
|
||||
args.push("-v", signedMAR.path);
|
||||
}
|
||||
|
||||
do_print('Running verify operation: ' + signmarBin.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;
|
||||
}
|
||||
|
||||
@ -94,7 +196,7 @@ function run_test() {
|
||||
* @param signedMAR The MAR file that should be signed
|
||||
* @param outMAR The MAR file to write to with signature stripped
|
||||
*/
|
||||
function stripMARSignature(signedMAR, outMAR) {
|
||||
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);
|
||||
@ -107,12 +209,20 @@ function run_test() {
|
||||
// Setup the command line arguments to create the MAR.
|
||||
let args = ["-r", signedMAR.path, outMAR.path];
|
||||
|
||||
do_print('Running sign operation: ' + signmarBin.path);
|
||||
process.init(signmarBin);
|
||||
process.run(true, args, args.length);
|
||||
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.
|
||||
do_check_eq(process.exitValue, 0);
|
||||
if (wantSuccess) {
|
||||
do_check_eq(process.exitValue, 0);
|
||||
} else {
|
||||
do_check_neq(process.exitValue, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -121,7 +231,10 @@ function run_test() {
|
||||
if (outMAR.exists()) {
|
||||
outMAR.remove(false);
|
||||
}
|
||||
|
||||
outMAR = do_get_file("multiple_signed_out.mar", true);
|
||||
if (outMAR.exists()) {
|
||||
outMAR.remove(false);
|
||||
}
|
||||
outMAR = do_get_file("out.mar", true);
|
||||
if (outMAR.exists()) {
|
||||
outMAR.remove(false);
|
||||
@ -133,36 +246,113 @@ function run_test() {
|
||||
}
|
||||
}
|
||||
|
||||
const wantFailure = false;
|
||||
const wantSuccess = true;
|
||||
// Define the unit tests to run.
|
||||
let tests = {
|
||||
// Test signing a MAR file
|
||||
test_sign: function() {
|
||||
// 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 = do_get_file("signed_out.mar", true);
|
||||
do_check_false(outMAR.exists());
|
||||
signMAR(inMAR, outMAR);
|
||||
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 = do_get_file("multiple_signed_out.mar", true);
|
||||
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: function() {
|
||||
test_verify_single: function() {
|
||||
let signedMAR = do_get_file("data/signed_pib_mar.mar");
|
||||
verifyMAR(signedMAR);
|
||||
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);
|
||||
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, false);
|
||||
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/" +
|
||||
@ -170,19 +360,205 @@ function run_test() {
|
||||
"binary_data_mar.mar");
|
||||
let signedMAR = do_get_file("signed_out.mar");
|
||||
let outMAR = do_get_file("out.mar", true);
|
||||
stripMARSignature(signedMAR, outMAR);
|
||||
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 = do_get_file("multiple_signed_out.mar");
|
||||
let outMAR = do_get_file("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 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 = do_get_file("signed_out.mar", true);
|
||||
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 = do_get_file("data/sigchanged_signed_pib_mar.mar", true);
|
||||
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 = do_get_file("data/sigchanged_signed_pib_mar.mar", true);
|
||||
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 = do_get_file("data/sigchanged_signed_pib_mar.mar", true);
|
||||
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 = do_get_file("out.mar", true);
|
||||
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, there should be 5.
|
||||
do_check_eq(run_tests(tests), 5);
|
||||
// Run all the tests
|
||||
do_check_eq(run_tests(tests), Object.keys(tests).length - 1);
|
||||
|
||||
do_register_cleanup(cleanup);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "mar.h"
|
||||
#include "mar_cmdline.h"
|
||||
|
||||
@ -22,28 +23,50 @@ int NSSInitCryptoContext(const char *NSSConfigDir);
|
||||
#endif
|
||||
|
||||
int mar_repackage_and_sign(const char *NSSConfigDir,
|
||||
const char *certName,
|
||||
const char * const *certNames,
|
||||
uint32_t certCount,
|
||||
const char *src,
|
||||
const char * dest);
|
||||
|
||||
static void print_usage() {
|
||||
printf("usage:\n");
|
||||
printf("Create a MAR file:\n");
|
||||
printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
|
||||
"{-c|-x|-t|-T} archive.mar [files...]\n");
|
||||
#ifndef NO_SIGN_VERIFY
|
||||
printf("Sign a MAR file:\n");
|
||||
printf(" mar [-C workingDir] -d NSSConfigDir -n certname -s "
|
||||
"archive.mar out_signed_archive.mar\n");
|
||||
printf("Strip a MAR signature:\n");
|
||||
printf(" mar [-C workingDir] -r "
|
||||
"signed_input_archive.mar output_archive.mar\n");
|
||||
printf("Extract a MAR signature:\n");
|
||||
printf(" mar [-C workingDir] -n(i) -X "
|
||||
"signed_input_archive.mar base_64_encoded_signature_file\n");
|
||||
printf("Import a MAR signature:\n");
|
||||
printf(" mar [-C workingDir] -n(i) -I "
|
||||
"signed_input_archive.mar base_64_encoded_signature_file "
|
||||
"changed_signed_output.mar\n");
|
||||
printf("(i) is the index of the certificate to extract\n");
|
||||
#if defined(XP_WIN) && !defined(MAR_NSS)
|
||||
printf("Verify a MAR file:\n");
|
||||
printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n");
|
||||
printf("At most %d signature certificate DER files are specified by "
|
||||
"-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES);
|
||||
#else
|
||||
printf("Verify a MAR file:\n");
|
||||
printf(" mar [-C workingDir] -d NSSConfigDir -n certname "
|
||||
"-v signed_archive.mar\n");
|
||||
printf("At most %d signature certificate names are specified by "
|
||||
"-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES);
|
||||
#endif
|
||||
printf("At most %d verification certificate names are specified by "
|
||||
"-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES);
|
||||
#endif
|
||||
printf("Print information on a MAR file:\n");
|
||||
printf(" mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
|
||||
"-i unsigned_archive_to_refresh.mar\n");
|
||||
printf("This program does not handle unicode file paths properly\n");
|
||||
}
|
||||
|
||||
static int mar_test_callback(MarFile *mar,
|
||||
@ -69,15 +92,29 @@ static int mar_test(const char *path) {
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *NSSConfigDir = NULL;
|
||||
char *certName = NULL;
|
||||
const char *certNames[MAX_SIGNATURES];
|
||||
char *MARChannelID = MAR_CHANNEL_ID;
|
||||
char *productVersion = MOZ_APP_VERSION;
|
||||
uint32_t i, k;
|
||||
int rv = -1;
|
||||
uint32_t certCount = 0;
|
||||
int32_t sigIndex = -1;
|
||||
#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
|
||||
HANDLE certFile;
|
||||
DWORD fileSize;
|
||||
/* We use DWORD here instead of uint64_t because it simplifies code with
|
||||
the Win32 API ReadFile which takes a DWORD. DER files will not be too
|
||||
large anyway. */
|
||||
DWORD fileSizes[MAX_SIGNATURES];
|
||||
DWORD read;
|
||||
char *certBuffer;
|
||||
char *DERFilePath = NULL;
|
||||
uint8_t *certBuffers[MAX_SIGNATURES];
|
||||
char *DERFilePaths[MAX_SIGNATURES];
|
||||
#endif
|
||||
|
||||
memset(certNames, 0, sizeof(certNames));
|
||||
#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
|
||||
memset(fileSizes, 0, sizeof(fileSizes));
|
||||
memset(certBuffers, 0, sizeof(certBuffers));
|
||||
memset(DERFilePaths, 0, sizeof(DERFilePaths));
|
||||
#endif
|
||||
|
||||
if (argc < 3) {
|
||||
@ -90,7 +127,8 @@ int main(int argc, char **argv) {
|
||||
argv[1][1] == 't' || argv[1][1] == 'x' ||
|
||||
argv[1][1] == 'v' || argv[1][1] == 's' ||
|
||||
argv[1][1] == 'i' || argv[1][1] == 'T' ||
|
||||
argv[1][1] == 'r')) {
|
||||
argv[1][1] == 'r' || argv[1][1] == 'X' ||
|
||||
argv[1][1] == 'I')) {
|
||||
break;
|
||||
/* -C workingdirectory */
|
||||
} else if (argv[1][0] == '-' && argv[1][1] == 'C') {
|
||||
@ -99,9 +137,17 @@ int main(int argc, char **argv) {
|
||||
argc -= 2;
|
||||
}
|
||||
#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
|
||||
/* -D DERFilePath */
|
||||
else if (argv[1][0] == '-' && argv[1][1] == 'D') {
|
||||
DERFilePath = argv[2];
|
||||
/* -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] == '0' + certCount || argv[1][2] == '\0')) {
|
||||
if (certCount >= MAX_SIGNATURES) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
DERFilePaths[certCount++] = argv[2];
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
@ -111,11 +157,30 @@ int main(int argc, char **argv) {
|
||||
NSSConfigDir = argv[2];
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
/* -n certName */
|
||||
} else if (argv[1][0] == '-' && argv[1][1] == 'n') {
|
||||
certName = argv[2];
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
/* -n certName, also matches -n[index] certName
|
||||
We allow an index for verifying to be symmetric
|
||||
with the import and export command line arguments. */
|
||||
} else if (argv[1][0] == '-' &&
|
||||
argv[1][1] == 'n' &&
|
||||
(argv[1][2] == '0' + certCount ||
|
||||
argv[1][2] == '\0' ||
|
||||
!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];
|
||||
@ -152,7 +217,6 @@ int main(int argc, char **argv) {
|
||||
return refresh_product_info_block(argv[2], &infoBlock);
|
||||
}
|
||||
case 'T': {
|
||||
int rv;
|
||||
struct ProductInformationBlock infoBlock;
|
||||
uint32_t numSignatures, numAdditionalBlocks;
|
||||
int hasSignatureBlock, hasAdditionalBlock;
|
||||
@ -189,43 +253,83 @@ int main(int argc, char **argv) {
|
||||
case 't':
|
||||
return mar_test(argv[2]);
|
||||
|
||||
/* Extract a MAR file */
|
||||
case 'x':
|
||||
return mar_extract(argv[2]);
|
||||
|
||||
#ifndef NO_SIGN_VERIFY
|
||||
case 'v':
|
||||
/* 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]);
|
||||
|
||||
#if defined(XP_WIN) && !defined(MAR_NSS)
|
||||
if (!DERFilePath) {
|
||||
/* 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;
|
||||
}
|
||||
/* If the mar program was built using CryptoAPI, then read in the buffer
|
||||
containing the cert from disk. */
|
||||
certFile = CreateFileA(DERFilePath, GENERIC_READ,
|
||||
FILE_SHARE_READ |
|
||||
FILE_SHARE_WRITE |
|
||||
FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0, NULL);
|
||||
if (INVALID_HANDLE_VALUE == certFile) {
|
||||
return -1;
|
||||
}
|
||||
fileSize = GetFileSize(certFile, NULL);
|
||||
certBuffer = malloc(fileSize);
|
||||
if (!ReadFile(certFile, certBuffer, fileSize, &read, NULL) ||
|
||||
fileSize != read) {
|
||||
CloseHandle(certFile);
|
||||
free(certBuffer);
|
||||
return -1;
|
||||
}
|
||||
CloseHandle(certFile);
|
||||
return import_signature(argv[2], sigIndex, argv[3], argv[4]);
|
||||
|
||||
if (mar_verify_signature(argv[2], certBuffer, fileSize, NULL)) {
|
||||
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);
|
||||
}
|
||||
|
||||
rv = mar_verify_signatures(argv[2], certBuffers, fileSizes,
|
||||
NULL, certCount);
|
||||
for (k = 0; k < certCount; ++k) {
|
||||
free(certBuffers[k]);
|
||||
}
|
||||
if (rv) {
|
||||
/* Determine if the source MAR file has the new fields for signing */
|
||||
int hasSignatureBlock;
|
||||
free(certBuffer);
|
||||
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");
|
||||
@ -236,10 +340,9 @@ int main(int argc, char **argv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(certBuffer);
|
||||
return 0;
|
||||
#else
|
||||
if (!NSSConfigDir || !certName) {
|
||||
if (!NSSConfigDir || certCount == 0) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
@ -249,16 +352,17 @@ int main(int argc, char **argv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return mar_verify_signature(argv[2], NULL, 0,
|
||||
certName);
|
||||
return mar_verify_signatures(argv[2], NULL, 0,
|
||||
certNames, certCount);
|
||||
|
||||
#endif /* defined(XP_WIN) && !defined(MAR_NSS) */
|
||||
case 's':
|
||||
if (!NSSConfigDir || !certName || argc < 4) {
|
||||
if (!NSSConfigDir || certCount == 0 || argc < 4) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
return mar_repackage_and_sign(NSSConfigDir, certName, argv[2], argv[3]);
|
||||
return mar_repackage_and_sign(NSSConfigDir, certNames, certCount,
|
||||
argv[2], argv[3]);
|
||||
|
||||
case 'r':
|
||||
return strip_signature_block(argv[2], argv[3]);
|
||||
|
@ -35,6 +35,8 @@ CryptoX_Result NSS_VerifySignature(VFYContext * const *ctx ,
|
||||
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, \
|
||||
@ -75,6 +77,7 @@ CryptoX_Result CyprtoAPI_VerifySignature(HCRYPTHASH *hash,
|
||||
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, \
|
||||
@ -105,6 +108,7 @@ CryptoX_Result CyprtoAPI_VerifySignature(HCRYPTHASH *hash,
|
||||
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, certName, cert) \
|
||||
|
@ -17,14 +17,16 @@
|
||||
#include "mar.h"
|
||||
#include "cryptox.h"
|
||||
|
||||
int mar_verify_signature_fp(FILE *fp,
|
||||
CryptoX_ProviderHandle provider,
|
||||
CryptoX_PublicKey key);
|
||||
int mar_verify_signature_for_fp(FILE *fp,
|
||||
CryptoX_ProviderHandle provider,
|
||||
CryptoX_PublicKey key,
|
||||
uint32_t signatureCount,
|
||||
char *extractedSignature);
|
||||
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
|
||||
@ -34,7 +36,8 @@ int mar_verify_signature_for_fp(FILE *fp,
|
||||
* @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 ctx The verify context.
|
||||
* @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
|
||||
@ -44,10 +47,12 @@ int
|
||||
ReadAndUpdateVerifyContext(FILE *fp,
|
||||
void *buffer,
|
||||
uint32_t size,
|
||||
CryptoX_SignatureHandle *ctx,
|
||||
CryptoX_SignatureHandle *ctxs,
|
||||
uint32_t count,
|
||||
const char *err)
|
||||
{
|
||||
if (!fp || !buffer || !ctx || !err) {
|
||||
uint32_t k;
|
||||
if (!fp || !buffer || !ctxs || count == 0 || !err) {
|
||||
fprintf(stderr, "ERROR: Invalid parameter specified.\n");
|
||||
return CryptoX_Error;
|
||||
}
|
||||
@ -61,40 +66,55 @@ ReadAndUpdateVerifyContext(FILE *fp,
|
||||
return CryptoX_Error;
|
||||
}
|
||||
|
||||
if (CryptoX_Failed(CryptoX_VerifyUpdate(ctx, buffer, size))) {
|
||||
fprintf(stderr, "ERROR: Could not update verify context for %s\n", err);
|
||||
return -2;
|
||||
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 the embedded signature of the specified file path.
|
||||
* 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 who's signature should be checked
|
||||
* @param certData The certificate file data.
|
||||
* @param sizeOfCertData The size of the cert data.
|
||||
* @param certName Used only if compiled as NSS, specifies the certName
|
||||
* @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_signature(const char *pathToMARFile,
|
||||
const char *certData,
|
||||
uint32_t sizeOfCertData,
|
||||
const char *certName) {
|
||||
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 cert;
|
||||
CryptoX_PublicKey key;
|
||||
CryptoX_Certificate certs[MAX_SIGNATURES];
|
||||
CryptoX_PublicKey keys[MAX_SIGNATURES];
|
||||
FILE *fp;
|
||||
uint32_t k;
|
||||
|
||||
if (!pathToMARFile || (!certData && !certName)) {
|
||||
memset(certs, 0, sizeof(certs));
|
||||
memset(keys, 0, sizeof(keys));
|
||||
|
||||
if (!pathToMARFile || certCount == 0) {
|
||||
fprintf(stderr, "ERROR: Invalid parameter specified.\n");
|
||||
return CryptoX_Error;
|
||||
}
|
||||
@ -111,98 +131,130 @@ mar_verify_signature(const char *pathToMARFile,
|
||||
return CryptoX_Error;
|
||||
}
|
||||
|
||||
if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData, sizeOfCertData,
|
||||
&key, certName, &cert))) {
|
||||
fclose(fp);
|
||||
fprintf(stderr, "ERROR: Could not load public key.\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_verify_signature_fp(fp, provider, key);
|
||||
rv = mar_extract_and_verify_signatures_fp(fp, provider, keys, certCount);
|
||||
fclose(fp);
|
||||
if (key) {
|
||||
CryptoX_FreePublicKey(&key);
|
||||
}
|
||||
|
||||
if (cert) {
|
||||
CryptoX_FreeCertificate(&cert);
|
||||
/* 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's signature by making sure at least one
|
||||
* signature verifies.
|
||||
* 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 certData The certificate data
|
||||
* @param sizeOfCertData The size of the data stored in certData
|
||||
* @param pathToMARFile The path of the MAR 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_signatureW(MarFile *mar,
|
||||
const char *certData,
|
||||
uint32_t sizeOfCertData) {
|
||||
int rv;
|
||||
mar_verify_signaturesW(MarFile *mar,
|
||||
const uint8_t * const *certData,
|
||||
const uint32_t *certDataSizes,
|
||||
uint32_t certCount) {
|
||||
int rv = -1;
|
||||
CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue;
|
||||
CryptoX_Certificate cert;
|
||||
CryptoX_PublicKey key;
|
||||
CryptoX_Certificate certs[MAX_SIGNATURES];
|
||||
CryptoX_PublicKey keys[MAX_SIGNATURES];
|
||||
uint32_t k;
|
||||
|
||||
if (!mar || !certData) {
|
||||
memset(certs, 0, sizeof(certs));
|
||||
memset(keys, 0, sizeof(keys));
|
||||
|
||||
if (!mar || !certData || !certDataSizes || certCount == 0) {
|
||||
fprintf(stderr, "ERROR: Invalid parameter specified.\n");
|
||||
return CryptoX_Error;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (!mar->fp) {
|
||||
fprintf(stderr, "ERROR: MAR file is not open.\n");
|
||||
return CryptoX_Error;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) {
|
||||
fprintf(stderr, "ERROR: Could not init crytpo library.\n");
|
||||
return CryptoX_Error;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData, sizeOfCertData,
|
||||
&key, "", &cert))) {
|
||||
fprintf(stderr, "ERROR: Could not load public key.\n");
|
||||
return CryptoX_Error;
|
||||
for (k = 0; k < certCount; ++k) {
|
||||
if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData[k], certDataSizes[k],
|
||||
&keys[k], "", &certs[k]))) {
|
||||
fprintf(stderr, "ERROR: Could not load public key.\n");
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
rv = mar_verify_signature_fp(mar->fp, provider, key);
|
||||
if (key) {
|
||||
CryptoX_FreePublicKey(&key);
|
||||
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]);
|
||||
}
|
||||
|
||||
if (certs[k]) {
|
||||
CryptoX_FreeCertificate(&certs[k]);
|
||||
}
|
||||
}
|
||||
|
||||
if (cert) {
|
||||
CryptoX_FreeCertificate(&cert);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Verifies a MAR file's signature by making sure at least one
|
||||
* signature verifies.
|
||||
*
|
||||
* 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 key The public key to use to verify the MAR
|
||||
* @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_verify_signature_fp(FILE *fp,
|
||||
CryptoX_ProviderHandle provider,
|
||||
CryptoX_PublicKey key) {
|
||||
mar_extract_and_verify_signatures_fp(FILE *fp,
|
||||
CryptoX_ProviderHandle provider,
|
||||
CryptoX_PublicKey *keys,
|
||||
uint32_t keyCount) {
|
||||
char buf[5] = {0};
|
||||
uint32_t signatureAlgorithmID, signatureCount, signatureLen, numVerified = 0;
|
||||
uint32_t signatureCount, signatureLen, numVerified = 0;
|
||||
uint32_t signatureAlgorithmIDs[MAX_SIGNATURES];
|
||||
int rv = -1;
|
||||
int64_t curPos;
|
||||
char *extractedSignature;
|
||||
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;
|
||||
@ -240,13 +292,13 @@ mar_verify_signature_fp(FILE *fp,
|
||||
return CryptoX_Error;
|
||||
}
|
||||
|
||||
for (i = 0; i < signatureCount && numVerified == 0; i++) {
|
||||
for (i = 0; i < signatureCount; i++) {
|
||||
/* Get the signature algorithm ID */
|
||||
if (fread(&signatureAlgorithmID, sizeof(uint32_t), 1, fp) != 1) {
|
||||
if (fread(&signatureAlgorithmIDs[i], sizeof(uint32_t), 1, fp) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not read signatures algorithm ID.\n");
|
||||
return CryptoX_Error;
|
||||
}
|
||||
signatureAlgorithmID = ntohl(signatureAlgorithmID);
|
||||
signatureAlgorithmIDs[i] = ntohl(signatureAlgorithmIDs[i]);
|
||||
|
||||
if (fread(&signatureLen, sizeof(uint32_t), 1, fp) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not read signatures length.\n");
|
||||
@ -261,89 +313,120 @@ mar_verify_signature_fp(FILE *fp,
|
||||
return CryptoX_Error;
|
||||
}
|
||||
|
||||
extractedSignature = malloc(signatureLen);
|
||||
if (!extractedSignature) {
|
||||
extractedSignatures[i] = malloc(signatureLen);
|
||||
if (!extractedSignatures[i]) {
|
||||
fprintf(stderr, "ERROR: Could allocate buffer for signature.\n");
|
||||
return CryptoX_Error;
|
||||
}
|
||||
if (fread(extractedSignature, signatureLen, 1, fp) != 1) {
|
||||
if (fread(extractedSignatures[i], signatureLen, 1, fp) != 1) {
|
||||
fprintf(stderr, "ERROR: Could not read extracted signature.\n");
|
||||
free(extractedSignature);
|
||||
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 (1 == signatureAlgorithmID) {
|
||||
curPos = ftello(fp);
|
||||
rv = mar_verify_signature_for_fp(fp,
|
||||
provider,
|
||||
key,
|
||||
signatureCount,
|
||||
extractedSignature);
|
||||
if (CryptoX_Succeeded(rv)) {
|
||||
numVerified++;
|
||||
if (signatureAlgorithmIDs[i] != 1) {
|
||||
fprintf(stderr, "ERROR: Unknown signature algorithm ID.\n");
|
||||
for (i = 0; i < signatureCount; ++i) {
|
||||
free(extractedSignatures[i]);
|
||||
}
|
||||
free(extractedSignature);
|
||||
if (fseeko(fp, curPos, SEEK_SET)) {
|
||||
fprintf(stderr, "ERROR: Could not seek back to last signature.\n");
|
||||
return CryptoX_Error;
|
||||
}
|
||||
} else {
|
||||
free(extractedSignature);
|
||||
return CryptoX_Error;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we reached here and we verified at least one
|
||||
signature, return success. */
|
||||
if (numVerified > 0) {
|
||||
return CryptoX_Success;
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: No signatures were verified.\n");
|
||||
return CryptoX_Error;
|
||||
curPos = ftello(fp);
|
||||
rv = mar_verify_signatures_for_fp(fp,
|
||||
provider,
|
||||
keys,
|
||||
(const uint8_t * const *)extractedSignatures,
|
||||
signatureCount,
|
||||
&numVerified);
|
||||
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 if a specific signature ID matches the extracted signature.
|
||||
* 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 key The public key to use to verify the MAR
|
||||
* @param signatureCount The number of signatures in the MAR file
|
||||
* @param extractedSignature The signature that should be verified
|
||||
* @return 0 on success
|
||||
* @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_signature_for_fp(FILE *fp,
|
||||
CryptoX_ProviderHandle provider,
|
||||
CryptoX_PublicKey key,
|
||||
uint32_t signatureCount,
|
||||
char *extractedSignature) {
|
||||
CryptoX_SignatureHandle signatureHandle;
|
||||
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 signatureLen;
|
||||
uint32_t signatureLengths[MAX_SIGNATURES];
|
||||
uint32_t i;
|
||||
int rv = CryptoX_Error;
|
||||
|
||||
if (!extractedSignature) {
|
||||
memset(signatureHandles, 0, sizeof(signatureHandles));
|
||||
memset(signatureLengths, 0, sizeof(signatureLengths));
|
||||
|
||||
if (!extractedSignatures || !numVerified) {
|
||||
fprintf(stderr, "ERROR: Invalid parameter specified.\n");
|
||||
return CryptoX_Error;
|
||||
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");
|
||||
return CryptoX_Error;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
CryptoX_VerifyBegin(provider, &signatureHandle, &key);
|
||||
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");
|
||||
return CryptoX_Error;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Bytes 0-3: MAR1
|
||||
@ -353,59 +436,80 @@ mar_verify_signature_for_fp(FILE *fp,
|
||||
if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp, buf,
|
||||
SIGNATURE_BLOCK_OFFSET +
|
||||
sizeof(uint32_t),
|
||||
&signatureHandle,
|
||||
signatureHandles,
|
||||
signatureCount,
|
||||
"signature block"))) {
|
||||
return CryptoX_Error;
|
||||
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),
|
||||
&signatureHandle,
|
||||
signatureHandles,
|
||||
signatureCount,
|
||||
"signature algorithm ID"))) {
|
||||
return CryptoX_Error;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp,
|
||||
&signatureLen,
|
||||
&signatureLengths[i],
|
||||
sizeof(uint32_t),
|
||||
&signatureHandle,
|
||||
signatureHandles,
|
||||
signatureCount,
|
||||
"signature length"))) {
|
||||
return CryptoX_Error;
|
||||
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;
|
||||
}
|
||||
signatureLen = ntohl(signatureLen);
|
||||
|
||||
/* Skip past the signature itself as those are not included */
|
||||
if (fseeko(fp, signatureLen, SEEK_CUR)) {
|
||||
if (fseeko(fp, signatureLengths[i], SEEK_CUR)) {
|
||||
fprintf(stderr, "ERROR: Could not seek past signature.\n");
|
||||
return CryptoX_Error;
|
||||
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");
|
||||
return CryptoX_Error;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (CryptoX_Failed(CryptoX_VerifyUpdate(&signatureHandle,
|
||||
buf, numRead))) {
|
||||
fprintf(stderr, "ERROR: Error updating verify context with"
|
||||
" data block.\n");
|
||||
return CryptoX_Error;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CryptoX_Failed(CryptoX_VerifySignature(&signatureHandle,
|
||||
&key,
|
||||
extractedSignature,
|
||||
signatureLen))) {
|
||||
fprintf(stderr, "ERROR: Error verifying signature.\n");
|
||||
return CryptoX_Error;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
return CryptoX_Success;
|
||||
rv = CryptoX_Success;
|
||||
failure:
|
||||
for (i = 0; i < signatureCount; i++) {
|
||||
CryptoX_FreeSignatureHandle(signatureHandles[i]);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ static char *outbuf = NULL;
|
||||
* @return TRUE on success
|
||||
*/
|
||||
BOOL
|
||||
LoadFileInResource(int name, int type, const char *&data, DWORD& size)
|
||||
LoadFileInResource(int name, int type, const uint8_t *&data, uint32_t& size)
|
||||
{
|
||||
HMODULE handle = GetModuleHandle(NULL);
|
||||
if (!handle) {
|
||||
@ -66,7 +66,7 @@ LoadFileInResource(int name, int type, const char *&data, DWORD& size)
|
||||
}
|
||||
|
||||
size = SizeofResource(handle, resourceInfoBlockHandle);
|
||||
data = static_cast<const char*>(::LockResource(resourceHandle));
|
||||
data = static_cast<const uint8_t*>(::LockResource(resourceHandle));
|
||||
FreeLibrary(handle);
|
||||
return TRUE;
|
||||
}
|
||||
@ -83,13 +83,13 @@ LoadFileInResource(int name, int type, const char *&data, DWORD& size)
|
||||
int
|
||||
VerifyLoadedCert(MarFile *archive, int name, int type)
|
||||
{
|
||||
DWORD size = 0;
|
||||
const char *data = NULL;
|
||||
uint32_t size = 0;
|
||||
const uint8_t *data = NULL;
|
||||
if (!LoadFileInResource(name, type, data, size) || !data || !size) {
|
||||
return CERT_LOAD_ERROR;
|
||||
}
|
||||
|
||||
if (mar_verify_signatureW(archive, data, size)) {
|
||||
if (mar_verify_signaturesW(archive, &data, &size, 1)) {
|
||||
return CERT_VERIFY_ERROR;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user