Bug 731382 - Security enhancements for silent updates. r=rstrong. a=akeybl
authorBrian R. Bondy <netzen@gmail.com>
Fri, 24 Feb 2012 16:29:41 -0500
changeset 88508 18840d884c978cc86cbc1f96599248d93be2c307
parent 88507 b5ef80040f7222e2e89d6f00b5851690d694b7a8
child 88509 fcaf9e4c7965de9b7d84a84ec4456cc9115b7f09
push id674
push userffxbld
push dateTue, 13 Mar 2012 21:17:50 +0000
treeherdermozilla-beta@e3c4c92dec31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong, akeybl
bugs731382
milestone12.0a2
Bug 731382 - Security enhancements for silent updates. r=rstrong. a=akeybl
browser/config/mozconfigs/linux32/debug
browser/config/mozconfigs/linux32/nightly
browser/config/mozconfigs/linux64/debug
browser/config/mozconfigs/linux64/nightly
browser/config/mozconfigs/macosx-universal/nightly
browser/config/mozconfigs/macosx32/debug
browser/config/mozconfigs/macosx64/debug
browser/config/mozconfigs/win32/debug
browser/config/mozconfigs/win32/nightly
browser/config/mozconfigs/win64/debug
browser/config/mozconfigs/win64/nightly
browser/confvars.sh
browser/installer/package-manifest.in
build/Makefile.in
build/update-settings.ini.in
config/autoconf.mk.in
configure.in
modules/libmar/Makefile.in
modules/libmar/sign/Makefile.in
modules/libmar/sign/mar_sign.c
modules/libmar/sign/nss_secutil.c
modules/libmar/sign/nss_secutil.h
modules/libmar/src/Makefile.in
modules/libmar/src/mar.h
modules/libmar/src/mar_cmdline.h
modules/libmar/src/mar_create.c
modules/libmar/src/mar_extract.c
modules/libmar/src/mar_private.h
modules/libmar/src/mar_read.c
modules/libmar/tests/Makefile.in
modules/libmar/tests/unit/data/0_sized_file
modules/libmar/tests/unit/data/0_sized_mar.mar
modules/libmar/tests/unit/data/1_byte_file
modules/libmar/tests/unit/data/1_byte_mar.mar
modules/libmar/tests/unit/data/binary_data_file
modules/libmar/tests/unit/data/binary_data_mar.mar
modules/libmar/tests/unit/data/cert8.db
modules/libmar/tests/unit/data/key3.db
modules/libmar/tests/unit/data/manipulated_signed_mar.mar
modules/libmar/tests/unit/data/multiple_file_mar.mar
modules/libmar/tests/unit/data/mycert.der
modules/libmar/tests/unit/data/no_pib_mar.mar
modules/libmar/tests/unit/data/secmod.db
modules/libmar/tests/unit/data/signed_no_pib_mar.mar
modules/libmar/tests/unit/data/signed_pib_mar.mar
modules/libmar/tests/unit/head_libmar.js.in
modules/libmar/tests/unit/test_create.js
modules/libmar/tests/unit/test_extract.js
modules/libmar/tests/unit/test_sign_verify.js
modules/libmar/tests/unit/xpcshell.ini
modules/libmar/tool/Makefile.in
modules/libmar/tool/mar.c
modules/libmar/verify/Makefile.in
modules/libmar/verify/cryptox.c
modules/libmar/verify/cryptox.h
modules/libmar/verify/mar_verify.c
testing/xpcshell/xpcshell.ini
toolkit/components/maintenanceservice/Makefile.in
toolkit/components/maintenanceservice/workmonitor.cpp
toolkit/mozapps/readstrings/errors.h
toolkit/mozapps/update/common/updatehelper.cpp
toolkit/mozapps/update/common/updatehelper.h
toolkit/mozapps/update/common/updatelogging.cpp
toolkit/mozapps/update/common/updatelogging.h
toolkit/mozapps/update/nsUpdateService.js
toolkit/mozapps/update/test/chrome/Makefile.in
toolkit/mozapps/update/test/sharedUpdateXML.js
toolkit/mozapps/update/test/unit/data/complete.mar
toolkit/mozapps/update/test/unit/data/complete_win.mar
toolkit/mozapps/update/test/unit/data/partial.mar
toolkit/mozapps/update/test/unit/data/partial_win.mar
toolkit/mozapps/update/test/unit/data/simple.mar
toolkit/mozapps/update/test/unit/data/simple_no_pib.mar
toolkit/mozapps/update/test/unit/head_update.js.in
toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update.js
toolkit/mozapps/update/updater/Makefile.in
toolkit/mozapps/update/updater/archivereader.cpp
toolkit/mozapps/update/updater/archivereader.h
toolkit/mozapps/update/updater/dep1.der
toolkit/mozapps/update/updater/dep2.der
toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der
toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der
toolkit/mozapps/update/updater/release_primary.der
toolkit/mozapps/update/updater/release_secondary.der
toolkit/mozapps/update/updater/resource.h
toolkit/mozapps/update/updater/updater.cpp
toolkit/mozapps/update/updater/updater.rc
toolkit/mozapps/update/updater/xpcshellCertificate.der
toolkit/toolkit-tiers.mk
xpcom/glue/nsVersionComparator.cpp
--- a/browser/config/mozconfigs/linux32/debug
+++ b/browser/config/mozconfigs/linux32/debug
@@ -1,10 +1,11 @@
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
+ac_add_options --enable-signmar
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
--- a/browser/config/mozconfigs/linux32/nightly
+++ b/browser/config/mozconfigs/linux32/nightly
@@ -1,11 +1,12 @@
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-codesighs
+ac_add_options --enable-signmar
 
 # Nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
--- a/browser/config/mozconfigs/linux64/debug
+++ b/browser/config/mozconfigs/linux64/debug
@@ -1,10 +1,11 @@
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
+ac_add_options --enable-signmar
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
--- a/browser/config/mozconfigs/linux64/nightly
+++ b/browser/config/mozconfigs/linux64/nightly
@@ -1,11 +1,12 @@
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-codesighs
+ac_add_options --enable-signmar
 
 # Nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
--- a/browser/config/mozconfigs/macosx-universal/nightly
+++ b/browser/config/mozconfigs/macosx-universal/nightly
@@ -2,16 +2,17 @@
 
 # Universal builds override the default of browser (bug 575283 comment 29)
 ac_add_options --enable-application=browser
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-codesighs
 ac_add_options --disable-install-strip
+ac_add_options --enable-signmar
 
 # Nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
--- a/browser/config/mozconfigs/macosx32/debug
+++ b/browser/config/mozconfigs/macosx32/debug
@@ -1,11 +1,12 @@
 . $topsrcdir/build/macosx/mozconfig.leopard
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
+ac_add_options --enable-signmar
 
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 ac_add_options --with-macbundlename-prefix=Firefox
--- a/browser/config/mozconfigs/macosx64/debug
+++ b/browser/config/mozconfigs/macosx64/debug
@@ -1,13 +1,14 @@
 . $topsrcdir/build/macosx/common
 
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-accessibility
+ac_add_options --enable-signmar
 
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 ac_add_options --with-macbundlename-prefix=Firefox
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -1,7 +1,8 @@
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
+ac_add_options --enable-signmar
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 mk_add_options MOZ_MAKE_FLAGS=-j1
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/nightly
@@ -1,14 +1,15 @@
 # for pgo
 mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) $(MOZ_OBJDIR)/_profile/pgo/profileserver.py'
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-jemalloc
+ac_add_options --enable-signmar
 
 # Nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
--- a/browser/config/mozconfigs/win64/debug
+++ b/browser/config/mozconfigs/win64/debug
@@ -1,10 +1,11 @@
 ac_add_options --target=x86_64-pc-mingw32
 ac_add_options --host=x86_64-pc-mingw32
 
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
+ac_add_options --enable-signmar
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 mk_add_options MOZ_MAKE_FLAGS=-j1
--- a/browser/config/mozconfigs/win64/nightly
+++ b/browser/config/mozconfigs/win64/nightly
@@ -2,16 +2,17 @@ ac_add_options --target=x86_64-pc-mingw3
 ac_add_options --host=x86_64-pc-mingw32
 
 # for pgo
 mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) $(MOZ_OBJDIR)/_profile/pgo/profileserver.py'
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-jemalloc
+ac_add_options --enable-signmar
 
 # Nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -38,26 +38,33 @@
 
 MOZ_APP_BASENAME=Firefox
 MOZ_APP_VENDOR=Mozilla
 MOZ_UPDATER=1
 MOZ_PHOENIX=1
 
 if test "$OS_ARCH" = "WINNT"; then
   if ! test "$HAVE_64BIT_OS"; then
+    MOZ_VERIFY_MAR_SIGNATURE=1
     MOZ_MAINTENANCE_SERVICE=1
   fi
 fi
 
 MOZ_CHROME_FILE_FORMAT=omni
 MOZ_SAFE_BROWSING=1
 MOZ_SERVICES_SYNC=1
 MOZ_APP_VERSION=$FIREFOX_VERSION
 MOZ_EXTENSIONS_DEFAULT=" gnomevfs"
 # MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
 # Changing either of these values requires a clobber to ensure correct results,
 # because branding dependencies are broken.
 MOZ_BRANDING_DIRECTORY=browser/branding/aurora
 MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
 MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+# This should usually be the same as the value MAR_CHANNEL_ID.
+# If more than one ID is needed, then you should use a comma separated list
+# of values.
+ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-aurora
+# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
+MAR_CHANNEL_ID=firefox-mozilla-aurora
 MOZ_PROFILE_MIGRATOR=1
 MOZ_EXTENSION_MANAGER=1
 MOZ_APP_STATIC_INI=1
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -92,16 +92,17 @@
 ; [Base Browser Files]
 #ifndef XP_UNIX
 @BINPATH@/@MOZ_APP_NAME@.exe
 #else
 @BINPATH@/@MOZ_APP_NAME@-bin
 @BINPATH@/@MOZ_APP_NAME@
 #endif
 @BINPATH@/application.ini
+@BINPATH@/update-settings.ini
 @BINPATH@/platform.ini
 #ifndef XP_OS2
 @BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
 #else
 @BINPATH@/mozsqlt3@DLL_SUFFIX@
 #endif
 @BINPATH@/blocklist.xml
 #ifdef XP_UNIX
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -65,16 +65,20 @@ ifeq (android,$(MOZ_WIDGET_TOOLKIT))
           mobile/robocop \
           $(NULL)
 endif
 endif
 
 ifdef MOZ_APP_BASENAME
 DIST_FILES = application.ini
 
+ifneq (android,$(MOZ_WIDGET_TOOLKIT))
+DIST_FILES += update-settings.ini
+endif
+
 ifdef LIBXUL_SDK
 GRE_MILESTONE = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build Milestone)
 APP_INI_DEPS = $(LIBXUL_DIST)/bin/platform.ini
 else
 GRE_MILESTONE = $(shell tail -n 1 $(topsrcdir)/config/milestone.txt 2>/dev/null || tail -1 $(topsrcdir)/config/milestone.txt)
 APP_INI_DEPS = $(topsrcdir)/config/milestone.txt
 endif
 
