Merge mozilla-central into mozilla-inbound
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 17 Oct 2012 13:03:16 -0400
changeset 110703 44265699e0a98fb5e9063b73c2015ae50efe5af4
parent 110702 414f6660e1189b68fae6c53b5e002355efa05908 (current diff)
parent 110572 bd12f4180358f5ef14e1d4a71c7c38fab3b1df8f (diff)
child 110704 e7ea9b548002ccfdc92b1c2dec4237249fbd4f8e
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
milestone19.0a1
Merge mozilla-central into mozilla-inbound
content/base/src/nsDocument.cpp
intl/uconv/src/charsetData.properties
testing/marionette/marionette-actors.js
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -25,17 +25,17 @@
 
 class ContentUnbinder;
 class nsContentList;
 class nsDOMAttributeMap;
 class nsDOMTokenList;
 class nsIControllers;
 class nsICSSDeclaration;
 class nsIDocument;
-class nsIDOMDOMStringMap;
+class nsDOMStringMap;
 class nsIDOMNamedNodeMap;
 class nsINodeInfo;
 class nsIURI;
 
 /**
  * Class that implements the nsIDOMNodeList interface (a list of children of
  * the content), by holding a reference to the content and delegating GetLength
  * and Item to its existing child list.
@@ -343,17 +343,17 @@ public:
      * @see nsGenericHTMLElement::GetStyle
      */
     nsCOMPtr<nsICSSDeclaration> mStyle;
 
     /**
      * The .dataset attribute.
      * @see nsGenericHTMLElement::GetDataset
      */
-    nsIDOMDOMStringMap* mDataset; // [Weak]
+    nsDOMStringMap* mDataset; // [Weak]
 
     /**
      * SMIL Overridde style rules (for SMIL animation of CSS properties)
      * @see nsIContent::GetSMILOverrideStyle
      */
     nsCOMPtr<nsICSSDeclaration> mSMILOverrideStyle;
 
     /**
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -1232,17 +1232,17 @@ FragmentOrElement::MarkUserDataHandler(v
 void
 FragmentOrElement::MarkNodeChildren(nsINode* aNode)
 {
   JSObject* o = GetJSObjectChild(aNode);
   xpc_UnmarkGrayObject(o);
 
   nsEventListenerManager* elm = aNode->GetListenerManager(false);
   if (elm) {
-    elm->UnmarkGrayJSListeners();
+    elm->MarkForCC();
   }
 
   if (aNode->HasProperties()) {
     nsIDocument* ownerDoc = aNode->OwnerDoc();
     ownerDoc->PropertyTable(DOM_USER_DATA)->
       Enumerate(aNode, FragmentOrElement::MarkUserData,
                 &nsCCUncollectableMarker::sGeneration);
     ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -580,17 +580,17 @@ WebSocket::Constructor(JSContext* aCx,
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket)
 
 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());
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
--- a/content/base/src/nsCCUncollectableMarker.cpp
+++ b/content/base/src/nsCCUncollectableMarker.cpp
@@ -87,61 +87,91 @@ MarkUserDataHandler(void* aNode, nsIAtom
   if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
     nsGenericElement::MarkUserDataHandler(aNode, aKey, aValue, 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;
   globalMM->GetChildCount(&childCount);
   for (uint32_t i = 0; i < childCount; ++i) {
     nsCOMPtr<nsIMessageListenerManager> childMM;
     globalMM->GetChildAt(i, getter_AddRefs(childMM));
     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);
     for (uint32_t j = 0; j < tabChildCount; ++j) {
       nsCOMPtr<nsIMessageListenerManager> childMM;
       windowMM->GetChildAt(j, getter_AddRefs(childMM));
       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();
         if (!et) {
           continue;
         }
         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
 MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS,
                   bool aPrepareForCC)
 {
   if (!aViewer) {
     return;
@@ -149,23 +179,23 @@ MarkContentViewer(nsIContentViewer* aVie
 
   nsIDocument *doc = aViewer->GetDocument();
   if (doc &&
       doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) {
     doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
     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();
       }
 
       doc->PropertyTable(DOM_USER_DATA_HANDLER)->
         EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration);
     } else if (aPrepareForCC) {
       // Unfortunately we need to still mark user data just before running CC so
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -3913,17 +3913,17 @@ ListenerEnumerator(PLDHashTable* aTable,
                    uint32_t aNumber, void* aArg)
 {
   EventListenerManagerMapEntry* entry =
     static_cast<EventListenerManagerMapEntry*>(aEntry);
   if (entry) {
     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;
 }
 
 void
 nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(uint32_t aGeneration)
 {
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1713,17 +1713,17 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsDocument,
                                               nsNodeUtils::LastRelease(this))
 
 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;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
   return nsGenericElement::CanSkipInCC(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -76,17 +76,17 @@ nsEventSource::~nsEventSource()
 //-----------------------------------------------------------------------------
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventSource)
 
 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());
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1321,10 +1321,13 @@ NS_NewChildProcessMessageManager(nsISync
 
 bool
 nsFrameMessageManager::MarkForCC()
 {
   uint32_t len = mListeners.Length();
   for (uint32_t i = 0; i < len; ++i) {
     xpc_TryUnmarkWrappedGrayObject(mListeners[i].mListener);
   }
+  if (mRefCnt.IsPurple()) {
+    mRefCnt.RemovePurple();
+  }
   return true;
 }
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -578,17 +578,17 @@ nsXMLHttpRequest::SetRequestObserver(nsI
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
 
 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());
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
--- a/content/events/src/nsDOMEventTargetHelper.cpp
+++ b/content/events/src/nsDOMEventTargetHelper.cpp
@@ -42,17 +42,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mListenerManager)
 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;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMEventTargetHelper)
   return tmp->IsBlackAndDoesNotNeedTracing(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -1174,22 +1174,25 @@ nsEventListenerManager::SizeOfIncludingT
     if (jsl) {
       n += jsl->SizeOfIncludingThis(aMallocSizeOf);
     }
   }
   return n;
 }
 
 void
-nsEventListenerManager::UnmarkGrayJSListeners()
+nsEventListenerManager::MarkForCC()
 {
   uint32_t count = mListeners.Length();
   for (uint32_t i = 0; i < count; ++i) {
     const nsListenerStruct& ls = mListeners.ElementAt(i);
     nsIJSEventListener* jsl = ls.GetJSListener();
     if (jsl) {
       xpc_UnmarkGrayObject(jsl->GetHandler());
       xpc_UnmarkGrayObject(jsl->GetEventScope());
     } else if (ls.mListenerType == eWrappedJSListener) {
       xpc_TryUnmarkWrappedGrayObject(ls.mListener);
     }
   }
+  if (mRefCnt.IsPurple()) {
+    mRefCnt.RemovePurple();
+  }
 }
--- a/content/events/src/nsEventListenerManager.h
+++ b/content/events/src/nsEventListenerManager.h
@@ -221,17 +221,17 @@ public:
    * false if there definitely isn't.
    */
   bool MayHaveTouchEventListener() { return mMayHaveTouchEventListener; }
 
   bool MayHaveMouseEnterLeaveEventListener() { return mMayHaveMouseEnterLeaveEventListener; }
 
   size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
 
-  void UnmarkGrayJSListeners();
+  void MarkForCC();
 
   nsISupports* GetTarget() { return mTarget; }
 protected:
   nsresult HandleEventSubType(nsListenerStruct* aListenerStruct,
                               nsIDOMEventListener* aListener,
                               nsIDOMEvent* aDOMEvent,
                               nsIDOMEventTarget* aCurrentTarget,
                               uint32_t aPhaseFlags,
--- a/content/html/content/src/nsDOMStringMap.cpp
+++ b/content/html/content/src/nsDOMStringMap.cpp
@@ -10,28 +10,35 @@
 #include "nsDOMClassInfoID.h"
 #include "nsGenericHTMLElement.h"
 #include "nsContentUtils.h"
 
 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.
     tmp->mElement->ClearDataset();
     tmp->mElement = nullptr;
   }
 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)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStringMap)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStringMap)
 
