From 3b04dbe2e6716206ad3dd3a4b616ece87ea445e3 Mon Sep 17 00:00:00 2001 From: Robin Thunell Date: Mon, 22 Sep 2014 07:58:59 -0700 Subject: [PATCH] Bug 1059208 - Add scripts for signing manifest files of Trusted Hosted Apps r=dkeeler --- security/apps/Makefile.in | 15 ++ security/apps/gen_cert_header.py | 12 +- .../tests/unit/test_signed_manifest/README.md | 17 ++ .../test_signed_manifest/create_test_files.sh | 181 ++++++++++++++++++ .../unit/test_signed_manifest/manifest.webapp | 10 + .../unit/test_signed_manifest/nss_ctypes.py | 136 +++++++++++++ .../test_signed_manifest/sign_b2g_manifest.py | 76 ++++++++ .../testInvalidSignedManifest/manifest.sig | Bin 0 -> 1501 bytes .../testValidSignedManifest/manifest.sig | Bin 0 -> 1494 bytes .../unit/test_signed_manifest/trusted_ca1.der | Bin 0 -> 928 bytes 10 files changed, 446 insertions(+), 1 deletion(-) create mode 100644 security/manager/ssl/tests/unit/test_signed_manifest/README.md create mode 100755 security/manager/ssl/tests/unit/test_signed_manifest/create_test_files.sh create mode 100644 security/manager/ssl/tests/unit/test_signed_manifest/manifest.webapp create mode 100644 security/manager/ssl/tests/unit/test_signed_manifest/nss_ctypes.py create mode 100644 security/manager/ssl/tests/unit/test_signed_manifest/sign_b2g_manifest.py create mode 100644 security/manager/ssl/tests/unit/test_signed_manifest/testInvalidSignedManifest/manifest.sig create mode 100644 security/manager/ssl/tests/unit/test_signed_manifest/testValidSignedManifest/manifest.sig create mode 100644 security/manager/ssl/tests/unit/test_signed_manifest/trusted_ca1.der diff --git a/security/apps/Makefile.in b/security/apps/Makefile.in index a9e23da1931..9d65c6d7785 100644 --- a/security/apps/Makefile.in +++ b/security/apps/Makefile.in @@ -4,6 +4,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. GEN_CERT_HEADER = $(srcdir)/gen_cert_header.py +TEST_SSL_PATH = $(srcdir)/../manager/ssl/tests/unit/test_signed_manifest/ marketplace-prod-public.inc: marketplace-prod-public.crt $(GEN_CERT_HEADER) $(PYTHON) $(GEN_CERT_HEADER) marketplaceProdPublicRoot $< > $@ @@ -20,6 +21,18 @@ marketplace-dev-reviewers.inc: marketplace-dev-reviewers.crt $(GEN_CERT_HEADER) marketplace-stage.inc: marketplace-stage.crt $(GEN_CERT_HEADER) $(PYTHON) $(GEN_CERT_HEADER) marketplaceStageRoot $< > $@ +ifeq ($(shell test -s trusted-app-public.der; echo $$?),0) +TRUSTED_APP_PUBLIC=trusted-app-public.der +else +TRUSTED_APP_PUBLIC= +endif + +manifest-signing-root.inc: $(TRUSTED_APP_PUBLIC) $(GEN_CERT_HEADER) + $(PYTHON) $(GEN_CERT_HEADER) trustedAppPublicRoot $(TRUSTED_APP_PUBLIC) > $@ + +manifest-signing-test-root.inc: $(TEST_SSL_PATH)trusted_ca1.der $(GEN_CERT_HEADER) + $(PYTHON) $(GEN_CERT_HEADER) trustedAppTestRoot $< > $@ + xpcshell.inc: $(srcdir)/../manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der $(GEN_CERT_HEADER) $(PYTHON) $(GEN_CERT_HEADER) xpcshellRoot $< > $@ @@ -29,5 +42,7 @@ export:: \ marketplace-dev-public.inc \ marketplace-dev-reviewers.inc \ marketplace-stage.inc \ + manifest-signing-root.inc \ + manifest-signing-test-root.inc \ xpcshell.inc \ $(NULL) diff --git a/security/apps/gen_cert_header.py b/security/apps/gen_cert_header.py index 58534736ed5..f77e5ccf1a3 100644 --- a/security/apps/gen_cert_header.py +++ b/security/apps/gen_cert_header.py @@ -22,8 +22,18 @@ def create_header(array_name, in_filename): print "};" return 0 +def create_empty_header(array_name): + # mfbt/ArrayUtils.h will not be able to pick up the + # correct specialization for ArrayLength(const array[0]) + # so add a value of 0 which will fail cert verification + # just the same as an empty array + print "const uint8_t " + array_name + "[] = { 0x0 };" + return 0 + if __name__ == '__main__': - if len(sys.argv) < 3: + if len(sys.argv) < 2: print 'ERROR: usage: gen_cert_header.py array_name in_filename' sys.exit(1); + if len(sys.argv) == 2: + sys.exit(create_empty_header(sys.argv[1])) sys.exit(create_header(sys.argv[1], sys.argv[2])) diff --git a/security/manager/ssl/tests/unit/test_signed_manifest/README.md b/security/manager/ssl/tests/unit/test_signed_manifest/README.md new file mode 100644 index 00000000000..a442c705280 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_signed_manifest/README.md @@ -0,0 +1,17 @@ +This folder contains the scripts needed to generate signed manifest files +to verify the Trusted Hosted Apps concept. + +Prerequisites: + +* NSS 3.4 or higher. +* Python 2.7 (should work with 2.6 also) +* Bash +* OpenSSL + +Usage: + +Run + I) For usage info execute ./create_test_files.sh --help + + II) Upload the signed manifest.webapp and manifest.sig to the + application hosting server. diff --git a/security/manager/ssl/tests/unit/test_signed_manifest/create_test_files.sh b/security/manager/ssl/tests/unit/test_signed_manifest/create_test_files.sh new file mode 100755 index 00000000000..cddbe17ec1e --- /dev/null +++ b/security/manager/ssl/tests/unit/test_signed_manifest/create_test_files.sh @@ -0,0 +1,181 @@ +#!/bin/bash +# +# Mode: shell-script; sh-indentation:2; +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +export BASE_PATH=`dirname $0` +echo $BASE_PATH + +# location of the 'sign_b2g_manifest.py' script +export SIGN_SCR_PATH=. + +DB_PATH=${BASE_PATH}/signingDB +PASSWORD_FILE=${DB_PATH}/passwordfile +VALID_MANIFEST_PATH=${BASE_PATH}/testValidSignedManifest +INVALID_MANIFEST_PATH=${BASE_PATH}/testInvalidSignedManifest + +TRUSTED_EE=trusted_ee1 +UNTRUSTED_EE=untrusted_ee1 +TRUSTED_CA=trusted_ca1 +UNTRUSTED_CA=untrusted_ca1 + +# Print usage info +usage() { + echo + echo + tput bold + echo "NAME" + tput sgr0 + echo " create_test_files.sh - Signing a manifest for Trusted Hosted Apps." + echo + tput bold + echo "SYNOPSIS" + tput sgr0 + echo " create_test_files.sh" + echo " create_test_files.sh [--regenerate-test-certs]" + echo " create_test_files.sh [--help]" + echo + tput bold + echo "DESCRIPTION" + tput sgr0 + echo " The script signs a manifest for Trusted Hosted Apps if no parameter" + echo " is given and if the manifest file and a certificate database directory" + echo " is present in the current directory." + echo " Two directories ./testValidSignedManifest and ./testInvalidSignedManifest" + echo " are generated containing a manifest signature file each, signed with valid" + echo " and invalid certificates respectively." + echo " If the --regenerate-test-certs parameter is given, a new certificate database" + echo " directory is generated before the signing takes place." + echo " If the certificate database is not present and the --regenerate-test-certs" + echo " parameter is not given the script exits whithout any operations." + echo + tput bold + echo "OPTIONS" + echo " --regenerate-test-certs," + tput sgr0 + echo " Generates a test certificate database and then signs the manifest.webapp" + echo " file in the current directory." + echo + tput bold + echo " --help," + tput sgr0 + echo " Show this usage information." + echo +} + +# Function to create a signing database +# Parameters: +# $1: Output directory (where the DB will be created) +# $2: Password file +createDB() { + local db_path=${1} + local password_file=${2} + + mkdir -p ${db_path} + echo insecurepassword > ${password_file} + certutil -d ${db_path} -N -f ${password_file} 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!) +# $6: Password file +addCerts() { + local db_path=${1} + local password_file=${6} + + org="O=Example Trusted 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_path} -f ${password_file} -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 + + certutil -d ${db_path} -L -n ${4} -r -o ${SIGN_SCR_PATH}/${4}.der + + rm -f ${noisefile} ${ee_responses} ${ca_responses} +} + +# Signs a manifest +# Parameters: +# $1: Database directory +# $2: Unsigned manifest file path +# $3: Signed manifest file path +# $4: Nickname of the signing certificate +# $5: Password file +signManifest() { + local db_path=${1} + local password_file=${5} + + python ${BASE_PATH}/${SIGN_SCR_PATH}/sign_b2g_manifest.py -d ${db_path} \ + -f ${password_file} -k ${4} -i ${2} -o ${3} +} + +# Generate the necessary files to be used for the signing +generate_files() { + # First create a new couple of signing DBs + rm -rf ${DB_PATH} ${VALID_MANIFEST_PATH} ${INVALID_MANIFEST_PATH} + createDB ${DB_PATH} ${PASSWORD_FILE} + addCerts ${DB_PATH} "Trusted Valid CA" "Trusted Corp Cert" ${TRUSTED_CA} ${TRUSTED_EE} ${PASSWORD_FILE} + addCerts ${DB_PATH} "Trusted Invalid CA" "Trusted Invalid Cert" ${UNTRUSTED_CA} ${UNTRUSTED_EE} ${PASSWORD_FILE} +} + +#Start of execution +if [ ${1} ] && [ "${1}" == '--regenerate-test-certs' ]; then + generate_files +elif [ "${1}" == '--help' ]; then + usage + exit 1 +else + if [ -d ${DB_PATH} ]; then + rm -rf ${VALID_MANIFEST_PATH} ${INVALID_MANIFEST_PATH} + else + echo "Error! The directory ${DB_PATH} does not exist!" + echo "New certificate database must be created!" + usage $0 + exit 1 + fi +fi + +# Create all the test manifests +mkdir -p ${VALID_MANIFEST_PATH} +mkdir -p ${INVALID_MANIFEST_PATH} + +CURDIR=`pwd` +cd $CURDIR + +# Sign a manifest file with a known issuer +signManifest ${DB_PATH} ${BASE_PATH}/manifest.webapp \ + ${VALID_MANIFEST_PATH}/manifest.sig \ + ${TRUSTED_EE} ${PASSWORD_FILE} + +# Sign a manifest file with a unknown issuer +signManifest ${DB_PATH} ${BASE_PATH}/manifest.webapp \ + ${INVALID_MANIFEST_PATH}/manifest.sig \ + ${UNTRUSTED_EE} ${PASSWORD_FILE} + +echo "Done!" diff --git a/security/manager/ssl/tests/unit/test_signed_manifest/manifest.webapp b/security/manager/ssl/tests/unit/test_signed_manifest/manifest.webapp new file mode 100644 index 00000000000..ad477719b48 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_signed_manifest/manifest.webapp @@ -0,0 +1,10 @@ +{ "name": "Trusted App Example", + "description": "A Manifest for a Trusted Hosted Application", + "type": "trusted", + "launch_path": "/index.html", + "icons": { "128" : "icon-128.png" }, + "version": 1, + "csp" : "script-src https://www.example.com; style-src https://www.example.com", + "permissions": { "device-storage:videos":{ "access": "readonly" }, "device-storage:pictures":{ "access": "readwrite" } }, + "default_locale": "en-US" +} diff --git a/security/manager/ssl/tests/unit/test_signed_manifest/nss_ctypes.py b/security/manager/ssl/tests/unit/test_signed_manifest/nss_ctypes.py new file mode 100644 index 00000000000..393ca249f77 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_signed_manifest/nss_ctypes.py @@ -0,0 +1,136 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +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_manifest/sign_b2g_manifest.py b/security/manager/ssl/tests/unit/test_signed_manifest/sign_b2g_manifest.py new file mode 100644 index 00000000000..acc5ac2f441 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_signed_manifest/sign_b2g_manifest.py @@ -0,0 +1,76 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import argparse +from base64 import b64encode +from hashlib import sha1 +import sys +import ctypes +import nss_ctypes + +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) + +# Sign a manifest file +def sign_file(in_file, out_file, cert, wincx): + contents = in_file.read() + in_file.close() + + # generate base64 encoded string of the sha1 digest of the input file + in_file_hash = b64encode(sha1(contents).digest()) + + # sign the base64 encoded string with the given certificate + in_file_signature = nss_create_detached_signature(cert, in_file_hash, wincx) + + # write the content of the output file + out_file.write(in_file_signature) + out_file.close() + +def main(): + parser = argparse.ArgumentParser(description='Sign a B2G app manifest.') + 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 manifest file (unsigned)") + parser.add_argument('-o', action='store', type=argparse.FileType('wb'), + required=True, help="output manifest file (signed)") + args = parser.parse_args() + + db_dir = args.d + password = args.f.readline().strip() + cert_nickname = args.k + cert = None + + try: + nss_ctypes.NSS_Init(db_dir) + wincx = nss_ctypes.SetPasswordContext(password) + cert = nss_ctypes.PK11_FindCertFromNickname(cert_nickname, wincx) + sign_file(args.i, args.o, cert, wincx) + 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_manifest/testInvalidSignedManifest/manifest.sig b/security/manager/ssl/tests/unit/test_signed_manifest/testInvalidSignedManifest/manifest.sig new file mode 100644 index 0000000000000000000000000000000000000000..d5eee196c09c719b655c73a0eac4d42a5c300d4b GIT binary patch literal 1501 zcmXqLV!g@6snzDu_MMlJooPW6>nVdK)?-YJjE39>oNTPxe9TNztPBR+2t|wwnwS?F zG%?RLXkuzzz|6$R1VjeBa0QIqK=q|y?aZMpY{E>T!7vVoFq5;Rp_qXP8&HN_nAbPI zG_NEvGfyEbGqv1M#Xt!p&Lu4ET9KGrkdvwqQdC-8lA5C6oL^LsUzAvqnV)AUZ6FC! z#VjlYll9CiOU%gxs&X`t6X!KFF|af;Ft9K-H#Lb8=QT#=8kECaLUm`!8c0K&C4%9s z)S?oDCPoG1AYx=?U~XdMWdMqEFf}nUGCY{1o@eq+Ax>d|y6Oh*i)a4i3#80UZ(I_5 z<>?zq^RBmZa#OBnuI5eR`gKf7eBDA;cau{!?|Hj+N=7wD?=A@TeYkr5_RblK4gnLR zPdV**GNq#J*h|k2J4^nZi6<8K@~z*!F?x2!Uxka$58rn3{QNN6^8V5#-*Z=O3Ot^- zG)|gvC4)IzyWxUwN@uiwv8L%5*w)XUqNy6b)yK}H>rUq!yBFH=@;0sg3N!fbZCKq< znq6MW-0ZS`uH@VE%cNG_lX{V*xzv_%-5y2>>$8;$mfLK%Ub_3i3WnY}QWCRf*}a?L z_A1`)#MO?oi|V$8^_bLrikQq{&7VC*(;*;c$I<+fi=6dLjLeHI49pC8fPpA0%*gnk zg~@=yKn%oF1@U+cxY#(f*%(<_*_oLQ_(0I(VWz}?PvKp&5eRf zH9rSRC@>!{h)DSKvZ%!A#{A;^K+V+^^oa4IUD%bj?Ht2L&J!Q~%cQvn= z&1yeS;xqeQT!nhuHq8_{_TxwR-ygG1@A`YuYB%5HeQgs93U9cl>gHum?JIwCxL5KK zgV;rlH1~$jl{YrZ-+8d*5V!Ygug>WK=VJ|K9X6ihx@N^C2KHq|=lVpqE%o2OXo`W< zHYvjcoo zcvsIgd|hARBXaF|=z|kHyVabx%t1~B!0g4+#K=(7CfYek!doKE+<)rE=&WarJEt%+ zg?;?7>9*U;^qh#iOF3@ofxi@Q$;>q6S;!mq=))IPgS|EuZ*T4InR_PWn1HfxWLVb@ zwgU^R&R$!RoiDH@O-y^mjK&E$&DY%o%-m%5$WC%xFEBy2KJMA4yY9*rw;g6Vs589& zBKdGH8~gLw^E3{M`K0yil)I_ubtuN&;o+o2-;kHn#4l?tU|ngRy7-m$iBl@vS<1Io zHe{~klKql)ri{fxK_==?();+B&xYU27OlF?5mitm9lzCyZ_)QD&x`i1oLl>$jJumH0xp{WY+E@GTCf@yC3jnRX8Xy1w literal 0 HcmV?d00001 diff --git a/security/manager/ssl/tests/unit/test_signed_manifest/testValidSignedManifest/manifest.sig b/security/manager/ssl/tests/unit/test_signed_manifest/testValidSignedManifest/manifest.sig new file mode 100644 index 0000000000000000000000000000000000000000..996226351151e85877de8affd9e850e8b5d76521 GIT binary patch literal 1494 zcmXqLV!g!1snzDu_MMlJooPW6>tTZ?*8NP3jE39>oNTPxe9TNztPBR+2t|wwnwaMp zG%?RGXkuzuz|6$R1VjeBa0QIqK=s97?aZMpY{E>T!7vVoFq5;Rp_qXP8&HN_nAbPI zG_NEvGfyEbGqv1M#Xt!p&Lu4ET9KGrkdvwqQdC-8lA5C6oL^LsUzAvqnV)AUX&??# z#VjlUlMPGE$por#G>{YLH8e4>G%_%-Fg7zQ z3eKrTB?e853do_u$jZRn#K_A46z5=SVq|2v=O^-UUGhQgN7sHkE$-E;c$C2Tc`+O3 zX7$M9${&ATK3cGDj{Etud-zU@_Rj8la^UtVW-+yiTb}$iYn!?Aj@rRE1}~9{8~|?JhJrDciIsZ)dWf>$-UUqMyuhBG+y>&3@PH)l)J@ zQ1QBx<>|)fn$C~y_ei+N?{(P8rnBYH0mlx>)wfPNKa>70w9abJZAXS0wH!_Fh3l9y zzG;?m%SiWqYI14iqdjoRF0@EEZWEmNJIcGQ& z*A<>w6*Ae3zcHKh+KU@{vVsC#V*K{ToXgX<`m#*T5$F&<%{Pzv%8PR`B6%(C@h?^X z1QZKuxlyO~TSTos{-sxb-@0Uj2hR_6tbEg^ z68Cs=mFPLw+6VUYCbe)L5#epvx8(Ns<<&P*cyuiZo z?M|Z4C)IXsWIcc3kwb0Je)XJRUhcjv58Bl(9+|iK|BYKewo5(IGqqRn+SK`8_ifA0 zSDdo@4VxJM0aMp6U?w)G7?7L@Di9Eb$AVa3?tqmsoXm#EWr=|dLWb3lA0-2uL>VX} zWLXTwSVX+5=Ni7QukaDM_B`~#37*|*&RganCjnsQVrgPz=;tw*nR@Mc>VA_9+f{R{ z()rfavwHowdHH|zF}pd-dNjQzzFp@%n@N9`b2!J1u4f-uBZ3#D_#HK`o~+TqedDBO zqWFSpvrEc-v@c#dK0+Xo5C4`wm;T&ZT3U&&qli}M@5`=-1z5ldPGOD3$fb1-k1 zxZ&QvpLxfoWEjm{Dz1{JUGik#{Y9et-YWsdWSZzv&*U literal 0 HcmV?d00001 diff --git a/security/manager/ssl/tests/unit/test_signed_manifest/trusted_ca1.der b/security/manager/ssl/tests/unit/test_signed_manifest/trusted_ca1.der new file mode 100644 index 0000000000000000000000000000000000000000..bf05308ccc5ad1d583bb1f7d3ae9dddf0367e64e GIT binary patch literal 928 zcmXqLVxD8r#MH8YnTe5!iILHOmyJ`a&7Gh=Wuy3k$$x!xD2cfodEL6vTNAO$;oJ3=Axc%}tG?B>0UCOpyf! z##v2_3dq63$jZRn#K_BF(8S2W)Wpchup-s`Q}Ve>XOB+EZ%$6$HA66Tn@4I<`!mh> zzPjDJndLS-`*3z;MTFP^%aacj+1j#SMh1JZc&J~TGU;+L!<%`x?4GQNTmC4ci(8#@ zss6hak?-f95kmkl~==eG9vl3nws&-IdOZ2ih%Xz^ELF^Ad?iyEEXho*koaNM?-`6W;K&$a0< zIF|}N<4+R}-E#Et%}C`a)+U9w*RF-_yT7kt-J!V#A(c#w%!{oJEDiX9@hU6K$oQXy z)qojD8Hj-dR6znf23%|$+H8!htnAFp27DlKevmi|GZPaFa^M2f9x!kj85+4dnO|z_ zOwRoFzN^dh+j?;gX4y4=UASi-nE4^>%8kyAO3ha${B-OW2=Hv*zrSS46uT2ArmvP% z-?E$GmUqXUd?OF<`^+&^Dv4(*)@#oR*S$KFuaCp8l;a7j;pXMBK#Nz%go_(ZNL zOM|6X`g_#sf8FV_;1b*NBKwZLD{95oRD}J0^6cNzQj@d411()G9`HIgKJmT#P3LyP HDam>O4tPvp literal 0 HcmV?d00001