@@ -96,16 +100,18 @@ SOURCE_REPO := $(shell cd $(topsrcdir) &
 ifdef SOURCE_REPO
 DEFINES += -DMOZ_SOURCE_REPO="$(SOURCE_REPO)"
 endif
 
 DEFINES += \
   -DMOZ_APP_BASENAME="$(MOZ_APP_BASENAME)" \
   -DMOZ_APP_VENDOR="$(MOZ_APP_VENDOR)" \
   -DMOZ_APP_ID="$(MOZ_APP_ID)" \
+  -DMAR_CHANNEL_ID="$(MAR_CHANNEL_ID)" \
+  -DACCEPTED_MAR_CHANNEL_IDS="$(ACCEPTED_MAR_CHANNEL_IDS)" \
   $(NULL)
 
 ifdef MOZ_APP_PROFILE
 DEFINES += -DMOZ_APP_PROFILE="$(MOZ_APP_PROFILE)"
 endif
 
 ifdef MOZILLA_OFFICIAL
 DEFINES += -DMOZILLA_OFFICIAL
@@ -150,16 +156,22 @@ GARBAGE_DIRS += $(_LEAKTEST_DIR)
 		$(topsrcdir)/build/pgo/blueprint/fancytype-screen.css \
 		$(NULL)
 
 leaktest.py: leaktest.py.in
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $^ > $@
 	chmod +x $@
 GARBAGE += leaktest.py
 
+ifneq (android,$(MOZ_WIDGET_TOOLKIT))
+update-settings.ini: update-settings.ini.in $(APP_INI_DEPS)
+	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
+GARBAGE += update-settings.ini
+endif
+
 ifdef MOZ_APP_BASENAME
 application.ini: application.ini.in $(APP_INI_DEPS)
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
 GARBAGE += application.ini
 
 ifdef MOZ_APP_STATIC_INI
 application.ini.h: appini_header.py application.ini 
 	$(PYTHON) $^ > $@
new file mode 100644
--- /dev/null
+++ b/build/update-settings.ini.in
@@ -0,0 +1,44 @@
+#if 0
+; ***** BEGIN LICENSE BLOCK *****
+; Version: MPL 1.1/GPL 2.0/LGPL 2.1
+;
+; The contents of this file are subject to the Mozilla Public License Version
+; 1.1 (the "License"); you may not use this file except in compliance with
+; the License. You may obtain a copy of the License at
+; http://www.mozilla.org/MPL/
+;
+; Software distributed under the License is distributed on an "AS IS" basis,
+; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+; for the specific language governing rights and limitations under the
+; License.
+;
+; The Original Code is MAR channel config.
+;
+; The Initial Developer of the Original Code is
+; Mozilla Foundation.
+; Portions created by the Initial Developer are Copyright (C) 2011
+; the Initial Developer. All Rights Reserved.
+;
+; Contributor(s):
+;   Brian R. Bondy <netzen@gmail.com>
+;
+; Alternatively, the contents of this file may be used under the terms of
+; either the GNU General Public License Version 2 or later (the "GPL"), or
+; the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+; in which case the provisions of the GPL or the LGPL are applicable instead
+; of those above. If you wish to allow use of your version of this file only
+; under the terms of either the GPL or the LGPL, and not to allow others to
+; use your version of this file under the terms of the MPL, indicate your
+; decision by deleting the provisions above and replace them with the notice
+; and other provisions required by the GPL or the LGPL. If you do not delete
+; the provisions above, a recipient may use your version of this file under
+; the terms of any one of the MPL, the GPL or the LGPL.
+;
+; ***** END LICENSE BLOCK *****
+#endif
+; If you modify this file updates may fail.
+; Do not modify this file.
+
+#filter substitution
+[Settings]
+ACCEPTED_MAR_CHANNEL_IDS=@ACCEPTED_MAR_CHANNEL_IDS@
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -48,16 +48,18 @@ FIREFOX_VERSION	= @FIREFOX_VERSION@
 
 MOZ_BUILD_APP = @MOZ_BUILD_APP@
 MOZ_APP_NAME	= @MOZ_APP_NAME@
 MOZ_APP_DISPLAYNAME = @MOZ_APP_DISPLAYNAME@
 MOZ_APP_BASENAME = @MOZ_APP_BASENAME@
 MOZ_APP_VENDOR = @MOZ_APP_VENDOR@
 MOZ_APP_PROFILE = @MOZ_APP_PROFILE@
 MOZ_APP_ID = @MOZ_APP_ID@
+MAR_CHANNEL_ID = @MAR_CHANNEL_ID@
+ACCEPTED_MAR_CHANNEL_IDS = @ACCEPTED_MAR_CHANNEL_IDS@
 MOZ_PROFILE_MIGRATOR = @MOZ_PROFILE_MIGRATOR@
 MOZ_EXTENSION_MANAGER = @MOZ_EXTENSION_MANAGER@
 MOZ_APP_UA_NAME = @MOZ_APP_UA_NAME@
 MOZ_APP_VERSION = @MOZ_APP_VERSION@
 MOZ_UA_BUILDID = @MOZ_UA_BUILDID@
 MOZ_MACBUNDLE_NAME = @MOZ_MACBUNDLE_NAME@
 MOZ_APP_STATIC_INI = @MOZ_APP_STATIC_INI@
 
@@ -134,16 +136,18 @@ MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLO
 ENABLE_TESTS	= @ENABLE_TESTS@
 IBMBIDI = @IBMBIDI@
 MOZ_UNIVERSALCHARDET = @MOZ_UNIVERSALCHARDET@
 ACCESSIBILITY = @ACCESSIBILITY@
 MOZ_BRANDING_DIRECTORY = @MOZ_BRANDING_DIRECTORY@
 XPCOM_USE_LEA = @XPCOM_USE_LEA@
 MOZ_INSTALLER	= @MOZ_INSTALLER@
 MOZ_MAINTENANCE_SERVICE	= @MOZ_MAINTENANCE_SERVICE@
+MOZ_VERIFY_MAR_SIGNATURE	= @MOZ_VERIFY_MAR_SIGNATURE@
+MOZ_ENABLE_SIGNMAR	= @MOZ_ENABLE_SIGNMAR@
 MOZ_UPDATER	= @MOZ_UPDATER@
 MOZ_UPDATE_CHANNEL	= @MOZ_UPDATE_CHANNEL@
 MOZ_UPDATE_PACKAGING	= @MOZ_UPDATE_PACKAGING@
 MOZ_DISABLE_PARENTAL_CONTROLS = @MOZ_DISABLE_PARENTAL_CONTROLS@
 NS_ENABLE_TSF = @NS_ENABLE_TSF@
 MOZ_SPELLCHECK = @MOZ_SPELLCHECK@
 MOZ_ANDROID_HISTORY = @MOZ_ANDROID_HISTORY@
 MOZ_WEBSMS_BACKEND = @MOZ_WEBSMS_BACKEND@
--- a/configure.in
+++ b/configure.in
@@ -6351,16 +6351,50 @@ if test -n "$MOZ_MAINTENANCE_SERVICE"; t
   if test "$OS_ARCH" = "WINNT"; then
     AC_DEFINE(MOZ_MAINTENANCE_SERVICE)
   else
     AC_MSG_ERROR([Can only build with --enable-maintenance-service with a Windows target])
   fi
 fi
 
 dnl ========================================================
+dnl Verify MAR signatures
+dnl ========================================================
+
+MOZ_ARG_ENABLE_BOOL(verify-mar,
+[  --enable-verify-mar     Enable verifying MAR signatures],
+    MOZ_VERIFY_MAR_SIGNATURE=1,
+    MOZ_VERIFY_MAR_SIGNATURE= )
+
+if test -n "$MOZ_VERIFY_MAR_SIGNATURE"; then
+  if test "$OS_ARCH" = "WINNT"; then
+    AC_DEFINE(MOZ_VERIFY_MAR_SIGNATURE)
+  else
+    AC_MSG_ERROR([Can only build with --enable-verify-mar with a Windows target])
+  fi
+fi
+
+dnl ========================================================
+dnl Enable building the signmar program.
+dnl This option is much different than the --enable-verify-mar option.
+dnl --enable-verify-mar is for enabling the verification check on MAR
+dnl files in the updater.  The --enable-signmar option is for building
+dnl the signmar program.
+dnl ========================================================
+
+MOZ_ARG_ENABLE_BOOL(sign-mar,
+[  --enable-signmar     Enable building the signmar program],
+    MOZ_ENABLE_SIGNMAR=1,
+    MOZ_ENABLE_SIGNMAR= )
+
+if test -n "$MOZ_ENABLE_SIGNMAR"; then
+  AC_DEFINE(MOZ_ENABLE_SIGNMAR)
+fi
+
+dnl ========================================================
 dnl Updater
 dnl ========================================================
 
 MOZ_ARG_DISABLE_BOOL(updater,
 [  --disable-updater       Disable building of updater],
     MOZ_UPDATER=,
     MOZ_UPDATER=1 )
 
@@ -8446,16 +8480,18 @@ AC_SUBST(IBMBIDI)
 AC_SUBST(MOZ_UNIVERSALCHARDET)
 AC_SUBST(ACCESSIBILITY)
 AC_SUBST(MOZ_SPELLCHECK)
 AC_SUBST(MOZ_JAVA_COMPOSITOR)
 AC_SUBST(MOZ_ONLY_TOUCH_EVENTS)
 AC_SUBST(MOZ_USER_DIR)
 AC_SUBST(MOZ_CRASHREPORTER)
 AC_SUBST(MOZ_MAINTENANCE_SERVICE)
+AC_SUBST(MOZ_VERIFY_MAR_SIGNATURE)
+AC_SUBST(MOZ_ENABLE_SIGNMAR)
 AC_SUBST(MOZ_UPDATER)
 AC_SUBST(MOZ_ANGLE)
 AC_SUBST(MOZ_DIRECTX_SDK_PATH)
 AC_SUBST(MOZ_DIRECTX_SDK_CPU_SUFFIX)
 AC_SUBST(MOZ_D3DX9_VERSION)
 AC_SUBST(MOZ_D3DX9_CAB)
 AC_SUBST(MOZ_D3DCOMPILER_CAB)
 AC_SUBST(MOZ_D3DX9_DLL)
@@ -8533,16 +8569,18 @@ if test -z "$MOZ_APP_NAME"; then
 fi
 
 AC_SUBST(MOZ_APP_NAME)
 AC_SUBST(MOZ_APP_DISPLAYNAME)
 AC_SUBST(MOZ_APP_BASENAME)
 AC_SUBST(MOZ_APP_VENDOR)
 AC_SUBST(MOZ_APP_PROFILE)
 AC_SUBST(MOZ_APP_ID)
+AC_SUBST(MAR_CHANNEL_ID)
+AC_SUBST(ACCEPTED_MAR_CHANNEL_IDS)
 AC_SUBST(MOZ_PROFILE_MIGRATOR)
 AC_SUBST(MOZ_EXTENSION_MANAGER)
 AC_DEFINE_UNQUOTED(MOZ_APP_UA_NAME, "$MOZ_APP_UA_NAME")
 AC_SUBST(MOZ_APP_UA_NAME)
 AC_DEFINE_UNQUOTED(MOZ_APP_UA_VERSION, "$MOZ_APP_VERSION")
 AC_SUBST(MOZ_APP_VERSION)
 AC_DEFINE_UNQUOTED(MOZ_UA_FIREFOX_VERSION, "$FIREFOX_VERSION")
 AC_DEFINE_UNQUOTED(FIREFOX_VERSION,$FIREFOX_VERSION)
--- a/modules/libmar/Makefile.in
+++ b/modules/libmar/Makefile.in
@@ -38,11 +38,30 @@
 
 DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-DIRS		= src tool
+DIRS = src
+MODULE = libmar_test
+
+ifdef MOZ_ENABLE_SIGNMAR
+DIRS += sign verify
+else
+ifeq ($(OS_ARCH),WINNT)
+# On Windows we don't verify with NSS and updater needs to link to it
+DIRS += verify
+endif
+endif
+
+# If we are building ./sign and ./verify then ./tool must come after it
+DIRS += tool
+
+ifdef ENABLE_TESTS
+ifdef MOZ_ENABLE_SIGNMAR
+DIRS += tests
+endif
+endif
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/modules/libmar/sign/Makefile.in
@@ -0,0 +1,69 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mar signing build config.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Brian R. Bondy <netzen@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = signmar
+LIBRARY_NAME = signmar
+FORCE_STATIC_LIB = 1
+ifeq ($(OS_ARCH),WINNT)
+USE_STATIC_LIBS = 1
+endif
+
+# This makefile just builds support for reading archives.
+CSRCS = \
+  mar_sign.c \
+  nss_secutil.c \
+  $(NULL)
+
+LOCAL_INCLUDES += -I$(srcdir)/../src \
+  -I$(srcdir)/../verify \
+  -I$(topsrcdir)/dist/include \
+  $(NULL)
+
+CFLAGS += -DMAR_NSS
+
+include $(topsrcdir)/config/rules.mk
+
+# The intermediate (.ii/.s) files for host and target can have the same name...
+# disable parallel builds
+.NOTPARALLEL:
new file mode 100644
--- /dev/null
+++ b/modules/libmar/sign/mar_sign.c
@@ -0,0 +1,818 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Archive signing code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifdef XP_WIN
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mar_private.h"
+#include "mar_cmdline.h"
+#include "mar.h"
+#include "cryptox.h"
+#ifndef XP_WIN
+#include <unistd.h>
+#endif
+
+#include "nss_secutil.h"
+
+/**
+ * Initializes the NSS context.
+ * 
+ * @param NSSConfigDir The config dir containing the private key to use
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+NSSInitCryptoContext(const char *NSSConfigDir)
+{
+  SECStatus status = NSS_Initialize(NSSConfigDir, 
+                                    "", "", SECMOD_DB, NSS_INIT_READONLY);
+  if (SECSuccess != status) {
+    fprintf(stderr, "ERROR: Could not initialize NSS\n");
+    return -1;
+  }
+
+  return 0;
+}
+
+/** 
+ * Obtains a signing context.
+ *
+ * @param  ctx A pointer to the signing context to fill
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+NSSSignBegin(const char *certName, 
+             SGNContext **ctx, 
+             SECKEYPrivateKey **privKey, 
+             CERTCertificate **cert,
+             PRUint32 *signatureLength) 
+{
+  secuPWData pwdata = { PW_NONE, 0 };
+  if (!certName || !ctx || !privKey || !cert || !signatureLength) {
+    fprintf(stderr, "ERROR: Invalid parameter passed to NSSSignBegin\n");
+    return -1;
+  }
+
+  /* Get the cert and embedded public key out of the database */
+  *cert = PK11_FindCertFromNickname(certName, &pwdata);
+  if (!*cert) {
+    fprintf(stderr, "ERROR: Could not find cert from nickname\n");
+    return -1;
+  }
+
+  /* Get the private key out of the database */
+  *privKey = PK11_FindKeyByAnyCert(*cert, &pwdata);
+  if (!*privKey) {
+    fprintf(stderr, "ERROR: Could not find private key\n");
+    return -1;
+  }
+
+  *signatureLength = PK11_SignatureLen(*privKey);
+
+  if (*signatureLength > BLOCKSIZE) {
+    fprintf(stderr, 
+            "ERROR: Program must be compiled with a larger block size"
+            " to support signing with signatures this large: %u.\n", 
+            *signatureLength);
+    return -1;
+  }
+
+  /* Check that the key length is large enough for our requirements */
+  if (*signatureLength < XP_MIN_SIGNATURE_LEN_IN_BYTES) {
+    fprintf(stderr, "ERROR: Key length must be >= %d bytes\n", 
+            XP_MIN_SIGNATURE_LEN_IN_BYTES);
+    return -1;
+  }
+
+  *ctx = SGN_NewContext (SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE, *privKey);
+  if (!*ctx) {
+    fprintf(stderr, "ERROR: Could not create signature context\n");
+    return -1;
+  }
+  
+  if (SGN_Begin(*ctx) != SECSuccess) {
+    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.
+ *
+ * @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  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, 
+                        PRUint32 size, SGNContext *ctx,
+                        const char *err) 
+{
+  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;
+  }
+  return 0;
+}
+
+/** 
+ * Adjusts each entry's content offset in the the passed in index by the 
+ * specified amount.
+ *
+ * @param indexBuf     A buffer containing the MAR index
+ * @param indexLength  The length of the MAR index
+ * @param offsetAmount The amount to adjust each index entry by
+*/
+void
+AdjustIndexContentOffsets(char *indexBuf, PRUint32 indexLength, PRUint32 offsetAmount) 
+{
+  PRUint32 *offsetToContent;
+  char *indexBufLoc = indexBuf;
+
+  /* Consume the index and adjust each index by the specified amount */
+  while (indexBufLoc != (indexBuf + indexLength)) {
+    /* Adjust the offset */
+    offsetToContent = (PRUint32 *)indexBufLoc; 
+    *offsetToContent = ntohl(*offsetToContent);
+    *offsetToContent += offsetAmount;
+    *offsetToContent = htonl(*offsetToContent);
+    /* Skip past the offset, length, and flags */
+    indexBufLoc += 3 * sizeof(PRUint32);
+    indexBufLoc += strlen(indexBufLoc) + 1;
+  }
+}
+
+/**
+ * Reads from fpSrc, writes it to fpDest, and updates 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  ctx    The signature context.
+ * @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, 
+                            PRUint32 size, SGNContext *ctx,
+                            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);
+}
+
+
+/**
+ * Reads from fpSrc, writes it to fpDest.
+ *
+ * @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  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
+*/
+int
+ReadAndWrite(FILE *fpSrc, FILE *fpDest, void *buffer, 
+             PRUint32 size, 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;
+  }
+
+  if (fwrite(buffer, size, 1, fpDest) != 1) {
+    fprintf(stderr, "ERROR: Could not write %s\n", err);
+    return -2;
+  }
+
+  return 0;
+}
+
+/**
+ * Writes out a copy of the MAR at src but with the signature block stripped.
+ *
+ * @param  src  The path of the source MAR file
+ * @param  dest The path of the MAR file to write out that 
+                has no signature block
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+strip_signature_block(const char *src, const char * dest)
+{
+  PRUint32 offsetToIndex, dstOffsetToIndex, indexLength, 
+    numSignatures = 0, leftOver;
+  PRInt32 stripAmount = 0;
+  PRInt64 oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR, numBytesToCopy,
+    numChunks, i;
+  FILE *fpSrc = NULL, *fpDest = NULL;
+  int rv = -1, hasSignatureBlock;
+  char buf[BLOCKSIZE];
+  char *indexBuf = NULL, *indexBufLoc;
+
+  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);
+    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;
+  }
+
+  /* MAR ID */
+  if (ReadAndWrite(fpSrc, fpDest, buf, MAR_ID_SIZE, "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;
+  }
+  offsetToIndex = ntohl(offsetToIndex);
+
+  /* Get the real size of the MAR */
+  oldPos = ftello(fpSrc);
+  if (fseeko(fpSrc, 0, SEEK_END)) {
+    fprintf(stderr, "ERROR: Could not seek to end of file.\n");
+    goto failure;
+  }
+  realSizeOfSrcMAR = ftello(fpSrc);
+  if (fseeko(fpSrc, oldPos, SEEK_SET)) {
+    fprintf(stderr, "ERROR: Could not seek back to current location.\n");
+    goto failure;
+  }
+
+  if (hasSignatureBlock) {
+    /* Get the MAR length and adjust its size */
+    if (fread(&sizeOfEntireMAR, 
+              sizeof(sizeOfEntireMAR), 1, fpSrc) != 1) {
+      fprintf(stderr, "ERROR: Could read mar size\n");
+      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 strip */
+    if (fread(&numSignatures, sizeof(numSignatures), 1, fpSrc) != 1) {
+      fprintf(stderr, "ERROR: Could read num signatures\n");
+      goto failure;
+    }
+    numSignatures = ntohl(numSignatures);
+
+    for (i = 0; i < numSignatures; i++) {
+      PRUint32 signatureLen;
+
+      /* Skip past the signature algorithm ID */
+      if (fseeko(fpSrc, sizeof(PRUint32), SEEK_CUR)) {
+        fprintf(stderr, "ERROR: Could not skip past signature algorithm ID\n");
+      }
+
+      /* Read in the length of the signature so we know how far to skip */
+      if (fread(&signatureLen, sizeof(PRUint32), 1, fpSrc) != 1) {
+        fprintf(stderr, "ERROR: Could not read signatures length.\n");
+        return CryptoX_Error;
+      }
+      signatureLen = ntohl(signatureLen);
+
+      /* Skip past the signature */
+      if (fseeko(fpSrc, signatureLen, SEEK_CUR)) {
+        fprintf(stderr, "ERROR: Could not skip past signature algorithm ID\n");
+      }
+
+      stripAmount += sizeof(PRUint32) + sizeof(PRUint32) + signatureLen; 
+    }
+
+  } else {
+    sizeOfEntireMAR = realSizeOfSrcMAR;
+    numSignatures = 0;
+  }
+
+  if (((PRInt64)offsetToIndex) > sizeOfEntireMAR) {
+    fprintf(stderr, "ERROR: Offset to index is larger than the file size.\n");
+    goto failure;
+  }
+
+  dstOffsetToIndex = offsetToIndex;
+  if (!hasSignatureBlock) {
+    dstOffsetToIndex += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
+  }
+  dstOffsetToIndex -= stripAmount;
+
+  /* Write out the index offset */
+  dstOffsetToIndex = htonl(dstOffsetToIndex);
+  if (fwrite(&dstOffsetToIndex, sizeof(dstOffsetToIndex), 1, fpDest) != 1) {
+    fprintf(stderr, "ERROR: Could not write offset to index\n");
+    goto failure;
+  }
+  dstOffsetToIndex = ntohl(dstOffsetToIndex);
+
+  /* Write out the new MAR file size */
+  if (!hasSignatureBlock) {
+    sizeOfEntireMAR += sizeof(sizeOfEntireMAR) + sizeof(numSignatures);
+  }
+  sizeOfEntireMAR -= stripAmount;
+
+  /* Write out the MAR size */
+  sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
+  if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fpDest) != 1) {
+    fprintf(stderr, "ERROR: Could not write size of MAR\n");
+    goto failure;
+  }
+  sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
+
+  /* Write out the number of signatures, which is 0 */
+  numSignatures = 0;
+  if (fwrite(&numSignatures, sizeof(numSignatures), 1, fpDest) != 1) {
+    fprintf(stderr, "ERROR: Could not write out num signatures\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) > ((PRInt64)offsetToIndex)) {
+    fprintf(stderr, "ERROR: Index offset is too small.\n");
+    goto failure;
+  }
+  numBytesToCopy = ((PRInt64)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 (ReadAndWrite(fpSrc, fpDest, buf, BLOCKSIZE, "content block")) {
+      goto failure;
+    }
+  }
+
+  /* Write out the left over */
+  if (ReadAndWrite(fpSrc, fpDest, buf, 
+                   leftOver, "left over content block")) {
+    goto failure;
+  }
+
+  /* Length of the index */
+  if (ReadAndWrite(fpSrc, fpDest, &indexLength, 
+                   sizeof(indexLength), "index length")) {
+    goto failure;
+  }
+  indexLength = ntohl(indexLength);
+
+  /* Consume the index and adjust each index by the difference */
+  indexBuf = malloc(indexLength);
+  indexBufLoc = indexBuf;
+  if (fread(indexBuf, indexLength, 1, fpSrc) != 1) {
+    fprintf(stderr, "ERROR: Could not read index\n");
+    goto failure;
+  }
+
+  /* Adjust each entry in the index */
+  if (hasSignatureBlock) {
+    AdjustIndexContentOffsets(indexBuf, indexLength, -stripAmount);
+  } else {
+    AdjustIndexContentOffsets(indexBuf, indexLength, 
+                              sizeof(sizeOfEntireMAR) + 
+                              sizeof(numSignatures) - 
+                              stripAmount);
+  }
+
+  if (fwrite(indexBuf, indexLength, 1, fpDest) != 1) {
+    fprintf(stderr, "ERROR: Could not write index\n");
+    goto failure;
+  }
+
+  rv = 0;
+failure: 
+  if (fpSrc) {
+    fclose(fpSrc);
+  }
+
+  if (fpDest) {
+    fclose(fpDest);
+  }
+
+  if (rv) {
+    remove(dest);
+  }
+
+  if (indexBuf) { 
+    free(indexBuf);
+  }
+
+  if (rv) {
+    remove(dest);
+  }
+  return rv;
+}
+
+/**
+ * Writes out a copy of the MAR at src but with an embedded signature.
+ * 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
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+mar_repackage_and_sign(const char *NSSConfigDir, 
+                       const char *certName, 
+                       const char *src, 
+                       const char *dest) 
+{
+  PRUint32 offsetToIndex, dstOffsetToIndex, indexLength, 
+    numSignatures = 0, signatureLength, leftOver,
+    signatureAlgorithmID, signatureSectionLength;
+  PRInt64 oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR, 
+    signaturePlaceholderOffset, numBytesToCopy, 
+    numChunks, i;
+  FILE *fpSrc = NULL, *fpDest = NULL;
+  int rv = -1, hasSignatureBlock;
+  SGNContext *ctx = NULL;
+  SECItem secItem;
+  char buf[BLOCKSIZE];
+  SECKEYPrivateKey *privKey = NULL;
+  CERTCertificate *cert = NULL; 
+  char *indexBuf = NULL, *indexBufLoc;
+
+  if (!NSSConfigDir || !certName || !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);
+    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;
+  }
+
+  /* MAR ID */
+  if (ReadWriteAndUpdateSignature(fpSrc, fpDest, 
+                                  buf, MAR_ID_SIZE, 
+                                  ctx, "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;
+  }
+  offsetToIndex = ntohl(offsetToIndex);
+
+  /* Get the real size of the MAR */
+  oldPos = ftello(fpSrc);
+  if (fseeko(fpSrc, 0, SEEK_END)) {
+    fprintf(stderr, "ERROR: Could not seek to end of file.\n");
+    goto failure;
+  }
+  realSizeOfSrcMAR = ftello(fpSrc);
+  if (fseeko(fpSrc, oldPos, SEEK_SET)) {
+    fprintf(stderr, "ERROR: Could not seek back to current location.\n");
+    goto failure;
+  }
+
+  if (hasSignatureBlock) {
+    /* Get the MAR length and adjust its size */
+    if (fread(&sizeOfEntireMAR, 
+              sizeof(sizeOfEntireMAR), 1, fpSrc) != 1) {
+      fprintf(stderr, "ERROR: Could read mar size\n");
+      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 */
+    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 */
+    if (numSignatures) {
+      fprintf(stderr, "ERROR: MAR is already signed\n");
+      goto failure;
+    }
+  } else {
+    sizeOfEntireMAR = realSizeOfSrcMAR;
+  }
+
+  if (((PRInt64)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;
+  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")) {
+    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")) {
+    goto failure;
+  }
+  sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
+
+  /* Write out the number of signatures, for now only 1 is supported */
+  numSignatures = 1;
+  numSignatures = htonl(numSignatures);
+  if (WriteAndUpdateSignature(fpDest, &numSignatures, 
+                              sizeof(numSignatures), ctx, "num signatures")) {
+    goto failure;
+  }
+  numSignatures = ntohl(numSignatures);
+
+  /* Write out the signature ID, for now only an ID of 1 is supported */
+  signatureAlgorithmID = htonl(1);
+  if (WriteAndUpdateSignature(fpDest, &signatureAlgorithmID, 
+                              sizeof(signatureAlgorithmID), 
+                              ctx, "num signatures")) {
+    goto failure;
+  }
+  signatureAlgorithmID = ntohl(signatureAlgorithmID);
+
+  /* Write out the signature length */
+  signatureLength = htonl(signatureLength);
+  if (WriteAndUpdateSignature(fpDest, &signatureLength, 
+                              sizeof(signatureLength), 
+                              ctx, "signature length")) {
+    goto failure;
+  }
+  signatureLength = ntohl(signatureLength);
+
+  /* Write out a placeholder for the signature, we'll come back to this later
+     *** THIS IS NOT SIGNED because it is a placeholder that will be replaced
+         below, plus it is going to be the signature itself. *** */
+  memset(buf, 0, sizeof(buf));
+  signaturePlaceholderOffset = ftello(fpDest);
+  if (fwrite(buf, signatureLength, 1, fpDest) != 1) {
+    fprintf(stderr, "ERROR: Could not write signature length\n");
+    goto failure;
+  }
+
+  /* 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) > ((PRInt64)offsetToIndex)) {
+    fprintf(stderr, "ERROR: Index offset is too small.\n");
+    goto failure;
+  }
+  numBytesToCopy = ((PRInt64)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")) {
+      goto failure;
+    }
+  }
+
+  /* Write out the left over */
+  if (ReadWriteAndUpdateSignature(fpSrc, fpDest, buf, 
+                                  leftOver, ctx, "left over content block")) {
+    goto failure;
+  }
+
+  /* Length of the index */
+  if (ReadWriteAndUpdateSignature(fpSrc, fpDest, &indexLength, 
+                                  sizeof(indexLength), ctx, "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) {
+    fprintf(stderr, "ERROR: Could not read index\n");
+    goto failure;
+  }
+
+  /* Adjust each entry in the index */
+  if (hasSignatureBlock) {
+    AdjustIndexContentOffsets(indexBuf, indexLength, signatureSectionLength);
+  } else {
+    AdjustIndexContentOffsets(indexBuf, indexLength, 
+                              sizeof(sizeOfEntireMAR) + 
+                              sizeof(numSignatures) + 
+                              signatureSectionLength);
+  }
+
+  if (WriteAndUpdateSignature(fpDest, indexBuf, 
+                              indexLength, ctx, "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;
+  }
+
+  /* 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;
+  }
+
+  rv = 0;
+failure: 
+  if (fpSrc) {
+    fclose(fpSrc);
+  }
+
+  if (fpDest) {
+    fclose(fpDest);
+  }
+
+  if (rv) {
+    remove(dest);
+  }
+
+  if (indexBuf) { 
+    free(indexBuf);
+  }
+
+  if (ctx) {
+    SGN_DestroyContext(ctx, PR_TRUE);
+  }
+
+  if (cert) {
+    CERT_DestroyCertificate(cert);
+  }
+
+  if (privKey) {
+    SECKEY_DestroyPrivateKey(privKey);
+  }
+
+  if (rv) {
+    remove(dest);
+  }
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/modules/libmar/sign/nss_secutil.c
@@ -0,0 +1,269 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is code copied from secutil and secpwd.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* With the exception of GetPasswordString, this file was
+   copied from NSS's cmd/lib/secutil.c hg revision 8f011395145e */
+
+#include "nss_secutil.h"
+
+#include "prprf.h"
+#ifdef XP_WIN
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+static char consoleName[] =  {
+#ifdef XP_UNIX
+  "/dev/tty"
+#else
+#ifdef XP_OS2
+  "\\DEV\\CON"
+#else
+  "CON:"
+#endif
+#endif
+};
+
+#if defined(_WINDOWS)
+static char * quiet_fgets (char *buf, int length, FILE *input)
+{
+  int c;
+  char *end = buf;
+
+  /* fflush (input); */
+  memset (buf, 0, length);
+
+  if (!isatty(fileno(input))) {
+    return fgets(buf,length,input);
+  }
+
+  while (1)
+  {
+#if defined (_WIN32_WCE)
+    c = getchar();	/* gets a character from stdin */
+#else
+    c = getch();	/* getch gets a character from the console */
+#endif
+    if (c == '\b')
+    {
+      if (end > buf)
+        end--;
+    }
+
+    else if (--length > 0)
+      *end++ = c;
+
+    if (!c || c == '\n' || c == '\r')
+      break;
+  }
+
+  return buf;
+}
+#endif
+
+char *
+GetPasswordString(void *arg, char *prompt)
+{
+  FILE *input = stdin;
+  char phrase[200] = {'\0'};
+  int isInputTerminal = isatty(fileno(stdin));
+
+#ifndef _WINDOWS
+  if (isInputTerminal) {
+    input = fopen(consoleName, "r");
+    if (input == NULL) {
+      fprintf(stderr, "Error opening input terminal for read\n");
+      return NULL;
+    }
+  }
+#endif 
+
+  if (isInputTerminal) {
+    fprintf(stdout, "Please enter your password:\n");
+    fflush(stdout);
+  }
+
+  QUIET_FGETS (phrase, sizeof(phrase), input);
+
+  if (isInputTerminal) {
+    fprintf(stdout, "\n");
+  }
+
+#ifndef _WINDOWS
+  if (isInputTerminal) {
+    fclose(input);
+  }
+#endif
+
+  /* Strip off the newlines if present */
+  if (phrase[PORT_Strlen(phrase)-1] == '\n' || 
+      phrase[PORT_Strlen(phrase)-1] == '\r') {
+    phrase[PORT_Strlen(phrase)-1] = 0;
+  }
+  return (char*) PORT_Strdup(phrase);
+}
+
+char *
+SECU_FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg)
+{
+  char* phrases, *phrase;
+  PRFileDesc *fd;
+  PRInt32 nb;
+  char *pwFile = arg;
+  int i;
+  const long maxPwdFileSize = 4096;
+  char* tokenName = NULL;
+  int tokenLen = 0;
+
+  if (!pwFile)
+    return 0;
+
+  if (retry) {
+    return 0;  /* no good retrying - the files contents will be the same */
+  }
+
+  phrases = PORT_ZAlloc(maxPwdFileSize);
+
+  if (!phrases) {
+    return 0; /* out of memory */
+  }
+
+  fd = PR_Open(pwFile, PR_RDONLY, 0);
+  if (!fd) {
+    fprintf(stderr, "No password file \"%s\" exists.\n", pwFile);
+    PORT_Free(phrases);
+    return NULL;
+  }
+
+  nb = PR_Read(fd, phrases, maxPwdFileSize);
+
+  PR_Close(fd);
+
+  if (nb == 0) {
+    fprintf(stderr,"password file contains no data\n");
+    PORT_Free(phrases);
+    return NULL;
+  }
+
+  if (slot) {
+    tokenName = PK11_GetTokenName(slot);
+    if (tokenName) {
+      tokenLen = PORT_Strlen(tokenName);
+    }
+  }
+  i = 0;
+  do
+  {
+    int startphrase = i;
+    int phraseLen;
+
+    /* handle the Windows EOL case */
+    while (phrases[i] != '\r' && phrases[i] != '\n' && i < nb) i++;
+    /* terminate passphrase */
+    phrases[i++] = '\0';
+    /* clean up any EOL before the start of the next passphrase */
+    while ( (i<nb) && (phrases[i] == '\r' || phrases[i] == '\n')) {
+      phrases[i++] = '\0';
+    }
+    /* now analyze the current passphrase */
+    phrase = &phrases[startphrase];
+    if (!tokenName)
+      break;
+    if (PORT_Strncmp(phrase, tokenName, tokenLen)) continue;
+    phraseLen = PORT_Strlen(phrase);
+    if (phraseLen < (tokenLen+1)) continue;
+    if (phrase[tokenLen] != ':') continue;
+    phrase = &phrase[tokenLen+1];
+    break;
+
+  } while (i<nb);
+
+  phrase = PORT_Strdup((char*)phrase);
+  PORT_Free(phrases);
+  return phrase;
+}
+
+char *
+SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg) 
+{
+    char prompt[255];
+    secuPWData *pwdata = (secuPWData *)arg;
+    secuPWData pwnull = { PW_NONE, 0 };
+    secuPWData pwxtrn = { PW_EXTERNAL, "external" };
+    char *pw;
+
+    if (pwdata == NULL)
+  pwdata = &pwnull;
+
+    if (PK11_ProtectedAuthenticationPath(slot)) {
+  pwdata = &pwxtrn;
+    }
+    if (retry && pwdata->source != PW_NONE) {
+  PR_fprintf(PR_STDERR, "Incorrect password/PIN entered.\n");
+      return NULL;
+    }
+
+    switch (pwdata->source) {
+    case PW_NONE:
+  sprintf(prompt, "Enter Password or Pin for \"%s\":",
+                   PK11_GetTokenName(slot));
+  return GetPasswordString(NULL, prompt);
+    case PW_FROMFILE:
+  /* Instead of opening and closing the file every time, get the pw
+   * once, then keep it in memory (duh).
+   */
+  pw = SECU_FilePasswd(slot, retry, pwdata->data);
+  pwdata->source = PW_PLAINTEXT;
+  pwdata->data = PL_strdup(pw);
+  /* it's already been dup'ed */
+  return pw;
+    case PW_EXTERNAL:
+  sprintf(prompt, 
+          "Press Enter, then enter PIN for \"%s\" on external device.\n",
+    PK11_GetTokenName(slot));
+  (void) GetPasswordString(NULL, prompt);
+      /* Fall Through */
+    case PW_PLAINTEXT:
+  return PL_strdup(pwdata->data);
+    default:
+  break;
+    }
+
+    PR_fprintf(PR_STDERR, "Password check failed:  No password found.\n");
+    return NULL;
+}
new file mode 100644
--- /dev/null
+++ b/modules/libmar/sign/nss_secutil.h
@@ -0,0 +1,73 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is code copied from secutil and secpwd.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* With the exception of GetPasswordString, this file was
+   copied from NSS's cmd/lib/secutil.h hg revision 8f011395145e */
+
+#ifndef NSS_SECUTIL_H_
+#define NSS_SECUTIL_H_
+
+#include "nss.h"
+#include "pk11pub.h"
+#include "cryptohi.h"
+#include "hasht.h"
+#include "cert.h"
+#include "key.h"
+
+typedef struct {
+  enum {
+    PW_NONE = 0,
+    PW_FROMFILE = 1,
+    PW_PLAINTEXT = 2,
+    PW_EXTERNAL = 3
+  } source;
+  char *data;
+} secuPWData;
+
+#if( defined(_WINDOWS) && !defined(_WIN32_WCE))
+#include <conio.h>
+#include <io.h>
+#define QUIET_FGETS quiet_fgets
+static char * quiet_fgets (char *buf, int length, FILE *input);
+#else
+#define QUIET_FGETS fgets
+#endif
+
+char *
+SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg);
+
+#endif
--- a/modules/libmar/src/Makefile.in
+++ b/modules/libmar/src/Makefile.in
@@ -48,25 +48,30 @@ LIBRARY_NAME	= mar
 HOST_LIBRARY_NAME = hostmar
 FORCE_STATIC_LIB = 1
 ifeq ($(OS_ARCH),WINNT)
 USE_STATIC_LIBS = 1
 endif
 
 # This makefile just builds support for reading archives.
 
-CSRCS		= \
+
+HOST_CSRCS	= \
 		mar_create.c \
 		mar_extract.c \
 		mar_read.c \
 		$(NULL)
-HOST_CSRCS	= $(CSRCS)
+
+CSRCS		= \
+		$(HOST_CSRCS) \
+		$(NULL)
 
 EXPORTS		= \
 		mar.h \
+		mar_cmdline.h \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 ifdef CROSS_COMPILE
 ifdef HOST_NSPR_MDCPUCFG
 HOST_CFLAGS     += -DMDCPUCFG=$(HOST_NSPR_MDCPUCFG)
 endif
--- a/modules/libmar/src/mar.h
+++ b/modules/libmar/src/mar.h
@@ -41,43 +41,55 @@
 
 /* We use NSPR here just to import the definition of PRUint32 */
 #include "prtypes.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+struct ProductInformationBlock {
+  const char *MARChannelID;
+  const char *productVersion;
+};
+
 /**
  * The MAR item data structure.
  */
 typedef struct MarItem_ {
   struct MarItem_ *next;  /* private field */
   PRUint32 offset;        /* offset into archive */
   PRUint32 length;        /* length of data in bytes */
   PRUint32 flags;         /* contains file mode bits */
   char name[1];           /* file path */
 } MarItem;
 
+#define TABLESIZE 256
+
+struct MarFile_ {
+  FILE *fp;
+  MarItem *item_table[TABLESIZE];
+};
+
 typedef struct MarFile_ MarFile;
 
 /**
  * Signature of callback function passed to mar_enum_items.
  * @param mar       The MAR file being visited.
  * @param item      The MAR item being visited.
  * @param data      The data parameter passed by the caller of mar_enum_items.
- * @returns         A non-zero value to stop enumerating.
+ * @return          A non-zero value to stop enumerating.
  */
 typedef int (* MarItemCallback)(MarFile *mar, const MarItem *item, void *data);
 
 /**
  * Open a MAR file for reading.
  * @param path      Specifies the path to the MAR file to open.  This path must
  *                  be compatible with fopen.
- * @returns         NULL if an error occurs.
+ * @return          NULL if an error occurs.
  */
 MarFile *mar_open(const char *path);
 
 #ifdef XP_WIN
 MarFile *mar_wopen(const PRUnichar *path);
 #endif
 
 /**
@@ -85,60 +97,95 @@ MarFile *mar_wopen(const PRUnichar *path
  * @param mar       The MarFile object to close.
  */
 void mar_close(MarFile *mar);
 
 /**
  * Find an item in the MAR file by name.
  * @param mar       The MarFile object to query.
  * @param item      The name of the item to query.
- * @returns         A const reference to a MAR item or NULL if not found.
+ * @return          A const reference to a MAR item or NULL if not found.
  */
 const MarItem *mar_find_item(MarFile *mar, const char *item);
 
 /**
  * Enumerate all MAR items via callback function.
  * @param mar       The MAR file to enumerate.
  * @param callback  The function to call for each MAR item.
  * @param data      A caller specified value that is passed along to the
  *                  callback function.
- * @returns         Zero if the enumeration ran to completion.  Otherwise, any
+ * @return          0 if the enumeration ran to completion.  Otherwise, any
  *                  non-zero return value from the callback is returned.
  */
 int mar_enum_items(MarFile *mar, MarItemCallback callback, void *data);
 
 /**
  * Read from MAR item at given offset up to bufsize bytes.
  * @param mar       The MAR file to read.
  * @param item      The MAR item to read.
  * @param offset    The byte offset relative to the start of the item.
  * @param buf       A pointer to a buffer to copy the data into.
  * @param bufsize   The length of the buffer to copy the data into.
- * @returns         The number of bytes written or a negative value if an
+ * @return          The number of bytes written or a negative value if an
  *                  error occurs.
  */
 int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
              int bufsize);
 
 /**
  * Create a MAR file from a set of files.
  * @param dest      The path to the file to create.  This path must be
  *                  compatible with fopen.
  * @param numfiles  The number of files to store in the archive.
  * @param files     The list of null-terminated file paths.  Each file
  *                  path must be compatible with fopen.
- * @returns         A non-zero value if an error occurs.
+ * @param infoBlock The information to store in the product information block.
+ * @return          A non-zero value if an error occurs.
  */
-int mar_create(const char *dest, int numfiles, char **files);
+int mar_create(const char *dest, 
+               int numfiles, 
+               char **files, 
+               struct ProductInformationBlock *infoBlock);
 
 /**
  * Extract a MAR file to the current working directory.
  * @param path      The path to the MAR file to extract.  This path must be
  *                  compatible with fopen.
- * @returns         A non-zero value if an error occurs.
+ * @return          A non-zero value if an error occurs.
  */
 int mar_extract(const char *path);
 