--- a/content/html/content/src/nsDOMStringMap.h
+++ b/content/html/content/src/nsDOMStringMap.h
@@ -8,26 +8,52 @@
 #define nsDOMStringMap_h
 
 #include "nsIDOMDOMStringMap.h"
 
 #include "nsCycleCollectionParticipant.h"
 #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
   // of returning arrays in IDL. Instead, we cast to this
   // class if this method needs to be called.
   nsresult GetDataPropList(nsTArray<nsString>& aResult);
 
   nsresult RemovePropInternal(nsIAtom* aAttr);
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -339,28 +339,35 @@ nsGenericHTMLElement::SetAttribute(const
 
     return SetAttr(kNameSpaceID_None, nameAtom, aValue, true);
   }
 
   return SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
                  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;
 }
 
 nsresult
 nsGenericHTMLElement::ClearDataset()
 {
   nsDOMSlots *slots = GetExistingDOMSlots();
 
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -214,16 +214,17 @@ public:
   NS_IMETHOD GetProperties(nsIDOMHTMLPropertiesCollection** aReturn);
   NS_IMETHOD GetAccessKey(nsAString &aAccessKey);
   NS_IMETHOD SetAccessKey(const nsAString& aAccessKey);
   NS_IMETHOD GetAccessKeyLabel(nsAString& aLabel);
   nsresult GetContentEditable(nsAString& aContentEditable);
   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);
 
   /**
    * Get width and height, using given image request if attributes are unset.
    */
   nsSize GetWidthHeightForImage(imgIRequest *aImageRequest);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -8477,23 +8477,23 @@ nsDOMStringMapSH::Enumerate(nsIXPConnect
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMStringMapSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
                             JSObject *globalObj, JSObject **parentObj)
 {
-  *parentObj = globalObj;
-
-  nsDOMStringMap* dataset = static_cast<nsDOMStringMap*>(nativeObj);
-
-  // Parent the string map to its element.
-  nsINode* element = dataset->GetElement();
-  return WrapNativeParent(cx, globalObj, element, element, parentObj);
+  nsDOMStringMap* map = nsDOMStringMap::FromSupports(nativeObj);
+  nsINode* native_parent = map->GetParentObject();
+  if (!native_parent) {
+    return nsDOMClassInfo::PreCreate(nativeObj, cx, globalObj, parentObj);
+  }
+
+  return WrapNativeParent(cx, globalObj, native_parent, parentObj);
 }
 
 NS_IMETHODIMP
 nsDOMStringMapSH::DelProperty(nsIXPConnectWrappedNative *wrapper,
                               JSContext *cx, JSObject *obj, jsid id,
                               jsval *vp, bool *_retval)
 {
   nsCOMPtr<nsIDOMDOMStringMap> dataset(do_QueryWrappedNative(wrapper, obj));
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1264,17 +1264,17 @@ MarkXBLHandlers(nsXBLPrototypeHandler* a
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
   if (tmp->IsBlackForCC()) {
     if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) {
       tmp->mCachedXBLPrototypeHandlers.EnumerateRead(MarkXBLHandlers, nullptr);
     }
     nsEventListenerManager* elm = tmp->GetListenerManager(false);
     if (elm) {
-      elm->UnmarkGrayJSListeners();
+      elm->MarkForCC();
     }
     tmp->UnmarkGrayTimers();
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow)
   return tmp->IsBlackForCC();
--- a/intl/uconv/src/charsetData.properties
+++ b/intl/uconv/src/charsetData.properties
@@ -57,16 +57,17 @@ t.61-8bit.isInternal                    
 ibm864.isInternal                       = true
 
 t.61-8bit.notForOutgoing                = true
 utf-7.notForOutgoing                    = true
 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
 iso-8859-8-e.notForOutgoing             = true
 iso-8859-8.notForOutgoing               = true
 iso-2022-kr.notForOutgoing              = true
 x-windows-949.notForOutgoing            = true
 x-johab.notForOutgoing                  = true
--- a/js/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/xpconnect/src/dom_quickstubs.qsconf
@@ -480,16 +480,17 @@ customIncludes = [
     'nsDocument.h',
     'nsDOMTokenList.h',
     'nsGenericDOMDataNode.h',
     'nsGenericElement.h',
     'nsGenericHTMLElement.h',
     'nsSVGStylableElement.h',
     'nsHTMLDocument.h',
     'nsDOMQS.h',
+    'nsDOMStringMap.h',
     'mozilla/dom/ImageData.h'
     ]
 
 customReturnInterfaces = [
     'nsIDOMCanvasPattern',
     'nsIDOMCanvasGradient',
     'nsIDOMImageData'
     ]
@@ -844,16 +845,21 @@ customMethodCalls = {
                 '    rv = error.ErrorCode();'
         },
     'nsIDOMHTMLElement_SetInnerHTML': {
         'thisType' : 'nsGenericHTMLElement',
         'code': '    mozilla::ErrorResult error;\n'
                 '    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'
                 '       don\'t actually implement GetStyle. */\n'
                 '    if (self->GetNameSpaceID() == kNameSpaceID_MathML)\n'
                 '      return xpc_qsThrow(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n'
                 '    nsIDOMCSSStyleDeclaration* result = '
                 'self->GetStyle(&rv);'
--- a/modules/libmar/sign/mar_sign.c
+++ b/modules/libmar/sign/mar_sign.c
@@ -17,16 +17,17 @@
 #include "mar_cmdline.h"
 #include "mar.h"
 #include "cryptox.h"
 #ifndef XP_WIN
 #include <unistd.h>
 #endif
 
 #include "nss_secutil.h"
+#include "base64.h"
 
 /**
  * Initializes the NSS context.
  * 
  * @param NSSConfigDir The config dir containing the private key to use
  * @return 0 on success
  *         -1 on error
 */
@@ -104,43 +105,50 @@ NSSSignBegin(const char *certName,
     fprintf(stderr, "ERROR: Could not begin signature\n");
     return -1;
   }
   
   return 0;
 }
 
 /**
- * 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;
   }
 
   if (fwrite(buffer, size, 1, fpDest) != 1) {
     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;
 }
 
 /** 
  * Adjusts each entry's content offset in the the passed in index by the 
  * specified amount.
  *
@@ -163,44 +171,47 @@ AdjustIndexContentOffsets(char *indexBuf
     *offsetToContent = htonl(*offsetToContent);
     /* Skip past the offset, length, and flags */
     indexBufLoc += 3 * sizeof(uint32_t);
     indexBufLoc += strlen(indexBufLoc) + 1;
   }
 }
 
 /**
- * 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
  *         -2 on write error
  *         -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;
   }
 
   if (fread(buffer, size, 1, fpSrc) != 1) {
     fprintf(stderr, "ERROR: Could not read %s\n", err);
     return -1;
   }
 
-  return WriteAndUpdateSignature(fpDest, buffer, size, ctx, err);
+  return WriteAndUpdateSignatures(fpDest, buffer, size, ctxs, ctxCount, err);
 }
 
 
 /**
  * Reads from fpSrc, writes it to fpDest.
  *
  * @param  fpSrc  The file pointer to read from.
  * @param  fpDest The file pointer to write to.
@@ -256,17 +267,17 @@ strip_signature_block(const char *src, c
 
   if (!src || !dest) {
     fprintf(stderr, "ERROR: Invalid parameter passed in.\n");
     return -1;
   }
 
   fpSrc = fopen(src, "rb");
   if (!fpSrc) {
-    fprintf(stderr, "ERROR: could not open source file: %s\n", dest);
+    fprintf(stderr, "ERROR: could not open source file: %s\n", src);
     goto failure;
   }
 
   fpDest = fopen(dest, "wb");
   if (!fpDest) {
     fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
     goto failure;
   }
@@ -463,87 +474,420 @@ failure:
 
   if (rv) {
     remove(dest);
   }
   return rv;
 }
 
 /**
- * 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;
   }
 
   if (NSSInitCryptoContext(NSSConfigDir)) {
     fprintf(stderr, "ERROR: Could not init config dir: %s\n", NSSConfigDir);
     goto failure;
   }
 
   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;
   }
 
   fpDest = fopen(dest, "wb");
   if (!fpDest) {
     fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
     goto failure;
   }
 
   /* Determine if the source MAR file has the new fields for signing or not */
   if (get_mar_file_info(src, &hasSignatureBlock, NULL, NULL, NULL, NULL)) {
     fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n");
     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;
   }
 
   /* Offset to index */
   if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fpSrc) != 1) {
     fprintf(stderr, "ERROR: Could not read offset\n");
     goto failure;
   }
