summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_signed_apps
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/unit/test_signed_apps')
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/README.md18
-rwxr-xr-xsecurity/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh213
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/nss_ctypes.py129
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/sign_b2g_app.py174
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/icon-128.pngbin0 -> 1633 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/index.html6
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/manifest.webapp8
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/icon-128.pngbin0 -> 1633 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/index.html6
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/manifest.webapp10
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/index.html10
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/manifest.webapp9
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/privileged-app-test-1.0.zipbin0 -> 23169 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/test-privileged-app-test-1.0.zipbin0 -> 22750 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/trusted_ca1.derbin0 -> 898 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app_1.zipbin0 -> 4220 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/unsigned_app_1.zipbin0 -> 2282 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/valid_app_1.zipbin0 -> 4222 bytes
18 files changed, 583 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/README.md b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/README.md
new file mode 100644
index 000000000..5c6201534
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/README.md
@@ -0,0 +1,18 @@
+This file contains the scripts and binary files needed to regenerate the signed
+files used on the signed apps test.
+
+Prerequisites:
+
+* NSS 3.4 or higher.
+* Python 2.7 (should work with 2.6 also)
+* Bash
+
+Usage:
+
+Run
+
+./create_test_files.sh
+
+The new test files will be created at the ./testApps directory. Just copy the
+contents of this directory to the test directory (dom/apps/tests/signed and
+security/manager/ssl/tests/unit) to use the new files.
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh
new file mode 100755
index 000000000..e211c3685
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh
@@ -0,0 +1,213 @@
+#!/bin/bash
+
+export NSS_DEFAULT_DB_TYPE=sql
+
+export BASE_PATH=`dirname $0`
+export SIGN_SCR_LOC=.
+export APPS_TEST_LOC=../../../../../../../dom/apps/tests/signed
+export TOOLKIT_WEBAPPS_TEST_LOC=../../../../../../../toolkit/webapps/tests/data/
+
+# Creates the entry zip files (unsigned apps) from the source directories
+packageApps() {
+APPS="unsigned_app_1 unsigned_app_origin unsigned_app_origin_toolkit_webapps"
+OLD_PWD=`pwd`
+cd ${BASE_PATH}
+for i in $APPS
+do
+ echo "Creating $i.zip"
+ cd $i && zip -r ../$i.zip . && cd ..
+done
+cd ${OLD_PWD}
+}
+
+
+# Function to create a signing database
+# Parameters:
+# $1: Output directory (where the DB will be created)
+createDb() {
+
+ db=$1
+
+ mkdir -p $db
+
+ # Insecure by design, so... please don't use this for anything serious
+ passwordfile=$db/passwordfile
+
+ echo insecurepassword > $passwordfile
+ certutil -d $db -N -f $passwordfile 2>&1 >/dev/null
+
+}
+
+# Add a CA cert and a signing cert to the database
+# Arguments:
+# $1: DB directory
+# $2: CA CN (don't include the CN=, just the value)
+# $3: Signing Cert CN (don't include the CN=, just the value)
+# $4: CA short name (don't use spaces!)
+# $5: Signing Cert short name (don't use spaces!)
+addCerts() {
+ org="O=Examplla Corporation,L=Mountain View,ST=CA,C=US"
+ ca_subj="CN=${2},${org}"
+ ee_subj="CN=${3},${org}"
+
+ noisefile=/tmp/noise.$$
+ head -c 32 /dev/urandom > $noisefile
+
+ ca_responses=/tmp/caresponses.$$
+ ee_responses=/tmp/earesponses
+
+ echo y > $ca_responses # Is this a CA?
+ echo >> $ca_responses # Accept default path length constraint (no constraint)
+ echo y >> $ca_responses # Is this a critical constraint?
+ echo n > $ee_responses # Is this a CA?
+ echo >> $ee_responses # Accept default path length constraint (no constraint)
+ echo y >> $ee_responses # Is this a critical constraint?
+
+ make_cert="certutil -d $db -f $passwordfile -S -g 2048 -Z SHA256 \
+ -z $noisefile -y 3 -2 --extKeyUsage critical,codeSigning"
+ $make_cert -v 480 -n ${4} -m 1 -s "$ca_subj" \
+ --keyUsage critical,certSigning -t ",,CTu" -x < $ca_responses 2>&1 >/dev/null
+ $make_cert -v 240 -n ${5} -c ${4} -m 2 -s "$ee_subj" \
+ --keyUsage critical,digitalSignature -t ",,," < $ee_responses 2>&1 >/dev/null
+
+ # In case we want to inspect the generated certs
+
+ # Also, we'll need this one later on
+ certutil -d $db -L -n ${4} -r -o $db/${4}.der
+ certutil -d $db -L -n ${5} -r -o $db/${5}.der
+
+ rm -f $noisefile $ee_responses $ca_responses
+}
+
+
+# Signs an app
+# Parameters:
+# $1: Database directory
+# $2: Unsigned ZIP file path
+# $3: Signed ZIP file path
+# $4: Store ID for the signed App
+# $5: Version of the signed App
+# $6: Nickname of the signing certificate
+signApp() {
+
+ db=$1
+
+ # Once again, this is INSECURE. It doesn't matter here but
+ # DON'T use this for anything production related
+ passwordfile=$db/passwordfile
+
+ python ${BASE_PATH}/${SIGN_SCR_LOC}/sign_b2g_app.py -d $db -f $passwordfile \
+ -k ${6} -i ${2} -o ${3} -S ${4} -V ${5}
+}
+
+DB_PATH=${BASE_PATH}/signingDB
+TEST_APP_PATH=${BASE_PATH}/testApps
+
+echo "Warning! The directories ${DB_PATH} and ${TEST_APP_PATH} will be erased!"
+echo "Do you want to proceed anyway?"
+select answer in "Yes" "No"
+do
+ case $answer in
+ Yes) break;;
+ No) exit 1;;
+ esac
+done
+
+rm -rf ${DB_PATH} ${TEST_APP_PATH}
+
+TRUSTED_EE=trusted_ee1
+UNTRUSTED_EE=untrusted_ee1
+TRUSTED_CA=trusted_ca1
+UNTRUSTED_CA=untrusted_ca1
+
+# First, we'll create a new couple of signing DBs
+createDb $DB_PATH
+addCerts $DB_PATH "Valid CA" "Store Cert" trusted_ca1 ${TRUSTED_EE}
+addCerts $DB_PATH "Invalid CA" "Invalid Cert" ${UNTRUSTED_CA} ${UNTRUSTED_EE}
+
+# Then we'll create the unsigned apps
+echo "Creating unsigned apps"
+packageApps
+
+# And then we'll create all the test apps...
+mkdir -p ${TEST_APP_PATH}
+
+# We need:
+# A valid signed file, with two different versions:
+# valid_app_1.zip
+# valid_app_2.zip
+VALID_UID=`uuidgen`
+signApp $DB_PATH ${BASE_PATH}/unsigned_app_1.zip \
+ $TEST_APP_PATH/valid_app_1.zip \
+ $VALID_UID 1 ${TRUSTED_EE}
+signApp $DB_PATH ${BASE_PATH}/unsigned_app_1.zip \
+ $TEST_APP_PATH/valid_app_2.zip \
+ $VALID_UID 2 ${TRUSTED_EE}
+
+
+# A corrupt_package:
+# corrupt_app_1.zip
+# A corrupt package is a package with a entry modified, for example...
+CURDIR=`pwd`
+export TEMP_DIR=$TEST_APP_PATH/aux_unzip_$$
+mkdir -p $TEMP_DIR
+cd $TEMP_DIR
+unzip ../valid_app_1.zip 2>&1 >/dev/null
+echo " - " >> index.html
+zip -r ../corrupt_app_1.zip * 2>&1 >/dev/null
+cd $CURDIR
+rm -rf $TEMP_DIR
+
+# A file signed by a unknown issuer
+# unknown_issuer_app_1.zip
+INVALID_UID=`uuidgen`
+signApp $DB_PATH ${BASE_PATH}/unsigned_app_1.zip \
+ $TEST_APP_PATH/unknown_issuer_app_1.zip \
+ $INVALID_UID 1 ${UNTRUSTED_EE}
+
+# And finally a priviledged signed file that includes the origin on the manifest
+# to avoid that reverting again
+PRIV_UID=`uuidgen`
+signApp $DB_PATH ${BASE_PATH}/unsigned_app_origin.zip \
+ $TEST_APP_PATH/origin_app_1.zip \
+ $PRIV_UID 1 ${TRUSTED_EE}
+
+# A privileged signed app needed for a toolkit/webapps test
+PRIV_TOOLKIT_UID=`uuidgen`
+signApp $DB_PATH ${BASE_PATH}/unsigned_app_origin_toolkit_webapps.zip \
+ $TEST_APP_PATH/custom_origin.zip \
+ $PRIV_TOOLKIT_UID 1 ${TRUSTED_EE}
+
+# Now let's copy the trusted cert to the app directory so we have everything
+# on the same place...
+cp ${DB_PATH}/${TRUSTED_CA}.der ${TEST_APP_PATH}
+
+cat <<EOF
+
+All done. The new test files are in ${TEST_APP_PATH}. You should copy the
+contents of that directory to the dom/apps/tests/signed directory and to
+the security/manager/ssl/tests/unit/test_signed_apps (which should be the
+parent of this directory) to install them.
+
+EOF
+
+echo "Do you wish me to do that for you now?"
+select answer in "Yes" "No"
+do
+ case $answer in
+ Yes) break;;
+ No) echo "Ok, not installing the new files"
+ echo "You should run: "
+ echo cp ${TEST_APP_PATH}/* ${BASE_PATH}/${APPS_TEST_LOC}
+ echo cp ${TEST_APP_PATH}/* ${TEST_APP_PATH}/../unsigned_app_1.zip ${BASE_PATH}/..
+ echo cp ${TEST_APP_PATH}/* ${BASE_PATH}/${TOOLKIT_WEBAPPS_TEST_LOC}
+ echo "to install them"
+ exit 0;;
+ esac
+done
+
+cp ${TEST_APP_PATH}/* ${BASE_PATH}/${APPS_TEST_LOC}
+cp ${TEST_APP_PATH}/* ${TEST_APP_PATH}/../unsigned_app_1.zip ${BASE_PATH}/..
+cp ${TEST_APP_PATH}/* ${BASE_PATH}/${TOOLKIT_WEBAPPS_TEST_LOC}
+
+echo "Done!"
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/nss_ctypes.py b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/nss_ctypes.py
new file mode 100644
index 000000000..35ca92959
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/nss_ctypes.py
@@ -0,0 +1,129 @@
+from ctypes import *
+import os
+import sys
+
+if sys.platform == 'darwin':
+ libprefix = "lib"
+ libsuffix = ".dylib"
+elif os.name == 'posix':
+ libprefix = "lib"
+ libsuffix = ".so"
+else: # assume windows
+ libprefix = ""
+ libsuffix = ".dll"
+
+plc = cdll.LoadLibrary(libprefix + "plc4" + libsuffix)
+nspr = cdll.LoadLibrary(libprefix + "nspr4" + libsuffix)
+nss = cdll.LoadLibrary(libprefix + "nss3" + libsuffix)
+smime = cdll.LoadLibrary(libprefix + "smime3" + libsuffix)
+
+nspr.PR_GetError.argtypes = []
+nspr.PR_GetError.restype = c_int32
+nspr.PR_ErrorToName.argtypes = [c_int32]
+nspr.PR_ErrorToName.restype = c_char_p
+
+def raise_if_not_SECSuccess(rv):
+ SECSuccess = 0
+ if (rv != SECSuccess):
+ raise ValueError(nspr.PR_ErrorToName(nspr.PR_GetError()))
+
+def raise_if_NULL(p):
+ if not p:
+ raise ValueError(nspr.PR_ErrorToName(nspr.PR_GetError()))
+ return p
+
+PRBool = c_int
+SECStatus = c_int
+
+# from secoidt.h
+SEC_OID_SHA1 = 4
+
+# from certt.h
+certUsageObjectSigner = 6
+
+class SECItem(Structure):
+ _fields_ = [("type", c_int),
+ ("data", c_char_p),
+ ("len", c_uint)]
+
+nss.NSS_Init.argtypes = [c_char_p]
+nss.NSS_Init.restype = SECStatus
+def NSS_Init(db_dir):
+ nss.NSS_Init.argtypes = [c_char_p]
+ nss.NSS_Init.restype = SECStatus
+ raise_if_not_SECSuccess(nss.NSS_Init(db_dir))
+
+nss.NSS_Shutdown.argtypes = []
+nss.NSS_Shutdown.restype = SECStatus
+def NSS_Shutdown():
+ raise_if_not_SECSuccess(nss.NSS_Shutdown())
+
+PK11PasswordFunc = CFUNCTYPE(c_char_p, c_void_p, PRBool, c_char_p)
+
+# pass the result of this as the wincx parameter when a wincx is required
+nss.PK11_SetPasswordFunc.argtypes = [PK11PasswordFunc]
+nss.PK11_SetPasswordFunc.restype = None
+
+# Set the return type as *void so Python doesn't touch it
+plc.PL_strdup.argtypes = [c_char_p]
+plc.PL_strdup.restype = c_void_p
+def SetPasswordContext(password):
+ def callback(slot, retry, arg):
+ return plc.PL_strdup(password)
+ wincx = PK11PasswordFunc(callback)
+ nss.PK11_SetPasswordFunc(wincx)
+ return wincx
+
+nss.CERT_GetDefaultCertDB.argtypes = []
+nss.CERT_GetDefaultCertDB.restype = c_void_p
+def CERT_GetDefaultCertDB():
+ return raise_if_NULL(nss.CERT_GetDefaultCertDB())
+
+nss.PK11_FindCertFromNickname.argtypes = [c_char_p, c_void_p]
+nss.PK11_FindCertFromNickname.restype = c_void_p
+def PK11_FindCertFromNickname(nickname, wincx):
+ return raise_if_NULL(nss.PK11_FindCertFromNickname(nickname, wincx))
+
+nss.CERT_DestroyCertificate.argtypes = [c_void_p]
+nss.CERT_DestroyCertificate.restype = None
+def CERT_DestroyCertificate(cert):
+ nss.CERT_DestroyCertificate(cert)
+
+smime.SEC_PKCS7CreateSignedData.argtypes = [c_void_p, c_int, c_void_p,
+ c_int, c_void_p,
+ c_void_p, c_void_p]
+smime.SEC_PKCS7CreateSignedData.restype = c_void_p
+def SEC_PKCS7CreateSignedData(cert, certusage, certdb, digestalg, digest, wincx):
+ item = SECItem(0, c_char_p(digest), len(digest))
+ return raise_if_NULL(smime.SEC_PKCS7CreateSignedData(cert, certusage, certdb,
+ digestalg,
+ pointer(item),
+ None, wincx))
+
+smime.SEC_PKCS7AddSigningTime.argtypes = [c_void_p]
+smime.SEC_PKCS7AddSigningTime.restype = SECStatus
+def SEC_PKCS7AddSigningTime(p7):
+ raise_if_not_SECSuccess(smime.SEC_PKCS7AddSigningTime(p7))
+
+smime.SEC_PKCS7IncludeCertChain.argtypes = [c_void_p, c_void_p]
+smime.SEC_PKCS7IncludeCertChain.restype = SECStatus
+def SEC_PKCS7IncludeCertChain(p7, wincx):
+ raise_if_not_SECSuccess(smime.SEC_PKCS7IncludeCertChain(p7, wincx))
+
+SEC_PKCS7EncoderOutputCallback = CFUNCTYPE(None, c_void_p, c_void_p, c_long)
+smime.SEC_PKCS7Encode.argtypes = [c_void_p, SEC_PKCS7EncoderOutputCallback,
+ c_void_p, c_void_p, c_void_p, c_void_p]
+smime.SEC_PKCS7Encode.restype = SECStatus
+def SEC_PKCS7Encode(p7, bulkkey, wincx):
+ outputChunks = []
+ def callback(chunks, data, len):
+ outputChunks.append(string_at(data, len))
+ callbackWrapper = SEC_PKCS7EncoderOutputCallback(callback)
+ raise_if_not_SECSuccess(smime.SEC_PKCS7Encode(p7, callbackWrapper,
+ None, None, None, wincx))
+ return "".join(outputChunks)
+
+smime.SEC_PKCS7DestroyContentInfo.argtypes = [c_void_p]
+smime.SEC_PKCS7DestroyContentInfo.restype = None
+def SEC_PKCS7DestroyContentInfo(p7):
+ smime.SEC_PKCS7DestroyContentInfo(p7)
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/sign_b2g_app.py b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/sign_b2g_app.py
new file mode 100644
index 000000000..89b385a9b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/sign_b2g_app.py
@@ -0,0 +1,174 @@
+import argparse
+from base64 import b64encode
+from hashlib import sha1
+import sys
+import zipfile
+import ctypes
+
+import nss_ctypes
+
+# Change the limits in JarSignatureVerification.cpp when you change the limits
+# here.
+max_entry_uncompressed_len = 100 * 1024 * 1024
+max_total_uncompressed_len = 500 * 1024 * 1024
+max_entry_count = 100 * 1000
+max_entry_filename_len = 1024
+max_mf_len = max_entry_count * 50
+max_sf_len = 1024
+
+
+
+def nss_load_cert(nss_db_dir, nss_password, cert_nickname):
+ nss_ctypes.NSS_Init(nss_db_dir)
+ try:
+ wincx = nss_ctypes.SetPasswordContext(nss_password)
+ cert = nss_ctypes.PK11_FindCertFromNickname(cert_nickname, wincx)
+ return (wincx, cert)
+ except:
+ nss_ctypes.NSS_Shutdown()
+ raise
+
+def nss_create_detached_signature(cert, dataToSign, wincx):
+ certdb = nss_ctypes.CERT_GetDefaultCertDB()
+ p7 = nss_ctypes.SEC_PKCS7CreateSignedData(cert,
+ nss_ctypes.certUsageObjectSigner,
+ certdb,
+ nss_ctypes.SEC_OID_SHA1,
+ sha1(dataToSign).digest(),
+ wincx )
+ try:
+ nss_ctypes.SEC_PKCS7AddSigningTime(p7)
+ nss_ctypes.SEC_PKCS7IncludeCertChain(p7, wincx)
+ return nss_ctypes.SEC_PKCS7Encode(p7, None, wincx)
+ finally:
+ nss_ctypes.SEC_PKCS7DestroyContentInfo(p7)
+
+# We receive a ids_json string for the toBeSigned app
+def sign_zip(in_zipfile_name, out_zipfile_name, cert, wincx, ids_json):
+ mf_entries = []
+ seen_entries = set()
+
+ total_uncompressed_len = 0
+ entry_count = 0
+ with zipfile.ZipFile(out_zipfile_name, 'w') as out_zip:
+ with zipfile.ZipFile(in_zipfile_name, 'r') as in_zip:
+ for entry_info in in_zip.infolist():
+ name = entry_info.filename
+
+ # Check for reserved and/or insane (potentially malicious) names
+ if name.endswith("/"):
+ pass
+ # Do nothing; we don't copy directory entries since they are just a
+ # waste of space.
+ elif name.lower().startswith("meta-inf/"):
+ # META-INF/* is reserved for our use
+ raise ValueError("META-INF entries are not allowed: %s" % (name))
+ elif len(name) > max_entry_filename_len:
+ raise ValueError("Entry's filename is too long: %s" % (name))
+ # TODO: elif name has invalid characters...
+ elif name in seen_entries:
+ # It is possible for a zipfile to have duplicate entries (with the exact
+ # same filenames). Python's zipfile module accepts them, but our zip
+ # reader in Gecko cannot do anything useful with them, and there's no
+ # sane reason for duplicate entries to exist, so reject them.
+ raise ValueError("Duplicate entry in input file: %s" % (name))
+ else:
+ entry_count += 1
+ if entry_count > max_entry_count:
+ raise ValueError("Too many entries in input archive")
+
+ seen_entries.add(name)
+
+ # Read in the input entry, but be careful to avoid going over the
+ # various limits we have, to minimize the likelihood that we'll run
+ # out of memory. Note that we can't use the length from entry_info
+ # because that might not be accurate if the input zip file is
+ # maliciously crafted to contain misleading metadata.
+ with in_zip.open(name, 'r') as entry_file:
+ contents = entry_file.read(max_entry_uncompressed_len + 1)
+ if len(contents) > max_entry_uncompressed_len:
+ raise ValueError("Entry is too large: %s" % (name))
+ total_uncompressed_len += len(contents)
+ if total_uncompressed_len > max_total_uncompressed_len:
+ raise ValueError("Input archive is too large")
+
+ # Copy the entry, using the same compression as used in the input file
+ out_zip.writestr(entry_info, contents)
+
+ # Add the entry to the manifest we're building
+ mf_entries.append('Name: %s\nSHA1-Digest: %s\n'
+ % (name, b64encode(sha1(contents).digest())))
+ if (ids_json):
+ mf_entries.append('Name: %s\nSHA1-Digest: %s\n'
+ % ("META-INF/ids.json", b64encode(sha1(ids_json).digest())))
+
+ mf_contents = 'Manifest-Version: 1.0\n\n' + '\n'.join(mf_entries)
+ if len(mf_contents) > max_mf_len:
+ raise ValueError("Generated MANIFEST.MF is too large: %d" % (len(mf_contents)))
+
+ sf_contents = ('Signature-Version: 1.0\nSHA1-Digest-Manifest: %s\n'
+ % (b64encode(sha1(mf_contents).digest())))
+ if len(sf_contents) > max_sf_len:
+ raise ValueError("Generated SIGNATURE.SF is too large: %d"
+ % (len(mf_contents)))
+
+ p7 = nss_create_detached_signature(cert, sf_contents, wincx)
+
+ # write the signature, SF, and MF
+ out_zip.writestr("META-INF/A.RSA", p7, zipfile.ZIP_DEFLATED)
+ out_zip.writestr("META-INF/A.SF", sf_contents, zipfile.ZIP_DEFLATED)
+ out_zip.writestr("META-INF/MANIFEST.MF", mf_contents, zipfile.ZIP_DEFLATED)
+ if (ids_json):
+ out_zip.writestr("META-INF/ids.json", ids_json, zipfile.ZIP_DEFLATED)
+
+def main():
+ parser = argparse.ArgumentParser(description='Sign a B2G app.')
+ parser.add_argument('-d', action='store',
+ required=True, help='NSS database directory')
+ parser.add_argument('-f', action='store',
+ type=argparse.FileType('rb'),
+ required=True, help='password file')
+ parser.add_argument('-k', action='store',
+ required=True, help="nickname of signing cert.")
+ parser.add_argument('-i', action='store', type=argparse.FileType('rb'),
+ required=True, help="input JAR file (unsigned)")
+ parser.add_argument('-o', action='store', type=argparse.FileType('wb'),
+ required=True, help="output JAR file (signed)")
+ parser.add_argument('-I', '--ids-file', action='store', type=argparse.FileType('rb'),
+ help="Path to the ids.json file", dest='I')
+ parser.add_argument('-S', '--storeId', action='store',
+ help="Store Id for the package", dest='S')
+ parser.add_argument('-V', '--storeVersion', action='store', type=int,
+ help="Package Version", dest='V')
+ args = parser.parse_args()
+
+ # Sadly nested groups and neccesarily inclusive groups (http://bugs.python.org/issue11588)
+ # are not implemented. Note that this means the automatic help is slighty incorrect
+ if not((not args.I and args.V and args.S) or (args.I and not args.V and not args.S)):
+ raise ValueError("Either -I or -S and -V must be specified")
+
+ if (args.I):
+ ids_contents = args.I.read(max_entry_uncompressed_len+1)
+ else:
+ ids_contents = '''{
+ "id": "%(id)s",
+ "version": %(version)d
+}
+''' % {"id": args.S, "version": args.V}
+ if len(ids_contents) > max_entry_uncompressed_len:
+ raise ValueError("Entry is too large: %s" % (name))
+
+ db_dir = args.d
+ password = args.f.readline().strip()
+ cert_nickname = args.k
+
+ (wincx, cert) = nss_load_cert(db_dir, password, cert_nickname)
+ try:
+ sign_zip(args.i, args.o, cert, wincx, ids_contents)
+ return 0
+ finally:
+ nss_ctypes.CERT_DestroyCertificate(cert)
+ nss_ctypes.NSS_Shutdown()
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/icon-128.png b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/icon-128.png
new file mode 100644
index 000000000..d6fd07a41
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/icon-128.png
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/index.html b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/index.html
new file mode 100644
index 000000000..2acf635e8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/index.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<html lang=en>
+<head><meta charset=utf-8><title>Simple App</title></head>
+<body><p>This is a Simple App.</body>
+</html>
+
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/manifest.webapp b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/manifest.webapp
new file mode 100644
index 000000000..447cd1e71
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/manifest.webapp
@@ -0,0 +1,8 @@
+{ "name": "Simple App"
+, "description": "A Simple Open Web App"
+, "launch_path": "tests/dom/apps/tests/signed_app.sjs"
+, "icons": { "128" : "icon-128.png" }
+, "installs_allowed_from": [ "https://marketplace.mozilla.com", "http://mochi.test:8888" ]
+, "version": 1
+, "default_locale": "en-US"
+}
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/icon-128.png b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/icon-128.png
new file mode 100644
index 000000000..d6fd07a41
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/icon-128.png
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/index.html b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/index.html
new file mode 100644
index 000000000..9f2adf57b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/index.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<html lang=en>
+<head><meta charset=utf-8><title>Simple App</title></head>
+<body><p>This is a Simple App.</body>
+</html>
+
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/manifest.webapp b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/manifest.webapp
new file mode 100644
index 000000000..d2393e3c3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/manifest.webapp
@@ -0,0 +1,10 @@
+{ "name": "Simple App",
+ "description": "A Simple Open Web App",
+ "type": "privileged",
+ "origin": "app://test.origin.privileged.app",
+ "launch_path": "tests/dom/apps/tests/signed_app.sjs",
+ "icons": { "128" : "icon-128.png" },
+ "installs_allowed_from": [ "https://marketplace.mozilla.com", "http://mochi.test:8888" ],
+ "version": 1,
+ "default_locale": "en-US"
+}
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/index.html b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/index.html
new file mode 100644
index 000000000..12c6881c2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/index.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test app</title>
+</head>
+<body>
+Test app:
+<iframe src="http://127.0.0.1:8888/chrome/toolkit/webapps/tests/app.sjs?appreq"></iframe>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/manifest.webapp b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/manifest.webapp
new file mode 100644
index 000000000..55103ca5a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/manifest.webapp
@@ -0,0 +1,9 @@
+{
+ "name": "Custom Origin Test",
+ "version": 1,
+ "size": 777,
+ "package_path": "custom_origin.zip",
+ "launch_path": "/index.html",
+ "origin": "app://test.origin.privileged.app",
+ "type": "privileged"
+}
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/privileged-app-test-1.0.zip b/security/manager/ssl/tests/unit/test_signed_apps/privileged-app-test-1.0.zip
new file mode 100644
index 000000000..3a12106c8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/privileged-app-test-1.0.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/test-privileged-app-test-1.0.zip b/security/manager/ssl/tests/unit/test_signed_apps/test-privileged-app-test-1.0.zip
new file mode 100644
index 000000000..ec137ea14
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/test-privileged-app-test-1.0.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der b/security/manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der
new file mode 100644
index 000000000..0200a7f35
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app_1.zip b/security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app_1.zip
new file mode 100644
index 000000000..374e45f6f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app_1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/unsigned_app_1.zip b/security/manager/ssl/tests/unit/test_signed_apps/unsigned_app_1.zip
new file mode 100644
index 000000000..731e050ab
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/unsigned_app_1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/valid_app_1.zip b/security/manager/ssl/tests/unit/test_signed_apps/valid_app_1.zip
new file mode 100644
index 000000000..74a35a420
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/valid_app_1.zip
Binary files differ