+/**
+ * Verifies the embedded signature for the specified mar file.
+ * 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.
+ * @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,
+                          PRUint32 sizeOfCertData);
+#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
+ * @return 0 on success, -1 on failure
+*/
+int
+mar_read_product_info_block(MarFile *mar, 
+                            struct ProductInformationBlock *infoBlock);
+
 #ifdef __cplusplus
 }
 #endif
 
 #endif  /* MAR_H__ */
new file mode 100644
--- /dev/null
+++ b/modules/libmar/src/mar_cmdline.h
@@ -0,0 +1,122 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is command line utility function declarations.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef MAR_CMDLINE_H__
+#define MAR_CMDLINE_H__
+
+/* We use NSPR here just to import the definition of PRUint32 */
+#include "prtypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Determines MAR file information.
+ *
+ * @param path                   The path of the MAR file to check.
+ * @param hasSignatureBlock      Optional out parameter specifying if the MAR
+ *                               file is has a signature block or not.
+ * @param numSignatures          Optional out parameter for storing the number
+ *                               of signatures in the MAR file.
+ * @param hasAdditionalBlocks    Optional out parameter specifying if the MAR
+ *                               file has additional blocks or not.
+ * @param offsetAdditionalBlocks Optional out parameter for the offset to the 
+ *                               first additional block. Value is only valid if
+ *                               has_additional_blocks
+ *                               is not equal to 0.
+ * @param numAdditionalBlocks    Optional out parameter for the number of
+ *                               additional blocks.  Value is only valid if
+ *                               has_additional_blocks is not euqal to 0.
+ * @return 0 on success and non-zero on failure.
+ */
+int get_mar_file_info(const char *path, 
+                      int *hasSignatureBlock,
+                      int *numSignatures,
+                      int *hasAdditionalBlocks,
+                      int *offsetAdditionalBlocks,
+                      int *numAdditionalBlocks);
+
+/**
+ * Verifies the embedded signature of the specified file path.
+ * 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
+ * @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,
+                         PRUint32 sizeOfCertData,
+                         const char *certName);
+
+/** 
+ * Reads the product info block from the MAR file's additional block section.
+ * The caller is responsible for freeing the fields in infoBlock
+ * if the return is successful.
+ *
+ * @param infoBlock Out parameter for where to store the result to
+ * @return 0 on success, -1 on failure
+*/
+int
+read_product_info_block(char *path, 
+                        struct ProductInformationBlock *infoBlock);
+
+/**
+ * Writes out a copy of the MAR at src but with the signature block stripped.
+ *
+ * @param  src  The path of the source MAR file
+ * @param  dest The path of the MAR file to write out that 
+                has no signature block
+ * @return 0 on success
+ *         -1 on error
+*/
+int
+strip_signature_block(const char *src, const char * dest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* MAR_CMDLINE_H__ */
--- a/modules/libmar/src/mar_create.c
+++ b/modules/libmar/src/mar_create.c
@@ -16,16 +16,17 @@
  * The Original Code is Mozilla Archive code.
  *
  * The Initial Developer of the Original Code is Google Inc.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Darin Fisher <darin@meer.net>
+ *  Brian R. Bondy <netzen@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -35,20 +36,20 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
+#include "mar_private.h"
+#include "mar_cmdline.h"
 #include "mar.h"
-#include "mar_private.h"
 
 #ifdef XP_WIN
 #include <winsock2.h>
 #else
 #include <netinet/in.h>
 #include <unistd.h>
 #endif
 
@@ -120,19 +121,216 @@ static int mar_concat_file(FILE *fp, con
       break;
     }
   }
 
   fclose(in);
   return rv;
 }
 
-int mar_create(const char *dest, int num_files, char **files) {
+/**
+ * Writes out the product information block to the specified file.
+ *
+ * @param fp           The opened MAR file being created.
+ * @param stack        A pointer to the MAR item stack being used to create 
+ *                     the MAR
+ * @param product_info The product info block to store in the file.
+ * @return 0 on success.
+*/
+static int
+mar_concat_product_info_block(FILE *fp, 
+                              struct MarItemStack *stack,
+                              struct ProductInformationBlock *infoBlock)
+{
+  char buf[PIB_MAX_MAR_CHANNEL_ID_SIZE + PIB_MAX_PRODUCT_VERSION_SIZE];
+  PRUint32 additionalBlockID = 1, infoBlockSize, unused;
+  if (!fp || !infoBlock || 
+      !infoBlock->MARChannelID ||
+      !infoBlock->productVersion) {
+    return -1;
+  }
+ 
+  /* The MAR channel name must be < 64 bytes per the spec */
+  if (strlen(infoBlock->MARChannelID) > PIB_MAX_MAR_CHANNEL_ID_SIZE) {
+    return -1;
+  }
+
+  /* The product version must be < 32 bytes per the spec */
+  if (strlen(infoBlock->productVersion) > PIB_MAX_PRODUCT_VERSION_SIZE) {
+    return -1;
+  }
+
+  /* Although we don't need the product information block size to include the
+     maximum MAR channel name and product version, we allocate the maximum
+     amount to make it easier to modify the MAR file for repurposing MAR files
+     to different MAR channels. + 2 is for the NULL terminators. */
+  infoBlockSize = sizeof(infoBlockSize) +
+                  sizeof(additionalBlockID) +
+                  PIB_MAX_MAR_CHANNEL_ID_SIZE +
+                  PIB_MAX_PRODUCT_VERSION_SIZE + 2;
+  if (stack) {
+    stack->last_offset += infoBlockSize;
+  }
+
+  /* Write out the product info block size */
+  infoBlockSize = htonl(infoBlockSize);
+  if (fwrite(&infoBlockSize, 
+      sizeof(infoBlockSize), 1, fp) != 1) {
+    return -1;
+  }
+  infoBlockSize = ntohl(infoBlockSize);
+
+  /* Write out the product info block ID */
+  additionalBlockID = htonl(additionalBlockID);
+  if (fwrite(&additionalBlockID, 
+      sizeof(additionalBlockID), 1, fp) != 1) {
+    return -1;
+  }
+  additionalBlockID = ntohl(additionalBlockID);
+
+  /* Write out the channel name and NULL terminator */
+  if (fwrite(infoBlock->MARChannelID, 
+      strlen(infoBlock->MARChannelID) + 1, 1, fp) != 1) {
+    return -1;
+  }
+
+  /* Write out the product version string and NULL terminator */
+  if (fwrite(infoBlock->productVersion, 
+      strlen(infoBlock->productVersion) + 1, 1, fp) != 1) {
+    return -1;
+  }
+
+  /* Write out the rest of the block that is unused */
+  unused = infoBlockSize - (sizeof(infoBlockSize) +
+                            sizeof(additionalBlockID) +
+                            strlen(infoBlock->MARChannelID) + 
+                            strlen(infoBlock->productVersion) + 2);
+  memset(buf, 0, sizeof(buf));
+  if (fwrite(buf, unused, 1, fp) != 1) {
+    return -1;
+  }
+  return 0;
+}
+
+/** 
+ * Refreshes the product information block with the new information.
+ * The input MAR must not be signed or the function call will fail.
+ * 
+ * @param path             The path to the MAR file who's product info block
+ *                         should be refreshed.
+ * @param infoBlock Out parameter for where to store the result to
+ * @return 0 on success, -1 on failure
+*/
+int
+refresh_product_info_block(const char *path,
+                           struct ProductInformationBlock *infoBlock)
+{
+  FILE *fp ;
+  int rv;
+  PRUint32 hasSignatureBlock, numSignatures, additionalBlocks, 
+    additionalBlockSize, additionalBlockID, offsetAdditionalBlocks, 
+    numAdditionalBlocks, i;
+  PRInt64 oldPos;
+
+  rv = get_mar_file_info(path, 
+                         &hasSignatureBlock,
+                         &numSignatures,
+                         &additionalBlocks,
+                         &offsetAdditionalBlocks,
+                         &numAdditionalBlocks);
+  if (rv) {
+    fprintf(stderr, "ERROR: Could not obtain MAR information.\n");
+    return -1;
+  }
+
+  if (hasSignatureBlock && numSignatures) {
+    fprintf(stderr, "ERROR: Cannot refresh a signed MAR\n");
+    return -1;
+  }
+
+  fp = fopen(path, "r+b");
+  if (!fp) {
+    fprintf(stderr, "ERROR: could not open target file: %s\n", path);
+    return -1;
+  }
+
+  if (fseeko(fp, offsetAdditionalBlocks, SEEK_SET)) {
+    fprintf(stderr, "ERROR: could not seek to additional blocks\n");
+    fclose(fp);
+    return -1;
+  }
+
+  for (i = 0; i < numAdditionalBlocks; ++i) {
+    /* Get the position of the start of this block */
+    oldPos = ftello(fp);
+
+    /* Read the additional block size */
+    if (fread(&additionalBlockSize, 
+              sizeof(additionalBlockSize), 
+              1, fp) != 1) {
+      return -1;
+    }
+    additionalBlockSize = ntohl(additionalBlockSize);
+
+    /* Read the additional block ID */
+    if (fread(&additionalBlockID, 
+              sizeof(additionalBlockID), 
+              1, fp) != 1) {
+      return -1;
+    }
+    additionalBlockID = ntohl(additionalBlockID);
+
+    if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
+      if (fseeko(fp, oldPos, SEEK_SET)) {
+        fprintf(stderr, "Could not seek back to Product Information Block\n");
+        fclose(fp);
+        return -1;
+      }
+
+      if (mar_concat_product_info_block(fp, NULL, infoBlock)) {
+        fprintf(stderr, "Could not concat Product Information Block\n");
+        fclose(fp);
+        return -1;
+      }
+
+      fclose(fp);
+      return 0;
+    } else {
+      /* This is not the additional block you're looking for. Move along. */
+      if (fseek(fp, additionalBlockSize, SEEK_CUR)) {
+        fprintf(stderr, "ERROR: Could not seek past current block.\n");
+        fclose(fp);
+        return -1;
+      }
+    }
+  }
+
+  /* If we had a product info block we would have already returned */
+  fclose(fp);
+  fprintf(stderr, "ERROR: Could not refresh because block does not exist\n");
+  return -1;
+}
+
+/**
+ * Create a MAR file from a set of files.
+ * @param dest      The path to the file to create.  This path must be
+ *                  compatible with fopen.
+ * @param numfiles  The number of files to store in the archive.
+ * @param files     The list of null-terminated file paths.  Each file
+ *                  path must be compatible with fopen.
+ * @param infoBlock The information to store in the product information block.
+ * @return A non-zero value if an error occurs.
+ */
+int mar_create(const char *dest, int 
+               num_files, char **files, 
+               struct ProductInformationBlock *infoBlock) {
   struct MarItemStack stack;
-  PRUint32 offset_to_index = 0, size_of_index;
+  PRUint32 offset_to_index = 0, size_of_index, 
+    numSignatures, numAdditionalSections;
+  PRUint64 sizeOfEntireMAR = 0;
   struct stat st;
   FILE *fp;
   int i, rv = -1;
 
   memset(&stack, 0, sizeof(stack));
 
   fp = fopen(dest, "wb");
   if (!fp) {
@@ -140,17 +338,45 @@ int mar_create(const char *dest, int num
     return -1;
   }
 
   if (fwrite(MAR_ID, MAR_ID_SIZE, 1, fp) != 1)
     goto failure;
   if (fwrite(&offset_to_index, sizeof(PRUint32), 1, fp) != 1)
     goto failure;
 
-  stack.last_offset = MAR_ID_SIZE + sizeof(PRUint32);
+  stack.last_offset = MAR_ID_SIZE + 
+                      sizeof(offset_to_index) +
+                      sizeof(numSignatures) + 
+                      sizeof(numAdditionalSections) +
+                      sizeof(sizeOfEntireMAR);
+
+  /* We will circle back on this at the end of the MAR creation to fill it */
+  if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) {
+    goto failure;
+  }
+
+  /* Write out the number of signatures, for now only at most 1 is supported */
+  numSignatures = 0;
+  if (fwrite(&numSignatures, sizeof(numSignatures), 1, fp) != 1) {
+    goto failure;
+  }
+
+  /* Write out the number of additional sections, for now just 1 
+     for the product info block */
+  numAdditionalSections = htonl(1);
+  if (fwrite(&numAdditionalSections, 
+             sizeof(numAdditionalSections), 1, fp) != 1) {
+    goto failure;
+  }
+  numAdditionalSections = ntohl(numAdditionalSections);
+
+  if (mar_concat_product_info_block(fp, &stack, infoBlock)) {
+    goto failure;
+  }
 
   for (i = 0; i < num_files; ++i) {
     if (stat(files[i], &st)) {
       fprintf(stderr, "ERROR: file not found: %s\n", files[i]);
       goto failure;
     }
 
     if (mar_push(&stack, st.st_size, st.st_mode & 0777, files[i]))
@@ -163,22 +389,37 @@ int mar_create(const char *dest, int num
 
   /* write out the index (prefixed with length of index) */
   size_of_index = htonl(stack.size_used);
   if (fwrite(&size_of_index, sizeof(size_of_index), 1, fp) != 1)
     goto failure;
   if (fwrite(stack.head, stack.size_used, 1, fp) != 1)
     goto failure;
 
+  /* To protect against invalid MAR files, we assumes that the MAR file 
+     size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
+  if (ftell(fp) > MAX_SIZE_OF_MAR_FILE) {
+    goto failure;
+  }
+
   /* write out offset to index file in network byte order */
   offset_to_index = htonl(stack.last_offset);
   if (fseek(fp, MAR_ID_SIZE, SEEK_SET))
     goto failure;
   if (fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1)
     goto failure;
+  offset_to_index = ntohl(stack.last_offset);
+  
+  sizeOfEntireMAR = ((PRUint64)stack.last_offset) +
+                    stack.size_used +
+                    sizeof(size_of_index);
+  sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
+  if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1)
+    goto failure;
+  sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
 
   rv = 0;
 failure: 
   if (stack.head)
     free(stack.head);
   fclose(fp);
   if (rv)
     remove(dest);
--- a/modules/libmar/src/mar_extract.c
+++ b/modules/libmar/src/mar_extract.c
@@ -36,19 +36,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <string.h>
 #include <stdlib.h>
-#include <stdio.h>
+#include "mar_private.h"
 #include "mar.h"
-#include "mar_private.h"
 
 #ifdef XP_WIN
 #include <io.h>
 #include <direct.h>
 #endif
 
 /* Ensure that the directory containing this file exists */
 static int mar_ensure_parent_dir(const char *path)
--- a/modules/libmar/src/mar_private.h
+++ b/modules/libmar/src/mar_private.h
@@ -34,17 +34,84 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef MAR_PRIVATE_H__
 #define MAR_PRIVATE_H__
 
+#include "prtypes.h"
+#include "limits.h"
+
+/* Code in this module requires a guarantee that the size
+   of PRUint32 and PRUint64 are 4 and 8 bytes respectively. */
+PR_STATIC_ASSERT(sizeof(PRUint32) == 4);
+PR_STATIC_ASSERT(sizeof(PRUint64) == 8);
+
 #define BLOCKSIZE 4096
 #define ROUND_UP(n, incr) (((n) / (incr) + 1) * (incr))
 
 #define MAR_ID "MAR1"
 #define MAR_ID_SIZE 4
 
+/* The signature block comes directly after the header block 
+   which is 16 bytes */
+#define SIGNATURE_BLOCK_OFFSET 16
+
+/* 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 ((PRInt64)524288000)
+
+/* Existing code makes assumptions that the file size is
+   smaller than LONG_MAX. */
+PR_STATIC_ASSERT(MAX_SIZE_OF_MAR_FILE < ((PRInt64)LONG_MAX));
+
+/* We store at most the size up to the signature block + 4 
+   bytes per BLOCKSIZE bytes */
+PR_STATIC_ASSERT(sizeof(BLOCKSIZE) < \
+  (SIGNATURE_BLOCK_OFFSET + sizeof(PRUint32)));
+
+/* The maximum size of any signature supported by current and future
+   implementations of the signmar program. */
+#define MAX_SIGNATURE_LENGTH 2048
+
+/* Each additional block has a unique ID.  
+   The product information block has an ID of 1. */
+#define PRODUCT_INFO_BLOCK_ID 1
+
 #define MAR_ITEM_SIZE(namelen) (3*sizeof(PRUint32) + (namelen) + 1)
 
+/* Product Information Block (PIB) constants */
+#define PIB_MAX_MAR_CHANNEL_ID_SIZE 63
+#define PIB_MAX_PRODUCT_VERSION_SIZE 31
+
+/* The mar program is compiled as a host bin so we don't have access to NSPR at
+   runtime.  For that reason we use ntohl, htonl, and define HOST_TO_NETWORK64 
+   instead of the NSPR equivalents. */
+#ifdef XP_WIN
+#include <winsock2.h>
+#define ftello _ftelli64
+#define fseeko _fseeki64
+#else
+#define _FILE_OFFSET_BITS 64
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+
+#define HOST_TO_NETWORK64(x) ( \
+  ((((PRUint64) x) & 0xFF) << 56) | \
+  ((((PRUint64) x) >> 8) & 0xFF) << 48) | \
+  (((((PRUint64) x) >> 16) & 0xFF) << 40) | \
+  (((((PRUint64) x) >> 24) & 0xFF) << 32) | \
+  (((((PRUint64) x) >> 32) & 0xFF) << 24) | \
+  (((((PRUint64) x) >> 40) & 0xFF) << 16) | \
+  (((((PRUint64) x) >> 48) & 0xFF) << 8) | \
+  (((PRUint64) x) >> 56)
+#define NETWORK_TO_HOST64 HOST_TO_NETWORK64
+
 #endif  /* MAR_PRIVATE_H__ */
--- a/modules/libmar/src/mar_read.c
+++ b/modules/libmar/src/mar_read.c
@@ -34,34 +34,26 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <sys/types.h>
 #include <fcntl.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
+#include "mar_private.h"
 #include "mar.h"
-#include "mar_private.h"
 
 #ifdef XP_WIN
 #include <winsock2.h>
 #else
 #include <netinet/in.h>
 #endif
 
-#define TABLESIZE 256
-
-struct MarFile_ {
-  FILE *fp;
-  MarItem *item_table[TABLESIZE];
-};
-
 /* this is the same hash algorithm used by nsZipArchive.cpp */
 static PRUint32 mar_hash_name(const char *name) {
   PRUint32 val = 0;
   unsigned char* c;
 
   for (c = (unsigned char *) name; *c; ++c)
     val = val*37 + *c;
 
@@ -238,16 +230,135 @@ void mar_close(MarFile *mar) {
       item = item->next;
       free(temp);
     }
   }
 
   free(mar);
 }
 