@@ -569,134 +913,146 @@ mar_repackage_and_sign(const char *NSSCo
       goto failure;
     }
     sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
     if (sizeOfEntireMAR != realSizeOfSrcMAR) {
       fprintf(stderr, "ERROR: Source MAR is not of the right size\n");
       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;
     }
   } else {
     sizeOfEntireMAR = realSizeOfSrcMAR;
   }
 
   if (((int64_t)offsetToIndex) > sizeOfEntireMAR) {
     fprintf(stderr, "ERROR: Offset to index is larger than the file size.\n");
     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);
   }
   dstOffsetToIndex += signatureSectionLength;
 
   /* 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);
 
   /* Write out the new MAR file size */
   sizeOfEntireMAR += signatureSectionLength;
   if (!hasSignatureBlock) {
     sizeOfEntireMAR += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
   }
 
   /* 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);
+  signaturePlaceholderOffset = ftello(fpDest);
+
+  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 */
-  signatureLength = htonl(signatureLength);
-  if (WriteAndUpdateSignature(fpDest, &signatureLength, 
-                              sizeof(signatureLength), 
-                              ctx, "signature length")) {
-    goto failure;
-  }
-  signatureLength = ntohl(signatureLength);
+    /* 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));
-  signaturePlaceholderOffset = ftello(fpDest);
-  if (fwrite(buf, signatureLength, 1, fpDest) != 1) {
-    fprintf(stderr, "ERROR: Could not write signature length\n");
-    goto failure;
+    /* 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
      offsetToIndex unfortunately has to remain 32-bit because for backwards
      compatibility with the old MAR file format. */
   if (ftello(fpSrc) > ((int64_t)offsetToIndex)) {
     fprintf(stderr, "ERROR: Index offset is too small.\n");
     goto failure;
   }
   numBytesToCopy = ((int64_t)offsetToIndex) - ftello(fpSrc);
   numChunks = numBytesToCopy / BLOCKSIZE;
   leftOver = numBytesToCopy % BLOCKSIZE;
 
   /* 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);
 
   /* Consume the index and adjust each index by signatureSectionLength */
   indexBuf = malloc(indexLength);
   indexBufLoc = indexBuf;
   if (fread(indexBuf, indexLength, 1, fpSrc) != 1) {
@@ -709,48 +1065,59 @@ mar_repackage_and_sign(const char *NSSCo
     AdjustIndexContentOffsets(indexBuf, indexLength, signatureSectionLength);
   } else {
     AdjustIndexContentOffsets(indexBuf, indexLength, 
                               sizeof(sizeOfEntireMAR) + 
                               sizeof(numSignatures) + 
                               signatureSectionLength);
   }
 
-  if (WriteAndUpdateSignature(fpDest, indexBuf, 
-                              indexLength, ctx, "index")) {
+  if (WriteAndUpdateSignatures(fpDest, indexBuf,
+                               indexLength, ctxs, certCount, "index")) {
     goto failure;
   }
 
   /* Ensure that we don't sign a file that is too large to be accepted by
      the verification function. */
   if (ftello(fpDest) > MAX_SIZE_OF_MAR_FILE) {
     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 */
   if (fseeko(fpDest, signaturePlaceholderOffset, SEEK_SET)) {
     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(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;
 failure: 
   if (fpSrc) {
     fclose(fpSrc);
   }
 
@@ -761,25 +1128,31 @@ failure:
   if (rv) {
     remove(dest);
   }
 
   if (indexBuf) { 
     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;
 }
--- a/modules/libmar/src/mar.h
+++ b/modules/libmar/src/mar.h
@@ -10,16 +10,25 @@
 /* We use NSPR here just to import the definition of uint32_t */
 #include "prtypes.h"
 #include "mozilla/StandardInteger.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* We have a MAX_SIGNATURES limit so that an invalid MAR will never
+ * waste too much of either updater's or signmar's time.
+ * It is also used at various places internally and will affect memory usage.
+ * If you want to increase this value above 9 then you need to adjust parsing
+ * code in tool/mar.c.
+*/
+#define MAX_SIGNATURES 8
+PR_STATIC_ASSERT(MAX_SIGNATURES <= 9);
+
 struct ProductInformationBlock {
   const char *MARChannelID;
   const char *productVersion;
 };
 
 /**
  * The MAR item data structure.
  */
@@ -118,32 +127,40 @@ int mar_create(const char *dest,
  * Extract a MAR file to the current working directory.
  * @param path      The path to the MAR file to extract.  This path must be
  *                  compatible with fopen.
  * @return          A non-zero value if an error occurs.
  */
 int mar_extract(const char *path);
 
 /**
- * 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
 
 /** 
  * Reads the product info block from the MAR file's additional block section.
  * The caller is responsible for freeing the fields in infoBlock
  * if the return is successful.
  *
  * @param infoBlock Out parameter for where to store the result to
--- a/modules/libmar/src/mar_cmdline.h
+++ b/modules/libmar/src/mar_cmdline.h
@@ -35,34 +35,46 @@ struct ProductInformationBlock;
 int get_mar_file_info(const char *path, 
                       int *hasSignatureBlock,
                       uint32_t *numSignatures,
                       int *hasAdditionalBlocks,
                       uint32_t *offsetAdditionalBlocks,
                       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.
  * The caller is responsible for freeing the fields in infoBlock
  * if the return is successful.
  *
  * @param infoBlock Out parameter for where to store the result to
  * @return 0 on success, -1 on failure
@@ -91,13 +103,41 @@ refresh_product_info_block(const char *p
  * @param  dest The path of the MAR file to write out that 
                 has no signature block
  * @return 0 on success
  *         -1 on error
 */
 int
 strip_signature_block(const char *src, const char * dest);
 
+/**
+ * Extracts a signature from a MAR file, base64 encodes it, and writes it out
+ *
+ * @param  src       The path of the source MAR file
+ * @param  sigIndex  The index of the signature to extract
+ * @param  dest      The path of file to write the signature to
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+extract_signature(const char *src, uint32_t sigIndex, const char * dest);
+
+/**
+ * Imports a base64 encoded signature into a MAR file
+ *
+ * @param  src           The path of the source MAR file
+ * @param  sigIndex      The index of the signature to import
+ * @param  base64SigFile A file which contains the signature to import
+ * @param  dest          The path of the destination MAR file with replaced signature
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+import_signature(const char *src,
+                 uint32_t sigIndex,
+                 const char * base64SigFile,
+                 const char *dest);
+
 #ifdef __cplusplus
 }
 #endif
 
 #endif  /* MAR_CMDLINE_H__ */
--- a/modules/libmar/src/mar_private.h
+++ b/modules/libmar/src/mar_private.h
@@ -21,20 +21,16 @@ PR_STATIC_ASSERT(sizeof(uint64_t) == 8);
 
 #define MAR_ID "MAR1"
 #define MAR_ID_SIZE 4
 
 /* The signature block comes directly after the header block 
    which is 16 bytes */
 #define SIGNATURE_BLOCK_OFFSET 16
 
-/* 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)
 
 /* Existing code makes assumptions that the file size is
    smaller than LONG_MAX. */
 PR_STATIC_ASSERT(MAX_SIZE_OF_MAR_FILE < ((int64_t)LONG_MAX));
 
--- a/modules/libmar/src/mar_read.c
+++ b/modules/libmar/src/mar_read.c
@@ -249,17 +249,17 @@ int get_mar_file_info_fp(FILE *fp,
   offsetToIndex = ntohl(offsetToIndex);
 
   if (numSignatures) {
      /* Skip past the MAR file size field */
     if (fseek(fp, sizeof(uint64_t), SEEK_CUR)) {
       return -1;
     }
 
-    /* Read the offset to the index. */
+    /* Read the number of signatures field */
     if (fread(numSignatures, sizeof(*numSignatures), 1, fp) != 1) {
       return -1;
     }
     *numSignatures = ntohl(*numSignatures);
   }
 
   /* Skip to the first index entry past the index size field 
      We do it in 2 calls because offsetToIndex + sizeof(uint32_t) 
index 561e5051d9fa8671d57a079335de1e7b83a02f2e..43551f1297ae2d953fd9c06f1c320d112308ce0a
GIT binary patch
literal 65536
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
zb0P9&E(k(lXe_>0EHK)agFpLa9{>OV00000000000000000000000000000000000
G@bRA!1i%RZ
index 970e8ffc0b9c280420cf9e3ddd270efd86fecdd6..7c7203bb4d43c36f3ac3f62e100c2bf6062a7939
GIT binary patch
literal 16384
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5832b526fd5d6d043f291cf2147909460bb168a2
GIT binary patch
literal 1357
zc$_tq30DmW7=|B>rifIsg}AAyv{I?4Xw{w;L`_L5N{b3LF{EsjQrTK?(I|sNiEcy6
zC1fu`hzQr9A&jLmnRC47d%qv>obx_z)?O1pT9^ML?biE#>TRFzC#Sky`PSR~5A|_!
zv!jzr##X18BzW(=I^KVF^3(a<l^OSHpS$X;SzjCG?2x~y(>gQB*LK;`R*U+q+jRmx
zndn?E(W!KOo&C|q?w3^kq`!{JzME3_O|q!-$nK{f#iEHpw`I?Mdt|P2<jqT)=AkM=
zkJ*DYhYxYN6;W9oX;IYWEF~7omSl_!E;CiF9j#av8&W8ebR_hM3d-&nPMulldEc(i
z$bU(FQd@svOW?c48Q~Lj!uKn>TIpIxsW|mJ>kv_%Z@;Q4Ci<(Adr(QowF^;FmbQsS
zf%4PG3|29Ta+Tbz2@|_lrKxSvj95|FkUi3_%5B@-W;R6J(eOD~qxI?i4-UoA-YZ|X
zXN=SF5$PDNzEW9Z<)2ni?5U`rw$n><W9%{U9HD<*Oi59M`TaRo@767?+El8&yE`HG
zs^;(+ebzL@#VFoy$c_F`r0~QlP*!_@fwaW|`yRIs&7oSy^SgozTlZ(ShAFqKNc3|E
zNU5nGJ4hs0KVYt@O;(k@$&DQqg=QKH0+u~C-BLD3E`0g8VB5X39QC8K3QlU6Z?=44
zk(4jzo0I2$Z`i3&Ve-$eQ{HY}Xyj2PVMCj;WB7*DOPQ_u2HQsLRnqOgE^2ALY0_U}
z(fQ2SLQ)aY5o)9u;y-g=$h6Lws67MC1nts3b~}nH+`LrAR>((w&bxEL@X!@;xZ)2E
zuGwElSeVS05w^W-YG};6w9awV7eg~WfkR)Jf}@{OkDh5tun+DKWUP4UaXPGfUj3^x
zkCzR~JU>ien%K}HTYJ$~Q2kIT$NQ#C*E*kC^|tnq@|w{umflM)`_J@EEj_-*G9tyu
z(@s+|DJ(8cEL-Cq8D4$lr0S3WKhJpmL-H~j1I!*T*Be^CeOL8PrE|^)CuygPoleB{
zyz)N~7g;)HV7$kdJRPm_F`u25WHsfhuT*|5FzJX@+kKEmiJx0X`C+MD!fLT@PTDk^
zv%aZ0qt+%rkfayu8N29v7pxbSN2(W#f=WNhKe=TtpIpCi@@Ivn_Y;O3j;m1$e0{<o
z<g}aecLq|u^XQ8-KO=)I{pgPz0~m-rgHS*bB?hC+5QZ|0;fz3qk&I$As*GVQ0@N6X
zIvV_fCR%7S9vyViLmvYSnZQJhFvf&Qm|})GlbM1AmRK>BX;@=}Eq2)Bz;qmO!WkD_
zal;)CX5fhz-uN(+S<J?lIn2cme|{x^K;{v|e1cg(2%&_rkVS+O!D1qbBAOVM5KA2K
zEM*zXNg$EmNMZ#m`JGj)CYd#)kjh%tv7QZVB#m@3*hD5-Y$lr=a@oRGwy~WZ<dIJS
zh3sS(yAiU7z3iiiKiE$(B^=;S4swXYlyZbJj&h80DmYFhCpbwJBC7d|Q=F!TGt^SY
zS;U;<JoQ}QBA003GFQ0DHLi1mn>5nIEpF4y9qw|E7Vh(ahqUsD$F%W;r?m5o=e$6|
zOaA5+uX#fU|L~S~bn>1LeB@s~@tH4trHgKQ`1W0pqkHw(>q@Eblc<=#C@Ls6F>pap
Md{AKMqQxQn2PeGxi2wiq
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..11c4cbedc674bdc10274658a4cc0cfdfc05e74bd
GIT binary patch
literal 1465
zc$}@2`CAPL7=>S@no=oZgeYA~L`jK8b(QvoNS1PiB2q~-DMhM5_KYpnm`ss`v81b1
zl#pA9kmZI((L&+cMkX_l|6tDZEx*6dImcb-WeN&=`Bw@BT}QrayM3F~5qVqvRFzbd
zRm3s+RZ6)QmyKVgG#ZpRRuu`CK3U!zr=lEq&Q(KWkC|M2p>@p;ooffK3nu6Ho@!`T
zHN|+y2f=y!?ut*s{bVnf9*!11sI+t*{3R_sW9ImZ`wm7jZDUbRN>H)ed%;n$d|v#<
z^`ViOomqzF9q#Y68%B02+B6P*pH(~2{e5nc*7@^VMkCuMC7G3F@4Hp(-|w1-O;Fsh
zxFc8Vy3Y&{)&{@Q^{PH><LlCAw2#E}ROMVBVbPFCxqt!dF3B&9DXSNlWS5IyKbA8x
zU!iu_vgMRjxM0(?y5q|Is}q~nm{g}lU0x=OFqjg$;&A(MyNXaX!<@p0W9)3db0{C)
zmJpNCp{HXVF>hSYu$xxmh`m`BU(?Uj&W@K!&kFMtr<2#avF*i0OMB}r#s=b`jf2v{
zi}N&Et>!+xTR36YY{%>b>6!;R?LnU#RSeTIXZq~BGg31n!*|vilV>&|`iai_bE^!^
zbpwx`OU)eIzj(fVR<pWEeUa>lL|c389UFb8R}VasK6PkaD^oPqkB%uZEpI-$u=eSJ
z4T*Lm(p+}lu8Z{)SjK53%63bYI#z!Csvlmp>dlRy3!R#q1FbtX-?Wb&xV1pwUAARV
zu#%>NoNKb4UhR;f;ty@zbM$&>_ucN`AXSR54s|ZSv$ZPenY45L=Uy`!B;M@@tu&lk
zpMB@xt-N!$r%~Pgossp$kzw*HTZc}H4{P=do+TdGvLL(9*r_L4<(qx3?1>KWE%-Vv
zSrVgnqe7+Sy5XrFK7RQ(M9-T_kCd2x+%NegkXd-0-F8UQbZo}!lWN0AXi1bj*KzQk
z-I1Ac$iO_U(Y8|bDfz9fu2ePFW|d4Kpdq0$UUg|JMHlOR8dLMbZuKk6EA1n4?AFk>
z__{=Af?;gG8lTTCrk<td7MaeLfg2sY_C{@qUZUDLY5MWLQt4=!xPFd_dQG}^`j<4t
z)j1B&r}et4rk-D8Tw+%-$Yh7pu5Brg#+$3TuASxL=KEA`N$w@(d$G2o78R&hjkr88
zBt*1*oyhz@4lMrmZQX@lB0DFchsR_$k+Z!mre>o|{7w1))D5|=D0W8yMU?mfWmM>a
zDr%_H6Ad)!MQ^m|LtlQRAKLV100Yrs5QEW0k0IzIU?{^dz>wh>F@lj8Gm6oeV9FTG
z7>hX;jKh-gOu&ktn20qt*dk;ScGxo+2OM$285dk}!yOMinZi`On8tLxnSl>8nZ<18
z;ENw~5it*c=Cgo>1Q5ugE@(k4VJX2ZBZN@GSWY-Ah+rkFh~#IYSWPrBtRa>-*0PRx
z){{UYN&LbFHnNFiHnWATY-2k+*hvbz_?6x4VK4jGPbvpUBb^L>;~>A2$sztAi);>a
zgdC2N%Q5me&IwNPC#N{g8P1Z=ISLSSo<fSaKrtnha*<1vQO;#5xWZMgQOR{AR8h?h
zZgPtnZgYoP?sAX&)bW6aJmN7@>UqLbp3%TxJg1Q+UhtA;T4<$>SG=a34&LyVcgT3p
t2R`zN&-~37I{Es|06JY&+||o+-@I^oeo%;ic$8?Ne}um%Feo^He*jm_31$EQ
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/data/multiple_signed_pib_mar.sig.0
@@ -0,0 +1,6 @@
+myuujl0MBwyLCrp8I84HrDbGLe+T5yfAR869QWvhcet/CglmuEsQEJczAoK8PtOR
+HcqczCRFtxNRKDPOUC0i+CS7RAQG4XJd9uprqHtB28s4SR/9nXKfWDnH2UYq9hkt
+X6qTab9M9ySrugOugomDb3ej/qYoNfJN9RnkLP4GP+cl96bWPU33rL0Uu7sUKizu
+QoYzxKeZ0r9hGMpOP2l/Jn+pydoEWGVB1mzvIFLPqD9cShUvV80xs8teV0G9IncC
+ZRuBwwNkfMTgJDCnxbrw3gIqNXMN1zjssztyJIpT2q8JGs+F6H0wz515xm32dCdQ
+b3Oo8a9Dx28NKKq83DJDQA==
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/data/multiple_signed_pib_mar.sig.1
@@ -0,0 +1,6 @@
+xSnuhHyf8iEdPnRgNw9w0Tu6dJimNv+etdZagvbftkGuBlQArhPn7unCOEQ+jS0n
+ummJHp1yv64Q7Tte4te8OpRaR6eE333bHfFp++cKKJ2jWFeZ1SwRn59cWX0w4z9u
+I4VJmqzOKDUgZq24m6MfGr9iRKbrDjDgvfapzRkZNNU/I0jv20+G+vIUysQGLSN7
+fMAxxeurZNbinIiFQyudSpLU2n5PJDh/FIX2lt8H8nX5/yNyznbz0Gm+/hGMZj7+
+EfPxLxyOuSRVxI0ebAcRBQJLiyEh1iIluvjuBKohCxkWkEZG3weCz29JxdWOzobj
+3/6D+xJW5M1V8aE7EEjt4w==
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/data/multiple_signed_pib_mar.sig.2
@@ -0,0 +1,6 @@
+VQmSlk8q2tmSd+C/d3ADde0lQoJw611sWboc7GOnFTRRsO0D61fIl3tlXLn/N4vN
+fCHQxwrszCizC1ddt9Bf5ujBqcAx+ZrN+iT2NlK2j6TN6K1W8LENJgCE7IXN5h1G
+VVryo5OkJzWd50DLX/qL9EAg3wx+P3b2BWXkhMuCDGvtAL3C4Ffnm7dw0hjErsEV
+X0cB5O5ozM0dOih+GNNX++wxT8E1NqNJOGaJR1KYeY17agz+QlSvFt/fL/a64Fsw
+DtOeGZ79nQZ6qkbmUxLXDQ630y3AQ8ceMJFIlI+T3Tk1DUuAWUpMXOICaqzDCdh+
+QC5nuQ7OK8Ycbm5fkIFfNQ==
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f31f1df20668365a67e3fc10c5159e3b30d6b59d
GIT binary patch
literal 1465
zc%1E&YdjSO9EKl6Bdk>#Mb}fzrJQs`t(=2MxkQ(vqBDgOAt@74x@@wpT6Cdp8j`4K
z2^mUM99lL?7ixxuZmXlT6x*kL-^abb|K<Dhd*9bZ;vodpWd4?FLElm7Tc!RvYLc_Z
zTXUdhf<|HV_?m&)mKA1C659+*9czjufe+So#A>MfUzj&|aFT^`+{IaS`wgxis}{`8
zAL?#oQ8V9c+$%wm{Q$KGVcv>IftgW~J5^Rrqu-{49rv`ZyzOAD&@+=|B?go@cMDDx
zs^rAQZwrY?>rFQjJ$HGj*J9eMX45vIJH39UOLunhu%e=2#-@*LcUqKZ9KKoNqkG-e
zCLnfV?8$4513djC^+8X@c+_Uvc+J(BER_rMs+LP7#p5EB{YGxRtl}G8-YhWBxKjA^
zzOu1sy;g%&XWoo3!L9|3x#}ZoceHOduT6=pSgQy(oFB42^I5K4Wr&th*2TM1?8N_b
z_&0||>IV+F88_ZO5Yb!`5vsDGYl3ZDXoq*ul0yB?l^Hry-OqHXbV#ozMfrIZyqlIF
zj~;uYQlqolC~uI|JO75PwY}_Qsqpm?d5=J0>2ZGV33>bJ#ZS*_O=NrL4tcAA!=k0n
z(-Kb@ic;FdRkEIh7veEZnlUyT6{>zM+pFR<1G^}`)GTc~njd;ow>+mzN9L&1@_2Q%
z++ey<jBcIuO{dVUOk|nnWaS_4=#d<`J8F$)ukFIz;Z04G6@|^q%(d%M^-|xasBOw}
zXk9R*K}$Qo&aBj~a+LZ0IS2P9-m?~I&D*kMuCv!e<u%!t)o;a!C#@>bt}&_54-S^?
z+bR?N!-3WPU+g0BklD?VxVp}EmO0sr5n4<#_Yv~nNeSh?C=NgsH3sql>S!<sO|;PF
zLk9B^Lm0|1bQsRZ=%U96Mxu`aqZrK?#xjoa2$;Y`3^C#pj4{C!GbS+^bA(L6f~knG
zWExgjGo2ZH%1mZqgBS_6*kR9X9B^a~PRwN<&bZ);8}o6;g9R*P5sQ)H$r6^b3@^M{
zj*QRnVFfGk#Sed0^+5|@4S@u)mS92%WgTIxC!7szB!bV0WD`+DvzZuT*}_)h*v58t
zu#+$Nl6ZEJz*p>M4}00ieh!exLB8e?NhEWaBOK)zDWr0oZ}^t)NaF<GlTHSioFt1=
zWOJGvayi3Uejtx?{K$Foxj+Gh6mgMaexig@%DBX3%DF-Xm0aZ-*Quf!IW^RBgP*xc
z9l!7^^)zsc+ca{AyWHbGO*Hd>hx|qhztc(^?fk(bI_RW}$2{RF&v?$Cyx=7Yx_QNG
qdU(TMyrq|S{RS}TtKz<1R_^!0xfKDyK4FnEU!QOvnSVf#AMXLyT><9+
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..625b80e683e39cf470c6a298db4754d68ad4c286
GIT binary patch
literal 679
zc$_n6Vp?p_#Mr%nnTe5!iIrhys<X8LFB_*;n@8JsUPeY%Rt5uMLqP+6Hs(+kW?>HB
zN(JZCq7owmIdNV?BSQlNV?#3|Gh_28AlKNy5YEM}sfkeu*>FZy2IeM4eg>d87gG}>
zBg2hKjU^h*iXT3>v3L|;^s1~%Yb!plW4O1}ymb+OqxFtb`?c!rTlDU}3ef+exm#=h
zdEM28`z95yYPiDKQJ5k;LHym59g&Y_R57ZbII(AzU%~zUeWpCW8RnT*)@`{tOTMAO
z=RxI#2Uj;A?8)=#Y5jlk<FVMOLDCw389)E=)K_%Mw(U)pJJ$6&(EG+Uo&|oK`Myaj
z|D21NYR|BInWJ1F>mA;m_g61k7g#wze??>Uii<~MkG)(bCRP4$t^aII<Ab}!dc!5t
z#P84LyIiK|!5UfVTx{mN;jWXWv|NG3+%L=JUi>s>JFQ=~^ZMrJaw~u8nYoHppFQlo
z{@%(I6>X8kbh(L4%!~|-Xu;163YZf2nVpIY%6L1M2<XjP8g6rJZvAYw4K_>C&FlT^
z4tU@2Wu3sbaJ6d5i(IQt@4t-2JLk`IZ(0znv+UWFNvzE5DW!Fjd~KC-)ms+F7IA&B
zK5#HcL;mE;`#0Y$xW8$?xsLp-`%i4@w^W((^w`%odN<roc$ZcZ-F|+G$1L7ps|ocr
zb5~7m+8nf;Pft=Ly<q{jLfYjCDQWQ?YqT3HS5|xcdS>#$vbLO|;w)2f|9OX)i4$Kx
zKECKgN<--6`FC0Rch=~*Fy6KoS>t=wXW6ZbM}AaiEC{NO+sMCc*|QC-R@!@vgTlC`
jBzmY{v@&w6(pWFMPSr+OQdC+0!rr=qOOq<!b8-RzC6f>+
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bff05a6cf1b215b502de798a58117f047fb9608d
GIT binary patch
literal 679
zc$_n6Vp?p_#Mr%nnTe5!iIrhys&lFVFB_*;n@8JsUPeY%Rt5uMLqP+6Hs(+kW?>HB
zN(JZCq7q{RIdNV?BSQlNV?#3|GgI>@AlKNy5YEM}sfkeu*>FZy2IeM4eg>d87gG}>
zBg5v2i(?L4YP&P#k5@zt_u_wTF<e`Vzj(f?S*h=#vE|=v<$d!6H@;eZZEn(a2I-3A
z2B8fdOl!3Sl$^G3aJLI{y-eD4ap&pRHapzzrY%0RsH1E9!#8KLu9S*fsvI;G|9ZUj
zpq1&qFW*h;N-rjQiriiJOQ`aoK}1gQKfT-L7dkAATLLC^oBTGMbMQzq>*Lm4KJxr+
zZHGl3t#6g)`n~b3MDdzD{T|s(vTl_MTWS{m7QJWT9_Vi~C(I^3vF6_U)B4861rM*7
z-j#8guxlO9waG8v`PA{WWa{*m_`lEicQE;(CP&Pa50@(HZ`mXzub$2KQ1oG)RdUD#
ze}>Ae^)n5ZpXhqe#LURRh!*_Jpn#dVVe!<mb=OZVOU+oJ|5Gx1|H5qx_nJ!|IL#V6
z@8iBDEEgvhwa7N*Px9ap{H1?;o5F%WyR24CW?E3Gx!0t(*7uNr<lHI2JQ3@rP3C)7
zX?Jh_QO9MTvp>(BE_3R^Z22!4+qs&p<^>!$x}f*i%H#qE_b)HmpG;3MYETg{vv}&q
z6?IAcSM=#gTldXs`IS)qF67+7@V*J#k1Vf<R@uKZ`;VeeWzM>H1-aW3o%a`|Xf(U<
zn3|T=b&GCufAOcF{^Z>mt1ch(zkEXZ@{D#ind;>C2KJBY3cBikM`TzAvf8hj!BM)P
lb7HW67<&u<wW!*Fhg;J;5BBm&N(8;D)~UaI(RuotKmgTs9{T_Q
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/data/signed_pib_mar.signature.0
@@ -0,0 +1,6 @@
+VTw+yunNUglvAxNevIP1A+1aWNgD479tbZy4t8uDuC9AJ0nLeLXYBfklGxaKCzo4
+/UdWFfv1gJmqbnCAlZrZ9UJLUVZOUAwKb+V90bC7pBnGy7xplKBDm40SIs4fwWZl
+EGRt8GCPjYKgsYp+jScNMD8B4J3bPSR7m0c1TjXV4pZMhT7LJ+iLfHiy5+WiGBgZ
+9qor7plYxfZFgg4moAA3iIXIJbNORUEWfz9b7rsMmiwZO4XmMSDNUutkj9Jl+9gB
+XRwrwL0QLvAuYwIzB0HDdl/LPCC+UDEMKigcPhjwFnpN17qUks0fRxId8e4P8m2H
+rumgMHGhwx3uagGTTufQSw==
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/data/signed_pib_mar.signature.mycert2
@@ -0,0 +1,6 @@
+wYPEMA2nfrMfkL5+//r9Of8JicdmG5KxAzYUhSR3d/vr075LhGkc6nQc5orDIRnz
+vuMBRIqoAsK3IdtCEbZ6rRKN9d+N7HfvmLdDXGpK3lr9NKKTnk1n/0o7ziRi3Fm8
+hOkJpdit7OHV0RH5GBSM8tQziXGN+qe51W2otMA4d8+oa4tp2D7W4SSUxxJwBPiu
+5CJAA68qaxzhWd5iVtU8mcjbYOKZAciIPgqBxhgmukqNrShQpnwcop/WHggL7lxI
+QWZYpuU6MMxVmLSiAAEAwLMwL2UqHxcGQjIuepu9ikbJ251SwxYiH3xRMkYpQNNv
+YFEb9pm2HJq9oNgZUGakDQ==
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..41ad2b3f99226434d0d545de655c7a5d45313de7
GIT binary patch
literal 937
zc$}@1X;%yY7=@o{Y$23T8d9=_Ohxw0RFWtaB4V0o7}>>;WGgDhRw%nrS;|t%R#H*c
zkP;EeF8dzIgrDG^bKm><-t)XJQV&ZIhVe(j{hA~AYXRq$=ZJOUgOiQdWdv7OR`ssd
z4$oB>Z8$2l(GN9KseZgF$QTl$6uk@(J&njVG5nZOE^vrQ(8@|PDY4T{4NTNie}15@
zP`!;#v-h6jTso$zdu-$mmr2#meXp7M-t#&W@<MxGabiVT;SJsI2KuW%+_DazrLK%W
zSvV~rC9{_*Kf!;Mve>q$%q%KbZ)UA;Nsmn>!o%kNqB5^iU(dpRF?l6Ek75LQVYc;`
zUNC5X%2}P*(Jd}W6uYyib-e~}yq%<q(Q!}uh_8%{PKnjj$xIe^^D{SYQOC~8B5-SZ
zgyY4M?H<|9O^t)xtsFZ^3TF7Y8-0mM6>Ul1Rcz>{*sJqzi^(-9agln+?46{pt^<e4
z20KWww6+k-E%|>EpoNfH)TRz~sfRZ8X+T4CXhdV0po<<&(We>BX@LQTw8RJzt!Rxg
zZ7{)<wwTe5_H>{loiL{}UFeDh-4J7m71ng82R8Ji7rp62U;5D>TLvIOiXHYiFc3!u
z;lyB^8NyIpaAg>7xZ}ZaMlcdjMll*M#xNFd#xWilKFIN90u%8=!6eOSN~Yk?RHiYV
z8O&rBvzbExbD4*V`79uig#@vPU>384r7UAPA%wDmm4p#a1glt$nl-Ft9qZXZBvEW+
z6Pt-9hAnJm8{65zPIj@ISoW}&eZ&z@0{c0@K@O2f5{Eg$QI3&J3dcz$jT4;Y6sI{u
zIvJcLlXGN|O%CU|KrVS)<Pw*;!d0%3&vkBalLBs0ND;Rw<_>o$;U4!X<pB>V;}PXN
z<_S-E#&cfql2=slnm4@V9q;+TM?UeHFMQ=2->KvWKdIsu)inUL(lDU;L#-MXQhb%u
S<+B&aCdvckGKF%gAHM+)iVgGt
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9c0c213c7c24413520db7087d08394cd5e7a6b32
GIT binary patch
literal 1357
zc$_tq2{#o87=|B`(5O*elaf-Fh=jC|VoEi%NrXwd6-81aQBt_HXwzoWVoD`zjYu;!
z*HR)y33t>)sYqoTOC-ZE=k%WM{eHl6&igF3Ty6*h_4q#n7x%pZeU<7zsS`1e4kcdg
zlvE4#b*+5OE{B!cJvAHYd#_w{eqLaa^FE(EC5xJk`HA+m^7HisUAxZC-dz1EX?)6?
z{G6#FcWkyFu224O+iq?|afCSdb!bx2)h}9k9clhP&ZUYe3$2YBJfBZ+>1?j(FibpQ
zEs9(5L3Wmc^xOaox&0YSO$7^2?{~<Yso<@0y})&PV(#OIW?^z!vl2UdFj$rr=sTqD
zUH59cHcO2Q^*%AVW<$S>%?NK)OFF2jTh%-wxBjlGeX@7x7K#6-$^uJ`CE{taN>+!O
z&Uoursf0%A3?Cc3CFV%(__#@X3v(7|m&E6;>NVBQ7!upw(pWg#(=@f?<Xr9iZyavP
zP-Zb9qP`+Iz(>uqSz@eNQr8qJFEgm*sGE%c4%rN2>C)OVb78`KolgTo3TB*lz5Ax7
zC$m^{Vo0mBw6BU@UfOfN?80>E=f`gpMSV6)?^Uy17pp8&9v|RT+#_|bBhWS4qw+w;
z#Y^T!PUBt|dR`3w+BQXXQqvCG8jVfI4t-8BeOpp)EbehjzFj3*(V%F5YJ|n8!&8i7
z&Cf>nYA^6w^p@Z$oms64`aX@0hHhy$ucydGM^v`ymq%R4bJ;UJQt$p4uTv@+DN?D;
z57fnqP9?+luZ;5A<#0o*Jzs2G<|y8Kd1tq1bg}TgqpJ3^qxXyr_XTCO%~w4iyXJ+`
z%WoV^cRpNt(X+_g(os`4w<9Hcw0wL_SmDC%YqbmFD&i(S*eo(k2u(|uwDzn$5w8)T
zJGHLTeQ?%>_S{zOZ58F+g0M{a&d9}sm)3Qh<ll2wY_?QC+!l1)pz~^PbXP%vm52Sj
z{3!Dg!bGX9N~u=r%WQi@o95UW=!baBNglU5X>V{b5}Ec_CVRZD_inFImz(^g#v`Y;
zWtFpN(m2&8K3$fJ9@WXl8kq^M=|q|c;sx2#O+trrTFUZi?j=Dj>uXJ<l-Js%H_X^~
zQYS=cVBHn@rYWz@VRv4gmRRyWr&{#d+SNuBl8_%hqiSXI$FhiUJGl?`0#TITFv;P|
ziep_u`x!|0%|nKP$TA2y<WXQS-=WA5hVng13}ZMW5TMKtj6{V|j7F6)s4<pts571k
zXrPG}+Dt?TT_&N2K9ezE3WgYA%v4O6#&l*d6I0BXg*mgCg9VoS$Xw=O#e5d9ke{%|
zhD9u92}@aqEz7ZE1@<_w5=WeH=4V#n!fISugOIhXV?A!Tvw@9x;K?Su*o-$@*oqI^
z@Wqeq__Kom0@=wfg4j(kd)P||p@bnKoCx-@p935ul0zKk2uC?a6vsKiNltN^Xnx@g
zF`VTb=ea;EzjBdF#BrG`TqT}sB#=lF$)u3Vb#Cw*Y1|~83~rIhZGPtu?r@hs5tBtW
zIplJW`{eO}d<u9-A&+=W5yd>=DJ49klrqYZ@D~+4=LLWBl1i$0#cQg0!&}}_LoM&A
z;{zZ0L_G~O(nK@=&_XM1wDXw`I{88uU+Lywdg$#J<d{A^_PJ8Je-gLWdUy-B1-h;m
M`Uzd#J-pob4<(TijQ{`u
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3c765fd9354eda4fae35597be1d9c2693250aa80
GIT binary patch
literal 1465
zc$}@2`8yQ|7=>RXT82tdVOlMzM3QO>SHz90(hyDPTJB_RQ_?2w(JF~bw4qHhmb6Tx
zQ9>nBV~eIp*&-zojb@(ygE`N){Qf@Y9Ff3IA7rEWSF)KsN2X_`zRkW>_nVA;1pDmE
z9^6V*uZ`XpdOK{7ui=q&m#LSY3!?L-a-4-TV*CUKP5SlHac8o;);USP{Sez#lh-@e
zXSS!Fci1KOH;InB#D{#+QF54+usCUx`$icfmFfyz*QIF%eVhyOg5}Nn4Ul;?Bz9Pm
zrn1S(;$0Ixq#o2nO9#s<d8cYC6tzuj7D)0GY%gv%do`u#@!Ias`nAhb1|@WMXzO@{
zIxo9(&~0AfM#q5(*2Y77Y3fhz5<VGdRPO9&tbMMJrFys3F=@0a$Q>~%qQWgkN2aqX
zsI5t5y;a5tJ4M@5=?%rE&BxD&o^Lj?)|s9k(6nH`d3Dw)HIq@b%9g6a?;Mn$x!kp_
zmVcP_NjRb3#VEfF%K~`~wF&-`%T}$Ubt4=)D+6`w#%KHWc9$dsxV*evSbEnXeo6e5
za#gh>t5YLA*Vb8Ga1QI|61!}9e22!kgtry9*1ezMbY{gzRpFl3hnzM?wq=Z2rscl1
z<7|LuQe=L`^hrVEWh_O$?)_7=#0LA7vIWU{Zi?Oh^;%_tBEy!fn35Yuax!g-#7{NL
zO>AzqeVHm}UfN+C*C`v_<|vsZ=bpFYk@eD!*AFv~w%;2m5+yI4X%XczLe(NhF5kr4
zeCOHrtFlA9LKd$7deC0sc5UcGua=PDLes_v(^J(WRBuL=TdM~IZaUpMa-;dYr@rsL
zb4cDRbyBpWtg2|@Q15zaGtJ;G;R&C%L{EpYUu;ZY*mYF-czB+yO!*jS?3kjrAYuMs
z`$UVceU%KX^1}s5g&~Vf8*H{;l6>7+a%xL)O7h*^%G0u<S9VPdd6hHFLGAU_oEwvh
zbYFJX+qDR`4a_L%K4x)ky(mt5i`)Xk(z38HtH}piZ~DfVD*NSr^sjDA%#A4wY?#}U
z8kLl@vL-uQ$1pA2_gMTkN!Mc6u!XXvdzNh2TUj0#thgq5Z1NL_aE*oll^V@+_TtXO
zc6r-;i#aNiLAlpo8f7auUEffbm1)+dyP{xGTE%I(8?BLjCdVddq+M;aJ^Vz`@P3%E
zym<JwK|lV-fo0#msYqbwI@?NMYinWSDl|7m-#}MStk3@^DWrR%*b7;DBZoYF=u1EP
z^8*SfGJt^$LW#i)K^YZ>GK}G<qJ}y@GJ=teVl-nIiw2r#q0KmS7|#SIqRS-o&}T9R
zOu>+;7%`3M%-|<xG7Do&FhzhFvoXhlIap$akhxf6g9uyZF`ot4v5=p!$ALvS;>2Qp
zVF}K-;L1|OxZ#cmo=Et$2bve&_~45l{w!w&D_O;A0$9UZ0$E28>)F6YHnEvtwh+Qr
zwy~WZ>|__a*+VE{>}4POIlw^<5zb*EI6@>xi6WX~9OncvoFtYwPH~zuoaG$xB#_8?
zE^v`cTqcPtBy*J%ej}AM(#hZ&nOx@vH@U^{+~yCmxWk`hbC-MMaGzZA$manMc|-w^
zDWr%e6!Vm4{6z_+lu=Fv&#9z}7yQjjUQtaAuc_q?Z>gi61{!JN9nG}xo>o5akv2Zj
r&SyI4<O^MN^Yxnn)O)J9r<bL_c_H1+%U`@I$kjs}Aa<2_`FipX74#4R
--- a/modules/libmar/tests/unit/test_create.js
+++ b/modules/libmar/tests/unit/test_create.js
@@ -66,11 +66,11 @@ function run_test() {
     cleanup_per_test: function() {
       let outMAR = do_get_file("out.mar", true);
       if (outMAR.exists()) {
         outMAR.remove(false);
       }
     }
   };
 
-  // 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);
 }
--- a/modules/libmar/tests/unit/test_extract.js
+++ b/modules/libmar/tests/unit/test_extract.js
@@ -80,11 +80,11 @@ function run_test() {
     cleanup_per_test: function() {
       let outDir = do_get_file("out", true);
       if (outDir.exists()) {
         outDir.remove(true);
       }
     }
   };
 
-  // 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);
 }
--- a/modules/libmar/tests/unit/test_sign_verify.js
+++ b/modules/libmar/tests/unit/test_sign_verify.js
@@ -4,84 +4,186 @@
 function run_test() {
 
   /**
    * Signs a MAR file.
    *
    * @param inMAR The MAR file that should be signed
    * @param outMAR The MAR file to create
   */
-  function signMAR(inMAR, outMAR) {
+  function signMAR(inMAR, outMAR, certs, wantSuccess, useShortHandCmdLine) {
+    // Get a process to the signmar binary from the dist/bin directory.
+    let process = Cc["@mozilla.org/process/util;1"].
+                  createInstance(Ci.nsIProcess);
+    let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
+
+    // Make sure the signmar binary exists and is an executable.
+    do_check_true(signmarBin.exists());
+    do_check_true(signmarBin.isExecutable());
+
+    // Setup the command line arguments to sign the MAR.
+    let NSSConfigDir = do_get_file("data");
+    let args = ["-d", NSSConfigDir.path];
+    if (certs.length == 1 && useShortHandCmdLine) {
+      args.push("-n", certs[0]);
+    } else {
+      for (i = 0; i < certs.length; i++) {
+        args.push("-n" + i, certs[i]);
+      }
+    }
+    args.push("-s", inMAR.path, outMAR.path);
+
+    process.init(signmarBin);
+    try {
+      process.run(true, args, args.length);
+    } catch(e) {
+      // On Windows negative return value throws an exception
+      process.exitValue = -1;
+    }
+
+    // Verify signmar returned 0 for success.
+    if (wantSuccess) {
+      do_check_eq(process.exitValue, 0);
+    } else {
+      do_check_neq(process.exitValue, 0);
+    }
+  }
+
+
+  /**
+   * Extract a MAR signature.
+   *
+   * @param inMAR        The MAR file who's signature should be extracted
+   * @param sigIndex     The index of the signature to extract
+   * @param extractedSig The file where the extracted signature will be stored
+   * @param wantSuccess  True if a successful signmar return code is desired
+  */
+  function extractMARSignature(inMAR, sigIndex, extractedSig, wantSuccess) {
     // Get a process to the signmar binary from the dist/bin directory.
     let process = Cc["@mozilla.org/process/util;1"].
                   createInstance(Ci.nsIProcess);
     let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
 
     // Make sure the signmar binary exists and is an executable.
     do_check_true(signmarBin.exists());
     do_check_true(signmarBin.isExecutable());
 
-    // Setup the command line arguments to create the MAR.
-    let NSSConfigDir = do_get_file("data");
-    let args = ["-d", NSSConfigDir.path, "-n", "mycert", "-s", 
-                inMAR.path, outMAR.path];
+    // Setup the command line arguments to extract the signature in the MAR.
+    let args = ["-n" + sigIndex, "-X", inMAR.path, extractedSig.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);
+    }
   }
 
   /**
-   * Verifies a MAR file.
+   * Import a MAR signature.
    *
-   * @param signedMAR Verifies a MAR file
+   * @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 verifyMAR(signedMAR, wantSuccess) {
-    if (wantSuccess === undefined) {
-      wantSuccess = true;
-    }
+  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());
 
-    let DERFile = do_get_file("data/mycert.der");
+    // Setup the command line arguments to import the signature in the MAR.
+    let args = ["-n" + sigIndex, "-I", inMAR.path, sigFile.path, outMAR.path];
+
+    process.init(signmarBin);
+    try {
+      process.run(true, args, args.length);
+    } catch(e) {
+      // On Windows negative return value throws an exception
+      process.exitValue = -1;
+    }
+
+    // Verify signmar returned 0 for success.
+    if (wantSuccess) {
+      do_check_eq(process.exitValue, 0);
+    } else {
+      do_check_neq(process.exitValue, 0);
+    }
+  }
+
+  /**
+   * Verifies a MAR file.
+   *
+   * @param signedMAR Verifies a MAR file
+  */
+  function verifyMAR(signedMAR, wantSuccess, certs, useShortHandCmdLine) {
+    // Get a process to the signmar binary from the dist/bin directory.
+    let process = Cc["@mozilla.org/process/util;1"].
+                  createInstance(Ci.nsIProcess);
+    let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
+
+    // Make sure the signmar binary exists and is an executable.
+    do_check_true(signmarBin.exists());
+    do_check_true(signmarBin.isExecutable());
 
     // Will reference the arguments to use for verification in signmar
-    let args;
+    let args = [];
 
     // The XPCShell test wiki indicates this is the preferred way for 
     // Windows detection.
     var isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 
     // Setup the command line arguments to create the MAR.
     // Windows vs. Linux/Mac/... have different command line for verification 
     // since  on Windows we verify with CryptoAPI and on all other platforms 
     // 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;
     }
 
     // Verify signmar returned 0 for success.
     if (wantSuccess) {
       do_check_eq(process.exitValue, 0);
     } else {
       do_check_neq(process.exitValue, 0);
@@ -89,100 +191,374 @@ function run_test() {
   }
 
   /**
    * Strips a MAR signature.
    *
    * @param signedMAR The MAR file that should be signed
    * @param outMAR The MAR file to write to with signature stripped
   */
-  function stripMARSignature(signedMAR, outMAR) {
+  function stripMARSignature(signedMAR, outMAR, wantSuccess) {
     // Get a process to the signmar binary from the dist/bin directory.
     let process = Cc["@mozilla.org/process/util;1"].
                   createInstance(Ci.nsIProcess);
     let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
 
     // Make sure the signmar binary exists and is an executable.
     do_check_true(signmarBin.exists());
     do_check_true(signmarBin.isExecutable());
 
     // Setup the command line arguments to create the MAR.
     let args = ["-r", signedMAR.path, outMAR.path];
 
-    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);
+    }
   }
 
 
   function cleanup() {
     let outMAR = do_get_file("signed_out.mar", true);
     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);
     }
 
     let outDir = do_get_file("out", true);
     if (outDir.exists()) {
       outDir.remove(true);
     }
   }
 
+  const wantFailure = false;
+  const wantSuccess = true;
   // Define the unit tests to run.
   let tests = {
-    // Test signing a MAR file
-    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, 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);
-    }, 
+      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/" + 
                                     refMARPrefix + 
                                     "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);
 }
--- a/modules/libmar/tool/mar.c
+++ b/modules/libmar/tool/mar.c
@@ -1,54 +1,77 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include "mar.h"
 #include "mar_cmdline.h"
 
 #ifdef XP_WIN
 #include <windows.h>
 #include <direct.h>
 #define chdir _chdir
 #else
 #include <unistd.h>
 #endif
 
 #if !defined(NO_SIGN_VERIFY) && (!defined(XP_WIN) || defined(MAR_NSS))
 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, 
                              const MarItem *item, 
                              void *unused) {
   printf("%u\t0%o\t%s\n", item->length, item->flags, item->name);
   return 0;
 }
@@ -64,63 +87,105 @@ static int mar_test(const char *path) {
   mar_enum_items(mar, mar_test_callback, NULL);
 
   mar_close(mar);
   return 0;
 }
 
 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) {
     print_usage();
     return -1;
   }
 
   while (argc > 0) {
     if (argv[1][0] == '-' && (argv[1][1] == 'c' || 
         argv[1][1] == 't' || argv[1][1] == 'x' || 
         argv[1][1] == 'v' || argv[1][1] == 's' ||
         argv[1][1] == 'i' || argv[1][1] == 'T' ||
-        argv[1][1] == 'r')) {
+        argv[1][1] == 'r' || argv[1][1] == 'X' ||
+        argv[1][1] == 'I')) {
       break;
     /* -C workingdirectory */
     } else if (argv[1][0] == '-' && argv[1][1] == 'C') {
       chdir(argv[2]);
       argv += 2;
       argc -= 2;
     } 
 #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
-    /* -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;
     }
 #endif
     /* -d NSSConfigdir */
     else if (argv[1][0] == '-' && argv[1][1] == 'd') {
       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];
       argv += 2;
       argc -= 2;
     /* Product Version */
     } else if (argv[1][0] == '-' && argv[1][1] == 'V') {
       productVersion = argv[2];
@@ -147,17 +212,16 @@ int main(int argc, char **argv) {
   }
   case 'i': {
     struct ProductInformationBlock infoBlock;
     infoBlock.MARChannelID = MARChannelID;
     infoBlock.productVersion = productVersion;
     return refresh_product_info_block(argv[2], &infoBlock);
   }
   case 'T': {
-    int rv;
     struct ProductInformationBlock infoBlock;
     uint32_t numSignatures, numAdditionalBlocks;
     int hasSignatureBlock, hasAdditionalBlock;
     if (!get_mar_file_info(argv[2], 
                            &hasSignatureBlock,
                            &numSignatures,
                            &hasAdditionalBlock, 
                            NULL, &numAdditionalBlocks)) {
@@ -184,86 +248,126 @@ int main(int argc, char **argv) {
       }
      }
     printf("\n");
     /* The fall through from 'T' to 't' is intentional */
   }
   case 't':
     return mar_test(argv[2]);
 
+  /* Extract a MAR file */
   case 'x':
     return mar_extract(argv[2]);
 
 #ifndef NO_SIGN_VERIFY
-  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 import_signature(argv[2], sigIndex, argv[3], argv[4]);
+
+  case 'v':
+
+#if defined(XP_WIN) && !defined(MAR_NSS)
+    if (certCount == 0) {
+      print_usage();
       return -1;
     }
-    fileSize = GetFileSize(certFile, NULL);
-    certBuffer = malloc(fileSize);
-    if (!ReadFile(certFile, certBuffer, fileSize, &read, NULL) || 
-        fileSize != read) {
+
+    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);
-      free(certBuffer);
-      return -1;
     }
-    CloseHandle(certFile);
 
-    if (mar_verify_signature(argv[2], certBuffer, fileSize, NULL)) {
+    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");
       } else if (!hasSignatureBlock) {
         fprintf(stderr, "ERROR: The MAR file is in the old format so has"
                         " no signature to verify.\n");
       }
       return -1;
     }
 
-    free(certBuffer);
     return 0;
 #else
-    if (!NSSConfigDir || !certName) {
+    if (!NSSConfigDir || certCount == 0) {
       print_usage();
       return -1;
     }
 
     if (NSSInitCryptoContext(NSSConfigDir)) {
       fprintf(stderr, "ERROR: Could not initialize crypto library.\n");
       return -1;
     }
 
-    return mar_verify_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]);
 #endif /* endif NO_SIGN_VERIFY disabled */
 
   default:
     print_usage();
     return -1;
--- a/modules/libmar/verify/cryptox.h
+++ b/modules/libmar/verify/cryptox.h
@@ -30,16 +30,18 @@ CryptoX_Result NSS_VerifySignature(VFYCo
 #define CryptoX_ProviderHandle void*
 #define CryptoX_SignatureHandle VFYContext *
 #define CryptoX_PublicKey SECKEYPublicKey *
 #define CryptoX_Certificate CERTCertificate *
 #define CryptoX_InitCryptoProvider(CryptoHandle) \
   CryptoX_Success
 #define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
   NSS_VerifyBegin(SignatureHandle, PublicKey)
+#define CryptoX_FreeSignatureHandle(SignatureHandle) \
+  VFY_DestroyContext(SignatureHandle, PR_TRUE)
 #define CryptoX_VerifyUpdate(SignatureHandle, buf, len) \
   VFY_Update(*SignatureHandle, (const unsigned char*)(buf), len)
 #define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, \
                               publicKey, certName, cert) \
   NSS_LoadPublicKey(certName, publicKey, cert)
 #define CryptoX_VerifySignature(hash, publicKey, signedData, len) \
   NSS_VerifySignature(hash, (const unsigned char *)(signedData), len)
 #define CryptoX_FreePublicKey(key) \