+/** 
+ * Reads the product info block from the MAR file's additional block section.
+ * The caller is responsible for freeing the fields in infoBlock
+ * if the return is successful.
+ *
+ * @param infoBlock Out parameter for where to store the result to
+ * @return 0 on success, -1 on failure
+*/
+int
+read_product_info_block(char *path, 
+                        struct ProductInformationBlock *infoBlock)
+{
+  int rv;
+  MarFile mar;
+  mar.fp = fopen(path, "rb");
+  if (!mar.fp) {
+    return -1;
+  }
+  rv = mar_read_product_info_block(&mar, infoBlock);
+  fclose(mar.fp);
+  return rv;
+}
+
+/** 
+ * Reads the product info block from the MAR file's additional block section.
+ * The caller is responsible for freeing the fields in infoBlock
+ * if the return is successful.
+ *
+ * @param infoBlock Out parameter for where to store the result to
+ * @return 0 on success, -1 on failure
+*/
+int
+mar_read_product_info_block(MarFile *mar, 
+                            struct ProductInformationBlock *infoBlock)
+{
+  int i, hasAdditionalBlocks, offset, 
+    offsetAdditionalBlocks, numAdditionalBlocks,
+    additionalBlockSize, additionalBlockID;
+  /* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and 
+     product version < 32 bytes + 3 NULL terminator bytes. */
+  char buf[97] = { '\0' };
+  int ret = get_mar_file_info_fp(mar->fp, NULL, NULL,
+                                 &hasAdditionalBlocks, 
+                                 &offsetAdditionalBlocks, 
+                                 &numAdditionalBlocks);
+  for (i = 0; i < numAdditionalBlocks; ++i) {
+    /* Read the additional block size */
+    if (fread(&additionalBlockSize, 
+              sizeof(additionalBlockSize), 
+              1, mar->fp) != 1) {
+      return -1;
+    }
+    additionalBlockSize = ntohl(additionalBlockSize) - 
+                          sizeof(additionalBlockSize) - 
+                          sizeof(additionalBlockID);
+
+    /* Read the additional block ID */
+    if (fread(&additionalBlockID, 
+              sizeof(additionalBlockID), 
+              1, mar->fp) != 1) {
+      return -1;
+    }
+    additionalBlockID = ntohl(additionalBlockID);
+
+    if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
+      const char *location;
+      int len;
+
+      /* This block must be at most 104 bytes.
+         MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL 
+         terminator bytes. We only check for 96 though because we remove 8 
+         bytes above from the additionalBlockSize: We subtract 
+         sizeof(additionalBlockSize) and sizeof(additionalBlockID) */
+      if (additionalBlockSize > 96) {
+        return -1;
+      }
+
+    if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) {
+        return -1;
+      }
+
+      /* Extract the MAR channel name from the buffer.  For now we
+         point to the stack allocated buffer but we strdup this
+         if we are within bounds of each field's max length. */
+      location = buf;
+      len = strlen(location);
+      infoBlock->MARChannelID = location;
+      location += len + 1;
+      if (len >= 64) {
+        infoBlock->MARChannelID = NULL;
+        return -1;
+      }
+
+      /* Extract the version from the buffer */
+      len = strlen(location);
+      infoBlock->productVersion = location;
+      location += len + 1;
+      if (len >= 32) {
+        infoBlock->MARChannelID = NULL;
+        infoBlock->productVersion = NULL;
+        return -1;
+      }
+      infoBlock->MARChannelID = 
+        strdup(infoBlock->MARChannelID);
+      infoBlock->productVersion = 
+        strdup(infoBlock->productVersion);
+      return 0;
+    } else {
+      /* This is not the additional block you're looking for. Move along. */
+      if (fseek(mar->fp, additionalBlockSize, SEEK_CUR)) {
+        return -1;
+      }
+    }
+  }
+
+  /* If we had a product info block we would have already returned */
+  return -1;
+}
+
 const MarItem *mar_find_item(MarFile *mar, const char *name) {
   PRUint32 hash;
   const MarItem *item;
 
   hash = mar_hash_name(name);
 
   item = mar->item_table[hash];
   while (item && strcmp(item->name, name) != 0)
@@ -286,8 +397,198 @@ int mar_read(MarFile *mar, const MarItem
   if (nr > bufsize)
     nr = bufsize;
 
   if (fseek(mar->fp, item->offset + offset, SEEK_SET))
     return -1;
 
   return fread(buf, 1, nr, mar->fp);
 }
+
+
+/**
+ * Determines the MAR file information.
+ *
+ * @param path                   The path of the MAR file to check.
+ * @param hasSignatureBlock      Optional out parameter specifying if the MAR
+ *                               file is has a signature block or not.
+ * @param numSignatures          Optional out parameter for storing the number
+ *                               of signatures in the MAR file.
+ * @param hasAdditionalBlocks    Optional out parameter specifying if the MAR
+ *                               file has additional blocks or not.
+ * @param offsetAdditionalBlocks Optional out parameter for the offset to the 
+ *                               first additional block. Value is only valid if
+ *                               has_additional_blocks
+ *                               is not equal to 0.
+ * @param numAdditionalBlocks    Optional out parameter for the number of
+ *                               additional blocks.  Value is only valid if
+ *                               has_additional_blocks is not euqal to 0.
+ * @return 0 on success and non-zero on failure.
+ */
+int get_mar_file_info(const char *path, 
+                      int *hasSignatureBlock,
+                      int *numSignatures,
+                      int *hasAdditionalBlocks,
+                      int *offsetAdditionalBlocks,
+                      int *numAdditionalBlocks)
+{
+  int rv;
+  FILE *fp = fopen(path, "rb");
+  if (!fp) {
+    return -1;
+  }
+
+  rv = get_mar_file_info_fp(fp, hasSignatureBlock, 
+                            numSignatures, hasAdditionalBlocks,
+                            offsetAdditionalBlocks, numAdditionalBlocks);
+
+  fclose(fp);
+  return rv;
+}
+
+/**
+ * Determines the MAR file information.
+ *
+ * @param fp                     An opened MAR file in read mode.
+ * @param hasSignatureBlock      Optional out parameter specifying if the MAR
+ *                               file is has a signature block or not.
+ * @param numSignatures          Optional out parameter for storing the number
+ *                               of signatures in the MAR file.
+ * @param hasAdditionalBlocks    Optional out parameter specifying if the MAR
+ *                               file has additional blocks or not.
+ * @param offsetAdditionalBlocks Optional out parameter for the offset to the 
+ *                               first additional block. Value is only valid if
+ *                               has_additional_blocks
+ *                               is not equal to 0.
+ * @param numAdditionalBlocks    Optional out parameter for the number of
+ *                               additional blocks.  Value is only valid if
+ *                               has_additional_blocks is not euqal to 0.
+ * @return 0 on success and non-zero on failure.
+ */
+int get_mar_file_info_fp(FILE *fp, 
+                         int *hasSignatureBlock,
+                         int *numSignatures,
+                         int *hasAdditionalBlocks,
+                         int *offsetAdditionalBlocks,
+                         int *numAdditionalBlocks)
+{
+  PRUint32 offsetToIndex, offsetToContent, signatureCount, signatureLen;
+  int i;
+  
+  /* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
+  if (!hasSignatureBlock && !hasAdditionalBlocks) {
+    return -1;
+  }
+
+
+  /* Skip to the start of the offset index */
+  if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) {
+    return -1;
+  }
+
+  /* Read the offset to the index. */
+  if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fp) != 1) {
+    return -1;
+  }
+  offsetToIndex = ntohl(offsetToIndex);
+
+  if (numSignatures) {
+     /* Skip past the MAR file size field */
+    if (fseek(fp, sizeof(PRUint64), SEEK_CUR)) {
+      return -1;
+    }
+
+    /* Read the offset to the index. */
+    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(PRUint32) 
+     could oerflow in theory. */
+  if (fseek(fp, offsetToIndex, SEEK_SET)) {
+    return -1;
+  }
+
+  if (fseek(fp, sizeof(PRUint32), SEEK_CUR)) {
+    return -1;
+  }
+
+  /* Read the first offset to content field. */
+  if (fread(&offsetToContent, sizeof(offsetToContent), 1, fp) != 1) {
+    return -1;
+  }
+  offsetToContent = ntohl(offsetToContent);
+
+  /* Check if we have a new or old MAR file */
+  if (hasSignatureBlock) {
+    if (offsetToContent == MAR_ID_SIZE + sizeof(PRUint32)) {
+      *hasSignatureBlock = 0;
+    } else {
+      *hasSignatureBlock = 1;
+    }
+  }
+
+  /* If the caller doesn't care about the product info block 
+     value, then just return */
+  if (!hasAdditionalBlocks) {
+    return 0;
+  }
+
+   /* Skip to the start of the signature block */
+  if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
+    return -1;
+  }
+
+  /* Get the number of signatures */
+  if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
+    return -1;
+  }
+  signatureCount = ntohl(signatureCount);
+
+  /* Check that we have less than the max amount of signatures so we don't
+     waste too much of either updater's or signmar's time. */
+  if (signatureCount > MAX_SIGNATURES) {
+    return -1;
+  }
+
+  /* Skip past the whole signature block */
+  for (i = 0; i < signatureCount; i++) {
+    /* Skip past the signature algorithm ID */
+    if (fseek(fp, sizeof(PRUint32), SEEK_CUR)) {
+      return -1;
+    }
+
+    /* Read the signature length and skip past the signature */
+    if (fread(&signatureLen, sizeof(PRUint32), 1, fp) != 1) {
+      return -1;
+    }
+    signatureLen = ntohl(signatureLen);
+    if (fseek(fp, signatureLen, SEEK_CUR)) {
+      return -1;
+    }
+  }
+
+  if (ftell(fp) == offsetToContent) {
+    *hasAdditionalBlocks = 0;
+  } else {
+    if (numAdditionalBlocks) {
+      /* We have an additional block, so read in the number of additional blocks
+         and set the offset. */
+      *hasAdditionalBlocks = 1;
+      if (fread(numAdditionalBlocks, sizeof(PRUint32), 1, fp) != 1) {
+        return -1;
+      }
+      *numAdditionalBlocks = ntohl(*numAdditionalBlocks);
+      if (offsetAdditionalBlocks) {
+        *offsetAdditionalBlocks = ftell(fp);
+      }
+    } else if (offsetAdditionalBlocks) {
+      /* numAdditionalBlocks is not specified but offsetAdditionalBlocks 
+         is, so fill it! */
+      *offsetAdditionalBlocks = ftell(fp) + sizeof(PRUint32);
+    }
+  }
+
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/Makefile.in
@@ -0,0 +1,69 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is libmar test code. 
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2012
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Brian R. Bondy <netzen@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH     = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+relativesrcdir = modules/libmar/tests
+
+include $(DEPTH)/config/autoconf.mk
+
+XPCSHELL_TESTS = \
+  unit \
+  $(NULL)
+
+TESTROOT = $(call core_abspath,$(DEPTH))/_tests/xpcshell/$(relativesrcdir)
+
+DEFINES += -DBIN_SUFFIX=$(BIN_SUFFIX)
+
+include $(topsrcdir)/config/rules.mk
+
+libs:: unit/head_libmar.js.in
+	$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) $^ > $(TESTROOT)/unit/head_libmar.js
+
+ifneq ($(OS_TARGET),Android)
+ifndef MOZ_PROFILE_GENERATE
+libs::
+	$(INSTALL) ../tool/signmar$(BIN_SUFFIX) $(TESTROOT)/unit
+	$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)nss3$(DLL_SUFFIX) $(TESTROOT)/unit
+	$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)nssutil3$(DLL_SUFFIX) $(TESTROOT)/unit
+	$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)plc4$(DLL_SUFFIX) $(TESTROOT)/unit
+	$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)nspr4$(DLL_SUFFIX) $(TESTROOT)/unit
+	$(INSTALL) $(DEPTH)/dist/bin/$(DLL_PREFIX)plds4$(DLL_SUFFIX) $(TESTROOT)/unit
+endif
+endif # Not Android
new file mode 100644
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..357eeb9a873429ce491e0ebce3abcdd77c143ba2
GIT binary patch
literal 157
zc%1Wf3^HV3U}#`~fVohb5r{KDw1cl>P`tB;qo1FvPrRp#1B0Qlo<X7^0~LWJ!hXhW
R2Jyw2RjDcQX_+~x3;<<p4vqi-
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/data/1_byte_file
@@ -0,0 +1,1 @@
+1
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a137f11adc46994b3808ee1ea3933403d61af93e
GIT binary patch
literal 157
zc%1Wf3^HV3U}$83fVohb5r{KDw1cl>P`tB;qo1FvPrRp#1B0Qlo<X7^1GNCiBncpH
VfLO}7%`iTxvLrP=Ei)&T0RUw44psmF
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a0d7369e4551faa649af1ab1e5ee00f5d5e7d60f
GIT binary patch
literal 512
zc$@{n0{{>J005x2ZQHhO+qP}nwr$(CZQHg{1Q1Xlfdvs%Fu{coQYfK?5mq?iMG#RW
zkwp<zG||NnQ!KH?5m!9%C6G`ei6xO#GRdWoQYxvXkybkCWsp%OnPrhxHreHnQ!csX
zkyk$X6;Mzig%wd$F~yZoQYodCQC2zSRZvkSl~qwyHPzKnQ!TaCQCB_nHPBEajWy9!
zGtIToQY)>s(N;U{b<j~KopsSwH{JEnQ!l;s(N{nH4KUCkgAFm%FvE>7(kP>iG1fTa
zO)$|UlT9(zG}Fy6(=4;iG1olvEwIoci!HI#GRv*7(kiR1vDP~4ZLrZMn{BbxHrws6
z(=NO1vDZHP9dOVghaGX$F~^;7(kZ8%an?EKU2xGQmtAqyHP_v6(=E5%ao0WfJ@C*Y
ok3I3!Gta&7(krjM@zy)<eelsIpMCMwH{bp6(=WgM@z+290bBcjfdBvi
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7fef469898099c1e7ffb2e3a567232da775f65e7
GIT binary patch
literal 673
zc$}@$_nQv{0LSr{BQhf*<BG`4xR4dy;c~`Z9I33YFL!dBOXtE_k&)Ffipt1d86{iU
zdu5MOQJL8#C3L5M!TWjM&*%5g^Nx;)4I^Zr`-DvUe`QFsbNYryN5lr|MMcEKL^cUD
ztRF6{a)nC4Fb|FlLS&T5Lo&<aVOeF9T@E?r@`&8>cvN2bJSM*a9#>Eyg%uI%2~R4j
zn5Pt1!qZABrL;22D(4yHRZvkSVV+f46;(Z_n(At(sg~!}R>up%MX0Nu`XarkfhY|%
z(pVGGV!WiOW@0th!pmBE#j9S^N^5Pj)lNWrK^=4yCtfG7r=6Cdi>?xNljIHEy(w7_
zDc;gkFTJJeqpyDY8{lmN4f2k`h8Sv?cMUhfNTZDQp7(v=Lt~6J&UhdB*aQ=O;!~fQ
zWU?uy`rI_r%`np}v&}KrJo7E^g@qPbY>6*@WvQ<%v)l?Rt+Lu0YklKe>#VoIMw@K5
z#a7#Fx5G}m?6${V`+R4=0}eXmup^E-=C~8S_k)vu^pl^Ra@sF`^_w%!I_JC#F8bXi
zmtAqyHP_wnhd<r)ms|e!kK6ur$6fc*0|-qst-p};7M6ENND3yW2I7J#!9b^k#CZP!
DtBa59
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..561e5051d9fa8671d57a079335de1e7b83a02f2e
GIT binary patch
literal 65536
zc%1FrdrXyO9KiACeb3>Xg9yYem7CNgT|}LOV4C1TC4-bkD8~(vo6(|yqJRS(8y-m#
z?-3V?ikqc4)TqSf(1;o|Os5bgAVZBt9HzM;xB(+>Ypv0LF8=8IdH3x1doJ($ZqI)I
zK95j2#0Vi|A(S_S@E=Qsl@OAcY(g`Y$Wu`<5htdEay-x5nsHA|lBe2#P9x34UjYCB
z0000000000000000000000000cyg3tP~Wfb(>LpX7;B>d00000000000000000000
z00000007|0(8xmAYi)H;X{}U>Fr~sy9h~mAI&MdboS434EC`Jd!VtG3Ieyz#p%f<l
zC4Gh7<a^uK<U7;15dZ)H000000000000000000000002^pU_A`=|BF+CxlvSluNZn
zX@7}AmSovX6nI=*skKy@Kff<<-=L)=nVE^{j!<h|=UD2zv|e6Xt@q-W7cUK;==%KC
zo%S80^z7e1Etv_0QL+|e<FjO=B#AP+k_R5A2E+Y3bAQj>vm)L6rn@#o9h{XJn}7Pq
zuEvBzvu$rg{~G;MyG@dLLrTV(Bro@@!+91#k9!PGhq{);wtiKX7}$5;uV#gvcUsom
zVBfjd*5sd@*XI?wFs!la({edty&)<43*GS3bCikc4z25kUW&b^E04;qa>+?OeP@x4
zX;yUWBiA<)v|o-iY%fbo>e_JC_031yzN`JVWus@yrc$Tc!TTY7G3Qf<j8@&Nn-gs!
zt0U`%q|o$s^B;%b?s15H$1C}isx0Qiw;aMdj8U&GxV}81sqz*3*UwuyZfH21?Nh3A
zam*ZgRq~0?IcpI&{{__&w{yqU)kW3y-ph5DWrZZ9e{8;Dd}D(A7SyQ@lqHOQw6yax
zvC1O6;$Ym-?ays062}iZ2i5Iq@%SeA>PmI)AD+(r0i#_>?N+;c*9{caHa&>^IN@TE
zF7~(7yAA3O^4o7&-%?ig>}<4NwBN2uZaa3ObJ)~d;8|GT(c_a-`19Tu-z^^Ka{0bH
zCf@#Nwum(-XB!^AFFOTPbrjsaT;DV@-_JJl@ks0K;>_CS(#^j(u3x*XJJ8UW+~}@u
z`{Yi=$d%0-GkWZIxoxS;FIpI8Yva88z(Z|KiQ4^4XzJR$D)X<;DqV_GA_A_(v{k2F
z*LWWD%abkzM>Xy3S1!BRdm&^XI;61X<dt*J1YH<d_4!Vh+V{%Wm<DpE&g)B))j3UR
zX%u2?RxQNTN-=M&&i`$(V!FYRnZB(600000000000000000000000000001t{t3Xp
Bm1h6|
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..970e8ffc0b9c280420cf9e3ddd270efd86fecdd6
GIT binary patch
literal 16384
zc%1Fn`8U-29|!Qym@!P2!5CY%WE<<qTG5q#NsTc~B+FQbVXWD*gsiz#hNQ?+$S!Lx
zAu`U*+(Npxa>=E*Qe9if{eJK5hx-rQ+qvhwALqQz>v_)Whu4p<b6yY<>kdH>8w4Rn
zAc*gO5#oj*81z>`oL>Xm*AD-)|2cvX|IduT*m>wLY+vhN#|iruTLAz7000000002s
zzk?InMsK0tp<B__=zMfOZviR^Re;(D000000091Na0mwB6A>GDMI@I}ibcvM4~&}~
z2FHsc1iNNrh^<B&wPnjk%v*mzl8P%Hk=bA{L?VJ&uayP&)V{EzS75RDWlw8~FQ>yB
z6`=CMPNO}6eJ^jn`tzJpxNRJUuYt@Rf4v{6F1lNOvTi#U%56t_b!{%`l1lo2q7iK!
z7AXC}8;$~Idd!byE!w9xq+1FP$I1Ea>QhGSuE5Q=X3XzjlZ>WLE>*>c!g9@=9t=_x
zHBBSLrlycX38`5zu5;|n^aWn1|4XyZ6i#hJrLFh56~{TNeU<gd`CHC%klDU(Z-rxQ
zk@a&tNw(*<Oy%rVEm!!c^DPxk0*M-=a477Mp>q$q`!g?Fm5o99aE_BKsm-w@ZLT;*
z=Wsanad>82-@^O(q&|McWEG6VO)ib}^QWz+=`9};yY($!VPBq!3Xv0OU0ZcYI^QA|
zsod!t1GA1zyw636$EAJ5c`Uf38@EkwP4<=F99eAl7yQ*8Y2McSp-8~!!49WN4RTK>
zl^~xd27T9fdOFm{4Q@c6iMXg2UF{Ox!9$fi(X+QQKG8NE*qF)RVRDCeGRo!T=gOJ%
zXx*ahEG)hGGEGwNiy-akDHPEThG!Vxt`QX9t-qJ7WhdU9y<9WEI%-5tkWgCI!Eauo
z8BA%fnGhlmck!-IocAP*2XU!;B2A9RXS_&C9z_YxzCMsOQSME(Y<)K{{*;xoYpRHg
zB_?HyC$#)-`M#n~KA+QW?4f%PJ}9$K#?<ylg=ezluI8A0x{Y}0%lkX8;%7w6^snqm
zH%IkYFgEqRGbnx*j5%yPX>oN|ZhNWD)!|H-lp;CBeXFj;d-|%ZUHOwnwV2u`i28S4
z7vVm*)012m^(I%Zbrz~;TLz~$igx;?=+qBnVUAJmaGzD%PGdEn^OJ{hJy@FyaiSyA
z?;bo4&np}pJsU73Xw*Phe62ZGds)xLtMlP;b18<Q_!~Q7)df}myY^19Qw|Mt{Vl$C
z#s-)gUR7!pO;X*x3+Hv)$rr}Ag}K5L1-fi@UgpiJ9>OdhjYawwDUwb%2hXdqU+&Va
z!g|HFXAKD*3#8=Ll)F|pyq2{%S-DBMO+;T>eilT;wk}D3DP`dpw`ul;X}gc*J*=!!
zX+sqyQ@DbTH6Ljq7nBIaLN_6f`r9vv-oBm64|<hjONH=9^6^ux^<uL{nJ^Z!ydpa;
z!JT2;5GkBY@>_V@bgefpEBxr}N+&7%hE!*^*$v^i9PbfbEKx>HRf%7`+*e)UtX>q~
z+0eH*vq{mWm?IUN%SH-Rahh!o)ANVvUnxR1Z%p|*c~e8mr1ZPcta}bMm5p_nRgZK*
zY0(wk4JB(t&$5=X&sd4(Q9^oWtE4k)SIgi4^iZl{zbV#p+Bz+WX{nQ`tu8Ul=Ez9u
zHW#SfSDF4m3_}ib$f37BG4c(<{V(xZFTGoA!0~pbIbVSdDD=IcYodz-ljfd}Tjz2O
zy)jJM!f}=<VKd2!(z6*wj1#$MMvU&s`mvbG>&Nm;=b6Fr>PKaqImvHxUh(k=yT|BY
zB9;-G;Vni<XL59hV_l4OeNXJ!pX4)9-S$j9u%;|@L)%+XHXSmQH?SbYl_sc@@uMG?
zi?Vt2uFvDH78rAz$oosMN#^TRS<Q0aniP2Um>Yi)bx(8xD}{c4vqRNGn$=7&3j5jS
z{yphui!4M7rlb^e=e=^(rjy~T>u;}LIuV6>DTgO;*d!-V@~X3=nY1i!ff2ViZo^aj
zL8!s7*t(!2a>?H_xGcAp91dp9>QKg;O9L!cRt?%tV6-BWMQrp%elp9e6kRz&RIq2(
z7MHO(+c^ZKHfrn&2_picu;Zt8)G6~CWv;2U5*gvuzbNuAA8;FAmpo0wKiC@#E{zPD
zC9x!J*Xey-J!53mv2!H{yEaz|EAFk~JBhpt#-joJE=?}IJ1}C@6l!SKdkIVNJrY`l
zEOpQI#5zqUx|+*AKkoeF{bPbwt-NBS!S7SGOEe_J5_U{s+SH7CE-xt+q>gk$0Y}r#
zrpH>xH6Aez(%S@nSHC-UYrcV=k-tl=z)bki=oELFYN$Ic3@!*uPPI&Z<M<@x?zAzx
z!B~c8gnu<J^!H0q_$iU+x9t>aVtar*88`ei$UQVPf*#_j>f!6<;SYE43JIms1HbkE
k0RR91000000001hf8O4={&NEW00000000000Pv6d1N1X=)c^nh
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4ce15f12284935359395d03999a514dbdbf7ac7b
GIT binary patch
literal 938
zc$}@1_g@SE0L8x*rG>1F7I&dTI<6gcB&C5PE1WYTk`)?KR#JpeHsz2#JEQCoGLo55
zQIVBZDGG%@!TWsPd%wTW_jyZYV$g`;k2LnwhT31N(OG=6{hK&XJBNkYu@b8&-84am
zHCa(5HDQHQ3i1}0$5t8G8s@o`?G~=`NIX5JvRO%>scDMjzW>*%q9kqqa8UzslBac+
zV46Y5RIPZG-nQ^hvfyNiTy>#Wg>|Zhex&ewLQe8IQGj2jZ9$x@Y{$D-`ofwFhYG{v
zBE#JNv(mNZS?<zJH%a~yY2$k=`g+rl#t}oSU5eb5S<-{f+NmkS9L+jeHFR^1&{XJ5
zRCc%N=GXjaf?H6&X1RKc{MB&}1?F!ZPkQN}i5K{+jJ|4?r+!(PX|eO((zYtU(wApl
zBZc$rGL{&d1o{4`eyohQe=%J*CoMR3MRrWFsGpO>sOH}mlj_>VTPl<Hc9;72^zo8=
zxVj*=x3p4-`F~PF9S!PHA5FAqfHpelqK7^WX+&cTXhKt((VP|-(vnuRrVU0IV}dDW
zw51&agqYKw4s^r<5uNBv7cA+D6=J&49SPRhV2d60IN*pAJ?M!uE=cJ`Z(Qkv8+~!d
zgMN78g*QI*X8;3{F^IwVGK8TFV>l!5V<e*(jhrzk7|S@uGXZ}l){YjyWTp_vRDzhs
zbY?J<Sp+kiIm{)5P{Igj9udqZk|-9ikVPzJ3DGPihGoPO$8uH>&q`LYngrIcmUSeO
z#CkTckxgu73tQR7c9Kb92TFFbi`}HMhrR4$KL<F-Ar5ndqa5QnX{b2CNltN^bTT-@
zS<Z2u3tZ$9m$|}KGP%ZeZji-IZgHDCWOJ7s?s1<7Jme9N$>j-8dB$_{$ftl8yrhsK
zih0FrN_fLt-tnFfl=6{JeC7*ZDWjYUzEMdP-}yl`HT>jP9RS9)4AlNmy^aOd*nl9#
R%uxAwMX*Bd9}qYJo!@sO2}J+^
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..183493a36810dcfb21fcc07c349708dd7a99ffd3
GIT binary patch
literal 723
zc$}@$iIWcm90l-?B~mFxNvPaM(*9Z{61(lL9J`Bk73J4&+uvq$gx%DxYq!%)uH09-
z&#q08qtG4EC3GQY<mi&pZ!!J?Z|2RL`M&win|bq+<5B}e<vWY0ZRb>x2)h+(Y;s&`
zFur+QN=jl<uth?wKuo>pP{74PpfD>+QN>)MxDqZ^(q%4pg;K6`mC~+ujWWu*)^*Cc
zUU@gDprT4{bd#G^Rz+3SR98bywbWL}E$X^eJ-3ODMBFY$eRsH119xetk-If^k0zRm
z73W^@5+rKoKFzhzQY%T4rD&~<wo<jzUI!g@a=!<3*2RM!(pAvILee}UU08-kBduk6
zOm{u>)XU>~>!YtM*`CnPlb(_zSAPQxG{|6i@;&Vt&w9@DUNFQ^!wfgVNTZB4#*4-p
zXS@j}ddVb{O)=Fp)6Fo`EVIq=vRBMC&wL9kw8&yhEVayXE4=D8uY1Fr3cO{dx4mPP
z)!wznTI;O$p7(9A(Iy}G&__P@iBEmzbDM4Pg{`*v(pSFrjc;xDogH@C<$FK)(NBK1
z+a7!UVxRpEIOveWes#oAzd7c(6HfZwAO7^0zn$`r)Bg3JGld&Si7@Z{?h2xVS(&-v
p^k7D2kFfJbkwPO7Ov}j*U${pW8+G;}EwfjsZ%!~hlpQ)}{tp#7o8bTe
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..185b2dff4a696f6ec9b7330eeefddd4d57617eac
GIT binary patch
literal 677
zc$_n6Vp?d>#MrrjnTe5!iIrix_HIW5UN%mxHjlRNyo`*jtPBQ1h5`nBY|No7%);!x
zl?u+OMI{Du;=G1N21Z5(1}4Vl#uiavt|^?0RZkP660*^ZtPIRejQk8haW1ANMn;B3
z!VBJOulye6aBuR<$&GfEoYz$i0@!_98j_~1?yowL)5{}tDd9=N+1r8zoX1OQ*A*D4
zw)9Qq_Wbc8OuF}hS<<yFi*h~QytCbICu~yDBH?Qzao%m(3duJ{LF%C=*3Mtd6cZd)
z(7w^|kC-@1Zl&lopWpgPj|~^cx387$Dqa0hL+~|M!nLn*emMr4{~j+~R8jCC<eXgK
z*Zf0!4_pb?y%N1pdhhpl0dEqwmHuwxd+K~SS1@i<+`-?BL6x^TPyR`JA)1t7RJ@XH
zQR0*o(WpC3@$M=Ytz%BFvJ&yy#wQhWyszDKp`ol)!*5qc)6A~*+^O<9Y-S1@X0mUZ
zwdt^lwc&myW<~}^w7_Qu1&pVy%0ag7MLGXxTijd5;KUuZq$hPkp<47ThM7Gwo(CJR
zXz%nr=g2<!gRaaMm;Vn6Zu3>Y^7%Gv@9Fn(b8~jjGE90_`sg_Oq-nQr^50}x^`h(q
zzecC<TBchQm)`sH`qgyZ8Hew@FzuRgwOKD~&bJ4$N1i5TicDx{ND5=&3H#W=B<-^H
z&h$t751;-kZ!6UB<L|Y*a~k$uUKo2rD%jidsYlp};uEUux8^-u^7l+^c<l?3Duv`#
z(`KoM3JJ<ocYie4zJOhIT~Mj_)U}*j*R#mZDT#48pLlCi#YGO?iMCT2_xZ-3ZvMhz
idHvOnfNu!_Gq$fdvq8~w$2X_tWwLu)7rVXwHW>gHs~D94
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6eaaf6bbef3416a0faa87f0d2bce1c834072c64c
GIT binary patch
literal 553
zc$^K=1$P$&008kHlNj9%BHi7Hz@&u<jLz|g4VW|xWTKQ3N|&_K-QC@t(xG%Hh#;YY
zyZ7$h7r4-nu;K&-6GxB_#1&6`2?R?hk;IZnDw*U`NGX-n(nu?v^fJgOlgzTn>O<Ll
zB)c4P$|biv^2#T_0tzamup){ornnMHDy6hC$||S43O-iRCn~9|iV&Zws+#I*sHv9P
zK2t|sp*~kneGP<ZsFB8+XsVg!T4<@2*4hZyRzN%LMTpcvN1a6ZLT6oc)lGLj^wdjl
z(fa7?OZ~+7N`GJb#sC8iGT0E``p!_p3^&5}elXG~qm41vIO9z)(Ik`oXo{((nQn%e
zX8Fm_W}9QKdFESSp+y#3VyR`8TVbVDR$F7Ob=KQpqfIv3VykVo+hM0&cH3jGefB%x
zphFHj;;3VeJK>~LPCMhQbI!Zq7Z+V}*%en^bKMO$-SVs7+;+!Z_uTiuLytW6#P9y_
z)H8p2?k_LA^tV@Dd*iKt{OdpOycbK>SYtm3icOr2?V`E`dc}lC1fm1s9iqBK`X7CI
BhxY&g
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4c2e0cc78f95d42f502ac842bf2b0738b3236830
GIT binary patch
literal 16384
zc%1Fp%}T>S5Ww+iDy0XhUc8Acc-WiJ2M8YOMS@a@FA%ev5?Zq*O+gPnhG%^yFP@9O
zhG^^tYC*gRR^<PKVP-#=e4Ix*dx?kyB3q9lyLBnsA}#SD-KGed++OD8k-v0T+&+sJ
zH1X@YtxY@?0000000000004l$BaoM!MenJ%=xzE<000000QiZHC|Rsch(~o8X(rrd
z|2&S>X;Im-NG56+4PtfJ@2fLs<`Y|=->0s~@{!5QP-o6*mD;izmNv_;L#@@=-mDfg
zn_f8+YM+nHrB>M_8O=g9pPHny%O<*64A%Piz36i{zm=O+ec{HoII**|%%@fEijNaN
bhyTz%4(ojI)gSoo17F_&00000z<>7+2oFWR
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f6e449e3eaf14c8e5b6117eccaf0ebf9ff443d89
GIT binary patch
literal 829
zc$_7Z2U`pP0EXW>rG${3qLg!}l$DV^t`aFDGj|mw(nNzY8Zs`F6{4&LqRdMXp_0pr
zlVl}(r$SMD&+~mh;C;T=Zj^&L2)ghu0;`5m`Ck=+#6Goh-#6aHirC1^7Y5RjOjX^4
zZra)|r2z)U<wYe@&7gTIY1u+?dgmHNy>X^`-rWE%_rs+vXXfop(61VztXoqSq^UG2
ze5vdB=U9_RA!a72f%-1F5j`!eEyC?v7=F#tx6|70q==P=C5fxm*F~n7O6+1s%c2U(
zBz=CTzZd$o${6A8WYb#s)AMTbotUtPX315#sln0m-6F#rOUwG^55%h8QPcCY#|s3u
zQkljoq1ucJs~Z{iYH^h|F`;!e@uI<NHT<*Wk&<}HwTngS!Fz}57zXc1P$Vi7Wv=e`
zlG9E;b5lARo6k41jVtO=qf-$QEh^qOTUIX0@oZ$Eihw4lp-xjY(4-k!G^YhE(WVux
zX@d@JX-9iH(2-7bMwc$=p-)%3VSpjsF~XQ02<eH4Ui799CYWMIU(B(fAN?7?Kn7vS
zV1_W1VOTMo5sYLMqZz|k#xb4=h_S{7TP9-1Bqn3e6dZ75D$|(G44jzBEM_x@xy-|v
z`ACrBf-7z;U?J{!un12Uvji`e;*Af!_~B0g%UI3|0tq6R5LU8^P{LTv8rHIoa3Y8#
ziuFXZfsJfpGcsb>!dA8sOB~zT!A^Fun|SuHmwhC#p93V4#6b>mm?In|nG{k<;~2*|
zK{_WnMFyFi<_u>!$9XOwCyR?@lfxx0lgkyZlE*c!bAx;eDC8zZ+(JPyx4FYz?s1<I
z9`Fz)k0|9aWjvvrr#$01FL+4>uc+iTZ+Oc)-t&QveBv`-_{ulFQ^gOesiBsi{Ngut
j)bpoN2)%|6G`OMKsKgnmhnK`R(AibuFL7SrvDl5jblwIU
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8d854a1ced7275046cfee44ff9dc240d20f5b6e9
GIT binary patch
literal 937
zc$}@1_g@SE0L8x*B?(zsEq6s1>FOE^byV6ovcfqOk*pAvvXUZ%vMGn`k<KW4gp6db
z1|fS@ltST8@IIgS-tX`8eZEqe7&PMeBaL0Pq4w8mbQWJTd!69z;5a`oUSbualPT!9
zDmSLIX3q18MFk7X<16*-^$Xm~b_!Q`CZ8Dfy+vt=kx`oDPSDrNk`(Qr2vGxZinnd9
zV6tBLB&|eMqb(7iWMQcix$11M3fpvZ-DqJ|Qhw@bQE)(xeNlp}Y}?zHy26@l#|r%;
zBK?Pbr)OzJTJF%vGEDsvZRej6d!>1J)2PAKE+rnyT<Lyi?ew%EPR15ijoqE2G!+fU
zDQ)bm16m$Vat|%kELU$;xH93M!1RsNF(2KNi2}dnv6qYs)GsJ=%(vfOY@!PI@a&Xp
zv~aFN_96qrQ2!s*50r`Bil^x0XNJWu%Zqy<^6D;WU-NH^@pbLuE0xK6dr1BK+<fGo
zt}cjeEUgq`{-4xPM}vCQM-wd?piM(`XoN0}X+l%<Xhw5d(2`c@)0#H4r5)`tzz`#h
zF`)wjgqUJRM>=7S2n#yX1xvbOg&1pWkYI})_BhZDN1W(R4|?K^3sQQ~8&}+Lrw<-@
z;)ORp_~J)j`q3X50~m-ugBZ*ZhBAx*hBJbZ$Qgx#(Trg%fdnzGcC=t7Fp&@@5z1ty
zFqLUcCyW`)WESDfW)2ZV62)AiiD4e|S-?UT5zAuYSVBArEM*yqEN2BPNn#bNSwk`@
ztYsbR*}z6Nv6(GwC6zR`p=3Kd*hxCO*v%gHvXA{7;2?)MOa@2DM8#2#ahwxmk<Cd?
zahfxn<s9d^z(p>R!)2~;m0YfIog3UFk6Yw(n>*a)9`|{`Lmu&%Cp@KqLW(Hn8P6%<
z1uuC;DX)3MTi)@W4}9bkpZUU9$|$FTZ+xecDt=H+4L|u+2Y^8>1GPU?uVX<qIyh7@
QZMJ-@B1|C<3JwY6H(x{vH2?qr
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/head_libmar.js.in
@@ -0,0 +1,140 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const BIN_SUFFIX = "@BIN_SUFFIX@";
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+/**
+ * Compares binary data of 2 arrays and throws if they aren't the same.
+ * Throws on mismatch, does nothing on match.
+ *
+ * @param arr1 The first array to compare
+ * @param arr2 The second array to compare
+*/
+function compareBinaryData(arr1, arr2) {
+  do_check_eq(arr1.length, arr2.length);
+  for (let i = 0; i < arr1.length; i++) {
+    if (arr1[i] != arr2[i]) {
+      throw "Data differs at index " + i;
+    }
+  }
+}
+
+/** 
+ * Reads a file's data and returns it
+ *
+ * @param file The file to read the data from
+ * @return a byte array for the data in the file.
+*/
+function getBinaryFileData(file) {
+  let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
+                   createInstance(Ci.nsIFileInputStream);
+  // Open as RD_ONLY with default permissions.
+  fileStream.init(file, 0x01, -1, null);
+
+  // Check the returned size versus the expected size.
+  let stream = Cc["@mozilla.org/binaryinputstream;1"].
+               createInstance(Ci.nsIBinaryInputStream);
+  stream.setInputStream(fileStream);
+  let bytes = stream.readByteArray(stream.available());
+  fileStream.close();
+  return bytes;
+}
+
+/**
+ * Runs each method in the passed in object
+ * Every method of the passed in object that starts with test_ will be ran
+ * The cleanup_per_test method of the object will be run right away, it will be
+ * registered to be the cleanup function, and it will be run between each test.
+ *
+ * @return The number of tests ran
+*/
+function run_tests(obj) {
+  let cleanup_per_test = obj.cleanup_per_test;
+  if (cleanup_per_test === undefined) {
+    cleanup_per_test = function() {};
+  }
+
+  do_register_cleanup(cleanup_per_test);
+
+  // Make sure there's nothing left over from a preious failed test
+  cleanup_per_test();
+
+  let ranCount = 0;
+  // hasOwnProperty ensures we only see direct properties and not all
+  for (let f in obj) {
+    if (typeof obj[f] === "function" && 
+        obj.hasOwnProperty(f) &&
+        f.toString().indexOf("test_") === 0) {
+      obj[f]();
+      cleanup_per_test();
+      ranCount++;
+    }
+  }
+  return ranCount;
+}
+
+/**
+ * Creates a MAR file with the content of files.
+ *
+ * @param outMAR  The file where the MAR should be created to
+ * @param dataDir The directory where the relative file paths exist
+ * @param files   The relative file paths of the files to include in the MAR
+*/
+function createMAR(outMAR, dataDir, files) {
+  // You cannot create an empy MAR.
+  do_check_true(files.length > 0);
+
+  // Get an nsIProcess to the signmar binary.
+  let process = Cc["@mozilla.org/process/util;1"].
+                createInstance(Ci.nsIProcess);
+  let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
+
+  // Make sure the signmar binary exists and is an executable.
+  do_check_true(signmarBin.exists());
+  do_check_true(signmarBin.isExecutable());
+
+  // Setup the command line arguments to create the MAR.
+  let args = ["-C", dataDir.path, "-c", outMAR.path];
+  args = args.concat(files);
+
+  do_print('Running: ' + signmarBin.path);
+  process.init(signmarBin);
+  process.run(true, args, args.length);
+
+  // Verify signmar returned 0 for success.
+  do_check_eq(process.exitValue, 0);
+
+  // Verify the out MAR file actually exists.
+  do_check_true(outMAR.exists());
+}
+
+/**
+ * Extracts a MAR file to the specified output directory.
+ *
+ * @param mar     The MAR file that should be matched
+ * @param dataDir The directory to extract to
+*/
+function extractMAR(mar, dataDir) {
+  // Get an nsIProcess to the signmar binary.
+  let process = Cc["@mozilla.org/process/util;1"].
+                createInstance(Ci.nsIProcess);
+  let signmarBin = do_get_file("signmar" + BIN_SUFFIX);
+
+  // Make sure the signmar binary exists and is an executable.
+  do_check_true(signmarBin.exists());
+  do_check_true(signmarBin.isExecutable());
+
+  // Setup the command line arguments to create the MAR.
+  let args = ["-C", dataDir.path, "-x", mar.path];
+
+  do_print('Running: ' + signmarBin.path);
+  process.init(signmarBin);
+  process.run(true, args, args.length);
+
+  // Verify signmar returned 0 for success.
+  do_check_eq(process.exitValue, 0);
+}
+
+
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/test_create.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+
+  /**
+   * Creates MAR from the passed files, compares it to the reference MAR.
+   *
+   * @param refMARFileName The name of the MAR file that should match
+   * @param files          The files that should go in the created MAR
+   * @param checkNoMAR     If true return an error if a file already exists
+  */
+  function run_one_test(refMARFileName, files, checkNoMAR) {
+    if (checkNoMAR === undefined) {
+      checkNoMAR = true;
+    }
+
+    // Ensure the MAR we will create doesn't already exist.
+    let outMAR = do_get_file("out.mar", true);
+    if (checkNoMAR) {
+      do_check_false(outMAR.exists());
+    }
+
+    // Create the actual MAR file.
+    createMAR(outMAR, do_get_file("data"), files);
+
+    // Get the reference MAR data.
+    let refMAR = do_get_file("data/" + refMARFileName);
+    let refMARData = getBinaryFileData(refMAR);
+
+    // Verify the data of the MAR is what it should be.
+    let outMARData = getBinaryFileData(outMAR);
+    compareBinaryData(outMARData, outMARData);
+  }
+
+  // Define the unit tests to run.
+  let tests = {
+    // Test creating a MAR file with a 0 byte file.
+    test_zero_sized: function() {
+      return run_one_test("0_sized_mar.mar", ["0_sized_file"]);
+    },
+    // Test creating a MAR file with a 1 byte file.
+    test_one_byte: function() {
+      return run_one_test("1_byte_mar.mar", ["1_byte_file"]);
+    },
+    // Test creating a MAR file with binary data.
+    test_binary_data: function() {
+      return run_one_test("binary_data_mar.mar", ["binary_data_file"]);
+    },
+    // Test creating a MAR file with multiple files inside of it.
+    test_multiple_file: function() {
+      return run_one_test("multiple_file_mar.mar", 
+                          ["0_sized_file", "1_byte_file", "binary_data_file"]);
+    },
+    // Test creating a MAR file on top of a different one that already exists
+    // at the location the new one will be created at.
+    test_overwrite_already_exists: function() {
+      let differentFile = do_get_file("data/1_byte_mar.mar");
+      let outMARDir = do_get_file(".");
+      differentFile.copyTo(outMARDir, "out.mar");
+      return run_one_test("binary_data_mar.mar", ["binary_data_file"], false);
+    },
+    // Between each test make sure the out MAR does not exist.
+    cleanup_per_test: function() {
+      let outMAR = 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);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/test_extract.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+
+  /**
+   * Extracts a MAR and makes sure each file matches the reference files.
+   *
+   * @param marFileName The name of the MAR file to extract
+   * @param files       The files that the extracted MAR should contain
+  */
+  function run_one_test(marFileName, files) { 
+    // Get the MAR file that we will be extracting
+    let mar = do_get_file("data/" + marFileName);
+
+    // Get the path that we will extract to
+    let outDir = do_get_file("out", true);
+    do_check_false(outDir.exists());
+    outDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0777);
+
+    // Get the ref files and the files that will be extracted.
+    let outFiles = [];
+    let refFiles = [];
+    for (let i = 0; i < files.length; i++) {
+      let outFile = do_get_file("out/" + files[i], true);
+      do_check_false(outFile.exists());
+
+      outFiles.push(outFile);
+      refFiles.push(do_get_file("data/" + files[i]));
+    }
+
+    // Extract the MAR contents into the ./out dir.
+    extractMAR(mar, outDir);
+
+    // Compare to make sure the extracted files are the same.
+    for (let i = 0; i < files.length; i++) {
+      do_check_true(outFiles[i].exists());
+      let refFileData = getBinaryFileData(refFiles[i]);
+      let outFileData = getBinaryFileData(outFiles[i]);
+      compareBinaryData(refFileData, outFileData);
+    }
+  }
+
+  // Define the unit tests to run.
+  let tests = {
+    // Test extracting a MAR file with a 0 byte file.
+    test_zero_sized: function() {
+      return run_one_test("0_sized_mar.mar", ["0_sized_file"]);
+    }, 
+    // Test extracting a MAR file with a 1 byte file.
+    test_one_byte: function() {
+      return run_one_test("1_byte_mar.mar", ["1_byte_file"]);
+    },
+    // Test extracting a MAR file with binary data.
+    test_binary_data: function() {
+      return run_one_test("binary_data_mar.mar", ["binary_data_file"]);
+    },
+    // Test extracting a MAR without a product information block (PIB) which
+    // contains binary data.
+    test_no_pib: function() {
+      return run_one_test("no_pib_mar.mar", ["binary_data_file"]);
+    },
+    // Test extracting a MAR without a product information block (PIB) that is
+    // signed and which contains binary data.
+    test_no_pib_signed: function() {
+      return run_one_test("signed_no_pib_mar.mar", ["binary_data_file"]);
+    },
+    // Test extracting a MAR with a product information block (PIB) that is
+    // signed and which contains binary data.
+    test_pib_signed: function() {
+      return run_one_test("signed_pib_mar.mar", ["binary_data_file"]);
+    },
+    // Test extracting a MAR file with multiple files inside of it.
+    test_multiple_file: function() {
+      return run_one_test("multiple_file_mar.mar", 
+                          ["0_sized_file", "1_byte_file", "binary_data_file"]);
+    }, 
+    // Between each test make sure the out directory and its subfiles do 
+    // not exist.
+    cleanup_per_test: function() {
+      let outDir = 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);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/test_sign_verify.js
@@ -0,0 +1,187 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+
+  /**
+   * Signs a MAR file.
+   *
+   * @param inMAR The MAR file that should be signed
+   * @param outMAR The MAR file to create
+  */
+  function signMAR(inMAR, outMAR) {
+    // 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];
+
+    do_print('Running sign operation: ' + signmarBin.path);
+    process.init(signmarBin);
+    process.run(true, args, args.length);
+
+    // Verify signmar returned 0 for success.
+    do_check_eq(process.exitValue, 0);
+  }
+
+  /**
+   * Verifies a MAR file.
+   *
+   * @param signedMAR Verifies a MAR file
+  */
+  function verifyMAR(signedMAR, wantSuccess) {
+    if (wantSuccess === undefined) {
+      wantSuccess = true;
+    }
+    // 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");
+
+    // Will reference the arguments to use for verification in signmar
+    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];
+    } else {
+      let NSSConfigDir = do_get_file("data");
+      args = ["-d", NSSConfigDir.path, "-n", "mycert", "-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) {
+      process.exitValue = -1;
+    }
+
+    // Verify signmar returned 0 for success.
+    if (wantSuccess) {
+      do_check_eq(process.exitValue, 0);
+    } else {
+      do_check_neq(process.exitValue, 0);
+    }
+  }
+
+  /**
+   * Strips a MAR signature.
+   *
+   * @param signedMAR The MAR file that should be signed
+   * @param outMAR The MAR file to write to with signature stripped
+  */
+  function stripMARSignature(signedMAR, outMAR) {
+    // 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);
+
+    // Verify signmar returned 0 for success.
+    do_check_eq(process.exitValue, 0);
+  }
+
+
+  function cleanup() {
+    let outMAR = do_get_file("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);
+    }
+  }
+
+  // Define the unit tests to run.
+  let tests = {
+    // Test signing a MAR file
+    test_sign: function() {
+      let inMAR = do_get_file("data/binary_data_mar.mar");
+      let outMAR = do_get_file("signed_out.mar", true);
+      do_check_false(outMAR.exists());
+      signMAR(inMAR, outMAR);
+      do_check_true(outMAR.exists());
+      let outMARData = getBinaryFileData(outMAR);
+      let refMAR = do_get_file("data/signed_pib_mar.mar");
+      let refMARData = getBinaryFileData(refMAR);
+      compareBinaryData(outMARData, refMARData);
+    }, 
+    // Test verifying a signed MAR file
+    test_verify: function() {
+      let signedMAR = do_get_file("data/signed_pib_mar.mar");
+      verifyMAR(signedMAR);
+    }, 
+    // 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);
+    }, 
+    // 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);
+    }, 
+    // Test to make sure a stripped MAR is the same as the original MAR
+    test_strip_signature: function() {
+      let originalMAR = do_get_file("data/binary_data_mar.mar");
+      let signedMAR = do_get_file("signed_out.mar");
+      let outMAR = do_get_file("out.mar", true);
+      stripMARSignature(signedMAR, outMAR);
+
+      // Verify that the stripped MAR matches the original data MAR exactly
+      let outMARData = getBinaryFileData(outMAR);
+      let originalMARData = getBinaryFileData(originalMAR);
+      compareBinaryData(outMARData, originalMARData);
+    },
+  };
+
+  cleanup();
+
+  // Run all the tests, there should be 5.
+  do_check_eq(run_tests(tests), 5);
+
+  do_register_cleanup(cleanup);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libmar/tests/unit/xpcshell.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+head = head_libmar.js
+tail = 
+
+[test_create.js]
+[test_extract.js]
+[test_sign_verify.js]
--- a/modules/libmar/tool/Makefile.in
+++ b/modules/libmar/tool/Makefile.in
@@ -46,28 +46,69 @@ include $(DEPTH)/config/autoconf.mk
 MODULE		= mar
 ifeq ($(OS_ARCH),WINNT)
 USE_STATIC_LIBS = 1
 endif
 
 # The mar executable is output into dist/host/bin since it is something that
 # would only be used by our build system and should not itself be included in a
 # Mozilla distribution.
-HOST_PROGRAM	= mar$(HOST_BIN_SUFFIX)
+HOST_PROGRAM = mar$(HOST_BIN_SUFFIX)
+
+ifdef MOZ_ENABLE_SIGNMAR
+PROGRAM = signmar$(BIN_SUFFIX)
+endif
+
+# Don't link the against libmozglue because we don't need it.
+MOZ_GLUE_LDFLAGS =
+MOZ_GLUE_PROGRAM_LDFLAGS =
 
+DEFINES += \
+  -DMAR_CHANNEL_ID='"$(MAR_CHANNEL_ID)"' \
+  -DMOZ_APP_VERSION='"$(MOZ_APP_VERSION)"' \
+  $(NULL)
+
+ifndef MOZ_ENABLE_SIGNMAR
+DEFINES += \
+  -DNO_SIGN_VERIFY \
+  $(NULL)
+endif
 
-HOST_CSRCS	= \
-		mar.c \
-		$(NULL)
+HOST_CFLAGS += \
+  -DNO_SIGN_VERIFY \
+  $(DEFINES) \
+  $(NULL)
+
+HOST_CSRCS = \
+  mar.c \
+  $(NULL)
+CSRCS = $(HOST_CSRCS)
 
-HOST_LIBS	= $(DIST)/host/lib/$(LIB_PREFIX)hostmar.$(LIB_SUFFIX)
+HOST_LIBS = $(DIST)/host/lib/$(LIB_PREFIX)hostmar.$(LIB_SUFFIX)
+LIBS = $(DEPTH)/modules/libmar/src/$(LIB_PREFIX)mar.$(LIB_SUFFIX)
+
+ifdef MOZ_ENABLE_SIGNMAR
+LIBS += \
+  $(DEPTH)/modules/libmar/sign/$(LIB_PREFIX)signmar.$(LIB_SUFFIX) \
+  $(DEPTH)/modules/libmar/verify/$(LIB_PREFIX)verifymar.$(LIB_SUFFIX) \
+  $(DIST)/lib/$(LIB_PREFIX)nss3.$(LIB_SUFFIX) \
+  $(DIST)/lib/$(LIB_PREFIX)nssutil3.$(LIB_SUFFIX) \
+  $(NSPR_LIBS) \
+  $(NULL)
+endif
 
 ifeq ($(HOST_OS_ARCH),WINNT)
 HOST_EXTRA_LIBS += $(call EXPAND_LIBNAME,ws2_32)
+EXTRA_LIBS += $(call EXPAND_LIBNAME,ws2_32)
+ifdef MOZ_ENABLE_SIGNMAR
+EXTRA_LIBS += $(call EXPAND_LIBNAME,crypt32)
+EXTRA_LIBS += $(call EXPAND_LIBNAME,advapi32)
+endif
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 ifdef CROSS_COMPILE
 ifdef HOST_NSPR_MDCPUCFG
-HOST_CFLAGS     += -DMDCPUCFG=$(HOST_NSPR_MDCPUCFG)
+HOST_CFLAGS += -DMDCPUCFG=$(HOST_NSPR_MDCPUCFG)
+CFLAGS += -DMDCPUCFG=$(HOST_NSPR_MDCPUCFG)
 endif
 endif
--- a/modules/libmar/tool/mar.c
+++ b/modules/libmar/tool/mar.c
@@ -32,30 +32,60 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <stdio.h>
+#include <stdlib.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 *src, 
+                           const char * dest);
+
 static void print_usage() {
-    printf("usage: mar [-C dir] {-c|-x|-t} archive.mar [files...]\n");
+  printf("usage:\n");
+  printf("  mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
+         "{-c|-x|-t|-T} archive.mar [files...]\n");
+#ifndef NO_SIGN_VERIFY
+  printf("  mar [-C workingDir] -d NSSConfigDir -n certname -s "
+         "archive.mar out_signed_archive.mar\n");
+  printf("  mar [-C workingDir] -r "
+         "signed_input_archive.mar output_archive.mar\n");
+#if defined(XP_WIN) && !defined(MAR_NSS)
+  printf("  mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n");
+#else 
+  printf("  mar [-C workingDir] -d NSSConfigDir -n certname "
+    "-v signed_archive.mar\n");
+#endif
+#endif
+  printf("  mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
+         "-i unsigned_archive_to_refresh.mar\n");
 }
 
-static int mar_test_callback(MarFile *mar, const MarItem *item, void *unused) {
+static int mar_test_callback(MarFile *mar, 
+                             const MarItem *item, 
+                             void *unused) {
   printf("%u\t0%o\t%s\n", item->length, item->flags, item->name);
   return 0;
 }
 
 static int mar_test(const char *path) {
   MarFile *mar;
 
   mar = mar_open(path);
@@ -65,35 +95,211 @@ static int mar_test(const char *path) {
   printf("SIZE\tMODE\tNAME\n");
   mar_enum_items(mar, mar_test_callback, NULL);
 
   mar_close(mar);
   return 0;
 }
 
 int main(int argc, char **argv) {
-  int command;
+  char *NSSConfigDir = NULL;
+  char *certName = NULL;
+  char *MARChannelID = MAR_CHANNEL_ID;
+  char *productVersion = MOZ_APP_VERSION;
+#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
+  HANDLE certFile;
+  DWORD fileSize;
+  DWORD read;
+  char *certBuffer;
+  char *DERFilePath = NULL;
+#endif
 
   if (argc < 3) {
     print_usage();
     return -1;
   }
 
-  if (argv[1][1] == 'C') {
-    chdir(argv[2]);
-    argv += 2;
-    argc -= 2;
+  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')) {
+      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];
+      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;
+    /* MAR channel ID */
+    } else if (argv[1][0] == '-' && argv[1][1] == 'H') {
+      MARChannelID = argv[2];
+      argv += 2;
+      argc -= 2;
+    /* Product Version */
+    } else if (argv[1][0] == '-' && argv[1][1] == 'V') {
+      productVersion = argv[2];
+      argv += 2;
+      argc -= 2;
+    }
+    else {
+      print_usage();
+      return -1;
+    }
+  }
+
+  if (argv[1][0] != '-') {
+    print_usage();
+    return -1;
   }
 
   switch (argv[1][1]) {
-  case 'c':
-    return mar_create(argv[2], argc - 3, argv + 3);
+  case 'c': {
+    struct ProductInformationBlock infoBlock;
+    infoBlock.MARChannelID = MARChannelID;
+    infoBlock.productVersion = productVersion;
+    return mar_create(argv[2], argc - 3, argv + 3, &infoBlock);
+  }
+  case 'i': {
+    struct ProductInformationBlock infoBlock;
+    infoBlock.MARChannelID = MARChannelID;
+    infoBlock.productVersion = productVersion;
+    return refresh_product_info_block(argv[2], &infoBlock);
+  }
+  case 'T': {
+    int rv;
+    struct ProductInformationBlock infoBlock;
+    int hasSignatureBlock, numSignatures, 
+      hasAdditionalBlock, numAdditionalBlocks;
+    if (!get_mar_file_info(argv[2], 
+                           &hasSignatureBlock,
+                           &numSignatures,
+                           &hasAdditionalBlock, 
+                           NULL, &numAdditionalBlocks)) {
+      if (hasSignatureBlock) {
+        printf("Signature block found with %d signature%s\n", 
+               numSignatures, 
+               numSignatures != 1 ? "s" : "");
+      }
+      if (hasAdditionalBlock) {
+        printf("%d additional block%s found:\n", 
+               numAdditionalBlocks,
+               numAdditionalBlocks != 1 ? "s" : "");
+      }
+
+      rv = read_product_info_block(argv[2], &infoBlock);
+      if (!rv) {
+        printf("  - Product Information Block:\n");
+        printf("    - MAR channel name: %s\n"
+               "    - Product version: %s\n",
+               infoBlock.MARChannelID,
+               infoBlock.productVersion);
+        free((void *)infoBlock.MARChannelID);
+        free((void *)infoBlock.productVersion);
+      }
+     }
+    printf("\n");
+    // The fall through from 'T' to 't' is intentional
+  }
   case 't':
     return mar_test(argv[2]);
+
   case 'x':
     return mar_extract(argv[2]);
+
+#ifndef NO_SIGN_VERIFY
+  case 'v':
+
+#if defined(XP_WIN) && !defined(MAR_NSS)
+    if (!DERFilePath) {
+      print_usage();
+      return -1;
+    }
+    /* If the mar program was built using CryptoAPI, then read in the buffer
+       containing the cert from disk. */
+    certFile = CreateFileA(DERFilePath, GENERIC_READ, 
+                           FILE_SHARE_READ | 
+                           FILE_SHARE_WRITE | 
+                           FILE_SHARE_DELETE, 
+                           NULL, 
+                           OPEN_EXISTING, 
+                           0, NULL);
+    if (INVALID_HANDLE_VALUE == certFile) {
+      return -1;
+    }
+    fileSize = GetFileSize(certFile, NULL);
+    certBuffer = malloc(fileSize);
+    if (!ReadFile(certFile, certBuffer, fileSize, &read, NULL) || 
+        fileSize != read) {
+      CloseHandle(certFile);
+      free(certBuffer);
+      return -1;
+    }
+    CloseHandle(certFile);
+
+    if (mar_verify_signature(argv[2], certBuffer, fileSize, NULL)) {
+      /* 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) {
+      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);
+
+#endif /* defined(XP_WIN) && !defined(MAR_NSS) */
+  case 's':
+    if (!NSSConfigDir || !certName || argc < 4) {
+      print_usage();
+      return -1;
+    }
+    return mar_repackage_and_sign(NSSConfigDir, certName, 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;
   }
 
   return 0;
 }
new file mode 100644
--- /dev/null
+++ b/modules/libmar/verify/Makefile.in
@@ -0,0 +1,69 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mar verify build config.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Brian R. Bondy <netzen@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = verifymar
+LIBRARY_NAME = verifymar
+FORCE_STATIC_LIB = 1
+ifeq ($(OS_ARCH),WINNT)
+USE_STATIC_LIBS = 1
+endif
+
+# This makefile just builds support for reading archives.
+CSRCS	= \
+  mar_verify.c \
+  cryptox.c \
+  $(NULL)
+
+LOCAL_INCLUDES += -I$(srcdir)/../src
+
+ifneq ($(OS_ARCH),WINNT)
+DEFINES += -DMAR_NSS
+LOCAL_INCLUDES += -I$(srcdir)/../sign
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+# The intermediate (.ii/.s) files for host and target can have the same name...
+# disable parallel builds
+.NOTPARALLEL:
new file mode 100644
--- /dev/null
+++ b/modules/libmar/verify/cryptox.c
@@ -0,0 +1,304 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is cryptographic wrappers for Mozilla archive code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifdef XP_WIN
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#endif
+
+#include <stdlib.h>
+#include "cryptox.h"
+
+#if defined(MAR_NSS)
+
+/** 
+ * Loads the public key for the specified cert name from the NSS store.
+ * 
+ * @param certName The cert name to find.
+ * @param publicKey Out parameter for the public key to use.
+ * @param cert      Out parameter for the certificate to use.
+ * @return CryptoX_Success on success, CryptoX_Error on error.
+*/
+CryptoX_Result
+NSS_LoadPublicKey(const char *certNickname, 
+                  SECKEYPublicKey **publicKey, 
+                  CERTCertificate **cert)
+{
+  secuPWData pwdata = { PW_NONE, 0 };
+  if (!cert || !publicKey || !cert) {
+    return CryptoX_Error;
+  }
+
+  /* Get the cert and embedded public key out of the database */
+  *cert = PK11_FindCertFromNickname(certNickname, &pwdata);
+  if (!*cert) {
+    return CryptoX_Error;
+  }
+  *publicKey = CERT_ExtractPublicKey(*cert);
+  if (!*publicKey) {
+    CERT_DestroyCertificate(*cert);
+    return CryptoX_Error;
+  }
+  return CryptoX_Success;
+}
+
+CryptoX_Result
+NSS_VerifyBegin(VFYContext **ctx, 
+                SECKEYPublicKey * const *publicKey)
+{
+  SECStatus status;
+  if (!ctx || !publicKey || !*publicKey) {
+    return CryptoX_Error;
+  }
+
+  /* Check that the key length is large enough for our requirements */
+  if ((SECKEY_PublicKeyStrength(*publicKey) * 8) < 
+      XP_MIN_SIGNATURE_LEN_IN_BYTES) {
+    fprintf(stderr, "ERROR: Key length must be >= %d bytes\n", 
+            XP_MIN_SIGNATURE_LEN_IN_BYTES);
+    return CryptoX_Error;
+  }
+
+  *ctx = VFY_CreateContext(*publicKey, NULL, 
+                           SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE, NULL);
+  if (*ctx == NULL) {
+    return CryptoX_Error;
+  }
+
+  status = VFY_Begin(*ctx);
+  return SECSuccess == status ? CryptoX_Success : CryptoX_Error;
+}
+
+/**
+ * Verifies if a verify context matches the passed in signature.
+ *
+ * @param ctx          The verify context that the signature should match.
+ * @param signature    The signature to match.
+ * @param signatureLen The length of the signature.
+ * @return CryptoX_Success on success, CryptoX_Error on error.
+*/
+CryptoX_Result
+NSS_VerifySignature(VFYContext * const *ctx, 
+                    const unsigned char *signature, 
+                    unsigned int signatureLen)
+{
+  SECItem signedItem;
+  SECStatus status;
+  if (!ctx || !signature || !*ctx) {
+    return CryptoX_Error;
+  }
+
+  signedItem.len = signatureLen;
+  signedItem.data = (unsigned char*)signature;
+  status = VFY_EndWithSignature(*ctx, &signedItem);
+  return SECSuccess == status ? CryptoX_Success : CryptoX_Error;
+}
+
+#elif defined(XP_WIN)
+/**
+ * Verifies if a signature + public key matches a hash context.
+ *
+ * @param hash      The hash context that the signature should match.
+ * @param pubKey    The public key to use on the signature.
+ * @param signature The signature to check.
+ * @param signatureLen The length of the signature.
+ * @return CryptoX_Success on success, CryptoX_Error on error.
+*/
+CryptoX_Result
+CyprtoAPI_VerifySignature(HCRYPTHASH *hash, 
+                          HCRYPTKEY *pubKey,
+                          const BYTE *signature, 
+                          DWORD signatureLen)
+{
+  DWORD i;
+  BOOL result;
+/* Windows APIs expect the bytes in the signature to be in little-endian 
+ * order, but we write the signature in big-endian order.  Other APIs like 
+ * NSS and OpenSSL expect big-endian order.
+ */
+  BYTE *signatureReversed;
+  if (!hash || !pubKey || !signature || signatureLen < 1) {
+    return CryptoX_Error;
+  }
+
+  signatureReversed = malloc(signatureLen);
+  if (!signatureReversed) {
+    return CryptoX_Error;
+  }
+
+  for (i = 0; i < signatureLen; i++) {
+    signatureReversed[i] = signature[signatureLen - 1 - i]; 
+  }
+  result = CryptVerifySignature(*hash, signatureReversed,
+                                signatureLen, *pubKey, NULL, 0);
+  free(signatureReversed);
+  return result ? CryptoX_Success : CryptoX_Error;
+}
+
+/** 
+ * Obtains the public key for the passed in cert data
+ * 
+ * @param provider       The cyrto provider
+ * @param certData       Data of the certificate to extract the public key from
+ * @param sizeOfCertData The size of the certData buffer
+ * @param certStore      Pointer to the handle of the certificate store to use
+ * @param CryptoX_Success on success
+*/
+CryptoX_Result
+CryptoAPI_LoadPublicKey(HCRYPTPROV provider, 
+                        BYTE *certData,
+                        DWORD sizeOfCertData,
+                        HCRYPTKEY *publicKey,
+                        HCERTSTORE *certStore)
+{
+  CRYPT_DATA_BLOB blob;
+  CERT_CONTEXT *context;
+  if (!provider || !certData || !publicKey || !certStore) {
+    return CryptoX_Error;
+  }
+
+  blob.cbData = sizeOfCertData;
+  blob.pbData = certData;
+  if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, 
+                        CERT_QUERY_CONTENT_FLAG_CERT, 
+                        CERT_QUERY_FORMAT_FLAG_BINARY, 
+                        0, NULL, NULL, NULL, 
+                        certStore, NULL, (const void **)&context)) {
+    return CryptoX_Error;
+  }
+
+  if (!CryptImportPublicKeyInfo(provider, 
+                                PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+                                &context->pCertInfo->SubjectPublicKeyInfo,
+                                publicKey)) {
+    CertFreeCertificateContext(context);
+    return CryptoX_Error;
+  }
+
+  CertFreeCertificateContext(context);
+  return CryptoX_Success;
+}
+
+/* Try to acquire context in this way:
+  * 1. Enhanced provider without creating a new key set
+  * 2. Enhanced provider with creating a new key set
+  * 3. Default provider without creating a new key set
+  * 4. Default provider without creating a new key set
+  * #2 and #4 should not be needed because of the CRYPT_VERIFYCONTEXT, 
+  * but we add it just in case.
+  *
+  * @param provider Out parameter containing the provider handle.
+  * @return CryptoX_Success on success, CryptoX_Error on error.
+ */
+CryptoX_Result
+CryptoAPI_InitCryptoContext(HCRYPTPROV *provider)
+{
+  if (!CryptAcquireContext(provider, 
+                           NULL, 
+                           MS_ENHANCED_PROV, 
+                           PROV_RSA_FULL, 
+                           CRYPT_VERIFYCONTEXT)) {
+    if (!CryptAcquireContext(provider, 
+                             NULL, 
+                             MS_ENHANCED_PROV, 
+                             PROV_RSA_FULL, 
+                             CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) {
+      if (!CryptAcquireContext(provider, 
+                               NULL, 
+                               NULL, 
+                               PROV_RSA_FULL, 
+                               CRYPT_VERIFYCONTEXT)) {
+        if (!CryptAcquireContext(provider, 
+                                 NULL, 
+                                 NULL, 
+                                 PROV_RSA_FULL, 
+                                 CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) {
+          *provider = CryptoX_InvalidHandleValue;
+          return CryptoX_Error;
+        }
+      }
+    }
+  }
+  return CryptoX_Success;
+}
+
+/** 
+  * Begins a signature verification hash context
+  *
+  * @param provider The crypt provider to use
+  * @param hash     Out parameter for a handle to the hash context
+  * @return CryptoX_Success on success, CryptoX_Error on error.
+*/
+CryptoX_Result
+CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash)
+{
+  BOOL result;
+  if (!provider || !hash) {
+    return CryptoX_Error;
+  }
+
+  *hash = (HCRYPTHASH)NULL;
+  result = CryptCreateHash(provider, CALG_SHA1,
+                           0, 0, hash);
+  return result ? CryptoX_Success : CryptoX_Error;
+}
+
+/** 
+  * Updates a signature verification hash context
+  *
+  * @param hash The hash context to udpate
+  * @param buf  The buffer to update the hash context with
+  * @param len The size of the passed in buffer
+  * @return CryptoX_Success on success, CryptoX_Error on error.
+*/
+CryptoX_Result
+CryptoAPI_VerifyUpdate(HCRYPTHASH* hash, BYTE *buf, DWORD len)
+{
+  BOOL result;
+  if (!hash || !buf) {
+    return CryptoX_Error;
+  }
+
+  result = CryptHashData(*hash, buf, len, 0);
+  return result ? CryptoX_Success : CryptoX_Error;
+}
+
+#endif
+
+
+
new file mode 100644
--- /dev/null
+++ b/modules/libmar/verify/cryptox.h
@@ -0,0 +1,150 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is cryptographic wrappers for Mozilla archive code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef CRYPTOX_H
+#define CRYPTOX_H
+
+#define XP_MIN_SIGNATURE_LEN_IN_BYTES 256
+
+#define CryptoX_Result int
+#define CryptoX_Success 0
+#define CryptoX_Error (-1)
+#define CryptoX_Succeeded(X) ((X) == CryptoX_Success)
+#define CryptoX_Failed(X) ((X) != CryptoX_Success)
+
+#if defined(MAR_NSS)
+
+#include "nss_secutil.h"
+
+CryptoX_Result NSS_LoadPublicKey(const char *certNickname, 
+                                 SECKEYPublicKey **publicKey, 
+                                 CERTCertificate **cert);
+CryptoX_Result NSS_VerifyBegin(VFYContext **ctx, 
+                               SECKEYPublicKey * const *publicKey);
+CryptoX_Result NSS_VerifySignature(VFYContext * const *ctx , 
+                                   const unsigned char *signature, 
+                                   unsigned int signatureLen);
+
+#define CryptoX_InvalidHandleValue NULL
+#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_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) \
+  SECKEY_DestroyPublicKey(*key)
+#define CryptoX_FreeCertificate(cert) \
+  CERT_DestroyCertificate(*cert)
+
+#elif defined(XP_WIN) 
+
+#include <windows.h>
+#include <wincrypt.h>
+
+CryptoX_Result CryptoAPI_InitCryptoContext(HCRYPTPROV *provider);
+CryptoX_Result CryptoAPI_LoadPublicKey(HCRYPTPROV hProv, 
+                                       BYTE *certData,
+                                       DWORD sizeOfCertData,
+                                       HCRYPTKEY *publicKey,
+                                       HCERTSTORE *cert);
+CryptoX_Result CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash);
+CryptoX_Result CryptoAPI_VerifyUpdate(HCRYPTHASH* hash, 
+                                      BYTE *buf, DWORD len);
+CryptoX_Result CyprtoAPI_VerifySignature(HCRYPTHASH *hash, 
+                                         HCRYPTKEY *pubKey,
+                                         const BYTE *signature, 
+                                         DWORD signatureLen);
+
+#define CryptoX_InvalidHandleValue ((ULONG_PTR)NULL)
+#define CryptoX_ProviderHandle HCRYPTPROV
+#define CryptoX_SignatureHandle HCRYPTHASH
+#define CryptoX_PublicKey HCRYPTKEY
+#define CryptoX_Certificate HCERTSTORE
+#define CryptoX_InitCryptoProvider(CryptoHandle) \
+  CryptoAPI_InitCryptoContext(CryptoHandle)
+#define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
+  CryptoAPI_VerifyBegin(CryptoHandle, SignatureHandle)
+#define CryptoX_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)
+#define CryptoX_FreePublicKey(key) \
+  CryptDestroyKey(*(key))
+#define CryptoX_FreeCertificate(cert) \
+  CertCloseStore(*(cert), CERT_CLOSE_STORE_FORCE_FLAG);
+
+#else
+
+/* This default implementation is necessary because we don't want to
+ * link to NSS from updater code on non Windows platforms.  On Windows
+ * we use CyrptoAPI instead of NSS.  We don't call any function as they
+ * would just fail, but this simplifies linking.
+ */
+
+#define CryptoX_InvalidHandleValue NULL
+#define CryptoX_ProviderHandle void*
+#define CryptoX_SignatureHandle void*
+#define CryptoX_PublicKey void*
+#define CryptoX_Certificate void*
+#define CryptoX_InitCryptoProvider(CryptoHandle) \
+  CryptoX_Error
+#define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
+  CryptoX_Error
+#define CryptoX_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
+
+#endif
new file mode 100644
--- /dev/null
+++ b/modules/libmar/verify/mar_verify.c
@@ -0,0 +1,444 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Archive verify code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifdef XP_WIN
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mar_private.h"
+#include "mar.h"
+#include "cryptox.h"
+
+int mar_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, 
+                                PRUint32 signatureCount,
+                                char *extractedSignature);
+
+/**
+ * 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  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,
+                           PRUint32 size, 
+                           CryptoX_SignatureHandle *ctx,
+                           const char *err) 
+{
+  if (!fp || !buffer || !ctx || !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;
+  }
+  return CryptoX_Success;
+}
+
+/**
+ * Verifies the embedded signature of the specified file path.
+ * 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
+ * @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,
+                     PRUint32 sizeOfCertData,
+                     const char *certName) {
+  int rv;
+  CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue;
+  CryptoX_Certificate cert;
+  CryptoX_PublicKey key;
+  FILE *fp;
+  
+  if (!pathToMARFile || (!certData && !certName)) {
+    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;
+  }
+
+  rv = mar_verify_signature_fp(fp, provider, key);
+  fclose(fp);
+  if (key) {
+    CryptoX_FreePublicKey(&key);
+  }
+
+  if (cert) {
+    CryptoX_FreeCertificate(&cert);
+  }
+  return rv;
+}
+
+#ifdef XP_WIN
+/**
+ * Verifies a MAR file's signature by making sure at least one 
+ * signature verifies.
+ * 
+ * @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
+ * @return 0 on success
+*/
+int
+mar_verify_signatureW(MarFile *mar, 
+                      const char *certData,
+                      PRUint32 sizeOfCertData) {
+  int rv;
+  CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue;
+  CryptoX_Certificate cert;
+  CryptoX_PublicKey key;
+  
+  if (!mar || !certData) {
+    fprintf(stderr, "ERROR: Invalid parameter specified.\n");
+    return CryptoX_Error;
+  }
+
+  if (!mar->fp) {
+    fprintf(stderr, "ERROR: MAR file is not open.\n");
+    return CryptoX_Error;
+  }
+
+  if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) { 
+    fprintf(stderr, "ERROR: Could not init crytpo library.\n");
+    return CryptoX_Error;
+  }
+
+  if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData, sizeOfCertData,
+                                           &key, "", &cert))) {
+    fprintf(stderr, "ERROR: Could not load public key.\n");
+    return CryptoX_Error;
+  }
+
+  rv = mar_verify_signature_fp(mar->fp, provider, key);
+  if (key) {
+    CryptoX_FreePublicKey(&key);
+  }
+
+  if (cert) {
+    CryptoX_FreeCertificate(&cert);
+  }
+  return rv;
+}
+#endif
+
+/**
+ * Verifies a MAR file's signature by making sure at least one 
+ * signature verifies.
+ * 
+ * @param  fp       An opened MAR file handle
+ * @param  provider A library provider
+ * @param  key      The public key to use to verify the MAR
+ * @return 0 on success
+*/
+int
+mar_verify_signature_fp(FILE *fp,
+                        CryptoX_ProviderHandle provider, 
+                        CryptoX_PublicKey key) {
+  char buf[5] = {0};
+  PRUint32 signatureAlgorithmID, signatureCount, signatureLen, numVerified = 0;
+  int rv = -1;
+  PRInt64 curPos;
+  char *extractedSignature;
+  PRUint32 i;
+
+  if (!fp) {
+    fprintf(stderr, "ERROR: Invalid file pointer passed.\n");
+    return CryptoX_Error;
+  }
+  
+  /* To protect against invalid MAR files, we assumes that the MAR file 
+     size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
+  if (fseeko(fp, 0, SEEK_END)) {
+    fprintf(stderr, "ERROR: Could not seek to the end of the MAR file.\n");
+    return CryptoX_Error;
+  }
+  if (ftello(fp) > MAX_SIZE_OF_MAR_FILE) {
+    fprintf(stderr, "ERROR: MAR file is too large to be verified.\n");
+    return CryptoX_Error;
+  }
+
+  /* Skip to the start of the signature block */
+  if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
+    fprintf(stderr, "ERROR: Could not seek to the signature block.\n");
+    return CryptoX_Error;
+  }
+
+  /* Get the number of signatures */
+  if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
+    fprintf(stderr, "ERROR: Could not read number of signatures.\n");
+    return CryptoX_Error;
+  }
+  signatureCount = ntohl(signatureCount);
+
+  /* Check that we have less than the max amount of signatures so we don't
+     waste too much of either updater's or signmar's time. */
+  if (signatureCount > MAX_SIGNATURES) {
+    fprintf(stderr, "ERROR: At most %d signatures can be specified.\n",
+            MAX_SIGNATURES);
+    return CryptoX_Error;
+  }
+
+  for (i = 0; i < signatureCount && numVerified == 0; i++) {
+    /* Get the signature algorithm ID */
+    if (fread(&signatureAlgorithmID, sizeof(PRUint32), 1, fp) != 1) {
+      fprintf(stderr, "ERROR: Could not read signatures algorithm ID.\n");
+      return CryptoX_Error;
+    }
+    signatureAlgorithmID = ntohl(signatureAlgorithmID);
+  
+    if (fread(&signatureLen, sizeof(PRUint32), 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) {
+      fprintf(stderr, "ERROR: Could allocate buffer for signature.\n");
+      return CryptoX_Error;
+    }
+    if (fread(extractedSignature, signatureLen, 1, fp) != 1) {
+      fprintf(stderr, "ERROR: Could not read extracted signature.\n");
+      free(extractedSignature);
+      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++;
+      }
+      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);
+    }
+  }
+
+  /* If we reached here and we verified at least one 
+     signature, return success. */
+  if (numVerified > 0) {
+    return CryptoX_Success;
+  } else {
+    fprintf(stderr, "ERROR: No signatures were verified.\n");
+    return CryptoX_Error;
+  }
+}
+
+/**
+ * Verifies if a specific signature ID matches the extracted 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  signatureCount        The number of signatures in the MAR file
+ * @param  extractedSignature    The signature that should be verified
+ * @return 0 on success
+*/
+int
+mar_verify_signature_for_fp(FILE *fp, 
+                            CryptoX_ProviderHandle provider, 
+                            CryptoX_PublicKey key, 
+                            PRUint32 signatureCount,
+                            char *extractedSignature) {
+  CryptoX_SignatureHandle signatureHandle;
+  char buf[BLOCKSIZE];
+  PRUint32 signatureLen;
+  PRUint32 i;
+
+  if (!extractedSignature) {
+    fprintf(stderr, "ERROR: Invalid parameter specified.\n");
+    return CryptoX_Error;
+  }
+
+  /* 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;
+  }
+
+  CryptoX_VerifyBegin(provider, &signatureHandle, &key);
+
+  /* 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;
+  }
+
+  /* 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(PRUint32),
+                                                &signatureHandle,
+                                                "signature block"))) {
+    return CryptoX_Error;
+  }
+
+  for (i = 0; i < signatureCount; i++) {
+    /* Get the signature algorithm ID */
+    if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp,
+                                                  &buf, 
+                                                  sizeof(PRUint32),
+                                                  &signatureHandle, 
+                                                  "signature algorithm ID"))) {
+        return CryptoX_Error;
+    }
+
+    if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp, 
+                                                  &signatureLen, 
+                                                  sizeof(PRUint32), 
+                                                  &signatureHandle, 
+                                                  "signature length"))) {
+      return CryptoX_Error;
+    }
+    signatureLen = ntohl(signatureLen);
+
+    /* Skip past the signature itself as those are not included */
+    if (fseeko(fp, signatureLen, SEEK_CUR)) {
+      fprintf(stderr, "ERROR: Could not seek past signature.\n");
+      return CryptoX_Error;
+    }
+  }
+
+  while (!feof(fp)) {
+    int numRead = fread(buf, 1, BLOCKSIZE , fp);
+    if (ferror(fp)) {
+      fprintf(stderr, "ERROR: Error reading data block.\n");
+      return CryptoX_Error;
+    }
+
+    if (CryptoX_Failed(CryptoX_VerifyUpdate(&signatureHandle, 
+                                            buf, numRead))) {
+      fprintf(stderr, "ERROR: Error updating verify context with"
+                      " data block.\n");
+      return CryptoX_Error;
+    }
+  }
+
+  if (CryptoX_Failed(CryptoX_VerifySignature(&signatureHandle, 
+                                             &key,
+                                             extractedSignature, 
+                                             signatureLen))) {
+    fprintf(stderr, "ERROR: Error verifying signature.\n");
+    return CryptoX_Error;
+  }
+
+  return CryptoX_Success;
+}
--- a/testing/xpcshell/xpcshell.ini
+++ b/testing/xpcshell/xpcshell.ini
@@ -101,8 +101,11 @@ run-if.config = ipc
 [include:chrome/test/unit_ipc/xpcshell.ini]
 [include:extensions/cookie/test/unit_ipc/xpcshell.ini]
 [include:ipc/testshell/tests/xpcshell.ini]
 [include:modules/libpref/test/unit_ipc/xpcshell.ini]
 [include:netwerk/test/unit_ipc/xpcshell.ini]
 [include:netwerk/cookie/test/unit_ipc/xpcshell.ini]
 [include:toolkit/components/contentprefs/tests/unit_ipc/xpcshell.ini]
 [include:uriloader/exthandler/tests/unit_ipc/xpcshell.ini]
+
+[include:modules/libmar/tests/unit/xpcshell.ini]
+skip-if = os == "android"
--- a/toolkit/components/maintenanceservice/Makefile.in
+++ b/toolkit/components/maintenanceservice/Makefile.in
@@ -52,19 +52,20 @@ CPPSRCS = \
   $(NULL)
 
 # For debugging purposes only
 #DEFINES += -DDISABLE_UPDATER_AUTHENTICODE_CHECK
 
 PROGRAM = maintenanceservice$(BIN_SUFFIX)
 DIST_PROGRAM = maintenanceservice$(BIN_SUFFIX)
 
-# Don't link the maintenanceservice against libmozutils. See bug 687139
-MOZ_UTILS_LDFLAGS =
-MOZ_UTILS_PROGRAM_LDFLAGS =
+# Don't link the maintenanceservice against mozglue.dll. See bug 687139 and
+# bug 725876
+MOZ_GLUE_LDFLAGS =
+MOZ_GLUE_PROGRAM_LDFLAGS =
 
 LIBS += \
   ../../mozapps/update/common/$(LIB_PREFIX)updatecommon.$(LIB_SUFFIX) \
   $(NULL)
 
 USE_STATIC_LIBS = 1
 HAVE_PROGRESSUI = 1
 RCINCLUDE = maintenanceservice.rc
--- a/toolkit/components/maintenanceservice/workmonitor.cpp
+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
@@ -411,17 +411,17 @@ ProcessSoftwareUpdateCommand(DWORD argc,
     // When there is a certificate check error on the updater.exe application,
     // we want to write out the error.
     if (!WriteStatusFailure(argv[1], 
                             SERVICE_UPDATER_SIGN_ERROR)) {
       LOG(("Could not write pending state to update.status.  (%d)\n", 
            GetLastError()));
     }
   }
-  LocalFree(argv);
+
   return result;
 }
 
 /**
  * Executes a service command.
  *
  * @param argc The number of arguments in argv
  * @param argv The service command line arguments, argv[0] and argv[1]
--- a/toolkit/mozapps/readstrings/errors.h
+++ b/toolkit/mozapps/readstrings/errors.h
@@ -51,16 +51,23 @@
 #define ELEVATION_CANCELED 9
 #define READ_STRINGS_MEM_ERROR 10
 #define ARCHIVE_READER_MEM_ERROR 11
 #define BSPATCH_MEM_ERROR 12
 #define UPDATER_MEM_ERROR 13
 #define UPDATER_QUOTED_PATH_MEM_ERROR 14
 #define BAD_ACTION_ERROR 15
 #define STRING_CONVERSION_ERROR 16
+#define CERT_LOAD_ERROR 17
+#define CERT_HANDLING_ERROR 18
+#define CERT_VERIFY_ERROR 19
+#define ARCHIVE_NOT_OPEN 20
+#define COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR 21
+#define MAR_CHANNEL_MISMATCH_ERROR 22
+#define VERSION_DOWNGRADE_ERROR 23
 
 // The following error codes are only used by updater.exe
 // when a fallback key exists and XPCShell tests are being run.
 #define FALLBACKKEY_UNKNOWN_ERROR 100
 #define FALLBACKKEY_REGPATH_ERROR 101
 #define FALLBACKKEY_NOKEY_ERROR 102
 #define FALLBACKKEY_SERVICE_NO_STOP_ERROR 103
 #define FALLBACKKEY_LAUNCH_ERROR 104
--- a/toolkit/mozapps/update/common/updatehelper.cpp
+++ b/toolkit/mozapps/update/common/updatehelper.cpp
@@ -34,16 +34,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <windows.h>
 #include <stdio.h>
 #include "shlobj.h"
 #include "updatehelper.h"
+#include "pathhash.h"
 
 // Needed for PathAppendW
 #include <shlwapi.h>
 // Needed for CreateToolhelp32Snapshot
 #include <tlhelp32.h>
 #pragma comment(lib, "shlwapi.lib") 
 
 WCHAR* MakeCommandLine(int argc, WCHAR **argv);
@@ -332,17 +333,17 @@ StartServiceCommand(int argc, LPCWSTR* a
  *
  * @param  exePath The path of the executable to run
  * @param  argc    The total number of arguments in argv
  * @param  argv    An array of null terminated strings to pass to the exePath, 
  *                 argv[0] must be the path to the updater.exe
  * @return ERROR_SUCCESS if successful
  */
 DWORD
-LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR* argv)
+LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR* argv)
 {
   // The service command is the same as the updater.exe command line except 
   // it has 2 extra args: 1) The Path to udpater.exe, and 2) the command 
   // being executed which is "software-update"
   LPCWSTR *updaterServiceArgv = new LPCWSTR[argc + 2];
   updaterServiceArgv[0] = L"MozillaMaintenance";
   updaterServiceArgv[1] = L"software-update";
 
@@ -568,18 +569,18 @@ WaitForServiceStop(LPCWSTR serviceName, 
 }
 
 /**
  * Determines if there is at least one process running for the specified
  * application. A match will be found across any session for any user.
  *
  * @param process The process to check for existance
  * @return ERROR_NOT_FOUND if the process was not found
- * @       ERROR_SUCCESS if the process was found and there were no errors
- * @       Other Win32 system error code for other errors
+ *         ERROR_SUCCESS if the process was found and there were no errors
+ *         Other Win32 system error code for other errors
 **/
 DWORD
 IsProcessRunning(LPCWSTR filename)
 {
   // Take a snapshot of all processes in the system.
   HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
   if (INVALID_HANDLE_VALUE == snapshot) {
     return GetLastError();
@@ -626,8 +627,28 @@ WaitForProcessExit(LPCWSTR filename, DWO
   }
 
   if (ERROR_SUCCESS == applicationRunningError) {
     return WAIT_TIMEOUT;
   }
 
   return applicationRunningError;
 }
+
+/**
+ *  Determines if the fallback key exists or not
+ *
+ *  @return TRUE if the fallback key exists and there was no error checking
+*/
+BOOL
+DoesFallbackKeyExist()
+{
+  HKEY testOnlyFallbackKey;
+  if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
+                    TEST_ONLY_FALLBACK_KEY_PATH, 0,
+                    KEY_READ | KEY_WOW64_64KEY, 
+                    &testOnlyFallbackKey) != ERROR_SUCCESS) {
+    return FALSE;
+  }
+
+  RegCloseKey(testOnlyFallbackKey);
+  return TRUE;
+}
--- a/toolkit/mozapps/update/common/updatehelper.h
+++ b/toolkit/mozapps/update/common/updatehelper.h
@@ -36,15 +36,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 BOOL LaunchWinPostProcess(const WCHAR *installationDir,
                           const WCHAR *updateInfoDir, 
                           bool forceSync,
                           HANDLE userToken);
 BOOL StartServiceUpdate(int argc, LPWSTR *argv);
 BOOL GetUpdateDirectoryPath(LPWSTR path);
-DWORD LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR *argv);
+DWORD LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR *argv);
 BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
 BOOL WriteStatusPending(LPCWSTR updateDirPath);
 DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
 DWORD WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds);
+BOOL DoesFallbackKeyExist();
 
 #define SVC_NAME L"MozillaMaintenance"
--- a/toolkit/mozapps/update/common/updatelogging.cpp
+++ b/toolkit/mozapps/update/common/updatelogging.cpp
@@ -46,17 +46,17 @@
 #include <stdarg.h>
 
 #include "updatelogging.h"
 
 UpdateLog::UpdateLog() : logFP(NULL)
 {
 }
 
-void UpdateLog::Init(NS_tchar* sourcePath, NS_tchar* fileName)
+void UpdateLog::Init(NS_tchar* sourcePath, const NS_tchar* fileName)
 {
   if (logFP)
     return;
 
   this->sourcePath = sourcePath;
   NS_tchar logFile[MAXPATHLEN];
   NS_tsnprintf(logFile, sizeof(logFile)/sizeof(logFile[0]),
     NS_T("%s/%s"), sourcePath, fileName);
--- a/toolkit/mozapps/update/common/updatelogging.h
+++ b/toolkit/mozapps/update/common/updatelogging.h
@@ -45,17 +45,17 @@ class UpdateLog
 {
 public:
   static UpdateLog & GetPrimaryLog() 
   {
     static UpdateLog primaryLog;
     return primaryLog;
   }
 
-  void Init(NS_tchar* sourcePath, NS_tchar* fileName);
+  void Init(NS_tchar* sourcePath, const NS_tchar* fileName);
   void Finish();
   void Flush();
   void Printf(const char *fmt, ... );
 
   ~UpdateLog()
   {
     Finish();
   }
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -136,24 +136,35 @@ const STATE_APPLYING        = "applying"
 const STATE_SUCCEEDED       = "succeeded";
 const STATE_DOWNLOAD_FAILED = "download-failed";
 const STATE_FAILED          = "failed";
 
 // From updater/errors.h:
 const WRITE_ERROR        = 7;
 const UNEXPECTED_ERROR   = 8;
 const ELEVATION_CANCELED = 9;
+
+// Windows service specific errors
 const SERVICE_UPDATER_COULD_NOT_BE_STARTED = 16000;
 const SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001;
 const SERVICE_UPDATER_SIGN_ERROR           = 16002;
 const SERVICE_UPDATER_COMPARE_ERROR        = 16003;
 const SERVICE_UPDATER_IDENTITY_ERROR       = 16004;
 const SERVICE_STILL_APPLYING_ON_SUCCESS    = 16005;
 const SERVICE_STILL_APPLYING_ON_FAILURE    = 16006;
 
+// Updater MAR security errors
+const CERT_LOAD_ERROR                         = 17;
+const CERT_HANDLING_ERROR                     = 18;
+const CERT_VERIFY_ERROR                       = 19;
+const ARCHIVE_NOT_OPEN                        = 20;
+const COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR = 21;
+const MAR_CHANNEL_MISMATCH_ERROR              = 22;
+const VERSION_DOWNGRADE_ERROR                 = 23;
+
 const CERT_ATTR_CHECK_FAILED_NO_UPDATE  = 100;
 const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
 const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
 
 const DOWNLOAD_CHUNK_SIZE           = 300000; // bytes
 const DOWNLOAD_BACKGROUND_INTERVAL  = 600;    // seconds
 const DOWNLOAD_FOREGROUND_INTERVAL  = 0;
 
@@ -1408,17 +1419,24 @@ UpdateService.prototype = {
         }
 
         if (update.errorCode == SERVICE_UPDATER_COULD_NOT_BE_STARTED ||
             update.errorCode == SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS ||
             update.errorCode == SERVICE_UPDATER_SIGN_ERROR ||
             update.errorCode == SERVICE_UPDATER_COMPARE_ERROR ||
             update.errorCode == SERVICE_UPDATER_IDENTITY_ERROR ||
             update.errorCode == SERVICE_STILL_APPLYING_ON_SUCCESS ||
-            update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE) {
+            update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE ||
+            update.errorCode == CERT_LOAD_ERROR ||
+            update.errorCode == CERT_HANDLING_ERROR ||
+            update.errorCode == CERT_VERIFY_ERROR ||
+            update.errorCode == ARCHIVE_NOT_OPEN ||
+            update.errorCode == COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR ||
+            update.errorCode == MAR_CHANNEL_MISMATCH_ERROR ||
+            update.errorCode == VERSION_DOWNGRADE_ERROR) {
           var failCount = getPref("getIntPref", 
                                   PREF_APP_UPDATE_SERVICE_ERRORS, 0);
           var maxFail = getPref("getIntPref", 
                                 PREF_APP_UPDATE_SERVICE_MAX_ERRORS, 
                                 DEFAULT_SERVICE_MAX_ERRORS);
 
           // As a safety, when the service reaches maximum failures, it will
           // disable itself and fallback to using the normal update mechanism
--- a/toolkit/mozapps/update/test/chrome/Makefile.in
+++ b/toolkit/mozapps/update/test/chrome/Makefile.in
@@ -39,17 +39,17 @@ DEPTH     = ../../../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 relativesrcdir = toolkit/mozapps/update/test/chrome
 
 include $(DEPTH)/config/autoconf.mk
 
 _OTHER_FILES = \
-  ../unit/data/simple.mar \
+  ../unit/data/simple_no_pib.mar \
   $(NULL)
 
 _CHROME_FILES = \
   test_0011_check_basic.xul \
   test_0012_check_basic_license.xul \
   test_0013_check_incompat_basic.xul \
   test_0015_check_incompat_basic_addons.xul \
   test_0014_check_incompat_basic_license.xul \
--- a/toolkit/mozapps/update/test/sharedUpdateXML.js
+++ b/toolkit/mozapps/update/test/sharedUpdateXML.js
@@ -6,17 +6,17 @@
  * Helper functions for creating xml strings used by application update tests.
  *
  * !IMPORTANT - This file contains everything needed (along with dependencies)
  * by the updates.sjs file used by the mochitest-chrome tests. Since xpcshell
  * used by the http server is launched with -v 170 this file must not use
  * features greater than JavaScript 1.7.
  */
 
-const FILE_SIMPLE_MAR = "simple.mar";
+const FILE_SIMPLE_MAR = "simple_no_pib.mar";
 
 const SIZE_SIMPLE_MAR = "351";
 
 const MD5_HASH_SIMPLE_MAR    = "d0a7f84dacc55a252ab916668a7cb216";
 const SHA1_HASH_SIMPLE_MAR   = "f5053f9552d087c6c6ed83f9b19405eccf1436fc";
 const SHA256_HASH_SIMPLE_MAR = "663c7cbd11fe45b0a71438387db924d205997ab85ccf5" +
                                "b40aebbdaef179796ab";
 const SHA384_HASH_SIMPLE_MAR = "a57250554755a9f42b91932993599bb6b05e063dcbd71" +
index 65eff48c31b2b98c3d687f301867d6c34230730a..c572c4d8a213cd2080393a7cd7121300ff95ff1f
GIT binary patch
literal 12498
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
Ljqkts{2JIV8CgK9
index 1e13dc137447fb7b3e3a1e1a2573077a216b9c40..6901e5343a0c9dca489a3d5086d0579db4fe034c
GIT binary patch
literal 97714
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
index 790cf218ed45de92bca5261d4c3c786220995ecc..7a937d075e0f45e53af388314cd0a410122beec7
GIT binary patch
literal 11936
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
CVb@Lo
index 878e64baefe164fd9728035525b7b02f29631a72..f2a91d4a3661689d25d652934dc6d459323cef59
GIT binary patch
literal 10344
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
SXa0y)ZFnWt|7GSkkbeg@GXeJi
index 42f42bca17260b14e70d16c12c8855c2c2788eab..94a07ab5d32cdf4680a999921b83153795546165
GIT binary patch
literal 735
zc%1Wf3^HV3V9I9z1E%{xf)Rol8HBv0>OP&Az3(a0r(H|}H$GG+if-F~+_JK6;o-~%
zjl&6{AspNxE;e^gtEd0|){^_PV48M`=<ATvYK(tg=4GZmm^@c+ugT5e^y`afr7sIe
zOX2%@(9z@Q5=Q>V5B=0VCf2yB=<=nlR%=tbGkNzWt}w5UT{bI&lkPwE{WZf}g+D%X
zk-WTIl+@Y_EOs?FjW+JJe>bPc>}2-YkDFgVyQAk-w)&y?lM@Pcyxfb|CMz#B@Xj?l
z-s!pTQc8`o4{yY04=1y(0-q1tjmt&8Ol^MRmzwtT%aRuh6qeQoYQ~wWiTU(AYE@2C
zEf0V4VAEdDnc558Yx_zBmY;gry?O2ii(6vPs;@%BA_GKM6eJgCq~_%4mZTP!Fc=!?
z86+AqP!TvqWmp<IMydrzCM}sdoq>V(dIN(%0MkMSF^1)8_ZbuzLd;gQId*n@HR2SS
zw}5fM1*gklr*5tJpr*vbwq8}E)RpJon~H_M-Hq0)PJJz6Cz-N;wqk2x!okTO%T<C~
zIoY&4BMT6Aq!jr6W?<k4*}=fj2()9F+IgTIiYs3+PFy%)nU7Divha~vqO!c=9HA9+
zGZr*Fyl1EPq^n?|!ifj7zah+7a-e7qFx9vN%~51vV^CA^0h(gSaDkC2rOIfYQ>9MM
z7v^2lkKUQ+C!(ThAhR6g(;Q%Eb^z%VAib@$ASJORRWCOYm|j2u3lw_;Bs>su@t^?L
mFG|hLFH23)P0P$lEe0i26Ch!Yt|}DF4goS9Q&QZ3N*MrQgZW(m
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..42f42bca17260b14e70d16c12c8855c2c2788eab
GIT binary patch
literal 351
zc%1Wf3^HV3V0iBom0@Y<7^xN<nY3i;bOr|A>kSM70Za=S#2A*V-Dgl>2r*mH=GfWs
z)reDQ-U7x27o0AKow~K=gPIZ#+j>=vQdgdTZz>l4b~jqHI`y@Pon*@X*@~@&2?r;C
zELRC`<z&<Hj4VLdvE)F}90mp^cc2}L3~UT)Dn39P3>hvkGNn`*&2y^M$@#*(Yx>bU
z6a7R~G!0~yGa$646!`vTVBiNikAa~P=)7fW=Yg6PSH5DLxNyQUAD?Dr;Ulv|WqHLp
zLM!HGENFOm&ra`2SHVJs6Axy819~+Fh&g~bg@J)_TWLW`Vo9oAZem_$T553#161A`
sAs1f)6xR<0vqOMP$CMPe%$!t^%H=@pfv&PBH8;O3HAOcKsHm6$05yzs)Bpeg
--- a/toolkit/mozapps/update/test/unit/head_update.js.in
+++ b/toolkit/mozapps/update/test/unit/head_update.js.in
@@ -85,16 +85,18 @@ const URL_PATH = "data";
 
 const APPLY_TO_DIR_SUFFIX = "_applyToDir/";
 const HELPER_BIN_FILE = "TestAUSHelper" + BIN_SUFFIX;
 const MAR_COMPLETE_FILE = "data/complete.mar";
 const MAR_PARTIAL_FILE = "data/partial.mar";
 const UPDATER_BIN_FILE = "updater" + BIN_SUFFIX;
 const MAINTENANCE_SERVICE_BIN_FILE = "maintenanceservice.exe";
 const MAINTENANCE_SERVICE_INSTALLER_BIN_FILE = "maintenanceservice_installer.exe";
+const UPDATE_SETTINGS_INI_FILE = "update-settings.ini";
+const UPDATE_SETTINGS_CONTENTS = "[Settings]\nMAR_CHANNEL_ID=xpcshell-test\n"
 const UPDATES_DIR_SUFFIX = "_mar";
 
 const LOG_COMPLETE_SUCCESS = "data/complete_log_success";
 const LOG_COMPLETE_CC_SUCCESS = "data/complete_cc_log_success";
 
 const LOG_PARTIAL_SUCCESS = "data/partial_log_success";
 const LOG_PARTIAL_FAILURE = "data/partial_log_failure";
 
@@ -438,16 +440,20 @@ function runUpdate() {
   let cwdPath = callbackApp.parent.path;
   if (/ /.test(cwdPath))
     cwdPath = '"' + cwdPath + '"';
 
   let callbackAppPath = callbackApp.path;
   if (/ /.test(callbackAppPath))
     callbackAppPath = '"' + callbackAppPath + '"';
 
+  let updateSettingsIni = getApplyDirFile(null, true);
+  updateSettingsIni.append(UPDATE_SETTINGS_INI_FILE);
+  writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
+
   let args = [updatesDirPath, applyToDirPath, 0, cwdPath, callbackAppPath].
              concat(gCallbackArgs);
   let process = AUS_Cc["@mozilla.org/process/util;1"].
                 createInstance(AUS_Ci.nsIProcess);
   process.init(updateBin);
   process.run(true, args, args.length);
   return process.exitValue;
 }
@@ -744,16 +750,20 @@ function runUpdateUsingService(aInitialS
 
   // The service will execute maintenanceservice_installer.exe and
   // will copy maintenanceservice.exe out of the same directory from
   // the installation directory.  So we need to make sure both of those
   // bins always exist in the installation directory.
   copyBinToApplyToDir(MAINTENANCE_SERVICE_BIN_FILE);
   copyBinToApplyToDir(MAINTENANCE_SERVICE_INSTALLER_BIN_FILE);
 
+  let updateSettingsIni = getApplyDirFile(null, true);
+  updateSettingsIni.append(UPDATE_SETTINGS_INI_FILE);
+  writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
+
   // Firefox does not wait for the service command to finish, but
   // we still launch the process sync to avoid intermittent failures with
   // the log file not being written out yet.
   // We will rely on watching the update.status file and waiting for the service
   // to stop to know the service command is done.
   process.run(true, args, args.length);
 
   resetEnvironment();
--- a/toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update.js
+++ b/toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update.js
@@ -99,16 +99,20 @@ function run_test() {
   let updaterIniContents = "[Strings]\n" +
                            "Title=Update Test\n" +
                            "Info=Application Update XPCShell Test - " +
                            "test_0200_general.js\n";
   updaterIni = processDir.clone();
   updaterIni.append(FILE_UPDATER_INI);
   writeFile(updaterIni, updaterIniContents);
 
+  let updateSettingsIni = processDir.clone();
+  updateSettingsIni.append(UPDATE_SETTINGS_INI_FILE);
+  writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
+
   let launchBin = getLaunchBin();
   let args = getProcessArgs();
   logTestInfo("launching " + launchBin.path + " " + args.join(" "));
 
   gProcess = AUS_Cc["@mozilla.org/process/util;1"].
                 createInstance(AUS_Ci.nsIProcess);
   gProcess.init(launchBin);
 
--- a/toolkit/mozapps/update/updater/Makefile.in
+++ b/toolkit/mozapps/update/updater/Makefile.in
@@ -61,16 +61,17 @@ LOCAL_INCLUDES += -I$(srcdir)/../../read
 LIBS += \
   ../common/$(LIB_PREFIX)updatecommon.$(LIB_SUFFIX) \
   $(DEPTH)/modules/libmar/src/$(LIB_PREFIX)mar.$(LIB_SUFFIX) \
   ../../readstrings/$(LIB_PREFIX)readstrings.$(LIB_SUFFIX) \
   $(BZ2_LIBS) \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
+LIBS += $(DEPTH)/modules/libmar/verify/$(LIB_PREFIX)verifymar.$(LIB_SUFFIX)
 USE_STATIC_LIBS = 1
 HAVE_PROGRESSUI = 1
 RCINCLUDE = updater.rc
 CPPSRCS += \
   progressui_win.cpp \
   $(NULL)
 OS_LIBS += $(call EXPAND_LIBNAME,comctl32 ws2_32 shell32)
 DEFINES += -DUNICODE -D_UNICODE
@@ -106,17 +107,20 @@ ifdef MOZ_DEBUG
 MOZ_WINCONSOLE = 1
 else
 MOZ_WINCONSOLE = 0
 endif
 endif
 
 include $(topsrcdir)/config/rules.mk
 
-DEFINES += -DNS_NO_XPCOM
+DEFINES += -DNS_NO_XPCOM \
+  -DMAR_CHANNEL_ID='"$(MAR_CHANNEL_ID)"' \
+  -DMOZ_APP_VERSION='"$(MOZ_APP_VERSION)"' \
+  $(NULL)
 
 ifdef _MSC_VER
 WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 libs:: updater.png
 	$(NSINSTALL) -D $(DIST)/bin/icons
@@ -134,9 +138,23 @@ libs::
 	rm -f $(DIST)/bin/updater
 endif
 
 ifeq (,$(filter-out WINNT,$(OS_ARCH)))
 # Pick up nsWindowsRestart.cpp
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
 endif
 
+ifeq ($(OS_ARCH),WINNT)
+EXTRA_LIBS += $(call EXPAND_LIBNAME,crypt32)
+EXTRA_LIBS += $(call EXPAND_LIBNAME,advapi32)
+endif
+
 CXXFLAGS += $(BZ2_CFLAGS)
+
+ifneq (,$(filter beta release esr,$(MOZ_UPDATE_CHANNEL)))
+RCFLAGS += -DMAR_SIGNING_RELEASE_BETA=1
+else
+ifneq (,$(filter nightly aurora nightly-elm nightly-profiling nightly-oak,$(MOZ_UPDATE_CHANNEL)))
+RCFLAGS += -DMAR_SIGNING_AURORA_NIGHTLY=1
+endif
+endif
+
--- a/toolkit/mozapps/update/updater/archivereader.cpp
+++ b/toolkit/mozapps/update/updater/archivereader.cpp
@@ -37,28 +37,215 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include <string.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include "bzlib.h"
 #include "archivereader.h"
 #include "errors.h"
+#include "nsAlgorithm.h"
+#ifdef XP_WIN
+#include "updatehelper.h"
+#endif
+
+#define UPDATER_NO_STRING_GLUE_STL
+#include "../../../../xpcom/build/nsVersionComparator.cpp"
+#undef UPDATER_NO_STRING_GLUE_STL
 
 #if defined(XP_UNIX)
 # include <sys/types.h>
 #elif defined(XP_WIN)
 # include <io.h>
 #endif
 
 static int inbuf_size  = 262144;
 static int outbuf_size = 262144;
 static char *inbuf  = NULL;
 static char *outbuf = NULL;
 
+#ifdef XP_WIN
+#include "resource.h"
+
+/**
+ * Obtains the data of the specified resource name and type.
+ *
+ * @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)
+{
+  HMODULE handle = GetModuleHandle(NULL);
+  if (!handle) {
+    return FALSE;
+  }
+
+  HRSRC resourceInfoBlockHandle = FindResource(handle, 
+                                               MAKEINTRESOURCE(name),
+                                               MAKEINTRESOURCE(type));
+  if (!resourceInfoBlockHandle) {
+    FreeLibrary(handle);
+    return FALSE;
+  }
+
+  HGLOBAL resourceHandle = LoadResource(handle, resourceInfoBlockHandle);
+  if (!resourceHandle) {
+    FreeLibrary(handle);
+    return FALSE;
+  }
+
+  size = SizeofResource(handle, resourceInfoBlockHandle);
+  data = static_cast<const char*>(::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;
+  if (!LoadFileInResource(name, type, data, size) || !data || !size) {
+    return CERT_LOAD_ERROR;
+  }
+
+  if (mar_verify_signatureW(archive, data, size)) {
+    return CERT_VERIFY_ERROR;
+  }
+
+  return OK;
+}
+#endif
+
+
+/**
+ * Performs a verification on the opened MAR file.  Both the primary and backup 
+ * keys stored are stored in the current process and at least the primary key 
+ * will be tried.  Success will be returned as long as one of the two 
+ * signatures verify.
+ *
+ * @return OK on success
+*/
+int
+ArchiveReader::VerifySignature()
+{
+  if (!mArchive) {
+    return ARCHIVE_NOT_OPEN;
+  }
+
+#ifdef XP_WIN
+  // If the fallback key exists we're running an XPCShell test and we should
+  // use the XPCShell specific cert for the signed MAR.
+  int rv;
+  if (DoesFallbackKeyExist()) {
+    rv = VerifyLoadedCert(mArchive, IDR_XPCSHELL_CERT, TYPE_CERT);
+  } else {
+    rv = VerifyLoadedCert(mArchive, IDR_PRIMARY_CERT, TYPE_CERT);
+    if (rv != OK) {
+      rv = VerifyLoadedCert(mArchive, IDR_BACKUP_CERT, TYPE_CERT);
+    }
+  }
+  return rv;
+#else
+  return OK;
+#endif
+}
+
+/**
+ * Verifies that the MAR file matches the current product, channel, and version
+ * 
+ * @param MARChannelID   The MAR channel name to use, only updates from MARs
+ *                       with a matching MAR channel name will succeed.
+ *                       If an empty string is passed, no check will be done
+ *                       for the channel name in the product information block.
+ *                       If a comma separated list of values is passed then
+ *                       one value must match.
+ * @param appVersion     The application version to use, only MARs with an
+ *                       application version >= to appVersion will be applied.
+ * @return OK on success
+ *         COULD_NOT_READ_PRODUCT_INFO_BLOCK if the product info block 
+ *                                           could not be read.
+ *         MARCHANNEL_MISMATCH_ERROR         if update-settings.ini's MAR 
+ *                                           channel ID doesn't match the MAR
+ *                                           file's MAR channel ID. 
+ *         VERSION_DOWNGRADE_ERROR           if the application version for
+ *                                           this updater is newer than the
+ *                                           one in the MAR.
+ */
+int
+ArchiveReader::VerifyProductInformation(const char *MARChannelID, 
+                                        const char *appVersion)
+{
+  if (!mArchive) {
+    return ARCHIVE_NOT_OPEN;
+  }
+
+  ProductInformationBlock productInfoBlock;
+  int rv = mar_read_product_info_block(mArchive, 
+                                       &productInfoBlock);
+  if (rv != OK) {
+    return COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR;
+  }
+
+  // Only check the MAR channel name if specified, it should be passed in from
+  // the update-settings.ini file.
+  if (MARChannelID && strlen(MARChannelID)) {
+    // Check for at least one match in the comma separated list of values.
+    const char *delimiter = " ,\t";
+    // Make a copy of the string in case a read only memory buffer 
+    // was specified.  strtok modifies the input buffer.
+    char channelCopy[512] = { 0 };
+    strncpy(channelCopy, MARChannelID, sizeof(channelCopy) - 1);
+    char *channel = strtok(channelCopy, delimiter);
+    rv = MAR_CHANNEL_MISMATCH_ERROR;
+    while(channel) {
+      if (!strcmp(channel, productInfoBlock.MARChannelID)) {
+        rv = OK;
+        break;
+      }
+      channel = strtok(NULL, delimiter);
+    }
+  }
+
+  if (rv == OK) {
+    /* Compare both versions to ensure we don't have a downgrade
+        1 if appVersion is older than productInfoBlock.productVersion
+        -1 if appVersion is newer than productInfoBlock.productVersion 
+        0 if appVersion is the same as productInfoBlock.productVersion 
+       This even works with strings like:
+        - 12.0a1 being older than 12.0a2
+        - 12.0a2 being older than 12.0b1
+        - 12.0a1 being older than 12.0
+        - 12.0 being older than 12.1a1 */
+    int versionCompareResult = 
+      NS_CompareVersions(appVersion, productInfoBlock.productVersion);
+    if (-1 == versionCompareResult) {
+      rv = VERSION_DOWNGRADE_ERROR;
+    }
+  }
+
+  free((void *)productInfoBlock.MARChannelID);
+  free((void *)productInfoBlock.productVersion);
+  return rv;
+}
+
 int
 ArchiveReader::Open(const NS_tchar *path)
 {
   if (mArchive)
     Close();
 
   if (!inbuf) {
     inbuf = (char *)malloc(inbuf_size);
--- a/toolkit/mozapps/update/updater/archivereader.h
+++ b/toolkit/mozapps/update/updater/archivereader.h
@@ -51,16 +51,19 @@
 // This class provides an API to extract files from an update archive.
 class ArchiveReader
 {
 public:
   ArchiveReader() : mArchive(NULL) {}
   ~ArchiveReader() { Close(); }
 
   int Open(const NS_tchar *path);
+  int VerifySignature();
+  int VerifyProductInformation(const char *MARChannelID, 
+                               const char *appVersion);
   void Close();
 
   int ExtractFile(const char *item, const NS_tchar *destination);
   int ExtractFileToStream(const char *item, FILE *fp);
 
 private:
   int ExtractItemToStream(const MarItem *item, FILE *fp);
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..95b4ef38c6bf669c964430a93d727415a89b8184
GIT binary patch
literal 671
zc$_n6Vw!Ex#Mr!mnTe5!iIrj6(H(aTc-c6$+C196^D;8BvN9O(8}b@(voVLVFblJ!
zq!t(&$cghB8W|WG8W@@y7?_wxfw?AdE~bViMkQoJ8Ce;an;7{SfZ|+CO^l2T=L|0`
z+#c7-ds62A9ggOekxT!cj$N%kvt#k)iC=eR2~Kyuta?&q`b_5Y)<*wzrmg1rwRpCn
zm)G8pEU#^5*tB^v+?aho@}BYp$ApUb*1z+gcs}V0nK3QZTv~pe+sQR$_iF5>$z9LW
zwP0LOxc*PB&-TiFO9EX!>^WqzFh$SKaY|O>RcDR=Il{tgI*r6jrE8*-;=jzd_<dsG
zva^BjS2~o+EnBedr%W+V#=lJ>ul|WCZmrDR67xpD?b~nn7y7z9$p^Myy!_2+*_m5v
z>-X(H|KYiiRPOPMCNl0uEbP|j3pyU9>83l`zy4qHG*Q^8@0swQYF^!{gMa;ki|?28
z8HlKhyI*EvW@KPQ3wCBuzyvy#?fZK#?S1_T<_6yNkL%7%{#Nfh*;8aub=LLv&Sfm?
zgI>>;eZKm#bH<f|-8SEt!a8>=&9|Pnf=BDVu)%$<y^B{(eKbkq^OLmI#`%XHH+QTE
zjGrSPS2bg+V*THlhPD?<9_a}77GBwI^m=prNqII={YP@24filU2n=$udi>(<PZ=|f
z3$rUb<Q+afe7iW;ZH|NJ&f{vSr&fPwt7pk?JH(uP=4urW`>*p4S6)d;nLBgm37O6L
zPU-U-FRzW#joi`jc)p`TY=FSbf`BE;zyGYB_-Mt#_+KT{UkEq$_WONwQMO<0xmem$
h<g)CO?=w;k|KZ<0eJX1~S7I4M&c&Z^kH}~V0RZ^tAJhN<
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a460d6a16dd16e2bbe0d899479e55f5d4e486a84
GIT binary patch
literal 671
zc$_n6Vw!Ex#Mr!mnTe5!iIrj6(VapDylk9WZ60mkc^Mg5Ss4uY4S5Z?*_cCFn1xwV
zQVWa><ivRmjSLJ84Gc{U3=J%!z+4kJ7gIwMqY|>AjI0dIO^o~uKyfanCPqevt4XZ3
zHs^2h$vpqBah?D4q8pVgek+T&q^=e^^z6e4rDpaMn<Nfz^xU{=d4l_FmKD!#PJI==
z|L=pQlY-|rG;^)FCbaS0k`;5<N)<esmoD6}caMkt58;^Q?D4LCbBokY?>>8L|2}iK
z&bB(MhUYiGo>`OYamGBQDpUKs+00T0!`S}?S1UjNe=E2**>%BNn;*f8G#|5681|cg
zy)1g=+4avRhFy*+!N$(4dxV!4baS4*_}Fn~nTuKMkE5nDm$oJT>X@eH&T#pmN;;Fq
zoU(#D+OO=hLei&wh>YC4H?3oOzV^mAvp`pwsy**Te4Hov9a<J6z?P!A>%4yhQ-r=<
z5qHDGeZ5S~j0}uu!Ojc{7~vypn|;3W-Ft6(Cxh`tmui)UVXGpu`R#=6oBs;jWYxC%
z9{NASOPlTC{|OhI-iUk_*{x|6!C+V+Qs?0v`aVU*jLEYlv+jy9dy3z@@1`YF)^V+H
zW?EEiRH(%;tt+wdQ{k=3=vnn{h0iD0Jy;dJ^l1C4c(?hU+dAh4H@99gcK7l%U0Em5
zzU+j{-2>d~!lrStt-N63d+OFN9@mQ^tm|$r-{qDXu|j5|)~o}`e4dxDPhVTqVYOm)
zboSBhn|DQXeZI71_I`os*=)x?KUgl6{dw6Uv&pABf4jcPJlfDT>x;ag-m|t~@A?pV
eE9H#ETn4e~a)-<W(@SCs&NT7L-}<so%M1X?#S~%y
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b22124798d43ca9f0b1e3b8ad23d410b93d04228
GIT binary patch
literal 679
zc$_n6Vp?p_#Mr%nnTe5!iIrj6(Nnnwylk9WZ60mkc^Mg5Ss4t34FwJO*_cCFn1wm=
zGSf3kaw-iC<ivRmjSLJ84Gc|93=9mTz+4kJ7rUk=MkQp!8Ce;an;7{SfZ|+CO^l2T
zkL*u)SBT}ShR)6Wkt%se?9J_$0aCJSIes@<UT$B~8laf7GN?vccfz*oS?0-s0i4Tf
z&CX_?%e1W2V9(^6r0M1E@F;29#TVcIbo_4Uu2E*2{)6kf{>lE&U4?vgj*W{Q_;SBp
zURIUJbLX$Yjoowiu5vg!V^?orh$oZqxnHXmY81(YWYowyUN>KQ_1faruK81foh{yX
z9FXE(9hRW~QKCBRv&sHR%h;DzMbz*-<56zTnV9^`?r4*a>63YzZ@papm~Zo!^Z>qY
z<<^;!eG*yH*F#Tw#0WgdO}??M<KB^`l$jlWRGI`g$=>5Sb<y>0McukPY_DG4(Q!;T
z=`8zKFsN-+D-$y#10!1SGlK$V(X9!Al`k?=`1-!(pWE@+_g*2_A6plNhS!ywXLxP(
zK6+x6?1oKdQO}I_9zFPKw-$ebA#+VsT=A93f(`3udR^w^P1c@h6`Xx5irIJB7u!uP
zt5!aFb0uJ6w#hx7;>{lp+1zbe^uAo>*@KUHj62Fc$rTpv)K-1g&?I2$V(X+KvsCkC
zl<(eULFKZg@u7QmPZT`UZdN)mMRBuA3d8fuuH9EdCVXeTvABJobI-%LXAf-Ww^WCU
z7O&;=&M7H$>;JYsebT9U&XTXrl#5Ee3S%n$Z&9@HOyWa5wN+_L?uDOTue?R<bjH3P
iA2OSK`dJ>I*spb1i;?|}df>D9qQ6gbrbO$Vp9%o^tQ|c7
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2dffbd02da0fd04c0471fd02c7637e9e3062dd64
GIT binary patch
literal 679
zc$_n6Vp?p_#Mr%nnTe5!iIrj6(No<9ylk9WZ60mkc^Mg5Ss4t34FwJO*_cCFn1wm=
zGSf3kaw?4s<ivRmjSLJ84Gc|9fH(@wHGy-nYieRtLN=U{m4Ug5k)Hu5&c)Qk$jI={
zV5&&xn$W%zIvd<KtvtP1Qhb$TzW=TCDM}qai@x$bmAj%BJ3Ys6u7meH?)fg)<vTjh
zd;Unhyz59&GUu_Q+i&$9J1UpG>RP;%L3q_B!Q{I3YZ~GW58nJZ-Mr&LX!SEsYYw)g
z77dMmm*sY+{FnMAu`ERS$d7FEzGHk_bgo{wYOBIjby|InWZIHnyU(n5mT0O{*d)DF
zv#xA&e&>R9wLCj!>KjgO7kX3>X>WA!+w|ifdjFZ`-gsEoqF#1UH16_|pW#au`<%bP
z#;QA2Dqw&9ya!K<?{afJJzSI8+TQ)&=jZ{IwF^!>pY`kQga?N*mX|A^P@d{0_gJ%i
z>GjrPSB895V<u)s21c~tX9fj~@UyoYT{YZ193*^a_pT~9eU;7sjDy+^@j4Yoo^bvh
zLV+QP?b#|HCtB9ZzqL8v-oE9x{?SnOfY4tHCqD@DwtQ#!;iW<0{sV`c!xLRZ8P;4^
z-Ri9>A9iX<z$vNxi=H3u|9-B^;3jTnU!ttH|1{(OrMK#iN^f_Uo!|29ns-QBOiEGs
z+MR5w@p63Aoz4qqWQvtu772MAa!Nn8uhJ|d{nh-)q-;@*ZwI}e{7*UcVz=)PPercS
zD;YcD3)UHlq@{BGKhmbI^*iQ2-@RKWcKy}hc+zm;R|h|LgAD7ei>>~rZ+u(Ab=Q-H
gRmrS#|LQM#j@p|R9i7^|ZU6QBS4vE7x+VQ{0p#%-jsO4v
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..11417c35e7ffe8af28486e716722be8211bf865f
GIT binary patch
literal 709
zc$_n6VmfHh#JF++GZP~d6Dz~iZJ(tKc-c6$+C196^D;8BvN9Mb87dgau`!3TFbj(Z
zrRJn27N;usItD2eXQt<6=A|oSr&b!siSrs78dw^c7#SH`8XH7`xQ0e>E>UJQF)ATj
z&B)5Y+{DPw02Jq9YGPz$*i)zdy)o0-Lw|ilqVLyQQ}<~$^PJ3=+;m&_{*SrEJf~9i
zCjKk3SA;aSKf2DVHD~Lb)hjz^PxfE(^(>q4Z@a>~p3LI6Cgio)xLo_s;`6`Y?u+@K
zE(?`aCu-et*;{J7oQ3`5`WbU#OBA(sbImCfdlPi3CeopLr}Wpu3)H8+*=>HYUdJ~{
z=MQJrv7)7tpCk9)N;*_@@i41p&pyl2*k#`7E4!AObUfdFPVd2ur7vBdvu@%vb@k)y
zH?Rm;YW6jA=Z^CeleRWp4?A5`-!j$GZ33V6b#-~E9mbz5JY6S8ynX#%N6ty{*n>GM
zig)v0voz?D6q3IChR0`4J@Z_vw)pKeyc?OA85tPSB8M3iFeTC_b1T!uw$vUiGQVoa
zkRUc^j^2|Pq1)@d75;pWb5QTv6nA%n)UJvv``$KgIPit1VqcM`pV<3{pLrRtItNTn
zpU7KlThTw|&iVEGOCOw@XHl|PydlnE-c_>;wcUv<{f&-J2bpjCXI=1gTB<$Ya?1D3
zm$c_U^z(fl`(5wnK7-fmm34nRNXJAkDL7)fvrgmKbk3bC1r1!ZY_?2Jm#WH$54^VW
z=@Ne@<>^0vOgLrraXCx+2k$O6Z3B<pleTbloh#Uv6Z-Vhmw7$c<-fT$F|I!M$D{w}
zjl(t$-<R{Gs60EjB=PqIx8*%SGACY$Wo!NXvhCf^8PoK8IW#{UEL84`n6mRC0G|pe
A;Q#;t
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..16a7ef6d91d9e9fd5efd41837c7019898e9ef20f
GIT binary patch
literal 713
zc$_n6VmfNj#JF|=GZP~d6Dz~ieWIQQylk9WZ60mkc^Mg5Ss4se43!KN*qB3En1v;S
zQgc!hi&GVR9fK5#Gt=`j^U@WvQ!5pW4CKUl4Gj$}jm!-#OiYX|qCi|jBRH2xQ<@l+
zkgaEAWngY%<Yxeib1^kBGBT`Q=dRl(rSzeGQ{mk=uQNCTn>yp39bf9Bx_{f#>8*;K
z)~Bbxv9V0h$-nw`y+uoEpw)b)TMX<+o?cgrG}-_2SJ|z1{*D_<_E^kxYP|NMUuNso
z$182K_WyE>;CDH?LiPE=yQ+>i<}_{5U$-J$>d!squxH8h)ZO}49nNL{+O%&^(T|xP
zf}3qJRx2Lkf1tF&?CpE@1Ch~6wx*q5ek}hORx5R?l#BPS@g^Zlkq3cHnF}-pDz>TL
zt9@r0%XjR<yln-ks$Ge{U3Y9X<h>>~Q?x9ivi8K}DxdbR-`2lg_4w_pEC0gP7X*20
zZT}WEd&@&v_3T|ceHa$2%Nr$K+f}=V$<fzzHxn}>10z}lF@pkTQn%%Mi)WK(y^M~^
z`r5f>`P%<V%Ev#-E;F0bno>4v^Nwep%4^>FF|IptR7`w_=7fHZz3RIT)iWr+IB>@9
z?yKEXdc<017j^cX?Nk3OzU6X@`W5LLYEIYkwC%alZyt-^qraI|;bJAP<kAesq-}*C
zZ#`A2lGggOvDiQ$kWsr}w^#*FfL1f>BF3GK*B7qq?*FIGY_8WH`0BTYbyjMSyh}uN
z_7s1{g|Yt%ZJedoe3^FVwQ2U+3qLX|L|c>Z=(&1E|NR}llKIZ99d7mAYHyErbwwoo
zK4F#p@29tm>9ga7>pNIKpZKjYdvRfdT&?_kfp474rgwc}u6)4NwElwARs)a6WdKc$
BD)s;X
--- a/toolkit/mozapps/update/updater/resource.h
+++ b/toolkit/mozapps/update/updater/resource.h
@@ -1,20 +1,24 @@
 //{{NO_DEPENDENCIES}}
 // Microsoft Visual C++ generated include file.
 // Used by updater.rc
 //
 #define IDD_DIALOG                      101
 #define IDC_PROGRESS                    1000
 #define IDC_INFO                        1002
 #define IDI_DIALOG                      1003
+#define TYPE_CERT                       512
+#define IDR_PRIMARY_CERT                1004
+#define IDR_BACKUP_CERT                 1005
 #define IDS_UPDATER_IDENTITY            1006
+#define IDR_XPCSHELL_CERT               1007
 
 // Next default values for new objects
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        102
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1007
+#define _APS_NEXT_CONTROL_VALUE         1008
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -63,16 +63,17 @@
  *
  *  precomplete
  *  -----------
  *  method   = "remove" | "rmdir"
  */
 #include "bspatch.h"
 #include "progressui.h"
 #include "archivereader.h"
+#include "readstrings.h"
 #include "errors.h"
 #include "bzlib.h"
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
 
@@ -186,16 +187,25 @@ public:
   FILE* get() {
     return mFile;
   }
 
 private:
   FILE* mFile;
 };
 
+struct MARChannelStringTable {
+  MARChannelStringTable() 
+  {
+    MARChannelID[0] = '\0';
+  }
+
+  char MARChannelID[MAX_TEXT_LEN];
+};
+
 //-----------------------------------------------------------------------------
 
 typedef void (* ThreadFunc)(void *param);
 
 #ifdef XP_WIN
 #include <process.h>
 
 class Thread
@@ -1447,16 +1457,17 @@ WriteStatusApplying()
 
   static const char kApplying[] = "applying";
   if (fwrite(kApplying, strlen(kApplying), 1, file) != 1)
     return false;
 
   return true;
 }
 
+#ifdef MOZ_MAINTENANCE_SERVICE
 /* 
  * Read the update.status file and sets isPendingService to true if
  * the status is set to pending-service.
  *
  * @param  isPendingService Out parameter for specifying if the status
  *         is set to pending-service or not.
  * @return true if the information was retrieved and it is pending
  *         or pending-service.
@@ -1481,17 +1492,19 @@ IsUpdateStatusPending(bool &isPendingSer
   const char kPendingService[] = "pending-service";
   isPending = strncmp(buf, kPending, 
                       sizeof(kPending) - 1) == 0;
 
   isPendingService = strncmp(buf, kPendingService, 
                              sizeof(kPendingService) - 1) == 0;
   return isPending;
 }
-
+#endif
+
+#ifdef XP_WIN
 /* 
  * Read the update.status file and sets isSuccess to true if
  * the status is set to succeeded.
  *
  * @param  isSucceeded Out parameter for specifying if the status
  *         is set to succeeded or not.
  * @return true if the information was retrieved and it is succeeded.
 */
@@ -1511,38 +1524,99 @@ IsUpdateStatusSucceeded(bool &isSucceede
   fread(buf, sizeof(buf), 1, file);
 
   const char kSucceeded[] = "succeeded";
   isSucceeded = strncmp(buf, kSucceeded, 
                         sizeof(kSucceeded) - 1) == 0;
   return true;
 }
 
-#ifdef XP_WIN
 static void 
 WaitForServiceFinishThread(void *param)
 {
   // We wait at most 10 minutes, we already waited 5 seconds previously
   // before deciding to show this UI.
   WaitForServiceStop(SVC_NAME, 595);
   LOG(("calling QuitProgressUI\n"));
   QuitProgressUI();
 }
 #endif
 
+/**
+ * This function reads in the ACCEPTED_MAR_CHANNEL_IDS from update-settings.ini
+ *
+ * @param path    The path to the ini file that is to be read
+ * @param results A pointer to the location to store the read strings
+ * @return OK on success
+ */
+static int
+ReadMARChannelIDs(const NS_tchar *path, MARChannelStringTable *results)
+{
+  const unsigned int kNumStrings = 1;
+  const char *kUpdaterKeys = "ACCEPTED_MAR_CHANNEL_IDS\0";
+  char updater_strings[kNumStrings][MAX_TEXT_LEN];
+
+  int result = ReadStrings(path, kUpdaterKeys, kNumStrings,
+                           updater_strings, "Settings");
+
+  strncpy(results->MARChannelID, updater_strings[0], MAX_TEXT_LEN - 1);
+  results->MARChannelID[MAX_TEXT_LEN - 1] = 0;
+
+  return result;
+}
+
+struct UpdateThreadData 
+{
+  UpdateThreadData(bool performMARChecks) :
+    mPerformMARChecks(performMARChecks)
+  {
+  }
+
+  bool mPerformMARChecks;
+};
+
 static void
 UpdateThreadFunc(void *param)
 {
+  UpdateThreadData *threadData = reinterpret_cast<UpdateThreadData*>(param);
+  bool performMARChecks = threadData && threadData->mPerformMARChecks;
+  delete threadData;
+  
   // open ZIP archive and process...
-
+  int rv;
   NS_tchar dataFile[MAXPATHLEN];
   NS_tsnprintf(dataFile, sizeof(dataFile)/sizeof(dataFile[0]),
                NS_T("%s/update.mar"), gSourcePath);
 
-  int rv = gArchiveReader.Open(dataFile);
+  rv = gArchiveReader.Open(dataFile);
+
+  if (performMARChecks) {
+#ifdef MOZ_VERIFY_MAR_SIGNATURE
+    if (rv == OK) {
+      rv = gArchiveReader.VerifySignature();
+    }
+
+    if (rv == OK) {
+      NS_tchar updateSettingsPath[MAX_TEXT_LEN];
+      NS_tsnprintf(updateSettingsPath, 
+                   sizeof(updateSettingsPath) / sizeof(updateSettingsPath[0]),
+                   NS_T("%supdate-settings.ini"), gDestPath);
+      MARChannelStringTable MARStrings;
+      if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK) {
+        // If we can't read from update-settings.ini then we shouldn't impose
+        // a MAR restriction.  Some installations won't even include this file.
+        MARStrings.MARChannelID[0] = '\0';
+      }
+
+      rv = gArchiveReader.VerifyProductInformation(MARStrings.MARChannelID,
+                                                   MOZ_APP_VERSION);
+    }
+#endif
+  }
+
   if (rv == OK) {
     rv = DoUpdate();
     gArchiveReader.Close();
   }
 
   if (rv) {
     LOG(("failed: %d\n", rv));
   }
@@ -1610,25 +1684,17 @@ int NS_main(int argc, NS_tchar **argv)
 
   // We never want the service to be used unless we build with
   // the maintenance service.
 #ifdef MOZ_MAINTENANCE_SERVICE
   IsUpdateStatusPending(useService);
   // Our tests run with a different apply directory for each test.
   // We use this registry key on our test slaves to store the 
   // allowed name/issuers.
-  HKEY testOnlyFallbackKey;
-  if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
-                    TEST_ONLY_FALLBACK_KEY_PATH, 0,
-                    KEY_READ | KEY_WOW64_64KEY, 
-                    &testOnlyFallbackKey) == ERROR_SUCCESS) {
-    testOnlyFallbackKeyExists = true;
-    RegCloseKey(testOnlyFallbackKey);
-  }
-
+  testOnlyFallbackKeyExists = DoesFallbackKeyExist();
 #endif
 #endif
 
   if (!WriteStatusApplying()) {
     LOG(("failed setting status to 'applying'\n"));
     return 1;
   }
 
@@ -2004,17 +2070,17 @@ int NS_main(int argc, NS_tchar **argv)
     CopyFileW(argv[callbackIndex], gCallbackBackupPath, false);
 
     // Since the process may be signaled as exited by WaitForSingleObject before
     // the release of the executable image try to lock the main executable file
     // multiple times before giving up.
     const int max_retries = 10;
     int retries = 1;
     do {
-      // By opening a file handle wihout FILE_SHARE_READ to the callback
+      // By opening a file handle without FILE_SHARE_READ to the callback
       // executable, the OS will prevent launching the process while it is
       // being updated.
       callbackFile = CreateFileW(argv[callbackIndex],
                                  DELETE | GENERIC_WRITE,
                                  // allow delete, rename, and write
                                  FILE_SHARE_DELETE | FILE_SHARE_WRITE,
                                  NULL, OPEN_EXISTING, 0, NULL);
       if (callbackFile != INVALID_HANDLE_VALUE)
@@ -2052,17 +2118,17 @@ int NS_main(int argc, NS_tchar **argv)
     NS_tmkdir(DELETE_DIR, 0755);
   }
 #endif /* XP_WIN */
 
   // Run update process on a background thread.  ShowProgressUI may return
   // before QuitProgressUI has been called, so wait for UpdateThreadFunc to
   // terminate.
   Thread t;
-  if (t.Run(UpdateThreadFunc, NULL) == 0) {
+  if (t.Run(UpdateThreadFunc, new UpdateThreadData(usingService)) == 0) {
     ShowProgressUI();
   }
   t.Join();
 
 #ifdef XP_WIN
   if (argc > callbackIndex) {
     CloseHandle(callbackFile);
     // Remove the copy of the callback executable.
@@ -2550,34 +2616,32 @@ int DoUpdate()
     LOG(("DoUpdate: error opening manifest file: " LOG_S "\n", manifest));
     return READ_ERROR;
   }
 
 
   ActionList list;
   NS_tchar *line;
   bool isFirstAction = true;
-  bool isComplete = false;
 
   while((line = mstrtok(kNL, &rb)) != 0) {
     // skip comments
     if (*line == NS_T('#'))
       continue;
 
     NS_tchar *token = mstrtok(kWhitespace, &line);
     if (!token) {
       LOG(("DoUpdate: token not found in manifest\n"));
       return PARSE_ERROR;
     }
 
     if (isFirstAction && NS_tstrcmp(token, NS_T("type")) == 0) {
       const NS_tchar *type = mstrtok(kQuote, &line);
       LOG(("UPDATE TYPE " LOG_S "\n", type));
       if (NS_tstrcmp(type, NS_T("complete")) == 0) {
-        isComplete = true;
         rv = AddPreCompleteActions(&list);
         if (rv)
           return rv;
       }
       isFirstAction = false;
       continue;
     }
 
--- a/toolkit/mozapps/update/updater/updater.rc
+++ b/toolkit/mozapps/update/updater/updater.rc
@@ -33,16 +33,35 @@ 1                       RT_MANIFEST     
 // Icon
 //
 
 IDI_DIALOG ICON "updater.ico"
 
 
 /////////////////////////////////////////////////////////////////////////////
 //
+// Embedded certificates for allowed MARs
+//
+
+#if defined(MAR_SIGNING_RELEASE_BETA)
+IDR_PRIMARY_CERT TYPE_CERT "release_primary.der"
+IDR_BACKUP_CERT TYPE_CERT  "release_secondary.der"
+#elif defined(MAR_SIGNING_AURORA_NIGHTLY)
+IDR_PRIMARY_CERT TYPE_CERT "nightly_aurora_level3_primary.der"
+IDR_BACKUP_CERT TYPE_CERT  "nightly_aurora_level3_secondary.der"
+#else
+IDR_PRIMARY_CERT TYPE_CERT "dep1.der"
+IDR_BACKUP_CERT TYPE_CERT  "dep2.der"
+#endif
+
+IDR_XPCSHELL_CERT TYPE_CERT "xpcshellCertificate.der"
+ 
+
+/////////////////////////////////////////////////////////////////////////////
+//
 // Embedded an identifier to uniquely identiy this as a Mozilla updater.
 //
 
 STRINGTABLE
 {
   IDS_UPDATER_IDENTITY, "moz-updater.exe-4cdccec4-5ee0-4a06-9817-4cd899a9db49"
 } 
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..185b2dff4a696f6ec9b7330eeefddd4d57617eac
GIT binary patch
literal 677
zc$_n6Vp?d>#MrrjnTe5!iIrix_HIW5UN%mxHjlRNyo`*jtPBQ1h5`nBY|No7%);!x
zl?u+OMI{Du;=G1N21Z5(1}4Vl#uiavt|^?0RZkP660*^ZtPIRejQk8haW1ANMn;B3
z!VBJOulye6aBuR<$&GfEoYz$i0@!_98j_~1?yowL)5{}tDd9=N+1r8zoX1OQ*A*D4
zw)9Qq_Wbc8OuF}hS<<yFi*h~QytCbICu~yDBH?Qzao%m(3duJ{LF%C=*3Mtd6cZd)
z(7w^|kC-@1Zl&lopWpgPj|~^cx387$Dqa0hL+~|M!nLn*emMr4{~j+~R8jCC<eXgK
z*Zf0!4_pb?y%N1pdhhpl0dEqwmHuwxd+K~SS1@i<+`-?BL6x^TPyR`JA)1t7RJ@XH
zQR0*o(WpC3@$M=Ytz%BFvJ&yy#wQhWyszDKp`ol)!*5qc)6A~*+^O<9Y-S1@X0mUZ
zwdt^lwc&myW<~}^w7_Qu1&pVy%0ag7MLGXxTijd5;KUuZq$hPkp<47ThM7Gwo(CJR
zXz%nr=g2<!gRaaMm;Vn6Zu3>Y^7%Gv@9Fn(b8~jjGE90_`sg_Oq-nQr^50}x^`h(q
zzecC<TBchQm)`sH`qgyZ8Hew@FzuRgwOKD~&bJ4$N1i5TicDx{ND5=&3H#W=B<-^H
z&h$t751;-kZ!6UB<L|Y*a~k$uUKo2rD%jidsYlp};uEUux8^-u^7l+^c<l?3Duv`#
z(`KoM3JJ<ocYie4zJOhIT~Mj_)U}*j*R#mZDT#48pLlCi#YGO?iMCT2_xZ-3ZvMhz
idHvOnfNu!_Gq$fdvq8~w$2X_tWwLu)7rVXwHW>gHs~D94
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -75,17 +75,16 @@ endif
 ifndef MOZ_NATIVE_JPEG
 tier_platform_dirs	+= media/libjpeg
 endif
 
 ifdef MOZ_UPDATER
 ifndef MOZ_NATIVE_BZ2
 tier_platform_dirs += modules/libbz2
 endif
-tier_platform_dirs += modules/libmar
 tier_platform_dirs += other-licenses/bsdiff
 endif
 
 tier_platform_dirs	+= gfx/qcms
 
 #
 # "gecko" - core components
 #
@@ -201,24 +200,28 @@ tier_platform_dirs	+= xpfe/components
 ifdef MOZ_ENABLE_XREMOTE
 tier_platform_dirs += widget/xremoteclient
 endif
 
 ifdef MOZ_SPELLCHECK
 tier_platform_dirs	+= extensions/spellcheck
 endif
 
-tier_platform_dirs	+= toolkit
-
 ifdef MOZ_PSM
 tier_platform_dirs	+= security/manager
 else
 tier_platform_dirs	+= security/manager/boot/public security/manager/ssl/public
 endif
 
+ifdef MOZ_UPDATER
+tier_platform_dirs += modules/libmar
+endif
+
+tier_platform_dirs	+= toolkit
+
 ifdef MOZ_PREF_EXTENSIONS
 tier_platform_dirs += extensions/pref
 endif
 
 tier_platform_dirs += services/crypto/component
 
 tier_platform_dirs += startupcache
 
--- a/xpcom/glue/nsVersionComparator.cpp
+++ b/xpcom/glue/nsVersionComparator.cpp
@@ -34,17 +34,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsVersionComparator.h"
 
 #include <stdlib.h>
 #include <string.h>
-#ifdef XP_WIN
+#if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL)
 #include <wchar.h>
 #include "nsStringGlue.h"
 #endif
 
 struct VersionPart {
   PRInt32     numA;
 
   const char *strB;    // NOT null-terminated, can be a null pointer