@@ -70,16 +72,17 @@ CryptoX_Result CyprtoAPI_VerifySignature
 #define CryptoX_ProviderHandle HCRYPTPROV
 #define CryptoX_SignatureHandle HCRYPTHASH
 #define CryptoX_PublicKey HCRYPTKEY
 #define CryptoX_Certificate HCERTSTORE
 #define CryptoX_InitCryptoProvider(CryptoHandle) \
   CryptoAPI_InitCryptoContext(CryptoHandle)
 #define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
   CryptoAPI_VerifyBegin(CryptoHandle, SignatureHandle)
+#define CryptoX_FreeSignatureHandle(SignatureHandle)
 #define CryptoX_VerifyUpdate(SignatureHandle, buf, len) \
   CryptoAPI_VerifyUpdate(SignatureHandle, (BYTE *)(buf), len)
 #define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, \
                               publicKey, certName, cert) \
   CryptoAPI_LoadPublicKey(CryptoHandle, (BYTE*)(certData), \
                           dataSize, publicKey, cert)
 #define CryptoX_VerifySignature(hash, publicKey, signedData, len) \
   CyprtoAPI_VerifySignature(hash, publicKey, signedData, len)
@@ -100,16 +103,17 @@ CryptoX_Result CyprtoAPI_VerifySignature
 #define CryptoX_ProviderHandle void*
 #define CryptoX_SignatureHandle void*
 #define CryptoX_PublicKey void*
 #define CryptoX_Certificate void*
 #define CryptoX_InitCryptoProvider(CryptoHandle) \
   CryptoX_Error
 #define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
   CryptoX_Error
+#define CryptoX_FreeSignatureHandle(SignatureHandle)
 #define CryptoX_VerifyUpdate(SignatureHandle, buf, len) CryptoX_Error
 #define CryptoX_LoadPublicKey(CryptoHandle, certData, dataSize, \
                               publicKey, certName, cert) \
   CryptoX_Error
 #define CryptoX_VerifySignature(hash, publicKey, signedData, len) CryptoX_Error
 #define CryptoX_FreePublicKey(key) CryptoX_Error
 
 #endif
--- a/modules/libmar/verify/mar_verify.c
+++ b/modules/libmar/verify/mar_verify.c
@@ -12,202 +12,254 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include "mar_private.h"
 #include "mar.h"
 #include "cryptox.h"
 
-int mar_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
  * stores them in the passed buffer.
  *
  * @param  fp     The file pointer to read from.
  * @param  buffer The buffer to store the read results.
  * @param  size   The number of bytes to read, buffer must be 
  *                at least of this size.
- * @param  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
  *         -2 on verify update error
 */
 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;
   }
 
   if (!size) { 
     return CryptoX_Success;
   }
 
   if (fread(buffer, size, 1, fp) != 1) {
     fprintf(stderr, "ERROR: Could not read %s\n", err);
     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;
   }
 
   fp = fopen(pathToMARFile, "rb");
   if (!fp) {
     fprintf(stderr, "ERROR: Could not open MAR file.\n");
     return CryptoX_Error;
   }
 
   if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) {
     fclose(fp);
     fprintf(stderr, "ERROR: Could not init crytpo library.\n");
     return CryptoX_Error;
   }
 
-  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;
   }
   
   /* To protect against invalid MAR files, we assumes that the MAR file 
      size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
   if (fseeko(fp, 0, SEEK_END)) {
@@ -235,177 +287,229 @@ mar_verify_signature_fp(FILE *fp,
   /* Check that we have less than the max amount of signatures so we don't
      waste too much of either updater's or signmar's time. */
   if (signatureCount > MAX_SIGNATURES) {
     fprintf(stderr, "ERROR: At most %d signatures can be specified.\n",
             MAX_SIGNATURES);
     return CryptoX_Error;
   }
 
-  for (i = 0; i < signatureCount && 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");
       return CryptoX_Error;
     }
     signatureLen = ntohl(signatureLen);
 
     /* To protected against invalid input make sure the signature length
        isn't too big. */
     if (signatureLen > MAX_SIGNATURE_LENGTH) {
       fprintf(stderr, "ERROR: Signature length is too large to verify.\n");
       return CryptoX_Error;
     }
 
-    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 
+  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 > 0) {
+  if (numVerified == signatureCount && keyCount == numVerified) {
     return CryptoX_Success;
+  }
+
+  if (numVerified == 0) {
+    fprintf(stderr, "ERROR: Not all signatures were verified.\n");
   } else {
-    fprintf(stderr, "ERROR: No signatures were verified.\n");
-    return CryptoX_Error;
+    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;
+
+  memset(signatureHandles, 0, sizeof(signatureHandles));
+  memset(signatureLengths, 0, sizeof(signatureLengths));
 
-  if (!extractedSignature) {
+  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
      Bytes 4-7: index offset 
      Bytes 8-15: size of entire MAR
    */
   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;
     }
-    signatureLen = ntohl(signatureLen);
+    signatureLengths[i] = ntohl(signatureLengths[i]);
+    if (signatureLengths[i] > MAX_SIGNATURE_LENGTH) {
+      fprintf(stderr, "ERROR: Embedded signature length is too large.\n");
+      goto failure;
+    }
 
     /* Skip past the signature itself as those are not included */
-    if (fseeko(fp, 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;
 }
--- a/toolkit/mozapps/update/updater/archivereader.cpp
+++ b/toolkit/mozapps/update/updater/archivereader.cpp
@@ -39,17 +39,17 @@ static char *outbuf = NULL;
  * @param  name The name ID of the resource
  * @param  type The type ID of the resource
  * @param  data Out parameter which sets the pointer to a buffer containing
  *                  the needed data.
  * @param  size Out parameter which sets the size of the returned data buffer 
  * @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) {
     return FALSE;
   }
 
   HRSRC resourceInfoBlockHandle = FindResource(handle, 
                                                MAKEINTRESOURCE(name),
@@ -61,40 +61,40 @@ LoadFileInResource(int name, int type, c
 
   HGLOBAL resourceHandle = LoadResource(handle, resourceInfoBlockHandle);
   if (!resourceHandle) {
     FreeLibrary(handle);
     return FALSE;
   }
 
   size = SizeofResource(handle, resourceInfoBlockHandle);
-  data = static_cast<const char*>(::LockResource(resourceHandle));
+  data = static_cast<const uint8_t*>(::LockResource(resourceHandle));
   FreeLibrary(handle);
   return TRUE;
 }
 
 /**
  * Performs a verification on the opened MAR file with the passed in
  * certificate name ID and type ID.
  *
  * @param  archive   The MAR file to verify the signature on
  * @param  name      The name ID of the resource
  * @param  type      THe type ID of the resource
  * @return OK on success, CERT_LOAD_ERROR or CERT_VERIFY_ERROR on failure.
 */
 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;
   }
 
   return OK;
 }
 #endif