./PaxHeaders.X/.gitignore0100644 0000000 0000000 00000000034 14763776540 014314 xustar000000000 0000000 28 mtime=1741684064.5200000 .gitignore0100644 0000000 0000000 00000000054 14763776540 011617 0ustar000000000 0000000 .gradle .idea *.iml build/ local.properties ./PaxHeaders.X/Android.bp0100644 0000000 0000000 00000000034 14763776540 014227 xustar000000000 0000000 28 mtime=1741684064.5220000 Android.bp0100644 0000000 0000000 00000004261 14763776540 011536 0ustar000000000 0000000 // // Copyright (C) 2016 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // apksig library, for signing APKs and verifying signatures of APKs // ============================================================ package { default_applicable_licenses: ["tools_apksig_license"], } // Added automatically by a large-scale-change // http://go/android-license-faq license { name: "tools_apksig_license", visibility: [":__subpackages__"], license_kinds: [ "SPDX-license-identifier-Apache-2.0", ], license_text: [ "LICENSE", ], } java_library_host { name: "apksig", srcs: [ "src/main/java/**/*.java", ], java_version: "1.8", target: { windows: { enabled: true, }, }, } // apksigner command-line tool for signing APKs and verifying their signatures // ============================================================ java_binary_host { name: "apksigner", srcs: ["src/apksigner/java/**/*.java"], java_resource_dirs: ["src/apksigner/java"], wrapper: "etc/apksigner", manifest: "src/apksigner/apksigner.mf", static_libs: [ "apksig", "conscrypt-unbundled", ], java_version: "1.8", target: { not_windows: { jni_libs: ["libconscrypt_openjdk_jni"], }, windows: { enabled: true, wrapper: "etc/apksigner.bat", }, }, } java_test_host { name: "apksig-test", srcs: ["src/test/java/**/*.java"], java_resource_dirs: ["src/test/resources"], static_libs: [ "apksig", "bouncycastle-unbundled", "conscrypt-unbundled", "junit" ] } ./PaxHeaders.X/LICENSE0100644 0000000 0000000 00000000034 14763776540 013331 xustar000000000 0000000 28 mtime=1741684064.5220000 LICENSE0100644 0000000 0000000 00000027141 14763776540 010642 0ustar000000000 0000000 Copyright (c) 2016, The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ./PaxHeaders.X/OWNERS0100644 0000000 0000000 00000000034 14763776540 013264 xustar000000000 0000000 28 mtime=1741684064.5230000 OWNERS0100644 0000000 0000000 00000000076 14763776540 010573 0ustar000000000 0000000 cbrubaker@google.com dcashman@google.com mpgroover@google.com ./PaxHeaders.X/PREUPLOAD.cfg0100644 0000000 0000000 00000000034 14763776540 014340 xustar000000000 0000000 28 mtime=1741684064.5230000 PREUPLOAD.cfg0100644 0000000 0000000 00000000323 14763776540 011642 0ustar000000000 0000000 [Builtin Hooks] google_java_format = true [Builtin Hooks Options] google_java_format = --sort-imports [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} ./PaxHeaders.X/README.md0100644 0000000 0000000 00000000034 14763776540 013603 xustar000000000 0000000 28 mtime=1741684064.5230000 README.md0100644 0000000 0000000 00000005366 14763776540 011121 0ustar000000000 0000000 # apksig apksig is a project which aims to simplify APK signing and checking whether APK signatures are expected to verify on Android. apksig supports [JAR signing](https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File) (used by Android since day one) and [APK Signature Scheme v2](https://source.android.com/security/apksigning/v2.html) (supported since Android Nougat, API Level 24). apksig is meant to be used outside of Android devices. The key feature of apksig is that it knows about differences in APK signature verification logic between different versions of the Android platform. apksig thus thoroughly checks whether an APK's signature is expected to verify on all Android platform versions supported by the APK. When signing an APK, apksig chooses the most appropriate cryptographic algorithms based on the Android platform versions supported by the APK being signed. The project consists of two subprojects: * apksig -- a pure Java library, and * apksigner -- a pure Java command-line tool based on the apksig library. ## apksig library apksig library offers three primitives: * `ApkSigner` which signs the provided APK so that it verifies on all Android platform versions supported by the APK. The range of platform versions can be customized. * `ApkVerifier` which checks whether the provided APK is expected to verify on all Android platform versions supported by the APK. The range of platform versions can be customized. * `(Default)ApkSignerEngine` which abstracts away signing APKs from parsing and building APKs. This is useful in optimized APK building pipelines, such as in Android Plugin for Gradle, which need to perform signing while building an APK, instead of after. For simpler use cases where the APK to be signed is available upfront, the `ApkSigner` above is easier to use. _NOTE: Some public classes of the library are in packages having the word "internal" in their name. These are not public API of the library. Do not use \*.internal.\* classes directly because these classes may change any time without regard to existing clients outside of `apksig` and `apksigner`._ ## apksigner command-line tool apksigner command-line tool offers two operations: * sign the provided APK so that it verifies on all Android platforms supported by the APK. Run `apksigner sign` for usage information. * check whether the provided APK's signatures are expected to verify on all Android platforms supported by the APK. Run `apksigner verify` for usage information. The tool determines the range of Android platform versions (API Levels) supported by the APK by inspecting the APK's AndroidManifest.xml. This behavior can be overridden by specifying the range of platform versions on the command-line. ./PaxHeaders.X/android_plugin_for_gradle.gradle0100644 0000000 0000000 00000000034 14763776540 020666 xustar000000000 0000000 28 mtime=1741684064.5230000 android_plugin_for_gradle.gradle0100644 0000000 0000000 00000001107 14763776540 016171 0ustar000000000 0000000 // Gradle project used when building the Android Plugin for Gradle apply from: "$rootDir/buildSrc/base/baseJava.gradle" dependencies { testCompile libs.junit } group = "com.android.tools.build" archivesBaseName = 'apksig' version = rootProject.ext.buildVersion project.ext.pomName = 'Android Tools apksig library' project.ext.pomDesc = 'Library for signing APKs and for checking that APK signatures verify on Android' apply from: "$rootDir/buildSrc/base/publish.gradle" apply from: "$rootDir/buildSrc/base/bintray.gradle" apply from: "$rootDir/buildSrc/base/javadoc.gradle" ./PaxHeaders.X/build.gradle0100644 0000000 0000000 00000000034 14763776540 014603 xustar000000000 0000000 28 mtime=1741684064.5230000 build.gradle0100644 0000000 0000000 00000001502 14763776540 012105 0ustar000000000 0000000 // Generic Gradle project apply plugin: 'application' sourceCompatibility = '1.8' repositories { jcenter() } application { mainClassName 'com.android.apksigner.ApkSignerTool' } sourceSets { main { java { srcDirs 'src/main/java', 'src/apksigner/java' } resources { srcDirs 'src/apksigner/java' } } } tasks.register('createTestGoldens', JavaExec) { classpath = sourceSets.test.runtimeClasspath mainClass = 'com.android.apksig.ApkSignerTest' args = ["src/test/resources/com/android/apksig"] } dependencies { implementation 'org.conscrypt:conscrypt-openjdk-uber:2.5.2' testImplementation 'junit:junit:4.13' testImplementation 'org.bouncycastle:bcprov-jdk15on:1.68' testImplementation 'org.conscrypt:conscrypt-openjdk-uber:2.5.1' } ./PaxHeaders.X/etc_0100644 0000000 0000000 00000000034 14763776540 013161 xustar000000000 0000000 28 mtime=1741684064.5230000 etc/0040755 0000000 0000000 00000000000 14763776540 010406 5ustar000000000 0000000 ./PaxHeaders.X/etc_apksigner0100644 0000000 0000000 00000000034 14763776540 015065 xustar000000000 0000000 28 mtime=1741684064.5240000 etc/apksigner0100755 0000000 0000000 00000005617 14763776540 012325 0ustar000000000 0000000 #!/bin/bash # # Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Set up prog to be the path of this script, including following symlinks, # and set up progdir to be the fully-qualified pathname of its directory. prog="$0" while [ -h "${prog}" ]; do newProg=`/bin/ls -ld "${prog}"` newProg=`expr "${newProg}" : ".* -> \(.*\)$"` if expr "x${newProg}" : 'x/' >/dev/null; then prog="${newProg}" else progdir=`dirname "${prog}"` prog="${progdir}/${newProg}" fi done oldwd=`pwd` progdir=`dirname "${prog}"` cd "${progdir}" progdir=`pwd` prog="${progdir}"/`basename "${prog}"` cd "${oldwd}" jarfile=apksigner.jar libdir="$progdir" if [ ! -r "$libdir/$jarfile" ]; then # set apksigner.jar location for the SDK case libdir="$libdir/lib" fi if [ ! -r "$libdir/$jarfile" ]; then # set apksigner.jar location for the Android tree case libdir=`dirname "$progdir"`/framework # also include the library directory for any provider native libraries providerLibdir=`dirname "$progdir"`/lib64 fi if [ ! -r "$libdir/$jarfile" ]; then echo `basename "$prog"`": can't find $jarfile" exit 1 fi # By default, give apksigner a max heap size of 1 gig. This can be overridden # by using a "-J" option (see below). defaultMx="-Xmx1024M" # The following will extract any initial parameters of the form # "-J" from the command line and pass them to the Java # invocation (instead of to apksigner). This makes it possible for you to add # a command-line parameter such as "-JXmx256M" in your scripts, for # example. "java" (with no args) and "java -X" give a summary of # available options. javaOpts="" while expr "x$1" : 'x-J' >/dev/null; do opt=`expr "x$1" : 'x-J\(.*\)'` javaOpts="${javaOpts} -${opt}" if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then defaultMx="no" elif expr "x${opt}" : "xDjava.library.path=" >/dev/null; then defaultLibdir="no" fi shift done if [ "${defaultMx}" != "no" ]; then javaOpts="${javaOpts} ${defaultMx}" fi if [ "${defaultLibdir}" != "no" ] && [ -n $providerLibdir ]; then javaOpts="${javaOpts} -Djava.library.path=$providerLibdir" fi if [ "$OSTYPE" = "cygwin" ]; then # For Cygwin, convert the jarfile path into native Windows style. jarpath=`cygpath -w "$libdir/$jarfile"` else jarpath="$libdir/$jarfile" fi exec java $javaOpts -jar "$jarpath" "$@" ./PaxHeaders.X/etc_apksigner.bat0100644 0000000 0000000 00000000034 14763776540 015632 xustar000000000 0000000 28 mtime=1741684064.5240000 etc/apksigner.bat0100755 0000000 0000000 00000006241 14763776540 013064 0ustar000000000 0000000 @echo off REM Copyright (C) 2016 The Android Open Source Project REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM don't modify the caller's environment setlocal REM Locate apksigner.jar in the directory where apksigner.bat was found and start it. REM Set up prog to be the path of this script, including following symlinks, REM and set up progdir to be the fully-qualified pathname of its directory. set prog=%~f0 @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. exit /b 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. exit /b 1 :init set jarfile=apksigner.jar set "frameworkdir=%~dp0" rem frameworkdir must not end with a dir sep. set "frameworkdir=%frameworkdir:~0,-1%" if exist "%frameworkdir%\%jarfile%" goto JarFileOk set "frameworkdir=%~dp0lib" if exist "%frameworkdir%\%jarfile%" goto JarFileOk set "frameworkdir=%~dp0..\framework" :JarFileOk set "jarpath=%frameworkdir%\%jarfile%" set javaOpts= set args= REM By default, give apksigner a max heap size of 1 gig and a stack size of 1meg. rem This can be overridden by using "-JXmx..." and "-JXss..." options below. set defaultXmx=-Xmx1024M set defaultXss=-Xss1m REM Capture all arguments that are not -J options. REM Note that when reading the input arguments with %1, the cmd.exe REM automagically converts --name=value arguments into 2 arguments "--name" REM followed by "value". apksigner has been changed to know how to deal with that. set params= :firstArg if [%1]==[] goto endArgs set "a=%~1" if [%defaultXmx%]==[] goto notXmx if "%a:~0,5%" NEQ "-JXmx" goto notXmx set defaultXmx= :notXmx if [%defaultXss%]==[] goto notXss if "%a:~0,5%" NEQ "-JXss" goto notXss set defaultXss= :notXss if "%a:~0,2%" NEQ "-J" goto notJ set javaOpts=%javaOpts% -%a:~2% shift /1 goto firstArg :notJ set params=%params% %1 shift /1 goto firstArg :endArgs set javaOpts=%javaOpts% %defaultXmx% %defaultXss% call "%java_exe%" %javaOpts% -jar "%jarpath%" %params% ./PaxHeaders.X/gradle_0100644 0000000 0000000 00000000034 14763776540 013644 xustar000000000 0000000 28 mtime=1741684064.5240000 gradle/0040755 0000000 0000000 00000000000 14763776540 011071 5ustar000000000 0000000 ./PaxHeaders.X/gradle_wrapper_0100644 0000000 0000000 00000000034 14763776540 015404 xustar000000000 0000000 28 mtime=1741684064.5240000 gradle/wrapper/0040755 0000000 0000000 00000000000 14763776540 012551 5ustar000000000 0000000 ./PaxHeaders.X/gradle_wrapper_gradle-wrapper.jar0100644 0000000 0000000 00000000034 14763776540 021014 xustar000000000 0000000 28 mtime=1741684064.5240000 gradle/wrapper/gradle-wrapper.jar0100644 0000000 0000000 00000170250 14763776540 016165 0ustar000000000 0000000 PKA META-INF/PKAm>=@?META-INF/MANIFEST.MFMLK-. K-*ϳR03-IM+I, dZ)%*%rrPKAorg/PKA org/gradle/PKAorg/gradle/wrapper/PKAw' ^*org/gradle/wrapper/GradleWrapperMain.classX|^,CRJIJ3 %Ԅ\-")NhiKttA7C6mi{{wqn|?y}6 ^rܤBŋbDKUTHp^ n~R^xZSQ'zy6XߨMxdU.oSq.wN?ޥN[{WTU4 >Qw)8*!GG咑˸~ܣἊpq''??/| _£ _, *:o;:"GF;v<{w@ r~ZoᖨmŒ;淛ɔ'CzYt %D;cɘK,tHnQZM$4 _?7?[1}H,%.bZ-Ö>7Z,}t԰ZawO%i/_E#&;uk=,+tKň-{cqգC;f$D@LbTR?V9ppȨrL&#I z6e9Qy ڧC1G!ґ3GGT[ȵ/m-pi 6"nKeyE!& ~@w&\: CAZmc 7V4?R'9%P~*3l-Gk)~_j~,Nn=5 j ~w~?h8j81 q׺I-?hKiJQ(2FZD&*D95Lk@U>PtR&5D(^ B& DL'H_:iFP_R *%"(]O&M;4dE8JNV(Le&Bj\jTǯ3mM,ˤXE4 ׳\X-Ѡ5b-SSD&$,OYRf3崌JNߒ:c7&H %pDTm4E^$Y#9&/wd#1N80ߝ;&QBg{xWzJM)d˂SlTlpfQIS#uJm3u2UgaIϧ_8 梧tN/$79 O*#W*xFLdzWM /HAG/p7U_׸po>%"wrk i\58kEbOc(o 2ϣr,Ph3HpD)rI[5gb [Bh$eqK2y ,孕&U lV.UV-6O65$qD#y3hDpmô X؀؈ ݸڂc  hM؉ `7ng8lZoGc n, ư|ݹNWhR;=+f[VL$"^CQW3]u`HPKA.q/3#gradle-wrapper-classpath.properties+(JM.)M/JLIMԁ2ˋ Rt3RSJJ2sSmPKA)gradle-wrapper-parameter-names.propertiesPKAorg/gradle/cli/PKA?<S1org/gradle/cli/AbstractCommandLineConverter.classT]oA= "~C?R*Oh4!aYm` Cߢ/42Pa9{s?PF@;:v Dc@{xY9yx\Ycfs ڑ߱Vg{m[xKH[{ʅ'&?bNӵKV-)%^{m!mQaظ|jUnl㌟R{N}f[ C -kږ=,ȍ؂ak^߫^t ֔-J_,/]ctˡ+;XrCuVQ CW uhCaoܠ1a6C2QľLK &!9a{ Q23Qu {'M~rՐ3|i153 mnDevf߆J~-CH;"d9edc DqָSX`Iw/!):Fy7_|@8_A #FVX! K`R~;x)>=NRnp؜ch⟐ϔ4=c/-ePKA׃X ;org/gradle/cli/AbstractPropertiesCommandLineConverter.classV[WUN20 HԄKKIQJ;L0fOZZ_tA}N.i2Y9};vO6Q0e|`|`w,'? H` = V>Bx&c.cC i?]cЗ1-ci8"qB[׊kcrƔmeBhЋfb~MVd8i ޞf'$ F]p䙶0fv}-QԬB"9U ёѻE#0ff[-PvT4V5%\X3LXQgIWW]p0?:ĒVJFOMA`i ҮqA1\W@ \A܊A)5"ɓ48,FhJ+g ^zKŽa@7e2ZfYTRQxuWx8R E10ogm74Ӂr_q9\WOw/jhz"#z,Zi.ZFkP*؏$}iB+ c na]bᶂ1|( 2>R1>aʠSz5 xA$>UDP~.*8 %>Ä;g+ZEkߌM' g6-0 : NVϟ=!!{LX`e2zFI3U\%Ne5CC]Z]AၺmѸYCYbZ.+&%b `)s4 "(({j^<Q*>\{#%N׃\&9IБԍl,kșK؄%k)WYj>dr6gݫ =04 ֺb9zZέʆ*Ge8TIț;Hߌ)DNt< -OHtmٔ 3m1lmW4 EWrC~-r-enge-Yqqext˞H*N7mSpv!0iBl{] Gh*,F@E4%gJvrDeA6\pA!NցB/0 dS8܊G_+L+~䊣 YU܉Z=L8mgVIn|#ᮀщ8!Wq$ [q.^fq{\9PKAu`&3org/gradle/cli/CommandLineParser$AfterOptions.classmOA{-r- >W+p"D!$MFQ_,QO{f{(~/$QI4׳&3ٝ0c@$(Hb$,S:2^Zr0XdͪI^um:#.vuqg!uax4}̐\)f}͖K%2Cc4}[>|;P\tyaj$!)|_W|.oEZ#jc .k*EOD:fzr3*-TjrXE{aO,{O52XT[|h$p)5P d mʊ}Q}3*"d CUX!w 췤b긖u1Ls 3o |OaFBÇN&ڇn[\|yzw˗M{=izCP=ER4gd!>i@3_&U A;^"G;82G_Yyi-Vl`!z!PW]V̍jFW_ywGO=>djqR MI1d4D7tO:uSIgȧ8PKA+$u <org/gradle/cli/CommandLineParser$BeforeFirstSubCommand.classVRV-8,:I؄uBkқ,W3}t2kGi{$ L˞=~ڳ?sx$,ҧhX14GV%\ b8 ǪP!C:nqܖqGNFy{ F=:0YTRw*zJZ;9V]dpk[u GӢbj;:!lTKu_-UH4Z70?YMêۅFiAcVQuҞ ] QAP 90 ̂ZvmLݵm={~*YNl0Nɔ7ۄZeA]=v5GHrmmT Po8.\AvOQ`}#y¬{g3bC{ rְ4:ӂ{1`83$Oĩ@>TA />1:L|鱮_)PQ5ϐ6hKb^.W訃vzdzlJ =U^j^G3.\_ ۨ&Vm0LyuUt`Q 6lcnZ{4:*甚UhYL.w?Ik xI"B\,śV~D-۴M{?J,#|xbi;4gl}lAƻ$3(نZJ2pi/pѬC*l}{Kp~C #Hc}8m1Wc>-3E= p19"z["W1ʖ1Ʈ!#V-\w_vA@W1I,VS!H?ds745)'HrH!P+!w08*928_gޱi^E`!J$ SDgp#ɇiBPKAmKForg/gradle/cli/CommandLineParser$CaseInsensitiveStringComparator.classS]OA=X["Jd/ƴi&&MGRn_0o"M ;ss9~+j`A/)<#;:]4Q6Q1QeH7+&CX:dH`XHW:=2!WRd2x'}E@n;ly'#x)B ^t<5ai-WPs( f?<;1?ݡ!볙RjQ =w_Lkl dGKE?fJmC\uaa_ ]9ty0fHXcVp2]o 3vu Y؅ma6Cw* X1A>{$a*E [bXɁ?oF)as=aFU%ahdɦ(ʟa|,idN{+` yL[+B')+3$Z}rBDȔv4AlʘUVQ;qv`nZ49,{N]b\T~gHO""ˁXIR4b"ta o%겆[ PKA< K x=org/gradle/cli/CommandLineParser$KnownOptionParserState.classXwUթd$P*!(d% j`D")TŪ}TDEpGQY=~?GzK:_{ug>Іq˰MF]F)r!C: a0C.c $y5Y :HHɨh{WF#'.7LW^<#x <}d?Jk5,׵  Q6P4u39,{eI%4Z:!L SC^+@ط peD}⮈gD<A)s\%M c`WS@‡jRnRm'4iF7@L]`n1Z6׺@4n=R0zHyM&56֨ 5(VwгE @d-$6CmGG2ifu[psi_6s lv [ۺl \U_ -gItwδ-9S2kn:Pv7_[1dI_J4} 6bdr:O8]@~IӛA҇AO$n[-<: #Y#\xA_.QQT*F+ڥ7S+M.H(Dy~DѼ4*N$zCYa hh"D,?% P)cx5P+^-ij׌tފ=ފ}-VmbCꉎC>gQ_>G~٧1\8*snӺaU%B$)j cqm,]@x;Ao=s܆ɷ d"Zwacv{I  1,埝wb6_|j!T1V|E PHZ &tq&GQ$P$ˋ݄mh0њjeY C9Ǩw|?8CG`,JgqL8kفN!>>Ozj}7ĿpL_Z|!%tSPKar3LX3ȇMr ߣoqM%~n')mhlZ: qjR{\-Aȸ'(^^l%%{?=h&_EVWF(]_q#PKA=<org/gradle/cli/CommandLineParser$MissingOptionArgState.class]O`g*LD jD &xY=%mgox!Bep0bUgSkµ&w;Sgl84YH K;Abio-AmpoIy9 .p\.4l#tb,_h%V`1y-t n5s|}r/Hg!. µ-a@;jaAt7&< !éH/XCP߯ >?PKA^a=org/gradle/cli/CommandLineParser$OptionAwareParserState.classUn@=q$6\ʥm@)p)E E*oĮO x$ J|Mݒ*A./ޝgf퟿PHkБ1&0@VŔ3*rbUQPq!^uMn׊m2Q5\arᚵ?%r\e˶; 'eeթQ h5+x+ *o;.Cɶg9Zu6jI,ӽ]Mb%} Ds ns%OXv}i9CL+H-q > I~/OcWH+9-Q5,iNHE $uƜ~q*u‚EKXf dg5K^U R68E ;MA7ž2LKmUel{{呠ZO2o̿LVKΫUusyřcK9[6]j' )#C o O00!H0I# fSςRe1$8ȗP}4s2|=ѧQ*te躣K.@ >rd d$S3gH|$%!zp'a!xD"2{9 O蹀Sp HswnL8trI"!y3Ⱥ2GYv>i<2<l4!M{nV  #fdY"5>{}FCNS#ZOqϣHINQ2(R8˦S5 'l oPBF"^7% ߺͯ,g|,CғͦՈnWgx/"_~ fIZVθ *YdLP>؞Li*T,>@i&x+ # HMaRi2ElomIaq;F.O]R~LrJ,KF :}5(5cb] XtycRRj'R]U C({g!V@4LrD,Lu0N2)fZA^F~Z=\N;O&PKAQqx=org/gradle/cli/CommandLineParser$OptionStringComparator.classTOAfvۥ*BUXR@)!&&MԐm(-_7/xDW&c|[Q,Z~7}~#EHaF.  :T塆yM԰̐\];`P Y u۵w-u(3R-nR !}ڞ}mU#Z~A`n-=2mP{3RAkF5)ı0YktƋ%"{~H8G@t#WOQwKwfXk#pa)94Ca\bm)zTJoxmi=hٞviKXa/ǪXn`{#& dIc2d~ͰRX,ѫbO%q,Gφnd3Xwa^ .h",#=G0IId7ba,tBy`tE}M} U٘5ZQs'S$x'w*$/9%B';TDߥx 9c(!,LDh9n%$-8[8nSCJtd_ax3[48WȦgK h)dd<-FweW )Wp? ӸNv)1Bބgh"JPKA 2org/gradle/cli/CommandLineParser$ParserState.classS]o`~N) s|MqQݝh"ĄlK0 4KykvF7h?e r"QqJz%$%HV9fA(&5ݧsixn֐ a /ž7{ Z%?#M{<}3& YU6[M Pڕh&ߗP2egvnӁ$$T@|*>A%W|):T Џ3_qI_q3!Źt^= xc @C!:Z_ eYq>8YMHEFw@@9\"!vCz1v񫮥x&҃k$}ˌf!uTsj]p99+1:*4*4fE!./1Y ?PKA_)&org/gradle/cli/CommandLineParser.classYy`Tչ}B ("ك(5J"Nd0f&Bܨu֭u)j5QkRk>V_|KWmY;Lfc9|[;w^>a3Ogz|Cn|H4]?r&>֡98Sq4p<p&cdy%[(qi1V<^ԻW=rKrba1\Je^di;hM ta+ccZSSS1Ai2įRNd2EL1WNQ%e&E: RZTJ&:*o$*Rf*2CrWfL]ΒYWٹX)2G*՝vJ^YiRc-ybgO J2N^YT;Gereu@R+u^YAaY˅rW*M.5] bfL0fEG< ǣp@4D5Y-$3k13 ƃyuk.#[@4fFへ%5F: c1nmVG[)R7hLu9h,^H6dlD~㚬Ozk jfA1p)7L`ԊjVm]qeU.o \CQV2TṂ[]I#Ҩkɩ(5\9;~ ­6#֊h9dV4 ݖæ-92 E s eGY̨*:`,2iDϼ`8_ .,btDM%?whFW)BAHS : Ġ+$ɟ!ml P=م[ԟ996R6}ٶ&3: Өj/%ж3,5S9xp3caD  6\IPF;(/Gԭ+\LdDWG2(,*YS;SZeC^>$'NKBD"bᾄZ8S̿ưM3'X2ތs#)$"/ZO_G;:v.HtDh2gQ%\Gl\Kny0n7BmQn 2!s5VE:*%dlt&sä!4shٛS1S$dspճ?7iҨI&{צH70ђH(d6Y &+o#؊MdΚ|$fJ OcgKrθ-fv-<+VY謑R;SXl3 G"YfV9ʊ$9w٧^66R ٲh45z%؁i 5E&CnVl7!d!6C"_56ŪIJ!,`Rs~]5MUpˢ2q!_J&3n6'Y9D/1=TG. R_mC0rժC1GZv,'Nr0~tc1sK'+5lV1 揄C]x nXr~|c*]bmc!ߔ!ߒ4+b~ncQUo+Y{[D@jf**YDFORP 4wyr%wCk#*骖HhnNmaI[lX<me ܀5yԐ{<o`9چoz-h N4oj,%j6N/԰L}W/\WVu^ E&C/Ŭ-3ŭ/(\aetaSy9ըxWPPxU_t8G j_W_9ԦδD8T8Aexb3edKʌ!}ЂfsRrgj>V ;8b '`e(qÑI=#kv[8+>u YI8>xR'"h$O=kͪC@NRwk!QZjƚg1aC4yxdm@ífZ5,/_mkGA9x%wK3֖<8'C3qAd͌YGu,ّv?u}c^U76ڋ:pMMBh6cٜ.ڟ4,uKVx16oC}ix[v-@6{.>Wq/ y Ywfٸ=æ]kyM k>[ES\rZxE,clۃ{8u]lnG4gT1l\iP$!Wp R9u%: è*w݃ѻQ;cv#cqur/}=8e7N!MS{0` "DΛ߃I~E4\YkOsh4NIMڏ){P@ꩤރ^L+N>ʣyaz=F$v UE%(-E!DT㫼>ov#MKQp.sW(Ulj7Jd'X)u;sďHnLL.E[.Q}|FY(}܊OQ0PU8ar\.4`;21Alx W|p2 EL@&E$g]u,t"7#*!&)lg%/ y]>˧A㦬,G0ryp?NL71(aRNnM| İ{CY/I"F^j!9Aaf827o(̿ǒnQ}=y; 9vhw45}Qw 5|j:J`F'8-S}G0O-d?v/f7nxns̯+K!,-z[T*QEKKXcVl%I&Kd!Ar\V^u>0uC8̺1o`|s.V3|+C6J%GE%DKHUD]#d!\h-94"׳oV=)M궚j9_b+ op%8 O˜lؗkJ r>O3&f%1#|ts?s0֯]Y#G:CË2/"XeY^܃Wu.oKŠ]NKD=0~hָ!~h~Ss#WWGl=lZͺZׂ?y!O0-QGU'AN)CjÚRu6_z O؇i ,(oqϷ);_~7Xb1~G+o3V]RabOKE+VI1cafFrا },qJWk^l--pͬr[ NS^\֐H E~?ZZ{ж'AOų^T66ՖZ0~L,K˒UĢ(۲~%|O?t i&Zw\Ź wy)(X f2nH2S)T+So}RWUS&~]aV_42yO]Y3/! n~-Nyt,cp ~Ő"Vr+]m\ql/'lb{KǕlXWvr le{+ ;$lh!xɓOPKA>&org/gradle/cli/ParsedCommandLine.classWiwg~Fċ%vTMc[R8S)-LT(.Ph١li7 `rX;Ɵ}g$Kָ1G}.3ǽ?.v*`!*΢ .bufT,.+*WBM0#h'".T=^a}:|Fkt 0^7/㋸$__V0AVMa q[ ͼ1s#+3ɥ/E; f2n-C#2iҖ U0RҒKMfr)y(Q0r*T>c4yZq%sH˶YhM-C#\U08a~.k *)CxK )LAt\гkWYp#A͙ FA|Wݳw/뀽)xV5KԶx:ԕ|)#wOPccI(V^yU6KbѴ\j3TpUG(|VQH1M 7S8 RΊ|{Y#۶nQ/5^kKouO;=Fm,Rhq35$bwcLOFܙm Ln"9mG7Mhz*gJwrv詔DճEc YeyK?a{?՜qZ:fyȌYcm5 ^Xdkޏဂ6Y"|P[x[w= 1s=>4?RcfUDO38*$c/GW*S.5“~Un |ɘ_=0a 4 WU\0~pWLHZϖ:zypy*rK}jc븪֒[v.2}Ric9SVUPPIΝir+9gS QZyۇOBMkI'ƠG> =7rW=¶~̜gD1[f(O :6yP=𸟳J8Je2msQP0-^^Ц5[y?AC2?TjH܆mo"@1H1tXG1r}kL%װCРh&4o! ;]LHҰe(=Z[];7d?k'Wo A؅속=x5:ICab!Zcx(Dp1')1a8!N**&T=thT_P;='X/ FΙbX@u #Bwvp\}b9ûx?}dT}HH.o!syVOCI/a$qaxA1cx)R_J@{& (+}?Av3xG f97CTEB"]tF.'ޜ軅؜t<õ2i 1i_3%6 ;Aؓq6A-V4RgZlG orN$gdyE:+N:$̕ųxU '\5B%ǻ6 Z3]^ᜐk>lJ ;k>e'*6zEGe)A<],;[S;\Or=l8>Y ߀^9ي NLvczͭ&@S܇Қg\HVvWc .97{XŘ㰗Go )ȑ,I8sQHI 0$Abs9{`_i*^QUt  9%<' tEB^Krvs?`A^EAEXP QP dȹPKAytE,org/gradle/cli/ParsedCommandLineOption.classS]O@=ݯGePaeQ"hHV1YawRlIgN[`Y$ә{=?“4(H#b 冊$4-L%q[器+r⾊ [lpWAɶ(Ha1p#-g,C/{9*I Kxs UEJ%a:w^u"]a*s!"k~E쳟}V_*bTG;-%bXo"p(=@&_2w%^*$APPS =1D:!䉩If%ޱn05W(vbRD $"xj2Dϧxr7k> !V.p].GӱD\8"y RPKA\vB| :org/gradle/cli/ProjectPropertiesCommandLineConverter.classKO@D|?Pâu#Q+$C;1m  JW&.(1D,9vo/[@yl汕G)v }FHWkwLS!]nY7ZK:̿cJDZRysV;H+-)nkS#cruLXgh|BjFYDΏ%L%񎅎*_?ֈ:("<ڄbJՍ ؊tf^*K ߵ XUVi01k p8wZ8T0g?PaΛm=C Ss | 1\Zq-}C_JEˉjE+ w'PKA 8=|9org/gradle/cli/SystemPropertiesCommandLineConverter.classJ@ثmjE5BPąR/P~ӑ$&BJW 'iAY3͜l "lYlE <& d@HgL{:rRs:C*X4NĬQ ۴;hZ3a ѽG!]Gv7S"5eb o}ɸGtFMz9y~X{()spL`7e.KV, TXxɢfDTEGPWJmh~49AjxѰ sh gԙn85].FԒs9Q΢*s/@Ug J*ce+s+1 $p6/t-,;h-.Z >kZPKAorg/gradle/util/PKAorg/gradle/util/internal/PKAh~&org/gradle/util/internal/ZipSlip.classTKwF&#VxR£IZ $4BhiMx&38CeɕƤ馛tâtarSAw%<Z8oB1U钅z0f-\ZZfẅy 7,,p_+Ҧ-|cEFR1 xC0/H,6j"yC8eH-w9)SRTj7B4 )D< Ve,Á?$.D*^5~f?AȣjPVo'^oza.Eoc Ze8^#eސX?Ud1 64+q#Rcٝ| |hcTXPTyx-6j"R ?VD|tt:ԍbrWsvuWh֮N٧tg 14q"h6, l׸l͆flƤX}KjAS_#XL|kc6\ͭWUpQnqԸbцG7s==[H3ktVWԜTDQ&<)"l,mttSN1BMH4B!K<{4}K2ᢊKHy5>=_L#$3^=C>?Sån;PQp:W`fo};ƊwoadQm4Q>B3ew`~&RikjbsZ(419p&lawfWg{(+ppdr 5dI?Mq&@/{dDx9[pTPÙқdF"#L$ fFADx͈`1A]-uFc]휣\b?PKAҩ/org/gradle/wrapper/BootstrapMainStarter$1.classRn@=Ӹu1@[ʣ$Z bAU$ .=Mrx?ņ H,> qDB]ǜsub?2nv;c=Mgh\jxyڨ7@wr1ӈDf'2'=7҅@e3Y8hhe蓕ыeToe {j+/JW*# |jU"rPI XQb!| lZfrj݃2P}!Z/X-js%3 cm/82, pր&<'.kat^D+h; T*e @FKͨGDrK_Q@V 254wq*^17I&p^@WqPKAr'}  -org/gradle/wrapper/BootstrapMainStarter.classV[WWI0 ^" AX$HVT(Р(6!Nfҙ Bon/}?ZV[VSWWI$ k{?)$#(a6K.˘ü $-!%#EWpU5#%Ae\ ,qSH "#ᖄ1Xom| ThU,Y$p}"zk ʼap+miМ_4O9p0JʭNP̪ji";L+[jN{Z,r+q4ۡEU3Ҏj9ܚba0n ]5ciF~ʓhf⒦)j)‘& r,01h==#`K ^1L =F0JVCA˕ѽ _&@OuҟfHԖ!N m`_ơ?td !Ό/t4]Ʊp+gz6ڨR 1 opRpN[Љv1",fR*C?!ԩn"X1ւncE0ZIB9K)\gIF,L~W^^br&D)RT Ft 5GDt2^Qshm!nC"k;hI#LD囗.I/PKAFjQȢ~4org/gradle/wrapper/Download$ProxyAuthenticator.classUmsSE~&&bTTm""PRP0}oPZNzzoMQf? P?mINt=9g>zNd(F46Їwz&>0!øs GWn\ĄK"q@HcWu\c׭j20Ptr쉒-sTt7q?0L/1 nI2-GN׾\ނXi%QtMa/ Rzc1BYϽ9Qץ[]_ulQJ;/K7\ԴgTxW|-rsCl$[8ܼYN@ XlSS h@v/Qy򊥘leU81+1ac :9p] edkRͲKX*Msex60 gxs@58T&I$ж1БIPJQn_d6+6=T-$[TXf8~$犴7 6q͏w`/LSV<5hwb}ͷ(CnFR{Mݝ8QRD\Ù[6- o&=LJ~pV % ;Go5-")M]*I;CFZf oI0@caxFN-ê%-Ls-SG;I]u -Cut'8 u~a gjW¦G~CWC$Gxe )W `OB|B zwL Pd"-C#qHR0ul8j00I!Wfh l Jj@A6B.M(!i.#^/PKAT)!org/gradle/wrapper/Download.classZ |Tչro&7! 00*K"`QdLT$7d&aq nU֥֭heF.M[Y^m?ْ1̹|;ۗ{~硏10zbw=ɊT Hc\ fZ=  w41DaJyXLATYl} <>sdyb/b8Cm|§)Cy"ËvU[\)*1TF zO^*3^Z]$ ^7,<\ϫġZP#;T>G|׈Q&]r[^']/V1lF1]  w*Is=-B.n{Tެ=OhCUm% _(@UHe]>Ww (.qj31H0<#ח |Fr$TL[wi!օSۅs6/U2/gj?1Oة@L$ V} jIySO2Ws.u 8T7Ȍ(HjJn=]HmK0dH+z)0e߈}wQYs>cDlpqfk4a=]3hؕ q#>0 @޶FLJ d1P42n2 ހԙ,&M , ]ٙl8` 2DvS!3~iJθ97yCv^0Jim gn*1}艆&语^+RO)|W+|M 94oa'*,@`_fהzz,7s~ xRk8Zkx f3/k⵨,Gmաu(nCxN^Z|͐I\ZF;CvfgYkfV$ D*Lfe][܀ɮe*A鋇tIi[j., u4ެd80(yG6~X *Io`H#Lhߐh. ~:SM%&W8 D>C;fFjt'}N_k0==tP Z׭cCRlQ44߬-|Ɵۘ(ηiYP;N)VFѵN0e >Iy>Kwht7ݠѳt>5d#5#" sj|ߍ$4_G{5B;jCH(3џ 33t斸_F{>/5* 4Tik,ߚ4Hc%cHiNh<ʇ5>1c?pvBFNx0$c$I@@42E>*:֝2 XC1}[ / W*qf 5 36Hf?Gc_Ф_ѯEKw5.-k:Z;{bBݛ2S?&ޯчHqȿNz |/łP?"A rh-&4z~o kD(4q~~.  Wm3.H(Bw2M'4(?B=-+58 /ytzzн5{C5;xr4Y#c#XkDc$:d]{v GF" lvke;X4ɮ6¥"em^EbNCHƪ]̲,''-.S";HzkSS%Pq O[a%3r=Ғkg$քne&pzfNhrhd=Lgeެg*BI@Y8j0,D::|)lE v$iF>SyLjWM>:GbB rSwDSm.X" =i/<1ô MF똅'G8i3=?̈N!cΗeg^,s&ܙB.8^KrU}߮9sHެEGVPHK2p-Lԭ;*_mbYrQ< *",kMw0\ HQG*nIVK<4'16lwD׈\H*uؘBĨ/ z٭TA4.9vԮJR0koH|}$fZM(Lܸ!p-Q}pXFN5s:vLpFxInŬ Йʒz󳊇o2L-& &8mҙ[&=g ʡmSV*? 53/sI(~4i*^~û"aݜU?* 1]A5t-(z!MOc}s*^`/ޜ6?/P̋xVMN|UTrUa;LQr>-}_3plݏYNЃy$,g<98$lB_IQrM$i$:"U"U"Ub3!Wb [4k, zN K9#/tII5NzZk+o䭨ҋ֔f[0S{ s\iAֹhQ-Q*TQ笨!T|Uu) 9_j}-sj*VK{W(|j] ]p%{pzWʣ3+ J 巰p5a˳{i+ Anv%`GM>13?A߇ j𹎑s%c:A:EF&5 [0dXmE~-w~Nt!~|MVͣd!BYJ;v!zCaꆥQ`h7t%Qk³!/'2xm履2*t5}H׀\Aq-]F^I7*Fet7Zz+EU#1=3#luq=} Rzހ09N UPBm WMRmޑs؉HG(Lq0C!YdoBkF8Tq.P0Cȏ}o.E^ԾvЁNlϹ1n-GeѬtg垻NoS!c_ W9bg!7O{vzF4먆yVm4UtQЊ$t>'S 8yfQlWЦ{TkH)$Wdz|CYXvFJˁ>'y Զ=AWy9^UtbsV3 Y^GwWB@ Ɉ89}\uNiE`M aBky%*BQQf#I@s2]qJUN+kPC`d6Cб 0BfGnIWH"H+)oҍ@18S $G~E= 1dZ Me,:FGO=5.AfAo)^AJ}DINSJ& eϲ$AwPu?o,){;$lA}eS/I]r}ij^VJGƌ@腻z>WWje]A+^to`~xQJtLY4H/UjߤFVfw,\g9'fb6l-""Di0qo%<9/*!9KK .?߫kuPKAyL1org/gradle/wrapper/DownloadProgressListener.classu @E+jDE@ۖEERI#3c[>4v9OĪ.r%J[M DŽ,]8kߟ_PU:]3aG\^&at"-EeY˛8\3K tЎBNk؁PKA!9| 3org/gradle/wrapper/ExclusiveFileAccessManager.classWsWYګu|iVMNZ*;JlBNKƎ[t-vjSB[(P`x 3L$x0 }0+9%L=sݿOt]o3;6lKqoF3VB>%lW !8$` Dղ3"x D]AمEU WxcLQV$Cn.O#&A7zx N5@T3(zY4} ] ҳ4?Evqݏ<̏ LNp8>K.1Dg[A|4nkhLpSESv\c~]+蘭vvR5dw=#r<\KT8lo7`.ՐS WĮT U4]~T76v7BZ H ҫ@'c Klϓ31cDQDDwp>Pzp}@8B gmγ*& _$W +kr}FA?~(AZ8:ưq8 ӛ"؅Qc? z5)qjsDPh hF9҈u>c~iMȈvz5HM $?b)nIP$)SݳhѠǧB<q0.Ҷxd(WWw@M߉߭5S< a-sc-gONQM} (1#j R`뛿̀imP/QWM:;"+) Qgai'$|TZ/q '%|Bj,5jS7&0sP=Fu+ LNn ϯl?薹6&L͉ ˘,fDVN`Ye' [ZtVS)͎zs[!)K ֬$jS7L_wNN^yJixCM5Z^5'Ki?I,UG&W=-6$ᓮ쩝$-/N25#_"kҎێƴT.rR4#25-nMRĸVkXS+թ #9_Is#I$8tR՗n,fq5HS k'MOK8-py>nFwj1=GOIݖPu2N#X;yA禉4̘a J;4gK1`nTlyAlUІvۑ%YaRnF\㌂pd ) ƄyK8G| *6&$|Gw=@SJȨ<Hw=+)1s?3?// )5~෬wHf5CsGc$q^O(/+&!zr3z ޱhMnMM[f+I;wC^wt6)cE. /XI{)f4]b擞k]ԶˁFQ|\ Twqژ[-e;m9?"$Ke:hݥ&* XIm1RNCm \)Kwn5VPO{v#;-#hTgbZ.tU%f}1ΜSjW̤b,[dK;]>Ͷ /]CijYYk.2:I)^B ƹP2Q?u_I|Wu#eo[5si%.X`fw(2m qu"}n*`%+'!DOۢeIYBt]<'_m<FP]Qm~ӝcgc\wI{I7^ٖ.B_J+hag+AXTm.mPOtZA!X]O!͹DLo"wg;ӻ~#;Oha(DÊQT44""0 h ?H i=Ntk? `=5gkxY9 o Jɢf. Yw/lgQ3f;VcB4՞:4_Ez=5B߷q,Z<MXŇgćߓvMoq,muO+Zl=/C%R59E8xf"'9ECYAJDGIg! Y-}esUl)~ x,snJw8VS>Qn:}uM@")Rbn,q "Bo'᷇0POhJ0v8vBI܄ƣDvOaX>Vp8C" 4F1qq=c@<] 4."- ' {$9W \DSaq=.T><%p XJEĥwvN3 '!$i̭A\#$_ 1 6(߸?XGTЈOWo%D-IP=gTJV<bhpu  , ^ n?PKA?{-org/gradle/wrapper/Install$InstallCheck.class]OAi]BUH[ [ I&E/jݰ eain#oDK/Q3J 4Mٝ9ٙ?PcqeOP(ɰ Ee9.Hchx|/[asiY+Ѕ]Ol Z Vٰjmi6hb0_56w=a)ugՅjkX Ǐg h*2Uuup[xo\lֹ<vM~Ƕ)aX͟pNi?chNf{Uu9a R66QؔU'r0\"}@EV .hY>An[^LVdz,?`b%&R& $M+ stdn!f{_mc&Ց7*A/^fitB)[F3+~Y.S;lWhnpU1 (B N7`Hآ,J(Ο@+NdX [@-bY d-@$l6B?za4{WhkEBtkek*ߥ s=#۱㡞K`挘 ?fG?x_)%m'9bi"m?PKAgKe- org/gradle/wrapper/Install.classY |T?'Q d%! $/,q $ڊֽ-ݶt$`h[kwZk[UmUof2I>H}.gsϻy{'9vXi 'xi& H3Q^''k #5ޤfM2Oy4 |gk|<3jnָOkjn)/m҄4nF8x4?mdǤKs'ㄟlsJ;|%}t;!VOS\"%Og5D))|L4O/:yJbbIˈDj:̖m5f&j"F2i&+jWVopΊ oPuV1ԟgl7*#FJcULkB/fm2")W3y#v34>hlOrG4j;}Hhe괰nEz^tVG5D6G)NfWK$ o7W#fuKL1b҉BQ7wI82]!z}&d&j2̆TLl00)MF",A&;r} - ӰŴQvS cU%J%wIAVOS1aaVuXRØ3Ac*4:"șs<4V6d8kyE8_g]1w81;e[R-lIE`O)H)zr՜jkXm]00oü(dM iar hI \m,׺Xg'ӈJ5we0僼-ӄ!R-W-eSLk4F{4"Ӹf"֝k8u *툗4N&0> +dK"iN@Q6}G* ~x70`;3Aq@Vi~5JsU옊#48hRFDA%?O%ZL̮N2[Ռ/bc񔫺GЂ#\c`nV*泎Ak5iv{pI`jP4O8!;nl]ov2MZūb2#H~ٌDcupĦ ] 3pXڪՎX&R0z6jѨCXONkaprs8c K@x巚Sm>X%Sxxt@ `'\Fũah|czcƬDw Z,V+3FSgdӅMڜqIB5, C6,R86\ɚ NsI-);_:@%ٝf[&7:+dCfw)0"\3m֬YLGe\Lvy]-͜~)vF G.:+ChZrr ǦulPhU-3cf꼮\ FBn8K=n:!ұ^7S=_Uu!zDҥ:]LKu'Tʦt]et_y7_otoV5臠}MP!WC$U(t& "w6'A=wN]:HOA^zӕR#8(9#7X ⵌp,hĺ,9.9pf6}Vx 'A±ή랉*_aX|@{O]LgU4$Xăя&~i5 != tݮܱIʦpx`綞\+5܎kFwnM3hacfQL}]$AðИ386ZA-lV!:3= P;!փ[eKH@t`["}D*)X0@ ELB Ju*Sp֗-:32jU,Q?֧bV8jf GQUgDcΏ:?)OOtXbX<*htg*<-8J&u)b Tg?s =/BWzRQ߈EbOf u~NM"Puaɝ昴 JkHɈL/mD((E1Z<93Qt#t~_aח:5~%c[o#g,iݐᑻƑ/&-gAMhDH*U$fWq<ډE;;+i,́ si*ͣSi>]R&}HD&VO"Uz\]f?ICFA3!wC!tcB^xhlSiURa{9HEq1(WA%4NShs-+R2]dW諠V_\[Jh6 zH7a)4nƓnn7vފ=1uS`MMl*I ѣGНw3Mxzhw#o27CPqR]3# hF5[{X}o+5}@QߣռKhzDRxPqf00}4S~*3W7cb#@Tм*Y; 锏Zeh|R@o5J_%afm yr.K}00+ۍ${hG"8}=5s5VYc <M¦ܛE4  &ßs/i9(M" * $dUb' <w\wGA/#BvtK q ,F'KͱCݤ4;\>إ@^) 7̆AtKgh~ +ѣdYEWNqn^֥uK˄ADSޮ?}c*al7RnMU^/tW!{#}h^ZK !d:Cg6|h|@+XoGHCd&<|O&" h+(~:[`sBBiEaơ}~ {X4 S 3ϰY/hﯱ0(9x0=PBEKʣf jϨ+gN8_-!:~Po :S`(T0F{^wYגw|}Fj iޣ s,ڈ%A0Ա3xS`滨Z j#{Ւ?AW-iu{yח4$2Isе)?D%Sq֝\pW>eKn97'K\|ʫ`7{F7P< 'E[ V^y P9¾R*k*2w8ާSX׮S|I' >#xd@'9p' (Ħ1ؙQAyn1@ ~E< bPKA:o4org/gradle/wrapper/Logger.classko`Unc@&s*]711$KH`wʓ_OFg ?xR5Krw.??hG b1TP4w+ͶU].;`XxfX!R9Ӳ!1,j<8>?4dckqǐ{0#bvtUwxQ; 6m!W\5]1,)@F\s ˵w/'.Hqϰ3:NWfu-Ce"!lU#;m"x*@obw=Cj[$B# %qcI![3̔JgX&ovHIFi QV(Iؔ)r /,*ꊧP*qw(P&n_ݖ7ۛPKA`8org/gradle/wrapper/PathAssembler$LocalDistribution.classQJ@=x+PAA(*(mۥMRP?J*/3sf̜}{y,R6.]0UJ.da7p]1_ 1*BՂrCT_qiXOLZ|- Fd SM>W|+<*>~0Tuh Y&㙉Bb5y]KaOuF~%w-; VZG0T\ue/~֓"n04ψxVs!<~^@?f'"^ e6_ ^ uCěnri\5-Cٖk)XI6M兹əlfe*Z15G⨀1]3-Y咭8?;snblhȅ`*Ƥ^f7TK e%oq]@𔪩ic۽φER5e.c^Ε˹.ʆsoonS.5CT#u^Vϐe4WgQKkaIBEېwts|,X(i ( MeZfC mL4 jEL%lRm&es,е6Ai\TբbZ]'Sۆj]Oe&@Ņr1-@n:W*>AӁ 9K_(: _U&,E3@ЫW/MR ]$`yR7)x@[7M5?1mm#"F񶄯> 'qJH 񈄇1-aѷ5] 2rvRIj~>/I_l~w1N=Ŵ_v?J,/a]@îL1Si,gQiC&e :kkZVR!Vt6jۊu3±?ɸnI']cMngo!6XI5p2J%Uͨ]k: 39nۖŸ+~BĆv/\wit) gU71Kp޿-Т Qs9zM4'9KLDS^Q UsCi~KckC qWp{?6]-hW EiN:XЂ7izH".g2 ;C (sdɓ(&X8¾*pt#KA7LO.=Ћa*c1Qw;z-GxHzxt6п'Rj8uzvKtVG>q"!א]>.J}6$38Om$1K{=y':,8Bȉce%םYA U19:e Qun|Ac W 1{IX6Gst?'דKU 5QƠDvQNʡqZuxF{Ds9\Z!];<:ST"Bo!-p}[S)2BeTjs/#(ct`\:S8tS ')hPKA| 0org/gradle/wrapper/SystemPropertiesHandler.classVSU]!,- -3TR%A(.a,ݸٔW~L}Ǚ2cWڱ& $9s~sޛ?" Q P0Oc(xSNޒڔi3 fh@ԏ99-żT,H;XR=.r~{x_&cr|!:}mn~v":BԚvC 53;a&# 㖙u4Y9]aΘ'Գ(Vi=g:ƆVK9#]Q۶͔nv-mνv^g-*6`Ja#Y" GAN l-|,g+[ *DŧL縥 |+||+Xyvy)'1MnBʶ6w{/(X,(~> Wٹ## I=(DWF$v t$0;/~*xBDV` -n|/#)yeǤ2FIi&^ʫ,=A5ٶTmMfRص/}6-ƷbU $egRevX|3%_ ๡~cg5hOW -a.5T cO_ {LP; e3y؆:J{3Pw@y42ESp~Gw"03V;LM`q=4/Z>u`iN]xG7Y κcA=#7? I4lv14$ ĨnqR!9KM-z9BԯrW)5" ,9z|~_=/!~L08WIH-_{M F1WU> =SpL<`ݖ_bqWPKAYP-org/gradle/wrapper/WrapperConfiguration.class]S@BiwڠDAPVpZol&Ieq ?lng9g>_`B'< †آ4di;4Ґ /@X,Ԫ-[3ts"-"]R ܞ :N$;*gz$ |.\'r{GKt5Ue E-M,t昀/gP.ijVUP6 Ti+FM}Y2%ìuS5TT..TS~朡-Sj绮#ΰئh+}Wvwxto/ڽ@gz;0n-PjF!^}be! z+tJbSuOMe^0E`3IzeMzYɗ 'GBۂ1He+{DH@\ cT;Ka(5aBDEH!p w G-uwrI/Uq`+"b6:ଃRt1҆-ǘB:pw}g"pf1Γ1ڐo׆~gsCfHY₌ \|d5}@.HrA d5> )DY=.C>Hy7}@dLu}.H5> hPKAXy (org/gradle/wrapper/WrapperExecutor.classWy`M `6$bew!Qe5W 4`N6ddrhVkI-[l!J}3nK߻yq+o/^ /q ( yWjQp^S0j>?yS›x|3%go m<>yv8w{x<ŸCx/cޣ<{DCt7+X)Vzg'xefC2qe<` N2 v# Q)i6 'CxRBdj?qp 3<5?0$!u[khm6ټemZKXXqCӃPN0C\PFLm0򪫇SH z;+=J΁7$;{<$%g9ݲ =/ٱ`>YV_bdȴz0yM5i3;fKXeZ6XZεowM`iafPiٰkZ$tDs.1 l2GI]FV)Lր6º̴٦YMەK!=\ CRխs5댬aBK-&AcM|ܛsAo;?s,})0ȠeH $$ot)T%RT /Sdk2t cj}Yq],AF'liYrn{#h,'Kd]xL9\/miڤ.ͲuK)Ydm Rp %>@ F|QP$<jl髺Gc>iT M),IiNkK_9!K@]`vHfy2>GIGYoZvkJJS*ed ~g](B;yWLnRE|~B6&bJH:EJ1R&ȨIˬiGTZ+JX#T|_ai_tWэS1jQ5|zxU|Tq+nW-Tѧ*abK&U|د{̦jRݑ} ?bB==k\h'xgT><,dFE82'W'b} KJůyP-qdr-4M[U3%T'Y_TSwC?U g$l1#*"{ {"KG>1CY_&e"v}Y[qROw/seqnoʖpMYwVG[#2SX0),t9{bowwy(2%%ٻٻL-u޲ig⇱Gf8f4V|l!!lA} JÕ⺚ c555Z:ĵq=øoΌhƳSs1cKbƙS%\݌*J4&h2[YEc9<}OK8+hg%</:,"I[([)o6;X^A\4FI/& dNxQDh)\XcWo!1]=k*3TC#H).vUp3#Sᷜ+>"%gf<;:]{JEPkIVSQY-d)h\Hc+b7yƁay0p7DM"]LMQIǭϕ4u!'W!L =@?)META-INF/MANIFEST.MFPKAAorg/PKA Aorg/gradle/PKAAorg/gradle/wrapper/PKAw' ^*org/gradle/wrapper/GradleWrapperMain.classPKA.q/3# gradle-wrapper-classpath.propertiesPKA) gradle-wrapper-parameter-names.propertiesPKAAE org/gradle/cli/PKA?<S1t org/gradle/cli/AbstractCommandLineConverter.classPKA׃X ;org/gradle/cli/AbstractPropertiesCommandLineConverter.classPKA}yGK1org/gradle/cli/CommandLineArgumentException.classPKAg)Forg/gradle/cli/CommandLineConverter.classPKASf g&org/gradle/cli/CommandLineOption.classPKA튯(org/gradle/cli/CommandLineParser$1.classPKAiK ;org/gradle/cli/CommandLineParser$AfterFirstSubCommand.classPKAu`&3{!org/gradle/cli/CommandLineParser$AfterOptions.classPKA+$u <n$org/gradle/cli/CommandLineParser$BeforeFirstSubCommand.classPKAmKF(org/gradle/cli/CommandLineParser$CaseInsensitiveStringComparator.classPKA< K x=t+org/gradle/cli/CommandLineParser$KnownOptionParserState.classPKA=<2org/gradle/cli/CommandLineParser$MissingOptionArgState.classPKA^a=5org/gradle/cli/CommandLineParser$OptionAwareParserState.classPKA ^78org/gradle/cli/CommandLineParser$OptionComparator.classPKA,8;org/gradle/cli/CommandLineParser$OptionParserState.classPKA9,'3=org/gradle/cli/CommandLineParser$OptionString.classPKAQqx=@org/gradle/cli/CommandLineParser$OptionStringComparator.classPKA 2Corg/gradle/cli/CommandLineParser$ParserState.classPKAەk?(Forg/gradle/cli/CommandLineParser$UnknownOptionParserState.classPKA_)&bIorg/gradle/cli/CommandLineParser.classPKA>&\org/gradle/cli/ParsedCommandLine.classPKAytE,dorg/gradle/cli/ParsedCommandLineOption.classPKA\vB| :6gorg/gradle/cli/ProjectPropertiesCommandLineConverter.classPKA 8=|9 iorg/gradle/cli/SystemPropertiesCommandLineConverter.classPKAAjorg/gradle/util/PKAA korg/gradle/util/internal/PKAh~&Fkorg/gradle/util/internal/ZipSlip.classPKAҩ/oorg/gradle/wrapper/BootstrapMainStarter$1.classPKAr'}  -qorg/gradle/wrapper/BootstrapMainStarter.classPKAhQ}#cvorg/gradle/wrapper/Download$1.classPKA 2AFworg/gradle/wrapper/Download$DefaultDownloadProgressListener.classPKAFjQȢ~4{org/gradle/wrapper/Download$ProxyAuthenticator.classPKAT)!org/gradle/wrapper/Download.classPKAyL1org/gradle/wrapper/DownloadProgressListener.classPKA!9| 3דorg/gradle/wrapper/ExclusiveFileAccessManager.classPKA,y-ޚorg/gradle/wrapper/GradleUserHomeLookup.classPKA"org/gradle/wrapper/IDownload.classPKA-^]"org/gradle/wrapper/Install$1.classPKA?{-)org/gradle/wrapper/Install$InstallCheck.classPKAgKe- org/gradle/wrapper/Install.classPKA:o4org/gradle/wrapper/Logger.classPKA`8org/gradle/wrapper/PathAssembler$LocalDistribution.classPKA;+&org/gradle/wrapper/PathAssembler.classPKA| 0org/gradle/wrapper/SystemPropertiesHandler.classPKAYP-org/gradle/wrapper/WrapperConfiguration.classPKAXy (Norg/gradle/wrapper/WrapperExecutor.classPK77#o./PaxHeaders.X/gradle_wrapper_gradle-wrapper.properties0100644 0000000 0000000 00000000034 14763776540 022434 xustar000000000 0000000 28 mtime=1741684064.5270000 gradle/wrapper/gradle-wrapper.properties0100644 0000000 0000000 00000000335 14763776540 017601 0ustar000000000 0000000 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ./PaxHeaders.X/gradlew0100644 0000000 0000000 00000000034 14763776540 013674 xustar000000000 0000000 28 mtime=1741684064.5270000 gradlew0100755 0000000 0000000 00000020457 14763776540 011213 0ustar000000000 0000000 #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in # double quotes to make sure that they get re-expanded; and # * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ./PaxHeaders.X/gradlew.bat0100644 0000000 0000000 00000000034 14763776540 014441 xustar000000000 0000000 28 mtime=1741684064.5270000 gradlew.bat0100644 0000000 0000000 00000005464 14763776540 011756 0ustar000000000 0000000 @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ./PaxHeaders.X/src_0100644 0000000 0000000 00000000034 14763776540 013175 xustar000000000 0000000 28 mtime=1741684064.5270000 src/0040755 0000000 0000000 00000000000 14763776540 010422 5ustar000000000 0000000 ./PaxHeaders.X/src_apksigner_0100644 0000000 0000000 00000000034 14763776540 015240 xustar000000000 0000000 28 mtime=1741684064.5270000 src/apksigner/0040755 0000000 0000000 00000000000 14763776540 012405 5ustar000000000 0000000 ./PaxHeaders.X/src_apksigner_apksigner.mf0100644 0000000 0000000 00000000034 14763776540 017545 xustar000000000 0000000 28 mtime=1741684064.5270000 src/apksigner/apksigner.mf0100644 0000000 0000000 00000000060 14763776540 014705 0ustar000000000 0000000 Main-Class: com.android.apksigner.ApkSignerTool ./PaxHeaders.X/src_apksigner_java_0100644 0000000 0000000 00000000034 14763776540 016241 xustar000000000 0000000 28 mtime=1741684064.5270000 src/apksigner/java/0040755 0000000 0000000 00000000000 14763776540 013326 5ustar000000000 0000000 ./PaxHeaders.X/src_apksigner_java_com_0100644 0000000 0000000 00000000034 14763776540 017077 xustar000000000 0000000 28 mtime=1741684064.5270000 src/apksigner/java/com/0040755 0000000 0000000 00000000000 14763776540 014104 5ustar000000000 0000000 ./PaxHeaders.X/src_apksigner_java_com_android_0100644 0000000 0000000 00000000034 14763776540 020577 xustar000000000 0000000 28 mtime=1741684064.5270000 src/apksigner/java/com/android/0040755 0000000 0000000 00000000000 14763776540 015524 5ustar000000000 0000000 ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_0100644 0000000 0000000 00000000034 14763776540 022642 xustar000000000 0000000 28 mtime=1741684064.5270000 src/apksigner/java/com/android/apksigner/0040755 0000000 0000000 00000000000 14763776540 017507 5ustar000000000 0000000 ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_ApkSignerTool.java0100644 0000000 0000000 00000000034 14763776540 026224 xustar000000000 0000000 28 mtime=1741684064.5280000 src/apksigner/java/com/android/apksigner/ApkSignerTool.java0100644 0000000 0000000 00000175420 14763776540 023101 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksigner; import static com.android.apksig.Constants.LIBRARY_PAGE_ALIGNMENT_BYTES; import com.android.apksig.ApkSigner; import com.android.apksig.ApkVerifier; import com.android.apksig.SigningCertificateLineage; import com.android.apksig.SigningCertificateLineage.SignerCapabilities; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.MinSdkVersionException; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.RandomAccessFile; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.PublicKey; import java.security.Security; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.security.interfaces.DSAKey; import java.security.interfaces.DSAParams; import java.security.interfaces.ECKey; import java.security.interfaces.RSAKey; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.List; /** * Command-line tool for signing APKs and for checking whether an APK's signature are expected to * verify on Android devices. */ public class ApkSignerTool { private static final String VERSION = "0.9"; private static final String HELP_PAGE_GENERAL = "help.txt"; private static final String HELP_PAGE_SIGN = "help_sign.txt"; private static final String HELP_PAGE_VERIFY = "help_verify.txt"; private static final String HELP_PAGE_ROTATE = "help_rotate.txt"; private static final String HELP_PAGE_LINEAGE = "help_lineage.txt"; private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----"; private static final String END_CERTIFICATE = "-----END CERTIFICATE-----"; private static MessageDigest sha256 = null; private static MessageDigest sha1 = null; private static MessageDigest md5 = null; public static final int ZIP_MAGIC = 0x04034b50; public static void main(String[] params) throws Exception { if ((params.length == 0) || ("--help".equals(params[0])) || ("-h".equals(params[0]))) { printUsage(HELP_PAGE_GENERAL); return; } else if ("--version".equals(params[0])) { System.out.println(VERSION); return; } // BEGIN-AOSP addProviders(); // END-AOSP String cmd = params[0]; try { if ("sign".equals(cmd)) { sign(Arrays.copyOfRange(params, 1, params.length)); return; } else if ("verify".equals(cmd)) { verify(Arrays.copyOfRange(params, 1, params.length)); return; } else if ("rotate".equals(cmd)) { rotate(Arrays.copyOfRange(params, 1, params.length)); return; } else if ("lineage".equals(cmd)) { lineage(Arrays.copyOfRange(params, 1, params.length)); return; } else if ("help".equals(cmd)) { printUsage(HELP_PAGE_GENERAL); return; } else if ("version".equals(cmd)) { System.out.println(VERSION); return; } else { throw new ParameterException( "Unsupported command: " + cmd + ". See --help for supported commands"); } } catch (ParameterException | OptionsParser.OptionsException e) { System.err.println(e.getMessage()); System.exit(1); return; } } // BEGIN-AOSP /** * Adds additional security providers to add support for signature algorithms not covered by * the default providers. */ private static void addProviders() { try { Security.addProvider(new org.conscrypt.OpenSSLProvider()); } catch (UnsatisfiedLinkError e) { // This is expected if the library path does not include the native conscrypt library; // the default providers support all but PSS algorithms. } } // END-AOSP private static void sign(String[] params) throws Exception { if (params.length == 0) { printUsage(HELP_PAGE_SIGN); return; } File outputApk = null; File inputApk = null; boolean verbose = false; boolean v1SigningEnabled = true; boolean v2SigningEnabled = true; boolean v3SigningEnabled = true; boolean v4SigningEnabled = true; boolean forceSourceStampOverwrite = false; boolean sourceStampTimestampEnabled = true; boolean alignFileSize = false; boolean verityEnabled = false; boolean debuggableApkPermitted = true; boolean alignmentPreserved = false; int libPageAlignment = LIBRARY_PAGE_ALIGNMENT_BYTES; int minSdkVersion = 1; boolean minSdkVersionSpecified = false; int maxSdkVersion = Integer.MAX_VALUE; int rotationMinSdkVersion = V3SchemeConstants.DEFAULT_ROTATION_MIN_SDK_VERSION; boolean rotationTargetsDevRelease = false; List signers = new ArrayList<>(1); SignerParams signerParams = new SignerParams(); SigningCertificateLineage lineage = null; SignerParams sourceStampSignerParams = new SignerParams(); SigningCertificateLineage sourceStampLineage = null; List providers = new ArrayList<>(); ProviderInstallSpec providerParams = new ProviderInstallSpec(); OptionsParser optionsParser = new OptionsParser(params); String optionName; String optionOriginalForm = null; boolean v4SigningFlagFound = false; boolean sourceStampFlagFound = false; boolean deterministicDsaSigning = false; boolean otherSignersSignaturesPreserved = false; while ((optionName = optionsParser.nextOption()) != null) { optionOriginalForm = optionsParser.getOptionOriginalForm(); if (("help".equals(optionName)) || ("h".equals(optionName))) { printUsage(HELP_PAGE_SIGN); return; } else if ("out".equals(optionName)) { outputApk = new File(optionsParser.getRequiredValue("Output file name")); } else if ("in".equals(optionName)) { inputApk = new File(optionsParser.getRequiredValue("Input file name")); } else if ("min-sdk-version".equals(optionName)) { minSdkVersion = optionsParser.getRequiredIntValue("Mininimum API Level"); minSdkVersionSpecified = true; } else if ("max-sdk-version".equals(optionName)) { maxSdkVersion = optionsParser.getRequiredIntValue("Maximum API Level"); } else if ("rotation-min-sdk-version".equals(optionName)) { rotationMinSdkVersion = optionsParser.getRequiredIntValue( "Minimum API Level for Rotation"); } else if ("rotation-targets-dev-release".equals(optionName)) { rotationTargetsDevRelease = optionsParser.getOptionalBooleanValue(true); } else if ("v1-signing-enabled".equals(optionName)) { v1SigningEnabled = optionsParser.getOptionalBooleanValue(true); } else if ("v2-signing-enabled".equals(optionName)) { v2SigningEnabled = optionsParser.getOptionalBooleanValue(true); } else if ("v3-signing-enabled".equals(optionName)) { v3SigningEnabled = optionsParser.getOptionalBooleanValue(true); } else if ("v4-signing-enabled".equals(optionName)) { v4SigningEnabled = optionsParser.getOptionalBooleanValue(true); v4SigningFlagFound = true; } else if ("force-stamp-overwrite".equals(optionName)) { forceSourceStampOverwrite = optionsParser.getOptionalBooleanValue(true); } else if ("stamp-timestamp-enabled".equals(optionName)) { sourceStampTimestampEnabled = optionsParser.getOptionalBooleanValue(true); } else if ("align-file-size".equals(optionName)) { alignFileSize = true; } else if ("verity-enabled".equals(optionName)) { verityEnabled = optionsParser.getOptionalBooleanValue(true); } else if ("debuggable-apk-permitted".equals(optionName)) { debuggableApkPermitted = optionsParser.getOptionalBooleanValue(true); } else if ("alignment-preserved".equals(optionName)) { alignmentPreserved = optionsParser.getOptionalBooleanValue(true); } else if ("lib-page-alignment".equals(optionName)) { libPageAlignment = optionsParser.getRequiredIntValue( "Native library page alignment size in bytes"); } else if ("next-signer".equals(optionName)) { if (!signerParams.isEmpty()) { signers.add(signerParams); signerParams = new SignerParams(); } } else if ("signer-for-min-sdk-version".equals(optionName)) { if (!signerParams.isEmpty()) { signers.add(signerParams); signerParams = new SignerParams(); } signerParams.setMinSdkVersion(optionsParser.getRequiredIntValue( "Mininimum API Level for signing config")); } else if ("ks".equals(optionName)) { signerParams.setKeystoreFile(optionsParser.getRequiredValue("KeyStore file")); } else if ("ks-key-alias".equals(optionName)) { signerParams.setKeystoreKeyAlias( optionsParser.getRequiredValue("KeyStore key alias")); } else if ("ks-pass".equals(optionName)) { signerParams.setKeystorePasswordSpec( optionsParser.getRequiredValue("KeyStore password")); } else if ("key-pass".equals(optionName)) { signerParams.setKeyPasswordSpec(optionsParser.getRequiredValue("Key password")); } else if ("pass-encoding".equals(optionName)) { String charsetName = optionsParser.getRequiredValue("Password character encoding"); try { signerParams.setPasswordCharset( PasswordRetriever.getCharsetByName(charsetName)); } catch (IllegalArgumentException e) { throw new ParameterException( "Unsupported password character encoding requested using" + " --pass-encoding: " + charsetName); } } else if ("v1-signer-name".equals(optionName)) { signerParams.setV1SigFileBasename( optionsParser.getRequiredValue("JAR signature file basename")); } else if ("ks-type".equals(optionName)) { signerParams.setKeystoreType(optionsParser.getRequiredValue("KeyStore type")); } else if ("ks-provider-name".equals(optionName)) { signerParams.setKeystoreProviderName( optionsParser.getRequiredValue("JCA KeyStore Provider name")); } else if ("ks-provider-class".equals(optionName)) { signerParams.setKeystoreProviderClass( optionsParser.getRequiredValue("JCA KeyStore Provider class name")); } else if ("ks-provider-arg".equals(optionName)) { signerParams.setKeystoreProviderArg( optionsParser.getRequiredValue( "JCA KeyStore Provider constructor argument")); } else if ("key".equals(optionName)) { signerParams.setKeyFile(optionsParser.getRequiredValue("Private key file")); } else if ("cert".equals(optionName)) { signerParams.setCertFile(optionsParser.getRequiredValue("Certificate file")); } else if ("signer-lineage".equals(optionName)) { File lineageFile = new File( optionsParser.getRequiredValue("Lineage file for signing config")); signerParams.setSigningCertificateLineage(getLineageFromInputFile(lineageFile)); } else if ("lineage".equals(optionName)) { File lineageFile = new File(optionsParser.getRequiredValue("Lineage file")); lineage = getLineageFromInputFile(lineageFile); } else if ("v".equals(optionName) || "verbose".equals(optionName)) { verbose = optionsParser.getOptionalBooleanValue(true); } else if ("next-provider".equals(optionName)) { if (!providerParams.isEmpty()) { providers.add(providerParams); providerParams = new ProviderInstallSpec(); } } else if ("provider-class".equals(optionName)) { providerParams.className = optionsParser.getRequiredValue("JCA Provider class name"); } else if ("provider-arg".equals(optionName)) { providerParams.constructorParam = optionsParser.getRequiredValue("JCA Provider constructor argument"); } else if ("provider-pos".equals(optionName)) { providerParams.position = optionsParser.getRequiredIntValue("JCA Provider position"); } else if ("stamp-signer".equals(optionName)) { sourceStampFlagFound = true; sourceStampSignerParams = processSignerParams(optionsParser); } else if ("stamp-lineage".equals(optionName)) { File stampLineageFile = new File( optionsParser.getRequiredValue("Stamp Lineage File")); sourceStampLineage = getLineageFromInputFile(stampLineageFile); } else if ("deterministic-dsa-signing".equals(optionName)) { deterministicDsaSigning = optionsParser.getOptionalBooleanValue(false); } else if ("append-signature".equals(optionName)) { otherSignersSignaturesPreserved = optionsParser.getOptionalBooleanValue(true); } else { throw new ParameterException( "Unsupported option: " + optionOriginalForm + ". See --help for supported" + " options."); } } if (!signerParams.isEmpty()) { signers.add(signerParams); } signerParams = null; if (!providerParams.isEmpty()) { providers.add(providerParams); } providerParams = null; if (signers.isEmpty()) { throw new ParameterException("At least one signer must be specified"); } params = optionsParser.getRemainingParams(); if (inputApk != null) { // Input APK has been specified via preceding parameters. We don't expect any more // parameters. if (params.length > 0) { throw new ParameterException( "Unexpected parameter(s) after " + optionOriginalForm + ": " + params[0]); } } else { // Input APK has not been specified via preceding parameters. The next parameter is // supposed to be the path to input APK. if (params.length < 1) { throw new ParameterException("Missing input APK"); } else if (params.length > 1) { throw new ParameterException( "Unexpected parameter(s) after input APK (" + params[1] + ")"); } inputApk = new File(params[0]); } if ((minSdkVersionSpecified) && (minSdkVersion > maxSdkVersion)) { throw new ParameterException( "Min API Level (" + minSdkVersion + ") > max API Level (" + maxSdkVersion + ")"); } // Install additional JCA Providers for (ProviderInstallSpec providerInstallSpec : providers) { providerInstallSpec.installProvider(); } ApkSigner.SignerConfig sourceStampSignerConfig = null; List signerConfigs = new ArrayList<>(signers.size()); int signerNumber = 0; try (PasswordRetriever passwordRetriever = new PasswordRetriever()) { for (SignerParams signer : signers) { signerNumber++; signer.setName("signer #" + signerNumber); ApkSigner.SignerConfig signerConfig = getSignerConfig(signer, passwordRetriever, deterministicDsaSigning); if (signerConfig == null) { return; } signerConfigs.add(signerConfig); } if (sourceStampFlagFound) { sourceStampSignerParams.setName("stamp signer"); sourceStampSignerConfig = getSignerConfig(sourceStampSignerParams, passwordRetriever, deterministicDsaSigning); if (sourceStampSignerConfig == null) { return; } } } if (outputApk == null) { outputApk = inputApk; } File tmpOutputApk; if (inputApk.getCanonicalPath().equals(outputApk.getCanonicalPath())) { tmpOutputApk = File.createTempFile("apksigner", ".apk"); tmpOutputApk.deleteOnExit(); } else { tmpOutputApk = outputApk; } ApkSigner.Builder apkSignerBuilder = new ApkSigner.Builder(signerConfigs) .setInputApk(inputApk) .setOutputApk(tmpOutputApk) .setOtherSignersSignaturesPreserved(otherSignersSignaturesPreserved) .setV1SigningEnabled(v1SigningEnabled) .setV2SigningEnabled(v2SigningEnabled) .setV3SigningEnabled(v3SigningEnabled) .setV4SigningEnabled(v4SigningEnabled) .setForceSourceStampOverwrite(forceSourceStampOverwrite) .setSourceStampTimestampEnabled(sourceStampTimestampEnabled) .setAlignFileSize(alignFileSize) .setVerityEnabled(verityEnabled) .setV4ErrorReportingEnabled(v4SigningEnabled && v4SigningFlagFound) .setDebuggableApkPermitted(debuggableApkPermitted) .setSigningCertificateLineage(lineage) .setMinSdkVersionForRotation(rotationMinSdkVersion) .setRotationTargetsDevRelease(rotationTargetsDevRelease) .setAlignmentPreserved(alignmentPreserved) .setLibraryPageAlignmentBytes(libPageAlignment); if (minSdkVersionSpecified) { apkSignerBuilder.setMinSdkVersion(minSdkVersion); } if (v4SigningEnabled) { final File outputV4SignatureFile = new File(outputApk.getCanonicalPath() + ".idsig"); Files.deleteIfExists(outputV4SignatureFile.toPath()); apkSignerBuilder.setV4SignatureOutputFile(outputV4SignatureFile); } if (sourceStampSignerConfig != null) { apkSignerBuilder.setSourceStampSignerConfig(sourceStampSignerConfig) .setSourceStampSigningCertificateLineage(sourceStampLineage); } ApkSigner apkSigner = apkSignerBuilder.build(); try { apkSigner.sign(); } catch (MinSdkVersionException e) { String msg = e.getMessage(); if (!msg.endsWith(".")) { msg += '.'; } throw new MinSdkVersionException( "Failed to determine APK's minimum supported platform version" + ". Use --min-sdk-version to override", e); } if (!tmpOutputApk.getCanonicalPath().equals(outputApk.getCanonicalPath())) { Files.move( tmpOutputApk.toPath(), outputApk.toPath(), StandardCopyOption.REPLACE_EXISTING); } if (verbose) { System.out.println("Signed"); } } private static ApkSigner.SignerConfig getSignerConfig(SignerParams signer, PasswordRetriever passwordRetriever, boolean deterministicDsaSigning) { try { signer.loadPrivateKeyAndCerts(passwordRetriever); } catch (ParameterException e) { System.err.println( "Failed to load signer \"" + signer.getName() + "\": " + e.getMessage()); System.exit(2); return null; } catch (Exception e) { System.err.println("Failed to load signer \"" + signer.getName() + "\""); e.printStackTrace(); System.exit(2); return null; } String v1SigBasename; if (signer.getV1SigFileBasename() != null) { v1SigBasename = signer.getV1SigFileBasename(); } else if (signer.getKeystoreKeyAlias() != null) { v1SigBasename = signer.getKeystoreKeyAlias(); } else if (signer.getKeyFile() != null) { String keyFileName = new File(signer.getKeyFile()).getName(); int delimiterIndex = keyFileName.indexOf('.'); if (delimiterIndex == -1) { v1SigBasename = keyFileName; } else { v1SigBasename = keyFileName.substring(0, delimiterIndex); } } else { throw new RuntimeException("Neither KeyStore key alias nor private key file available"); } ApkSigner.SignerConfig.Builder signerConfigBuilder = new ApkSigner.SignerConfig.Builder( v1SigBasename, signer.getKeyConfig(), signer.getCerts(), deterministicDsaSigning); SigningCertificateLineage lineage = signer.getSigningCertificateLineage(); int minSdkVersion = signer.getMinSdkVersion(); if (minSdkVersion > 0) { signerConfigBuilder.setLineageForMinSdkVersion(lineage, minSdkVersion); } ApkSigner.SignerConfig signerConfig = signerConfigBuilder.build(); return signerConfig; } private static void verify(String[] params) throws Exception { if (params.length == 0) { printUsage(HELP_PAGE_VERIFY); return; } File inputApk = null; int minSdkVersion = 1; boolean minSdkVersionSpecified = false; int maxSdkVersion = Integer.MAX_VALUE; boolean maxSdkVersionSpecified = false; boolean printCerts = false; boolean printCertsPem = false; boolean verbose = false; boolean warningsTreatedAsErrors = false; boolean verifySourceStamp = false; File v4SignatureFile = null; OptionsParser optionsParser = new OptionsParser(params); String optionName; String optionOriginalForm = null; String sourceCertDigest = null; while ((optionName = optionsParser.nextOption()) != null) { optionOriginalForm = optionsParser.getOptionOriginalForm(); if ("min-sdk-version".equals(optionName)) { minSdkVersion = optionsParser.getRequiredIntValue("Mininimum API Level"); minSdkVersionSpecified = true; } else if ("max-sdk-version".equals(optionName)) { maxSdkVersion = optionsParser.getRequiredIntValue("Maximum API Level"); maxSdkVersionSpecified = true; } else if ("print-certs".equals(optionName)) { printCerts = optionsParser.getOptionalBooleanValue(true); } else if ("print-certs-pem".equals(optionName)) { printCertsPem = optionsParser.getOptionalBooleanValue(true); // If the PEM output of the certs is requested, this implicitly implies the // cert details should be printed. if (printCertsPem && !printCerts) { printCerts = true; } } else if (("v".equals(optionName)) || ("verbose".equals(optionName))) { verbose = optionsParser.getOptionalBooleanValue(true); } else if ("Werr".equals(optionName)) { warningsTreatedAsErrors = optionsParser.getOptionalBooleanValue(true); } else if (("help".equals(optionName)) || ("h".equals(optionName))) { printUsage(HELP_PAGE_VERIFY); return; } else if ("v4-signature-file".equals(optionName)) { v4SignatureFile = new File(optionsParser.getRequiredValue( "Input V4 Signature File")); } else if ("in".equals(optionName)) { inputApk = new File(optionsParser.getRequiredValue("Input APK file")); } else if ("verify-source-stamp".equals(optionName)) { verifySourceStamp = optionsParser.getOptionalBooleanValue(true); } else if ("stamp-cert-digest".equals(optionName)) { sourceCertDigest = optionsParser.getRequiredValue( "Expected source stamp certificate digest"); } else { throw new ParameterException( "Unsupported option: " + optionOriginalForm + ". See --help for supported" + " options."); } } params = optionsParser.getRemainingParams(); if (inputApk != null) { // Input APK has been specified in preceding parameters. We don't expect any more // parameters. if (params.length > 0) { throw new ParameterException( "Unexpected parameter(s) after " + optionOriginalForm + ": " + params[0]); } } else { // Input APK has not been specified in preceding parameters. The next parameter is // supposed to be the input APK. if (params.length < 1) { throw new ParameterException("Missing APK"); } else if (params.length > 1) { throw new ParameterException( "Unexpected parameter(s) after APK (" + params[1] + ")"); } inputApk = new File(params[0]); } if ((minSdkVersionSpecified) && (maxSdkVersionSpecified) && (minSdkVersion > maxSdkVersion)) { throw new ParameterException( "Min API Level (" + minSdkVersion + ") > max API Level (" + maxSdkVersion + ")"); } ApkVerifier.Builder apkVerifierBuilder = new ApkVerifier.Builder(inputApk); if (minSdkVersionSpecified) { apkVerifierBuilder.setMinCheckedPlatformVersion(minSdkVersion); } if (maxSdkVersionSpecified) { apkVerifierBuilder.setMaxCheckedPlatformVersion(maxSdkVersion); } if (v4SignatureFile != null) { if (!v4SignatureFile.exists()) { throw new ParameterException("V4 signature file does not exist: " + v4SignatureFile.getCanonicalPath()); } apkVerifierBuilder.setV4SignatureFile(v4SignatureFile); } ApkVerifier apkVerifier = apkVerifierBuilder.build(); ApkVerifier.Result result; try { result = verifySourceStamp ? apkVerifier.verifySourceStamp(sourceCertDigest) : apkVerifier.verify(); } catch (MinSdkVersionException e) { String msg = e.getMessage(); if (!msg.endsWith(".")) { msg += '.'; } throw new MinSdkVersionException( "Failed to determine APK's minimum supported platform version" + ". Use --min-sdk-version to override", e); } boolean verified = result.isVerified(); ApkVerifier.Result.SourceStampInfo sourceStampInfo = result.getSourceStampInfo(); boolean warningsEncountered = false; if (verified) { List signerCerts = result.getSignerCertificates(); if (verbose) { System.out.println("Verifies"); System.out.println( "Verified using v1 scheme (JAR signing): " + result.isVerifiedUsingV1Scheme()); System.out.println( "Verified using v2 scheme (APK Signature Scheme v2): " + result.isVerifiedUsingV2Scheme()); System.out.println( "Verified using v3 scheme (APK Signature Scheme v3): " + result.isVerifiedUsingV3Scheme()); System.out.println( "Verified using v3.1 scheme (APK Signature Scheme v3.1): " + result.isVerifiedUsingV31Scheme()); System.out.println( "Verified using v4 scheme (APK Signature Scheme v4): " + result.isVerifiedUsingV4Scheme()); System.out.println("Verified for SourceStamp: " + result.isSourceStampVerified()); if (!verifySourceStamp) { System.out.println("Number of signers: " + signerCerts.size()); } } if (printCerts) { // The v3.1 signature scheme allows key rotation to target T+ while the original // signing key can still be used with v3.0; if a v3.1 block is present then also // include the target SDK versions for both rotation and the original signing key. if (result.isVerifiedUsingV31Scheme()) { for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { printCertificate(signer.getCertificate(), "Signer (minSdkVersion=" + signer.getMinSdkVersion() + (signer.getRotationTargetsDevRelease() ? " (dev release=true)" : "") + ", maxSdkVersion=" + signer.getMaxSdkVersion() + ")", verbose, printCertsPem); } for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { printCertificate(signer.getCertificate(), "Signer (minSdkVersion=" + signer.getMinSdkVersion() + ", maxSdkVersion=" + signer.getMaxSdkVersion() + ")", verbose, printCertsPem); } } else { int signerNumber = 0; for (X509Certificate signerCert : signerCerts) { signerNumber++; printCertificate(signerCert, "Signer #" + signerNumber, verbose, printCertsPem); } } if (sourceStampInfo != null) { printCertificate(sourceStampInfo.getCertificate(), "Source Stamp Signer", verbose, printCertsPem); } } } else { System.err.println("DOES NOT VERIFY"); } for (ApkVerifier.IssueWithParams error : result.getErrors()) { System.err.println("ERROR: " + error); } @SuppressWarnings("resource") // false positive -- this resource is not opened here PrintStream warningsOut = warningsTreatedAsErrors ? System.err : System.out; for (ApkVerifier.IssueWithParams warning : result.getWarnings()) { warningsEncountered = true; warningsOut.println("WARNING: " + warning); } for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) { String signerName = signer.getName(); for (ApkVerifier.IssueWithParams error : signer.getErrors()) { System.err.println("ERROR: JAR signer " + signerName + ": " + error); } for (ApkVerifier.IssueWithParams warning : signer.getWarnings()) { warningsEncountered = true; warningsOut.println("WARNING: JAR signer " + signerName + ": " + warning); } } for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); for (ApkVerifier.IssueWithParams error : signer.getErrors()) { System.err.println( "ERROR: APK Signature Scheme v2 " + signerName + ": " + error); } for (ApkVerifier.IssueWithParams warning : signer.getWarnings()) { warningsEncountered = true; warningsOut.println( "WARNING: APK Signature Scheme v2 " + signerName + ": " + warning); } } for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); for (ApkVerifier.IssueWithParams error : signer.getErrors()) { System.err.println( "ERROR: APK Signature Scheme v3 " + signerName + ": " + error); } for (ApkVerifier.IssueWithParams warning : signer.getWarnings()) { warningsEncountered = true; warningsOut.println( "WARNING: APK Signature Scheme v3 " + signerName + ": " + warning); } } for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1) + "(minSdkVersion=" + signer.getMinSdkVersion() + ", maxSdkVersion=" + signer.getMaxSdkVersion() + ")"; for (ApkVerifier.IssueWithParams error : signer.getErrors()) { System.err.println( "ERROR: APK Signature Scheme v3.1 " + signerName + ": " + error); } for (ApkVerifier.IssueWithParams warning : signer.getWarnings()) { warningsEncountered = true; warningsOut.println( "WARNING: APK Signature Scheme v3.1 " + signerName + ": " + warning); } } if (sourceStampInfo != null) { for (ApkVerifier.IssueWithParams error : sourceStampInfo.getErrors()) { System.err.println("ERROR: SourceStamp: " + error); } for (ApkVerifier.IssueWithParams warning : sourceStampInfo.getWarnings()) { warningsOut.println("WARNING: SourceStamp: " + warning); } for (ApkVerifier.IssueWithParams infoMessage : sourceStampInfo.getInfoMessages()) { System.out.println("INFO: SourceStamp: " + infoMessage); } } if (!verified) { System.exit(1); return; } if ((warningsTreatedAsErrors) && (warningsEncountered)) { System.exit(1); return; } } private static void rotate(String[] params) throws Exception { if (params.length == 0) { printUsage(HELP_PAGE_ROTATE); return; } File outputKeyLineage = null; File inputKeyLineage = null; boolean verbose = false; SignerParams oldSignerParams = null; SignerParams newSignerParams = null; int minSdkVersion = 0; List providers = new ArrayList<>(); ProviderInstallSpec providerParams = new ProviderInstallSpec(); OptionsParser optionsParser = new OptionsParser(params); String optionName; String optionOriginalForm = null; while ((optionName = optionsParser.nextOption()) != null) { optionOriginalForm = optionsParser.getOptionOriginalForm(); if (("help".equals(optionName)) || ("h".equals(optionName))) { printUsage(HELP_PAGE_ROTATE); return; } else if ("out".equals(optionName)) { outputKeyLineage = new File(optionsParser.getRequiredValue("Output file name")); } else if ("in".equals(optionName)) { inputKeyLineage = new File(optionsParser.getRequiredValue("Input file name")); } else if ("old-signer".equals(optionName)) { oldSignerParams = processSignerParams(optionsParser); } else if ("new-signer".equals(optionName)) { newSignerParams = processSignerParams(optionsParser); } else if ("min-sdk-version".equals(optionName)) { minSdkVersion = optionsParser.getRequiredIntValue("Mininimum API Level"); } else if (("v".equals(optionName)) || ("verbose".equals(optionName))) { verbose = optionsParser.getOptionalBooleanValue(true); } else if ("next-provider".equals(optionName)) { if (!providerParams.isEmpty()) { providers.add(providerParams); providerParams = new ProviderInstallSpec(); } } else if ("provider-class".equals(optionName)) { providerParams.className = optionsParser.getRequiredValue("JCA Provider class name"); } else if ("provider-arg".equals(optionName)) { providerParams.constructorParam = optionsParser.getRequiredValue("JCA Provider constructor argument"); } else if ("provider-pos".equals(optionName)) { providerParams.position = optionsParser.getRequiredIntValue("JCA Provider position"); } else { throw new ParameterException( "Unsupported option: " + optionOriginalForm + ". See --help for supported" + " options."); } } if (!providerParams.isEmpty()) { providers.add(providerParams); } providerParams = null; if (oldSignerParams.isEmpty()) { throw new ParameterException("Signer parameters for old signer not present"); } if (newSignerParams.isEmpty()) { throw new ParameterException("Signer parameters for new signer not present"); } if (outputKeyLineage == null) { throw new ParameterException("Output lineage file parameter not present"); } params = optionsParser.getRemainingParams(); if (params.length > 0) { throw new ParameterException( "Unexpected parameter(s) after " + optionOriginalForm + ": " + params[0]); } // Install additional JCA Providers for (ProviderInstallSpec providerInstallSpec : providers) { providerInstallSpec.installProvider(); } try (PasswordRetriever passwordRetriever = new PasswordRetriever()) { // populate SignerConfig for old signer oldSignerParams.setName("old signer"); loadPrivateKeyAndCerts(oldSignerParams, passwordRetriever); SigningCertificateLineage.SignerConfig oldSignerConfig = new SigningCertificateLineage.SignerConfig.Builder( oldSignerParams.getKeyConfig(), oldSignerParams.getCerts().get(0)) .build(); // TOOD: don't require private key newSignerParams.setName("new signer"); loadPrivateKeyAndCerts(newSignerParams, passwordRetriever); SigningCertificateLineage.SignerConfig newSignerConfig = new SigningCertificateLineage.SignerConfig.Builder( newSignerParams.getKeyConfig(), newSignerParams.getCerts().get(0)) .build(); // ok we're all set up, let's rotate! SigningCertificateLineage lineage; if (inputKeyLineage != null) { // we already have history, add the new key to the end of it lineage = getLineageFromInputFile(inputKeyLineage); lineage.updateSignerCapabilities( oldSignerConfig, oldSignerParams.getSignerCapabilitiesBuilder().build()); lineage = lineage.spawnDescendant( oldSignerConfig, newSignerConfig, newSignerParams.getSignerCapabilitiesBuilder().build()); } else { // this is the first entry in our signing history, create a new one from the old and // new signer info lineage = new SigningCertificateLineage.Builder(oldSignerConfig, newSignerConfig) .setMinSdkVersion(minSdkVersion) .setOriginalCapabilities( oldSignerParams.getSignerCapabilitiesBuilder().build()) .setNewCapabilities( newSignerParams.getSignerCapabilitiesBuilder().build()) .build(); } // and write out the result lineage.writeToFile(outputKeyLineage); } if (verbose) { System.out.println("Rotation entry generated."); } } public static void lineage(String[] params) throws Exception { if (params.length == 0) { printUsage(HELP_PAGE_LINEAGE); return; } boolean verbose = false; boolean printCerts = false; boolean printCertsPem = false; boolean lineageUpdated = false; File inputKeyLineage = null; File outputKeyLineage = null; String optionName; OptionsParser optionsParser = new OptionsParser(params); List signers = new ArrayList<>(1); while ((optionName = optionsParser.nextOption()) != null) { if (("help".equals(optionName)) || ("h".equals(optionName))) { printUsage(HELP_PAGE_LINEAGE); return; } else if ("in".equals(optionName)) { inputKeyLineage = new File(optionsParser.getRequiredValue("Input file name")); } else if ("out".equals(optionName)) { outputKeyLineage = new File(optionsParser.getRequiredValue("Output file name")); } else if ("signer".equals(optionName)) { SignerParams signerParams = processSignerParams(optionsParser); signers.add(signerParams); } else if (("v".equals(optionName)) || ("verbose".equals(optionName))) { verbose = optionsParser.getOptionalBooleanValue(true); } else if ("print-certs".equals(optionName)) { printCerts = optionsParser.getOptionalBooleanValue(true); } else if ("print-certs-pem".equals(optionName)) { printCertsPem = optionsParser.getOptionalBooleanValue(true); // If the PEM output of the certs is requested, this implicitly implies the // cert details should be printed. if (printCertsPem && !printCerts) { printCerts = true; } } else { throw new ParameterException( "Unsupported option: " + optionsParser.getOptionOriginalForm() + ". See --help for supported options."); } } if (inputKeyLineage == null) { throw new ParameterException("Input lineage file parameter not present"); } SigningCertificateLineage lineage = getLineageFromInputFile(inputKeyLineage); try (PasswordRetriever passwordRetriever = new PasswordRetriever()) { for (int i = 0; i < signers.size(); i++) { SignerParams signerParams = signers.get(i); signerParams.setName("signer #" + (i + 1)); loadPrivateKeyAndCerts(signerParams, passwordRetriever); SigningCertificateLineage.SignerConfig signerConfig = new SigningCertificateLineage.SignerConfig.Builder( signerParams.getKeyConfig(), signerParams.getCerts().get(0)) .build(); try { // since only the caller specified capabilities will be updated a direct // comparison between the original capabilities of the signer and the // signerCapabilitiesBuilder object with potential default values is not // possible. Instead the capabilities should be updated first, then the new // capabilities can be compared against the original to determine if the // lineage has been updated and needs to be written out to a file. SignerCapabilities origCapabilities = lineage.getSignerCapabilities( signerConfig); lineage.updateSignerCapabilities( signerConfig, signerParams.getSignerCapabilitiesBuilder().build()); SignerCapabilities newCapabilities = lineage.getSignerCapabilities( signerConfig); if (origCapabilities.equals(newCapabilities)) { if (verbose) { System.out.println( "The provided signer capabilities for " + signerParams.getName() + " are unchanged."); } } else { lineageUpdated = true; if (verbose) { System.out.println( "Updated signer capabilities for " + signerParams.getName() + "."); } } } catch (IllegalArgumentException e) { throw new ParameterException( "The signer " + signerParams.getName() + " was not found in the specified lineage."); } } } if (printCerts) { List signingCerts = lineage.getCertificatesInLineage(); for (int i = 0; i < signingCerts.size(); i++) { X509Certificate signerCert = signingCerts.get(i); SignerCapabilities signerCapabilities = lineage.getSignerCapabilities(signerCert); printCertificate(signerCert, "Signer #" + (i + 1) + " in lineage", verbose, printCertsPem); printCapabilities(signerCapabilities); } } if (lineageUpdated) { if (outputKeyLineage != null) { lineage.writeToFile(outputKeyLineage); if (verbose) { System.out.println("Updated lineage saved to " + outputKeyLineage + "."); } } else { throw new ParameterException( "The lineage was modified but an output file for the lineage was not " + "specified"); } } } /** * Extracts the Signing Certificate Lineage from the provided lineage or APK file. */ private static SigningCertificateLineage getLineageFromInputFile(File inputLineageFile) throws ParameterException { try (RandomAccessFile f = new RandomAccessFile(inputLineageFile, "r")) { if (f.length() < 4) { throw new ParameterException("The input file is not a valid lineage file."); } DataSource apk = DataSources.asDataSource(f); int magicValue = apk.getByteBuffer(0, 4).order(ByteOrder.LITTLE_ENDIAN).getInt(); if (magicValue == SigningCertificateLineage.MAGIC) { return SigningCertificateLineage.readFromFile(inputLineageFile); } else if (magicValue == ZIP_MAGIC) { return SigningCertificateLineage.readFromApkFile(inputLineageFile); } else { throw new ParameterException("The input file is not a valid lineage file."); } } catch (IOException | ApkFormatException | IllegalArgumentException e) { throw new ParameterException(e.getMessage()); } } private static SignerParams processSignerParams(OptionsParser optionsParser) throws OptionsParser.OptionsException, ParameterException { SignerParams signerParams = new SignerParams(); String optionName; while ((optionName = optionsParser.nextOption()) != null) { if ("ks".equals(optionName)) { signerParams.setKeystoreFile(optionsParser.getRequiredValue("KeyStore file")); } else if ("ks-key-alias".equals(optionName)) { signerParams.setKeystoreKeyAlias( optionsParser.getRequiredValue("KeyStore key alias")); } else if ("ks-pass".equals(optionName)) { signerParams.setKeystorePasswordSpec( optionsParser.getRequiredValue("KeyStore password")); } else if ("key-pass".equals(optionName)) { signerParams.setKeyPasswordSpec(optionsParser.getRequiredValue("Key password")); } else if ("pass-encoding".equals(optionName)) { String charsetName = optionsParser.getRequiredValue("Password character encoding"); try { signerParams.setPasswordCharset( PasswordRetriever.getCharsetByName(charsetName)); } catch (IllegalArgumentException e) { throw new ParameterException( "Unsupported password character encoding requested using" + " --pass-encoding: " + charsetName); } } else if ("ks-type".equals(optionName)) { signerParams.setKeystoreType(optionsParser.getRequiredValue("KeyStore type")); } else if ("ks-provider-name".equals(optionName)) { signerParams.setKeystoreProviderName( optionsParser.getRequiredValue("JCA KeyStore Provider name")); } else if ("ks-provider-class".equals(optionName)) { signerParams.setKeystoreProviderClass( optionsParser.getRequiredValue("JCA KeyStore Provider class name")); } else if ("ks-provider-arg".equals(optionName)) { signerParams.setKeystoreProviderArg( optionsParser.getRequiredValue( "JCA KeyStore Provider constructor argument")); } else if ("key".equals(optionName)) { signerParams.setKeyFile(optionsParser.getRequiredValue("Private key file")); } else if ("cert".equals(optionName)) { signerParams.setCertFile(optionsParser.getRequiredValue("Certificate file")); } else if ("set-installed-data".equals(optionName)) { signerParams .getSignerCapabilitiesBuilder() .setInstalledData(optionsParser.getOptionalBooleanValue(true)); } else if ("set-shared-uid".equals(optionName)) { signerParams .getSignerCapabilitiesBuilder() .setSharedUid(optionsParser.getOptionalBooleanValue(true)); } else if ("set-permission".equals(optionName)) { signerParams .getSignerCapabilitiesBuilder() .setPermission(optionsParser.getOptionalBooleanValue(true)); } else if ("set-rollback".equals(optionName)) { signerParams .getSignerCapabilitiesBuilder() .setRollback(optionsParser.getOptionalBooleanValue(true)); } else if ("set-auth".equals(optionName)) { signerParams .getSignerCapabilitiesBuilder() .setAuth(optionsParser.getOptionalBooleanValue(true)); } else { // not a signer option, reset optionsParser and let caller deal with it optionsParser.putOption(); break; } } if (signerParams.isEmpty()) { throw new ParameterException("Signer specified without arguments"); } return signerParams; } private static void printUsage(String page) { try (BufferedReader in = new BufferedReader( new InputStreamReader( ApkSignerTool.class.getResourceAsStream(page), StandardCharsets.UTF_8))) { String line; while ((line = in.readLine()) != null) { System.out.println(line); } } catch (IOException e) { throw new RuntimeException("Failed to read " + page + " resource"); } } /** * @see #printCertificate(X509Certificate, String, boolean, boolean) */ public static void printCertificate(X509Certificate cert, String name, boolean verbose) throws NoSuchAlgorithmException, CertificateEncodingException { printCertificate(cert, name, verbose, false); } /** * Prints details from the provided certificate to stdout. * * @param cert the certificate to be displayed. * @param name the name to be used to identify the certificate. * @param verbose boolean indicating whether public key details from the certificate should be * displayed. * @param pemOutput boolean indicating whether the PEM encoding of the certificate should be * displayed. * @throws NoSuchAlgorithmException if an instance of MD5, SHA-1, or SHA-256 cannot be * obtained. * @throws CertificateEncodingException if an error is encountered when encoding the * certificate. */ public static void printCertificate(X509Certificate cert, String name, boolean verbose, boolean pemOutput) throws NoSuchAlgorithmException, CertificateEncodingException { if (cert == null) { throw new NullPointerException("cert == null"); } if (sha256 == null || sha1 == null || md5 == null) { sha256 = MessageDigest.getInstance("SHA-256"); sha1 = MessageDigest.getInstance("SHA-1"); md5 = MessageDigest.getInstance("MD5"); } System.out.println(name + " certificate DN: " + cert.getSubjectDN()); byte[] encodedCert = cert.getEncoded(); System.out.println(name + " certificate SHA-256 digest: " + HexEncoding.encode( sha256.digest(encodedCert))); System.out.println(name + " certificate SHA-1 digest: " + HexEncoding.encode( sha1.digest(encodedCert))); System.out.println( name + " certificate MD5 digest: " + HexEncoding.encode(md5.digest(encodedCert))); if (verbose) { PublicKey publicKey = cert.getPublicKey(); System.out.println(name + " key algorithm: " + publicKey.getAlgorithm()); int keySize = -1; if (publicKey instanceof RSAKey) { keySize = ((RSAKey) publicKey).getModulus().bitLength(); } else if (publicKey instanceof ECKey) { keySize = ((ECKey) publicKey).getParams() .getOrder().bitLength(); } else if (publicKey instanceof DSAKey) { // DSA parameters may be inherited from the certificate. We // don't handle this case at the moment. DSAParams dsaParams = ((DSAKey) publicKey).getParams(); if (dsaParams != null) { keySize = dsaParams.getP().bitLength(); } } System.out.println( name + " key size (bits): " + ((keySize != -1) ? String.valueOf(keySize) : "n/a")); byte[] encodedKey = publicKey.getEncoded(); System.out.println(name + " public key SHA-256 digest: " + HexEncoding.encode( sha256.digest(encodedKey))); System.out.println(name + " public key SHA-1 digest: " + HexEncoding.encode( sha1.digest(encodedKey))); System.out.println( name + " public key MD5 digest: " + HexEncoding.encode(md5.digest(encodedKey))); } if (pemOutput) { System.out.println(BEGIN_CERTIFICATE); final int lineWidth = 64; String pemEncodedCert = Base64.getEncoder().encodeToString(cert.getEncoded()); for (int i = 0; i < pemEncodedCert.length(); i += lineWidth) { System.out.println(pemEncodedCert.substring(i, i + lineWidth > pemEncodedCert.length() ? pemEncodedCert.length() : i + lineWidth)); } System.out.println(END_CERTIFICATE); } } /** * Prints the capabilities of the provided object to stdout. Each of the potential * capabilities is displayed along with a boolean indicating whether this object has * that capability. */ public static void printCapabilities(SignerCapabilities capabilities) { System.out.println("Has installed data capability: " + capabilities.hasInstalledData()); System.out.println("Has shared UID capability : " + capabilities.hasSharedUid()); System.out.println("Has permission capability : " + capabilities.hasPermission()); System.out.println("Has rollback capability : " + capabilities.hasRollback()); System.out.println("Has auth capability : " + capabilities.hasAuth()); } private static class ProviderInstallSpec { String className; String constructorParam; Integer position; private boolean isEmpty() { return (className == null) && (constructorParam == null) && (position == null); } private void installProvider() throws Exception { if (className == null) { throw new ParameterException( "JCA Provider class name (--provider-class) must be specified"); } Class providerClass = Class.forName(className); if (!Provider.class.isAssignableFrom(providerClass)) { throw new ParameterException( "JCA Provider class " + providerClass + " not subclass of " + Provider.class.getName()); } Provider provider; if (constructorParam != null) { try { // Single-arg Provider constructor provider = (Provider) providerClass.getConstructor(String.class) .newInstance(constructorParam); } catch (NoSuchMethodException e) { // Starting from JDK 9 the single-arg constructor accepting the configuration // has been replaced by a configure(String) method to be invoked after // instantiating the Provider with the no-arg constructor. provider = (Provider) providerClass.getConstructor().newInstance(); provider = (Provider) providerClass.getMethod("configure", String.class) .invoke(provider, constructorParam); } } else { // No-arg Provider constructor provider = (Provider) providerClass.getConstructor().newInstance(); } if (position == null) { Security.addProvider(provider); } else { Security.insertProviderAt(provider, position); } } } /** * Loads the private key and certificates from either the specified keystore or files specified * in the signer params using the provided passwordRetriever. * * @throws ParameterException if any errors are encountered when attempting to load * the private key and certificates. */ private static void loadPrivateKeyAndCerts(SignerParams params, PasswordRetriever passwordRetriever) throws ParameterException { try { params.loadPrivateKeyAndCerts(passwordRetriever); if (params.getKeystoreKeyAlias() != null) { params.setName(params.getKeystoreKeyAlias()); } else if (params.getKeyFile() != null) { String keyFileName = new File(params.getKeyFile()).getName(); int delimiterIndex = keyFileName.indexOf('.'); if (delimiterIndex == -1) { params.setName(keyFileName); } else { params.setName(keyFileName.substring(0, delimiterIndex)); } } else { throw new RuntimeException( "Neither KeyStore key alias nor private key file available for " + params.getName()); } } catch (ParameterException e) { throw new ParameterException( "Failed to load signer \"" + params.getName() + "\":" + e.getMessage()); } catch (Exception e) { e.printStackTrace(); throw new ParameterException("Failed to load signer \"" + params.getName() + "\""); } } } ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_HexEncoding.java0100644 0000000 0000000 00000000034 14763776540 025676 xustar000000000 0000000 28 mtime=1741684064.5300000 src/apksigner/java/com/android/apksigner/HexEncoding.java0100644 0000000 0000000 00000003472 14763776540 022550 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksigner; import java.nio.ByteBuffer; /** * Hexadecimal encoding where each byte is represented by two hexadecimal digits. */ class HexEncoding { /** Hidden constructor to prevent instantiation. */ private HexEncoding() {} private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); /** * Encodes the provided data as a hexadecimal string. */ public static String encode(byte[] data, int offset, int length) { StringBuilder result = new StringBuilder(length * 2); for (int i = 0; i < length; i++) { byte b = data[offset + i]; result.append(HEX_DIGITS[(b >>> 4) & 0x0f]); result.append(HEX_DIGITS[b & 0x0f]); } return result.toString(); } /** * Encodes the provided data as a hexadecimal string. */ public static String encode(byte[] data) { return encode(data, 0, data.length); } /** * Encodes the remaining bytes of the provided {@link ByteBuffer} as a hexadecimal string. */ public static String encodeRemaining(ByteBuffer data) { return encode(data.array(), data.arrayOffset() + data.position(), data.remaining()); } } ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_OptionsParser.java0100644 0000000 0000000 00000000034 14763776540 026313 xustar000000000 0000000 28 mtime=1741684064.5300000 src/apksigner/java/com/android/apksigner/OptionsParser.java0100644 0000000 0000000 00000016527 14763776540 023172 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksigner; import java.util.Arrays; /** * Parser of command-line options/switches/flags. * *

Supported option formats: *

    *
  • {@code --name value}
  • *
  • {@code --name=value}
  • *
  • {@code -name value}
  • *
  • {@code --name} (boolean options only)
  • *
* *

To use the parser, create an instance, providing it with the command-line parameters, then * iterate over options by invoking {@link #nextOption()} until it returns {@code null}. */ class OptionsParser { private final String[] mParams; private int mIndex; private int mPutBackIndex; private String mLastOptionValue; private String mPutBackLastOptionValue; private String mLastOptionOriginalForm; private String mPutBackLastOptionOriginalForm; /** * Constructs a new {@code OptionsParser} initialized with the provided command-line. */ public OptionsParser(String[] params) { mParams = params.clone(); } /** * Returns the name (without leading dashes) of the next option (starting with the very first * option) or {@code null} if there are no options left. * *

The value of this option can be obtained via {@link #getRequiredValue(String)}, * {@link #getRequiredIntValue(String)}, and {@link #getOptionalBooleanValue(boolean)}. */ public String nextOption() { if (mIndex >= mParams.length) { // No more parameters left return null; } String param = mParams[mIndex]; if (!param.startsWith("-")) { // Not an option return null; } mPutBackIndex = mIndex; mIndex++; mPutBackLastOptionOriginalForm = mLastOptionOriginalForm; mLastOptionOriginalForm = param; mPutBackLastOptionValue = mLastOptionValue; mLastOptionValue = null; if (param.startsWith("--")) { // FORMAT: --name value OR --name=value if ("--".equals(param)) { // End of options marker return null; } int valueDelimiterIndex = param.indexOf('='); if (valueDelimiterIndex != -1) { mLastOptionValue = param.substring(valueDelimiterIndex + 1); mLastOptionOriginalForm = param.substring(0, valueDelimiterIndex); return param.substring("--".length(), valueDelimiterIndex); } else { return param.substring("--".length()); } } else { // FORMAT: -name value return param.substring("-".length()); } } /** * Undoes the last call to nextOption(), if one was made. This allows callers to unwind state * so as not to eat up an option that is meant to be processed elsewhere. */ public void putOption() { mIndex = mPutBackIndex; mLastOptionOriginalForm = mPutBackLastOptionOriginalForm; mLastOptionValue = mPutBackLastOptionValue; } /** * Returns the original form of the current option. The original form includes the leading dash * or dashes. This is intended to be used for referencing the option in error messages. */ public String getOptionOriginalForm() { return mLastOptionOriginalForm; } /** * Returns the value of the current option, throwing an exception if the value is missing. */ public String getRequiredValue(String valueDescription) throws OptionsException { if (mLastOptionValue != null) { String result = mLastOptionValue; mLastOptionValue = null; return result; } if (mIndex >= mParams.length) { // No more parameters left throw new OptionsException( valueDescription + " missing after " + mLastOptionOriginalForm); } String param = mParams[mIndex]; if ("--".equals(param)) { // End of options marker throw new OptionsException( valueDescription + " missing after " + mLastOptionOriginalForm); } mIndex++; return param; } /** * Returns the value of the current numeric option, throwing an exception if the value is * missing or is not numeric. */ public int getRequiredIntValue(String valueDescription) throws OptionsException { String value = getRequiredValue(valueDescription); try { return Integer.parseInt(value); } catch (NumberFormatException e) { throw new OptionsException( valueDescription + " (" + mLastOptionOriginalForm + ") must be a decimal number: " + value); } } /** * Gets the value of the current boolean option. Boolean options are not required to have * explicitly specified values. */ public boolean getOptionalBooleanValue(boolean defaultValue) throws OptionsException { if (mLastOptionValue != null) { // --option=value form String stringValue = mLastOptionValue; mLastOptionValue = null; if ("true".equals(stringValue)) { return true; } else if ("false".equals(stringValue)) { return false; } throw new OptionsException( "Unsupported value for " + mLastOptionOriginalForm + ": " + stringValue + ". Only true or false supported."); } // --option (true|false) form OR just --option if (mIndex >= mParams.length) { return defaultValue; } String stringValue = mParams[mIndex]; if ("true".equals(stringValue)) { mIndex++; return true; } else if ("false".equals(stringValue)) { mIndex++; return false; } else { return defaultValue; } } /** * Returns the remaining command-line parameters. This is intended to be invoked once * {@link #nextOption()} returns {@code null}. */ public String[] getRemainingParams() { if (mIndex >= mParams.length) { return new String[0]; } String param = mParams[mIndex]; if ("--".equals(param)) { // Skip end of options marker return Arrays.copyOfRange(mParams, mIndex + 1, mParams.length); } else { return Arrays.copyOfRange(mParams, mIndex, mParams.length); } } /** * Indicates that an error was encountered while parsing command-line options. */ public static class OptionsException extends Exception { private static final long serialVersionUID = 1L; public OptionsException(String message) { super(message); } } } ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_ParameterException.java0100644 0000000 0000000 00000000034 14763776540 027302 xustar000000000 0000000 28 mtime=1741684064.5300000 src/apksigner/java/com/android/apksigner/ParameterException.java0100644 0000000 0000000 00000001654 14763776540 024154 0ustar000000000 0000000 /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksigner; /** * Indicates that there is an issue with command-line parameters provided to {@link ApkSignerTool}. */ public class ParameterException extends Exception { private static final long serialVersionUID = 1L; ParameterException(String message) { super(message); } } ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_PasswordRetriever.java0100644 0000000 0000000 00000000034 14763776540 027175 xustar000000000 0000000 28 mtime=1741684064.5300000 src/apksigner/java/com/android/apksigner/PasswordRetriever.java0100644 0000000 0000000 00000043440 14763776540 024046 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksigner; import java.io.ByteArrayOutputStream; import java.io.Console; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Retriever of passwords based on password specs supported by {@code apksigner} tool. * *

apksigner supports retrieving multiple passwords from the same source (e.g., file, standard * input) which adds the need to keep some sources open across password retrievals. This class * addresses the need. * *

To use this retriever, construct a new instance, use {@link #getPasswords(String, String, * Charset...)} to retrieve passwords, and then invoke {@link #close()} on the instance when done, * enabling the instance to release any held resources. */ public class PasswordRetriever implements AutoCloseable { public static final String SPEC_STDIN = "stdin"; /** Character encoding used by the console or {@code null} if not known. */ private final Charset mConsoleEncoding; private final Map mFileInputStreams = new HashMap<>(); private boolean mClosed; public PasswordRetriever() { mConsoleEncoding = getConsoleEncoding(); } /** * Returns the passwords described by the provided spec. The reason there may be more than one * password is compatibility with {@code keytool} and {@code jarsigner} which in certain cases * use the form of passwords encoded using the console's character encoding or the JVM default * encoding. * *

Supported specs: *

    *
  • stdin -- read password as a line from console, if available, or standard * input if console is not available
  • *
  • pass:password -- password specified inside the spec, starting after * {@code pass:}
  • *
  • file:path -- read password as a line from the specified file
  • *
  • env:name -- password is in the specified environment variable
  • *
* *

When the same file (including standard input) is used for providing multiple passwords, * the passwords are read from the file one line at a time. * * @param additionalPwdEncodings additional encodings for converting the password into KeyStore * or PKCS #8 encrypted key password. These encoding are used in addition to using the * password verbatim or encoded using JVM default character encoding. A useful encoding * to provide is the console character encoding on Windows machines where the console * may be different from the JVM default encoding. Unfortunately, there is no public API * to obtain the console's character encoding. */ public List getPasswords( String spec, String description, Charset... additionalPwdEncodings) throws IOException { // IMPLEMENTATION NOTE: Java KeyStore and PBEKeySpec APIs take passwords as arrays of // Unicode characters (char[]). Unfortunately, it appears that Sun/Oracle keytool and // jarsigner in some cases use passwords which are the encoded form obtained using the // console's character encoding. For example, if the encoding is UTF-8, keytool and // jarsigner will use the password which is obtained by upcasting each byte of the UTF-8 // encoded form to char. This occurs only when the password is read from stdin/console, and // does not occur when the password is read from a command-line parameter. // There are other tools which use the Java KeyStore API correctly. // Thus, for each password spec, a valid password is typically one of these three: // * Unicode characters, // * characters (upcast bytes) obtained from encoding the password using the console's // character encoding of the console used on the environment where the KeyStore was // created, // * characters (upcast bytes) obtained from encoding the password using the JVM's default // character encoding of the machine where the KeyStore was created. // // For a sample password "\u0061\u0062\u00a1\u00e4\u044e\u0031": // On Windows 10 with English US as the UI language, IBM437 is used as console encoding and // windows-1252 is used as the JVM default encoding: // * keytool -genkey -v -keystore native.jks -keyalg RSA -keysize 2048 -validity 10000 // -alias test // generates a keystore and key which decrypt only with // "\u0061\u0062\u00ad\u0084\u003f\u0031" // * keytool -genkey -v -keystore native.jks -keyalg RSA -keysize 2048 -validity 10000 // -alias test -storepass // generates a keystore and key which decrypt only with // "\u0061\u0062\u00a1\u00e4\u003f\u0031" // On modern OSX/Linux UTF-8 is used as the console and JVM default encoding: // * keytool -genkey -v -keystore native.jks -keyalg RSA -keysize 2048 -validity 10000 // -alias test // generates a keystore and key which decrypt only with // "\u0061\u0062\u00c2\u00a1\u00c3\u00a4\u00d1\u008e\u0031" // * keytool -genkey -v -keystore native.jks -keyalg RSA -keysize 2048 -validity 10000 // -alias test -storepass // generates a keystore and key which decrypt only with // "\u0061\u0062\u00a1\u00e4\u044e\u0031" // // We optimize for the case where the KeyStore was created on the same machine where // apksigner is executed. Thus, we can assume the JVM default encoding used for creating the // KeyStore is the same as the current JVM's default encoding. We can make a similar // assumption about the console's encoding. However, there is no public API for obtaining // the console's character encoding. Prior to Java 9, we could cheat by using Reflection API // to access Console.encoding field. However, in the official Java 9 JVM this field is not // only inaccessible, but results in warnings being spewed to stdout during access attempts. // As a result, we cannot auto-detect the console's encoding and thus rely on the user to // explicitly provide it to apksigner as a command-line parameter (and passed into this // method as additionalPwdEncodings), if the password is using non-ASCII characters. assertNotClosed(); if (spec.startsWith("pass:")) { char[] pwd = spec.substring("pass:".length()).toCharArray(); return getPasswords(pwd, additionalPwdEncodings); } else if (SPEC_STDIN.equals(spec)) { Console console = System.console(); if (console != null) { // Reading from console char[] pwd = console.readPassword(description + ": "); if (pwd == null) { throw new IOException("Failed to read " + description + ": console closed"); } return getPasswords(pwd, additionalPwdEncodings); } else { // Console not available -- reading from standard input System.out.println(description + ": "); byte[] encodedPwd = readEncodedPassword(System.in); if (encodedPwd.length == 0) { throw new IOException( "Failed to read " + description + ": standard input closed"); } // By default, textual input obtained via standard input is supposed to be decoded // using the in JVM default character encoding. return getPasswords(encodedPwd, Charset.defaultCharset(), additionalPwdEncodings); } } else if (spec.startsWith("file:")) { String name = spec.substring("file:".length()); File file = new File(name).getCanonicalFile(); InputStream in = mFileInputStreams.get(file); if (in == null) { in = new FileInputStream(file); mFileInputStreams.put(file, in); } byte[] encodedPwd = readEncodedPassword(in); if (encodedPwd.length == 0) { throw new IOException( "Failed to read " + description + " : end of file reached in " + file); } // By default, textual input from files is supposed to be treated as encoded using JVM's // default character encoding. return getPasswords(encodedPwd, Charset.defaultCharset(), additionalPwdEncodings); } else if (spec.startsWith("env:")) { String name = spec.substring("env:".length()); String value = System.getenv(name); if (value == null) { throw new IOException( "Failed to read " + description + ": environment variable " + value + " not specified"); } return getPasswords(value.toCharArray(), additionalPwdEncodings); } else { throw new IOException("Unsupported password spec for " + description + ": " + spec); } } /** * Returns the provided password and all password variants derived from the password. The * resulting list is guaranteed to contain at least one element. */ private List getPasswords(char[] pwd, Charset... additionalEncodings) { List passwords = new ArrayList<>(3); addPasswords(passwords, pwd, additionalEncodings); return passwords; } /** * Returns the provided password and all password variants derived from the password. The * resulting list is guaranteed to contain at least one element. * * @param encodedPwd password encoded using {@code encodingForDecoding}. */ private List getPasswords( byte[] encodedPwd, Charset encodingForDecoding, Charset... additionalEncodings) { List passwords = new ArrayList<>(4); // Decode password and add it and its variants to the list try { char[] pwd = decodePassword(encodedPwd, encodingForDecoding); addPasswords(passwords, pwd, additionalEncodings); } catch (IOException ignored) {} // Add the original encoded form addPassword(passwords, castBytesToChars(encodedPwd)); return passwords; } /** * Adds the provided password and its variants to the provided list of passwords. * *

NOTE: This method adds only the passwords/variants which are not yet in the list. */ private void addPasswords(List passwords, char[] pwd, Charset... additionalEncodings) { if ((additionalEncodings != null) && (additionalEncodings.length > 0)) { for (Charset encoding : additionalEncodings) { // Password encoded using provided encoding (usually the console's character // encoding) and upcast into char[] try { char[] encodedPwd = castBytesToChars(encodePassword(pwd, encoding)); addPassword(passwords, encodedPwd); } catch (IOException ignored) {} } } // Verbatim password addPassword(passwords, pwd); // Password encoded using the console encoding and upcast into char[] if (mConsoleEncoding != null) { try { char[] encodedPwd = castBytesToChars(encodePassword(pwd, mConsoleEncoding)); addPassword(passwords, encodedPwd); } catch (IOException ignored) {} } // Password encoded using the JVM default character encoding and upcast into char[] try { char[] encodedPwd = castBytesToChars(encodePassword(pwd, Charset.defaultCharset())); addPassword(passwords, encodedPwd); } catch (IOException ignored) {} } /** * Adds the provided password to the provided list. Does nothing if the password is already in * the list. */ private static void addPassword(List passwords, char[] password) { for (char[] existingPassword : passwords) { if (Arrays.equals(password, existingPassword)) { return; } } passwords.add(password); } private static byte[] encodePassword(char[] pwd, Charset cs) throws IOException { ByteBuffer pwdBytes = cs.newEncoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .encode(CharBuffer.wrap(pwd)); byte[] encoded = new byte[pwdBytes.remaining()]; pwdBytes.get(encoded); return encoded; } private static char[] decodePassword(byte[] pwdBytes, Charset encoding) throws IOException { CharBuffer pwdChars = encoding.newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .decode(ByteBuffer.wrap(pwdBytes)); char[] result = new char[pwdChars.remaining()]; pwdChars.get(result); return result; } /** * Upcasts each {@code byte} in the provided array of bytes to a {@code char} and returns the * resulting array of characters. */ private static char[] castBytesToChars(byte[] bytes) { if (bytes == null) { return null; } char[] chars = new char[bytes.length]; for (int i = 0; i < bytes.length; i++) { chars[i] = (char) (bytes[i] & 0xff); } return chars; } private static boolean isJava9OrHigherErrOnTheSideOfCaution() { // Before Java 9, this string is of major.minor form, such as "1.8" for Java 8. // From Java 9 onwards, this is a single number: major, such as "9" for Java 9. // See JEP 223: New Version-String Scheme. String versionString = System.getProperty("java.specification.version"); if (versionString == null) { // Better safe than sorry return true; } return !versionString.startsWith("1."); } /** * Returns the character encoding used by the console or {@code null} if the encoding is not * known. */ private static Charset getConsoleEncoding() { // IMPLEMENTATION NOTE: There is no public API for obtaining the console's character // encoding. We thus cheat by using implementation details of the most popular JVMs. // Unfortunately, this doesn't work on Java 9 JVMs where access to Console.encoding is // restricted by default and leads to spewing to stdout at runtime. if (isJava9OrHigherErrOnTheSideOfCaution()) { return null; } String consoleCharsetName = null; try { Method encodingMethod = Console.class.getDeclaredMethod("encoding"); encodingMethod.setAccessible(true); consoleCharsetName = (String) encodingMethod.invoke(null); } catch (ReflectiveOperationException ignored) { return null; } if (consoleCharsetName == null) { // Console encoding is the same as this JVM's default encoding return Charset.defaultCharset(); } try { return getCharsetByName(consoleCharsetName); } catch (IllegalArgumentException e) { return null; } } public static Charset getCharsetByName(String charsetName) throws IllegalArgumentException { // On Windows 10, cp65001 is the UTF-8 code page. For some reason, popular JVMs don't // have a mapping for cp65001... if ("cp65001".equalsIgnoreCase(charsetName)) { return StandardCharsets.UTF_8; } return Charset.forName(charsetName); } private static byte[] readEncodedPassword(InputStream in) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream(); int b; while ((b = in.read()) != -1) { if (b == '\n') { break; } else if (b == '\r') { int next = in.read(); if ((next == -1) || (next == '\n')) { break; } if (!(in instanceof PushbackInputStream)) { in = new PushbackInputStream(in); } ((PushbackInputStream) in).unread(next); } result.write(b); } return result.toByteArray(); } private void assertNotClosed() { if (mClosed) { throw new IllegalStateException("Closed"); } } @Override public void close() { for (InputStream in : mFileInputStreams.values()) { try { in.close(); } catch (IOException ignored) {} } mFileInputStreams.clear(); mClosed = true; } } ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_SignerParams.java0100644 0000000 0000000 00000000034 14763776540 026076 xustar000000000 0000000 28 mtime=1741684064.5310000 src/apksigner/java/com/android/apksigner/SignerParams.java0100644 0000000 0000000 00000055115 14763776540 022751 0ustar000000000 0000000 /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksigner; import static java.util.Arrays.stream; import com.android.apksig.KeyConfig; import com.android.apksig.SigningCertificateLineage; import com.android.apksig.SigningCertificateLineage.SignerCapabilities; import com.android.apksig.internal.util.X509CertificateUtils; import com.android.apksig.kms.KmsType; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyFactory; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.stream.Collectors; import javax.crypto.EncryptedPrivateKeyInfo; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; /** A utility class to load private key and certificates from a keystore or key and cert files. */ public class SignerParams { private String name; private String keystoreFile; private String keystoreKeyAlias; private String keystorePasswordSpec; private String keyPasswordSpec; private Charset passwordCharset; private String keystoreType; private String keystoreProviderName; private String keystoreProviderClass; private String keystoreProviderArg; private String keyFile; private String certFile; private String v1SigFileBasename; private KeyConfig mKeyConfig; private List certs; private final SignerCapabilities.Builder signerCapabilitiesBuilder = new SignerCapabilities.Builder(); private int minSdkVersion; private SigningCertificateLineage signingCertificateLineage; public String getName() { return name; } public void setName(String name) { this.name = name; } public void setKeystoreFile(String keystoreFile) { this.keystoreFile = keystoreFile; } public String getKeystoreKeyAlias() { return keystoreKeyAlias; } public void setKeystoreKeyAlias(String keystoreKeyAlias) { this.keystoreKeyAlias = keystoreKeyAlias; } public void setKeystorePasswordSpec(String keystorePasswordSpec) { this.keystorePasswordSpec = keystorePasswordSpec; } public void setKeyPasswordSpec(String keyPasswordSpec) { this.keyPasswordSpec = keyPasswordSpec; } public void setPasswordCharset(Charset passwordCharset) { this.passwordCharset = passwordCharset; } public void setKeystoreType(String keystoreType) { this.keystoreType = keystoreType; } public void setKeystoreProviderName(String keystoreProviderName) { this.keystoreProviderName = keystoreProviderName; } public void setKeystoreProviderClass(String keystoreProviderClass) { this.keystoreProviderClass = keystoreProviderClass; } public void setKeystoreProviderArg(String keystoreProviderArg) { this.keystoreProviderArg = keystoreProviderArg; } public String getKeyFile() { return keyFile; } public void setKeyFile(String keyFile) { this.keyFile = keyFile; } public void setCertFile(String certFile) { this.certFile = certFile; } public String getV1SigFileBasename() { return v1SigFileBasename; } public void setV1SigFileBasename(String v1SigFileBasename) { this.v1SigFileBasename = v1SigFileBasename; } /** * Returns the signing key of this signer. * * @deprecated Use {@link #getKeyConfig()} instead of accessing a {@link PrivateKey}. If the * user of ApkSigner is signing with a KMS instead of JCA, this method will return null. */ @Deprecated public PrivateKey getPrivateKey() { return mKeyConfig.match(jca -> jca.privateKey, kms -> null); } public KeyConfig getKeyConfig() { return mKeyConfig; } public List getCerts() { return certs; } public SignerCapabilities.Builder getSignerCapabilitiesBuilder() { return signerCapabilitiesBuilder; } public int getMinSdkVersion() { return minSdkVersion; } public void setMinSdkVersion(int minSdkVersion) { this.minSdkVersion = minSdkVersion; } public SigningCertificateLineage getSigningCertificateLineage() { return signingCertificateLineage; } public void setSigningCertificateLineage(SigningCertificateLineage lineage) { this.signingCertificateLineage = lineage; } boolean isEmpty() { return (name == null) && (keystoreFile == null) && (keystoreKeyAlias == null) && (keystorePasswordSpec == null) && (keyPasswordSpec == null) && (passwordCharset == null) && (keystoreType == null) && (keystoreProviderName == null) && (keystoreProviderClass == null) && (keystoreProviderArg == null) && (keyFile == null) && (certFile == null) && (v1SigFileBasename == null) && (mKeyConfig == null) && (certs == null); } public void loadPrivateKeyAndCerts(PasswordRetriever passwordRetriever) throws Exception { KmsType kmsType = stream(KmsType.values()) .filter(v -> v.toString().equalsIgnoreCase(keystoreType)) .findFirst() .orElse(null); if (kmsType != null) { if (keystoreKeyAlias == null) { throw new ParameterException( "key alias (--ks-key-alias) is required if ks-type is a cloud KMS"); } certs = loadCertsFromFile(certFile); mKeyConfig = new KeyConfig.Kms(kmsType, keystoreKeyAlias); return; } if (keystoreFile != null) { if (keyFile != null) { throw new ParameterException( "--ks and --key may not be specified at the same time"); } if (certFile != null) { throw new ParameterException( "--ks and --cert may not be specified at the same time"); } loadPrivateKeyAndCertsFromKeyStore(passwordRetriever); return; } if (keyFile != null) { loadPrivateKeyAndCertsFromFiles(passwordRetriever); return; } throw new ParameterException( "KeyStore (--ks), private key file (--key), or key alias and cloud provider" + " (--ks-key-alias and --ks-type) must be specified"); } private void loadPrivateKeyAndCertsFromKeyStore(PasswordRetriever passwordRetriever) throws Exception { if (keystoreFile == null) { throw new ParameterException("KeyStore (--ks) must be specified"); } // 1. Obtain a KeyStore implementation String ksType = (keystoreType != null) ? keystoreType : KeyStore.getDefaultType(); KeyStore ks; if (keystoreProviderName != null) { // Use a named Provider (assumes the provider is already installed) ks = KeyStore.getInstance(ksType, keystoreProviderName); } else if (keystoreProviderClass != null) { // Use a new Provider instance (does not require the provider to be installed) Class ksProviderClass = Class.forName(keystoreProviderClass); if (!Provider.class.isAssignableFrom(ksProviderClass)) { throw new ParameterException( "Keystore Provider class " + keystoreProviderClass + " not subclass of " + Provider.class.getName()); } Provider ksProvider; if (keystoreProviderArg != null) { try { // Single-arg Provider constructor ksProvider = (Provider) ksProviderClass.getConstructor(String.class) .newInstance(keystoreProviderArg); } catch (NoSuchMethodException e) { // Starting from JDK 9 the single-arg constructor accepting the configuration // has been replaced by a configure(String) method to be invoked after // instantiating the Provider with the no-arg constructor. ksProvider = (Provider) ksProviderClass.getConstructor().newInstance(); ksProvider = (Provider) ksProviderClass.getMethod("configure", String.class).invoke(ksProvider, keystoreProviderArg); } } else { // No-arg Provider constructor ksProvider = (Provider) ksProviderClass.getConstructor().newInstance(); } ks = KeyStore.getInstance(ksType, ksProvider); } else { // Use the highest-priority Provider which offers the requested KeyStore type ks = KeyStore.getInstance(ksType); } // 2. Load the KeyStore List keystorePasswords; Charset[] additionalPasswordEncodings; { String keystorePasswordSpec = (this.keystorePasswordSpec != null) ? this.keystorePasswordSpec : PasswordRetriever.SPEC_STDIN; additionalPasswordEncodings = (passwordCharset != null) ? new Charset[] {passwordCharset} : new Charset[0]; keystorePasswords = passwordRetriever.getPasswords(keystorePasswordSpec, "Keystore password for " + name, additionalPasswordEncodings); loadKeyStoreFromFile( ks, "NONE".equals(keystoreFile) ? null : keystoreFile, keystorePasswords); } // 3. Load the PrivateKey and cert chain from KeyStore String keyAlias = null; PrivateKey key = null; try { if (keystoreKeyAlias == null) { // Private key entry alias not specified. Find the key entry contained in this // KeyStore. If the KeyStore contains multiple key entries, return an error. Enumeration aliases = ks.aliases(); if (aliases != null) { while (aliases.hasMoreElements()) { String entryAlias = aliases.nextElement(); if (ks.isKeyEntry(entryAlias)) { keyAlias = entryAlias; if (keystoreKeyAlias != null) { throw new ParameterException( keystoreFile + " contains multiple key entries" + ". --ks-key-alias option must be used to specify" + " which entry to use."); } keystoreKeyAlias = keyAlias; } } } if (keystoreKeyAlias == null) { throw new ParameterException(keystoreFile + " does not contain key entries"); } } // Private key entry alias known. Load that entry's private key. keyAlias = keystoreKeyAlias; if (!ks.isKeyEntry(keyAlias)) { throw new ParameterException( keystoreFile + " entry \"" + keyAlias + "\" does not contain a key"); } Key entryKey; if (keyPasswordSpec != null) { // Key password spec is explicitly specified. Use this spec to obtain the // password and then load the key using that password. List keyPasswords = passwordRetriever.getPasswords( keyPasswordSpec, "Key \"" + keyAlias + "\" password for " + name, additionalPasswordEncodings); entryKey = getKeyStoreKey(ks, keyAlias, keyPasswords); } else { // Key password spec is not specified. This means we should assume that key // password is the same as the keystore password and that, if this assumption is // wrong, we should prompt for key password and retry loading the key using that // password. try { entryKey = getKeyStoreKey(ks, keyAlias, keystorePasswords); } catch (UnrecoverableKeyException expected) { List keyPasswords = passwordRetriever.getPasswords( PasswordRetriever.SPEC_STDIN, "Key \"" + keyAlias + "\" password for " + name, additionalPasswordEncodings); entryKey = getKeyStoreKey(ks, keyAlias, keyPasswords); } } if (entryKey == null) { throw new ParameterException( keystoreFile + " entry \"" + keyAlias + "\" does not contain a key"); } else if (!(entryKey instanceof PrivateKey)) { throw new ParameterException( keystoreFile + " entry \"" + keyAlias + "\" does not contain a private" + " key. It contains a key of algorithm: " + entryKey.getAlgorithm()); } key = (PrivateKey) entryKey; } catch (UnrecoverableKeyException e) { throw new IOException( "Failed to obtain key with alias \"" + keyAlias + "\" from " + keystoreFile + ". Wrong password?", e); } this.mKeyConfig = new KeyConfig.Jca(key); Certificate[] certChain = ks.getCertificateChain(keyAlias); if ((certChain == null) || (certChain.length == 0)) { throw new ParameterException( keystoreFile + " entry \"" + keyAlias + "\" does not contain certificates"); } this.certs = new ArrayList<>(certChain.length); for (Certificate cert : certChain) { this.certs.add((X509Certificate) cert); } } /** * Loads the password-protected keystore from storage. * * @param file file backing the keystore or {@code null} if the keystore is not file-backed, for * example, a PKCS #11 KeyStore. */ private static void loadKeyStoreFromFile(KeyStore ks, String file, List passwords) throws Exception { Exception lastFailure = null; for (char[] password : passwords) { try { if (file != null) { try (FileInputStream in = new FileInputStream(file)) { ks.load(in, password); } } else { ks.load(null, password); } return; } catch (Exception e) { lastFailure = e; } } if (lastFailure == null) { throw new RuntimeException("No keystore passwords"); } else { throw lastFailure; } } private PrivateKey loadPrivateKeyFromFile(String keyFile, PasswordRetriever passwordRetriever) throws ParameterException, IOException, GeneralSecurityException { if (keyFile == null) { throw new ParameterException("Private key file (--key) must be specified"); } byte[] privateKeyBlob = readFully(new File(keyFile)); PKCS8EncodedKeySpec keySpec; // Potentially encrypted key blob try { EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(privateKeyBlob); // The blob is indeed an encrypted private key blob String passwordSpec = (keyPasswordSpec != null) ? keyPasswordSpec : PasswordRetriever.SPEC_STDIN; Charset[] additionalPasswordEncodings = (passwordCharset != null) ? new Charset[] {passwordCharset} : new Charset[0]; List keyPasswords = passwordRetriever.getPasswords( passwordSpec, "Private key password for " + name, additionalPasswordEncodings); keySpec = decryptPkcs8EncodedKey(encryptedPrivateKeyInfo, keyPasswords); } catch (IOException e) { // The blob is not an encrypted private key blob if (keyPasswordSpec == null) { // Given that no password was specified, assume the blob is an unencrypted // private key blob keySpec = new PKCS8EncodedKeySpec(privateKeyBlob); } else { throw new InvalidKeySpecException( "Failed to parse encrypted private key blob " + keyFile, e); } } // Load the private key from its PKCS #8 encoded form. try { return loadPkcs8EncodedPrivateKey(keySpec); } catch (InvalidKeySpecException e) { throw new InvalidKeySpecException( "Failed to load PKCS #8 encoded private key from " + keyFile, e); } } private List loadCertsFromFile(String certFile) throws ParameterException, IOException, CertificateException { if (certFile == null) { throw new ParameterException("Certificate file (--cert) must be specified"); } try (FileInputStream in = new FileInputStream(certFile)) { return X509CertificateUtils.generateCertificates(in).stream() .map(X509Certificate.class::cast) .collect(Collectors.toList()); } } private static Key getKeyStoreKey(KeyStore ks, String keyAlias, List passwords) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException { UnrecoverableKeyException lastFailure = null; for (char[] password : passwords) { try { return ks.getKey(keyAlias, password); } catch (UnrecoverableKeyException e) { lastFailure = e; } } if (lastFailure == null) { throw new RuntimeException("No key passwords"); } else { throw lastFailure; } } private void loadPrivateKeyAndCertsFromFiles(PasswordRetriever passwordRetriever) throws Exception { this.certs = loadCertsFromFile(certFile); this.mKeyConfig = new KeyConfig.Jca(loadPrivateKeyFromFile(keyFile, passwordRetriever)); } private static PKCS8EncodedKeySpec decryptPkcs8EncodedKey( EncryptedPrivateKeyInfo encryptedPrivateKeyInfo, List passwords) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName()); InvalidKeySpecException lastKeySpecException = null; InvalidKeyException lastKeyException = null; for (char[] password : passwords) { PBEKeySpec decryptionKeySpec = new PBEKeySpec(password); try { SecretKey decryptionKey = keyFactory.generateSecret(decryptionKeySpec); return encryptedPrivateKeyInfo.getKeySpec(decryptionKey); } catch (InvalidKeySpecException e) { lastKeySpecException = e; } catch (InvalidKeyException e) { lastKeyException = e; } } if ((lastKeyException == null) && (lastKeySpecException == null)) { throw new RuntimeException("No passwords"); } else if (lastKeyException != null) { throw lastKeyException; } else { throw lastKeySpecException; } } private static PrivateKey loadPkcs8EncodedPrivateKey(PKCS8EncodedKeySpec spec) throws InvalidKeySpecException, NoSuchAlgorithmException { try { return KeyFactory.getInstance("RSA").generatePrivate(spec); } catch (InvalidKeySpecException expected) { } try { return KeyFactory.getInstance("EC").generatePrivate(spec); } catch (InvalidKeySpecException expected) { } try { return KeyFactory.getInstance("DSA").generatePrivate(spec); } catch (InvalidKeySpecException expected) { } throw new InvalidKeySpecException("Not an RSA, EC, or DSA private key"); } private static byte[] readFully(File file) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream(); try (FileInputStream in = new FileInputStream(file)) { drain(in, result); } return result.toByteArray(); } private static void drain(InputStream in, OutputStream out) throws IOException { byte[] buf = new byte[65536]; int chunkSize; while ((chunkSize = in.read(buf)) != -1) { out.write(buf, 0, chunkSize); } } } ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_help.txt0100644 0000000 0000000 00000000034 14763776540 024331 xustar000000000 0000000 28 mtime=1741684064.5320000 src/apksigner/java/com/android/apksigner/help.txt0100644 0000000 0000000 00000001474 14763776540 021203 0ustar000000000 0000000 USAGE: apksigner [options] apksigner --version apksigner --help EXAMPLE: apksigner sign --ks release.jks app.apk apksigner verify --verbose app.apk apksigner is a tool for signing Android APK files and for checking whether signatures of APK files will verify on Android devices. COMMANDS rotate Add a new signing certificate to the SigningCertificateLineage sign Sign the provided APK verify Check whether the provided APK is expected to verify on Android lineage Modify the capabilities of one or more signers in an existing SigningCertificateLineage version Show this tool's version number and exit help Show this usage page and exit ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_help_lineage.txt0100644 0000000 0000000 00000000034 14763776540 026015 xustar000000000 0000000 28 mtime=1741684064.5320000 src/apksigner/java/com/android/apksigner/help_lineage.txt0100644 0000000 0000000 00000025112 14763776540 022662 0ustar000000000 0000000 USAGE: apksigner lineage [options] This modifies the capabilities of one or more signers in the provided SigningCertificateLineage. This can be used to revoke capabilities of a previous signing certificate once the install base has been migrated to the new signing certificate. GENERAL OPTIONS --in Input SigningCertificateLineage. This file contains a binary representation of a SigningCertificateLineage object which contains the proof-of-rotation for different signing certificates. An APK previously signed with a SigningCertificateLineage can also be specified; the lineage will then be read from the signed data in the APK. --out File into which to put the binary representation of a SigningCertificateLineage object. --print-certs Show information about the signing certificates and their capabilities in the SigningCertificateLineage. --print-certs-pem Show information about the signing certificates and their capabilities in the SigningCertificateLineage; prints the PEM encoding of each signing certificate to stdout. -v, --verbose Verbose output mode. -h, --help Show help about this command and exit. PER-SIGNER OPTIONS This option is required for each signer to be modified in the provided SigningCertificateLineage. --signer Indicates the start of a new signing certificate to be modified. PER-SIGNER SIGNING KEY, CERTIFICATE, & CAPABILITY OPTIONS To modify the capabilities of a previous signer in the lineage the signer's private key and certificate must be specified. There are two ways to provide the signer's private key and certificate: (1) Java KeyStore (see --ks), or (2) private key file in PKCS #8 format and certificate file in X.509 format (see --key and --cert). The --set-xx capability options allow an older signing certificate to still be used in some situations on the platform even though the APK is now being signed by a newer signing certificate. By default, the new signer will have all capabilities, but the capability options can be specified for the new signer to act as a default level of trust when moving to a newer signing certificate. The capability options accept an optional boolean value of true or false; if this value is not specified then the option will default to true. --ks Load private key and certificate chain from the Java KeyStore initialized from the specified file. NONE means no file is needed by KeyStore, which is the case for some PKCS #11 KeyStores. --ks-key-alias Alias under which the private key and certificate are stored in the KeyStore. This must be specified if the KeyStore contains multiple keys. --ks-pass KeyStore password (see --ks). The following formats are supported: pass: password provided inline env: password provided in the named environment variable file: password provided in the named file, as a single line stdin password provided on standard input, as a single line A password is required to open a KeyStore. By default, the tool will prompt for password via console or standard input. When the same file (including standard input) is used for providing multiple passwords, the passwords are read from the file one line at a time. Passwords are read in the order of old-signer then new-signer and, within each signer, KeyStore password is read before the key password is read. --key-pass Password with which the private key is protected. The following formats are supported: pass: password provided inline env: password provided in the named environment variable file: password provided in the named file, as a single line stdin password provided on standard input, as a single line If --key-pass is not specified for a KeyStore key, this tool will attempt to load the key using the KeyStore password and, if that fails, will prompt for key password and attempt to load the key using that password. If --key-pass is not specified for a private key file key, this tool will prompt for key password only if a password is required. When the same file (including standard input) is used for providing multiple passwords, the passwords are read from the file one line at a time. Passwords are read in the order of old-signer then new-signer and, within each signer, KeyStore password is read before the key password is read. --pass-encoding Additional character encoding (e.g., ibm437 or utf-8) to try for passwords containing non-ASCII characters. KeyStores created by keytool are often encrypted not using the Unicode form of the password but rather using the form produced by encoding the password using the console's character encoding. apksigner by default tries to decrypt using several forms of the password: the Unicode form, the form encoded using the JVM default charset, and, on Java 8 and older, the form encoded using the console's charset. On Java 9, apksigner cannot detect the console's charset and may need to be provided with --pass-encoding when a non-ASCII password is used. --pass-encoding may also need to be provided for a KeyStore created by keytool on a different OS or in a different locale. --ks-type Type/algorithm of KeyStore to use. By default, the default type is used. --ks-provider-name Name of the JCA Provider from which to request the KeyStore implementation. By default, the highest priority provider is used. See --ks-provider-class for the alternative way to specify a provider. --ks-provider-class Fully-qualified class name of the JCA Provider from which to request the KeyStore implementation. By default, the provider is chosen based on --ks-provider-name. --ks-provider-arg Value to pass into the constructor of the JCA Provider class specified by --ks-provider-class. The value is passed into the constructor as java.lang.String. By default, the no-arg provider's constructor is used. --key Load private key from the specified file. If the key is password-protected, the password will be prompted via standard input unless specified otherwise using --key-pass. The file must be in PKCS #8 DER format. --cert Load certificate chain from the specified file. The file must be in X.509 PEM or DER format. --set-installed-data Sets whether installed data associated with this previous signing certificate should be trusted. This capability is required to perform signing certificate rotation during an upgrade on-device. Without it, the platform will not permit the app data from the old signing certificate to propogate to the new version. Typically this flag should be set to enable signing certificate rotation and may be unset later when the install base is as migrated as it will be. --set-shared-uid Sets whether apps signed with this previous signing certificate can share a UID with an app signed with the new signing certificate. This is useful in situations where shareUserId apps would like to change their signing certificate but can not guarantee the order of updates to those apps. --set-permission Sets whether apps signed with this previous signing certificate can be granted SIGNATURE permissions defined by an app signed with the new signing certificate. --set-rollback Sets whether the platform should allow an app to be upgraded to a newer version signed with this previous signing certificate. WARNING: This effectively removes any benefit of signing certificate rotation since a compromised key could retake control of an app even after the signing certificate rotation. This option should only be used if a problem is encountered when attempting to rotate an older signing certificate. --set-auth Sets whether apps signed with this previous signing certificate should be granted privileged access by the authenticator module using the new signing certificate. EXAMPLES 1. Remove all capabilities from a previous signer in the linage: $ apksigner lineage --in /path/to/existing/lineage --out /path/to/new/file \ --signer --ks release.jks --set-installed-data false \ --set-shared-uid false --set-permission false --set-rollback false \ --set-auth false 2. Display details about the signing certificates and their capabilities in the lineage: $ apksigner lineage --in /path/to/existing/lineage --print-certs -v ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_help_rotate.txt0100644 0000000 0000000 00000000034 14763776540 025707 xustar000000000 0000000 28 mtime=1741684064.5330000 src/apksigner/java/com/android/apksigner/help_rotate.txt0100644 0000000 0000000 00000030563 14763776540 022562 0ustar000000000 0000000 USAGE: apksigner rotate [options] This takes the provided keys and creates a SigningCertificateLineage entry linking the old to the new, for use in a key rotation scenario using APK Signature Scheme v3. GENERAL OPTIONS --in Input SigningCertificateLineage. This file contains a binary representation of a SigningCertificateLineage object, which contains the proof-of-rotation for different signing certificates. This can be used with APK Signature Scheme v3 to rotate the signing certificate for an APK. An APK previously signed with a SigningCertificateLineage can also be specified; the lineage will then be read from the signed data in the APK. --out File into which to put the binary representation of a SigningCertificateLineage object. -v, --verbose Verbose output mode -h, --help Show help about this command and exit PER-SIGNER OPTIONS These options specify the configuration of a particular signer. To rotate keys, two signers must be specified, an old and a new. --old-signer The signing information for the signer from which to be rotated. This will be used to sign a new entry in the SigningCertificateLineage allowing the addition of the new-signer. If an input SigningCertificateLineage object was provided, this signer must match the leaf descendant so that the existing signing certificate history may be extended. --new-signer The signing information for the signer to which you want to rotate. This will be the last key in the SigningCertificate object, signed by the old-signer. PER-SIGNER SIGNING KEY, CERTIFICATE, & CAPABILITY OPTIONS There are two ways to provide the signer's private key and certificate: (1) Java KeyStore (see --ks), or (2) private key file in PKCS #8 format and certificate file in X.509 format (see --key and --cert). The --set-xx capability options allow an older signing certificate to still be used in some situations on the platform even though the APK is now being signed by a newer signing certificate. By default, the new signer will have all capabilities, but the capability options can be specified for the new signer during rotation to act as a default level of trust when moving to a newer signing certificate. The capability options accept an optional boolean value of true or false; if this value is not specified then the option will default to true. Prior to Android 12, if multiple apps shared a common signer in their signing lineage with distinct capabilities assigned, a bug in the platform would cause the capabilities declared for this signer in one of the app's signing lineage to be assigned to this same common signer in the lineage of the rest of the apps. Apps that use the default capabilities, or that assign the same capabilities to a common signer in their lineage, are not impacted by this bug. --ks Load private key and certificate chain from the Java KeyStore initialized from the specified file. NONE means no file is needed by KeyStore, which is the case for some PKCS #11 KeyStores. --ks-key-alias Alias under which the private key and certificate are stored in the KeyStore. This must be specified if the KeyStore contains multiple keys. --ks-pass KeyStore password (see --ks). The following formats are supported: pass: password provided inline env: password provided in the named environment variable file: password provided in the named file, as a single line stdin password provided on standard input, as a single line A password is required to open a KeyStore. By default, the tool will prompt for password via console or standard input. When the same file (including standard input) is used for providing multiple passwords, the passwords are read from the file one line at a time. Passwords are read in the order of old-signer then new-signer and, within each signer, KeyStore password is read before the key password is read. --key-pass Password with which the private key is protected. The following formats are supported: pass: password provided inline env: password provided in the named environment variable file: password provided in the named file, as a single line stdin password provided on standard input, as a single line If --key-pass is not specified for a KeyStore key, this tool will attempt to load the key using the KeyStore password and, if that fails, will prompt for key password and attempt to load the key using that password. If --key-pass is not specified for a private key file key, this tool will prompt for key password only if a password is required. When the same file (including standard input) is used for providing multiple passwords, the passwords are read from the file one line at a time. Passwords are read in the order of old-signer then new-signer and, within each signer, KeyStore password is read before the key password is read. --pass-encoding Additional character encoding (e.g., ibm437 or utf-8) to try for passwords containing non-ASCII characters. KeyStores created by keytool are often encrypted not using the Unicode form of the password but rather using the form produced by encoding the password using the console's character encoding. apksigner by default tries to decrypt using several forms of the password: the Unicode form, the form encoded using the JVM default charset, and, on Java 8 and older, the form encoded using the console's charset. On Java 9, apksigner cannot detect the console's charset and may need to be provided with --pass-encoding when a non-ASCII password is used. --pass-encoding may also need to be provided for a KeyStore created by keytool on a different OS or in a different locale. --ks-type Type/algorithm of KeyStore to use. By default, the default type is used. --ks-provider-name Name of the JCA Provider from which to request the KeyStore implementation. By default, the highest priority provider is used. See --ks-provider-class for the alternative way to specify a provider. --ks-provider-class Fully-qualified class name of the JCA Provider from which to request the KeyStore implementation. By default, the provider is chosen based on --ks-provider-name. --ks-provider-arg Value to pass into the constructor of the JCA Provider class specified by --ks-provider-class. The value is passed into the constructor as java.lang.String. By default, the no-arg provider's constructor is used. --key Load private key from the specified file. If the key is password-protected, the password will be prompted via standard input unless specified otherwise using --key-pass. The file must be in PKCS #8 DER format. --cert Load certificate chain from the specified file. The file must be in X.509 PEM or DER format. --set-installed-data Sets whether installed data associated with this previous signing certificate should be trusted. This capability is required to perform signing certificate rotation during an upgrade on-device. Without it, the platform will not permit the app data from the old signing certificate to propogate to the new version. Typically this flag should be set to enable signing certificate rotation and may be unset later when the install base is as migrated as it will be. --set-shared-uid Sets whether apps signed with this previous signing certificate can share a UID with an app signed with the new signing certificate. This is useful in situations where shareUserId apps would like to change their signing certificate but can not guarantee the order of updates to those apps. --set-permission Sets whether apps signed with this previous signing certificate can be granted SIGNATURE permissions defined by an app signed with the new signing certificate. --set-rollback Sets whether the platform should allow an app to be upgraded to a newer version signed with this previous signing certificate. WARNING: This effectively removes any benefit of signing certificate rotation since a compromised key could retake control of an app even after the signing certificate rotation. This option should only be used if a problem is encountered when attempting to rotate an older signing certificate. --set-auth Sets whether apps signed with this previous signing certificate should be granted privileged access by the authenticator module using the new signing certificate. JCA PROVIDER INSTALLATION OPTIONS These options enable you to install additional Java Crypto Architecture (JCA) Providers, such as PKCS #11 providers. Use --next-provider to delimit options of different providers. Providers are installed in the order in which they appear on the command-line. --provider-class Fully-qualified class name of the JCA Provider. --provider-arg Value to pass into the constructor of the JCA Provider class specified by --provider-class. The value is passed into the constructor as java.lang.String. By default, the no-arg provider's constructor is used. --provider-pos Position / priority at which to install this provider in the JCA provider list. By default, the provider is installed as the lowest priority provider. See java.security.Security.insertProviderAt. EXAMPLES 1. Create a new SigningCertificateLineage to enable rotation: $ apksigner rotate --out /path/to/new/file --old-signer --ks release.jks \ --new-signer --ks release2.jks 2. Extend an existing SigningCertificateLineage to rotate again after previous rotation: $ apksigner rotate --in /path/to/existing/lineage --out /path/to/new/file \ --old-signer --ks release2.jks --new-signer --ks release3.jks 3. Create a new SigningCertificateLineage with explicit capabilities for the previous signer: $ apksigner rotate --out /path/to/new/file --old-signer --ks release.jks \ --set-installed-data true --set-shared-uid true --set-permission true --set-rollback false \ --set-auth true --new-signer --ks release2.jks ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_help_sign.txt0100644 0000000 0000000 00000000034 14763776540 025351 xustar000000000 0000000 28 mtime=1741684064.5330000 src/apksigner/java/com/android/apksigner/help_sign.txt0100644 0000000 0000000 00000042332 14763776540 022221 0ustar000000000 0000000 USAGE: apksigner sign [options] apk This signs the provided APK, stripping out any pre-existing signatures. Signing is performed using one or more signers, each represented by an asymmetric key pair and a corresponding certificate. Typically, an APK is signed by just one signer. For each signer, you need to provide the signer's private key and certificate. GENERAL OPTIONS --in Input APK file to sign. This is an alternative to specifying the APK as the very last parameter, after all options. Unless --out is specified, this file will be overwritten with the resulting signed APK. --out File into which to output the signed APK. By default, the APK is signed in-place, overwriting the input file. -v, --verbose Verbose output mode --v1-signing-enabled Whether to enable signing using JAR signing scheme (aka v1 signing scheme) used in Android since day one. By default, signing using this scheme is enabled based on min and max SDK version (see --min-sdk-version and --max-sdk-version). --v2-signing-enabled Whether to enable signing using APK Signature Scheme v2 (aka v2 signing scheme) introduced in Android Nougat, API Level 24. By default, signing using this scheme is enabled based on min and max SDK version (see --min-sdk-version and --max-sdk-version). --v3-signing-enabled Whether to enable signing using APK Signature Scheme v3 (aka v3 signing scheme) introduced in Android P, API Level 28. By default, signing using this scheme is enabled based on min and max SDK version (see --min-sdk-version and --max-sdk-version). Multiple signers are not supported when using v3 signing, but multiple signers may be provided in conjunction with the "lineage" option to make sure that the app is signed by an appropriate signer on all supported platform versions. --v4-signing-enabled Whether to enable signing using APK Signature Scheme v4 (aka v4 signing scheme) introduced in Android 11, API Level 30. By default, signing using this scheme is enabled based on min and max SDK version (see --min-sdk-version and --max-sdk-version). --force-stamp-overwrite Whether to overwrite existing source stamp in the APK, if found. By default, it is set to false. It has no effect if no source stamp signer config is provided. --align-file-size Produces APK file sized as multiples of 4K bytes. --verity-enabled Whether to enable the verity signature algorithm for the v2 and v3 signature schemes. --min-sdk-version Lowest API Level on which this APK's signatures will be verified. By default, the value from AndroidManifest.xml is used. The higher the value, the stronger security parameters are used when signing. --max-sdk-version Highest API Level on which this APK's signatures will be verified. By default, the highest possible value is used. --rotation-min-sdk-version Lowest API Level for which an APK's rotated signing key should be used to produce the APK's signature. The original signing key for the APK will be used for all previous platform versions. Specifying a value <= 32 (Android Sv2) will result in the original V3 signing block being used without platform targeting. By default, rotated signing keys will be used with the V3.1 signing block which supports Android T+. --rotation-targets-dev-release The specified rotation-min-sdk-version is intended for a platform release under development. During development of a new platform, the API Level of the previously released platform is used as the API Level of the development platform until the SDK is finalized. This flag allows targeting signing key rotation to a development platform with API Level X while preventing the rotated key from being used on the latest release platform with API Level X. --debuggable-apk-permitted Whether to permit signing android:debuggable="true" APKs. Android disables some of its security protections for such apps. For example, anybody with ADB shell access can execute arbitrary code in the context of a debuggable app and can read/write persistently stored data of the app. It is a good security practice to not sign debuggable APKs with production signing keys, because such APKs puts users at risk once leaked. By default, signing debuggable APKs is permitted, for backward compatibility with older apksigner versions. --alignment-preserved Whether the existing alignment within the APK should be preserved; the default for this setting is false. When this value is false, the value provided to --lib-page-alignment will be used to page align native library files and all other files will be aligned to 4 bytes in the APK. --lib-page-alignment The number of bytes to be used to page align native library files in the APK; the default value is 16384. --lineage Signing certificate history to use in the event that signing certificates changed for an APK using APK Signature Scheme v3 supported signing certificate rotation. This object may be created by the apksigner "rotate" command. If used, all signers used to sign the APK must be present in the signing lineage, and if v1 or v2 signing is enabled, the first (oldest) entry in the lineage must have a signer provided, so that it can be used for those v1 and/or v2 signing. Multiple signers are not supported when using APK Signature Scheme v3, so multiple signers input will correspond to different points in the lineage and will be used on older platform versions when the newest signer in the lineage is unsupported. An APK previously signed with a SigningCertificateLineage can also be specified; the lineage will then be read from the signed data in the APK. --deterministic-dsa-signing When signing with the DSA signature algorithm, whether to use the deterministic version as specified in RFC 6979. --append-signature Appends the current signature to any signatures that already exist within the APK. This option can be used when an APK is signed by multiple independent signers to allow each to add their own signature without needing to share their private key. This option can also be used to preserve existing key / value blocks that exist within the APK signing block. -h, --help Show help about this command and exit PER-SIGNER OPTIONS These options specify the configuration of a particular signer. To delimit options of different signers, use --next-signer. --next-signer Delimits options of two different signers. There is no need to use this option when only one signer is used. --v1-signer-name Basename for files comprising the JAR signature scheme (aka v1 scheme) signature of this signer. By default, KeyStore key alias or basename of key file is used. --stamp-signer The signing information for the signer of the source stamp to be included in the APK. --signer-for-min-sdk-version Requires an int value indicating the minimum SDK version for which this signing config should be used to produce the APK's signature. The value should be >= 28 (Android P), and any value <= 32 will apply to Android P through Sv2 (SDK versions 28 - 32); since the V3.0 signature scheme does not support verified SDK version targeting, only a single signing config <= 32 can be specified. --signer-lineage The lineage to be used for the current SDK targeted signing config. PER-SIGNER SIGNING KEY & CERTIFICATE OPTIONS There are two ways to provide the signer's private key and certificate: (1) Java KeyStore (see --ks), or (2) private key file in PKCS #8 format and certificate file in X.509 format (see --key and --cert). --ks Load private key and certificate chain from the Java KeyStore initialized from the specified file. NONE means no file is needed by KeyStore, which is the case for some PKCS #11 KeyStores. --ks-key-alias Alias under which the private key and certificate are stored in the KeyStore. This must be specified if the KeyStore contains multiple keys. --ks-pass KeyStore password (see --ks). The following formats are supported: pass: password provided inline env: password provided in the named environment variable file: password provided in the named file, as a single line stdin password provided on standard input, as a single line A password is required to open a KeyStore. By default, the tool will prompt for password via console or standard input. When the same file (including standard input) is used for providing multiple passwords, the passwords are read from the file one line at a time. Passwords are read in the order in which signers are specified and, within each signer, KeyStore password is read before the key password is read. --key-pass Password with which the private key is protected. The following formats are supported: pass: password provided inline env: password provided in the named environment variable file: password provided in the named file, as a single line stdin password provided on standard input, as a single line If --key-pass is not specified for a KeyStore key, this tool will attempt to load the key using the KeyStore password and, if that fails, will prompt for key password and attempt to load the key using that password. If --key-pass is not specified for a private key file key, this tool will prompt for key password only if a password is required. When the same file (including standard input) is used for providing multiple passwords, the passwords are read from the file one line at a time. Passwords are read in the order in which signers are specified and, within each signer, KeyStore password is read before the key password is read. --pass-encoding Additional character encoding (e.g., ibm437 or utf-8) to try for passwords containing non-ASCII characters. KeyStores created by keytool are often encrypted not using the Unicode form of the password but rather using the form produced by encoding the password using the console's character encoding. apksigner by default tries to decrypt using several forms of the password: the Unicode form, the form encoded using the JVM default charset, and, on Java 8 and older, the form encoded using the console's charset. On Java 9, apksigner cannot detect the console's charset and may need to be provided with --pass-encoding when a non-ASCII password is used. --pass-encoding may also need to be provided for a KeyStore created by keytool on a different OS or in a different locale. --ks-type Type/algorithm of KeyStore to use. By default, the default type is used. --ks-provider-name Name of the JCA Provider from which to request the KeyStore implementation. By default, the highest priority provider is used. See --ks-provider-class for the alternative way to specify a provider. --ks-provider-class Fully-qualified class name of the JCA Provider from which to request the KeyStore implementation. By default, the provider is chosen based on --ks-provider-name. --ks-provider-arg Value to pass into the constructor of the JCA Provider class specified by --ks-provider-class. The value is passed into the constructor as java.lang.String. By default, the no-arg provider's constructor is used. --key Load private key from the specified file. If the key is password-protected, the password will be prompted via standard input unless specified otherwise using --key-pass. The file must be in PKCS #8 DER format. --cert Load certificate chain from the specified file. The file must be in X.509 PEM or DER format. JCA PROVIDER INSTALLATION OPTIONS These options enable you to install additional Java Crypto Architecture (JCA) Providers, such as PKCS #11 providers. Use --next-provider to delimit options of different providers. Providers are installed in the order in which they appear on the command-line. --provider-class Fully-qualified class name of the JCA Provider. --provider-arg Value to pass into the constructor of the JCA Provider class specified by --provider-class. The value is passed into the constructor as java.lang.String. By default, the no-arg provider's constructor is used. --provider-pos Position / priority at which to install this provider in the JCA provider list. By default, the provider is installed as the lowest priority provider. See java.security.Security.insertProviderAt. EXAMPLES 1. Sign an APK, in-place, using the one and only key in keystore release.jks: $ apksigner sign --ks release.jks app.apk 1. Sign an APK, without overwriting, using the one and only key in keystore release.jks: $ apksigner sign --ks release.jks --in app.apk --out app-signed.apk 3. Sign an APK using a private key and certificate stored as individual files: $ apksigner sign --key release.pk8 --cert release.x509.pem app.apk 4. Sign an APK using two keys: $ apksigner sign --ks release.jks --next-signer --ks magic.jks app.apk 5. Sign an APK using PKCS #11 JCA Provider: $ apksigner sign --provider-class sun.security.pkcs11.SunPKCS11 \ --provider-arg token.cfg --ks NONE --ks-type PKCS11 app.apk 6. Sign an APK using a non-ASCII password KeyStore created on English Windows. The --pass-encoding parameter is not needed if apksigner is being run on English Windows with Java 8 or older. $ apksigner sign --ks release.jks --pass-encoding ibm437 app.apk 7. Sign an APK on Windows using a non-ASCII password KeyStore created on a modern OSX or Linux machine: $ apksigner sign --ks release.jks --pass-encoding utf-8 app.apk 8. Sign an APK with rotated signing certificate: $ apksigner sign --ks release.jks --next-signer --ks release2.jks \ --lineage /path/to/signing/history/lineage app.apk ./PaxHeaders.X/src_apksigner_java_com_android_apksigner_help_verify.txt0100644 0000000 0000000 00000000034 14763776540 025715 xustar000000000 0000000 28 mtime=1741684064.5340000 src/apksigner/java/com/android/apksigner/help_verify.txt0100644 0000000 0000000 00000003125 14763776540 022562 0ustar000000000 0000000 USAGE: apksigner verify [options] apk This checks whether the provided APK will verify on Android. By default, this checks whether the APK will verify on all Android platform versions supported by the APK (as declared using minSdkVersion in AndroidManifest.xml). Use --min-sdk-version and/or --max-sdk-version to verify the APK against a custom range of API Levels. OPTIONS --print-certs Show information about the APK's signing certificates --print-certs-pem Show information about the APK's signing certificates and prints the PEM encoding of each signing certificate to stdout. -v, --verbose Verbose output mode --min-sdk-version Lowest API Level on which this APK's signatures will be verified. By default, the value from AndroidManifest.xml is used. --max-sdk-version Highest API Level on which this APK's signatures will be verified. By default, the highest possible value is used. -Werr Treat warnings as errors --in APK file to verify. This is an alternative to specifying the APK as the very last parameter, after all options. -h, --help Show help about this command and exit EXAMPLES 1. Check whether the APK's signatures are expected to verify on all Android platforms declared as supported by this APK: $ apksigner verify app.apk 2. Check whether the APK's signatures are expected to verify on Android platforms with API Level 15 and higher: $ apksigner verify --min-sdk-version 15 app.apk ./PaxHeaders.X/src_main_0100644 0000000 0000000 00000000034 14763776540 014201 xustar000000000 0000000 28 mtime=1741684064.5340000 src/main/0040755 0000000 0000000 00000000000 14763776540 011346 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_0100644 0000000 0000000 00000000034 14763776540 015202 xustar000000000 0000000 28 mtime=1741684064.5340000 src/main/java/0040755 0000000 0000000 00000000000 14763776540 012267 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_0100644 0000000 0000000 00000000034 14763776540 016040 xustar000000000 0000000 28 mtime=1741684064.5340000 src/main/java/com/0040755 0000000 0000000 00000000000 14763776540 013045 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_0100644 0000000 0000000 00000000034 14763776540 017540 xustar000000000 0000000 28 mtime=1741684064.5340000 src/main/java/com/android/0040755 0000000 0000000 00000000000 14763776540 014465 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_0100644 0000000 0000000 00000000034 14763776540 021076 xustar000000000 0000000 28 mtime=1741684064.5340000 src/main/java/com/android/apksig/0040755 0000000 0000000 00000000000 14763776540 015743 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_ApkSigner.java0100644 0000000 0000000 00000000034 14763776540 023622 xustar000000000 0000000 28 mtime=1741684064.5340000 src/main/java/com/android/apksig/ApkSigner.java0100644 0000000 0000000 00000250525 14763776540 020477 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.Constants.LIBRARY_PAGE_ALIGNMENT_BYTES; import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT; import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V3_SUPPORT; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkSigningBlockNotFoundException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.apk.MinSdkVersionException; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.ByteBufferDataSource; import com.android.apksig.internal.zip.CentralDirectoryRecord; import com.android.apksig.internal.zip.EocdRecord; import com.android.apksig.internal.zip.LocalFileRecord; import com.android.apksig.internal.zip.ZipUtils; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSinks; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import com.android.apksig.util.ReadableDataSink; import com.android.apksig.zip.ZipFormatException; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.SignatureException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * APK signer. * *

The signer preserves as much of the input APK as possible. For example, it preserves the order * of APK entries and preserves their contents, including compressed form and alignment of data. * *

Use {@link Builder} to obtain instances of this signer. * * @see Application Signing */ public class ApkSigner { /** * Extensible data block/field header ID used for storing information about alignment of * uncompressed entries as well as for aligning the entries's data. See ZIP appnote.txt section * 4.5 Extensible data fields. */ private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID = (short) 0xd935; /** * Minimum size (in bytes) of the extensible data block/field used for alignment of uncompressed * entries. */ private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES = 6; private static final short ANDROID_FILE_ALIGNMENT_BYTES = 4096; /** Name of the Android manifest ZIP entry in APKs. */ private static final String ANDROID_MANIFEST_ZIP_ENTRY_NAME = "AndroidManifest.xml"; private final List mSignerConfigs; private final SignerConfig mSourceStampSignerConfig; private final SigningCertificateLineage mSourceStampSigningCertificateLineage; private final boolean mForceSourceStampOverwrite; private final boolean mSourceStampTimestampEnabled; private final Integer mMinSdkVersion; private final int mRotationMinSdkVersion; private final boolean mRotationTargetsDevRelease; private final boolean mV1SigningEnabled; private final boolean mV2SigningEnabled; private final boolean mV3SigningEnabled; private final boolean mV4SigningEnabled; private final boolean mAlignFileSize; private final boolean mVerityEnabled; private final boolean mV4ErrorReportingEnabled; private final boolean mDebuggableApkPermitted; private final boolean mOtherSignersSignaturesPreserved; private final boolean mAlignmentPreserved; private final int mLibraryPageAlignmentBytes; private final String mCreatedBy; private final ApkSignerEngine mSignerEngine; private final File mInputApkFile; private final DataSource mInputApkDataSource; private final File mOutputApkFile; private final DataSink mOutputApkDataSink; private final DataSource mOutputApkDataSource; private final File mOutputV4File; private final SigningCertificateLineage mSigningCertificateLineage; private ApkSigner( List signerConfigs, SignerConfig sourceStampSignerConfig, SigningCertificateLineage sourceStampSigningCertificateLineage, boolean forceSourceStampOverwrite, boolean sourceStampTimestampEnabled, Integer minSdkVersion, int rotationMinSdkVersion, boolean rotationTargetsDevRelease, boolean v1SigningEnabled, boolean v2SigningEnabled, boolean v3SigningEnabled, boolean v4SigningEnabled, boolean alignFileSize, boolean verityEnabled, boolean v4ErrorReportingEnabled, boolean debuggableApkPermitted, boolean otherSignersSignaturesPreserved, boolean alignmentPreserved, int libraryPageAlignmentBytes, String createdBy, ApkSignerEngine signerEngine, File inputApkFile, DataSource inputApkDataSource, File outputApkFile, DataSink outputApkDataSink, DataSource outputApkDataSource, File outputV4File, SigningCertificateLineage signingCertificateLineage) { mSignerConfigs = signerConfigs; mSourceStampSignerConfig = sourceStampSignerConfig; mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; mForceSourceStampOverwrite = forceSourceStampOverwrite; mSourceStampTimestampEnabled = sourceStampTimestampEnabled; mMinSdkVersion = minSdkVersion; mRotationMinSdkVersion = rotationMinSdkVersion; mRotationTargetsDevRelease = rotationTargetsDevRelease; mV1SigningEnabled = v1SigningEnabled; mV2SigningEnabled = v2SigningEnabled; mV3SigningEnabled = v3SigningEnabled; mV4SigningEnabled = v4SigningEnabled; mAlignFileSize = alignFileSize; mVerityEnabled = verityEnabled; mV4ErrorReportingEnabled = v4ErrorReportingEnabled; mDebuggableApkPermitted = debuggableApkPermitted; mOtherSignersSignaturesPreserved = otherSignersSignaturesPreserved; mAlignmentPreserved = alignmentPreserved; mLibraryPageAlignmentBytes = libraryPageAlignmentBytes; mCreatedBy = createdBy; mSignerEngine = signerEngine; mInputApkFile = inputApkFile; mInputApkDataSource = inputApkDataSource; mOutputApkFile = outputApkFile; mOutputApkDataSink = outputApkDataSink; mOutputApkDataSource = outputApkDataSource; mOutputV4File = outputV4File; mSigningCertificateLineage = signingCertificateLineage; } /** * Signs the input APK and outputs the resulting signed APK. The input APK is not modified. * * @throws IOException if an I/O error is encountered while reading or writing the APKs * @throws ApkFormatException if the input APK is malformed * @throws NoSuchAlgorithmException if the APK signatures cannot be produced or verified because * a required cryptographic algorithm implementation is missing * @throws InvalidKeyException if a signature could not be generated because a signing key is * not suitable for generating the signature * @throws SignatureException if an error occurred while generating or verifying a signature * @throws IllegalStateException if this signer's configuration is missing required information * or if the signing engine is in an invalid state. */ public void sign() throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, IllegalStateException { Closeable in = null; DataSource inputApk; try { if (mInputApkDataSource != null) { inputApk = mInputApkDataSource; } else if (mInputApkFile != null) { RandomAccessFile inputFile = new RandomAccessFile(mInputApkFile, "r"); in = inputFile; inputApk = DataSources.asDataSource(inputFile); } else { throw new IllegalStateException("Input APK not specified"); } Closeable out = null; try { DataSink outputApkOut; DataSource outputApkIn; if (mOutputApkDataSink != null) { outputApkOut = mOutputApkDataSink; outputApkIn = mOutputApkDataSource; } else if (mOutputApkFile != null) { RandomAccessFile outputFile = new RandomAccessFile(mOutputApkFile, "rw"); out = outputFile; outputFile.setLength(0); outputApkOut = DataSinks.asDataSink(outputFile); outputApkIn = DataSources.asDataSource(outputFile); } else { throw new IllegalStateException("Output APK not specified"); } sign(inputApk, outputApkOut, outputApkIn); } finally { if (out != null) { out.close(); } } } finally { if (in != null) { in.close(); } } } private void sign(DataSource inputApk, DataSink outputApkOut, DataSource outputApkIn) throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { // Step 1. Find input APK's main ZIP sections ApkUtils.ZipSections inputZipSections; try { inputZipSections = ApkUtils.findZipSections(inputApk); } catch (ZipFormatException e) { throw new ApkFormatException("Malformed APK: not a ZIP archive", e); } long inputApkSigningBlockOffset = -1; DataSource inputApkSigningBlock = null; try { ApkUtils.ApkSigningBlock apkSigningBlockInfo = ApkUtils.findApkSigningBlock(inputApk, inputZipSections); inputApkSigningBlockOffset = apkSigningBlockInfo.getStartOffset(); inputApkSigningBlock = apkSigningBlockInfo.getContents(); } catch (ApkSigningBlockNotFoundException e) { // Input APK does not contain an APK Signing Block. That's OK. APKs are not required to // contain this block. It's only needed if the APK is signed using APK Signature Scheme // v2 and/or v3. } DataSource inputApkLfhSection = inputApk.slice( 0, (inputApkSigningBlockOffset != -1) ? inputApkSigningBlockOffset : inputZipSections.getZipCentralDirectoryOffset()); // Step 2. Parse the input APK's ZIP Central Directory ByteBuffer inputCd = getZipCentralDirectory(inputApk, inputZipSections); List inputCdRecords = parseZipCentralDirectory(inputCd, inputZipSections); List pinPatterns = extractPinPatterns(inputCdRecords, inputApkLfhSection); List pinByteRanges = pinPatterns == null ? null : new ArrayList<>(); // Step 3. Obtain a signer engine instance ApkSignerEngine signerEngine; if (mSignerEngine != null) { // Use the provided signer engine signerEngine = mSignerEngine; } else { // Construct a signer engine from the provided parameters int minSdkVersion; if (mMinSdkVersion != null) { // No need to extract minSdkVersion from the APK's AndroidManifest.xml minSdkVersion = mMinSdkVersion; } else { // Need to extract minSdkVersion from the APK's AndroidManifest.xml minSdkVersion = getMinSdkVersionFromApk(inputCdRecords, inputApkLfhSection); } List engineSignerConfigs = new ArrayList<>(mSignerConfigs.size()); for (SignerConfig signerConfig : mSignerConfigs) { DefaultApkSignerEngine.SignerConfig.Builder signerConfigBuilder = new DefaultApkSignerEngine.SignerConfig.Builder( signerConfig.getName(), signerConfig.getKeyConfig(), signerConfig.getCertificates(), signerConfig.getDeterministicDsaSigning()); int signerMinSdkVersion = signerConfig.getMinSdkVersion(); SigningCertificateLineage signerLineage = signerConfig.getSigningCertificateLineage(); if (signerMinSdkVersion > 0) { signerConfigBuilder.setLineageForMinSdkVersion(signerLineage, signerMinSdkVersion); } engineSignerConfigs.add(signerConfigBuilder.build()); } DefaultApkSignerEngine.Builder signerEngineBuilder = new DefaultApkSignerEngine.Builder(engineSignerConfigs, minSdkVersion) .setV1SigningEnabled(mV1SigningEnabled) .setV2SigningEnabled(mV2SigningEnabled) .setV3SigningEnabled(mV3SigningEnabled) .setVerityEnabled(mVerityEnabled) .setDebuggableApkPermitted(mDebuggableApkPermitted) .setOtherSignersSignaturesPreserved(mOtherSignersSignaturesPreserved) .setSigningCertificateLineage(mSigningCertificateLineage) .setMinSdkVersionForRotation(mRotationMinSdkVersion) .setRotationTargetsDevRelease(mRotationTargetsDevRelease); if (mCreatedBy != null) { signerEngineBuilder.setCreatedBy(mCreatedBy); } if (mSourceStampSignerConfig != null) { signerEngineBuilder.setStampSignerConfig( new DefaultApkSignerEngine.SignerConfig.Builder( mSourceStampSignerConfig.getName(), mSourceStampSignerConfig.getKeyConfig(), mSourceStampSignerConfig.getCertificates(), mSourceStampSignerConfig.getDeterministicDsaSigning()) .build()); signerEngineBuilder.setSourceStampTimestampEnabled(mSourceStampTimestampEnabled); } if (mSourceStampSigningCertificateLineage != null) { signerEngineBuilder.setSourceStampSigningCertificateLineage( mSourceStampSigningCertificateLineage); } signerEngine = signerEngineBuilder.build(); } // Step 4. Provide the signer engine with the input APK's APK Signing Block (if any) if (inputApkSigningBlock != null) { signerEngine.inputApkSigningBlock(inputApkSigningBlock); } // Step 5. Iterate over input APK's entries and output the Local File Header + data of those // entries which need to be output. Entries are iterated in the order in which their Local // File Header records are stored in the file. This is to achieve better data locality in // case Central Directory entries are in the wrong order. List inputCdRecordsSortedByLfhOffset = new ArrayList<>(inputCdRecords); Collections.sort( inputCdRecordsSortedByLfhOffset, CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR); int lastModifiedDateForNewEntries = -1; int lastModifiedTimeForNewEntries = -1; long inputOffset = 0; long outputOffset = 0; byte[] sourceStampCertificateDigest = null; Map outputCdRecordsByName = new HashMap<>(inputCdRecords.size()); for (final CentralDirectoryRecord inputCdRecord : inputCdRecordsSortedByLfhOffset) { String entryName = inputCdRecord.getName(); if (Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME.equals(entryName)) { continue; // We'll re-add below if needed. } if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(entryName)) { try { sourceStampCertificateDigest = LocalFileRecord.getUncompressedData( inputApkLfhSection, inputCdRecord, inputApkLfhSection.size()); } catch (ZipFormatException ex) { throw new ApkFormatException("Bad source stamp entry"); } continue; // Existing source stamp is handled below as needed. } ApkSignerEngine.InputJarEntryInstructions entryInstructions = signerEngine.inputJarEntry(entryName); boolean shouldOutput; switch (entryInstructions.getOutputPolicy()) { case OUTPUT: shouldOutput = true; break; case OUTPUT_BY_ENGINE: case SKIP: shouldOutput = false; break; default: throw new RuntimeException( "Unknown output policy: " + entryInstructions.getOutputPolicy()); } long inputLocalFileHeaderStartOffset = inputCdRecord.getLocalFileHeaderOffset(); if (inputLocalFileHeaderStartOffset > inputOffset) { // Unprocessed data in input starting at inputOffset and ending and the start of // this record's LFH. We output this data verbatim because this signer is supposed // to preserve as much of input as possible. long chunkSize = inputLocalFileHeaderStartOffset - inputOffset; inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut); outputOffset += chunkSize; inputOffset = inputLocalFileHeaderStartOffset; } LocalFileRecord inputLocalFileRecord; try { inputLocalFileRecord = LocalFileRecord.getRecord( inputApkLfhSection, inputCdRecord, inputApkLfhSection.size()); } catch (ZipFormatException e) { throw new ApkFormatException("Malformed ZIP entry: " + inputCdRecord.getName(), e); } inputOffset += inputLocalFileRecord.getSize(); ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest = entryInstructions.getInspectJarEntryRequest(); if (inspectEntryRequest != null) { fulfillInspectInputJarEntryRequest( inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest); } if (shouldOutput) { // Find the max value of last modified, to be used for new entries added by the // signer. int lastModifiedDate = inputCdRecord.getLastModificationDate(); int lastModifiedTime = inputCdRecord.getLastModificationTime(); if ((lastModifiedDateForNewEntries == -1) || (lastModifiedDate > lastModifiedDateForNewEntries) || ((lastModifiedDate == lastModifiedDateForNewEntries) && (lastModifiedTime > lastModifiedTimeForNewEntries))) { lastModifiedDateForNewEntries = lastModifiedDate; lastModifiedTimeForNewEntries = lastModifiedTime; } inspectEntryRequest = signerEngine.outputJarEntry(entryName); if (inspectEntryRequest != null) { fulfillInspectInputJarEntryRequest( inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest); } // Output entry's Local File Header + data long outputLocalFileHeaderOffset = outputOffset; OutputSizeAndDataOffset outputLfrResult = outputInputJarEntryLfhRecord( inputApkLfhSection, inputLocalFileRecord, outputApkOut, outputLocalFileHeaderOffset); outputOffset += outputLfrResult.outputBytes; long outputDataOffset = outputLocalFileHeaderOffset + outputLfrResult.dataOffsetBytes; if (pinPatterns != null) { boolean pinFileHeader = false; for (Hints.PatternWithRange pinPattern : pinPatterns) { if (pinPattern.matcher(inputCdRecord.getName()).matches()) { Hints.ByteRange dataRange = new Hints.ByteRange(outputDataOffset, outputOffset); Hints.ByteRange pinRange = pinPattern.ClampToAbsoluteByteRange(dataRange); if (pinRange != null) { pinFileHeader = true; pinByteRanges.add(pinRange); } } } if (pinFileHeader) { pinByteRanges.add( new Hints.ByteRange(outputLocalFileHeaderOffset, outputDataOffset)); } } // Enqueue entry's Central Directory record for output CentralDirectoryRecord outputCdRecord; if (outputLocalFileHeaderOffset == inputLocalFileRecord.getStartOffsetInArchive()) { outputCdRecord = inputCdRecord; } else { outputCdRecord = inputCdRecord.createWithModifiedLocalFileHeaderOffset( outputLocalFileHeaderOffset); } outputCdRecordsByName.put(entryName, outputCdRecord); } } long inputLfhSectionSize = inputApkLfhSection.size(); if (inputOffset < inputLfhSectionSize) { // Unprocessed data in input starting at inputOffset and ending and the end of the input // APK's LFH section. We output this data verbatim because this signer is supposed // to preserve as much of input as possible. long chunkSize = inputLfhSectionSize - inputOffset; inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut); outputOffset += chunkSize; inputOffset = inputLfhSectionSize; } // Step 6. Sort output APK's Central Directory records in the order in which they should // appear in the output List outputCdRecords = new ArrayList<>(inputCdRecords.size() + 10); for (CentralDirectoryRecord inputCdRecord : inputCdRecords) { String entryName = inputCdRecord.getName(); CentralDirectoryRecord outputCdRecord = outputCdRecordsByName.get(entryName); if (outputCdRecord != null) { outputCdRecords.add(outputCdRecord); } } if (lastModifiedDateForNewEntries == -1) { lastModifiedDateForNewEntries = 0x3a21; // Jan 1 2009 (DOS) lastModifiedTimeForNewEntries = 0; } // Step 7. Generate and output SourceStamp certificate hash, if necessary. This may output // more Local File Header + data entries and add to the list of output Central Directory // records. if (signerEngine.isEligibleForSourceStamp()) { byte[] uncompressedData = signerEngine.generateSourceStampCertificateDigest(); if (mForceSourceStampOverwrite || sourceStampCertificateDigest == null || Arrays.equals(uncompressedData, sourceStampCertificateDigest)) { outputOffset += outputDataToOutputApk( SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME, uncompressedData, outputOffset, outputCdRecords, lastModifiedTimeForNewEntries, lastModifiedDateForNewEntries, outputApkOut); } else { throw new ApkFormatException( String.format( "Cannot generate SourceStamp. APK contains an existing entry with" + " the name: %s, and it is different than the provided source" + " stamp certificate", SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME)); } } // Step 7.5. Generate pinlist.meta file if necessary. // This has to be before the step 8 so that the file is signed. if (pinByteRanges != null) { // Covers JAR signature and zip central dir entry. // The signature files don't have to be pinned, but pinning them isn't that wasteful // since the total size is small. pinByteRanges.add(new Hints.ByteRange(outputOffset, Long.MAX_VALUE)); String entryName = Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME; byte[] uncompressedData = Hints.encodeByteRangeList(pinByteRanges); requestOutputEntryInspection(signerEngine, entryName, uncompressedData); outputOffset += outputDataToOutputApk( entryName, uncompressedData, outputOffset, outputCdRecords, lastModifiedTimeForNewEntries, lastModifiedDateForNewEntries, outputApkOut); } // Step 8. Generate and output JAR signatures, if necessary. This may output more Local File // Header + data entries and add to the list of output Central Directory records. ApkSignerEngine.OutputJarSignatureRequest outputJarSignatureRequest = signerEngine.outputJarEntries(); if (outputJarSignatureRequest != null) { for (ApkSignerEngine.OutputJarSignatureRequest.JarEntry entry : outputJarSignatureRequest.getAdditionalJarEntries()) { String entryName = entry.getName(); byte[] uncompressedData = entry.getData(); requestOutputEntryInspection(signerEngine, entryName, uncompressedData); outputOffset += outputDataToOutputApk( entryName, uncompressedData, outputOffset, outputCdRecords, lastModifiedTimeForNewEntries, lastModifiedDateForNewEntries, outputApkOut); } outputJarSignatureRequest.done(); } // Step 9. Construct output ZIP Central Directory in an in-memory buffer long outputCentralDirSizeBytes = 0; for (CentralDirectoryRecord record : outputCdRecords) { outputCentralDirSizeBytes += record.getSize(); } if (outputCentralDirSizeBytes > Integer.MAX_VALUE) { throw new IOException( "Output ZIP Central Directory too large: " + outputCentralDirSizeBytes + " bytes"); } ByteBuffer outputCentralDir = ByteBuffer.allocate((int) outputCentralDirSizeBytes); for (CentralDirectoryRecord record : outputCdRecords) { record.copyTo(outputCentralDir); } outputCentralDir.flip(); DataSource outputCentralDirDataSource = new ByteBufferDataSource(outputCentralDir); long outputCentralDirStartOffset = outputOffset; int outputCentralDirRecordCount = outputCdRecords.size(); // Step 10. Construct output ZIP End of Central Directory record in an in-memory buffer // because it can be adjusted in Step 11 due to signing block. // - CD offset (it's shifted by signing block) // - Comments (when the output file needs to be sized 4k-aligned) ByteBuffer outputEocd = EocdRecord.createWithModifiedCentralDirectoryInfo( inputZipSections.getZipEndOfCentralDirectory(), outputCentralDirRecordCount, outputCentralDirDataSource.size(), outputCentralDirStartOffset); // Step 11. Generate and output APK Signature Scheme v2 and/or v3 signatures and/or // SourceStamp signatures, if necessary. // This may insert an APK Signing Block just before the output's ZIP Central Directory ApkSignerEngine.OutputApkSigningBlockRequest2 outputApkSigningBlockRequest = signerEngine.outputZipSections2( outputApkIn, outputCentralDirDataSource, DataSources.asDataSource(outputEocd)); if (outputApkSigningBlockRequest != null) { int padding = outputApkSigningBlockRequest.getPaddingSizeBeforeApkSigningBlock(); byte[] outputApkSigningBlock = outputApkSigningBlockRequest.getApkSigningBlock(); outputApkSigningBlockRequest.done(); long fileSize = outputCentralDirStartOffset + outputCentralDirDataSource.size() + padding + outputApkSigningBlock.length + outputEocd.remaining(); if (mAlignFileSize && (fileSize % ANDROID_FILE_ALIGNMENT_BYTES != 0)) { int eocdPadding = (int) (ANDROID_FILE_ALIGNMENT_BYTES - fileSize % ANDROID_FILE_ALIGNMENT_BYTES); // Replace EOCD with padding one so that output file size can be the multiples of // alignment. outputEocd = EocdRecord.createWithPaddedComment(outputEocd, eocdPadding); // Since EoCD has changed, we need to regenerate signing block as well. outputApkSigningBlockRequest = signerEngine.outputZipSections2( outputApkIn, new ByteBufferDataSource(outputCentralDir), DataSources.asDataSource(outputEocd)); outputApkSigningBlock = outputApkSigningBlockRequest.getApkSigningBlock(); outputApkSigningBlockRequest.done(); } outputApkOut.consume(ByteBuffer.allocate(padding)); outputApkOut.consume(outputApkSigningBlock, 0, outputApkSigningBlock.length); ZipUtils.setZipEocdCentralDirectoryOffset( outputEocd, outputCentralDirStartOffset + padding + outputApkSigningBlock.length); } // Step 12. Output ZIP Central Directory and ZIP End of Central Directory outputCentralDirDataSource.feed(0, outputCentralDirDataSource.size(), outputApkOut); outputApkOut.consume(outputEocd); signerEngine.outputDone(); // Step 13. Generate and output APK Signature Scheme v4 signatures, if necessary. if (mV4SigningEnabled) { signerEngine.signV4(outputApkIn, mOutputV4File, !mV4ErrorReportingEnabled); } } private static void requestOutputEntryInspection( ApkSignerEngine signerEngine, String entryName, byte[] uncompressedData) throws IOException { ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest = signerEngine.outputJarEntry(entryName); if (inspectEntryRequest != null) { inspectEntryRequest.getDataSink().consume( uncompressedData, 0, uncompressedData.length); inspectEntryRequest.done(); } } private static long outputDataToOutputApk( String entryName, byte[] uncompressedData, long localFileHeaderOffset, List outputCdRecords, int lastModifiedTimeForNewEntries, int lastModifiedDateForNewEntries, DataSink outputApkOut) throws IOException { ZipUtils.DeflateResult deflateResult = ZipUtils.deflate(ByteBuffer.wrap(uncompressedData)); byte[] compressedData = deflateResult.output; long uncompressedDataCrc32 = deflateResult.inputCrc32; long numOfDataBytes = LocalFileRecord.outputRecordWithDeflateCompressedData( entryName, lastModifiedTimeForNewEntries, lastModifiedDateForNewEntries, compressedData, uncompressedDataCrc32, uncompressedData.length, outputApkOut); outputCdRecords.add( CentralDirectoryRecord.createWithDeflateCompressedData( entryName, lastModifiedTimeForNewEntries, lastModifiedDateForNewEntries, uncompressedDataCrc32, compressedData.length, uncompressedData.length, localFileHeaderOffset)); return numOfDataBytes; } private static void fulfillInspectInputJarEntryRequest( DataSource lfhSection, LocalFileRecord localFileRecord, ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest) throws IOException, ApkFormatException { try { localFileRecord.outputUncompressedData(lfhSection, inspectEntryRequest.getDataSink()); } catch (ZipFormatException e) { throw new ApkFormatException("Malformed ZIP entry: " + localFileRecord.getName(), e); } inspectEntryRequest.done(); } private static class OutputSizeAndDataOffset { public long outputBytes; public long dataOffsetBytes; public OutputSizeAndDataOffset(long outputBytes, long dataOffsetBytes) { this.outputBytes = outputBytes; this.dataOffsetBytes = dataOffsetBytes; } } private OutputSizeAndDataOffset outputInputJarEntryLfhRecord( DataSource inputLfhSection, LocalFileRecord inputRecord, DataSink outputLfhSection, long outputOffset) throws IOException { long inputOffset = inputRecord.getStartOffsetInArchive(); if (inputOffset == outputOffset && mAlignmentPreserved) { // This record's data will be aligned same as in the input APK. return new OutputSizeAndDataOffset( inputRecord.outputRecord(inputLfhSection, outputLfhSection), inputRecord.getDataStartOffsetInRecord()); } int dataAlignmentMultiple = getInputJarEntryDataAlignmentMultiple(inputRecord); if ((dataAlignmentMultiple <= 1) || ((inputOffset % dataAlignmentMultiple) == (outputOffset % dataAlignmentMultiple) && mAlignmentPreserved)) { // This record's data will be aligned same as in the input APK. return new OutputSizeAndDataOffset( inputRecord.outputRecord(inputLfhSection, outputLfhSection), inputRecord.getDataStartOffsetInRecord()); } long inputDataStartOffset = inputOffset + inputRecord.getDataStartOffsetInRecord(); if ((inputDataStartOffset % dataAlignmentMultiple) != 0 && mAlignmentPreserved) { // This record's data is not aligned in the input APK. No need to align it in the // output. return new OutputSizeAndDataOffset( inputRecord.outputRecord(inputLfhSection, outputLfhSection), inputRecord.getDataStartOffsetInRecord()); } // This record's data needs to be re-aligned in the output. This is achieved using the // record's extra field. ByteBuffer aligningExtra = createExtraFieldToAlignData( inputRecord.getExtra(), outputOffset + inputRecord.getExtraFieldStartOffsetInsideRecord(), dataAlignmentMultiple); long dataOffset = (long) inputRecord.getDataStartOffsetInRecord() + aligningExtra.remaining() - inputRecord.getExtra().remaining(); return new OutputSizeAndDataOffset( inputRecord.outputRecordWithModifiedExtra( inputLfhSection, aligningExtra, outputLfhSection), dataOffset); } private int getInputJarEntryDataAlignmentMultiple(LocalFileRecord entry) { if (entry.isDataCompressed()) { // Compressed entries don't need to be aligned return 1; } // Attempt to obtain the alignment multiple from the entry's extra field. ByteBuffer extra = entry.getExtra(); if (extra.hasRemaining()) { extra.order(ByteOrder.LITTLE_ENDIAN); // FORMAT: sequence of fields. Each field consists of: // * uint16 ID // * uint16 size // * 'size' bytes: payload while (extra.remaining() >= 4) { short headerId = extra.getShort(); int dataSize = ZipUtils.getUnsignedInt16(extra); if (dataSize > extra.remaining()) { // Malformed field -- insufficient input remaining break; } if (headerId != ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID) { // Skip this field extra.position(extra.position() + dataSize); continue; } // This is APK alignment field. // FORMAT: // * uint16 alignment multiple (in bytes) // * remaining bytes -- padding to achieve alignment of data which starts after // the extra field if (dataSize < 2) { // Malformed break; } return ZipUtils.getUnsignedInt16(extra); } } // Fall back to filename-based defaults return (entry.getName().endsWith(".so")) ? mLibraryPageAlignmentBytes : 4; } private static ByteBuffer createExtraFieldToAlignData( ByteBuffer original, long extraStartOffset, int dataAlignmentMultiple) { if (dataAlignmentMultiple <= 1) { return original; } // In the worst case scenario, we'll increase the output size by 6 + dataAlignment - 1. ByteBuffer result = ByteBuffer.allocate(original.remaining() + 5 + dataAlignmentMultiple); result.order(ByteOrder.LITTLE_ENDIAN); // Step 1. Output all extra fields other than the one which is to do with alignment // FORMAT: sequence of fields. Each field consists of: // * uint16 ID // * uint16 size // * 'size' bytes: payload while (original.remaining() >= 4) { short headerId = original.getShort(); int dataSize = ZipUtils.getUnsignedInt16(original); if (dataSize > original.remaining()) { // Malformed field -- insufficient input remaining break; } if (((headerId == 0) && (dataSize == 0)) || (headerId == ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID)) { // Ignore the field if it has to do with the old APK data alignment method (filling // the extra field with 0x00 bytes) or the new APK data alignment method. original.position(original.position() + dataSize); continue; } // Copy this field (including header) to the output original.position(original.position() - 4); int originalLimit = original.limit(); original.limit(original.position() + 4 + dataSize); result.put(original); original.limit(originalLimit); } // Step 2. Add alignment field // FORMAT: // * uint16 extra header ID // * uint16 extra data size // Payload ('data size' bytes) // * uint16 alignment multiple (in bytes) // * remaining bytes -- padding to achieve alignment of data which starts after the // extra field long dataMinStartOffset = extraStartOffset + result.position() + ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES; int paddingSizeBytes = (dataAlignmentMultiple - ((int) (dataMinStartOffset % dataAlignmentMultiple))) % dataAlignmentMultiple; result.putShort(ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID); ZipUtils.putUnsignedInt16(result, 2 + paddingSizeBytes); ZipUtils.putUnsignedInt16(result, dataAlignmentMultiple); result.position(result.position() + paddingSizeBytes); result.flip(); return result; } private static ByteBuffer getZipCentralDirectory( DataSource apk, ApkUtils.ZipSections apkSections) throws IOException, ApkFormatException { long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes(); if (cdSizeBytes > Integer.MAX_VALUE) { throw new ApkFormatException("ZIP Central Directory too large: " + cdSizeBytes); } long cdOffset = apkSections.getZipCentralDirectoryOffset(); ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes); cd.order(ByteOrder.LITTLE_ENDIAN); return cd; } private static List parseZipCentralDirectory( ByteBuffer cd, ApkUtils.ZipSections apkSections) throws ApkFormatException { long cdOffset = apkSections.getZipCentralDirectoryOffset(); int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount(); List cdRecords = new ArrayList<>(expectedCdRecordCount); Set entryNames = new HashSet<>(expectedCdRecordCount); for (int i = 0; i < expectedCdRecordCount; i++) { CentralDirectoryRecord cdRecord; int offsetInsideCd = cd.position(); try { cdRecord = CentralDirectoryRecord.getRecord(cd); } catch (ZipFormatException e) { throw new ApkFormatException( "Malformed ZIP Central Directory record #" + (i + 1) + " at file offset " + (cdOffset + offsetInsideCd), e); } String entryName = cdRecord.getName(); if (!entryNames.add(entryName)) { throw new ApkFormatException( "Multiple ZIP entries with the same name: " + entryName); } cdRecords.add(cdRecord); } if (cd.hasRemaining()) { throw new ApkFormatException( "Unused space at the end of ZIP Central Directory: " + cd.remaining() + " bytes starting at file offset " + (cdOffset + cd.position())); } return cdRecords; } private static CentralDirectoryRecord findCdRecord( List cdRecords, String name) { for (CentralDirectoryRecord cdRecord : cdRecords) { if (name.equals(cdRecord.getName())) { return cdRecord; } } return null; } /** * Returns the contents of the APK's {@code AndroidManifest.xml} or {@code null} if this entry * is not present in the APK. */ static ByteBuffer getAndroidManifestFromApk( List cdRecords, DataSource lhfSection) throws IOException, ApkFormatException, ZipFormatException { CentralDirectoryRecord androidManifestCdRecord = findCdRecord(cdRecords, ANDROID_MANIFEST_ZIP_ENTRY_NAME); if (androidManifestCdRecord == null) { throw new ApkFormatException("Missing " + ANDROID_MANIFEST_ZIP_ENTRY_NAME); } return ByteBuffer.wrap( LocalFileRecord.getUncompressedData( lhfSection, androidManifestCdRecord, lhfSection.size())); } /** * Return list of pin patterns embedded in the pin pattern asset file. If no such file, return * {@code null}. */ private static List extractPinPatterns( List cdRecords, DataSource lhfSection) throws IOException, ApkFormatException { CentralDirectoryRecord pinListCdRecord = findCdRecord(cdRecords, Hints.PIN_HINT_ASSET_ZIP_ENTRY_NAME); List pinPatterns = null; if (pinListCdRecord != null) { pinPatterns = new ArrayList<>(); byte[] patternBlob; try { patternBlob = LocalFileRecord.getUncompressedData( lhfSection, pinListCdRecord, lhfSection.size()); } catch (ZipFormatException ex) { throw new ApkFormatException("Bad " + pinListCdRecord); } pinPatterns = Hints.parsePinPatterns(patternBlob); } return pinPatterns; } /** * Returns the minimum Android version (API Level) supported by the provided APK. This is based * on the {@code android:minSdkVersion} attributes of the APK's {@code AndroidManifest.xml}. */ private static int getMinSdkVersionFromApk( List cdRecords, DataSource lhfSection) throws IOException, MinSdkVersionException { ByteBuffer androidManifest; try { androidManifest = getAndroidManifestFromApk(cdRecords, lhfSection); } catch (ZipFormatException | ApkFormatException e) { throw new MinSdkVersionException( "Failed to determine APK's minimum supported Android platform version", e); } return ApkUtils.getMinSdkVersionFromBinaryAndroidManifest(androidManifest); } /** * Configuration of a signer. * *

Use {@link Builder} to obtain configuration instances. */ public static class SignerConfig { private final String mName; private final KeyConfig mKeyConfig; private final List mCertificates; private final boolean mDeterministicDsaSigning; private final int mMinSdkVersion; private final SigningCertificateLineage mSigningCertificateLineage; private SignerConfig(Builder builder) { mName = builder.mName; mKeyConfig = builder.mKeyConfig; mCertificates = Collections.unmodifiableList(new ArrayList<>(builder.mCertificates)); mDeterministicDsaSigning = builder.mDeterministicDsaSigning; mMinSdkVersion = builder.mMinSdkVersion; mSigningCertificateLineage = builder.mSigningCertificateLineage; } /** Returns the name of this signer. */ public String getName() { return mName; } /** * Returns the signing key of this signer. * * @deprecated Use {@link #getKeyConfig()} instead of accessing a {@link PrivateKey} * directly. If the user of ApkSigner is signing with a KMS instead of JCA, this method * will return null. */ @Deprecated public PrivateKey getPrivateKey() { return mKeyConfig.match(jca -> jca.privateKey, kms -> null); } public KeyConfig getKeyConfig() { return mKeyConfig; } /** * Returns the certificate(s) of this signer. The first certificate's public key corresponds * to this signer's private key. */ public List getCertificates() { return mCertificates; } /** * If this signer is a DSA signer, whether or not the signing is done deterministically. */ public boolean getDeterministicDsaSigning() { return mDeterministicDsaSigning; } /** Returns the minimum SDK version for which this signer should be used. */ public int getMinSdkVersion() { return mMinSdkVersion; } /** Returns the {@link SigningCertificateLineage} for this signer. */ public SigningCertificateLineage getSigningCertificateLineage() { return mSigningCertificateLineage; } /** Builder of {@link SignerConfig} instances. */ public static class Builder { private final String mName; private final KeyConfig mKeyConfig; private final List mCertificates; private final boolean mDeterministicDsaSigning; private int mMinSdkVersion; private SigningCertificateLineage mSigningCertificateLineage; /** * Constructs a new {@code Builder}. * * @deprecated use {@link #Builder(String, KeyConfig, List)} instead * @param name signer's name. The name is reflected in the name of files comprising the * JAR signature of the APK. * @param privateKey signing key * @param certificates list of one or more X.509 certificates. The subject public key of * the first certificate must correspond to the {@code privateKey}. */ @Deprecated public Builder(String name, PrivateKey privateKey, List certificates) { this(name, privateKey, certificates, false); } /** * Constructs a new {@code Builder}. * * @deprecated use {@link #Builder(String, KeyConfig, List, boolean)} instead * @param name signer's name. The name is reflected in the name of files comprising the * JAR signature of the APK. * @param privateKey signing key * @param certificates list of one or more X.509 certificates. The subject public key of * the first certificate must correspond to the {@code privateKey}. * @param deterministicDsaSigning When signing using DSA, whether or not the * deterministic variant (RFC6979) should be used. */ @Deprecated public Builder( String name, PrivateKey privateKey, List certificates, boolean deterministicDsaSigning) { if (name.isEmpty()) { throw new IllegalArgumentException("Empty name"); } mName = name; mKeyConfig = new KeyConfig.Jca(privateKey); mCertificates = new ArrayList<>(certificates); mDeterministicDsaSigning = deterministicDsaSigning; } /** * Constructs a new {@code Builder}. * * @param name signer's name. The name is reflected in the name of files comprising the * JAR signature of the APK. * @param keyConfig signing key configuration * @param certificates list of one or more X.509 certificates. The subject public key of * the first certificate must correspond to the {@code privateKey}. */ public Builder(String name, KeyConfig keyConfig, List certificates) { this(name, keyConfig, certificates, false); } /** * Constructs a new {@code Builder}. * * @param name signer's name. The name is reflected in the name of files comprising the * JAR signature of the APK. * @param keyConfig signing key configuration * @param certificates list of one or more X.509 certificates. The subject public key of * the first certificate must correspond to the {@code privateKey}. * @param deterministicDsaSigning When signing using DSA, whether or not the * deterministic variant (RFC6979) should be used. */ public Builder( String name, KeyConfig keyConfig, List certificates, boolean deterministicDsaSigning) { if (name.isEmpty()) { throw new IllegalArgumentException("Empty name"); } mName = name; mKeyConfig = keyConfig; mCertificates = new ArrayList<>(certificates); mDeterministicDsaSigning = deterministicDsaSigning; } /** @see #setLineageForMinSdkVersion(SigningCertificateLineage, int) */ public Builder setMinSdkVersion(int minSdkVersion) { return setLineageForMinSdkVersion(null, minSdkVersion); } /** * Sets the specified {@code minSdkVersion} as the minimum Android platform version * (API level) for which the provided {@code lineage} (where applicable) should be used * to produce the APK's signature. This method is useful if callers want to specify a * particular rotated signer or lineage with restricted capabilities for later * platform releases. * *

Note:>The V1 and V2 signature schemes do not support key rotation and * signing lineages with capabilities; only an app's original signer(s) can be used for * the V1 and V2 signature blocks. Because of this, only a value of {@code * minSdkVersion} >= 28 (Android P) where support for the V3 signature scheme was * introduced can be specified. * *

Note:Due to limitations with platform targeting in the V3.0 signature * scheme, specifying a {@code minSdkVersion} value <= 32 (Android Sv2) will result in * the current {@code SignerConfig} being used in the V3.0 signing block and applied to * Android P through at least Sv2 (and later depending on the {@code minSdkVersion} for * subsequent {@code SignerConfig} instances). Because of this, only a single {@code * SignerConfig} can be instantiated with a minimum SDK version <= 32. * * @param lineage the {@code SigningCertificateLineage} to target the specified {@code * minSdkVersion} * @param minSdkVersion the minimum SDK version for which this {@code SignerConfig} * should be used * @return this {@code Builder} instance * * @throws IllegalArgumentException if the provided {@code minSdkVersion} < 28 or the * certificate provided in the constructor is not in the specified {@code lineage}. */ public Builder setLineageForMinSdkVersion(SigningCertificateLineage lineage, int minSdkVersion) { if (minSdkVersion < AndroidSdkVersion.P) { throw new IllegalArgumentException( "SDK targeted signing config is only supported with the V3 signature " + "scheme on Android P (SDK version " + AndroidSdkVersion.P + ") and later"); } if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT) { minSdkVersion = AndroidSdkVersion.P; } mMinSdkVersion = minSdkVersion; // If a lineage is provided, ensure the signing certificate for this signer is in // the lineage; in the case of multiple signing certificates, the first is always // used in the lineage. if (lineage != null && !lineage.isCertificateInLineage(mCertificates.get(0))) { throw new IllegalArgumentException( "The provided lineage does not contain the signing certificate, " + mCertificates.get(0).getSubjectDN() + ", for this SignerConfig"); } mSigningCertificateLineage = lineage; return this; } /** * Returns a new {@code SignerConfig} instance configured based on the configuration of * this builder. */ public SignerConfig build() { return new SignerConfig(this); } } } /** * Builder of {@link ApkSigner} instances. * *

The builder requires the following information to construct a working {@code ApkSigner}: * *

    *
  • Signer configs or {@link ApkSignerEngine} -- provided in the constructor, *
  • APK to be signed -- see {@link #setInputApk(File) setInputApk} variants, *
  • where to store the output signed APK -- see {@link #setOutputApk(File) setOutputApk} * variants. *
*/ public static class Builder { private final List mSignerConfigs; private SignerConfig mSourceStampSignerConfig; private SigningCertificateLineage mSourceStampSigningCertificateLineage; private boolean mForceSourceStampOverwrite = false; private boolean mSourceStampTimestampEnabled = true; private boolean mV1SigningEnabled = true; private boolean mV2SigningEnabled = true; private boolean mV3SigningEnabled = true; private boolean mV4SigningEnabled = true; private boolean mAlignFileSize = false; private boolean mVerityEnabled = false; private boolean mV4ErrorReportingEnabled = false; private boolean mDebuggableApkPermitted = true; private boolean mOtherSignersSignaturesPreserved; private boolean mAlignmentPreserved = false; private int mLibraryPageAlignmentBytes = LIBRARY_PAGE_ALIGNMENT_BYTES; private String mCreatedBy; private Integer mMinSdkVersion; private int mRotationMinSdkVersion = V3SchemeConstants.DEFAULT_ROTATION_MIN_SDK_VERSION; private boolean mRotationTargetsDevRelease = false; private final ApkSignerEngine mSignerEngine; private File mInputApkFile; private DataSource mInputApkDataSource; private File mOutputApkFile; private DataSink mOutputApkDataSink; private DataSource mOutputApkDataSource; private File mOutputV4File; private SigningCertificateLineage mSigningCertificateLineage; // APK Signature Scheme v3 only supports a single signing certificate, so to move to v3 // signing by default, but not require prior clients to update to explicitly disable v3 // signing for multiple signers, we modify the mV3SigningEnabled depending on the provided // inputs (multiple signers and mSigningCertificateLineage in particular). Maintain two // extra variables to record whether or not mV3SigningEnabled has been set directly by a // client and so should override the default behavior. private boolean mV3SigningExplicitlyDisabled = false; private boolean mV3SigningExplicitlyEnabled = false; /** * Constructs a new {@code Builder} for an {@code ApkSigner} which signs using the provided * signer configurations. The resulting signer may be further customized through this * builder's setters, such as {@link #setMinSdkVersion(int)}, {@link * #setV1SigningEnabled(boolean)}, {@link #setV2SigningEnabled(boolean)}, {@link * #setOtherSignersSignaturesPreserved(boolean)}, {@link #setCreatedBy(String)}. * *

{@link #Builder(ApkSignerEngine)} is an alternative for advanced use cases where more * control over low-level details of signing is desired. */ public Builder(List signerConfigs) { if (signerConfigs.isEmpty()) { throw new IllegalArgumentException("At least one signer config must be provided"); } if (signerConfigs.size() > 1) { // APK Signature Scheme v3 only supports single signer, unless a // SigningCertificateLineage is provided, in which case this will be reset to true, // since we don't yet have a v4 scheme about which to worry mV3SigningEnabled = false; } mSignerConfigs = new ArrayList<>(signerConfigs); mSignerEngine = null; } /** * Constructs a new {@code Builder} for an {@code ApkSigner} which signs using the provided * signing engine. This is meant for advanced use cases where more control is needed over * the lower-level details of signing. For typical use cases, {@link #Builder(List)} is more * appropriate. */ public Builder(ApkSignerEngine signerEngine) { if (signerEngine == null) { throw new NullPointerException("signerEngine == null"); } mSignerEngine = signerEngine; mSignerConfigs = null; } /** Sets the signing configuration of the source stamp to be embedded in the APK. */ public Builder setSourceStampSignerConfig(SignerConfig sourceStampSignerConfig) { mSourceStampSignerConfig = sourceStampSignerConfig; return this; } /** * Sets the source stamp {@link SigningCertificateLineage}. This structure provides proof of * signing certificate rotation for certificates previously used to sign source stamps. */ public Builder setSourceStampSigningCertificateLineage( SigningCertificateLineage sourceStampSigningCertificateLineage) { mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; return this; } /** * Sets whether the APK should overwrite existing source stamp, if found. * * @param force {@code true} to require the APK to be overwrite existing source stamp */ public Builder setForceSourceStampOverwrite(boolean force) { mForceSourceStampOverwrite = force; return this; } /** * Sets whether the source stamp should contain the timestamp attribute with the time * at which the source stamp was signed. */ public Builder setSourceStampTimestampEnabled(boolean value) { mSourceStampTimestampEnabled = value; return this; } /** * Sets the APK to be signed. * * @see #setInputApk(DataSource) */ public Builder setInputApk(File inputApk) { if (inputApk == null) { throw new NullPointerException("inputApk == null"); } mInputApkFile = inputApk; mInputApkDataSource = null; return this; } /** * Sets the APK to be signed. * * @see #setInputApk(File) */ public Builder setInputApk(DataSource inputApk) { if (inputApk == null) { throw new NullPointerException("inputApk == null"); } mInputApkDataSource = inputApk; mInputApkFile = null; return this; } /** * Sets the location of the output (signed) APK. {@code ApkSigner} will create this file if * it doesn't exist. * * @see #setOutputApk(ReadableDataSink) * @see #setOutputApk(DataSink, DataSource) */ public Builder setOutputApk(File outputApk) { if (outputApk == null) { throw new NullPointerException("outputApk == null"); } mOutputApkFile = outputApk; mOutputApkDataSink = null; mOutputApkDataSource = null; return this; } /** * Sets the readable data sink which will receive the output (signed) APK. After signing, * the contents of the output APK will be available via the {@link DataSource} interface of * the sink. * *

This variant of {@code setOutputApk} is useful for avoiding writing the output APK to * a file. For example, an in-memory data sink, such as {@link * DataSinks#newInMemoryDataSink()}, could be used instead of a file. * * @see #setOutputApk(File) * @see #setOutputApk(DataSink, DataSource) */ public Builder setOutputApk(ReadableDataSink outputApk) { if (outputApk == null) { throw new NullPointerException("outputApk == null"); } return setOutputApk(outputApk, outputApk); } /** * Sets the sink which will receive the output (signed) APK. Data received by the {@code * outputApkOut} sink must be visible through the {@code outputApkIn} data source. * *

This is an advanced variant of {@link #setOutputApk(ReadableDataSink)}, enabling the * sink and the source to be different objects. * * @see #setOutputApk(ReadableDataSink) * @see #setOutputApk(File) */ public Builder setOutputApk(DataSink outputApkOut, DataSource outputApkIn) { if (outputApkOut == null) { throw new NullPointerException("outputApkOut == null"); } if (outputApkIn == null) { throw new NullPointerException("outputApkIn == null"); } mOutputApkFile = null; mOutputApkDataSink = outputApkOut; mOutputApkDataSource = outputApkIn; return this; } /** * Sets the location of the V4 output file. {@code ApkSigner} will create this file if it * doesn't exist. */ public Builder setV4SignatureOutputFile(File v4SignatureOutputFile) { if (v4SignatureOutputFile == null) { throw new NullPointerException("v4HashRootOutputFile == null"); } mOutputV4File = v4SignatureOutputFile; return this; } /** * Sets the minimum Android platform version (API Level) on which APK signatures produced by * the signer being built must verify. This method is useful for overriding the default * behavior where the minimum API Level is obtained from the {@code android:minSdkVersion} * attribute of the APK's {@code AndroidManifest.xml}. * *

Note: This method may result in APK signatures which don't verify on some * Android platform versions supported by the APK. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. * * @throws IllegalStateException if this builder was initialized with an {@link * ApkSignerEngine} */ public Builder setMinSdkVersion(int minSdkVersion) { checkInitializedWithoutEngine(); mMinSdkVersion = minSdkVersion; return this; } /** * Sets the minimum Android platform version (API Level) for which an APK's rotated signing * key should be used to produce the APK's signature. The original signing key for the APK * will be used for all previous platform versions. If a rotated key with signing lineage is * not provided then this method is a noop. This method is useful for overriding the * default behavior where Android T is set as the minimum API level for rotation. * *

Note:Specifying a {@code minSdkVersion} value <= 32 (Android Sv2) will result * in the original V3 signing block being used without platform targeting. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. * * @throws IllegalStateException if this builder was initialized with an {@link * ApkSignerEngine} */ public Builder setMinSdkVersionForRotation(int minSdkVersion) { checkInitializedWithoutEngine(); // If the provided SDK version does not support v3.1, then use the default SDK version // with rotation support. if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT) { mRotationMinSdkVersion = MIN_SDK_WITH_V3_SUPPORT; } else { mRotationMinSdkVersion = minSdkVersion; } return this; } /** * Sets whether the rotation-min-sdk-version is intended to target a development release; * this is primarily required after the T SDK is finalized, and an APK needs to target U * during its development cycle for rotation. * *

This is only required after the T SDK is finalized since S and earlier releases do * not know about the V3.1 block ID, but once T is released and work begins on U, U will * use the SDK version of T during development. Specifying a rotation-min-sdk-version of T's * SDK version along with setting {@code enabled} to true will allow an APK to use the * rotated key on a device running U while causing this to be bypassed for T. * *

Note:If the rotation-min-sdk-version is less than or equal to 32 (Android * Sv2), then the rotated signing key will be used in the v3.0 signing block and this call * will be a noop. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. */ public Builder setRotationTargetsDevRelease(boolean enabled) { checkInitializedWithoutEngine(); mRotationTargetsDevRelease = enabled; return this; } /** * Sets whether the APK should be signed using JAR signing (aka v1 signature scheme). * *

By default, whether APK is signed using JAR signing is determined by {@code * ApkSigner}, based on the platform versions supported by the APK or specified using {@link * #setMinSdkVersion(int)}. Disabling JAR signing will result in APK signatures which don't * verify on Android Marshmallow (Android 6.0, API Level 23) and lower. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. * * @param enabled {@code true} to require the APK to be signed using JAR signing, {@code * false} to require the APK to not be signed using JAR signing. * @throws IllegalStateException if this builder was initialized with an {@link * ApkSignerEngine} * @see JAR * signing */ public Builder setV1SigningEnabled(boolean enabled) { checkInitializedWithoutEngine(); mV1SigningEnabled = enabled; return this; } /** * Sets whether the APK should be signed using APK Signature Scheme v2 (aka v2 signature * scheme). * *

By default, whether APK is signed using APK Signature Scheme v2 is determined by * {@code ApkSigner} based on the platform versions supported by the APK or specified using * {@link #setMinSdkVersion(int)}. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. * * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme * v2, {@code false} to require the APK to not be signed using APK Signature Scheme v2. * @throws IllegalStateException if this builder was initialized with an {@link * ApkSignerEngine} * @see APK Signature * Scheme v2 */ public Builder setV2SigningEnabled(boolean enabled) { checkInitializedWithoutEngine(); mV2SigningEnabled = enabled; return this; } /** * Sets whether the APK should be signed using APK Signature Scheme v3 (aka v3 signature * scheme). * *

By default, whether APK is signed using APK Signature Scheme v3 is determined by * {@code ApkSigner} based on the platform versions supported by the APK or specified using * {@link #setMinSdkVersion(int)}. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. * *

Note: APK Signature Scheme v3 only supports a single signing certificate, but * may take multiple signers mapping to different targeted platform versions. * * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme * v3, {@code false} to require the APK to not be signed using APK Signature Scheme v3. * @throws IllegalStateException if this builder was initialized with an {@link * ApkSignerEngine} */ public Builder setV3SigningEnabled(boolean enabled) { checkInitializedWithoutEngine(); mV3SigningEnabled = enabled; if (enabled) { mV3SigningExplicitlyEnabled = true; } else { mV3SigningExplicitlyDisabled = true; } return this; } /** * Sets whether the APK should be signed using APK Signature Scheme v4. * *

V4 signing requires that the APK be v2 or v3 signed. * * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme v2 * or v3 and generate an v4 signature file */ public Builder setV4SigningEnabled(boolean enabled) { checkInitializedWithoutEngine(); mV4SigningEnabled = enabled; mV4ErrorReportingEnabled = enabled; return this; } /** * Sets whether errors during v4 signing should be reported and halt the signing process. * *

Error reporting for v4 signing is disabled by default, but will be enabled if the * caller invokes {@link #setV4SigningEnabled} with a value of true. This method is useful * for tools that enable v4 signing by default but don't want to fail the signing process if * the user did not explicitly request the v4 signing. * * @param enabled {@code false} to prevent errors encountered during the V4 signing from * halting the signing process */ public Builder setV4ErrorReportingEnabled(boolean enabled) { checkInitializedWithoutEngine(); mV4ErrorReportingEnabled = enabled; return this; } /** * Sets whether the output APK files should be sized as multiples of 4K. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. * * @throws IllegalStateException if this builder was initialized with an {@link * ApkSignerEngine} */ public Builder setAlignFileSize(boolean alignFileSize) { checkInitializedWithoutEngine(); mAlignFileSize = alignFileSize; return this; } /** * Sets whether to enable the verity signature algorithm for the v2 and v3 signature * schemes. * * @param enabled {@code true} to enable the verity signature algorithm for inclusion in the * v2 and v3 signature blocks. */ public Builder setVerityEnabled(boolean enabled) { checkInitializedWithoutEngine(); mVerityEnabled = enabled; return this; } /** * Sets whether the APK should be signed even if it is marked as debuggable ({@code * android:debuggable="true"} in its {@code AndroidManifest.xml}). For backward * compatibility reasons, the default value of this setting is {@code true}. * *

It is dangerous to sign debuggable APKs with production/release keys because Android * platform loosens security checks for such APKs. For example, arbitrary unauthorized code * may be executed in the context of such an app by anybody with ADB shell access. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. */ public Builder setDebuggableApkPermitted(boolean permitted) { checkInitializedWithoutEngine(); mDebuggableApkPermitted = permitted; return this; } /** * Sets whether signatures produced by signers other than the ones configured in this engine * should be copied from the input APK to the output APK. * *

By default, signatures of other signers are omitted from the output APK. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. * * @throws IllegalStateException if this builder was initialized with an {@link * ApkSignerEngine} */ public Builder setOtherSignersSignaturesPreserved(boolean preserved) { checkInitializedWithoutEngine(); mOtherSignersSignaturesPreserved = preserved; return this; } /** * Sets the value of the {@code Created-By} field in JAR signature files. * *

Note: This method may only be invoked when this builder is not initialized * with an {@link ApkSignerEngine}. * * @throws IllegalStateException if this builder was initialized with an {@link * ApkSignerEngine} */ public Builder setCreatedBy(String createdBy) { checkInitializedWithoutEngine(); if (createdBy == null) { throw new NullPointerException(); } mCreatedBy = createdBy; return this; } private void checkInitializedWithoutEngine() { if (mSignerEngine != null) { throw new IllegalStateException( "Operation is not available when builder initialized with an engine"); } } /** * Sets the {@link SigningCertificateLineage} to use with the v3 signature scheme. This * structure provides proof of signing certificate rotation linking {@link SignerConfig} * objects to previous ones. */ public Builder setSigningCertificateLineage( SigningCertificateLineage signingCertificateLineage) { if (signingCertificateLineage != null) { mV3SigningEnabled = true; mSigningCertificateLineage = signingCertificateLineage; } return this; } /** * Sets whether the existing alignment within the APK should be preserved; the * default for this setting is false. When this value is false, the value provided to * {@link #setLibraryPageAlignmentBytes(int)} will be used to page align native library * files and 4 bytes will be used to align all other uncompressed files. */ public Builder setAlignmentPreserved(boolean alignmentPreserved) { mAlignmentPreserved = alignmentPreserved; return this; } /** * Sets the number of bytes to be used to page align native library files in the APK; the * default for this setting is {@link Constants#LIBRARY_PAGE_ALIGNMENT_BYTES}. */ public Builder setLibraryPageAlignmentBytes(int libraryPageAlignmentBytes) { mLibraryPageAlignmentBytes = libraryPageAlignmentBytes; return this; } /** * Returns a new {@code ApkSigner} instance initialized according to the configuration of * this builder. */ public ApkSigner build() { if (mV3SigningExplicitlyDisabled && mV3SigningExplicitlyEnabled) { throw new IllegalStateException( "Builder configured to both enable and disable APK " + "Signature Scheme v3 signing"); } if (mV3SigningExplicitlyDisabled) { mV3SigningEnabled = false; } if (mV3SigningExplicitlyEnabled) { mV3SigningEnabled = true; } // If V4 signing is not explicitly set, and V2/V3 signing is disabled, then V4 signing // must be disabled as well as it is dependent on V2/V3. if (mV4SigningEnabled && !mV2SigningEnabled && !mV3SigningEnabled) { if (!mV4ErrorReportingEnabled) { mV4SigningEnabled = false; } else { throw new IllegalStateException( "APK Signature Scheme v4 signing requires at least " + "v2 or v3 signing to be enabled"); } } // TODO - if v3 signing is enabled, check provided signers and history to see if valid return new ApkSigner( mSignerConfigs, mSourceStampSignerConfig, mSourceStampSigningCertificateLineage, mForceSourceStampOverwrite, mSourceStampTimestampEnabled, mMinSdkVersion, mRotationMinSdkVersion, mRotationTargetsDevRelease, mV1SigningEnabled, mV2SigningEnabled, mV3SigningEnabled, mV4SigningEnabled, mAlignFileSize, mVerityEnabled, mV4ErrorReportingEnabled, mDebuggableApkPermitted, mOtherSignersSignaturesPreserved, mAlignmentPreserved, mLibraryPageAlignmentBytes, mCreatedBy, mSignerEngine, mInputApkFile, mInputApkDataSource, mOutputApkFile, mOutputApkDataSink, mOutputApkDataSource, mOutputV4File, mSigningCertificateLineage); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_ApkSignerEngine.java0100644 0000000 0000000 00000000034 14763776540 024750 xustar000000000 0000000 28 mtime=1741684064.5390000 src/main/java/com/android/apksig/ApkSignerEngine.java0100644 0000000 0000000 00000061077 14763776540 021627 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import com.android.apksig.util.RunnablesExecutor; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.util.List; import java.util.Set; /** * APK signing logic which is independent of how input and output APKs are stored, parsed, and * generated. * *

Operating Model

* * The abstract operating model is that there is an input APK which is being signed, thus producing * an output APK. In reality, there may be just an output APK being built from scratch, or the input * APK and the output APK may be the same file. Because this engine does not deal with reading and * writing files, it can handle all of these scenarios. * *

The engine is stateful and thus cannot be used for signing multiple APKs. However, once * the engine signed an APK, the engine can be used to re-sign the APK after it has been modified. * This may be more efficient than signing the APK using a new instance of the engine. See * Incremental Operation. * *

In the engine's operating model, a signed APK is produced as follows. *

    *
  1. JAR entries to be signed are output,
  2. *
  3. JAR archive is signed using JAR signing, thus adding the so-called v1 signature to the * output,
  4. *
  5. JAR archive is signed using APK Signature Scheme v2, thus adding the so-called v2 signature * to the output.
  6. *
* *

The input APK may contain JAR entries which, depending on the engine's configuration, may or * may not be output (e.g., existing signatures may need to be preserved or stripped) or which the * engine will overwrite as part of signing. The engine thus offers {@link #inputJarEntry(String)} * which tells the client whether the input JAR entry needs to be output. This avoids the need for * the client to hard-code the aspects of APK signing which determine which parts of input must be * ignored. Similarly, the engine offers {@link #inputApkSigningBlock(DataSource)} to help the * client avoid dealing with preserving or stripping APK Signature Scheme v2 signature of the input * APK. * *

To use the engine to sign an input APK (or a collection of JAR entries), follow these * steps: *

    *
  1. Obtain a new instance of the engine -- engine instances are stateful and thus cannot be used * for signing multiple APKs.
  2. *
  3. Locate the input APK's APK Signing Block and provide it to * {@link #inputApkSigningBlock(DataSource)}.
  4. *
  5. For each JAR entry in the input APK, invoke {@link #inputJarEntry(String)} to determine * whether this entry should be output. The engine may request to inspect the entry.
  6. *
  7. For each output JAR entry, invoke {@link #outputJarEntry(String)} which may request to * inspect the entry.
  8. *
  9. Once all JAR entries have been output, invoke {@link #outputJarEntries()} which may request * that additional JAR entries are output. These entries comprise the output APK's JAR * signature.
  10. *
  11. Locate the ZIP Central Directory and ZIP End of Central Directory sections in the output and * invoke {@link #outputZipSections2(DataSource, DataSource, DataSource)} which may request that * an APK Signature Block is inserted before the ZIP Central Directory. The block contains the * output APK's APK Signature Scheme v2 signature.
  12. *
  13. Invoke {@link #outputDone()} to signal that the APK was output in full. The engine will * confirm that the output APK is signed.
  14. *
  15. Invoke {@link #close()} to signal that the engine will no longer be used. This lets the * engine free any resources it no longer needs. *
* *

Some invocations of the engine may provide the client with a task to perform. The client is * expected to perform all requested tasks before proceeding to the next stage of signing. See * documentation of each method about the deadlines for performing the tasks requested by the * method. * *

Incremental Operation

* * The engine supports incremental operation where a signed APK is produced, then modified and * re-signed. This may be useful for IDEs, where an app is frequently re-signed after small changes * by the developer. Re-signing may be more efficient than signing from scratch. * *

To use the engine in incremental mode, keep notifying the engine of changes to the APK through * {@link #inputApkSigningBlock(DataSource)}, {@link #inputJarEntry(String)}, * {@link #inputJarEntryRemoved(String)}, {@link #outputJarEntry(String)}, * and {@link #outputJarEntryRemoved(String)}, perform the tasks requested by the engine through * these methods, and, when a new signed APK is desired, run through steps 5 onwards to re-sign the * APK. * *

Output-only Operation

* * The engine's abstract operating model consists of an input APK and an output APK. However, it is * possible to use the engine in output-only mode where the engine's {@code input...} methods are * not invoked. In this mode, the engine has less control over output because it cannot request that * some JAR entries are not output. Nevertheless, the engine will attempt to make the output APK * signed and will report an error if cannot do so. * * @see Application Signing */ public interface ApkSignerEngine extends Closeable { default void setExecutor(RunnablesExecutor executor) { throw new UnsupportedOperationException("setExecutor method is not implemented"); } /** * Initializes the signer engine with the data already present in the apk (if any). There * might already be data that can be reused if the entries has not been changed. * * @param manifestBytes * @param entryNames * @return set of entry names which were processed by the engine during the initialization, a * subset of entryNames */ default Set initWith(byte[] manifestBytes, Set entryNames) { throw new UnsupportedOperationException("initWith method is not implemented"); } /** * Indicates to this engine that the input APK contains the provided APK Signing Block. The * block may contain signatures of the input APK, such as APK Signature Scheme v2 signatures. * * @param apkSigningBlock APK signing block of the input APK. The provided data source is * guaranteed to not be used by the engine after this method terminates. * * @throws IOException if an I/O error occurs while reading the APK Signing Block * @throws ApkFormatException if the APK Signing Block is malformed * @throws IllegalStateException if this engine is closed */ void inputApkSigningBlock(DataSource apkSigningBlock) throws IOException, ApkFormatException, IllegalStateException; /** * Indicates to this engine that the specified JAR entry was encountered in the input APK. * *

When an input entry is updated/changed, it's OK to not invoke * {@link #inputJarEntryRemoved(String)} before invoking this method. * * @return instructions about how to proceed with this entry * * @throws IllegalStateException if this engine is closed */ InputJarEntryInstructions inputJarEntry(String entryName) throws IllegalStateException; /** * Indicates to this engine that the specified JAR entry was output. * *

It is unnecessary to invoke this method for entries added to output by this engine (e.g., * requested by {@link #outputJarEntries()}) provided the entries were output with exactly the * data requested by the engine. * *

When an already output entry is updated/changed, it's OK to not invoke * {@link #outputJarEntryRemoved(String)} before invoking this method. * * @return request to inspect the entry or {@code null} if the engine does not need to inspect * the entry. The request must be fulfilled before {@link #outputJarEntries()} is * invoked. * * @throws IllegalStateException if this engine is closed */ InspectJarEntryRequest outputJarEntry(String entryName) throws IllegalStateException; /** * Indicates to this engine that the specified JAR entry was removed from the input. It's safe * to invoke this for entries for which {@link #inputJarEntry(String)} hasn't been invoked. * * @return output policy of this JAR entry. The policy indicates how this input entry affects * the output APK. The client of this engine should use this information to determine * how the removal of this input APK's JAR entry affects the output APK. * * @throws IllegalStateException if this engine is closed */ InputJarEntryInstructions.OutputPolicy inputJarEntryRemoved(String entryName) throws IllegalStateException; /** * Indicates to this engine that the specified JAR entry was removed from the output. It's safe * to invoke this for entries for which {@link #outputJarEntry(String)} hasn't been invoked. * * @throws IllegalStateException if this engine is closed */ void outputJarEntryRemoved(String entryName) throws IllegalStateException; /** * Indicates to this engine that all JAR entries have been output. * * @return request to add JAR signature to the output or {@code null} if there is no need to add * a JAR signature. The request will contain additional JAR entries to be output. The * request must be fulfilled before * {@link #outputZipSections2(DataSource, DataSource, DataSource)} is invoked. * * @throws ApkFormatException if the APK is malformed in a way which is preventing this engine * from producing a valid signature. For example, if the engine uses the provided * {@code META-INF/MANIFEST.MF} as a template and the file is malformed. * @throws NoSuchAlgorithmException if a signature could not be generated because a required * cryptographic algorithm implementation is missing * @throws InvalidKeyException if a signature could not be generated because a signing key is * not suitable for generating the signature * @throws SignatureException if an error occurred while generating a signature * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR * entries, or if the engine is closed */ OutputJarSignatureRequest outputJarEntries() throws ApkFormatException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, IllegalStateException; /** * Indicates to this engine that the ZIP sections comprising the output APK have been output. * *

The provided data sources are guaranteed to not be used by the engine after this method * terminates. * * @deprecated This is now superseded by {@link #outputZipSections2(DataSource, DataSource, * DataSource)}. * * @param zipEntries the section of ZIP archive containing Local File Header records and data of * the ZIP entries. In a well-formed archive, this section starts at the start of the * archive and extends all the way to the ZIP Central Directory. * @param zipCentralDirectory ZIP Central Directory section * @param zipEocd ZIP End of Central Directory (EoCD) record * * @return request to add an APK Signing Block to the output or {@code null} if the output must * not contain an APK Signing Block. The request must be fulfilled before * {@link #outputDone()} is invoked. * * @throws IOException if an I/O error occurs while reading the provided ZIP sections * @throws ApkFormatException if the provided APK is malformed in a way which prevents this * engine from producing a valid signature. For example, if the APK Signing Block * provided to the engine is malformed. * @throws NoSuchAlgorithmException if a signature could not be generated because a required * cryptographic algorithm implementation is missing * @throws InvalidKeyException if a signature could not be generated because a signing key is * not suitable for generating the signature * @throws SignatureException if an error occurred while generating a signature * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR * entries or to output JAR signature, or if the engine is closed */ @Deprecated OutputApkSigningBlockRequest outputZipSections( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd) throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, IllegalStateException; /** * Indicates to this engine that the ZIP sections comprising the output APK have been output. * *

The provided data sources are guaranteed to not be used by the engine after this method * terminates. * * @param zipEntries the section of ZIP archive containing Local File Header records and data of * the ZIP entries. In a well-formed archive, this section starts at the start of the * archive and extends all the way to the ZIP Central Directory. * @param zipCentralDirectory ZIP Central Directory section * @param zipEocd ZIP End of Central Directory (EoCD) record * * @return request to add an APK Signing Block to the output or {@code null} if the output must * not contain an APK Signing Block. The request must be fulfilled before * {@link #outputDone()} is invoked. * * @throws IOException if an I/O error occurs while reading the provided ZIP sections * @throws ApkFormatException if the provided APK is malformed in a way which prevents this * engine from producing a valid signature. For example, if the APK Signing Block * provided to the engine is malformed. * @throws NoSuchAlgorithmException if a signature could not be generated because a required * cryptographic algorithm implementation is missing * @throws InvalidKeyException if a signature could not be generated because a signing key is * not suitable for generating the signature * @throws SignatureException if an error occurred while generating a signature * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR * entries or to output JAR signature, or if the engine is closed */ OutputApkSigningBlockRequest2 outputZipSections2( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd) throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, IllegalStateException; /** * Indicates to this engine that the signed APK was output. * *

This does not change the output APK. The method helps the client confirm that the current * output is signed. * * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR * entries or to output signatures, or if the engine is closed */ void outputDone() throws IllegalStateException; /** * Generates a V4 signature proto and write to output file. * * @param data Input data to calculate a verity hash tree and hash root * @param outputFile To store the serialized V4 Signature. * @param ignoreFailures Whether any failures will be silently ignored. * @throws InvalidKeyException if a signature could not be generated because a signing key is * not suitable for generating the signature * @throws NoSuchAlgorithmException if a signature could not be generated because a required * cryptographic algorithm implementation is missing * @throws SignatureException if an error occurred while generating a signature * @throws IOException if protobuf fails to be serialized and written to file */ void signV4(DataSource data, File outputFile, boolean ignoreFailures) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException; /** * Checks if the signing configuration provided to the engine is capable of creating a * SourceStamp. */ default boolean isEligibleForSourceStamp() { return false; } /** Generates the digest of the certificate used to sign the source stamp. */ default byte[] generateSourceStampCertificateDigest() throws SignatureException { return new byte[0]; } /** * Indicates to this engine that it will no longer be used. Invoking this on an already closed * engine is OK. * *

This does not change the output APK. For example, if the output APK is not yet fully * signed, it will remain so after this method terminates. */ @Override void close(); /** * Instructions about how to handle an input APK's JAR entry. * *

The instructions indicate whether to output the entry (see {@link #getOutputPolicy()}) and * may contain a request to inspect the entry (see {@link #getInspectJarEntryRequest()}), in * which case the request must be fulfilled before {@link ApkSignerEngine#outputJarEntries()} is * invoked. */ public static class InputJarEntryInstructions { private final OutputPolicy mOutputPolicy; private final InspectJarEntryRequest mInspectJarEntryRequest; /** * Constructs a new {@code InputJarEntryInstructions} instance with the provided entry * output policy and without a request to inspect the entry. */ public InputJarEntryInstructions(OutputPolicy outputPolicy) { this(outputPolicy, null); } /** * Constructs a new {@code InputJarEntryInstructions} instance with the provided entry * output mode and with the provided request to inspect the entry. * * @param inspectJarEntryRequest request to inspect the entry or {@code null} if there's no * need to inspect the entry. */ public InputJarEntryInstructions( OutputPolicy outputPolicy, InspectJarEntryRequest inspectJarEntryRequest) { mOutputPolicy = outputPolicy; mInspectJarEntryRequest = inspectJarEntryRequest; } /** * Returns the output policy for this entry. */ public OutputPolicy getOutputPolicy() { return mOutputPolicy; } /** * Returns the request to inspect the JAR entry or {@code null} if there is no need to * inspect the entry. */ public InspectJarEntryRequest getInspectJarEntryRequest() { return mInspectJarEntryRequest; } /** * Output policy for an input APK's JAR entry. */ public static enum OutputPolicy { /** Entry must not be output. */ SKIP, /** Entry should be output. */ OUTPUT, /** Entry will be output by the engine. The client can thus ignore this input entry. */ OUTPUT_BY_ENGINE, } } /** * Request to inspect the specified JAR entry. * *

The entry's uncompressed data must be provided to the data sink returned by * {@link #getDataSink()}. Once the entry's data has been provided to the sink, {@link #done()} * must be invoked. */ interface InspectJarEntryRequest { /** * Returns the data sink into which the entry's uncompressed data should be sent. */ DataSink getDataSink(); /** * Indicates that entry's data has been provided in full. */ void done(); /** * Returns the name of the JAR entry. */ String getEntryName(); } /** * Request to add JAR signature (aka v1 signature) to the output APK. * *

Entries listed in {@link #getAdditionalJarEntries()} must be added to the output APK after * which {@link #done()} must be invoked. */ interface OutputJarSignatureRequest { /** * Returns JAR entries that must be added to the output APK. */ List getAdditionalJarEntries(); /** * Indicates that the JAR entries contained in this request were added to the output APK. */ void done(); /** * JAR entry. */ public static class JarEntry { private final String mName; private final byte[] mData; /** * Constructs a new {@code JarEntry} with the provided name and data. * * @param data uncompressed data of the entry. Changes to this array will not be * reflected in {@link #getData()}. */ public JarEntry(String name, byte[] data) { mName = name; mData = data.clone(); } /** * Returns the name of this ZIP entry. */ public String getName() { return mName; } /** * Returns the uncompressed data of this JAR entry. */ public byte[] getData() { return mData.clone(); } } } /** * Request to add the specified APK Signing Block to the output APK. APK Signature Scheme v2 * signature(s) of the APK are contained in this block. * *

The APK Signing Block returned by {@link #getApkSigningBlock()} must be placed into the * output APK such that the block is immediately before the ZIP Central Directory, the offset of * ZIP Central Directory in the ZIP End of Central Directory record must be adjusted * accordingly, and then {@link #done()} must be invoked. * *

If the output contains an APK Signing Block, that block must be replaced by the block * contained in this request. * * @deprecated This is now superseded by {@link OutputApkSigningBlockRequest2}. */ @Deprecated interface OutputApkSigningBlockRequest { /** * Returns the APK Signing Block. */ byte[] getApkSigningBlock(); /** * Indicates that the APK Signing Block was output as requested. */ void done(); } /** * Request to add the specified APK Signing Block to the output APK. APK Signature Scheme v2 * signature(s) of the APK are contained in this block. * *

The APK Signing Block returned by {@link #getApkSigningBlock()} must be placed into the * output APK such that the block is immediately before the ZIP Central Directory. Immediately * before the APK Signing Block must be padding consists of the number of 0x00 bytes returned by * {@link #getPaddingSizeBeforeApkSigningBlock()}. The offset of ZIP Central Directory in the * ZIP End of Central Directory record must be adjusted accordingly, and then {@link #done()} * must be invoked. * *

If the output contains an APK Signing Block, that block must be replaced by the block * contained in this request. */ interface OutputApkSigningBlockRequest2 { /** * Returns the APK Signing Block. */ byte[] getApkSigningBlock(); /** * Indicates that the APK Signing Block was output as requested. */ void done(); /** * Returns the number of 0x00 bytes the caller must place immediately before APK Signing * Block. */ int getPaddingSizeBeforeApkSigningBlock(); } } ./PaxHeaders.X/src_main_java_com_android_apksig_ApkVerificationIssue.java0100644 0000000 0000000 00000000034 14763776540 026026 xustar000000000 0000000 28 mtime=1741684064.5410000 src/main/java/com/android/apksig/ApkVerificationIssue.java0100644 0000000 0000000 00000017267 14763776540 022707 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; /** * This class is intended as a lightweight representation of an APK signature verification issue * where the client does not require the additional textual details provided by a subclass. */ public class ApkVerificationIssue { /* The V2 signer(s) could not be read from the V2 signature block */ public static final int V2_SIG_MALFORMED_SIGNERS = 1; /* A V2 signature block exists without any V2 signers */ public static final int V2_SIG_NO_SIGNERS = 2; /* Failed to parse a signer's block in the V2 signature block */ public static final int V2_SIG_MALFORMED_SIGNER = 3; /* Failed to parse the signer's signature record in the V2 signature block */ public static final int V2_SIG_MALFORMED_SIGNATURE = 4; /* The V2 signer contained no signatures */ public static final int V2_SIG_NO_SIGNATURES = 5; /* The V2 signer's certificate could not be parsed */ public static final int V2_SIG_MALFORMED_CERTIFICATE = 6; /* No signing certificates exist for the V2 signer */ public static final int V2_SIG_NO_CERTIFICATES = 7; /* Failed to parse the V2 signer's digest record */ public static final int V2_SIG_MALFORMED_DIGEST = 8; /* The V3 signer(s) could not be read from the V3 signature block */ public static final int V3_SIG_MALFORMED_SIGNERS = 9; /* A V3 signature block exists without any V3 signers */ public static final int V3_SIG_NO_SIGNERS = 10; /* Failed to parse a signer's block in the V3 signature block */ public static final int V3_SIG_MALFORMED_SIGNER = 11; /* Failed to parse the signer's signature record in the V3 signature block */ public static final int V3_SIG_MALFORMED_SIGNATURE = 12; /* The V3 signer contained no signatures */ public static final int V3_SIG_NO_SIGNATURES = 13; /* The V3 signer's certificate could not be parsed */ public static final int V3_SIG_MALFORMED_CERTIFICATE = 14; /* No signing certificates exist for the V3 signer */ public static final int V3_SIG_NO_CERTIFICATES = 15; /* Failed to parse the V3 signer's digest record */ public static final int V3_SIG_MALFORMED_DIGEST = 16; /* The source stamp signer contained no signatures */ public static final int SOURCE_STAMP_NO_SIGNATURE = 17; /* The source stamp signer's certificate could not be parsed */ public static final int SOURCE_STAMP_MALFORMED_CERTIFICATE = 18; /* The source stamp contains a signature produced using an unknown algorithm */ public static final int SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM = 19; /* Failed to parse the signer's signature in the source stamp signature block */ public static final int SOURCE_STAMP_MALFORMED_SIGNATURE = 20; /* The source stamp's signature block failed verification */ public static final int SOURCE_STAMP_DID_NOT_VERIFY = 21; /* An exception was encountered when verifying the source stamp */ public static final int SOURCE_STAMP_VERIFY_EXCEPTION = 22; /* The certificate digest in the APK does not match the expected digest */ public static final int SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH = 23; /* * The APK contains a source stamp signature block without a corresponding stamp certificate * digest in the APK contents. */ public static final int SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST = 24; /* * The APK does not contain the source stamp certificate digest file nor the source stamp * signature block. */ public static final int SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING = 25; /* * None of the signatures provided by the source stamp were produced with a known signature * algorithm. */ public static final int SOURCE_STAMP_NO_SUPPORTED_SIGNATURE = 26; /* * The source stamp signer's certificate in the signing block does not match the certificate in * the APK. */ public static final int SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK = 27; /* The APK could not be properly parsed due to a ZIP or APK format exception */ public static final int MALFORMED_APK = 28; /* An unexpected exception was caught when attempting to verify the APK's signatures */ public static final int UNEXPECTED_EXCEPTION = 29; /* The APK contains the certificate digest file but does not contain a stamp signature block */ public static final int SOURCE_STAMP_SIG_MISSING = 30; /* Source stamp block contains a malformed attribute. */ public static final int SOURCE_STAMP_MALFORMED_ATTRIBUTE = 31; /* Source stamp block contains an unknown attribute. */ public static final int SOURCE_STAMP_UNKNOWN_ATTRIBUTE = 32; /** * Failed to parse the SigningCertificateLineage structure in the source stamp * attributes section. */ public static final int SOURCE_STAMP_MALFORMED_LINEAGE = 33; /** * The source stamp certificate does not match the terminal node in the provided * proof-of-rotation structure describing the stamp certificate history. */ public static final int SOURCE_STAMP_POR_CERT_MISMATCH = 34; /** * The source stamp SigningCertificateLineage attribute contains a proof-of-rotation record * with signature(s) that did not verify. */ public static final int SOURCE_STAMP_POR_DID_NOT_VERIFY = 35; /** No V1 / jar signing signature blocks were found in the APK. */ public static final int JAR_SIG_NO_SIGNATURES = 36; /** An exception was encountered when parsing the V1 / jar signer in the signature block. */ public static final int JAR_SIG_PARSE_EXCEPTION = 37; /** The source stamp timestamp attribute has an invalid value. */ public static final int SOURCE_STAMP_INVALID_TIMESTAMP = 38; private final int mIssueId; private final String mFormat; private final Object[] mParams; /** * Constructs a new {@code ApkVerificationIssue} using the provided {@code format} string and * {@code params}. */ public ApkVerificationIssue(String format, Object... params) { mIssueId = -1; mFormat = format; mParams = params; } /** * Constructs a new {@code ApkVerificationIssue} using the provided {@code issueId} and {@code * params}. */ public ApkVerificationIssue(int issueId, Object... params) { mIssueId = issueId; mFormat = null; mParams = params; } /** * Returns the numeric ID for this issue. */ public int getIssueId() { return mIssueId; } /** * Returns the optional parameters for this issue. */ public Object[] getParams() { return mParams; } @Override public String toString() { // If this instance was created by a subclass with a format string then return the same // formatted String as the subclass. if (mFormat != null) { return String.format(mFormat, mParams); } StringBuilder result = new StringBuilder("mIssueId: ").append(mIssueId); for (Object param : mParams) { result.append(", ").append(param.toString()); } return result.toString(); } } ./PaxHeaders.X/src_main_java_com_android_apksig_ApkVerifier.java0100644 0000000 0000000 00000000034 14763776540 024146 xustar000000000 0000000 28 mtime=1741684064.5420000 src/main/java/com/android/apksig/ApkVerifier.java0100644 0000000 0000000 00000472165 14763776540 021031 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; import static com.android.apksig.apk.ApkUtils.computeSha256DigestBytes; import static com.android.apksig.apk.ApkUtils.getTargetSandboxVersionFromBinaryAndroidManifest; import static com.android.apksig.apk.ApkUtils.getTargetSdkVersionFromBinaryAndroidManifest; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_SOURCE_STAMP; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.toHex; import static com.android.apksig.internal.apk.v1.V1SchemeConstants.MANIFEST_ENTRY_NAME; import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT; import com.android.apksig.ApkVerifier.Result.V2SchemeSignerInfo; import com.android.apksig.ApkVerifier.Result.V3SchemeSignerInfo; import com.android.apksig.SigningCertificateLineage.SignerConfig; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigResult; import com.android.apksig.internal.apk.ApkSignerInfo; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils.Result.SignerInfo.ContentDigest; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.apk.SignatureNotFoundException; import com.android.apksig.internal.apk.stamp.SourceStampConstants; import com.android.apksig.internal.apk.stamp.V2SourceStampVerifier; import com.android.apksig.internal.apk.v1.V1SchemeVerifier; import com.android.apksig.internal.apk.v2.V2SchemeConstants; import com.android.apksig.internal.apk.v2.V2SchemeVerifier; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeVerifier; import com.android.apksig.internal.apk.v4.V4SchemeVerifier; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.zip.CentralDirectoryRecord; import com.android.apksig.internal.zip.LocalFileRecord; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import com.android.apksig.util.RunnablesExecutor; import com.android.apksig.zip.ZipFormatException; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * APK signature verifier which mimics the behavior of the Android platform. * *

The verifier is designed to closely mimic the behavior of Android platforms. This is to enable * the verifier to be used for checking whether an APK's signatures are expected to verify on * Android. * *

Use {@link Builder} to obtain instances of this verifier. * * @see Application Signing */ public class ApkVerifier { private static final Set LINEAGE_RELATED_ISSUES = new HashSet<>(Arrays.asList( Issue.V3_SIG_MALFORMED_LINEAGE, Issue.V3_INCONSISTENT_LINEAGES, Issue.V3_SIG_POR_DID_NOT_VERIFY, Issue.V3_SIG_POR_CERT_MISMATCH)); private static final Map SUPPORTED_APK_SIG_SCHEME_NAMES = loadSupportedApkSigSchemeNames(); private static Map loadSupportedApkSigSchemeNames() { Map supportedMap = new HashMap<>(2); supportedMap.put( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2, "APK Signature Scheme v2"); supportedMap.put( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3, "APK Signature Scheme v3"); return supportedMap; } private final File mApkFile; private final DataSource mApkDataSource; private final File mV4SignatureFile; private final Integer mMinSdkVersion; private final int mMaxSdkVersion; private ApkVerifier( File apkFile, DataSource apkDataSource, File v4SignatureFile, Integer minSdkVersion, int maxSdkVersion) { mApkFile = apkFile; mApkDataSource = apkDataSource; mV4SignatureFile = v4SignatureFile; mMinSdkVersion = minSdkVersion; mMaxSdkVersion = maxSdkVersion; } /** * Verifies the APK's signatures and returns the result of verification. The APK can be * considered verified iff the result's {@link Result#isVerified()} returns {@code true}. * The verification result also includes errors, warnings, and information about signers such * as their signing certificates. * *

Verification succeeds iff the APK's signature is expected to verify on all Android * platform versions specified via the {@link Builder}. If the APK's signature is expected to * not verify on any of the specified platform versions, this method returns a result with one * or more errors and whose {@link Result#isVerified()} returns {@code false}, or this method * throws an exception. * * @throws IOException if an I/O error is encountered while reading the APK * @throws ApkFormatException if the APK is malformed * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a * required cryptographic algorithm implementation is missing * @throws IllegalStateException if this verifier's configuration is missing required * information. */ public Result verify() throws IOException, ApkFormatException, NoSuchAlgorithmException, IllegalStateException { Closeable in = null; try { DataSource apk; if (mApkDataSource != null) { apk = mApkDataSource; } else if (mApkFile != null) { RandomAccessFile f = new RandomAccessFile(mApkFile, "r"); in = f; apk = DataSources.asDataSource(f, 0, f.length()); } else { throw new IllegalStateException("APK not provided"); } return verify(apk); } finally { if (in != null) { in.close(); } } } /** * Verifies the APK's signatures and returns the result of verification. The APK can be * considered verified iff the result's {@link Result#isVerified()} returns {@code true}. * The verification result also includes errors, warnings, and information about signers. * * @param apk APK file contents * @throws IOException if an I/O error is encountered while reading the APK * @throws ApkFormatException if the APK is malformed * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a * required cryptographic algorithm implementation is missing */ private Result verify(DataSource apk) throws IOException, ApkFormatException, NoSuchAlgorithmException { int maxSdkVersion = mMaxSdkVersion; ApkUtils.ZipSections zipSections; try { zipSections = ApkUtils.findZipSections(apk); } catch (ZipFormatException e) { throw new ApkFormatException("Malformed APK: not a ZIP archive", e); } ByteBuffer androidManifest = null; int minSdkVersion = verifyAndGetMinSdkVersion(apk, zipSections); Result result = new Result(); Map> signatureSchemeApkContentDigests = new HashMap<>(); // The SUPPORTED_APK_SIG_SCHEME_NAMES contains the mapping from version number to scheme // name, but the verifiers use this parameter as the schemes supported by the target SDK // range. Since the code below skips signature verification based on max SDK the mapping of // supported schemes needs to be modified to ensure the verifiers do not report a stripped // signature for an SDK range that does not support that signature version. For instance an // APK with V1, V2, and V3 signatures and a max SDK of O would skip the V3 signature // verification, but the SUPPORTED_APK_SIG_SCHEME_NAMES contains version 3, so when the V2 // verification is performed it would see the stripping protection attribute, see that V3 // is in the list of supported signatures, and report a stripped signature. Map supportedSchemeNames = getSupportedSchemeNames(maxSdkVersion); // Android N and newer attempts to verify APKs using the APK Signing Block, which can // include v2 and/or v3 signatures. If none is found, it falls back to JAR signature // verification. If the signature is found but does not verify, the APK is rejected. Set foundApkSigSchemeIds = new HashSet<>(2); if (maxSdkVersion >= AndroidSdkVersion.N) { RunnablesExecutor executor = RunnablesExecutor.SINGLE_THREADED; // Android T and newer attempts to verify APKs using APK Signature Scheme V3.1. v3.0 // also includes stripping protection for the minimum SDK version on which the rotated // signing key should be used. int rotationMinSdkVersion = 0; if (maxSdkVersion >= MIN_SDK_WITH_V31_SUPPORT) { try { ApkSigningBlockUtils.Result v31Result = new V3SchemeVerifier.Builder(apk, zipSections, Math.max(minSdkVersion, MIN_SDK_WITH_V31_SUPPORT), maxSdkVersion) .setRunnablesExecutor(executor) .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID) .build() .verify(); foundApkSigSchemeIds.add(VERSION_APK_SIGNATURE_SCHEME_V31); rotationMinSdkVersion = v31Result.signers.stream().mapToInt( signer -> signer.minSdkVersion).min().orElse(0); result.mergeFrom(v31Result); signatureSchemeApkContentDigests.put( VERSION_APK_SIGNATURE_SCHEME_V31, getApkContentDigestsFromSigningSchemeResult(v31Result)); } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { // v3.1 signature not required } if (result.containsErrors()) { return result; } } // Android P and newer attempts to verify APKs using APK Signature Scheme v3; since a // V3.1 block should only be written with a V3.0 block, always perform the V3.0 check // if the minSdkVersion supports V3.0. if (maxSdkVersion >= AndroidSdkVersion.P) { try { V3SchemeVerifier.Builder builder = new V3SchemeVerifier.Builder(apk, zipSections, Math.max(minSdkVersion, AndroidSdkVersion.P), maxSdkVersion) .setRunnablesExecutor(executor) .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID); if (rotationMinSdkVersion > 0) { builder.setRotationMinSdkVersion(rotationMinSdkVersion); } ApkSigningBlockUtils.Result v3Result = builder.build().verify(); foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); result.mergeFrom(v3Result); signatureSchemeApkContentDigests.put( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3, getApkContentDigestsFromSigningSchemeResult(v3Result)); } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { // v3 signature not required unless a v3.1 signature was found as a v3.1 // signature is intended to support key rotation on T+ with the v3 signature // containing the original signing key. if (foundApkSigSchemeIds.contains( VERSION_APK_SIGNATURE_SCHEME_V31)) { result.addError(Issue.V31_BLOCK_FOUND_WITHOUT_V3_BLOCK); } } if (result.containsErrors()) { return result; } } // Attempt to verify the APK using v2 signing if necessary. Platforms prior to Android P // ignore APK Signature Scheme v3 signatures and always attempt to verify either JAR or // APK Signature Scheme v2 signatures. Android P onwards verifies v2 signatures only if // no APK Signature Scheme v3 (or newer scheme) signatures were found. if (minSdkVersion < AndroidSdkVersion.P || foundApkSigSchemeIds.isEmpty()) { try { ApkSigningBlockUtils.Result v2Result = V2SchemeVerifier.verify( executor, apk, zipSections, supportedSchemeNames, foundApkSigSchemeIds, Math.max(minSdkVersion, AndroidSdkVersion.N), maxSdkVersion); foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); result.mergeFrom(v2Result); signatureSchemeApkContentDigests.put( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2, getApkContentDigestsFromSigningSchemeResult(v2Result)); } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { // v2 signature not required } if (result.containsErrors()) { return result; } } // If v4 file is specified, use additional verification on it if (mV4SignatureFile != null) { final ApkSigningBlockUtils.Result v4Result = V4SchemeVerifier.verify(apk, mV4SignatureFile); foundApkSigSchemeIds.add( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4); result.mergeFrom(v4Result); if (result.containsErrors()) { return result; } } } // Android O and newer requires that APKs targeting security sandbox version 2 and higher // are signed using APK Signature Scheme v2 or newer. if (maxSdkVersion >= AndroidSdkVersion.O) { if (androidManifest == null) { androidManifest = getAndroidManifestFromApk(apk, zipSections); } int targetSandboxVersion = getTargetSandboxVersionFromBinaryAndroidManifest(androidManifest.slice()); if (targetSandboxVersion > 1) { if (foundApkSigSchemeIds.isEmpty()) { result.addError( Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION, targetSandboxVersion); } } } List cdRecords = V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections); // Attempt to verify the APK using JAR signing if necessary. Platforms prior to Android N // ignore APK Signature Scheme v2 signatures and always attempt to verify JAR signatures. // Android N onwards verifies JAR signatures only if no APK Signature Scheme v2 (or newer // scheme) signatures were found. if ((minSdkVersion < AndroidSdkVersion.N) || (foundApkSigSchemeIds.isEmpty())) { V1SchemeVerifier.Result v1Result = V1SchemeVerifier.verify( apk, zipSections, supportedSchemeNames, foundApkSigSchemeIds, minSdkVersion, maxSdkVersion); result.mergeFrom(v1Result); signatureSchemeApkContentDigests.put( ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME, getApkContentDigestFromV1SigningScheme(cdRecords, apk, zipSections)); } if (result.containsErrors()) { return result; } // Verify the SourceStamp, if found in the APK. try { CentralDirectoryRecord sourceStampCdRecord = null; for (CentralDirectoryRecord cdRecord : cdRecords) { if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals( cdRecord.getName())) { sourceStampCdRecord = cdRecord; break; } } // If SourceStamp file is found inside the APK, there must be a SourceStamp // block in the APK signing block as well. if (sourceStampCdRecord != null) { byte[] sourceStampCertificateDigest = LocalFileRecord.getUncompressedData( apk, sourceStampCdRecord, zipSections.getZipCentralDirectoryOffset()); ApkSigResult sourceStampResult = V2SourceStampVerifier.verify( apk, zipSections, sourceStampCertificateDigest, signatureSchemeApkContentDigests, Math.max(minSdkVersion, AndroidSdkVersion.R), maxSdkVersion); result.mergeFrom(sourceStampResult); } } catch (SignatureNotFoundException ignored) { result.addWarning(Issue.SOURCE_STAMP_SIG_MISSING); } catch (ZipFormatException e) { throw new ApkFormatException("Failed to read APK", e); } if (result.containsErrors()) { return result; } // Check whether v1 and v2 scheme signer identifies match, provided both v1 and v2 // signatures verified. if ((result.isVerifiedUsingV1Scheme()) && (result.isVerifiedUsingV2Scheme())) { ArrayList v1Signers = new ArrayList<>(result.getV1SchemeSigners()); ArrayList v2Signers = new ArrayList<>(result.getV2SchemeSigners()); ArrayList v1SignerCerts = new ArrayList<>(); ArrayList v2SignerCerts = new ArrayList<>(); for (Result.V1SchemeSignerInfo signer : v1Signers) { try { v1SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded())); } catch (CertificateEncodingException e) { throw new IllegalStateException( "Failed to encode JAR signer " + signer.getName() + " certs", e); } } for (Result.V2SchemeSignerInfo signer : v2Signers) { try { v2SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded())); } catch (CertificateEncodingException e) { throw new IllegalStateException( "Failed to encode APK Signature Scheme v2 signer (index: " + signer.getIndex() + ") certs", e); } } for (int i = 0; i < v1SignerCerts.size(); i++) { ByteArray v1Cert = v1SignerCerts.get(i); if (!v2SignerCerts.contains(v1Cert)) { Result.V1SchemeSignerInfo v1Signer = v1Signers.get(i); v1Signer.addError(Issue.V2_SIG_MISSING); break; } } for (int i = 0; i < v2SignerCerts.size(); i++) { ByteArray v2Cert = v2SignerCerts.get(i); if (!v1SignerCerts.contains(v2Cert)) { Result.V2SchemeSignerInfo v2Signer = v2Signers.get(i); v2Signer.addError(Issue.JAR_SIG_MISSING); break; } } } // If there is a v3 scheme signer and an earlier scheme signer, make sure that there is a // match, or in the event of signing certificate rotation, that the v1/v2 scheme signer // matches the oldest signing certificate in the provided SigningCertificateLineage if (result.isVerifiedUsingV3Scheme() && (result.isVerifiedUsingV1Scheme() || result.isVerifiedUsingV2Scheme())) { SigningCertificateLineage lineage = result.getSigningCertificateLineage(); X509Certificate oldSignerCert; if (result.isVerifiedUsingV1Scheme()) { List v1Signers = result.getV1SchemeSigners(); if (v1Signers.size() != 1) { // APK Signature Scheme v3 only supports single-signers, error to sign with // multiple and then only one result.addError(Issue.V3_SIG_MULTIPLE_PAST_SIGNERS); } oldSignerCert = v1Signers.get(0).mCertChain.get(0); } else { List v2Signers = result.getV2SchemeSigners(); if (v2Signers.size() != 1) { // APK Signature Scheme v3 only supports single-signers, error to sign with // multiple and then only one result.addError(Issue.V3_SIG_MULTIPLE_PAST_SIGNERS); } oldSignerCert = v2Signers.get(0).mCerts.get(0); } if (lineage == null) { // no signing certificate history with which to contend, just make sure that v3 // matches previous versions List v3Signers = result.getV3SchemeSigners(); if (v3Signers.size() != 1) { // multiple v3 signers should never exist without rotation history, since // multiple signers implies a different signer for different platform versions result.addError(Issue.V3_SIG_MULTIPLE_SIGNERS); } try { if (!Arrays.equals(oldSignerCert.getEncoded(), v3Signers.get(0).mCerts.get(0).getEncoded())) { result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH); } } catch (CertificateEncodingException e) { // we just go the encoding for the v1/v2 certs above, so must be v3 throw new RuntimeException( "Failed to encode APK Signature Scheme v3 signer cert", e); } } else { // we have some signing history, make sure that the root of the history is the same // as our v1/v2 signer try { lineage = lineage.getSubLineage(oldSignerCert); if (lineage.size() != 1) { // the v1/v2 signer was found, but not at the root of the lineage result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH); } } catch (IllegalArgumentException e) { // the v1/v2 signer was not found in the lineage result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH); } } } // If there is a v4 scheme signer, make sure that their certificates match. // The apkDigest field in the v4 signature should match the selected v2/v3. if (result.isVerifiedUsingV4Scheme()) { List v4Signers = result.getV4SchemeSigners(); List digestsFromV4 = v4Signers.get(0).getContentDigests(); if (digestsFromV4.size() != 1) { result.addError(Issue.V4_SIG_UNEXPECTED_DIGESTS, digestsFromV4.size()); if (digestsFromV4.isEmpty()) { return result; } } final byte[] digestFromV4 = digestsFromV4.get(0).getValue(); if (result.isVerifiedUsingV3Scheme()) { final boolean isV31 = result.isVerifiedUsingV31Scheme(); final int expectedSize = isV31 ? 2 : 1; if (v4Signers.size() != expectedSize) { result.addError(isV31 ? Issue.V41_SIG_NEEDS_TWO_SIGNERS : Issue.V4_SIG_MULTIPLE_SIGNERS); return result; } checkV4Signer(result.getV3SchemeSigners(), v4Signers.get(0).mCerts, digestFromV4, result); if (isV31) { List digestsFromV41 = v4Signers.get(1).getContentDigests(); if (digestsFromV41.size() != 1) { result.addError(Issue.V4_SIG_UNEXPECTED_DIGESTS, digestsFromV41.size()); if (digestsFromV41.isEmpty()) { return result; } } final byte[] digestFromV41 = digestsFromV41.get(0).getValue(); checkV4Signer(result.getV31SchemeSigners(), v4Signers.get(1).mCerts, digestFromV41, result); } } else if (result.isVerifiedUsingV2Scheme()) { if (v4Signers.size() != 1) { result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS); } List v2Signers = result.getV2SchemeSigners(); if (v2Signers.size() != 1) { result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS); } // Compare certificates. checkV4Certificate(v4Signers.get(0).mCerts, v2Signers.get(0).mCerts, result); // Compare digests. final byte[] digestFromV2 = pickBestDigestForV4( v2Signers.get(0).getContentDigests()); if (!Arrays.equals(digestFromV4, digestFromV2)) { result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH, 2, toHex(digestFromV2), toHex(digestFromV4)); } } else { throw new RuntimeException("V4 signature must be also verified with V2/V3"); } } // If the targetSdkVersion has a minimum required signature scheme version then verify // that the APK was signed with at least that version. try { if (androidManifest == null) { androidManifest = getAndroidManifestFromApk(apk, zipSections); } } catch (ApkFormatException e) { // If the manifest is not available then skip the minimum signature scheme requirement // to support bundle verification. } if (androidManifest != null) { int targetSdkVersion = getTargetSdkVersionFromBinaryAndroidManifest( androidManifest.slice()); int minSchemeVersion = getMinimumSignatureSchemeVersionForTargetSdk(targetSdkVersion); // The platform currently only enforces a single minimum signature scheme version, but // when later platform versions support another minimum version this will need to be // expanded to verify the minimum based on the target and maximum SDK version. if (minSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME && maxSdkVersion >= targetSdkVersion) { switch (minSchemeVersion) { case VERSION_APK_SIGNATURE_SCHEME_V2: if (result.isVerifiedUsingV2Scheme()) { break; } // Allow this case to fall through to the next as a signature satisfying a // later scheme version will also satisfy this requirement. case VERSION_APK_SIGNATURE_SCHEME_V3: if (result.isVerifiedUsingV3Scheme() || result.isVerifiedUsingV31Scheme()) { break; } result.addError(Issue.MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET, targetSdkVersion, minSchemeVersion); } } } if (result.containsErrors()) { return result; } // Verified result.setVerified(); if (result.isVerifiedUsingV31Scheme()) { List v31Signers = result.getV31SchemeSigners(); result.addSignerCertificate(v31Signers.get(v31Signers.size() - 1).getCertificate()); } else if (result.isVerifiedUsingV3Scheme()) { List v3Signers = result.getV3SchemeSigners(); result.addSignerCertificate(v3Signers.get(v3Signers.size() - 1).getCertificate()); } else if (result.isVerifiedUsingV2Scheme()) { for (Result.V2SchemeSignerInfo signerInfo : result.getV2SchemeSigners()) { result.addSignerCertificate(signerInfo.getCertificate()); } } else if (result.isVerifiedUsingV1Scheme()) { for (Result.V1SchemeSignerInfo signerInfo : result.getV1SchemeSigners()) { result.addSignerCertificate(signerInfo.getCertificate()); } } else { throw new RuntimeException( "APK verified, but has not verified using any of v1, v2 or v3 schemes"); } return result; } /** * Verifies and returns the minimum SDK version, either as provided to the builder or as read * from the {@code apk}'s AndroidManifest.xml. */ private int verifyAndGetMinSdkVersion(DataSource apk, ApkUtils.ZipSections zipSections) throws ApkFormatException, IOException { if (mMinSdkVersion != null) { if (mMinSdkVersion < 0) { throw new IllegalArgumentException( "minSdkVersion must not be negative: " + mMinSdkVersion); } if ((mMinSdkVersion != null) && (mMinSdkVersion > mMaxSdkVersion)) { throw new IllegalArgumentException( "minSdkVersion (" + mMinSdkVersion + ") > maxSdkVersion (" + mMaxSdkVersion + ")"); } return mMinSdkVersion; } ByteBuffer androidManifest = null; // Need to obtain minSdkVersion from the APK's AndroidManifest.xml if (androidManifest == null) { androidManifest = getAndroidManifestFromApk(apk, zipSections); } int minSdkVersion = ApkUtils.getMinSdkVersionFromBinaryAndroidManifest(androidManifest.slice()); if (minSdkVersion > mMaxSdkVersion) { throw new IllegalArgumentException( "minSdkVersion from APK (" + minSdkVersion + ") > maxSdkVersion (" + mMaxSdkVersion + ")"); } return minSdkVersion; } /** * Returns the mapping of signature scheme version to signature scheme name for all signature * schemes starting from V2 supported by the {@code maxSdkVersion}. */ private static Map getSupportedSchemeNames(int maxSdkVersion) { Map supportedSchemeNames; if (maxSdkVersion >= AndroidSdkVersion.P) { supportedSchemeNames = SUPPORTED_APK_SIG_SCHEME_NAMES; } else if (maxSdkVersion >= AndroidSdkVersion.N) { supportedSchemeNames = new HashMap<>(1); supportedSchemeNames.put(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2, SUPPORTED_APK_SIG_SCHEME_NAMES.get( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2)); } else { supportedSchemeNames = Collections.emptyMap(); } return supportedSchemeNames; } /** * Verifies the APK's source stamp signature and returns the result of the verification. * *

The APK's source stamp can be considered verified if the result's {@link * Result#isVerified} returns {@code true}. The details of the source stamp verification can * be obtained from the result's {@link Result#getSourceStampInfo()}} including the success or * failure cause from {@link Result.SourceStampInfo#getSourceStampVerificationStatus()}. If the * verification fails additional details regarding the failure can be obtained from {@link * Result#getAllErrors()}}. */ public Result verifySourceStamp() { return verifySourceStamp(null); } /** * Verifies the APK's source stamp signature, including verification that the SHA-256 digest of * the stamp signing certificate matches the {@code expectedCertDigest}, and returns the result * of the verification. * *

A value of {@code null} for the {@code expectedCertDigest} will verify the source stamp, * if present, without verifying the actual source stamp certificate used to sign the source * stamp. This can be used to verify an APK contains a properly signed source stamp without * verifying a particular signer. * * @see #verifySourceStamp() */ public Result verifySourceStamp(String expectedCertDigest) { Closeable in = null; try { DataSource apk; if (mApkDataSource != null) { apk = mApkDataSource; } else if (mApkFile != null) { RandomAccessFile f = new RandomAccessFile(mApkFile, "r"); in = f; apk = DataSources.asDataSource(f, 0, f.length()); } else { throw new IllegalStateException("APK not provided"); } return verifySourceStamp(apk, expectedCertDigest); } catch (IOException e) { return createSourceStampResultWithError( Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR, Issue.UNEXPECTED_EXCEPTION, e); } finally { if (in != null) { try { in.close(); } catch (IOException ignored) { } } } } /** * Compares the digests coming from signature blocks. Returns {@code true} if at least one * digest algorithm is present in both digests and actual digests for all common algorithms * are the same. */ public static boolean compareDigests( Map firstDigests, Map secondDigests) throws NoSuchAlgorithmException { Set intersectKeys = new HashSet<>(firstDigests.keySet()); intersectKeys.retainAll(secondDigests.keySet()); if (intersectKeys.isEmpty()) { return false; } for (ContentDigestAlgorithm algorithm : intersectKeys) { if (!Arrays.equals(firstDigests.get(algorithm), secondDigests.get(algorithm))) { return false; } } return true; } /** * Verifies the provided {@code apk}'s source stamp signature, including verification of the * SHA-256 digest of the stamp signing certificate matches the {@code expectedCertDigest}, and * returns the result of the verification. * * @see #verifySourceStamp(String) */ private Result verifySourceStamp(DataSource apk, String expectedCertDigest) { try { ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); int minSdkVersion = verifyAndGetMinSdkVersion(apk, zipSections); // Attempt to obtain the source stamp's certificate digest from the APK. List cdRecords = V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections); CentralDirectoryRecord sourceStampCdRecord = null; for (CentralDirectoryRecord cdRecord : cdRecords) { if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) { sourceStampCdRecord = cdRecord; break; } } // If the source stamp's certificate digest is not available within the APK then the // source stamp cannot be verified; check if a source stamp signing block is in the // APK's signature block to determine the appropriate status to return. if (sourceStampCdRecord == null) { boolean stampSigningBlockFound; try { ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( VERSION_SOURCE_STAMP); ApkSigningBlockUtils.findSignature(apk, zipSections, SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID, result); stampSigningBlockFound = true; } catch (ApkSigningBlockUtils.SignatureNotFoundException e) { stampSigningBlockFound = false; } if (stampSigningBlockFound) { return createSourceStampResultWithError( Result.SourceStampInfo.SourceStampVerificationStatus.STAMP_NOT_VERIFIED, Issue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST); } else { return createSourceStampResultWithError( Result.SourceStampInfo.SourceStampVerificationStatus.STAMP_MISSING, Issue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING); } } // Verify that the contents of the source stamp certificate digest match the expected // value, if provided. byte[] sourceStampCertificateDigest = LocalFileRecord.getUncompressedData( apk, sourceStampCdRecord, zipSections.getZipCentralDirectoryOffset()); if (expectedCertDigest != null) { String actualCertDigest = ApkSigningBlockUtils.toHex(sourceStampCertificateDigest); if (!expectedCertDigest.equalsIgnoreCase(actualCertDigest)) { return createSourceStampResultWithError( Result.SourceStampInfo.SourceStampVerificationStatus .CERT_DIGEST_MISMATCH, Issue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH, actualCertDigest, expectedCertDigest); } } Map> signatureSchemeApkContentDigests = new HashMap<>(); Map supportedSchemeNames = getSupportedSchemeNames(mMaxSdkVersion); Set foundApkSigSchemeIds = new HashSet<>(2); Result result = new Result(); ApkSigningBlockUtils.Result v3Result = null; if (mMaxSdkVersion >= AndroidSdkVersion.P) { v3Result = getApkContentDigests(apk, zipSections, foundApkSigSchemeIds, supportedSchemeNames, signatureSchemeApkContentDigests, VERSION_APK_SIGNATURE_SCHEME_V3, Math.max(minSdkVersion, AndroidSdkVersion.P)); if (v3Result != null && v3Result.containsErrors()) { result.mergeFrom(v3Result); return mergeSourceStampResult( Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR, result); } } ApkSigningBlockUtils.Result v2Result = null; if (mMaxSdkVersion >= AndroidSdkVersion.N && (minSdkVersion < AndroidSdkVersion.P || foundApkSigSchemeIds.isEmpty())) { v2Result = getApkContentDigests(apk, zipSections, foundApkSigSchemeIds, supportedSchemeNames, signatureSchemeApkContentDigests, VERSION_APK_SIGNATURE_SCHEME_V2, Math.max(minSdkVersion, AndroidSdkVersion.N)); if (v2Result != null && v2Result.containsErrors()) { result.mergeFrom(v2Result); return mergeSourceStampResult( Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR, result); } } if (minSdkVersion < AndroidSdkVersion.N || foundApkSigSchemeIds.isEmpty()) { signatureSchemeApkContentDigests.put(VERSION_JAR_SIGNATURE_SCHEME, getApkContentDigestFromV1SigningScheme(cdRecords, apk, zipSections)); } ApkSigResult sourceStampResult = V2SourceStampVerifier.verify( apk, zipSections, sourceStampCertificateDigest, signatureSchemeApkContentDigests, minSdkVersion, mMaxSdkVersion); result.mergeFrom(sourceStampResult); // Since the caller is only seeking to verify the source stamp the Result can be marked // as verified if the source stamp verification was successful. if (sourceStampResult.verified) { result.setVerified(); } else { // To prevent APK signature verification with a failed / missing source stamp the // source stamp verification will only log warnings; to allow the caller to capture // the failure reason treat all warnings as errors. result.setWarningsAsErrors(true); } return result; } catch (ApkFormatException | IOException | ZipFormatException e) { return createSourceStampResultWithError( Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR, Issue.MALFORMED_APK, e); } catch (NoSuchAlgorithmException e) { return createSourceStampResultWithError( Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR, Issue.UNEXPECTED_EXCEPTION, e); } catch (SignatureNotFoundException e) { return createSourceStampResultWithError( Result.SourceStampInfo.SourceStampVerificationStatus.STAMP_NOT_VERIFIED, Issue.SOURCE_STAMP_SIG_MISSING); } } /** * Creates and returns a {@code Result} that can be returned for source stamp verification * with the provided source stamp {@code verificationStatus}, and logs an error for the * specified {@code issue} and {@code params}. */ private static Result createSourceStampResultWithError( Result.SourceStampInfo.SourceStampVerificationStatus verificationStatus, Issue issue, Object... params) { Result result = new Result(); result.addError(issue, params); return mergeSourceStampResult(verificationStatus, result); } /** * Creates a new {@link Result.SourceStampInfo} under the provided {@code result} and sets the * source stamp status to the provided {@code verificationStatus}. */ private static Result mergeSourceStampResult( Result.SourceStampInfo.SourceStampVerificationStatus verificationStatus, Result result) { result.mSourceStampInfo = new Result.SourceStampInfo(verificationStatus); return result; } /** * Gets content digests, signing lineage and certificates from the given {@code schemeId} block * alongside encountered errors info and creates a new {@code Result} containing all this * information. */ public static Result getSigningBlockResult( DataSource apk, ApkUtils.ZipSections zipSections, int sdkVersion, int schemeId) throws IOException, NoSuchAlgorithmException{ Map> sigSchemeApkContentDigests = new HashMap<>(); Map supportedSchemeNames = getSupportedSchemeNames(sdkVersion); Set foundApkSigSchemeIds = new HashSet<>(2); Result result = new Result(); result.mergeFrom(getApkContentDigests(apk, zipSections, foundApkSigSchemeIds, supportedSchemeNames, sigSchemeApkContentDigests, schemeId, sdkVersion, sdkVersion)); return result; } /** * Gets the content digest from the {@code result}'s signers. Ignores {@code ContentDigest}s * for which {@code SignatureAlgorithm} is {@code null}. */ public static Map getContentDigestsFromResult( Result result, int schemeId) { Map apkContentDigests = new HashMap<>(); if (!(schemeId == VERSION_APK_SIGNATURE_SCHEME_V2 || schemeId == VERSION_APK_SIGNATURE_SCHEME_V3 || schemeId == VERSION_APK_SIGNATURE_SCHEME_V31)) { return apkContentDigests; } switch (schemeId) { case VERSION_APK_SIGNATURE_SCHEME_V2: for (V2SchemeSignerInfo signerInfo : result.getV2SchemeSigners()) { getContentDigests(signerInfo.getContentDigests(), apkContentDigests); } break; case VERSION_APK_SIGNATURE_SCHEME_V3: for (Result.V3SchemeSignerInfo signerInfo : result.getV3SchemeSigners()) { getContentDigests(signerInfo.getContentDigests(), apkContentDigests); } break; case VERSION_APK_SIGNATURE_SCHEME_V31: for (Result.V3SchemeSignerInfo signerInfo : result.getV31SchemeSigners()) { getContentDigests(signerInfo.getContentDigests(), apkContentDigests); } break; } return apkContentDigests; } private static void getContentDigests( List digests, Map contentDigestsMap) { for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest : digests) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById( contentDigest.getSignatureAlgorithmId()); if (signatureAlgorithm == null) { continue; } contentDigestsMap.put(signatureAlgorithm.getContentDigestAlgorithm(), contentDigest.getValue()); } } /** * Checks whether a given {@code result} contains errors indicating that a signing certificate * lineage is incorrect. */ public static boolean containsLineageErrors( Result result) { if (!result.containsErrors()) { return false; } return (result.getAllErrors().stream().map(i -> i.getIssue()) .anyMatch(error -> LINEAGE_RELATED_ISSUES.contains(error))); } /** * Gets a lineage from the first signer from a given {@code result}. * If the {@code result} contains errors related to the lineage incorrectness or there are no * signers or certificates, it returns {@code null}. * If the lineage is empty but there is a signer, it returns a 1-element lineage containing * the signing key. */ public static SigningCertificateLineage getLineageFromResult( Result result, int sdkVersion, int schemeId) throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { if (!(schemeId == VERSION_APK_SIGNATURE_SCHEME_V3 || schemeId == VERSION_APK_SIGNATURE_SCHEME_V31) || containsLineageErrors(result)) { return null; } List signersInfo = schemeId == VERSION_APK_SIGNATURE_SCHEME_V3 ? result.getV3SchemeSigners() : result.getV31SchemeSigners(); if (signersInfo.isEmpty()) { return null; } V3SchemeSignerInfo firstSignerInfo = signersInfo.get(0); SigningCertificateLineage lineage = firstSignerInfo.mSigningCertificateLineage; if (lineage == null && firstSignerInfo.getCertificate() != null) { try { lineage = new SigningCertificateLineage.Builder( new SignerConfig.Builder( /* keyConfig= */ (KeyConfig) null, firstSignerInfo.getCertificate()) .build()) .build(); } catch (Exception e) { return null; } } return lineage; } /** * Obtains the APK content digest(s) and adds them to the provided {@code * sigSchemeApkContentDigests}, returning an {@code ApkSigningBlockUtils.Result} that can be * merged with a {@code Result} to notify the client of any errors. * *

Note, this method currently only supports signature scheme V2 and V3; to obtain the * content digests for V1 signatures use {@link * #getApkContentDigestFromV1SigningScheme(List, DataSource, ApkUtils.ZipSections)}. If a * signature scheme version other than V2 or V3 is provided a {@code null} value will be * returned. */ private ApkSigningBlockUtils.Result getApkContentDigests(DataSource apk, ApkUtils.ZipSections zipSections, Set foundApkSigSchemeIds, Map supportedSchemeNames, Map> sigSchemeApkContentDigests, int apkSigSchemeVersion, int minSdkVersion) throws IOException, NoSuchAlgorithmException { return getApkContentDigests(apk, zipSections, foundApkSigSchemeIds, supportedSchemeNames, sigSchemeApkContentDigests, apkSigSchemeVersion, minSdkVersion, mMaxSdkVersion); } /** * Obtains the APK content digest(s) and adds them to the provided {@code * sigSchemeApkContentDigests}, returning an {@code ApkSigningBlockUtils.Result} that can be * merged with a {@code Result} to notify the client of any errors. * *

Note, this method currently only supports signature scheme V2 and V3; to obtain the * content digests for V1 signatures use {@link * #getApkContentDigestFromV1SigningScheme(List, DataSource, ApkUtils.ZipSections)}. If a * signature scheme version other than V2 or V3 is provided a {@code null} value will be * returned. */ private static ApkSigningBlockUtils.Result getApkContentDigests(DataSource apk, ApkUtils.ZipSections zipSections, Set foundApkSigSchemeIds, Map supportedSchemeNames, Map> sigSchemeApkContentDigests, int apkSigSchemeVersion, int minSdkVersion, int maxSdkVersion) throws IOException, NoSuchAlgorithmException { if (!(apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V2 || apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V3 || apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V31)) { return null; } ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(apkSigSchemeVersion); SignatureInfo signatureInfo; try { int sigSchemeBlockId; switch (apkSigSchemeVersion) { case VERSION_APK_SIGNATURE_SCHEME_V31: sigSchemeBlockId = V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID; break; case VERSION_APK_SIGNATURE_SCHEME_V3: sigSchemeBlockId = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; break; default: sigSchemeBlockId = V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID; } signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections, sigSchemeBlockId, result); } catch (ApkSigningBlockUtils.SignatureNotFoundException e) { return null; } foundApkSigSchemeIds.add(apkSigSchemeVersion); Set contentDigestsToVerify = new HashSet<>(1); if (apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V2) { V2SchemeVerifier.parseSigners(signatureInfo.signatureBlock, contentDigestsToVerify, supportedSchemeNames, foundApkSigSchemeIds, minSdkVersion, maxSdkVersion, result); } else { V3SchemeVerifier.parseSigners(signatureInfo.signatureBlock, contentDigestsToVerify, result); } Map apkContentDigests = new EnumMap<>( ContentDigestAlgorithm.class); for (ApkSigningBlockUtils.Result.SignerInfo signerInfo : result.signers) { for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest : signerInfo.contentDigests) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById( contentDigest.getSignatureAlgorithmId()); if (signatureAlgorithm == null) { continue; } apkContentDigests.put(signatureAlgorithm.getContentDigestAlgorithm(), contentDigest.getValue()); } } sigSchemeApkContentDigests.put(apkSigSchemeVersion, apkContentDigests); return result; } private static void checkV4Signer(List v3Signers, List v4Certs, byte[] digestFromV4, Result result) { if (v3Signers.size() != 1) { result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS); } // Compare certificates. checkV4Certificate(v4Certs, v3Signers.get(0).mCerts, result); // Compare digests. final byte[] digestFromV3 = pickBestDigestForV4(v3Signers.get(0).getContentDigests()); if (!Arrays.equals(digestFromV4, digestFromV3)) { result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH, 3, toHex(digestFromV3), toHex(digestFromV4)); } } private static void checkV4Certificate(List v4Certs, List v2v3Certs, Result result) { try { byte[] v4Cert = v4Certs.get(0).getEncoded(); byte[] cert = v2v3Certs.get(0).getEncoded(); if (!Arrays.equals(cert, v4Cert)) { result.addError(Issue.V4_SIG_V2_V3_SIGNERS_MISMATCH); } } catch (CertificateEncodingException e) { throw new RuntimeException("Failed to encode APK signer cert", e); } } private static byte[] pickBestDigestForV4( List contentDigests) { Map apkContentDigests = new HashMap<>(); collectApkContentDigests(contentDigests, apkContentDigests); return ApkSigningBlockUtils.pickBestDigestForV4(apkContentDigests); } private static Map getApkContentDigestsFromSigningSchemeResult( ApkSigningBlockUtils.Result apkSigningSchemeResult) { Map apkContentDigests = new HashMap<>(); for (ApkSigningBlockUtils.Result.SignerInfo signerInfo : apkSigningSchemeResult.signers) { collectApkContentDigests(signerInfo.contentDigests, apkContentDigests); } return apkContentDigests; } private static Map getApkContentDigestFromV1SigningScheme( List cdRecords, DataSource apk, ApkUtils.ZipSections zipSections) throws IOException, ApkFormatException { CentralDirectoryRecord manifestCdRecord = null; Map v1ContentDigest = new EnumMap<>( ContentDigestAlgorithm.class); for (CentralDirectoryRecord cdRecord : cdRecords) { if (MANIFEST_ENTRY_NAME.equals(cdRecord.getName())) { manifestCdRecord = cdRecord; break; } } if (manifestCdRecord == null) { // No JAR signing manifest file found. For SourceStamp verification, returning an empty // digest is enough since this would affect the final digest signed by the stamp, and // thus an empty digest will invalidate that signature. return v1ContentDigest; } try { byte[] manifestBytes = LocalFileRecord.getUncompressedData( apk, manifestCdRecord, zipSections.getZipCentralDirectoryOffset()); v1ContentDigest.put( ContentDigestAlgorithm.SHA256, computeSha256DigestBytes(manifestBytes)); return v1ContentDigest; } catch (ZipFormatException e) { throw new ApkFormatException("Failed to read APK", e); } } private static void collectApkContentDigests( List contentDigests, Map apkContentDigests) { for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest : contentDigests) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(contentDigest.getSignatureAlgorithmId()); if (signatureAlgorithm == null) { continue; } ContentDigestAlgorithm contentDigestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm(); apkContentDigests.put(contentDigestAlgorithm, contentDigest.getValue()); } } private static ByteBuffer getAndroidManifestFromApk( DataSource apk, ApkUtils.ZipSections zipSections) throws IOException, ApkFormatException { List cdRecords = V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections); try { return ApkSigner.getAndroidManifestFromApk( cdRecords, apk.slice(0, zipSections.getZipCentralDirectoryOffset())); } catch (ZipFormatException e) { throw new ApkFormatException("Failed to read AndroidManifest.xml", e); } } private static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdkVersion) { if (targetSdkVersion >= AndroidSdkVersion.R) { return VERSION_APK_SIGNATURE_SCHEME_V2; } return VERSION_JAR_SIGNATURE_SCHEME; } /** * Result of verifying an APKs signatures. The APK can be considered verified iff * {@link #isVerified()} returns {@code true}. */ public static class Result { private final List mErrors = new ArrayList<>(); private final List mWarnings = new ArrayList<>(); private final List mSignerCerts = new ArrayList<>(); private final List mV1SchemeSigners = new ArrayList<>(); private final List mV1SchemeIgnoredSigners = new ArrayList<>(); private final List mV2SchemeSigners = new ArrayList<>(); private final List mV3SchemeSigners = new ArrayList<>(); private final List mV31SchemeSigners = new ArrayList<>(); private final List mV4SchemeSigners = new ArrayList<>(); private SourceStampInfo mSourceStampInfo; private boolean mVerified; private boolean mVerifiedUsingV1Scheme; private boolean mVerifiedUsingV2Scheme; private boolean mVerifiedUsingV3Scheme; private boolean mVerifiedUsingV31Scheme; private boolean mVerifiedUsingV4Scheme; private boolean mSourceStampVerified; private boolean mWarningsAsErrors; private SigningCertificateLineage mSigningCertificateLineage; /** * Returns {@code true} if the APK's signatures verified. */ public boolean isVerified() { return mVerified; } private void setVerified() { mVerified = true; } /** * Returns {@code true} if the APK's JAR signatures verified. */ public boolean isVerifiedUsingV1Scheme() { return mVerifiedUsingV1Scheme; } /** * Returns {@code true} if the APK's APK Signature Scheme v2 signatures verified. */ public boolean isVerifiedUsingV2Scheme() { return mVerifiedUsingV2Scheme; } /** * Returns {@code true} if the APK's APK Signature Scheme v3 signature verified. */ public boolean isVerifiedUsingV3Scheme() { return mVerifiedUsingV3Scheme; } /** * Returns {@code true} if the APK's APK Signature Scheme v3.1 signature verified. */ public boolean isVerifiedUsingV31Scheme() { return mVerifiedUsingV31Scheme; } /** * Returns {@code true} if the APK's APK Signature Scheme v4 signature verified. */ public boolean isVerifiedUsingV4Scheme() { return mVerifiedUsingV4Scheme; } /** * Returns {@code true} if the APK's SourceStamp signature verified. */ public boolean isSourceStampVerified() { return mSourceStampVerified; } /** * Returns the verified signers' certificates, one per signer. */ public List getSignerCertificates() { return mSignerCerts; } private void addSignerCertificate(X509Certificate cert) { mSignerCerts.add(cert); } /** * Returns information about JAR signers associated with the APK's signature. These are the * signers used by Android. * * @see #getV1SchemeIgnoredSigners() */ public List getV1SchemeSigners() { return mV1SchemeSigners; } /** * Returns information about JAR signers ignored by the APK's signature verification * process. These signers are ignored by Android. However, each signer's errors or warnings * will contain information about why they are ignored. * * @see #getV1SchemeSigners() */ public List getV1SchemeIgnoredSigners() { return mV1SchemeIgnoredSigners; } /** * Returns information about APK Signature Scheme v2 signers associated with the APK's * signature. */ public List getV2SchemeSigners() { return mV2SchemeSigners; } /** * Returns information about APK Signature Scheme v3 signers associated with the APK's * signature. * * Multiple signers represent different targeted platform versions, not * a signing identity of multiple signers. APK Signature Scheme v3 only supports single * signer identities. */ public List getV3SchemeSigners() { return mV3SchemeSigners; } /** * Returns information about APK Signature Scheme v3.1 signers associated with the APK's * signature. * * Multiple signers represent different targeted platform versions, not * a signing identity of multiple signers. APK Signature Scheme v3.1 only supports single * signer identities. */ public List getV31SchemeSigners() { return mV31SchemeSigners; } /** * Returns information about APK Signature Scheme v4 signers associated with the APK's * signature. */ public List getV4SchemeSigners() { return mV4SchemeSigners; } /** * Returns information about SourceStamp associated with the APK's signature. */ public SourceStampInfo getSourceStampInfo() { return mSourceStampInfo; } /** * Returns the combined SigningCertificateLineage associated with this APK's APK Signature * Scheme v3 signing block. */ public SigningCertificateLineage getSigningCertificateLineage() { return mSigningCertificateLineage; } void addError(Issue msg, Object... parameters) { mErrors.add(new IssueWithParams(msg, parameters)); } void addWarning(Issue msg, Object... parameters) { mWarnings.add(new IssueWithParams(msg, parameters)); } /** * Sets whether warnings should be treated as errors. */ void setWarningsAsErrors(boolean value) { mWarningsAsErrors = value; } /** * Returns errors encountered while verifying the APK's signatures. */ public List getErrors() { if (!mWarningsAsErrors) { return mErrors; } else { List allErrors = new ArrayList<>(); allErrors.addAll(mErrors); allErrors.addAll(mWarnings); return allErrors; } } /** * Returns warnings encountered while verifying the APK's signatures. */ public List getWarnings() { return mWarnings; } private void mergeFrom(V1SchemeVerifier.Result source) { mVerifiedUsingV1Scheme = source.verified; mErrors.addAll(source.getErrors()); mWarnings.addAll(source.getWarnings()); for (V1SchemeVerifier.Result.SignerInfo signer : source.signers) { mV1SchemeSigners.add(new V1SchemeSignerInfo(signer)); } for (V1SchemeVerifier.Result.SignerInfo signer : source.ignoredSigners) { mV1SchemeIgnoredSigners.add(new V1SchemeSignerInfo(signer)); } } private void mergeFrom(ApkSigResult source) { switch (source.signatureSchemeVersion) { case VERSION_SOURCE_STAMP: mSourceStampVerified = source.verified; if (!source.mSigners.isEmpty()) { mSourceStampInfo = new SourceStampInfo(source.mSigners.get(0)); } break; default: throw new IllegalArgumentException( "Unknown ApkSigResult Signing Block Scheme Id " + source.signatureSchemeVersion); } } private void mergeFrom(ApkSigningBlockUtils.Result source) { if (source == null) { return; } if (source.containsErrors()) { mErrors.addAll(source.getErrors()); } if (source.containsWarnings()) { mWarnings.addAll(source.getWarnings()); } switch (source.signatureSchemeVersion) { case VERSION_APK_SIGNATURE_SCHEME_V2: mVerifiedUsingV2Scheme = source.verified; for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) { mV2SchemeSigners.add(new V2SchemeSignerInfo(signer)); } break; case VERSION_APK_SIGNATURE_SCHEME_V3: mVerifiedUsingV3Scheme = source.verified; for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) { mV3SchemeSigners.add(new V3SchemeSignerInfo(signer)); } // Do not overwrite a previously set lineage from a v3.1 signing block. if (mSigningCertificateLineage == null) { mSigningCertificateLineage = source.signingCertificateLineage; } break; case VERSION_APK_SIGNATURE_SCHEME_V31: mVerifiedUsingV31Scheme = source.verified; for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) { mV31SchemeSigners.add(new V3SchemeSignerInfo(signer)); } mSigningCertificateLineage = source.signingCertificateLineage; break; case VERSION_APK_SIGNATURE_SCHEME_V4: mVerifiedUsingV4Scheme = source.verified; for (ApkSigningBlockUtils.Result.SignerInfo signer : source.signers) { mV4SchemeSigners.add(new V4SchemeSignerInfo(signer)); } break; case VERSION_SOURCE_STAMP: mSourceStampVerified = source.verified; if (!source.signers.isEmpty()) { mSourceStampInfo = new SourceStampInfo(source.signers.get(0)); } break; default: throw new IllegalArgumentException("Unknown Signing Block Scheme Id"); } } /** * Returns {@code true} if an error was encountered while verifying the APK. Any error * prevents the APK from being considered verified. */ public boolean containsErrors() { if (!mErrors.isEmpty()) { return true; } if (mWarningsAsErrors && !mWarnings.isEmpty()) { return true; } if (!mV1SchemeSigners.isEmpty()) { for (V1SchemeSignerInfo signer : mV1SchemeSigners) { if (signer.containsErrors()) { return true; } if (mWarningsAsErrors && !signer.getWarnings().isEmpty()) { return true; } } } if (!mV2SchemeSigners.isEmpty()) { for (V2SchemeSignerInfo signer : mV2SchemeSigners) { if (signer.containsErrors()) { return true; } if (mWarningsAsErrors && !signer.getWarnings().isEmpty()) { return true; } } } if (!mV3SchemeSigners.isEmpty()) { for (V3SchemeSignerInfo signer : mV3SchemeSigners) { if (signer.containsErrors()) { return true; } if (mWarningsAsErrors && !signer.getWarnings().isEmpty()) { return true; } } } if (!mV31SchemeSigners.isEmpty()) { for (V3SchemeSignerInfo signer : mV31SchemeSigners) { if (signer.containsErrors()) { return true; } if (mWarningsAsErrors && !signer.getWarnings().isEmpty()) { return true; } } } if (mSourceStampInfo != null) { if (mSourceStampInfo.containsErrors()) { return true; } if (mWarningsAsErrors && !mSourceStampInfo.getWarnings().isEmpty()) { return true; } } return false; } /** * Returns all errors for this result, including any errors from signature scheme signers * and the source stamp. */ public List getAllErrors() { List errors = new ArrayList<>(); errors.addAll(mErrors); if (mWarningsAsErrors) { errors.addAll(mWarnings); } if (!mV1SchemeSigners.isEmpty()) { for (V1SchemeSignerInfo signer : mV1SchemeSigners) { errors.addAll(signer.mErrors); if (mWarningsAsErrors) { errors.addAll(signer.getWarnings()); } } } if (!mV2SchemeSigners.isEmpty()) { for (V2SchemeSignerInfo signer : mV2SchemeSigners) { errors.addAll(signer.mErrors); if (mWarningsAsErrors) { errors.addAll(signer.getWarnings()); } } } if (!mV3SchemeSigners.isEmpty()) { for (V3SchemeSignerInfo signer : mV3SchemeSigners) { errors.addAll(signer.mErrors); if (mWarningsAsErrors) { errors.addAll(signer.getWarnings()); } } } if (!mV31SchemeSigners.isEmpty()) { for (V3SchemeSignerInfo signer : mV31SchemeSigners) { errors.addAll(signer.mErrors); if (mWarningsAsErrors) { errors.addAll(signer.getWarnings()); } } } if (mSourceStampInfo != null) { errors.addAll(mSourceStampInfo.getErrors()); if (mWarningsAsErrors) { errors.addAll(mSourceStampInfo.getWarnings()); } } return errors; } /** * Information about a JAR signer associated with the APK's signature. */ public static class V1SchemeSignerInfo { private final String mName; private final List mCertChain; private final String mSignatureBlockFileName; private final String mSignatureFileName; private final List mErrors; private final List mWarnings; private V1SchemeSignerInfo(V1SchemeVerifier.Result.SignerInfo result) { mName = result.name; mCertChain = result.certChain; mSignatureBlockFileName = result.signatureBlockFileName; mSignatureFileName = result.signatureFileName; mErrors = result.getErrors(); mWarnings = result.getWarnings(); } /** * Returns a user-friendly name of the signer. */ public String getName() { return mName; } /** * Returns the name of the JAR entry containing this signer's JAR signature block file. */ public String getSignatureBlockFileName() { return mSignatureBlockFileName; } /** * Returns the name of the JAR entry containing this signer's JAR signature file. */ public String getSignatureFileName() { return mSignatureFileName; } /** * Returns this signer's signing certificate or {@code null} if not available. The * certificate is guaranteed to be available if no errors were encountered during * verification (see {@link #containsErrors()}. * *

This certificate contains the signer's public key. */ public X509Certificate getCertificate() { return mCertChain.isEmpty() ? null : mCertChain.get(0); } /** * Returns the certificate chain for the signer's public key. The certificate containing * the public key is first, followed by the certificate (if any) which issued the * signing certificate, and so forth. An empty list may be returned if an error was * encountered during verification (see {@link #containsErrors()}). */ public List getCertificateChain() { return mCertChain; } /** * Returns {@code true} if an error was encountered while verifying this signer's JAR * signature. Any error prevents the signer's signature from being considered verified. */ public boolean containsErrors() { return !mErrors.isEmpty(); } /** * Returns errors encountered while verifying this signer's JAR signature. Any error * prevents the signer's signature from being considered verified. */ public List getErrors() { return mErrors; } /** * Returns warnings encountered while verifying this signer's JAR signature. Warnings * do not prevent the signer's signature from being considered verified. */ public List getWarnings() { return mWarnings; } private void addError(Issue msg, Object... parameters) { mErrors.add(new IssueWithParams(msg, parameters)); } } /** * Information about an APK Signature Scheme v2 signer associated with the APK's signature. */ public static class V2SchemeSignerInfo { private final int mIndex; private final List mCerts; private final List mErrors; private final List mWarnings; private final List mContentDigests; private V2SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result) { mIndex = result.index; mCerts = result.certs; mErrors = result.getErrors(); mWarnings = result.getWarnings(); mContentDigests = result.contentDigests; } /** * Returns this signer's {@code 0}-based index in the list of signers contained in the * APK's APK Signature Scheme v2 signature. */ public int getIndex() { return mIndex; } /** * Returns this signer's signing certificate or {@code null} if not available. The * certificate is guaranteed to be available if no errors were encountered during * verification (see {@link #containsErrors()}. * *

This certificate contains the signer's public key. */ public X509Certificate getCertificate() { return mCerts.isEmpty() ? null : mCerts.get(0); } /** * Returns this signer's certificates. The first certificate is for the signer's public * key. An empty list may be returned if an error was encountered during verification * (see {@link #containsErrors()}). */ public List getCertificates() { return mCerts; } private void addError(Issue msg, Object... parameters) { mErrors.add(new IssueWithParams(msg, parameters)); } public boolean containsErrors() { return !mErrors.isEmpty(); } public List getErrors() { return mErrors; } public List getWarnings() { return mWarnings; } public List getContentDigests() { return mContentDigests; } } /** * Information about an APK Signature Scheme v3 signer associated with the APK's signature. */ public static class V3SchemeSignerInfo { private final int mIndex; private final List mCerts; private final List mErrors; private final List mWarnings; private final List mContentDigests; private final int mMinSdkVersion; private final int mMaxSdkVersion; private final boolean mRotationTargetsDevRelease; private final SigningCertificateLineage mSigningCertificateLineage; private V3SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result) { mIndex = result.index; mCerts = result.certs; mErrors = result.getErrors(); mWarnings = result.getWarnings(); mContentDigests = result.contentDigests; mMinSdkVersion = result.minSdkVersion; mMaxSdkVersion = result.maxSdkVersion; mSigningCertificateLineage = result.signingCertificateLineage; mRotationTargetsDevRelease = result.additionalAttributes.stream().mapToInt( attribute -> attribute.getId()).anyMatch( attrId -> attrId == V3SchemeConstants.ROTATION_ON_DEV_RELEASE_ATTR_ID); } /** * Returns this signer's {@code 0}-based index in the list of signers contained in the * APK's APK Signature Scheme v3 signature. */ public int getIndex() { return mIndex; } /** * Returns this signer's signing certificate or {@code null} if not available. The * certificate is guaranteed to be available if no errors were encountered during * verification (see {@link #containsErrors()}. * *

This certificate contains the signer's public key. */ public X509Certificate getCertificate() { return mCerts.isEmpty() ? null : mCerts.get(0); } /** * Returns this signer's certificates. The first certificate is for the signer's public * key. An empty list may be returned if an error was encountered during verification * (see {@link #containsErrors()}). */ public List getCertificates() { return mCerts; } public boolean containsErrors() { return !mErrors.isEmpty(); } public List getErrors() { return mErrors; } public List getWarnings() { return mWarnings; } public List getContentDigests() { return mContentDigests; } /** * Returns the minimum SDK version on which this signer should be verified. */ public int getMinSdkVersion() { return mMinSdkVersion; } /** * Returns the maximum SDK version on which this signer should be verified. */ public int getMaxSdkVersion() { return mMaxSdkVersion; } /** * Returns whether rotation is targeting a development release. * *

A development release uses the SDK version of the previously released platform * until the SDK of the development release is finalized. To allow rotation to target * a development release after T, this attribute must be set to ensure rotation is * used on the development release but ignored on the released platform with the same * API level. */ public boolean getRotationTargetsDevRelease() { return mRotationTargetsDevRelease; } /** * Returns the {@link SigningCertificateLineage} for this signer; when an APK has * SDK targeted signing configs, the lineage of each signer could potentially contain * a subset of the full signing lineage and / or different capabilities for each signer * in the lineage. */ public SigningCertificateLineage getSigningCertificateLineage() { return mSigningCertificateLineage; } } /** * Information about an APK Signature Scheme V4 signer associated with the APK's * signature. */ public static class V4SchemeSignerInfo { private final int mIndex; private final List mCerts; private final List mErrors; private final List mWarnings; private final List mContentDigests; private V4SchemeSignerInfo(ApkSigningBlockUtils.Result.SignerInfo result) { mIndex = result.index; mCerts = result.certs; mErrors = result.getErrors(); mWarnings = result.getWarnings(); mContentDigests = result.contentDigests; } /** * Returns this signer's {@code 0}-based index in the list of signers contained in the * APK's APK Signature Scheme v3 signature. */ public int getIndex() { return mIndex; } /** * Returns this signer's signing certificate or {@code null} if not available. The * certificate is guaranteed to be available if no errors were encountered during * verification (see {@link #containsErrors()}. * *

This certificate contains the signer's public key. */ public X509Certificate getCertificate() { return mCerts.isEmpty() ? null : mCerts.get(0); } /** * Returns this signer's certificates. The first certificate is for the signer's public * key. An empty list may be returned if an error was encountered during verification * (see {@link #containsErrors()}). */ public List getCertificates() { return mCerts; } public boolean containsErrors() { return !mErrors.isEmpty(); } public List getErrors() { return mErrors; } public List getWarnings() { return mWarnings; } public List getContentDigests() { return mContentDigests; } } /** * Information about SourceStamp associated with the APK's signature. */ public static class SourceStampInfo { public enum SourceStampVerificationStatus { /** The stamp is present and was successfully verified. */ STAMP_VERIFIED, /** The stamp is present but failed verification. */ STAMP_VERIFICATION_FAILED, /** The expected cert digest did not match the digest in the APK. */ CERT_DIGEST_MISMATCH, /** The stamp is not present at all. */ STAMP_MISSING, /** The stamp is at least partially present, but was not able to be verified. */ STAMP_NOT_VERIFIED, /** The stamp was not able to be verified due to an unexpected error. */ VERIFICATION_ERROR } private final List mCertificates; private final List mCertificateLineage; private final List mErrors; private final List mWarnings; private final List mInfoMessages; private final SourceStampVerificationStatus mSourceStampVerificationStatus; private final long mTimestamp; private SourceStampInfo(ApkSignerInfo result) { mCertificates = result.certs; mCertificateLineage = result.certificateLineage; mErrors = ApkVerificationIssueAdapter.getIssuesFromVerificationIssues( result.getErrors()); mWarnings = ApkVerificationIssueAdapter.getIssuesFromVerificationIssues( result.getWarnings()); mInfoMessages = ApkVerificationIssueAdapter.getIssuesFromVerificationIssues( result.getInfoMessages()); if (mErrors.isEmpty() && mWarnings.isEmpty()) { mSourceStampVerificationStatus = SourceStampVerificationStatus.STAMP_VERIFIED; } else { mSourceStampVerificationStatus = SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED; } mTimestamp = result.timestamp; } SourceStampInfo(SourceStampVerificationStatus sourceStampVerificationStatus) { mCertificates = Collections.emptyList(); mCertificateLineage = Collections.emptyList(); mErrors = Collections.emptyList(); mWarnings = Collections.emptyList(); mInfoMessages = Collections.emptyList(); mSourceStampVerificationStatus = sourceStampVerificationStatus; mTimestamp = 0; } /** * Returns the SourceStamp's signing certificate or {@code null} if not available. The * certificate is guaranteed to be available if no errors were encountered during * verification (see {@link #containsErrors()}. * *

This certificate contains the SourceStamp's public key. */ public X509Certificate getCertificate() { return mCertificates.isEmpty() ? null : mCertificates.get(0); } /** * Returns a list containing all of the certificates in the stamp certificate lineage. */ public List getCertificatesInLineage() { return mCertificateLineage; } public boolean containsErrors() { return !mErrors.isEmpty(); } /** * Returns {@code true} if any info messages were encountered during verification of * this source stamp. */ public boolean containsInfoMessages() { return !mInfoMessages.isEmpty(); } public List getErrors() { return mErrors; } public List getWarnings() { return mWarnings; } /** * Returns a {@code List} of {@link IssueWithParams} representing info messages * that were encountered during verification of the source stamp. */ public List getInfoMessages() { return mInfoMessages; } /** * Returns the reason for any source stamp verification failures, or {@code * STAMP_VERIFIED} if the source stamp was successfully verified. */ public SourceStampVerificationStatus getSourceStampVerificationStatus() { return mSourceStampVerificationStatus; } /** * Returns the epoch timestamp in seconds representing the time this source stamp block * was signed, or 0 if the timestamp is not available. */ public long getTimestampEpochSeconds() { return mTimestamp; } } } /** * Error or warning encountered while verifying an APK's signatures. */ public enum Issue { /** * APK is not JAR-signed. */ JAR_SIG_NO_SIGNATURES("No JAR signatures"), /** * APK signature scheme v1 has exceeded the maximum number of jar signers. *

    *
  • Parameter 1: maximum allowed signers ({@code Integer})
  • *
  • Parameter 2: total number of signers ({@code Integer})
  • *
*/ JAR_SIG_MAX_SIGNATURES_EXCEEDED( "APK Signature Scheme v1 only supports a maximum of %1$d signers, found %2$d"), /** * APK does not contain any entries covered by JAR signatures. */ JAR_SIG_NO_SIGNED_ZIP_ENTRIES("No JAR entries covered by JAR signatures"), /** * APK contains multiple entries with the same name. * *
    *
  • Parameter 1: name ({@code String})
  • *
*/ JAR_SIG_DUPLICATE_ZIP_ENTRY("Duplicate entry: %1$s"), /** * JAR manifest contains a section with a duplicate name. * *
    *
  • Parameter 1: section name ({@code String})
  • *
*/ JAR_SIG_DUPLICATE_MANIFEST_SECTION("Duplicate section in META-INF/MANIFEST.MF: %1$s"), /** * JAR manifest contains a section without a name. * *
    *
  • Parameter 1: section index (1-based) ({@code Integer})
  • *
*/ JAR_SIG_UNNNAMED_MANIFEST_SECTION( "Malformed META-INF/MANIFEST.MF: invidual section #%1$d does not have a name"), /** * JAR signature file contains a section without a name. * *
    *
  • Parameter 1: signature file name ({@code String})
  • *
  • Parameter 2: section index (1-based) ({@code Integer})
  • *
*/ JAR_SIG_UNNNAMED_SIG_FILE_SECTION( "Malformed %1$s: invidual section #%2$d does not have a name"), /** APK is missing the JAR manifest entry (META-INF/MANIFEST.MF). */ JAR_SIG_NO_MANIFEST("Missing META-INF/MANIFEST.MF"), /** * JAR manifest references an entry which is not there in the APK. * *
    *
  • Parameter 1: entry name ({@code String})
  • *
*/ JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST( "%1$s entry referenced by META-INF/MANIFEST.MF not found in the APK"), /** * JAR manifest does not list a digest for the specified entry. * *
    *
  • Parameter 1: entry name ({@code String})
  • *
*/ JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST("No digest for %1$s in META-INF/MANIFEST.MF"), /** * JAR signature does not list a digest for the specified entry. * *
    *
  • Parameter 1: entry name ({@code String})
  • *
  • Parameter 2: signature file name ({@code String})
  • *
*/ JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE("No digest for %1$s in %2$s"), /** * The specified JAR entry is not covered by JAR signature. * *
    *
  • Parameter 1: entry name ({@code String})
  • *
*/ JAR_SIG_ZIP_ENTRY_NOT_SIGNED("%1$s entry not signed"), /** * JAR signature uses different set of signers to protect the two specified ZIP entries. * *
    *
  • Parameter 1: first entry name ({@code String})
  • *
  • Parameter 2: first entry signer names ({@code List})
  • *
  • Parameter 3: second entry name ({@code String})
  • *
  • Parameter 4: second entry signer names ({@code List})
  • *
*/ JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH( "Entries %1$s and %3$s are signed with different sets of signers" + " : <%2$s> vs <%4$s>"), /** * Digest of the specified ZIP entry's data does not match the digest expected by the JAR * signature. * *
    *
  • Parameter 1: entry name ({@code String})
  • *
  • Parameter 2: digest algorithm (e.g., SHA-256) ({@code String})
  • *
  • Parameter 3: name of the entry in which the expected digest is specified * ({@code String})
  • *
  • Parameter 4: base64-encoded actual digest ({@code String})
  • *
  • Parameter 5: base64-encoded expected digest ({@code String})
  • *
*/ JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY( "%2$s digest of %1$s does not match the digest specified in %3$s" + ". Expected: <%5$s>, actual: <%4$s>"), /** * Digest of the JAR manifest main section did not verify. * *
    *
  • Parameter 1: digest algorithm (e.g., SHA-256) ({@code String})
  • *
  • Parameter 2: name of the entry in which the expected digest is specified * ({@code String})
  • *
  • Parameter 3: base64-encoded actual digest ({@code String})
  • *
  • Parameter 4: base64-encoded expected digest ({@code String})
  • *
*/ JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY( "%1$s digest of META-INF/MANIFEST.MF main section does not match the digest" + " specified in %2$s. Expected: <%4$s>, actual: <%3$s>"), /** * Digest of the specified JAR manifest section does not match the digest expected by the * JAR signature. * *
    *
  • Parameter 1: section name ({@code String})
  • *
  • Parameter 2: digest algorithm (e.g., SHA-256) ({@code String})
  • *
  • Parameter 3: name of the signature file in which the expected digest is specified * ({@code String})
  • *
  • Parameter 4: base64-encoded actual digest ({@code String})
  • *
  • Parameter 5: base64-encoded expected digest ({@code String})
  • *
*/ JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY( "%2$s digest of META-INF/MANIFEST.MF section for %1$s does not match the digest" + " specified in %3$s. Expected: <%5$s>, actual: <%4$s>"), /** * JAR signature file does not contain the whole-file digest of the JAR manifest file. The * digest speeds up verification of JAR signature. * *
    *
  • Parameter 1: name of the signature file ({@code String})
  • *
*/ JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE( "%1$s does not specify digest of META-INF/MANIFEST.MF" + ". This slows down verification."), /** * APK is signed using APK Signature Scheme v2 or newer, but JAR signature file does not * contain protections against stripping of these newer scheme signatures. * *
    *
  • Parameter 1: name of the signature file ({@code String})
  • *
*/ JAR_SIG_NO_APK_SIG_STRIP_PROTECTION( "APK is signed using APK Signature Scheme v2 but these signatures may be stripped" + " without being detected because %1$s does not contain anti-stripping" + " protections."), /** * JAR signature of the signer is missing a file/entry. * *
    *
  • Parameter 1: name of the encountered file ({@code String})
  • *
  • Parameter 2: name of the missing file ({@code String})
  • *
*/ JAR_SIG_MISSING_FILE("Partial JAR signature. Found: %1$s, missing: %2$s"), /** * An exception was encountered while verifying JAR signature contained in a signature block * against the signature file. * *
    *
  • Parameter 1: name of the signature block file ({@code String})
  • *
  • Parameter 2: name of the signature file ({@code String})
  • *
  • Parameter 3: exception ({@code Throwable})
  • *
*/ JAR_SIG_VERIFY_EXCEPTION("Failed to verify JAR signature %1$s against %2$s: %3$s"), /** * JAR signature contains unsupported digest algorithm. * *
    *
  • Parameter 1: name of the signature block file ({@code String})
  • *
  • Parameter 2: digest algorithm OID ({@code String})
  • *
  • Parameter 3: signature algorithm OID ({@code String})
  • *
  • Parameter 4: API Levels on which this combination of algorithms is not supported * ({@code String})
  • *
  • Parameter 5: user-friendly variant of digest algorithm ({@code String})
  • *
  • Parameter 6: user-friendly variant of signature algorithm ({@code String})
  • *
*/ JAR_SIG_UNSUPPORTED_SIG_ALG( "JAR signature %1$s uses digest algorithm %5$s and signature algorithm %6$s which" + " is not supported on API Level(s) %4$s for which this APK is being" + " verified"), /** * An exception was encountered while parsing JAR signature contained in a signature block. * *
    *
  • Parameter 1: name of the signature block file ({@code String})
  • *
  • Parameter 2: exception ({@code Throwable})
  • *
*/ JAR_SIG_PARSE_EXCEPTION("Failed to parse JAR signature %1$s: %2$s"), /** * An exception was encountered while parsing a certificate contained in the JAR signature * block. * *
    *
  • Parameter 1: name of the signature block file ({@code String})
  • *
  • Parameter 2: exception ({@code Throwable})
  • *
*/ JAR_SIG_MALFORMED_CERTIFICATE("Malformed certificate in JAR signature %1$s: %2$s"), /** * JAR signature contained in a signature block file did not verify against the signature * file. * *
    *
  • Parameter 1: name of the signature block file ({@code String})
  • *
  • Parameter 2: name of the signature file ({@code String})
  • *
*/ JAR_SIG_DID_NOT_VERIFY("JAR signature %1$s did not verify against %2$s"), /** * JAR signature contains no verified signers. * *
    *
  • Parameter 1: name of the signature block file ({@code String})
  • *
*/ JAR_SIG_NO_SIGNERS("JAR signature %1$s contains no signers"), /** * JAR signature file contains a section with a duplicate name. * *
    *
  • Parameter 1: signature file name ({@code String})
  • *
  • Parameter 1: section name ({@code String})
  • *
*/ JAR_SIG_DUPLICATE_SIG_FILE_SECTION("Duplicate section in %1$s: %2$s"), /** * JAR signature file's main section doesn't contain the mandatory Signature-Version * attribute. * *
    *
  • Parameter 1: signature file name ({@code String})
  • *
*/ JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE( "Malformed %1$s: missing Signature-Version attribute"), /** * JAR signature file references an unknown APK signature scheme ID. * *
    *
  • Parameter 1: name of the signature file ({@code String})
  • *
  • Parameter 2: unknown APK signature scheme ID ({@code} Integer)
  • *
*/ JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID( "JAR signature %1$s references unknown APK signature scheme ID: %2$d"), /** * JAR signature file indicates that the APK is supposed to be signed with a supported APK * signature scheme (in addition to the JAR signature) but no such signature was found in * the APK. * *
    *
  • Parameter 1: name of the signature file ({@code String})
  • *
  • Parameter 2: APK signature scheme ID ({@code} Integer)
  • *
  • Parameter 3: APK signature scheme English name ({@code} String)
  • *
*/ JAR_SIG_MISSING_APK_SIG_REFERENCED( "JAR signature %1$s indicates the APK is signed using %3$s but no such signature" + " was found. Signature stripped?"), /** * JAR entry is not covered by signature and thus unauthorized modifications to its contents * will not be detected. * *
    *
  • Parameter 1: entry name ({@code String})
  • *
*/ JAR_SIG_UNPROTECTED_ZIP_ENTRY( "%1$s not protected by signature. Unauthorized modifications to this JAR entry" + " will not be detected. Delete or move the entry outside of META-INF/."), /** * APK which is both JAR-signed and signed using APK Signature Scheme v2 contains an APK * Signature Scheme v2 signature from this signer, but does not contain a JAR signature * from this signer. */ JAR_SIG_MISSING("No JAR signature from this signer"), /** * APK is targeting a sandbox version which requires APK Signature Scheme v2 signature but * no such signature was found. * *
    *
  • Parameter 1: target sandbox version ({@code Integer})
  • *
*/ NO_SIG_FOR_TARGET_SANDBOX_VERSION( "Missing APK Signature Scheme v2 signature required for target sandbox version" + " %1$d"), /** * APK is targeting an SDK version that requires a minimum signature scheme version, but the * APK is not signed with that version or later. * *
    *
  • Parameter 1: target SDK Version (@code Integer})
  • *
  • Parameter 2: minimum signature scheme version ((@code Integer})
  • *
*/ MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET( "Target SDK version %1$d requires a minimum of signature scheme v%2$d; the APK is" + " not signed with this or a later signature scheme"), /** * APK which is both JAR-signed and signed using APK Signature Scheme v2 contains a JAR * signature from this signer, but does not contain an APK Signature Scheme v2 signature * from this signer. */ V2_SIG_MISSING("No APK Signature Scheme v2 signature from this signer"), /** * Failed to parse the list of signers contained in the APK Signature Scheme v2 signature. */ V2_SIG_MALFORMED_SIGNERS("Malformed list of signers"), /** * Failed to parse this signer's signer block contained in the APK Signature Scheme v2 * signature. */ V2_SIG_MALFORMED_SIGNER("Malformed signer block"), /** * Public key embedded in the APK Signature Scheme v2 signature of this signer could not be * parsed. * *
    *
  • Parameter 1: error details ({@code Throwable})
  • *
*/ V2_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"), /** * This APK Signature Scheme v2 signer's certificate could not be parsed. * *
    *
  • Parameter 1: index ({@code 0}-based) of the certificate in the signer's list of * certificates ({@code Integer})
  • *
  • Parameter 2: sequence number ({@code 1}-based) of the certificate in the signer's * list of certificates ({@code Integer})
  • *
  • Parameter 3: error details ({@code Throwable})
  • *
*/ V2_SIG_MALFORMED_CERTIFICATE("Malformed certificate #%2$d: %3$s"), /** * Failed to parse this signer's signature record contained in the APK Signature Scheme v2 * signature. * *
    *
  • Parameter 1: record number (first record is {@code 1}) ({@code Integer})
  • *
*/ V2_SIG_MALFORMED_SIGNATURE("Malformed APK Signature Scheme v2 signature record #%1$d"), /** * Failed to parse this signer's digest record contained in the APK Signature Scheme v2 * signature. * *
    *
  • Parameter 1: record number (first record is {@code 1}) ({@code Integer})
  • *
*/ V2_SIG_MALFORMED_DIGEST("Malformed APK Signature Scheme v2 digest record #%1$d"), /** * This APK Signature Scheme v2 signer contains a malformed additional attribute. * *
    *
  • Parameter 1: attribute number (first attribute is {@code 1}) {@code Integer})
  • *
*/ V2_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE("Malformed additional attribute #%1$d"), /** * APK Signature Scheme v2 signature references an unknown APK signature scheme ID. * *
    *
  • Parameter 1: signer index ({@code Integer})
  • *
  • Parameter 2: unknown APK signature scheme ID ({@code} Integer)
  • *
*/ V2_SIG_UNKNOWN_APK_SIG_SCHEME_ID( "APK Signature Scheme v2 signer: %1$s references unknown APK signature scheme ID: " + "%2$d"), /** * APK Signature Scheme v2 signature indicates that the APK is supposed to be signed with a * supported APK signature scheme (in addition to the v2 signature) but no such signature * was found in the APK. * *
    *
  • Parameter 1: signer index ({@code Integer})
  • *
  • Parameter 2: APK signature scheme English name ({@code} String)
  • *
*/ V2_SIG_MISSING_APK_SIG_REFERENCED( "APK Signature Scheme v2 signature %1$s indicates the APK is signed using %2$s but " + "no such signature was found. Signature stripped?"), /** * APK signature scheme v2 has exceeded the maximum number of signers. *
    *
  • Parameter 1: maximum allowed signers ({@code Integer})
  • *
  • Parameter 2: total number of signers ({@code Integer})
  • *
*/ V2_SIG_MAX_SIGNATURES_EXCEEDED( "APK Signature Scheme V2 only supports a maximum of %1$d signers, found %2$d"), /** * APK Signature Scheme v2 signature contains no signers. */ V2_SIG_NO_SIGNERS("No signers in APK Signature Scheme v2 signature"), /** * This APK Signature Scheme v2 signer contains a signature produced using an unknown * algorithm. * *
    *
  • Parameter 1: algorithm ID ({@code Integer})
  • *
*/ V2_SIG_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"), /** * This APK Signature Scheme v2 signer contains an unknown additional attribute. * *
    *
  • Parameter 1: attribute ID ({@code Integer})
  • *
*/ V2_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE("Unknown additional attribute: ID %1$#x"), /** * An exception was encountered while verifying APK Signature Scheme v2 signature of this * signer. * *
    *
  • Parameter 1: signature algorithm ({@link SignatureAlgorithm})
  • *
  • Parameter 2: exception ({@code Throwable})
  • *
*/ V2_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"), /** * APK Signature Scheme v2 signature over this signer's signed-data block did not verify. * *
    *
  • Parameter 1: signature algorithm ({@link SignatureAlgorithm})
  • *
*/ V2_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"), /** * This APK Signature Scheme v2 signer offers no signatures. */ V2_SIG_NO_SIGNATURES("No signatures"), /** * This APK Signature Scheme v2 signer offers signatures but none of them are supported. */ V2_SIG_NO_SUPPORTED_SIGNATURES("No supported signatures: %1$s"), /** * This APK Signature Scheme v2 signer offers no certificates. */ V2_SIG_NO_CERTIFICATES("No certificates"), /** * This APK Signature Scheme v2 signer's public key listed in the signer's certificate does * not match the public key listed in the signatures record. * *
    *
  • Parameter 1: hex-encoded public key from certificate ({@code String})
  • *
  • Parameter 2: hex-encoded public key from signatures record ({@code String})
  • *
*/ V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD( "Public key mismatch between certificate and signature record: <%1$s> vs <%2$s>"), /** * This APK Signature Scheme v2 signer's signature algorithms listed in the signatures * record do not match the signature algorithms listed in the signatures record. * *
    *
  • Parameter 1: signature algorithms from signatures record ({@code List})
  • *
  • Parameter 2: signature algorithms from digests record ({@code List})
  • *
*/ V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS( "Signature algorithms mismatch between signatures and digests records" + ": %1$s vs %2$s"), /** * The APK's digest does not match the digest contained in the APK Signature Scheme v2 * signature. * *
    *
  • Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})
  • *
  • Parameter 2: hex-encoded expected digest of the APK ({@code String})
  • *
  • Parameter 3: hex-encoded actual digest of the APK ({@code String})
  • *
*/ V2_SIG_APK_DIGEST_DID_NOT_VERIFY( "APK integrity check failed. %1$s digest mismatch." + " Expected: <%2$s>, actual: <%3$s>"), /** * Failed to parse the list of signers contained in the APK Signature Scheme v3 signature. */ V3_SIG_MALFORMED_SIGNERS("Malformed list of signers"), /** * Failed to parse this signer's signer block contained in the APK Signature Scheme v3 * signature. */ V3_SIG_MALFORMED_SIGNER("Malformed signer block"), /** * Public key embedded in the APK Signature Scheme v3 signature of this signer could not be * parsed. * *
    *
  • Parameter 1: error details ({@code Throwable})
  • *
*/ V3_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"), /** * This APK Signature Scheme v3 signer's certificate could not be parsed. * *
    *
  • Parameter 1: index ({@code 0}-based) of the certificate in the signer's list of * certificates ({@code Integer})
  • *
  • Parameter 2: sequence number ({@code 1}-based) of the certificate in the signer's * list of certificates ({@code Integer})
  • *
  • Parameter 3: error details ({@code Throwable})
  • *
*/ V3_SIG_MALFORMED_CERTIFICATE("Malformed certificate #%2$d: %3$s"), /** * Failed to parse this signer's signature record contained in the APK Signature Scheme v3 * signature. * *
    *
  • Parameter 1: record number (first record is {@code 1}) ({@code Integer})
  • *
*/ V3_SIG_MALFORMED_SIGNATURE("Malformed APK Signature Scheme v3 signature record #%1$d"), /** * Failed to parse this signer's digest record contained in the APK Signature Scheme v3 * signature. * *
    *
  • Parameter 1: record number (first record is {@code 1}) ({@code Integer})
  • *
*/ V3_SIG_MALFORMED_DIGEST("Malformed APK Signature Scheme v3 digest record #%1$d"), /** * This APK Signature Scheme v3 signer contains a malformed additional attribute. * *
    *
  • Parameter 1: attribute number (first attribute is {@code 1}) {@code Integer})
  • *
*/ V3_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE("Malformed additional attribute #%1$d"), /** * APK Signature Scheme v3 signature contains no signers. */ V3_SIG_NO_SIGNERS("No signers in APK Signature Scheme v3 signature"), /** * APK Signature Scheme v3 signature contains multiple signers (only one allowed per * platform version). */ V3_SIG_MULTIPLE_SIGNERS("Multiple APK Signature Scheme v3 signatures found for a single " + " platform version."), /** * APK Signature Scheme v3 signature found, but multiple v1 and/or multiple v2 signers * found, where only one may be used with APK Signature Scheme v3 */ V3_SIG_MULTIPLE_PAST_SIGNERS("Multiple signatures found for pre-v3 signing with an APK " + " Signature Scheme v3 signer. Only one allowed."), /** * APK Signature Scheme v3 signature found, but its signer doesn't match the v1/v2 signers, * or have them as the root of its signing certificate history */ V3_SIG_PAST_SIGNERS_MISMATCH( "v3 signer differs from v1/v2 signer without proper signing certificate lineage."), /** * This APK Signature Scheme v3 signer contains a signature produced using an unknown * algorithm. * *
    *
  • Parameter 1: algorithm ID ({@code Integer})
  • *
*/ V3_SIG_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"), /** * This APK Signature Scheme v3 signer contains an unknown additional attribute. * *
    *
  • Parameter 1: attribute ID ({@code Integer})
  • *
*/ V3_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE("Unknown additional attribute: ID %1$#x"), /** * An exception was encountered while verifying APK Signature Scheme v3 signature of this * signer. * *
    *
  • Parameter 1: signature algorithm ({@link SignatureAlgorithm})
  • *
  • Parameter 2: exception ({@code Throwable})
  • *
*/ V3_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"), /** * The APK Signature Scheme v3 signer contained an invalid value for either min or max SDK * versions. * *
    *
  • Parameter 1: minSdkVersion ({@code Integer}) *
  • Parameter 2: maxSdkVersion ({@code Integer}) *
*/ V3_SIG_INVALID_SDK_VERSIONS("Invalid SDK Version parameter(s) encountered in APK Signature " + "scheme v3 signature: minSdkVersion %1$s maxSdkVersion: %2$s"), /** * APK Signature Scheme v3 signature over this signer's signed-data block did not verify. * *
    *
  • Parameter 1: signature algorithm ({@link SignatureAlgorithm})
  • *
*/ V3_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"), /** * This APK Signature Scheme v3 signer offers no signatures. */ V3_SIG_NO_SIGNATURES("No signatures"), /** * This APK Signature Scheme v3 signer offers signatures but none of them are supported. */ V3_SIG_NO_SUPPORTED_SIGNATURES("No supported signatures"), /** * This APK Signature Scheme v3 signer offers no certificates. */ V3_SIG_NO_CERTIFICATES("No certificates"), /** * This APK Signature Scheme v3 signer's minSdkVersion listed in the signer's signed data * does not match the minSdkVersion listed in the signatures record. * *
    *
  • Parameter 1: minSdkVersion in signature record ({@code Integer})
  • *
  • Parameter 2: minSdkVersion in signed data ({@code Integer})
  • *
*/ V3_MIN_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD( "minSdkVersion mismatch between signed data and signature record:" + " <%1$s> vs <%2$s>"), /** * This APK Signature Scheme v3 signer's maxSdkVersion listed in the signer's signed data * does not match the maxSdkVersion listed in the signatures record. * *
    *
  • Parameter 1: maxSdkVersion in signature record ({@code Integer})
  • *
  • Parameter 2: maxSdkVersion in signed data ({@code Integer})
  • *
*/ V3_MAX_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD( "maxSdkVersion mismatch between signed data and signature record:" + " <%1$s> vs <%2$s>"), /** * This APK Signature Scheme v3 signer's public key listed in the signer's certificate does * not match the public key listed in the signatures record. * *
    *
  • Parameter 1: hex-encoded public key from certificate ({@code String})
  • *
  • Parameter 2: hex-encoded public key from signatures record ({@code String})
  • *
*/ V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD( "Public key mismatch between certificate and signature record: <%1$s> vs <%2$s>"), /** * This APK Signature Scheme v3 signer's signature algorithms listed in the signatures * record do not match the signature algorithms listed in the signatures record. * *
    *
  • Parameter 1: signature algorithms from signatures record ({@code List})
  • *
  • Parameter 2: signature algorithms from digests record ({@code List})
  • *
*/ V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS( "Signature algorithms mismatch between signatures and digests records" + ": %1$s vs %2$s"), /** * The APK's digest does not match the digest contained in the APK Signature Scheme v3 * signature. * *
    *
  • Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})
  • *
  • Parameter 2: hex-encoded expected digest of the APK ({@code String})
  • *
  • Parameter 3: hex-encoded actual digest of the APK ({@code String})
  • *
*/ V3_SIG_APK_DIGEST_DID_NOT_VERIFY( "APK integrity check failed. %1$s digest mismatch." + " Expected: <%2$s>, actual: <%3$s>"), /** * The signer's SigningCertificateLineage attribute containd a proof-of-rotation record with * signature(s) that did not verify. */ V3_SIG_POR_DID_NOT_VERIFY("SigningCertificateLineage attribute containd a proof-of-rotation" + " record with signature(s) that did not verify."), /** * Failed to parse the SigningCertificateLineage structure in the APK Signature Scheme v3 * signature's additional attributes section. */ V3_SIG_MALFORMED_LINEAGE("Failed to parse the SigningCertificateLineage structure in the " + "APK Signature Scheme v3 signature's additional attributes section."), /** * The APK's signing certificate does not match the terminal node in the provided * proof-of-rotation structure describing the signing certificate history */ V3_SIG_POR_CERT_MISMATCH( "APK signing certificate differs from the associated certificate found in the " + "signer's SigningCertificateLineage."), /** * The APK Signature Scheme v3 signers encountered do not offer a continuous set of * supported platform versions. Either they overlap, resulting in potentially two * acceptable signers for a platform version, or there are holes which would create problems * in the event of platform version upgrades. */ V3_INCONSISTENT_SDK_VERSIONS("APK Signature Scheme v3 signers supported min/max SDK " + "versions are not continuous."), /** * The APK Signature Scheme v3 signers don't cover all requested SDK versions. * *
    *
  • Parameter 1: minSdkVersion ({@code Integer}) *
  • Parameter 2: maxSdkVersion ({@code Integer}) *
*/ V3_MISSING_SDK_VERSIONS("APK Signature Scheme v3 signers supported min/max SDK " + "versions do not cover the entire desired range. Found min: %1$s max %2$s"), /** * The SigningCertificateLineages for different platform versions using APK Signature Scheme * v3 do not go together. Specifically, each should be a subset of another, with the size * of each increasing as the platform level increases. */ V3_INCONSISTENT_LINEAGES("SigningCertificateLineages targeting different platform versions" + " using APK Signature Scheme v3 are not all a part of the same overall lineage."), /** * The v3 stripping protection attribute for rotation is present, but a v3.1 signing block * was not found. * *
    *
  • Parameter 1: min SDK version supporting rotation from attribute ({@code Integer}) *
*/ V31_BLOCK_MISSING( "The v3 signer indicates key rotation should be supported starting from SDK " + "version %1$s, but a v3.1 block was not found"), /** * The v3 stripping protection attribute for rotation does not match the minimum SDK version * targeting rotation in the v3.1 signer block. * *
    *
  • Parameter 1: min SDK version supporting rotation from attribute ({@code Integer}) *
  • Parameter 2: min SDK version supporting rotation from v3.1 block ({@code Integer}) *
*/ V31_ROTATION_MIN_SDK_MISMATCH( "The v3 signer indicates key rotation should be supported starting from SDK " + "version %1$s, but the v3.1 block targets %2$s for rotation"), /** * The APK supports key rotation with SDK version targeting using v3.1, but the rotation min * SDK version stripping protection attribute was not written to the v3 signer. * *
    *
  • Parameter 1: min SDK version supporting rotation from v3.1 block ({@code Integer}) *
*/ V31_ROTATION_MIN_SDK_ATTR_MISSING( "APK supports key rotation starting from SDK version %1$s, but the v3 signer does" + " not contain the attribute to detect if this signature is stripped"), /** * The APK contains a v3.1 signing block without a v3.0 block. The v3.1 block should only * be used for targeting rotation for a later SDK version; if an APK's minSdkVersion is the * same as the SDK version for rotation then this should be written to a v3.0 block. */ V31_BLOCK_FOUND_WITHOUT_V3_BLOCK( "The APK contains a v3.1 signing block without a v3.0 base block"), /** * The APK contains a v3.0 signing block with a rotation-targets-dev-release attribute in * the signer; this attribute is only intended for v3.1 signers to indicate they should be * targeting the next development release that is using the SDK version of the previously * released platform SDK version. */ V31_ROTATION_TARGETS_DEV_RELEASE_ATTR_ON_V3_SIGNER( "The rotation-targets-dev-release attribute is only supported on v3.1 signers; " + "this attribute will be ignored by the platform in a v3.0 signer"), /** * APK Signing Block contains an unknown entry. * *
    *
  • Parameter 1: entry ID ({@code Integer})
  • *
*/ APK_SIG_BLOCK_UNKNOWN_ENTRY_ID("APK Signing Block contains unknown entry: ID %1$#x"), /** * Failed to parse this signer's signature record contained in the APK Signature Scheme * V4 signature. * *
    *
  • Parameter 1: record number (first record is {@code 1}) ({@code Integer})
  • *
*/ V4_SIG_MALFORMED_SIGNERS( "V4 signature has malformed signer block"), /** * This APK Signature Scheme V4 signer contains a signature produced using an * unknown algorithm. * *
    *
  • Parameter 1: algorithm ID ({@code Integer})
  • *
*/ V4_SIG_UNKNOWN_SIG_ALGORITHM( "V4 signature has unknown signing algorithm: %1$#x"), /** * This APK Signature Scheme V4 signer offers no signatures. */ V4_SIG_NO_SIGNATURES( "V4 signature has no signature found"), /** * This APK Signature Scheme V4 signer offers signatures but none of them are * supported. */ V4_SIG_NO_SUPPORTED_SIGNATURES( "V4 signature has no supported signature"), /** * APK Signature Scheme v3 signature over this signer's signed-data block did not verify. * *
    *
  • Parameter 1: signature algorithm ({@link SignatureAlgorithm})
  • *
*/ V4_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"), /** * An exception was encountered while verifying APK Signature Scheme v3 signature of this * signer. * *
    *
  • Parameter 1: signature algorithm ({@link SignatureAlgorithm})
  • *
  • Parameter 2: exception ({@code Throwable})
  • *
*/ V4_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"), /** * Public key embedded in the APK Signature Scheme v4 signature of this signer could not be * parsed. * *
    *
  • Parameter 1: error details ({@code Throwable})
  • *
*/ V4_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"), /** * This APK Signature Scheme V4 signer's certificate could not be parsed. * *
    *
  • Parameter 1: index ({@code 0}-based) of the certificate in the signer's list of * certificates ({@code Integer})
  • *
  • Parameter 2: sequence number ({@code 1}-based) of the certificate in the signer's * list of certificates ({@code Integer})
  • *
  • Parameter 3: error details ({@code Throwable})
  • *
*/ V4_SIG_MALFORMED_CERTIFICATE( "V4 signature has malformed certificate"), /** * This APK Signature Scheme V4 signer offers no certificate. */ V4_SIG_NO_CERTIFICATE("V4 signature has no certificate"), /** * This APK Signature Scheme V4 signer's public key listed in the signer's * certificate does not match the public key listed in the signature proto. * *
    *
  • Parameter 1: hex-encoded public key from certificate ({@code String})
  • *
  • Parameter 2: hex-encoded public key from signature proto ({@code String})
  • *
*/ V4_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD( "V4 signature has mismatched certificate and signature: <%1$s> vs <%2$s>"), /** * The APK's hash root (aka digest) does not match the hash root contained in the Signature * Scheme V4 signature. * *
    *
  • Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})
  • *
  • Parameter 2: hex-encoded expected digest of the APK ({@code String})
  • *
  • Parameter 3: hex-encoded actual digest of the APK ({@code String})
  • *
*/ V4_SIG_APK_ROOT_DID_NOT_VERIFY( "V4 signature's hash tree root (content digest) did not verity"), /** * The APK's hash tree does not match the hash tree contained in the Signature * Scheme V4 signature. * *
    *
  • Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})
  • *
  • Parameter 2: hex-encoded expected hash tree of the APK ({@code String})
  • *
  • Parameter 3: hex-encoded actual hash tree of the APK ({@code String})
  • *
*/ V4_SIG_APK_TREE_DID_NOT_VERIFY( "V4 signature's hash tree did not verity"), /** * Using more than one Signer to sign APK Signature Scheme V4 signature. */ V4_SIG_MULTIPLE_SIGNERS( "V4 signature only supports one signer"), /** * V4.1 signature requires two signers to match the v3 and the v3.1. */ V41_SIG_NEEDS_TWO_SIGNERS("V4.1 signature requires two signers"), /** * The signer used to sign APK Signature Scheme V2/V3 signature does not match the signer * used to sign APK Signature Scheme V4 signature. */ V4_SIG_V2_V3_SIGNERS_MISMATCH( "V4 signature and V2/V3 signature have mismatched certificates"), /** * The v4 signature's digest does not match the digest from the corresponding v2 / v3 * signature. * *
    *
  • Parameter 1: Signature scheme of mismatched digest ({@code int}) *
  • Parameter 2: v2/v3 digest ({@code String}) *
  • Parameter 3: v4 digest ({@code String}) *
*/ V4_SIG_V2_V3_DIGESTS_MISMATCH( "V4 signature and V%1$d signature have mismatched digests, V%1$d digest: %2$s, V4" + " digest: %3$s"), /** * The v4 signature does not contain the expected number of digests. * *
    *
  • Parameter 1: Number of digests found ({@code int}) *
*/ V4_SIG_UNEXPECTED_DIGESTS( "V4 signature does not have the expected number of digests, found %1$d"), /** * The v4 signature format version isn't the same as the tool's current version, something * may go wrong. */ V4_SIG_VERSION_NOT_CURRENT( "V4 signature format version %1$d is different from the tool's current " + "version %2$d"), /** * The APK does not contain the source stamp certificate digest file nor the signature block * when verification expected a source stamp to be present. */ SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING( "Neither the source stamp certificate digest file nor the signature block are " + "present in the APK"), /** APK contains SourceStamp file, but does not contain a SourceStamp signature. */ SOURCE_STAMP_SIG_MISSING("No SourceStamp signature"), /** * SourceStamp's certificate could not be parsed. * *
    *
  • Parameter 1: error details ({@code Throwable}) *
*/ SOURCE_STAMP_MALFORMED_CERTIFICATE("Malformed certificate: %1$s"), /** Failed to parse SourceStamp's signature. */ SOURCE_STAMP_MALFORMED_SIGNATURE("Malformed SourceStamp signature"), /** * SourceStamp contains a signature produced using an unknown algorithm. * *
    *
  • Parameter 1: algorithm ID ({@code Integer}) *
*/ SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"), /** * An exception was encountered while verifying SourceStamp signature. * *
    *
  • Parameter 1: signature algorithm ({@link SignatureAlgorithm}) *
  • Parameter 2: exception ({@code Throwable}) *
*/ SOURCE_STAMP_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"), /** * SourceStamp signature block did not verify. * *
    *
  • Parameter 1: signature algorithm ({@link SignatureAlgorithm}) *
*/ SOURCE_STAMP_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"), /** SourceStamp offers no signatures. */ SOURCE_STAMP_NO_SIGNATURE("No signature"), /** * SourceStamp offers an unsupported signature. *
    *
  • Parameter 1: list of {@link SignatureAlgorithm}s in the source stamp * signing block. *
  • Parameter 2: {@code Exception} caught when attempting to obtain the list of * supported signatures. *
*/ SOURCE_STAMP_NO_SUPPORTED_SIGNATURE("Signature(s) {%1$s} not supported: %2$s"), /** * SourceStamp's certificate listed in the APK signing block does not match the certificate * listed in the SourceStamp file in the APK. * *
    *
  • Parameter 1: SHA-256 hash of certificate from SourceStamp block in APK signing * block ({@code String}) *
  • Parameter 2: SHA-256 hash of certificate from SourceStamp file in APK ({@code * String}) *
*/ SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK( "Certificate mismatch between SourceStamp block in APK signing block and" + " SourceStamp file in APK: <%1$s> vs <%2$s>"), /** * The APK contains a source stamp signature block without the expected certificate digest * in the APK contents. */ SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST( "A source stamp signature block was found without a corresponding certificate " + "digest in the APK"), /** * When verifying just the source stamp, the certificate digest in the APK does not match * the expected digest. *
    *
  • Parameter 1: SHA-256 digest of the source stamp certificate in the APK. *
  • Parameter 2: SHA-256 digest of the expected source stamp certificate. *
*/ SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH( "The source stamp certificate digest in the APK, %1$s, does not match the " + "expected digest, %2$s"), /** * Source stamp block contains a malformed attribute. * *
    *
  • Parameter 1: attribute number (first attribute is {@code 1}) {@code Integer})
  • *
*/ SOURCE_STAMP_MALFORMED_ATTRIBUTE("Malformed stamp attribute #%1$d"), /** * Source stamp block contains an unknown attribute. * *
    *
  • Parameter 1: attribute ID ({@code Integer})
  • *
*/ SOURCE_STAMP_UNKNOWN_ATTRIBUTE("Unknown stamp attribute: ID %1$#x"), /** * Failed to parse the SigningCertificateLineage structure in the source stamp * attributes section. */ SOURCE_STAMP_MALFORMED_LINEAGE("Failed to parse the SigningCertificateLineage " + "structure in the source stamp attributes section."), /** * The source stamp certificate does not match the terminal node in the provided * proof-of-rotation structure describing the stamp certificate history. */ SOURCE_STAMP_POR_CERT_MISMATCH( "APK signing certificate differs from the associated certificate found in the " + "signer's SigningCertificateLineage."), /** * The source stamp SigningCertificateLineage attribute contains a proof-of-rotation record * with signature(s) that did not verify. */ SOURCE_STAMP_POR_DID_NOT_VERIFY("Source stamp SigningCertificateLineage attribute " + "contains a proof-of-rotation record with signature(s) that did not verify."), /** * The source stamp timestamp attribute has an invalid value (<= 0). *
    *
  • Parameter 1: The invalid timestamp value. *
*/ SOURCE_STAMP_INVALID_TIMESTAMP( "The source stamp" + " timestamp attribute has an invalid value: %1$d"), /** * The APK could not be properly parsed due to a ZIP or APK format exception. *
    *
  • Parameter 1: The {@code Exception} caught when attempting to parse the APK. *
*/ MALFORMED_APK( "Malformed APK; the following exception was caught when attempting to parse the " + "APK: %1$s"), /** * An unexpected exception was caught when attempting to verify the signature(s) within the * APK. *
    *
  • Parameter 1: The {@code Exception} caught during verification. *
*/ UNEXPECTED_EXCEPTION( "An unexpected exception was caught when verifying the signature: %1$s"); private final String mFormat; Issue(String format) { mFormat = format; } /** * Returns the format string suitable for combining the parameters of this issue into a * readable string. See {@link java.util.Formatter} for format. */ private String getFormat() { return mFormat; } } /** * {@link Issue} with associated parameters. {@link #toString()} produces a readable formatted * form. */ public static class IssueWithParams extends ApkVerificationIssue { private final Issue mIssue; private final Object[] mParams; /** * Constructs a new {@code IssueWithParams} of the specified type and with provided * parameters. */ public IssueWithParams(Issue issue, Object[] params) { super(issue.mFormat, params); mIssue = issue; mParams = params; } /** * Returns the type of this issue. */ public Issue getIssue() { return mIssue; } /** * Returns the parameters of this issue. */ public Object[] getParams() { return mParams.clone(); } /** * Returns a readable form of this issue. */ @Override public String toString() { return String.format(mIssue.getFormat(), mParams); } } /** * Wrapped around {@code byte[]} which ensures that {@code equals} and {@code hashCode} operate * on the contents of the arrays rather than on references. */ private static class ByteArray { private final byte[] mArray; private final int mHashCode; private ByteArray(byte[] arr) { mArray = arr; mHashCode = Arrays.hashCode(mArray); } @Override public int hashCode() { return mHashCode; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof ByteArray)) { return false; } ByteArray other = (ByteArray) obj; if (hashCode() != other.hashCode()) { return false; } if (!Arrays.equals(mArray, other.mArray)) { return false; } return true; } } /** * Builder of {@link ApkVerifier} instances. * *

The resulting verifier by default checks whether the APK will verify on all platform * versions supported by the APK, as specified by {@code android:minSdkVersion} attributes in * the APK's {@code AndroidManifest.xml}. The range of platform versions can be customized using * {@link #setMinCheckedPlatformVersion(int)} and {@link #setMaxCheckedPlatformVersion(int)}. */ public static class Builder { private final File mApkFile; private final DataSource mApkDataSource; private File mV4SignatureFile; private Integer mMinSdkVersion; private int mMaxSdkVersion = Integer.MAX_VALUE; /** * Constructs a new {@code Builder} for verifying the provided APK file. */ public Builder(File apk) { if (apk == null) { throw new NullPointerException("apk == null"); } mApkFile = apk; mApkDataSource = null; } /** * Constructs a new {@code Builder} for verifying the provided APK. */ public Builder(DataSource apk) { if (apk == null) { throw new NullPointerException("apk == null"); } mApkDataSource = apk; mApkFile = null; } /** * Sets the oldest Android platform version for which the APK is verified. APK verification * will confirm that the APK is expected to install successfully on all known Android * platforms starting from the platform version with the provided API Level. The upper end * of the platform versions range can be modified via * {@link #setMaxCheckedPlatformVersion(int)}. * *

This method is useful for overriding the default behavior which checks that the APK * will verify on all platform versions supported by the APK, as specified by * {@code android:minSdkVersion} attributes in the APK's {@code AndroidManifest.xml}. * * @param minSdkVersion API Level of the oldest platform for which to verify the APK * @see #setMinCheckedPlatformVersion(int) */ public Builder setMinCheckedPlatformVersion(int minSdkVersion) { mMinSdkVersion = minSdkVersion; return this; } /** * Sets the newest Android platform version for which the APK is verified. APK verification * will confirm that the APK is expected to install successfully on all platform versions * supported by the APK up until and including the provided version. The lower end * of the platform versions range can be modified via * {@link #setMinCheckedPlatformVersion(int)}. * * @param maxSdkVersion API Level of the newest platform for which to verify the APK * @see #setMinCheckedPlatformVersion(int) */ public Builder setMaxCheckedPlatformVersion(int maxSdkVersion) { mMaxSdkVersion = maxSdkVersion; return this; } public Builder setV4SignatureFile(File v4SignatureFile) { mV4SignatureFile = v4SignatureFile; return this; } /** * Returns an {@link ApkVerifier} initialized according to the configuration of this * builder. */ public ApkVerifier build() { return new ApkVerifier( mApkFile, mApkDataSource, mV4SignatureFile, mMinSdkVersion, mMaxSdkVersion); } } /** * Adapter for converting base {@link ApkVerificationIssue} instances to their {@link * IssueWithParams} equivalent. */ public static class ApkVerificationIssueAdapter { private ApkVerificationIssueAdapter() { } // This field is visible for testing static final Map sVerificationIssueIdToIssue = new HashMap<>(); static { sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_SIGNERS, Issue.V2_SIG_MALFORMED_SIGNERS); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_NO_SIGNERS, Issue.V2_SIG_NO_SIGNERS); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_SIGNER, Issue.V2_SIG_MALFORMED_SIGNER); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_SIGNATURE, Issue.V2_SIG_MALFORMED_SIGNATURE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_NO_SIGNATURES, Issue.V2_SIG_NO_SIGNATURES); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_CERTIFICATE, Issue.V2_SIG_MALFORMED_CERTIFICATE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_NO_CERTIFICATES, Issue.V2_SIG_NO_CERTIFICATES); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V2_SIG_MALFORMED_DIGEST, Issue.V2_SIG_MALFORMED_DIGEST); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_SIGNERS, Issue.V3_SIG_MALFORMED_SIGNERS); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_NO_SIGNERS, Issue.V3_SIG_NO_SIGNERS); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_SIGNER, Issue.V3_SIG_MALFORMED_SIGNER); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_SIGNATURE, Issue.V3_SIG_MALFORMED_SIGNATURE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_NO_SIGNATURES, Issue.V3_SIG_NO_SIGNATURES); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_CERTIFICATE, Issue.V3_SIG_MALFORMED_CERTIFICATE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_NO_CERTIFICATES, Issue.V3_SIG_NO_CERTIFICATES); sVerificationIssueIdToIssue.put(ApkVerificationIssue.V3_SIG_MALFORMED_DIGEST, Issue.V3_SIG_MALFORMED_DIGEST); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_NO_SIGNATURE, Issue.SOURCE_STAMP_NO_SIGNATURE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_CERTIFICATE, Issue.SOURCE_STAMP_MALFORMED_CERTIFICATE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM, Issue.SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_SIGNATURE, Issue.SOURCE_STAMP_MALFORMED_SIGNATURE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY, Issue.SOURCE_STAMP_DID_NOT_VERIFY); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_VERIFY_EXCEPTION, Issue.SOURCE_STAMP_VERIFY_EXCEPTION); sVerificationIssueIdToIssue.put( ApkVerificationIssue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH, Issue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH); sVerificationIssueIdToIssue.put( ApkVerificationIssue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST, Issue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST); sVerificationIssueIdToIssue.put( ApkVerificationIssue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING, Issue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING); sVerificationIssueIdToIssue.put( ApkVerificationIssue.SOURCE_STAMP_NO_SUPPORTED_SIGNATURE, Issue.SOURCE_STAMP_NO_SUPPORTED_SIGNATURE); sVerificationIssueIdToIssue.put( ApkVerificationIssue .SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK, Issue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK); sVerificationIssueIdToIssue.put(ApkVerificationIssue.MALFORMED_APK, Issue.MALFORMED_APK); sVerificationIssueIdToIssue.put(ApkVerificationIssue.UNEXPECTED_EXCEPTION, Issue.UNEXPECTED_EXCEPTION); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_SIG_MISSING, Issue.SOURCE_STAMP_SIG_MISSING); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_ATTRIBUTE, Issue.SOURCE_STAMP_MALFORMED_ATTRIBUTE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE, Issue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_LINEAGE, Issue.SOURCE_STAMP_MALFORMED_LINEAGE); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_POR_CERT_MISMATCH, Issue.SOURCE_STAMP_POR_CERT_MISMATCH); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_POR_DID_NOT_VERIFY, Issue.SOURCE_STAMP_POR_DID_NOT_VERIFY); sVerificationIssueIdToIssue.put(ApkVerificationIssue.JAR_SIG_NO_SIGNATURES, Issue.JAR_SIG_NO_SIGNATURES); sVerificationIssueIdToIssue.put(ApkVerificationIssue.JAR_SIG_PARSE_EXCEPTION, Issue.JAR_SIG_PARSE_EXCEPTION); sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP, Issue.SOURCE_STAMP_INVALID_TIMESTAMP); } /** * Converts the provided {@code verificationIssues} to a {@code List} of corresponding * {@link IssueWithParams} instances. */ public static List getIssuesFromVerificationIssues( List verificationIssues) { List result = new ArrayList<>(verificationIssues.size()); for (ApkVerificationIssue issue : verificationIssues) { if (issue instanceof IssueWithParams) { result.add((IssueWithParams) issue); } else { result.add( new IssueWithParams(sVerificationIssueIdToIssue.get(issue.getIssueId()), issue.getParams())); } } return result; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_Constants.java0100644 0000000 0000000 00000000034 14763776540 023713 xustar000000000 0000000 28 mtime=1741684064.5490000 src/main/java/com/android/apksig/Constants.java0100644 0000000 0000000 00000005137 14763776540 020565 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import com.android.apksig.internal.apk.stamp.SourceStampConstants; import com.android.apksig.internal.apk.v1.V1SchemeConstants; import com.android.apksig.internal.apk.v2.V2SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeConstants; /** * Exports internally defined constants to allow clients to reference these values without relying * on internal code. */ public class Constants { private Constants() {} public static final int VERSION_SOURCE_STAMP = 0; public static final int VERSION_JAR_SIGNATURE_SCHEME = 1; public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2; public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3; public static final int VERSION_APK_SIGNATURE_SCHEME_V31 = 31; public static final int VERSION_APK_SIGNATURE_SCHEME_V4 = 4; /** * The maximum number of signers supported by the v1 and v2 APK Signature Schemes. */ public static final int MAX_APK_SIGNERS = 10; /** * The default page alignment for native library files in bytes. */ public static final short LIBRARY_PAGE_ALIGNMENT_BYTES = 16384; public static final String MANIFEST_ENTRY_NAME = V1SchemeConstants.MANIFEST_ENTRY_NAME; public static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID; public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; public static final int APK_SIGNATURE_SCHEME_V31_BLOCK_ID = V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID; public static final int PROOF_OF_ROTATION_ATTR_ID = V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID; public static final int V1_SOURCE_STAMP_BLOCK_ID = SourceStampConstants.V1_SOURCE_STAMP_BLOCK_ID; public static final int V2_SOURCE_STAMP_BLOCK_ID = SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID; public static final String OID_RSA_ENCRYPTION = "1.2.840.113549.1.1.1"; } ./PaxHeaders.X/src_main_java_com_android_apksig_DefaultApkSignerEngine.java0100644 0000000 0000000 00000000034 14763776540 026255 xustar000000000 0000000 28 mtime=1741684064.5500000 src/main/java/com/android/apksig/DefaultApkSignerEngine.java0100644 0000000 0000000 00000325616 14763776540 023136 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; import static com.android.apksig.apk.ApkUtils.computeSha256DigestBytes; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERITY_PADDING_BLOCK_ID; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME; import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT; import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V3_SUPPORT; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.apk.stamp.V2SourceStampSigner; import com.android.apksig.internal.apk.v1.DigestAlgorithm; import com.android.apksig.internal.apk.v1.V1SchemeConstants; import com.android.apksig.internal.apk.v1.V1SchemeSigner; import com.android.apksig.internal.apk.v1.V1SchemeVerifier; import com.android.apksig.internal.apk.v2.V2SchemeSigner; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeSigner; import com.android.apksig.internal.apk.v4.V4SchemeSigner; import com.android.apksig.internal.apk.v4.V4Signature; import com.android.apksig.internal.jar.ManifestParser; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.Pair; import com.android.apksig.internal.util.TeeDataSink; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSinks; import com.android.apksig.util.DataSource; import com.android.apksig.util.RunnablesExecutor; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Default implementation of {@link ApkSignerEngine}. * *

Use {@link Builder} to obtain instances of this engine. */ public class DefaultApkSignerEngine implements ApkSignerEngine { // IMPLEMENTATION NOTE: This engine generates a signed APK as follows: // 1. The engine asks its client to output input JAR entries which are not part of JAR // signature. // 2. If JAR signing (v1 signing) is enabled, the engine inspects the output JAR entries to // compute their digests, to be placed into output META-INF/MANIFEST.MF. It also inspects // the contents of input and output META-INF/MANIFEST.MF to borrow the main section of the // file. It does not care about individual (i.e., JAR entry-specific) sections. It then // emits the v1 signature (a set of JAR entries) and asks the client to output them. // 3. If APK Signature Scheme v2 (v2 signing) is enabled, the engine emits an APK Signing Block // from outputZipSections() and asks its client to insert this block into the output. // 4. If APK Signature Scheme v3 (v3 signing) is enabled, the engine includes it in the APK // Signing BLock output from outputZipSections() and asks its client to insert this block // into the output. If both v2 and v3 signing is enabled, they are both added to the APK // Signing Block before asking the client to insert it into the output. private final boolean mV1SigningEnabled; private final boolean mV2SigningEnabled; private final boolean mV3SigningEnabled; private final boolean mVerityEnabled; private final boolean mDebuggableApkPermitted; private final boolean mOtherSignersSignaturesPreserved; private final String mCreatedBy; private final List mSignerConfigs; private final List mTargetedSignerConfigs; private final SignerConfig mSourceStampSignerConfig; private final SigningCertificateLineage mSourceStampSigningCertificateLineage; private final boolean mSourceStampTimestampEnabled; private final int mMinSdkVersion; private final SigningCertificateLineage mSigningCertificateLineage; private List mPreservedV2Signers = Collections.emptyList(); private List> mPreservedSignatureBlocks = Collections.emptyList(); private List mV1SignerConfigs = Collections.emptyList(); private DigestAlgorithm mV1ContentDigestAlgorithm; private boolean mClosed; private boolean mV1SignaturePending; /** Names of JAR entries which this engine is expected to output as part of v1 signing. */ private Set mSignatureExpectedOutputJarEntryNames = Collections.emptySet(); /** Requests for digests of output JAR entries. */ private final Map mOutputJarEntryDigestRequests = new HashMap<>(); /** Digests of output JAR entries. */ private final Map mOutputJarEntryDigests = new HashMap<>(); /** Data of JAR entries emitted by this engine as v1 signature. */ private final Map mEmittedSignatureJarEntryData = new HashMap<>(); /** Requests for data of output JAR entries which comprise the v1 signature. */ private final Map mOutputSignatureJarEntryDataRequests = new HashMap<>(); /** * Request to obtain the data of MANIFEST.MF or {@code null} if the request hasn't been issued. */ private GetJarEntryDataRequest mInputJarManifestEntryDataRequest; /** * Request to obtain the data of AndroidManifest.xml or {@code null} if the request hasn't been * issued. */ private GetJarEntryDataRequest mOutputAndroidManifestEntryDataRequest; /** * Whether the package being signed is marked as {@code android:debuggable} or {@code null} if * this is not yet known. */ private Boolean mDebuggable; /** * Request to output the emitted v1 signature or {@code null} if the request hasn't been issued. */ private OutputJarSignatureRequestImpl mAddV1SignatureRequest; private boolean mV2SignaturePending; private boolean mV3SignaturePending; /** * Request to output the emitted v2 and/or v3 signature(s) {@code null} if the request hasn't * been issued. */ private OutputApkSigningBlockRequestImpl mAddSigningBlockRequest; private RunnablesExecutor mExecutor = RunnablesExecutor.MULTI_THREADED; /** * A Set of block IDs to be discarded when requesting to preserve the original signatures. */ private static final Set DISCARDED_SIGNATURE_BLOCK_IDS; static { DISCARDED_SIGNATURE_BLOCK_IDS = new HashSet<>(3); // The verity padding block is recomputed on an // ApkSigningBlockUtils.ANDROID_COMMON_PAGE_ALIGNMENT_BYTES boundary. DISCARDED_SIGNATURE_BLOCK_IDS.add(VERITY_PADDING_BLOCK_ID); // The source stamp block is not currently preserved; appending a new signature scheme // block will invalidate the previous source stamp. DISCARDED_SIGNATURE_BLOCK_IDS.add(Constants.V1_SOURCE_STAMP_BLOCK_ID); DISCARDED_SIGNATURE_BLOCK_IDS.add(Constants.V2_SOURCE_STAMP_BLOCK_ID); } private DefaultApkSignerEngine( List signerConfigs, List targetedSignerConfigs, SignerConfig sourceStampSignerConfig, SigningCertificateLineage sourceStampSigningCertificateLineage, boolean sourceStampTimestampEnabled, int minSdkVersion, boolean v1SigningEnabled, boolean v2SigningEnabled, boolean v3SigningEnabled, boolean verityEnabled, boolean debuggableApkPermitted, boolean otherSignersSignaturesPreserved, String createdBy, SigningCertificateLineage signingCertificateLineage) throws InvalidKeyException { if (signerConfigs.isEmpty() && targetedSignerConfigs.isEmpty()) { throw new IllegalArgumentException("At least one signer config must be provided"); } mV1SigningEnabled = v1SigningEnabled; mV2SigningEnabled = v2SigningEnabled; mV3SigningEnabled = v3SigningEnabled; mVerityEnabled = verityEnabled; mV1SignaturePending = v1SigningEnabled; mV2SignaturePending = v2SigningEnabled; mV3SignaturePending = v3SigningEnabled; mDebuggableApkPermitted = debuggableApkPermitted; mOtherSignersSignaturesPreserved = otherSignersSignaturesPreserved; mCreatedBy = createdBy; mSignerConfigs = signerConfigs; mTargetedSignerConfigs = targetedSignerConfigs; mSourceStampSignerConfig = sourceStampSignerConfig; mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; mSourceStampTimestampEnabled = sourceStampTimestampEnabled; mMinSdkVersion = minSdkVersion; mSigningCertificateLineage = signingCertificateLineage; if (v1SigningEnabled) { if (v3SigningEnabled) { // v3 signing only supports single signers, of which the oldest (first) will be the // one to use for v1 and v2 signing SignerConfig oldestConfig = !signerConfigs.isEmpty() ? signerConfigs.get(0) : targetedSignerConfigs.get(0); // in the event of signing certificate changes, make sure we have the oldest in the // signing history to sign with v1 if (signingCertificateLineage != null) { SigningCertificateLineage subLineage = signingCertificateLineage.getSubLineage( oldestConfig.mCertificates.get(0)); if (subLineage.size() != 1) { throw new IllegalArgumentException( "v1 signing enabled but the oldest signer in the" + " SigningCertificateLineage is missing. Please provide the" + " oldest signer to enable v1 signing"); } } createV1SignerConfigs(Collections.singletonList(oldestConfig), minSdkVersion); } else { createV1SignerConfigs(signerConfigs, minSdkVersion); } } } private void createV1SignerConfigs(List signerConfigs, int minSdkVersion) throws InvalidKeyException { mV1SignerConfigs = new ArrayList<>(signerConfigs.size()); Map v1SignerNameToSignerIndex = new HashMap<>(signerConfigs.size()); DigestAlgorithm v1ContentDigestAlgorithm = null; for (int i = 0; i < signerConfigs.size(); i++) { SignerConfig signerConfig = signerConfigs.get(i); List certificates = signerConfig.getCertificates(); PublicKey publicKey = certificates.get(0).getPublicKey(); String v1SignerName = V1SchemeSigner.getSafeSignerName(signerConfig.getName()); // Check whether the signer's name is unique among all v1 signers Integer indexOfOtherSignerWithSameName = v1SignerNameToSignerIndex.put(v1SignerName, i); if (indexOfOtherSignerWithSameName != null) { throw new IllegalArgumentException( "Signers #" + (indexOfOtherSignerWithSameName + 1) + " and #" + (i + 1) + " have the same name: " + v1SignerName + ". v1 signer names must be unique"); } DigestAlgorithm v1SignatureDigestAlgorithm = V1SchemeSigner.getSuggestedSignatureDigestAlgorithm(publicKey, minSdkVersion); V1SchemeSigner.SignerConfig v1SignerConfig = new V1SchemeSigner.SignerConfig(); v1SignerConfig.name = v1SignerName; v1SignerConfig.keyConfig = signerConfig.getKeyConfig(); v1SignerConfig.certificates = certificates; v1SignerConfig.signatureDigestAlgorithm = v1SignatureDigestAlgorithm; v1SignerConfig.deterministicDsaSigning = signerConfig.getDeterministicDsaSigning(); // For digesting contents of APK entries and of MANIFEST.MF, pick the algorithm // of comparable strength to the digest algorithm used for computing the signature. // When there are multiple signers, pick the strongest digest algorithm out of their // signature digest algorithms. This avoids reducing the digest strength used by any // of the signers to protect APK contents. if (v1ContentDigestAlgorithm == null) { v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm; } else { if (DigestAlgorithm.BY_STRENGTH_COMPARATOR.compare( v1SignatureDigestAlgorithm, v1ContentDigestAlgorithm) > 0) { v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm; } } mV1SignerConfigs.add(v1SignerConfig); } mV1ContentDigestAlgorithm = v1ContentDigestAlgorithm; mSignatureExpectedOutputJarEntryNames = V1SchemeSigner.getOutputEntryNames(mV1SignerConfigs); } private List createV2SignerConfigs( boolean apkSigningBlockPaddingSupported) throws InvalidKeyException { if (mV3SigningEnabled) { // v3 signing only supports single signers, of which the oldest (first) will be the one // to use for v1 and v2 signing List signerConfig = new ArrayList<>(); SignerConfig oldestConfig = !mSignerConfigs.isEmpty() ? mSignerConfigs.get(0) : mTargetedSignerConfigs.get(0); // first make sure that if we have signing certificate history that the oldest signer // corresponds to the oldest ancestor if (mSigningCertificateLineage != null) { SigningCertificateLineage subLineage = mSigningCertificateLineage.getSubLineage(oldestConfig.mCertificates.get(0)); if (subLineage.size() != 1) { throw new IllegalArgumentException( "v2 signing enabled but the oldest signer in" + " the SigningCertificateLineage is missing. Please provide" + " the oldest signer to enable v2 signing."); } } signerConfig.add( createSigningBlockSignerConfig( oldestConfig, apkSigningBlockPaddingSupported, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2)); return signerConfig; } else { return createSigningBlockSignerConfigs( apkSigningBlockPaddingSupported, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); } } private List processV3Configs( List rawConfigs) throws InvalidKeyException { // If the caller only specified targeted signing configs, ensure those configs cover the // full range for V3 support (or the APK's minSdkVersion if > P). int minRequiredV3SdkVersion = Math.max(AndroidSdkVersion.P, mMinSdkVersion); if (mSignerConfigs.isEmpty() && mTargetedSignerConfigs.get(0).getMinSdkVersion() > minRequiredV3SdkVersion) { throw new IllegalArgumentException( "The provided targeted signer configs do not cover the SDK range for V3 " + "support; either provide the original signer or ensure a signer " + "targets SDK version " + minRequiredV3SdkVersion); } List processedConfigs = new ArrayList<>(); // we have our configs, now touch them up to appropriately cover all SDK levels since APK // signature scheme v3 was introduced int currentMinSdk = Integer.MAX_VALUE; for (int i = rawConfigs.size() - 1; i >= 0; i--) { ApkSigningBlockUtils.SignerConfig config = rawConfigs.get(i); if (config.signatureAlgorithms == null) { // no valid algorithm was found for this signer, and we haven't yet covered all // platform versions, something's wrong String keyAlgorithm = config.certificates.get(0).getPublicKey().getAlgorithm(); throw new InvalidKeyException( "Unsupported key algorithm " + keyAlgorithm + " is " + "not supported for APK Signature Scheme v3 signing"); } if (i == rawConfigs.size() - 1) { // first go through the loop, config should support all future platform versions. // this assumes we don't deprecate support for signers in the future. If we do, // this needs to change config.maxSdkVersion = Integer.MAX_VALUE; } else { // If the previous signer was targeting a development release, then the current // signer's maxSdkVersion should overlap with the previous signer's minSdkVersion // to ensure the current signer applies to the production release. ApkSigningBlockUtils.SignerConfig prevSigner = processedConfigs.get( processedConfigs.size() - 1); if (prevSigner.signerTargetsDevRelease) { config.maxSdkVersion = prevSigner.minSdkVersion; } else { config.maxSdkVersion = currentMinSdk - 1; } } if (config.minSdkVersion == V3SchemeConstants.DEV_RELEASE) { // If the current signer is targeting the current development release, then set // the signer's minSdkVersion to the last production release and the flag indicating // this signer is targeting a dev release. config.minSdkVersion = V3SchemeConstants.PROD_RELEASE; config.signerTargetsDevRelease = true; } else if (config.minSdkVersion == 0) { config.minSdkVersion = getMinSdkFromV3SignatureAlgorithms( config.signatureAlgorithms); } // Truncate the lineage to the current signer if it is not the latest signer. X509Certificate signerCert = config.certificates.get(0); if (config.signingCertificateLineage != null && !config.signingCertificateLineage.isCertificateLatestInLineage(signerCert)) { config.signingCertificateLineage = config.signingCertificateLineage.getSubLineage( signerCert); } // we know that this config will be used, so add it to our result, order doesn't matter // at this point processedConfigs.add(config); currentMinSdk = config.minSdkVersion; if (config.signerTargetsDevRelease ? currentMinSdk < minRequiredV3SdkVersion : currentMinSdk <= minRequiredV3SdkVersion) { // this satisfies all we need, stop here break; } } if (currentMinSdk > AndroidSdkVersion.P && currentMinSdk > mMinSdkVersion) { // we can't cover all desired SDK versions, abort throw new InvalidKeyException( "Provided key algorithms not supported on all desired " + "Android SDK versions"); } return processedConfigs; } private List createV3SignerConfigs( boolean apkSigningBlockPaddingSupported) throws InvalidKeyException { return processV3Configs(createSigningBlockSignerConfigs(apkSigningBlockPaddingSupported, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3)); } private List processV31SignerConfigs( List v3SignerConfigs) { // The V3.1 signature scheme supports SDK targeted signing config, but this scheme should // only be used when a separate signing config exists for the V3.0 block. if (v3SignerConfigs.size() == 1) { return null; } // When there are multiple signing configs, the signer with the minimum SDK version should // be used for the V3.0 block, and all other signers should be used for the V3.1 block. int signerMinSdkVersion = v3SignerConfigs.stream().mapToInt( signer -> signer.minSdkVersion).min().orElse(AndroidSdkVersion.P); List v31SignerConfigs = new ArrayList<>(); Iterator v3SignerIterator = v3SignerConfigs.iterator(); while (v3SignerIterator.hasNext()) { ApkSigningBlockUtils.SignerConfig signerConfig = v3SignerIterator.next(); // If the signer config's minSdkVersion supports V3.1 and is not the min signer in the // list, then add it to the V3.1 signer configs and remove it from the V3.0 list. If // the signer is targeting the minSdkVersion as a development release, then it should // be included in V3.1 to allow the V3.0 block to target the production release of the // same SDK version. if (signerConfig.minSdkVersion >= MIN_SDK_WITH_V31_SUPPORT && (signerConfig.minSdkVersion > signerMinSdkVersion || (signerConfig.minSdkVersion >= signerMinSdkVersion && signerConfig.signerTargetsDevRelease))) { v31SignerConfigs.add(signerConfig); v3SignerIterator.remove(); } } return v31SignerConfigs; } private V4SchemeSigner.SignerConfig createV4SignerConfig() throws InvalidKeyException { List v4Configs = createSigningBlockSignerConfigs(true, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4); if (v4Configs.size() != 1) { // V4 uses signer config to connect back to v3. Use the same filtering logic. v4Configs = processV3Configs(v4Configs); } List v41configs = processV31SignerConfigs(v4Configs); return new V4SchemeSigner.SignerConfig(v4Configs, v41configs); } private ApkSigningBlockUtils.SignerConfig createSourceStampSignerConfig() throws InvalidKeyException { ApkSigningBlockUtils.SignerConfig config = createSigningBlockSignerConfig( mSourceStampSignerConfig, /* apkSigningBlockPaddingSupported= */ false, ApkSigningBlockUtils.VERSION_SOURCE_STAMP); if (mSourceStampSigningCertificateLineage != null) { config.signingCertificateLineage = mSourceStampSigningCertificateLineage.getSubLineage( config.certificates.get(0)); } return config; } private int getMinSdkFromV3SignatureAlgorithms(List algorithms) { int min = Integer.MAX_VALUE; for (SignatureAlgorithm algorithm : algorithms) { int current = algorithm.getMinSdkVersion(); if (current < min) { if (current <= mMinSdkVersion || current <= AndroidSdkVersion.P) { // this algorithm satisfies all of our needs, no need to keep looking return current; } else { min = current; } } } return min; } private List createSigningBlockSignerConfigs( boolean apkSigningBlockPaddingSupported, int schemeId) throws InvalidKeyException { List signerConfigs = new ArrayList<>(mSignerConfigs.size() + mTargetedSignerConfigs.size()); for (int i = 0; i < mSignerConfigs.size(); i++) { SignerConfig signerConfig = mSignerConfigs.get(i); signerConfigs.add( createSigningBlockSignerConfig( signerConfig, apkSigningBlockPaddingSupported, schemeId)); } if (schemeId >= VERSION_APK_SIGNATURE_SCHEME_V3) { for (int i = 0; i < mTargetedSignerConfigs.size(); i++) { SignerConfig signerConfig = mTargetedSignerConfigs.get(i); signerConfigs.add( createSigningBlockSignerConfig( signerConfig, apkSigningBlockPaddingSupported, schemeId)); } } return signerConfigs; } private ApkSigningBlockUtils.SignerConfig createSigningBlockSignerConfig( SignerConfig signerConfig, boolean apkSigningBlockPaddingSupported, int schemeId) throws InvalidKeyException { List certificates = signerConfig.getCertificates(); PublicKey publicKey = certificates.get(0).getPublicKey(); ApkSigningBlockUtils.SignerConfig newSignerConfig = new ApkSigningBlockUtils.SignerConfig(); newSignerConfig.keyConfig = signerConfig.getKeyConfig(); newSignerConfig.certificates = certificates; newSignerConfig.minSdkVersion = signerConfig.getMinSdkVersion(); newSignerConfig.signerTargetsDevRelease = signerConfig.getSignerTargetsDevRelease(); newSignerConfig.signingCertificateLineage = signerConfig.getSigningCertificateLineage(); switch (schemeId) { case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2: newSignerConfig.signatureAlgorithms = V2SchemeSigner.getSuggestedSignatureAlgorithms( publicKey, mMinSdkVersion, apkSigningBlockPaddingSupported && mVerityEnabled, signerConfig.getDeterministicDsaSigning()); break; case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3: try { newSignerConfig.signatureAlgorithms = V3SchemeSigner.getSuggestedSignatureAlgorithms( publicKey, mMinSdkVersion, apkSigningBlockPaddingSupported && mVerityEnabled, signerConfig.getDeterministicDsaSigning()); } catch (InvalidKeyException e) { // It is possible for a signer used for v1/v2 signing to not be allowed for use // with v3 signing. This is ok as long as there exists a more recent v3 signer // that covers all supported platform versions. Populate signatureAlgorithm // with null, it will be cleaned-up in a later step. newSignerConfig.signatureAlgorithms = null; } break; case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4: try { newSignerConfig.signatureAlgorithms = V4SchemeSigner.getSuggestedSignatureAlgorithms( publicKey, mMinSdkVersion, apkSigningBlockPaddingSupported, signerConfig.getDeterministicDsaSigning()); } catch (InvalidKeyException e) { // V4 is an optional signing schema, ok to proceed without. newSignerConfig.signatureAlgorithms = null; } break; case ApkSigningBlockUtils.VERSION_SOURCE_STAMP: newSignerConfig.signatureAlgorithms = Collections.singletonList( SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA256); break; default: throw new IllegalArgumentException("Unknown APK Signature Scheme ID requested"); } return newSignerConfig; } private boolean isDebuggable(String entryName) { return mDebuggableApkPermitted || !ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(entryName); } /** * Initializes DefaultApkSignerEngine with the existing MANIFEST.MF. This reads existing digests * from the MANIFEST.MF file (they are assumed correct) and stores them for the final signature * without recalculation. This step has a significant performance benefit in case of incremental * build. * *

This method extracts and stored computed digest for every entry that it would compute it * for in the {@link #outputJarEntry(String)} method * * @param manifestBytes raw representation of MANIFEST.MF file * @param entryNames a set of expected entries names * @return set of entry names which were processed by the engine during the initialization, a * subset of entryNames */ @Override @SuppressWarnings("AndroidJdkLibsChecker") public Set initWith(byte[] manifestBytes, Set entryNames) { V1SchemeVerifier.Result result = new V1SchemeVerifier.Result(); Pair> sections = V1SchemeVerifier.parseManifest(manifestBytes, entryNames, result); String alg = V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm); for (Map.Entry entry : sections.getSecond().entrySet()) { String entryName = entry.getKey(); if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entry.getKey()) && isDebuggable(entryName)) { V1SchemeVerifier.NamedDigest extractedDigest = null; Collection digestsToVerify = V1SchemeVerifier.getDigestsToVerify( entry.getValue(), "-Digest", mMinSdkVersion, Integer.MAX_VALUE); for (V1SchemeVerifier.NamedDigest digestToVerify : digestsToVerify) { if (digestToVerify.jcaDigestAlgorithm.equals(alg)) { extractedDigest = digestToVerify; break; } } if (extractedDigest != null) { mOutputJarEntryDigests.put(entryName, extractedDigest.digest); } } } return mOutputJarEntryDigests.keySet(); } @Override public void setExecutor(RunnablesExecutor executor) { mExecutor = executor; } @Override public void inputApkSigningBlock(DataSource apkSigningBlock) { checkNotClosed(); if ((apkSigningBlock == null) || (apkSigningBlock.size() == 0)) { return; } if (mOtherSignersSignaturesPreserved) { boolean schemeSignatureBlockPreserved = false; mPreservedSignatureBlocks = new ArrayList<>(); try { List> signatureBlocks = ApkSigningBlockUtils.getApkSignatureBlocks(apkSigningBlock); for (Pair signatureBlock : signatureBlocks) { if (signatureBlock.getSecond() == Constants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID) { // If a V2 signature block is found and the engine is configured to use V2 // then save any of the previous signers that are not part of the current // signing request. if (mV2SigningEnabled) { List, byte[]>> v2Signers = ApkSigningBlockUtils.getApkSignatureBlockSigners( signatureBlock.getFirst()); mPreservedV2Signers = new ArrayList<>(v2Signers.size()); for (Pair, byte[]> v2Signer : v2Signers) { if (!isConfiguredWithSigner(v2Signer.getFirst())) { mPreservedV2Signers.add(v2Signer.getSecond()); schemeSignatureBlockPreserved = true; } } } else { // else V2 signing is not enabled; save the entire signature block to be // added to the final APK signing block. mPreservedSignatureBlocks.add(signatureBlock); schemeSignatureBlockPreserved = true; } } else if (signatureBlock.getSecond() == Constants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID) { // Preserving other signers in the presence of a V3 signature block is only // supported if the engine is configured to resign the APK with the V3 // signature scheme, and the V3 signer in the signature block is the same // as the engine is configured to use. if (!mV3SigningEnabled) { throw new IllegalStateException( "Preserving an existing V3 signature is not supported"); } List, byte[]>> v3Signers = ApkSigningBlockUtils.getApkSignatureBlockSigners( signatureBlock.getFirst()); if (v3Signers.size() > 1) { throw new IllegalArgumentException( "The provided APK signing block contains " + v3Signers.size() + " V3 signers; the V3 signature scheme only supports" + " one signer"); } // If there is only a single V3 signer then ensure it is the signer // configured to sign the APK. if (v3Signers.size() == 1 && !isConfiguredWithSigner(v3Signers.get(0).getFirst())) { throw new IllegalStateException( "The V3 signature scheme only supports one signer; a request " + "was made to preserve the existing V3 signature, " + "but the engine is configured to sign with a " + "different signer"); } } else if (!DISCARDED_SIGNATURE_BLOCK_IDS.contains( signatureBlock.getSecond())) { mPreservedSignatureBlocks.add(signatureBlock); } } } catch (ApkFormatException | CertificateException | IOException e) { throw new IllegalArgumentException("Unable to parse the provided signing block", e); } // Signature scheme V3+ only support a single signer; if the engine is configured to // sign with V3+ then ensure no scheme signature blocks have been preserved. if (mV3SigningEnabled && schemeSignatureBlockPreserved) { throw new IllegalStateException( "Signature scheme V3+ only supports a single signer and cannot be " + "appended to the existing signature scheme blocks"); } return; } } /** * Returns whether the engine is configured to sign the APK with a signer using the specified * {@code signerCerts}. */ private boolean isConfiguredWithSigner(List signerCerts) { for (SignerConfig signerConfig : mSignerConfigs) { if (signerCerts.containsAll(signerConfig.getCertificates())) { return true; } } return false; } @Override public InputJarEntryInstructions inputJarEntry(String entryName) { checkNotClosed(); InputJarEntryInstructions.OutputPolicy outputPolicy = getInputJarEntryOutputPolicy(entryName); switch (outputPolicy) { case SKIP: return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.SKIP); case OUTPUT: return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.OUTPUT); case OUTPUT_BY_ENGINE: if (V1SchemeConstants.MANIFEST_ENTRY_NAME.equals(entryName)) { // We copy the main section of the JAR manifest from input to output. Thus, this // invalidates v1 signature and we need to see the entry's data. mInputJarManifestEntryDataRequest = new GetJarEntryDataRequest(entryName); return new InputJarEntryInstructions( InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE, mInputJarManifestEntryDataRequest); } return new InputJarEntryInstructions( InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE); default: throw new RuntimeException("Unsupported output policy: " + outputPolicy); } } @Override public InspectJarEntryRequest outputJarEntry(String entryName) { checkNotClosed(); invalidateV2Signature(); if (!isDebuggable(entryName)) { forgetOutputApkDebuggableStatus(); } if (!mV1SigningEnabled) { // No need to inspect JAR entries when v1 signing is not enabled. if (!isDebuggable(entryName)) { // To reject debuggable APKs we need to inspect the APK's AndroidManifest.xml to // check whether it declares that the APK is debuggable mOutputAndroidManifestEntryDataRequest = new GetJarEntryDataRequest(entryName); return mOutputAndroidManifestEntryDataRequest; } return null; } // v1 signing is enabled if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) { // This entry is covered by v1 signature. We thus need to inspect the entry's data to // compute its digest(s) for v1 signature. // TODO: Handle the case where other signer's v1 signatures are present and need to be // preserved. In that scenario we can't modify MANIFEST.MF and add/remove JAR entries // covered by v1 signature. invalidateV1Signature(); GetJarEntryDataDigestRequest dataDigestRequest = new GetJarEntryDataDigestRequest( entryName, V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm)); mOutputJarEntryDigestRequests.put(entryName, dataDigestRequest); mOutputJarEntryDigests.remove(entryName); if ((!mDebuggableApkPermitted) && (ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(entryName))) { // To reject debuggable APKs we need to inspect the APK's AndroidManifest.xml to // check whether it declares that the APK is debuggable mOutputAndroidManifestEntryDataRequest = new GetJarEntryDataRequest(entryName); return new CompoundInspectJarEntryRequest( entryName, mOutputAndroidManifestEntryDataRequest, dataDigestRequest); } return dataDigestRequest; } if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) { // This entry is part of v1 signature generated by this engine. We need to check whether // the entry's data is as output by the engine. invalidateV1Signature(); GetJarEntryDataRequest dataRequest; if (V1SchemeConstants.MANIFEST_ENTRY_NAME.equals(entryName)) { dataRequest = new GetJarEntryDataRequest(entryName); mInputJarManifestEntryDataRequest = dataRequest; } else { // If this entry is part of v1 signature which has been emitted by this engine, // check whether the output entry's data matches what the engine emitted. dataRequest = (mEmittedSignatureJarEntryData.containsKey(entryName)) ? new GetJarEntryDataRequest(entryName) : null; } if (dataRequest != null) { mOutputSignatureJarEntryDataRequests.put(entryName, dataRequest); } return dataRequest; } // This entry is not covered by v1 signature and isn't part of v1 signature. return null; } @Override public InputJarEntryInstructions.OutputPolicy inputJarEntryRemoved(String entryName) { checkNotClosed(); return getInputJarEntryOutputPolicy(entryName); } @Override public void outputJarEntryRemoved(String entryName) { checkNotClosed(); invalidateV2Signature(); if (!mV1SigningEnabled) { return; } if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) { // This entry is covered by v1 signature. invalidateV1Signature(); mOutputJarEntryDigests.remove(entryName); mOutputJarEntryDigestRequests.remove(entryName); mOutputSignatureJarEntryDataRequests.remove(entryName); return; } if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) { // This entry is part of the v1 signature generated by this engine. invalidateV1Signature(); return; } } @Override public OutputJarSignatureRequest outputJarEntries() throws ApkFormatException, InvalidKeyException, SignatureException, NoSuchAlgorithmException { checkNotClosed(); if (!mV1SignaturePending) { return null; } if ((mInputJarManifestEntryDataRequest != null) && (!mInputJarManifestEntryDataRequest.isDone())) { throw new IllegalStateException( "Still waiting to inspect input APK's " + mInputJarManifestEntryDataRequest.getEntryName()); } for (GetJarEntryDataDigestRequest digestRequest : mOutputJarEntryDigestRequests.values()) { String entryName = digestRequest.getEntryName(); if (!digestRequest.isDone()) { throw new IllegalStateException( "Still waiting to inspect output APK's " + entryName); } mOutputJarEntryDigests.put(entryName, digestRequest.getDigest()); } if (isEligibleForSourceStamp()) { MessageDigest messageDigest = MessageDigest.getInstance( V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm)); messageDigest.update(generateSourceStampCertificateDigest()); mOutputJarEntryDigests.put( SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME, messageDigest.digest()); } mOutputJarEntryDigestRequests.clear(); for (GetJarEntryDataRequest dataRequest : mOutputSignatureJarEntryDataRequests.values()) { if (!dataRequest.isDone()) { throw new IllegalStateException( "Still waiting to inspect output APK's " + dataRequest.getEntryName()); } } List apkSigningSchemeIds = new ArrayList<>(); if (mV2SigningEnabled) { apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); } if (mV3SigningEnabled) { apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); } byte[] inputJarManifest = (mInputJarManifestEntryDataRequest != null) ? mInputJarManifestEntryDataRequest.getData() : null; if (isEligibleForSourceStamp()) { inputJarManifest = V1SchemeSigner.generateManifestFile( mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest) .contents; } // Check whether the most recently used signature (if present) is still fine. checkOutputApkNotDebuggableIfDebuggableMustBeRejected(); List> signatureZipEntries; if ((mAddV1SignatureRequest == null) || (!mAddV1SignatureRequest.isDone())) { try { signatureZipEntries = V1SchemeSigner.sign( mV1SignerConfigs, mV1ContentDigestAlgorithm, mOutputJarEntryDigests, apkSigningSchemeIds, inputJarManifest, mCreatedBy); } catch (CertificateException e) { throw new SignatureException("Failed to generate v1 signature", e); } } else { V1SchemeSigner.OutputManifestFile newManifest = V1SchemeSigner.generateManifestFile( mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest); byte[] emittedSignatureManifest = mEmittedSignatureJarEntryData.get(V1SchemeConstants.MANIFEST_ENTRY_NAME); if (!Arrays.equals(newManifest.contents, emittedSignatureManifest)) { // Emitted v1 signature is no longer valid. try { signatureZipEntries = V1SchemeSigner.signManifest( mV1SignerConfigs, mV1ContentDigestAlgorithm, apkSigningSchemeIds, mCreatedBy, newManifest); } catch (CertificateException e) { throw new SignatureException("Failed to generate v1 signature", e); } } else { // Emitted v1 signature is still valid. Check whether the signature is there in the // output. signatureZipEntries = new ArrayList<>(); for (Map.Entry expectedOutputEntry : mEmittedSignatureJarEntryData.entrySet()) { String entryName = expectedOutputEntry.getKey(); byte[] expectedData = expectedOutputEntry.getValue(); GetJarEntryDataRequest actualDataRequest = mOutputSignatureJarEntryDataRequests.get(entryName); if (actualDataRequest == null) { // This signature entry hasn't been output. signatureZipEntries.add(Pair.of(entryName, expectedData)); continue; } byte[] actualData = actualDataRequest.getData(); if (!Arrays.equals(expectedData, actualData)) { signatureZipEntries.add(Pair.of(entryName, expectedData)); } } if (signatureZipEntries.isEmpty()) { // v1 signature in the output is valid return null; } // v1 signature in the output is not valid. } } if (signatureZipEntries.isEmpty()) { // v1 signature in the output is valid mV1SignaturePending = false; return null; } List sigEntries = new ArrayList<>(signatureZipEntries.size()); for (Pair entry : signatureZipEntries) { String entryName = entry.getFirst(); byte[] entryData = entry.getSecond(); sigEntries.add(new OutputJarSignatureRequest.JarEntry(entryName, entryData)); mEmittedSignatureJarEntryData.put(entryName, entryData); } mAddV1SignatureRequest = new OutputJarSignatureRequestImpl(sigEntries); return mAddV1SignatureRequest; } @Deprecated @Override public OutputApkSigningBlockRequest outputZipSections( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd) throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException { return outputZipSectionsInternal(zipEntries, zipCentralDirectory, zipEocd, false); } @Override public OutputApkSigningBlockRequest2 outputZipSections2( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd) throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException { return outputZipSectionsInternal(zipEntries, zipCentralDirectory, zipEocd, true); } private OutputApkSigningBlockRequestImpl outputZipSectionsInternal( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd, boolean apkSigningBlockPaddingSupported) throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException { checkNotClosed(); checkV1SigningDoneIfEnabled(); if (!mV2SigningEnabled && !mV3SigningEnabled && !isEligibleForSourceStamp()) { return null; } checkOutputApkNotDebuggableIfDebuggableMustBeRejected(); // adjust to proper padding Pair paddingPair = ApkSigningBlockUtils.generateApkSigningBlockPadding( zipEntries, apkSigningBlockPaddingSupported); DataSource beforeCentralDir = paddingPair.getFirst(); int padSizeBeforeApkSigningBlock = paddingPair.getSecond(); DataSource eocd = ApkSigningBlockUtils.copyWithModifiedCDOffset(beforeCentralDir, zipEocd); List> signingSchemeBlocks = new ArrayList<>(); ApkSigningBlockUtils.SigningSchemeBlockAndDigests v2SigningSchemeBlockAndDigests = null; ApkSigningBlockUtils.SigningSchemeBlockAndDigests v3SigningSchemeBlockAndDigests = null; // If the engine is configured to preserve previous signature blocks and any were found in // the existing APK signing block then add them to the list to be used to generate the // new APK signing block. if (mOtherSignersSignaturesPreserved && mPreservedSignatureBlocks != null && !mPreservedSignatureBlocks.isEmpty()) { signingSchemeBlocks.addAll(mPreservedSignatureBlocks); } // create APK Signature Scheme V2 Signature if requested if (mV2SigningEnabled) { invalidateV2Signature(); List v2SignerConfigs = createV2SignerConfigs(apkSigningBlockPaddingSupported); v2SigningSchemeBlockAndDigests = V2SchemeSigner.generateApkSignatureSchemeV2Block( mExecutor, beforeCentralDir, zipCentralDirectory, eocd, v2SignerConfigs, mV3SigningEnabled, mOtherSignersSignaturesPreserved ? mPreservedV2Signers : null); signingSchemeBlocks.add(v2SigningSchemeBlockAndDigests.signingSchemeBlock); } if (mV3SigningEnabled) { invalidateV3Signature(); List v3SignerConfigs = createV3SignerConfigs(apkSigningBlockPaddingSupported); List v31SignerConfigs = processV31SignerConfigs( v3SignerConfigs); if (v31SignerConfigs != null && v31SignerConfigs.size() > 0) { ApkSigningBlockUtils.SigningSchemeBlockAndDigests v31SigningSchemeBlockAndDigests = new V3SchemeSigner.Builder(beforeCentralDir, zipCentralDirectory, eocd, v31SignerConfigs) .setRunnablesExecutor(mExecutor) .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID) .build() .generateApkSignatureSchemeV3BlockAndDigests(); signingSchemeBlocks.add(v31SigningSchemeBlockAndDigests.signingSchemeBlock); } V3SchemeSigner.Builder builder = new V3SchemeSigner.Builder(beforeCentralDir, zipCentralDirectory, eocd, v3SignerConfigs) .setRunnablesExecutor(mExecutor) .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID); if (v31SignerConfigs != null && !v31SignerConfigs.isEmpty()) { // The V3.1 stripping protection writes the minimum SDK version from the targeted // signers as an additional attribute in the V3.0 signing block. int minSdkVersionForV31 = v31SignerConfigs.stream().mapToInt( signer -> signer.minSdkVersion).min().orElse(MIN_SDK_WITH_V31_SUPPORT); builder.setMinSdkVersionForV31(minSdkVersionForV31); } v3SigningSchemeBlockAndDigests = builder.build().generateApkSignatureSchemeV3BlockAndDigests(); signingSchemeBlocks.add(v3SigningSchemeBlockAndDigests.signingSchemeBlock); } if (isEligibleForSourceStamp()) { ApkSigningBlockUtils.SignerConfig sourceStampSignerConfig = createSourceStampSignerConfig(); Map> signatureSchemeDigestInfos = new HashMap<>(); if (mV3SigningEnabled) { signatureSchemeDigestInfos.put( VERSION_APK_SIGNATURE_SCHEME_V3, v3SigningSchemeBlockAndDigests.digestInfo); } if (mV2SigningEnabled) { signatureSchemeDigestInfos.put( VERSION_APK_SIGNATURE_SCHEME_V2, v2SigningSchemeBlockAndDigests.digestInfo); } if (mV1SigningEnabled) { Map v1SigningSchemeDigests = new HashMap<>(); try { // Jar signing related variables must have been already populated at this point // if V1 signing is enabled since it is happening before computations on the APK // signing block (V2/V3/V4/SourceStamp signing). byte[] inputJarManifest = (mInputJarManifestEntryDataRequest != null) ? mInputJarManifestEntryDataRequest.getData() : null; byte[] jarManifest = V1SchemeSigner.generateManifestFile( mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest) .contents; // The digest of the jar manifest does not need to be computed in chunks due to // the small size of the manifest. v1SigningSchemeDigests.put( ContentDigestAlgorithm.SHA256, computeSha256DigestBytes(jarManifest)); } catch (ApkFormatException e) { throw new RuntimeException("Failed to generate manifest file", e); } signatureSchemeDigestInfos.put( VERSION_JAR_SIGNATURE_SCHEME, v1SigningSchemeDigests); } V2SourceStampSigner v2SourceStampSigner = new V2SourceStampSigner.Builder(sourceStampSignerConfig, signatureSchemeDigestInfos) .setSourceStampTimestampEnabled(mSourceStampTimestampEnabled) .build(); signingSchemeBlocks.add(v2SourceStampSigner.generateSourceStampBlock()); } // create APK Signing Block with v2 and/or v3 and/or SourceStamp blocks byte[] apkSigningBlock = ApkSigningBlockUtils.generateApkSigningBlock(signingSchemeBlocks); mAddSigningBlockRequest = new OutputApkSigningBlockRequestImpl(apkSigningBlock, padSizeBeforeApkSigningBlock); return mAddSigningBlockRequest; } @Override public void outputDone() { checkNotClosed(); checkV1SigningDoneIfEnabled(); checkSigningBlockDoneIfEnabled(); } @Override public void signV4(DataSource dataSource, File outputFile, boolean ignoreFailures) throws SignatureException { if (outputFile == null) { if (ignoreFailures) { return; } throw new SignatureException("Missing V4 output file."); } try { V4SchemeSigner.SignerConfig v4SignerConfig = createV4SignerConfig(); V4SchemeSigner.generateV4Signature(dataSource, v4SignerConfig, outputFile); } catch (InvalidKeyException | IOException | NoSuchAlgorithmException e) { if (ignoreFailures) { return; } throw new SignatureException("V4 signing failed", e); } } /** For external use only to generate V4 & tree separately. */ public byte[] produceV4Signature(DataSource dataSource, OutputStream sigOutput) throws SignatureException { if (sigOutput == null) { throw new SignatureException("Missing V4 output streams."); } try { V4SchemeSigner.SignerConfig v4SignerConfig = createV4SignerConfig(); Pair pair = V4SchemeSigner.generateV4Signature(dataSource, v4SignerConfig); pair.getFirst().writeTo(sigOutput); return pair.getSecond(); } catch (InvalidKeyException | IOException | NoSuchAlgorithmException e) { throw new SignatureException("V4 signing failed", e); } } @Override public boolean isEligibleForSourceStamp() { return mSourceStampSignerConfig != null && (mV2SigningEnabled || mV3SigningEnabled || mV1SigningEnabled); } @Override public byte[] generateSourceStampCertificateDigest() throws SignatureException { if (mSourceStampSignerConfig.getCertificates().isEmpty()) { throw new SignatureException("No certificates configured for stamp"); } try { return computeSha256DigestBytes( mSourceStampSignerConfig.getCertificates().get(0).getEncoded()); } catch (CertificateEncodingException e) { throw new SignatureException("Failed to encode source stamp certificate", e); } } @Override public void close() { mClosed = true; mAddV1SignatureRequest = null; mInputJarManifestEntryDataRequest = null; mOutputAndroidManifestEntryDataRequest = null; mDebuggable = null; mOutputJarEntryDigestRequests.clear(); mOutputJarEntryDigests.clear(); mEmittedSignatureJarEntryData.clear(); mOutputSignatureJarEntryDataRequests.clear(); mAddSigningBlockRequest = null; } private void invalidateV1Signature() { if (mV1SigningEnabled) { mV1SignaturePending = true; } invalidateV2Signature(); } private void invalidateV2Signature() { if (mV2SigningEnabled) { mV2SignaturePending = true; mAddSigningBlockRequest = null; } } private void invalidateV3Signature() { if (mV3SigningEnabled) { mV3SignaturePending = true; mAddSigningBlockRequest = null; } } private void checkNotClosed() { if (mClosed) { throw new IllegalStateException("Engine closed"); } } private void checkV1SigningDoneIfEnabled() { if (!mV1SignaturePending) { return; } if (mAddV1SignatureRequest == null) { throw new IllegalStateException( "v1 signature (JAR signature) not yet generated. Skipped outputJarEntries()?"); } if (!mAddV1SignatureRequest.isDone()) { throw new IllegalStateException( "v1 signature (JAR signature) addition requested by outputJarEntries() hasn't" + " been fulfilled"); } for (Map.Entry expectedOutputEntry : mEmittedSignatureJarEntryData.entrySet()) { String entryName = expectedOutputEntry.getKey(); byte[] expectedData = expectedOutputEntry.getValue(); GetJarEntryDataRequest actualDataRequest = mOutputSignatureJarEntryDataRequests.get(entryName); if (actualDataRequest == null) { throw new IllegalStateException( "APK entry " + entryName + " not yet output despite this having been" + " requested"); } else if (!actualDataRequest.isDone()) { throw new IllegalStateException( "Still waiting to inspect output APK's " + entryName); } byte[] actualData = actualDataRequest.getData(); if (!Arrays.equals(expectedData, actualData)) { throw new IllegalStateException( "Output APK entry " + entryName + " data differs from what was requested"); } } mV1SignaturePending = false; } private void checkSigningBlockDoneIfEnabled() { if (!mV2SignaturePending && !mV3SignaturePending) { return; } if (mAddSigningBlockRequest == null) { throw new IllegalStateException( "Signed APK Signing BLock not yet generated. Skipped outputZipSections()?"); } if (!mAddSigningBlockRequest.isDone()) { throw new IllegalStateException( "APK Signing Block addition of signature(s) requested by" + " outputZipSections() hasn't been fulfilled yet"); } mAddSigningBlockRequest = null; mV2SignaturePending = false; mV3SignaturePending = false; } private void checkOutputApkNotDebuggableIfDebuggableMustBeRejected() throws SignatureException { if (mDebuggableApkPermitted) { return; } try { if (isOutputApkDebuggable()) { throw new SignatureException( "APK is debuggable (see android:debuggable attribute) and this engine is" + " configured to refuse to sign debuggable APKs"); } } catch (ApkFormatException e) { throw new SignatureException("Failed to determine whether the APK is debuggable", e); } } /** * Returns whether the output APK is debuggable according to its {@code android:debuggable} * declaration. */ private boolean isOutputApkDebuggable() throws ApkFormatException { if (mDebuggable != null) { return mDebuggable; } if (mOutputAndroidManifestEntryDataRequest == null) { throw new IllegalStateException( "Cannot determine debuggable status of output APK because " + ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME + " entry contents have not yet been requested"); } if (!mOutputAndroidManifestEntryDataRequest.isDone()) { throw new IllegalStateException( "Still waiting to inspect output APK's " + mOutputAndroidManifestEntryDataRequest.getEntryName()); } mDebuggable = ApkUtils.getDebuggableFromBinaryAndroidManifest( ByteBuffer.wrap(mOutputAndroidManifestEntryDataRequest.getData())); return mDebuggable; } private void forgetOutputApkDebuggableStatus() { mDebuggable = null; } /** Returns the output policy for the provided input JAR entry. */ private InputJarEntryInstructions.OutputPolicy getInputJarEntryOutputPolicy(String entryName) { if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) { return InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE; } if ((mOtherSignersSignaturesPreserved) || (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName))) { return InputJarEntryInstructions.OutputPolicy.OUTPUT; } return InputJarEntryInstructions.OutputPolicy.SKIP; } private static class OutputJarSignatureRequestImpl implements OutputJarSignatureRequest { private final List mAdditionalJarEntries; private volatile boolean mDone; private OutputJarSignatureRequestImpl(List additionalZipEntries) { mAdditionalJarEntries = Collections.unmodifiableList(new ArrayList<>(additionalZipEntries)); } @Override public List getAdditionalJarEntries() { return mAdditionalJarEntries; } @Override public void done() { mDone = true; } private boolean isDone() { return mDone; } } @SuppressWarnings("deprecation") private static class OutputApkSigningBlockRequestImpl implements OutputApkSigningBlockRequest, OutputApkSigningBlockRequest2 { private final byte[] mApkSigningBlock; private final int mPaddingBeforeApkSigningBlock; private volatile boolean mDone; private OutputApkSigningBlockRequestImpl(byte[] apkSigingBlock, int paddingBefore) { mApkSigningBlock = apkSigingBlock.clone(); mPaddingBeforeApkSigningBlock = paddingBefore; } @Override public byte[] getApkSigningBlock() { return mApkSigningBlock.clone(); } @Override public void done() { mDone = true; } private boolean isDone() { return mDone; } @Override public int getPaddingSizeBeforeApkSigningBlock() { return mPaddingBeforeApkSigningBlock; } } /** JAR entry inspection request which obtain the entry's uncompressed data. */ private static class GetJarEntryDataRequest implements InspectJarEntryRequest { private final String mEntryName; private final Object mLock = new Object(); private boolean mDone; private DataSink mDataSink; private ByteArrayOutputStream mDataSinkBuf; private GetJarEntryDataRequest(String entryName) { mEntryName = entryName; } @Override public String getEntryName() { return mEntryName; } @Override public DataSink getDataSink() { synchronized (mLock) { checkNotDone(); if (mDataSinkBuf == null) { mDataSinkBuf = new ByteArrayOutputStream(); } if (mDataSink == null) { mDataSink = DataSinks.asDataSink(mDataSinkBuf); } return mDataSink; } } @Override public void done() { synchronized (mLock) { if (mDone) { return; } mDone = true; } } private boolean isDone() { synchronized (mLock) { return mDone; } } private void checkNotDone() throws IllegalStateException { synchronized (mLock) { if (mDone) { throw new IllegalStateException("Already done"); } } } private byte[] getData() { synchronized (mLock) { if (!mDone) { throw new IllegalStateException("Not yet done"); } return (mDataSinkBuf != null) ? mDataSinkBuf.toByteArray() : new byte[0]; } } } /** JAR entry inspection request which obtains the digest of the entry's uncompressed data. */ private static class GetJarEntryDataDigestRequest implements InspectJarEntryRequest { private final String mEntryName; private final String mJcaDigestAlgorithm; private final Object mLock = new Object(); private boolean mDone; private DataSink mDataSink; private MessageDigest mMessageDigest; private byte[] mDigest; private GetJarEntryDataDigestRequest(String entryName, String jcaDigestAlgorithm) { mEntryName = entryName; mJcaDigestAlgorithm = jcaDigestAlgorithm; } @Override public String getEntryName() { return mEntryName; } @Override public DataSink getDataSink() { synchronized (mLock) { checkNotDone(); if (mDataSink == null) { mDataSink = DataSinks.asDataSink(getMessageDigest()); } return mDataSink; } } private MessageDigest getMessageDigest() { synchronized (mLock) { if (mMessageDigest == null) { try { mMessageDigest = MessageDigest.getInstance(mJcaDigestAlgorithm); } catch (NoSuchAlgorithmException e) { throw new RuntimeException( mJcaDigestAlgorithm + " MessageDigest not available", e); } } return mMessageDigest; } } @Override public void done() { synchronized (mLock) { if (mDone) { return; } mDone = true; mDigest = getMessageDigest().digest(); mMessageDigest = null; mDataSink = null; } } private boolean isDone() { synchronized (mLock) { return mDone; } } private void checkNotDone() throws IllegalStateException { synchronized (mLock) { if (mDone) { throw new IllegalStateException("Already done"); } } } private byte[] getDigest() { synchronized (mLock) { if (!mDone) { throw new IllegalStateException("Not yet done"); } return mDigest.clone(); } } } /** JAR entry inspection request which transparently satisfies multiple such requests. */ private static class CompoundInspectJarEntryRequest implements InspectJarEntryRequest { private final String mEntryName; private final InspectJarEntryRequest[] mRequests; private final Object mLock = new Object(); private DataSink mSink; private CompoundInspectJarEntryRequest( String entryName, InspectJarEntryRequest... requests) { mEntryName = entryName; mRequests = requests; } @Override public String getEntryName() { return mEntryName; } @Override public DataSink getDataSink() { synchronized (mLock) { if (mSink == null) { DataSink[] sinks = new DataSink[mRequests.length]; for (int i = 0; i < sinks.length; i++) { sinks[i] = mRequests[i].getDataSink(); } mSink = new TeeDataSink(sinks); } return mSink; } } @Override public void done() { for (InspectJarEntryRequest request : mRequests) { request.done(); } } } /** * Configuration of a signer. * *

Use {@link Builder} to obtain configuration instances. */ public static class SignerConfig { private final String mName; private final KeyConfig mKeyConfig; private final List mCertificates; private final boolean mDeterministicDsaSigning; private final int mMinSdkVersion; private final boolean mSignerTargetsDevRelease; private final SigningCertificateLineage mSigningCertificateLineage; private SignerConfig(Builder builder) { mName = builder.mName; mKeyConfig = builder.mKeyConfig; mCertificates = Collections.unmodifiableList(new ArrayList<>(builder.mCertificates)); mDeterministicDsaSigning = builder.mDeterministicDsaSigning; mMinSdkVersion = builder.mMinSdkVersion; mSignerTargetsDevRelease = builder.mSignerTargetsDevRelease; mSigningCertificateLineage = builder.mSigningCertificateLineage; } /** Returns the name of this signer. */ public String getName() { return mName; } /** * Returns the signing key of this signer. * * @deprecated Use {@link #getKeyConfig()} instead of accessing a {@link PrivateKey} * directly. If the user of ApkSigner is signing with a KMS instead of JCA, this method * will return null. */ @Deprecated public PrivateKey getPrivateKey() { return mKeyConfig.match(jca -> jca.privateKey, kms -> null); } public KeyConfig getKeyConfig() { return mKeyConfig; } /** * Returns the certificate(s) of this signer. The first certificate's public key corresponds * to this signer's private key. */ public List getCertificates() { return mCertificates; } /** * If this signer is a DSA signer, whether or not the signing is done deterministically. */ public boolean getDeterministicDsaSigning() { return mDeterministicDsaSigning; } /** Returns the minimum SDK version for which this signer should be used. */ public int getMinSdkVersion() { return mMinSdkVersion; } /** Returns whether this signer targets a development release. */ public boolean getSignerTargetsDevRelease() { return mSignerTargetsDevRelease; } /** Returns the {@link SigningCertificateLineage} for this signer. */ public SigningCertificateLineage getSigningCertificateLineage() { return mSigningCertificateLineage; } /** Builder of {@link SignerConfig} instances. */ public static class Builder { private final String mName; private final KeyConfig mKeyConfig; private final List mCertificates; private final boolean mDeterministicDsaSigning; private int mMinSdkVersion; private boolean mSignerTargetsDevRelease; private SigningCertificateLineage mSigningCertificateLineage; /** * Constructs a new {@code Builder}. * * @deprecated use {@link #Builder(String, KeyConfig, List)} instead * @param name signer's name. The name is reflected in the name of files comprising the * JAR signature of the APK. * @param privateKey signing key * @param certificates list of one or more X.509 certificates. The subject public key of * the first certificate must correspond to the {@code privateKey}. */ @Deprecated public Builder(String name, PrivateKey privateKey, List certificates) { this(name, privateKey, certificates, false); } /** * Constructs a new {@code Builder}. * * @deprecated use {@link #Builder(String, KeyConfig, List, boolean)} instead * @param name signer's name. The name is reflected in the name of files comprising the * JAR signature of the APK. * @param privateKey signing key * @param certificates list of one or more X.509 certificates. The subject public key of * the first certificate must correspond to the {@code privateKey}. * @param deterministicDsaSigning When signing using DSA, whether or not the * deterministic signing algorithm variant (RFC6979) should be used. */ @Deprecated public Builder( String name, PrivateKey privateKey, List certificates, boolean deterministicDsaSigning) { if (name.isEmpty()) { throw new IllegalArgumentException("Empty name"); } mName = name; mKeyConfig = new KeyConfig.Jca(privateKey); mCertificates = new ArrayList<>(certificates); mDeterministicDsaSigning = deterministicDsaSigning; } /** * Constructs a new {@code Builder}. * * @param name signer's name. The name is reflected in the name of files comprising the * JAR signature of the APK. * @param keyConfig signing key configuration. * @param certificates list of one or more X.509 certificates. The subject public key of * the first certificate must correspond to the {@code privateKey}. */ public Builder(String name, KeyConfig keyConfig, List certificates) { this(name, keyConfig, certificates, false); } /** * Constructs a new {@code Builder}. * * @param name signer's name. The name is reflected in the name of files comprising the * JAR signature of the APK. * @param keyConfig signing key configuration * @param certificates list of one or more X.509 certificates. The subject public key of * the first certificate must correspond to the {@code privateKey}. * @param deterministicDsaSigning When signing using DSA, whether or not the * deterministic signing algorithm variant (RFC6979) should be used. */ public Builder( String name, KeyConfig keyConfig, List certificates, boolean deterministicDsaSigning) { if (name.isEmpty()) { throw new IllegalArgumentException("Empty name"); } mName = name; mKeyConfig = keyConfig; mCertificates = new ArrayList<>(certificates); mDeterministicDsaSigning = deterministicDsaSigning; } /** @see #setLineageForMinSdkVersion(SigningCertificateLineage, int) */ public Builder setMinSdkVersion(int minSdkVersion) { return setLineageForMinSdkVersion(null, minSdkVersion); } /** * Sets the specified {@code minSdkVersion} as the minimum Android platform version * (API level) for which the provided {@code lineage} (where applicable) should be used * to produce the APK's signature. This method is useful if callers want to specify a * particular rotated signer or lineage with restricted capabilities for later * platform releases. * *

Note:>The V1 and V2 signature schemes do not support key rotation and * signing lineages with capabilities; only an app's original signer(s) can be used for * the V1 and V2 signature blocks. Because of this, only a value of {@code * minSdkVersion} >= 28 (Android P) where support for the V3 signature scheme was * introduced can be specified. * *

Note:Due to limitations with platform targeting in the V3.0 signature * scheme, specifying a {@code minSdkVersion} value <= 32 (Android Sv2) will result in * the current {@code SignerConfig} being used in the V3.0 signing block and applied to * Android P through at least Sv2 (and later depending on the {@code minSdkVersion} for * subsequent {@code SignerConfig} instances). Because of this, only a single {@code * SignerConfig} can be instantiated with a minimum SDK version <= 32. * * @param lineage the {@code SigningCertificateLineage} to target the specified {@code * minSdkVersion} * @param minSdkVersion the minimum SDK version for which this {@code SignerConfig} * should be used * @return this {@code Builder} instance * * @throws IllegalArgumentException if the provided {@code minSdkVersion} < 28 or the * certificate provided in the constructor is not in the specified {@code lineage}. */ public Builder setLineageForMinSdkVersion(SigningCertificateLineage lineage, int minSdkVersion) { if (minSdkVersion < AndroidSdkVersion.P) { throw new IllegalArgumentException( "SDK targeted signing config is only supported with the V3 signature " + "scheme on Android P (SDK version " + AndroidSdkVersion.P + ") and later"); } if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT) { minSdkVersion = AndroidSdkVersion.P; } mMinSdkVersion = minSdkVersion; // If a lineage is provided, ensure the signing certificate for this signer is in // the lineage; in the case of multiple signing certificates, the first is always // used in the lineage. if (lineage != null && !lineage.isCertificateInLineage(mCertificates.get(0))) { throw new IllegalArgumentException( "The provided lineage does not contain the signing certificate, " + mCertificates.get(0).getSubjectDN() + ", for this SignerConfig"); } mSigningCertificateLineage = lineage; return this; } /** * Sets whether this signer's min SDK version is intended to target a development * release. * *

This is primarily required for a signer testing on a platform's development * release; however, it is recommended that signer's use the latest development SDK * version instead of explicitly specifying this boolean. This class will properly * handle an SDK that is currently targeting a development release and will use the * finalized SDK version on release. */ private Builder setSignerTargetsDevRelease(boolean signerTargetsDevRelease) { if (signerTargetsDevRelease && mMinSdkVersion < MIN_SDK_WITH_V31_SUPPORT) { throw new IllegalArgumentException( "Rotation can only target a development release for signers targeting " + MIN_SDK_WITH_V31_SUPPORT + " or later"); } mSignerTargetsDevRelease = signerTargetsDevRelease; return this; } /** * Returns a new {@code SignerConfig} instance configured based on the configuration of * this builder. */ public SignerConfig build() { return new SignerConfig(this); } } } /** Builder of {@link DefaultApkSignerEngine} instances. */ public static class Builder { private List mSignerConfigs; private List mTargetedSignerConfigs; private SignerConfig mStampSignerConfig; private SigningCertificateLineage mSourceStampSigningCertificateLineage; private boolean mSourceStampTimestampEnabled = true; private final int mMinSdkVersion; private boolean mV1SigningEnabled = true; private boolean mV2SigningEnabled = true; private boolean mV3SigningEnabled = true; private int mRotationMinSdkVersion = V3SchemeConstants.DEFAULT_ROTATION_MIN_SDK_VERSION; private boolean mRotationTargetsDevRelease = false; private boolean mVerityEnabled = false; private boolean mDebuggableApkPermitted = true; private boolean mOtherSignersSignaturesPreserved; private String mCreatedBy = "1.0 (Android)"; private SigningCertificateLineage mSigningCertificateLineage; // APK Signature Scheme v3 only supports a single signing certificate, so to move to v3 // signing by default, but not require prior clients to update to explicitly disable v3 // signing for multiple signers, we modify the mV3SigningEnabled depending on the provided // inputs (multiple signers and mSigningCertificateLineage in particular). Maintain two // extra variables to record whether or not mV3SigningEnabled has been set directly by a // client and so should override the default behavior. private boolean mV3SigningExplicitlyDisabled = false; private boolean mV3SigningExplicitlyEnabled = false; /** * Constructs a new {@code Builder}. * * @param signerConfigs information about signers with which the APK will be signed. At * least one signer configuration must be provided. * @param minSdkVersion API Level of the oldest Android platform on which the APK is * supposed to be installed. See {@code minSdkVersion} attribute in the APK's {@code * AndroidManifest.xml}. The higher the version, the stronger signing features will be * enabled. */ public Builder(List signerConfigs, int minSdkVersion) { if (signerConfigs.isEmpty()) { throw new IllegalArgumentException("At least one signer config must be provided"); } if (signerConfigs.size() > 1) { // APK Signature Scheme v3 only supports single signer, unless a // SigningCertificateLineage is provided, in which case this will be reset to true, // since we don't yet have a v4 scheme about which to worry mV3SigningEnabled = false; } mSignerConfigs = new ArrayList<>(signerConfigs); mMinSdkVersion = minSdkVersion; } /** * Sets the APK signature schemes that should be enabled based on the options provided by * the caller. */ private void setEnabledSignatureSchemes() { if (mV3SigningExplicitlyDisabled && mV3SigningExplicitlyEnabled) { throw new IllegalStateException( "Builder configured to both enable and disable APK " + "Signature Scheme v3 signing"); } if (mV3SigningExplicitlyDisabled) { mV3SigningEnabled = false; } else if (mV3SigningExplicitlyEnabled) { mV3SigningEnabled = true; } } /** * Sets the SDK targeted signer configs based on the signing config and rotation options * provided by the caller. * * @throws InvalidKeyException if a {@link SigningCertificateLineage} cannot be created * from the provided options */ private void setTargetedSignerConfigs() throws InvalidKeyException { // If the caller specified any SDK targeted signer configs, then the min SDK version // should be set for those configs, all others should have a default 0 min SDK version. mSignerConfigs.sort(((signerConfig1, signerConfig2) -> signerConfig1.getMinSdkVersion() - signerConfig2.getMinSdkVersion())); // With the signer configs sorted, find the first targeted signer config with a min // SDK version > 0 to create the separate targeted signer configs. mTargetedSignerConfigs = new ArrayList<>(); for (int i = 0; i < mSignerConfigs.size(); i++) { if (mSignerConfigs.get(i).getMinSdkVersion() > 0) { mTargetedSignerConfigs = mSignerConfigs.subList(i, mSignerConfigs.size()); mSignerConfigs = mSignerConfigs.subList(0, i); break; } } // A lineage provided outside a targeted signing config is intended for the original // rotation; sort the untargeted signing configs based on this lineage and create a new // targeted signing config for the initial rotation. if (mSigningCertificateLineage != null) { if (!mTargetedSignerConfigs.isEmpty()) { // Only the initial rotation can use the rotation-min-sdk-version; all // subsequent targeted rotations must use targeted signing configs. int firstTargetedSdkVersion = mTargetedSignerConfigs.get(0).getMinSdkVersion(); if (mRotationMinSdkVersion >= firstTargetedSdkVersion) { throw new IllegalStateException( "The rotation-min-sdk-version, " + mRotationMinSdkVersion + ", must be less than the first targeted SDK version, " + firstTargetedSdkVersion); } } try { mSignerConfigs = mSigningCertificateLineage.sortSignerConfigs(mSignerConfigs); } catch (IllegalArgumentException e) { throw new IllegalStateException( "Provided signer configs do not match the " + "provided SigningCertificateLineage", e); } // Get the last signer in the lineage, create a new targeted signer from it, // and add it as a targeted signer config. SignerConfig rotatedSignerConfig = mSignerConfigs.remove(mSignerConfigs.size() - 1); SignerConfig.Builder rotatedConfigBuilder = new SignerConfig.Builder( rotatedSignerConfig.getName(), rotatedSignerConfig.getKeyConfig(), rotatedSignerConfig.getCertificates(), rotatedSignerConfig.getDeterministicDsaSigning()); rotatedConfigBuilder.setLineageForMinSdkVersion(mSigningCertificateLineage, mRotationMinSdkVersion); rotatedConfigBuilder.setSignerTargetsDevRelease(mRotationTargetsDevRelease); mTargetedSignerConfigs.add(0, rotatedConfigBuilder.build()); } mSigningCertificateLineage = mergeTargetedSigningConfigLineages(); } /** * Merges and returns the lineages from any caller provided SDK targeted {@link * SignerConfig} instances with an optional {@code lineage} specified as part of the general * signing config. * *

If multiple signing configs target the same SDK version, or if any of the lineages * cannot be merged, then an {@code IllegalStateException} is thrown. */ private SigningCertificateLineage mergeTargetedSigningConfigLineages() throws InvalidKeyException { SigningCertificateLineage mergedLineage = null; int prevSdkVersion = 0; for (SignerConfig signerConfig : mTargetedSignerConfigs) { int signerMinSdkVersion = signerConfig.getMinSdkVersion(); if (signerMinSdkVersion < AndroidSdkVersion.P) { throw new IllegalStateException( "Targeted signing config is not supported prior to SDK version " + AndroidSdkVersion.P + "; received value " + signerMinSdkVersion); } SigningCertificateLineage signerLineage = signerConfig.getSigningCertificateLineage(); // It is possible for a lineage to be null if the user is using one of the // signers from the lineage as the only signer to target an SDK version; create // a single element lineage to verify the signer is part of the merged lineage. if (signerLineage == null) { try { signerLineage = new SigningCertificateLineage.Builder( new SigningCertificateLineage.SignerConfig.Builder( signerConfig.mKeyConfig, signerConfig.mCertificates.get(0)) .build()) .build(); } catch (CertificateEncodingException | NoSuchAlgorithmException | SignatureException e) { throw new IllegalStateException( "Unable to create a SignerConfig for signer from certificate " + signerConfig.mCertificates.get(0).getSubjectDN()); } } // The V3.0 signature scheme does not support verified targeted SDK signing // configs; if a signer is targeting any SDK version < T, then it will // target P with the V3.0 signature scheme. if (signerMinSdkVersion < AndroidSdkVersion.T) { signerMinSdkVersion = AndroidSdkVersion.P; } // Ensure there are no SignerConfigs targeting the same SDK version. if (signerMinSdkVersion == prevSdkVersion) { throw new IllegalStateException( "Multiple SignerConfigs were found targeting SDK version " + signerMinSdkVersion); } // If multiple lineages have been provided, then verify each subsequent lineage // is a valid descendant or ancestor of the previously merged lineages. if (mergedLineage == null) { mergedLineage = signerLineage; } else { try { mergedLineage = mergedLineage.mergeLineageWith(signerLineage); } catch (IllegalArgumentException e) { throw new IllegalStateException( "The provided lineage targeting SDK " + signerMinSdkVersion + " is not in the signing history of the other targeted " + "signing configs", e); } } prevSdkVersion = signerMinSdkVersion; } return mergedLineage; } /** * Returns a new {@code DefaultApkSignerEngine} instance configured based on the * configuration of this builder. */ public DefaultApkSignerEngine build() throws InvalidKeyException { setEnabledSignatureSchemes(); setTargetedSignerConfigs(); // make sure our signers are appropriately setup if (mSigningCertificateLineage != null) { if (!mV3SigningEnabled && mSignerConfigs.size() > 1) { // this is a strange situation: we've provided a valid rotation history, but // are only signing with v1/v2. blow up, since we don't know for sure with // which signer the user intended to sign throw new IllegalStateException( "Provided multiple signers which are part of the" + " SigningCertificateLineage, but not signing with APK" + " Signature Scheme v3"); } } else if (mV3SigningEnabled && mSignerConfigs.size() > 1) { throw new IllegalStateException( "Multiple signing certificates provided for use with APK Signature Scheme" + " v3 without an accompanying SigningCertificateLineage"); } return new DefaultApkSignerEngine( mSignerConfigs, mTargetedSignerConfigs, mStampSignerConfig, mSourceStampSigningCertificateLineage, mSourceStampTimestampEnabled, mMinSdkVersion, mV1SigningEnabled, mV2SigningEnabled, mV3SigningEnabled, mVerityEnabled, mDebuggableApkPermitted, mOtherSignersSignaturesPreserved, mCreatedBy, mSigningCertificateLineage); } /** Sets the signer configuration for the SourceStamp to be embedded in the APK. */ public Builder setStampSignerConfig(SignerConfig stampSignerConfig) { mStampSignerConfig = stampSignerConfig; return this; } /** * Sets the source stamp {@link SigningCertificateLineage}. This structure provides proof of * signing certificate rotation for certificates previously used to sign source stamps. */ public Builder setSourceStampSigningCertificateLineage( SigningCertificateLineage sourceStampSigningCertificateLineage) { mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage; return this; } /** * Sets whether the source stamp should contain the timestamp attribute with the time * at which the source stamp was signed. */ public Builder setSourceStampTimestampEnabled(boolean value) { mSourceStampTimestampEnabled = value; return this; } /** * Sets whether the APK should be signed using JAR signing (aka v1 signature scheme). * *

By default, the APK will be signed using this scheme. */ public Builder setV1SigningEnabled(boolean enabled) { mV1SigningEnabled = enabled; return this; } /** * Sets whether the APK should be signed using APK Signature Scheme v2 (aka v2 signature * scheme). * *

By default, the APK will be signed using this scheme. */ public Builder setV2SigningEnabled(boolean enabled) { mV2SigningEnabled = enabled; return this; } /** * Sets whether the APK should be signed using APK Signature Scheme v3 (aka v3 signature * scheme). * *

By default, the APK will be signed using this scheme. */ public Builder setV3SigningEnabled(boolean enabled) { mV3SigningEnabled = enabled; if (enabled) { mV3SigningExplicitlyEnabled = true; } else { mV3SigningExplicitlyDisabled = true; } return this; } /** * Sets whether the APK should be signed using the verity signature algorithm in the v2 and * v3 signature blocks. * *

By default, the APK will be signed using the verity signature algorithm for the v2 and * v3 signature schemes. */ public Builder setVerityEnabled(boolean enabled) { mVerityEnabled = enabled; return this; } /** * Sets whether the APK should be signed even if it is marked as debuggable ({@code * android:debuggable="true"} in its {@code AndroidManifest.xml}). For backward * compatibility reasons, the default value of this setting is {@code true}. * *

It is dangerous to sign debuggable APKs with production/release keys because Android * platform loosens security checks for such APKs. For example, arbitrary unauthorized code * may be executed in the context of such an app by anybody with ADB shell access. */ public Builder setDebuggableApkPermitted(boolean permitted) { mDebuggableApkPermitted = permitted; return this; } /** * Sets whether signatures produced by signers other than the ones configured in this engine * should be copied from the input APK to the output APK. * *

By default, signatures of other signers are omitted from the output APK. */ public Builder setOtherSignersSignaturesPreserved(boolean preserved) { mOtherSignersSignaturesPreserved = preserved; return this; } /** Sets the value of the {@code Created-By} field in JAR signature files. */ public Builder setCreatedBy(String createdBy) { if (createdBy == null) { throw new NullPointerException(); } mCreatedBy = createdBy; return this; } /** * Sets the {@link SigningCertificateLineage} to use with the v3 signature scheme. This * structure provides proof of signing certificate rotation linking {@link SignerConfig} * objects to previous ones. */ public Builder setSigningCertificateLineage( SigningCertificateLineage signingCertificateLineage) { if (signingCertificateLineage != null) { mV3SigningEnabled = true; mSigningCertificateLineage = signingCertificateLineage; } return this; } /** * Sets the minimum Android platform version (API Level) for which an APK's rotated signing * key should be used to produce the APK's signature. The original signing key for the APK * will be used for all previous platform versions. If a rotated key with signing lineage is * not provided then this method is a noop. * *

By default, if a signing lineage is specified with {@link * #setSigningCertificateLineage(SigningCertificateLineage)}, then the APK Signature Scheme * V3.1 will be used to only apply the rotation on devices running Android T+. * *

Note:Specifying a {@code minSdkVersion} value <= 32 (Android Sv2) will result * in the original V3 signing block being used without platform targeting. */ public Builder setMinSdkVersionForRotation(int minSdkVersion) { // If the provided SDK version does not support v3.1, then use the default SDK version // with rotation support. if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT) { mRotationMinSdkVersion = MIN_SDK_WITH_V3_SUPPORT; } else { mRotationMinSdkVersion = minSdkVersion; } return this; } /** * Sets whether the rotation-min-sdk-version is intended to target a development release; * this is primarily required after the T SDK is finalized, and an APK needs to target U * during its development cycle for rotation. * *

This is only required after the T SDK is finalized since S and earlier releases do * not know about the V3.1 block ID, but once T is released and work begins on U, U will * use the SDK version of T during development. Specifying a rotation-min-sdk-version of T's * SDK version along with setting {@code enabled} to true will allow an APK to use the * rotated key on a device running U while causing this to be bypassed for T. * *

Note:If the rotation-min-sdk-version is less than or equal to 32 (Android * Sv2), then the rotated signing key will be used in the v3.0 signing block and this call * will be a noop. */ public Builder setRotationTargetsDevRelease(boolean enabled) { mRotationTargetsDevRelease = enabled; return this; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_Hints.java0100644 0000000 0000000 00000000034 14763776540 023024 xustar000000000 0000000 28 mtime=1741684064.5550000 src/main/java/com/android/apksig/Hints.java0100644 0000000 0000000 00000010655 14763776540 017677 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import java.io.IOException; import java.io.DataOutputStream; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public final class Hints { /** * Name of hint pattern asset file in APK. */ public static final String PIN_HINT_ASSET_ZIP_ENTRY_NAME = "assets/com.android.hints.pins.txt"; /** * Name of hint byte range data file in APK. Keep in sync with PinnerService.java. */ public static final String PIN_BYTE_RANGE_ZIP_ENTRY_NAME = "pinlist.meta"; private static int clampToInt(long value) { return (int) Math.max(0, Math.min(value, Integer.MAX_VALUE)); } public static final class ByteRange { final long start; final long end; public ByteRange(long start, long end) { this.start = start; this.end = end; } } public static final class PatternWithRange { final Pattern pattern; final long offset; final long size; public PatternWithRange(String pattern) { this.pattern = Pattern.compile(pattern); this.offset= 0; this.size = Long.MAX_VALUE; } public PatternWithRange(String pattern, long offset, long size) { this.pattern = Pattern.compile(pattern); this.offset = offset; this.size = size; } public Matcher matcher(CharSequence input) { return this.pattern.matcher(input); } public ByteRange ClampToAbsoluteByteRange(ByteRange rangeIn) { if (rangeIn.end - rangeIn.start < this.offset) { return null; } long rangeOutStart = rangeIn.start + this.offset; long rangeOutSize = Math.min(rangeIn.end - rangeOutStart, this.size); return new ByteRange(rangeOutStart, rangeOutStart + rangeOutSize); } } /** * Create a blob of bytes that PinnerService understands as a * sequence of byte ranges to pin. */ public static byte[] encodeByteRangeList(List pinByteRanges) { ByteArrayOutputStream bos = new ByteArrayOutputStream(pinByteRanges.size() * 8); DataOutputStream out = new DataOutputStream(bos); try { for (ByteRange pinByteRange : pinByteRanges) { out.writeInt(clampToInt(pinByteRange.start)); out.writeInt(clampToInt(pinByteRange.end - pinByteRange.start)); } } catch (IOException ex) { throw new AssertionError("impossible", ex); } return bos.toByteArray(); } public static ArrayList parsePinPatterns(byte[] patternBlob) { ArrayList pinPatterns = new ArrayList<>(); try { for (String rawLine : new String(patternBlob, "UTF-8").split("\n")) { String line = rawLine.replaceFirst("#.*", ""); // # starts a comment String[] fields = line.split(" "); if (fields.length == 1) { pinPatterns.add(new PatternWithRange(fields[0])); } else if (fields.length == 3) { long start = Long.parseLong(fields[1]); long end = Long.parseLong(fields[2]); pinPatterns.add(new PatternWithRange(fields[0], start, end - start)); } else { throw new AssertionError("bad pin pattern line " + line); } } } catch (UnsupportedEncodingException ex) { throw new RuntimeException("UTF-8 must be supported", ex); } return pinPatterns; } } ./PaxHeaders.X/src_main_java_com_android_apksig_JcaSignerEngine.java0100644 0000000 0000000 00000000034 14763776540 024732 xustar000000000 0000000 28 mtime=1741684064.5550000 src/main/java/com/android/apksig/JcaSignerEngine.java0100644 0000000 0000000 00000004403 14763776540 021577 0ustar000000000 0000000 /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Signature; import java.security.SignatureException; import java.security.spec.AlgorithmParameterSpec; /** Cryptographic signing using standard Java Crypto Architecture (JCA) */ public class JcaSignerEngine implements SignerEngine { private final PrivateKey mPrivateKey; private final String mSignatureAlgorithm; private final AlgorithmParameterSpec mAlgorithmParameterSpec; public JcaSignerEngine( PrivateKey privateKey, String signatureAlgorithm, AlgorithmParameterSpec algorithmParameterSpec) { if (privateKey == null) { throw new IllegalArgumentException("privateKey cannot be null"); } if (signatureAlgorithm == null) { throw new IllegalArgumentException("signatureAlgorithm cannot be null"); } mPrivateKey = privateKey; mSignatureAlgorithm = signatureAlgorithm; mAlgorithmParameterSpec = algorithmParameterSpec; } @Override public byte[] sign(byte[] data) throws InvalidKeyException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, SignatureException { Signature signature = Signature.getInstance(mSignatureAlgorithm); signature.initSign(mPrivateKey); if (mAlgorithmParameterSpec != null) { signature.setParameter(mAlgorithmParameterSpec); } signature.update(data); return signature.sign(); } } ./PaxHeaders.X/src_main_java_com_android_apksig_KeyConfig.java0100644 0000000 0000000 00000000034 14763776540 023615 xustar000000000 0000000 28 mtime=1741684064.5550000 src/main/java/com/android/apksig/KeyConfig.java0100644 0000000 0000000 00000004106 14763776540 020462 0ustar000000000 0000000 /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import com.android.apksig.kms.KmsType; import java.security.PrivateKey; import java.util.function.Function; /** * Represents the private key that will be used for signing, where that key could either be locally * accessible or exposed only via a Key Management Service (KMS). */ public abstract class KeyConfig { private KeyConfig() {} /** Helper function to perform some operation on a {@link KeyConfig} regardless of subtype. */ public abstract T match(Function local, Function kms); /** * For signing via Java Crypto Architecture (JCA). Simply wraps a {@link PrivateKey} that is * accessible locally. */ public static class Jca extends KeyConfig { public final PrivateKey privateKey; @Override public T match(Function jca, Function kms) { return jca.apply(this); } public Jca(PrivateKey privateKey) { this.privateKey = privateKey; } } /** For signing via a Key Management Service (KMS). */ public static class Kms extends KeyConfig { public final KmsType kmsType; public final String keyAlias; @Override public T match(Function jca, Function kms) { return kms.apply(this); } public Kms(KmsType kmsType, String keyAlias) { this.kmsType = kmsType; this.keyAlias = keyAlias; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_SignerEngine.java0100644 0000000 0000000 00000000034 14763776540 024314 xustar000000000 0000000 28 mtime=1741684064.5560000 src/main/java/com/android/apksig/SignerEngine.java0100644 0000000 0000000 00000002326 14763776540 021163 0ustar000000000 0000000 /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; /** A basic abstraction for cryptographic signing. */ public interface SignerEngine { /** * Cryptographically sign the input. * * @param data a blob of bytes to sign. * @return the signed bytes. */ byte[] sign(byte[] data) throws InvalidKeyException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, SignatureException; } ./PaxHeaders.X/src_main_java_com_android_apksig_SignerEngineFactory.java0100644 0000000 0000000 00000000034 14763776540 025644 xustar000000000 0000000 28 mtime=1741684064.5570000 src/main/java/com/android/apksig/SignerEngineFactory.java0100644 0000000 0000000 00000003560 14763776540 022514 0ustar000000000 0000000 /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import com.android.apksig.kms.KmsSignerEngine; import java.security.spec.AlgorithmParameterSpec; /** Simple util to fetch a signer engine based on provided config values. */ public class SignerEngineFactory { private SignerEngineFactory() {} /** * Retrieves an implementation based on the provided config. If keyConfig is a {@link * KeyConfig.Kms}, signatureAlgorithm and signatureAlgorithmParameterSpec are ignored. * * @param keyConfig kms key type and alias, or a local private key. * @param signatureAlgorithm required if keyConfig is {@link KeyConfig.Jca}, ignored if * keyConfig is {@link KeyConfig.Kms}. * @param algorithmParameterSpec optional, ignored if keyConfig is {@link KeyConfig.Kms}. * @return a concrete {@link SignerEngine} implementation. */ public static SignerEngine getImplementation( KeyConfig keyConfig, String signatureAlgorithm, AlgorithmParameterSpec algorithmParameterSpec) { return keyConfig.match( jca -> new JcaSignerEngine( jca.privateKey, signatureAlgorithm, algorithmParameterSpec), KmsSignerEngine::fromKmsConfig); } } ./PaxHeaders.X/src_main_java_com_android_apksig_SigningCertificateLineage.java0100644 0000000 0000000 00000000034 14763776540 026765 xustar000000000 0000000 28 mtime=1741684064.5570000 src/main/java/com/android/apksig/SigningCertificateLineage.java0100644 0000000 0000000 00000164304 14763776540 023641 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeSigner; import com.android.apksig.internal.apk.v3.V3SigningCertificateLineage; import com.android.apksig.internal.apk.v3.V3SigningCertificateLineage.SigningCertificateNode; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.ByteBufferUtils; import com.android.apksig.internal.util.Pair; import com.android.apksig.internal.util.RandomAccessFileDataSink; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import com.android.apksig.zip.ZipFormatException; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * APK Signer Lineage. * *

The signer lineage contains a history of signing certificates with each ancestor attesting to * the validity of its descendant. Each additional descendant represents a new identity that can be * used to sign an APK, and each generation has accompanying attributes which represent how the * APK would like to view the older signing certificates, specifically how they should be trusted in * certain situations. * *

Its primary use is to enable APK Signing Certificate Rotation. The Android platform verifies * the APK Signer Lineage, and if the current signing certificate for the APK is in the Signer * Lineage, and the Lineage contains the certificate the platform associates with the APK, it will * allow upgrades to the new certificate. * * @see Application Signing */ public class SigningCertificateLineage { public final static int MAGIC = 0x3eff39d1; private final static int FIRST_VERSION = 1; private static final int CURRENT_VERSION = FIRST_VERSION; /** accept data from already installed pkg with this cert */ private static final int PAST_CERT_INSTALLED_DATA = 1; /** accept sharedUserId with pkg with this cert */ private static final int PAST_CERT_SHARED_USER_ID = 2; /** grant SIGNATURE permissions to pkgs with this cert */ private static final int PAST_CERT_PERMISSION = 4; /** * Enable updates back to this certificate. WARNING: this effectively removes any benefit of * signing certificate changes, since a compromised key could retake control of an app even * after change, and should only be used if there is a problem encountered when trying to ditch * an older cert. */ private static final int PAST_CERT_ROLLBACK = 8; /** * Preserve authenticator module-based access in AccountManager gated by signing certificate. */ private static final int PAST_CERT_AUTH = 16; private final int mMinSdkVersion; /** * The signing lineage is just a list of nodes, with the first being the original signing * certificate and the most recent being the one with which the APK is to actually be signed. */ private final List mSigningLineage; private SigningCertificateLineage(int minSdkVersion, List list) { mMinSdkVersion = minSdkVersion; mSigningLineage = list; } /** * Creates a {@code SigningCertificateLineage} with a single signer in the lineage. */ private static SigningCertificateLineage createSigningLineage(int minSdkVersion, SignerConfig signer, SignerCapabilities capabilities) { SigningCertificateLineage signingCertificateLineage = new SigningCertificateLineage( minSdkVersion, new ArrayList<>()); return signingCertificateLineage.spawnFirstDescendant(signer, capabilities); } private static SigningCertificateLineage createSigningLineage( int minSdkVersion, SignerConfig parent, SignerCapabilities parentCapabilities, SignerConfig child, SignerCapabilities childCapabilities) throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { SigningCertificateLineage signingCertificateLineage = new SigningCertificateLineage(minSdkVersion, new ArrayList<>()); signingCertificateLineage = signingCertificateLineage.spawnFirstDescendant(parent, parentCapabilities); return signingCertificateLineage.spawnDescendant(parent, child, childCapabilities); } public static SigningCertificateLineage readFromBytes(byte[] lineageBytes) throws IOException { return readFromDataSource(DataSources.asDataSource(ByteBuffer.wrap(lineageBytes))); } public static SigningCertificateLineage readFromFile(File file) throws IOException { if (file == null) { throw new NullPointerException("file == null"); } RandomAccessFile inputFile = new RandomAccessFile(file, "r"); return readFromDataSource(DataSources.asDataSource(inputFile)); } public static SigningCertificateLineage readFromDataSource(DataSource dataSource) throws IOException { if (dataSource == null) { throw new NullPointerException("dataSource == null"); } ByteBuffer inBuff = dataSource.getByteBuffer(0, (int) dataSource.size()); inBuff.order(ByteOrder.LITTLE_ENDIAN); return read(inBuff); } /** * Extracts a Signing Certificate Lineage from a v3 signer proof-of-rotation attribute. * * * this may not give a complete representation of an APK's signing certificate history, * since the APK may have multiple signers corresponding to different platform versions. * Use readFromApkFile to handle this case. * * @param attrValue */ public static SigningCertificateLineage readFromV3AttributeValue(byte[] attrValue) throws IOException { List parsedLineage = V3SigningCertificateLineage.readSigningCertificateLineage(ByteBuffer.wrap( attrValue).order(ByteOrder.LITTLE_ENDIAN)); int minSdkVersion = calculateMinSdkVersion(parsedLineage); return new SigningCertificateLineage(minSdkVersion, parsedLineage); } /** * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the V3 * signature block of the provided APK File. * * @throws IllegalArgumentException if the provided APK does not contain a V3 signature block, * or if the V3 signature block does not contain a valid lineage. */ public static SigningCertificateLineage readFromApkFile(File apkFile) throws IOException, ApkFormatException { try (RandomAccessFile f = new RandomAccessFile(apkFile, "r")) { DataSource apk = DataSources.asDataSource(f, 0, f.length()); return readFromApkDataSource(apk); } } /** * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the V3 and * V3.1 signature blocks of the provided APK DataSource. * * @throws IllegalArgumentException if the provided APK does not contain a V3 nor V3.1 * signature block, or if the V3 and V3.1 signature blocks do not contain a valid lineage. */ public static SigningCertificateLineage readFromApkDataSource(DataSource apk) throws IOException, ApkFormatException { return readFromApkDataSource(apk, /* readV31Lineage= */ true, /* readV3Lineage= */true); } /** * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the V3.1 * signature blocks of the provided APK DataSource. * * @throws IllegalArgumentException if the provided APK does not contain a V3.1 signature block, * or if the V3.1 signature block does not contain a valid lineage. */ public static SigningCertificateLineage readV31FromApkDataSource(DataSource apk) throws IOException, ApkFormatException { return readFromApkDataSource(apk, /* readV31Lineage= */ true, /* readV3Lineage= */ false); } private static SigningCertificateLineage readFromApkDataSource( DataSource apk, boolean readV31Lineage, boolean readV3Lineage) throws IOException, ApkFormatException { ApkUtils.ZipSections zipSections; try { zipSections = ApkUtils.findZipSections(apk); } catch (ZipFormatException e) { throw new ApkFormatException(e.getMessage()); } List signatureInfoList = new ArrayList<>(); if (readV31Lineage) { try { ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31); signatureInfoList.add( ApkSigningBlockUtils.findSignature(apk, zipSections, V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID, result)); } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { // This could be expected if there's only a V3 signature block. } } if (readV3Lineage) { try { ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); signatureInfoList.add( ApkSigningBlockUtils.findSignature(apk, zipSections, V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID, result)); } catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { // This could be expected if the provided APK is not signed with the V3 signature // scheme } } if (signatureInfoList.isEmpty()) { String message; if (readV31Lineage && readV3Lineage) { message = "The provided APK does not contain a valid V3 nor V3.1 signature block."; } else if (readV31Lineage) { message = "The provided APK does not contain a valid V3.1 signature block."; } else if (readV3Lineage) { message = "The provided APK does not contain a valid V3 signature block."; } else { message = "No signature blocks were requested."; } throw new IllegalArgumentException(message); } List lineages = new ArrayList<>(1); for (SignatureInfo signatureInfo : signatureInfoList) { // FORMAT: // * length-prefixed sequence of length-prefixed signers: // * length-prefixed signed data // * minSDK // * maxSDK // * length-prefixed sequence of length-prefixed signatures // * length-prefixed public key ByteBuffer signers = getLengthPrefixedSlice(signatureInfo.signatureBlock); while (signers.hasRemaining()) { ByteBuffer signer = getLengthPrefixedSlice(signers); ByteBuffer signedData = getLengthPrefixedSlice(signer); try { SigningCertificateLineage lineage = readFromSignedData(signedData); lineages.add(lineage); } catch (IllegalArgumentException ignored) { // The current signer block does not contain a valid lineage, but it is possible // another block will. } } } SigningCertificateLineage result; if (lineages.isEmpty()) { throw new IllegalArgumentException( "The provided APK does not contain a valid lineage."); } else if (lineages.size() > 1) { result = consolidateLineages(lineages); } else { result = lineages.get(0); } return result; } /** * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the provided * signed data portion of a signer in a V3 signature block. * * @throws IllegalArgumentException if the provided signed data does not contain a valid * lineage. */ public static SigningCertificateLineage readFromSignedData(ByteBuffer signedData) throws IOException, ApkFormatException { // FORMAT: // * length-prefixed sequence of length-prefixed digests: // * length-prefixed sequence of certificates: // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded). // * uint-32: minSdkVersion // * uint-32: maxSdkVersion // * length-prefixed sequence of length-prefixed additional attributes: // * uint32: ID // * (length - 4) bytes: value // * uint32: Proof-of-rotation ID: 0x3ba06f8c // * length-prefixed proof-of-rotation structure // consume the digests through the maxSdkVersion to reach the lineage in the attributes getLengthPrefixedSlice(signedData); getLengthPrefixedSlice(signedData); signedData.getInt(); signedData.getInt(); // iterate over the additional attributes adding any lineages to the List ByteBuffer additionalAttributes = getLengthPrefixedSlice(signedData); List lineages = new ArrayList<>(1); while (additionalAttributes.hasRemaining()) { ByteBuffer attribute = getLengthPrefixedSlice(additionalAttributes); int id = attribute.getInt(); if (id == V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID) { byte[] value = ByteBufferUtils.toByteArray(attribute); SigningCertificateLineage lineage = readFromV3AttributeValue(value); lineages.add(lineage); } } SigningCertificateLineage result; // There should only be a single attribute with the lineage, but if there are multiple then // attempt to consolidate the lineages. if (lineages.isEmpty()) { throw new IllegalArgumentException("The signed data does not contain a valid lineage."); } else if (lineages.size() > 1) { result = consolidateLineages(lineages); } else { result = lineages.get(0); } return result; } public byte[] getBytes() { return write().array(); } public void writeToFile(File file) throws IOException { if (file == null) { throw new NullPointerException("file == null"); } RandomAccessFile outputFile = new RandomAccessFile(file, "rw"); writeToDataSink(new RandomAccessFileDataSink(outputFile)); } public void writeToDataSink(DataSink dataSink) throws IOException { if (dataSink == null) { throw new NullPointerException("dataSink == null"); } dataSink.consume(write()); } /** * Add a new signing certificate to the lineage. This effectively creates a signing certificate * rotation event, forcing APKs which include this lineage to be signed by the new signer. The * flags associated with the new signer are set to a default value. * * @param parent current signing certificate of the containing APK * @param child new signing certificate which will sign the APK contents */ public SigningCertificateLineage spawnDescendant(SignerConfig parent, SignerConfig child) throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { if (parent == null || child == null) { throw new NullPointerException("can't add new descendant to lineage with null inputs"); } SignerCapabilities signerCapabilities = new SignerCapabilities.Builder().build(); return spawnDescendant(parent, child, signerCapabilities); } /** * Add a new signing certificate to the lineage. This effectively creates a signing certificate * rotation event, forcing APKs which include this lineage to be signed by the new signer. * * @param parent current signing certificate of the containing APK * @param child new signing certificate which will sign the APK contents * @param childCapabilities flags */ public SigningCertificateLineage spawnDescendant( SignerConfig parent, SignerConfig child, SignerCapabilities childCapabilities) throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { if (parent == null) { throw new NullPointerException("parent == null"); } if (child == null) { throw new NullPointerException("child == null"); } if (childCapabilities == null) { throw new NullPointerException("childCapabilities == null"); } if (mSigningLineage.isEmpty()) { throw new IllegalArgumentException("Cannot spawn descendant signing certificate on an" + " empty SigningCertificateLineage: no parent node"); } // make sure that the parent matches our newest generation (leaf node/sink) SigningCertificateNode currentGeneration = mSigningLineage.get(mSigningLineage.size() - 1); if (!Arrays.equals(currentGeneration.signingCert.getEncoded(), parent.getCertificate().getEncoded())) { throw new IllegalArgumentException("SignerConfig Certificate containing private key" + " to sign the new SigningCertificateLineage record does not match the" + " existing most recent record"); } // create data to be signed, including the algorithm we're going to use SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(parent); ByteBuffer prefixedSignedData = ByteBuffer.wrap( V3SigningCertificateLineage.encodeSignedData( child.getCertificate(), signatureAlgorithm.getId())); prefixedSignedData.position(4); ByteBuffer signedDataBuffer = ByteBuffer.allocate(prefixedSignedData.remaining()); signedDataBuffer.put(prefixedSignedData); byte[] signedData = signedDataBuffer.array(); // create SignerConfig to do the signing List certificates = new ArrayList<>(1); certificates.add(parent.getCertificate()); ApkSigningBlockUtils.SignerConfig newSignerConfig = new ApkSigningBlockUtils.SignerConfig(); newSignerConfig.keyConfig = parent.getKeyConfig(); newSignerConfig.certificates = certificates; newSignerConfig.signatureAlgorithms = Collections.singletonList(signatureAlgorithm); // sign it List> signatures = ApkSigningBlockUtils.generateSignaturesOverData(newSignerConfig, signedData); // finally, add it to our lineage SignatureAlgorithm sigAlgorithm = SignatureAlgorithm.findById(signatures.get(0).getFirst()); byte[] signature = signatures.get(0).getSecond(); currentGeneration.sigAlgorithm = sigAlgorithm; SigningCertificateNode childNode = new SigningCertificateNode( child.getCertificate(), sigAlgorithm, null, signature, childCapabilities.getFlags()); List lineageCopy = new ArrayList<>(mSigningLineage); lineageCopy.add(childNode); return new SigningCertificateLineage(mMinSdkVersion, lineageCopy); } /** * The number of signing certificates in the lineage, including the current signer, which means * this value can also be used to V2determine the number of signing certificate rotations by * subtracting 1. */ public int size() { return mSigningLineage.size(); } private SignatureAlgorithm getSignatureAlgorithm(SignerConfig parent) throws InvalidKeyException { PublicKey publicKey = parent.getCertificate().getPublicKey(); // TODO switch to one signature algorithm selection, or add support for multiple algorithms List algorithms = V3SchemeSigner.getSuggestedSignatureAlgorithms( publicKey, mMinSdkVersion, false /* verityEnabled */, false /* deterministicDsaSigning */); return algorithms.get(0); } private SigningCertificateLineage spawnFirstDescendant( SignerConfig parent, SignerCapabilities signerCapabilities) { if (!mSigningLineage.isEmpty()) { throw new IllegalStateException("SigningCertificateLineage already has its first node"); } // check to make sure that the public key for the first node is acceptable for our minSdk try { getSignatureAlgorithm(parent); } catch (InvalidKeyException e) { throw new IllegalArgumentException("Algorithm associated with first signing certificate" + " invalid on desired platform versions", e); } // create "fake" signed data (there will be no signature over it, since there is no parent SigningCertificateNode firstNode = new SigningCertificateNode( parent.getCertificate(), null, null, new byte[0], signerCapabilities.getFlags()); return new SigningCertificateLineage(mMinSdkVersion, Collections.singletonList(firstNode)); } private static SigningCertificateLineage read(ByteBuffer inputByteBuffer) throws IOException { ApkSigningBlockUtils.checkByteOrderLittleEndian(inputByteBuffer); if (inputByteBuffer.remaining() < 8) { throw new IllegalArgumentException( "Improper SigningCertificateLineage format: insufficient data for header."); } if (inputByteBuffer.getInt() != MAGIC) { throw new IllegalArgumentException( "Improper SigningCertificateLineage format: MAGIC header mismatch."); } return read(inputByteBuffer, inputByteBuffer.getInt()); } private static SigningCertificateLineage read(ByteBuffer inputByteBuffer, int version) throws IOException { switch (version) { case FIRST_VERSION: try { List nodes = V3SigningCertificateLineage.readSigningCertificateLineage( getLengthPrefixedSlice(inputByteBuffer)); int minSdkVersion = calculateMinSdkVersion(nodes); return new SigningCertificateLineage(minSdkVersion, nodes); } catch (ApkFormatException e) { // unable to get a proper length-prefixed lineage slice throw new IOException("Unable to read list of signing certificate nodes in " + "SigningCertificateLineage", e); } default: throw new IllegalArgumentException( "Improper SigningCertificateLineage format: unrecognized version."); } } private static int calculateMinSdkVersion(List nodes) { if (nodes == null) { throw new IllegalArgumentException("Can't calculate minimum SDK version of null nodes"); } int minSdkVersion = AndroidSdkVersion.P; // lineage introduced in P for (SigningCertificateNode node : nodes) { if (node.sigAlgorithm != null) { int nodeMinSdkVersion = node.sigAlgorithm.getMinSdkVersion(); if (nodeMinSdkVersion > minSdkVersion) { minSdkVersion = nodeMinSdkVersion; } } } return minSdkVersion; } private ByteBuffer write() { byte[] encodedLineage = V3SigningCertificateLineage.encodeSigningCertificateLineage(mSigningLineage); int payloadSize = 4 + 4 + 4 + encodedLineage.length; ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.putInt(MAGIC); result.putInt(CURRENT_VERSION); result.putInt(encodedLineage.length); result.put(encodedLineage); result.flip(); return result; } public byte[] encodeSigningCertificateLineage() { return V3SigningCertificateLineage.encodeSigningCertificateLineage(mSigningLineage); } public List sortSignerConfigs( List signerConfigs) { if (signerConfigs == null) { throw new NullPointerException("signerConfigs == null"); } // not the most elegant sort, but we expect signerConfigs to be quite small (1 or 2 signers // in most cases) and likely already sorted, so not worth the overhead of doing anything // fancier List sortedSignerConfigs = new ArrayList<>(signerConfigs.size()); for (int i = 0; i < mSigningLineage.size(); i++) { for (int j = 0; j < signerConfigs.size(); j++) { DefaultApkSignerEngine.SignerConfig config = signerConfigs.get(j); if (mSigningLineage.get(i).signingCert.equals(config.getCertificates().get(0))) { sortedSignerConfigs.add(config); break; } } } if (sortedSignerConfigs.size() != signerConfigs.size()) { throw new IllegalArgumentException("SignerConfigs supplied which are not present in the" + " SigningCertificateLineage"); } return sortedSignerConfigs; } /** * Returns the SignerCapabilities for the signer in the lineage that matches the provided * config. */ public SignerCapabilities getSignerCapabilities(SignerConfig config) { if (config == null) { throw new NullPointerException("config == null"); } X509Certificate cert = config.getCertificate(); return getSignerCapabilities(cert); } /** * Returns the SignerCapabilities for the signer in the lineage that matches the provided * certificate. */ public SignerCapabilities getSignerCapabilities(X509Certificate cert) { if (cert == null) { throw new NullPointerException("cert == null"); } for (int i = 0; i < mSigningLineage.size(); i++) { SigningCertificateNode lineageNode = mSigningLineage.get(i); if (lineageNode.signingCert.equals(cert)) { int flags = lineageNode.flags; return new SignerCapabilities.Builder(flags).build(); } } // the provided signer certificate was not found in the lineage throw new IllegalArgumentException("Certificate (" + cert.getSubjectDN() + ") not found in the SigningCertificateLineage"); } /** * Updates the SignerCapabilities for the signer in the lineage that matches the provided * config. Only those capabilities that have been modified through the setXX methods will be * updated for the signer to prevent unset default values from being applied. */ public void updateSignerCapabilities(SignerConfig config, SignerCapabilities capabilities) { if (config == null) { throw new NullPointerException("config == null"); } updateSignerCapabilities(config.getCertificate(), capabilities); } /** * Updates the {@code capabilities} for the signer with the provided {@code certificate} in the * lineage. Only those capabilities that have been modified through the setXX methods will be * updated for the signer to prevent unset default values from being applied. */ public void updateSignerCapabilities(X509Certificate certificate, SignerCapabilities capabilities) { if (certificate == null) { throw new NullPointerException("config == null"); } for (int i = 0; i < mSigningLineage.size(); i++) { SigningCertificateNode lineageNode = mSigningLineage.get(i); if (lineageNode.signingCert.equals(certificate)) { int flags = lineageNode.flags; SignerCapabilities newCapabilities = new SignerCapabilities.Builder( flags).setCallerConfiguredCapabilities(capabilities).build(); lineageNode.flags = newCapabilities.getFlags(); return; } } // the provided signer config was not found in the lineage throw new IllegalArgumentException("Certificate (" + certificate.getSubjectDN() + ") not found in the SigningCertificateLineage"); } /** * Returns a list containing all of the certificates in the lineage. */ public List getCertificatesInLineage() { List certs = new ArrayList<>(); for (int i = 0; i < mSigningLineage.size(); i++) { X509Certificate cert = mSigningLineage.get(i).signingCert; certs.add(cert); } return certs; } /** * Returns {@code true} if the specified config is in the lineage. */ public boolean isSignerInLineage(SignerConfig config) { if (config == null) { throw new NullPointerException("config == null"); } X509Certificate cert = config.getCertificate(); return isCertificateInLineage(cert); } /** * Returns {@code true} if the specified certificate is in the lineage. */ public boolean isCertificateInLineage(X509Certificate cert) { if (cert == null) { throw new NullPointerException("cert == null"); } for (int i = 0; i < mSigningLineage.size(); i++) { if (mSigningLineage.get(i).signingCert.equals(cert)) { return true; } } return false; } /** * Returns whether the provided {@code cert} is the latest signing certificate in the lineage. * *

This method will only compare the provided {@code cert} against the latest signing * certificate in the lineage; if a certificate that is not in the lineage is provided, this * method will return false. */ public boolean isCertificateLatestInLineage(X509Certificate cert) { if (cert == null) { throw new NullPointerException("cert == null"); } return mSigningLineage.get(mSigningLineage.size() - 1).signingCert.equals(cert); } private static int calculateDefaultFlags() { return PAST_CERT_INSTALLED_DATA | PAST_CERT_PERMISSION | PAST_CERT_SHARED_USER_ID | PAST_CERT_AUTH; } /** * Returns a new SigningCertificateLineage which terminates at the node corresponding to the * given certificate. This is useful in the event of rotating to a new signing algorithm that * is only supported on some platform versions. It enables a v3 signature to be generated using * this signing certificate and the shortened proof-of-rotation record from this sub lineage in * conjunction with the appropriate SDK version values. * * @param x509Certificate the signing certificate for which to search * @return A new SigningCertificateLineage if the given certificate is present. * * @throws IllegalArgumentException if the provided certificate is not in the lineage. */ public SigningCertificateLineage getSubLineage(X509Certificate x509Certificate) { if (x509Certificate == null) { throw new NullPointerException("x509Certificate == null"); } for (int i = 0; i < mSigningLineage.size(); i++) { if (mSigningLineage.get(i).signingCert.equals(x509Certificate)) { return new SigningCertificateLineage( mMinSdkVersion, new ArrayList<>(mSigningLineage.subList(0, i + 1))); } } // looks like we didn't find the cert, throw new IllegalArgumentException("Certificate not found in SigningCertificateLineage"); } /** * Consolidates all of the lineages found in an APK into one lineage. In so doing, it also * checks that all of the lineages are contained in one common lineage. * * An APK may contain multiple lineages, one for each signer, which correspond to different * supported platform versions. In this event, the lineage(s) from the earlier platform * version(s) should be present in the most recent, either directly or via a sublineage * that would allow the earlier lineages to merge with the most recent. * * This does not verify that the largest lineage corresponds to the most recent supported * platform version. That check is performed during v3 verification. */ public static SigningCertificateLineage consolidateLineages( List lineages) { if (lineages == null || lineages.isEmpty()) { return null; } SigningCertificateLineage consolidatedLineage = lineages.get(0); for (int i = 1; i < lineages.size(); i++) { consolidatedLineage = consolidatedLineage.mergeLineageWith(lineages.get(i)); } return consolidatedLineage; } /** * Merges this lineage with the provided {@code otherLineage}. * *

The merged lineage does not currently handle merging capabilities of common signers and * should only be used to determine the full signing history of a collection of lineages. */ public SigningCertificateLineage mergeLineageWith(SigningCertificateLineage otherLineage) { // Determine the ancestor and descendant lineages; if the original signer is in the other // lineage, then it is considered a descendant. SigningCertificateLineage ancestorLineage; SigningCertificateLineage descendantLineage; X509Certificate signerCert = mSigningLineage.get(0).signingCert; if (otherLineage.isCertificateInLineage(signerCert)) { descendantLineage = this; ancestorLineage = otherLineage; } else { descendantLineage = otherLineage; ancestorLineage = this; } int ancestorIndex = 0; int descendantIndex = 0; SigningCertificateNode ancestorNode; SigningCertificateNode descendantNode = descendantLineage.mSigningLineage.get( descendantIndex++); List mergedLineage = new ArrayList<>(); // Iterate through the ancestor lineage and add the current node to the resulting lineage // until the first node of the descendant is found. while (ancestorIndex < ancestorLineage.size()) { ancestorNode = ancestorLineage.mSigningLineage.get(ancestorIndex++); if (ancestorNode.signingCert.equals(descendantNode.signingCert)) { break; } mergedLineage.add(ancestorNode); } // If all of the nodes in the ancestor lineage have been added to the merged lineage, then // there is no overlap between this and the provided lineage. if (ancestorIndex == mergedLineage.size()) { throw new IllegalArgumentException( "The provided lineage is not a descendant or an ancestor of this lineage"); } // The descendant lineage's first node was in the ancestor's lineage above; add it to the // merged lineage. mergedLineage.add(descendantNode); while (ancestorIndex < ancestorLineage.size() && descendantIndex < descendantLineage.size()) { ancestorNode = ancestorLineage.mSigningLineage.get(ancestorIndex++); descendantNode = descendantLineage.mSigningLineage.get(descendantIndex++); if (!ancestorNode.signingCert.equals(descendantNode.signingCert)) { throw new IllegalArgumentException( "The provided lineage diverges from this lineage"); } mergedLineage.add(descendantNode); } // At this point, one or both of the lineages have been exhausted and all signers to this // point were a match between the two lineages; add any remaining elements from either // lineage to the merged lineage. while (ancestorIndex < ancestorLineage.size()) { mergedLineage.add(ancestorLineage.mSigningLineage.get(ancestorIndex++)); } while (descendantIndex < descendantLineage.size()) { mergedLineage.add(descendantLineage.mSigningLineage.get(descendantIndex++)); } return new SigningCertificateLineage(Math.min(mMinSdkVersion, otherLineage.mMinSdkVersion), mergedLineage); } /** * Checks whether given lineages are compatible. Returns {@code true} if an installed APK with * the oldLineage could be updated with an APK with the newLineage. */ public static boolean checkLineagesCompatibility( SigningCertificateLineage oldLineage, SigningCertificateLineage newLineage) { final ArrayList oldCertificates = oldLineage == null ? new ArrayList() : new ArrayList(oldLineage.getCertificatesInLineage()); final ArrayList newCertificates = newLineage == null ? new ArrayList() : new ArrayList(newLineage.getCertificatesInLineage()); if (oldCertificates.isEmpty()) { return true; } if (newCertificates.isEmpty()) { return false; } // Both lineages contain exactly the same certificates or the new lineage extends // the old one. The capabilities of particular certificates may have changed though but it // does not matter in terms of current compatibility. if (newCertificates.size() >= oldCertificates.size() && newCertificates.subList(0, oldCertificates.size()).equals(oldCertificates)) { return true; } ArrayList newCertificatesArray = new ArrayList(newCertificates); ArrayList oldCertificatesArray = new ArrayList(oldCertificates); int lastOldCertIndexInNew = newCertificatesArray.lastIndexOf( oldCertificatesArray.get(oldCertificatesArray.size()-1)); // The new lineage trims some nodes from the beginning of the old lineage and possibly // extends it at the end. The new lineage must contain the old signing certificate and // the nodes up until the node with signing certificate must be in the same order. // Good example 1: // old: A -> B -> C // new: B -> C -> D // Good example 2: // old: A -> B -> C // new: C // Bad example 1: // old: A -> B -> C // new: A -> C // Bad example 1: // old: A -> B // new: C -> B if (lastOldCertIndexInNew >= 0) { return newCertificatesArray.subList(0, lastOldCertIndexInNew+1).equals( oldCertificatesArray.subList( oldCertificates.size()-1-lastOldCertIndexInNew, oldCertificatesArray.size())); } // The new lineage can be shorter than the old one only if the last certificate of the new // lineage exists in the old lineage and has a rollback capability there. // Good example: // old: A -> B_withRollbackCapability -> C // new: A -> B // Bad example 1: // old: A -> B -> C // new: A -> B // Bad example 2: // old: A -> B_withRollbackCapability -> C // new: A -> B -> D return oldCertificates.subList(0, newCertificates.size()).equals(newCertificates) && oldLineage.getSignerCapabilities( oldCertificates.get(newCertificates.size()-1)).hasRollback(); } /** * Representation of the capabilities the APK would like to grant to its old signing * certificates. The {@code SigningCertificateLineage} provides two conceptual data structures. * 1) proof of rotation - Evidence that other parties can trust an APK's current signing * certificate if they trust an older one in this lineage * 2) self-trust - certain capabilities may have been granted by an APK to other parties based * on its own signing certificate. When it changes its signing certificate it may want to * allow the other parties to retain those capabilities. * {@code SignerCapabilties} provides a representation of the second structure. * *

Use {@link Builder} to obtain configuration instances. */ public static class SignerCapabilities { private final int mFlags; private final int mCallerConfiguredFlags; private SignerCapabilities(int flags) { this(flags, 0); } private SignerCapabilities(int flags, int callerConfiguredFlags) { mFlags = flags; mCallerConfiguredFlags = callerConfiguredFlags; } private int getFlags() { return mFlags; } /** * Returns {@code true} if the capabilities of this object match those of the provided * object. */ @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof SignerCapabilities)) return false; return this.mFlags == ((SignerCapabilities) other).mFlags; } @Override public int hashCode() { return 31 * mFlags; } /** * Returns {@code true} if this object has the installed data capability. */ public boolean hasInstalledData() { return (mFlags & PAST_CERT_INSTALLED_DATA) != 0; } /** * Returns {@code true} if this object has the shared UID capability. */ public boolean hasSharedUid() { return (mFlags & PAST_CERT_SHARED_USER_ID) != 0; } /** * Returns {@code true} if this object has the permission capability. */ public boolean hasPermission() { return (mFlags & PAST_CERT_PERMISSION) != 0; } /** * Returns {@code true} if this object has the rollback capability. */ public boolean hasRollback() { return (mFlags & PAST_CERT_ROLLBACK) != 0; } /** * Returns {@code true} if this object has the auth capability. */ public boolean hasAuth() { return (mFlags & PAST_CERT_AUTH) != 0; } /** * Builder of {@link SignerCapabilities} instances. */ public static class Builder { private int mFlags; private int mCallerConfiguredFlags; /** * Constructs a new {@code Builder}. */ public Builder() { mFlags = calculateDefaultFlags(); } /** * Constructs a new {@code Builder} with the initial capabilities set to the provided * flags. */ public Builder(int flags) { mFlags = flags; } /** * Set the {@code PAST_CERT_INSTALLED_DATA} flag in this capabilities object. This flag * is used by the platform to determine if installed data associated with previous * signing certificate should be trusted. In particular, this capability is required to * perform signing certificate rotation during an upgrade on-device. Without it, the * platform will not permit the app data from the old signing certificate to * propagate to the new version. Typically, this flag should be set to enable signing * certificate rotation, and may be unset later when the app developer is satisfied that * their install base is as migrated as it will be. */ public Builder setInstalledData(boolean enabled) { mCallerConfiguredFlags |= PAST_CERT_INSTALLED_DATA; if (enabled) { mFlags |= PAST_CERT_INSTALLED_DATA; } else { mFlags &= ~PAST_CERT_INSTALLED_DATA; } return this; } /** * Set the {@code PAST_CERT_SHARED_USER_ID} flag in this capabilities object. This flag * is used by the platform to determine if this app is willing to be sharedUid with * other apps which are still signed with the associated signing certificate. This is * useful in situations where sharedUserId apps would like to change their signing * certificate, but can't guarantee the order of updates to those apps. */ public Builder setSharedUid(boolean enabled) { mCallerConfiguredFlags |= PAST_CERT_SHARED_USER_ID; if (enabled) { mFlags |= PAST_CERT_SHARED_USER_ID; } else { mFlags &= ~PAST_CERT_SHARED_USER_ID; } return this; } /** * Set the {@code PAST_CERT_PERMISSION} flag in this capabilities object. This flag * is used by the platform to determine if this app is willing to grant SIGNATURE * permissions to apps signed with the associated signing certificate. Without this * capability, an application signed with the older certificate will not be granted the * SIGNATURE permissions defined by this app. In addition, if multiple apps define the * same SIGNATURE permission, the second one the platform sees will not be installable * if this capability is not set and the signing certificates differ. */ public Builder setPermission(boolean enabled) { mCallerConfiguredFlags |= PAST_CERT_PERMISSION; if (enabled) { mFlags |= PAST_CERT_PERMISSION; } else { mFlags &= ~PAST_CERT_PERMISSION; } return this; } /** * Set the {@code PAST_CERT_ROLLBACK} flag in this capabilities object. This flag * is used by the platform to determine if this app is willing to upgrade to a new * version that is signed by one of its past signing certificates. * * WARNING: this effectively removes any benefit of signing certificate changes, * since a compromised key could retake control of an app even after change, and should * only be used if there is a problem encountered when trying to ditch an older cert * */ public Builder setRollback(boolean enabled) { mCallerConfiguredFlags |= PAST_CERT_ROLLBACK; if (enabled) { mFlags |= PAST_CERT_ROLLBACK; } else { mFlags &= ~PAST_CERT_ROLLBACK; } return this; } /** * Set the {@code PAST_CERT_AUTH} flag in this capabilities object. This flag * is used by the platform to determine whether or not privileged access based on * authenticator module signing certificates should be granted. */ public Builder setAuth(boolean enabled) { mCallerConfiguredFlags |= PAST_CERT_AUTH; if (enabled) { mFlags |= PAST_CERT_AUTH; } else { mFlags &= ~PAST_CERT_AUTH; } return this; } /** * Applies the capabilities that were explicitly set in the provided capabilities object * to this builder. Any values that were not set will not be applied to this builder * to prevent unintentinoally setting a capability back to a default value. */ public Builder setCallerConfiguredCapabilities(SignerCapabilities capabilities) { // The mCallerConfiguredFlags should have a bit set for each capability that was // set by a caller. If a capability was explicitly set then the corresponding bit // in mCallerConfiguredFlags should be set. This allows the provided capabilities // to take effect for those set by the caller while those that were not set will // be cleared by the bitwise and and the initial value for the builder will remain. mFlags = (mFlags & ~capabilities.mCallerConfiguredFlags) | (capabilities.mFlags & capabilities.mCallerConfiguredFlags); return this; } /** * Returns a new {@code SignerConfig} instance configured based on the configuration of * this builder. */ public SignerCapabilities build() { return new SignerCapabilities(mFlags, mCallerConfiguredFlags); } } } /** * Configuration of a signer. Used to add a new entry to the {@link SigningCertificateLineage} * *

Use {@link Builder} to obtain configuration instances. */ public static class SignerConfig { private final KeyConfig mKeyConfig; private final X509Certificate mCertificate; private SignerConfig(KeyConfig keyConfig, X509Certificate certificate) { mKeyConfig = keyConfig; mCertificate = certificate; } /** * Returns the signing key of this signer. * * @deprecated Use {@link #getKeyConfig()} instead of accessing a {@link PrivateKey} * directly. If the user of ApkSigner is signing with a KMS instead of JCA, this method * will return null. */ @Deprecated public PrivateKey getPrivateKey() { return mKeyConfig.match(jca -> jca.privateKey, kms -> null); } public KeyConfig getKeyConfig() { return mKeyConfig; } /** * Returns the certificate(s) of this signer. The first certificate's public key corresponds * to this signer's private key. */ public X509Certificate getCertificate() { return mCertificate; } /** * Builder of {@link SignerConfig} instances. */ public static class Builder { private final KeyConfig mKeyConfig; private final X509Certificate mCertificate; /** * Constructs a new {@code Builder}. * * @deprecated use {@link #Builder(KeyConfig, X509Certificate)} instead * @param privateKey signing key * @param certificate the X.509 certificate with a subject public key of the {@code * privateKey}. */ @Deprecated public Builder(PrivateKey privateKey, X509Certificate certificate) { mKeyConfig = new KeyConfig.Jca(privateKey); mCertificate = certificate; } /** * Constructs a new {@code Builder}. * * @param keyConfig signing key configuration * @param certificate the X.509 certificate with a subject public key of the {@code * privateKey}. */ public Builder(KeyConfig keyConfig, X509Certificate certificate) { mKeyConfig = keyConfig; mCertificate = certificate; } /** * Returns a new {@code SignerConfig} instance configured based on the configuration of * this builder. */ public SignerConfig build() { return new SignerConfig(mKeyConfig, mCertificate); } } } /** * Builder of {@link SigningCertificateLineage} instances. */ public static class Builder { private final SignerConfig mOriginalSignerConfig; private final SignerConfig mNewSignerConfig; private SignerCapabilities mOriginalCapabilities; private SignerCapabilities mNewCapabilities; private int mMinSdkVersion; /** * Constructs a new {@code Builder}. * * @param originalSignerConfig first signer in this lineage, parent of the next * @param newSignerConfig new signer in the lineage; the new signing key that the APK will * use */ public Builder( SignerConfig originalSignerConfig, SignerConfig newSignerConfig) { if (originalSignerConfig == null || newSignerConfig == null) { throw new NullPointerException("Can't pass null SignerConfigs when constructing a " + "new SigningCertificateLineage"); } mOriginalSignerConfig = originalSignerConfig; mNewSignerConfig = newSignerConfig; } /** * Constructs a new {@code Builder} that is intended to create a {@code * SigningCertificateLineage} with a single signer in the signing history. * * @param originalSignerConfig first signer in this lineage */ public Builder(SignerConfig originalSignerConfig) { if (originalSignerConfig == null) { throw new NullPointerException("Can't pass null SignerConfigs when constructing a " + "new SigningCertificateLineage"); } mOriginalSignerConfig = originalSignerConfig; mNewSignerConfig = null; } /** * Sets the minimum Android platform version (API Level) on which this lineage is expected * to validate. It is possible that newer signers in the lineage may not be recognized on * the given platform, but as long as an older signer is, the lineage can still be used to * sign an APK for the given platform. * * By default, this value is set to the value for the * P release, since this structure was created for that release, and will also be set to * that value if a smaller one is specified. */ public Builder setMinSdkVersion(int minSdkVersion) { mMinSdkVersion = minSdkVersion; return this; } /** * Sets capabilities to give {@code mOriginalSignerConfig}. These capabilities allow an * older signing certificate to still be used in some situations on the platform even though * the APK is now being signed by a newer signing certificate. */ public Builder setOriginalCapabilities(SignerCapabilities signerCapabilities) { if (signerCapabilities == null) { throw new NullPointerException("signerCapabilities == null"); } mOriginalCapabilities = signerCapabilities; return this; } /** * Sets capabilities to give {@code mNewSignerConfig}. These capabilities allow an * older signing certificate to still be used in some situations on the platform even though * the APK is now being signed by a newer signing certificate. By default, the new signer * will have all capabilities, so when first switching to a new signing certificate, these * capabilities have no effect, but they will act as the default level of trust when moving * to a new signing certificate. */ public Builder setNewCapabilities(SignerCapabilities signerCapabilities) { if (signerCapabilities == null) { throw new NullPointerException("signerCapabilities == null"); } mNewCapabilities = signerCapabilities; return this; } public SigningCertificateLineage build() throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { if (mMinSdkVersion < AndroidSdkVersion.P) { mMinSdkVersion = AndroidSdkVersion.P; } if (mOriginalCapabilities == null) { mOriginalCapabilities = new SignerCapabilities.Builder().build(); } if (mNewSignerConfig == null) { return createSigningLineage(mMinSdkVersion, mOriginalSignerConfig, mOriginalCapabilities); } if (mNewCapabilities == null) { mNewCapabilities = new SignerCapabilities.Builder().build(); } return createSigningLineage( mMinSdkVersion, mOriginalSignerConfig, mOriginalCapabilities, mNewSignerConfig, mNewCapabilities); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_SourceStampVerifier.java0100644 0000000 0000000 00000000034 14763776540 025700 xustar000000000 0000000 28 mtime=1741684064.5610000 src/main/java/com/android/apksig/SourceStampVerifier.java0100644 0000000 0000000 00000116447 14763776540 022561 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.Constants.VERSION_APK_SIGNATURE_SCHEME_V2; import static com.android.apksig.Constants.VERSION_APK_SIGNATURE_SCHEME_V3; import static com.android.apksig.Constants.VERSION_JAR_SIGNATURE_SCHEME; import static com.android.apksig.apk.ApkUtilsLite.computeSha256DigestBytes; import static com.android.apksig.internal.apk.stamp.SourceStampConstants.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; import static com.android.apksig.internal.apk.v1.V1SchemeConstants.MANIFEST_ENTRY_NAME; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtilsLite; import com.android.apksig.internal.apk.ApkSigResult; import com.android.apksig.internal.apk.ApkSignerInfo; import com.android.apksig.internal.apk.ApkSigningBlockUtilsLite; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.apk.SignatureNotFoundException; import com.android.apksig.internal.apk.stamp.SourceStampConstants; import com.android.apksig.internal.apk.stamp.V2SourceStampVerifier; import com.android.apksig.internal.apk.v2.V2SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import com.android.apksig.internal.zip.CentralDirectoryRecord; import com.android.apksig.internal.zip.LocalFileRecord; import com.android.apksig.internal.zip.ZipUtils; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import com.android.apksig.zip.ZipFormatException; import com.android.apksig.zip.ZipSections; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; /** * APK source stamp verifier intended only to verify the validity of the stamp signature. * *

Note, this verifier does not validate the signatures of the jar signing / APK signature blocks * when obtaining the digests for verification. This verifier should only be used in cases where * another mechanism has already been used to verify the APK signatures. */ public class SourceStampVerifier { private final File mApkFile; private final DataSource mApkDataSource; private final int mMinSdkVersion; private final int mMaxSdkVersion; private SourceStampVerifier( File apkFile, DataSource apkDataSource, int minSdkVersion, int maxSdkVersion) { mApkFile = apkFile; mApkDataSource = apkDataSource; mMinSdkVersion = minSdkVersion; mMaxSdkVersion = maxSdkVersion; } /** * Verifies the APK's source stamp signature and returns the result of the verification. * *

The APK's source stamp can be considered verified if the result's {@link * Result#isVerified()} returns {@code true}. If source stamp verification fails all of the * resulting errors can be obtained from {@link Result#getAllErrors()}, or individual errors * can be obtained as follows: *

    *
  • Obtain the generic errors via {@link Result#getErrors()} *
  • Obtain the V2 signers via {@link Result#getV2SchemeSigners()}, then for each signer * query for any errors with {@link Result.SignerInfo#getErrors()} *
  • Obtain the V3 signers via {@link Result#getV3SchemeSigners()}, then for each signer * query for any errors with {@link Result.SignerInfo#getErrors()} *
  • Obtain the source stamp signer via {@link Result#getSourceStampInfo()}, then query * for any stamp errors with {@link Result.SourceStampInfo#getErrors()} *
*/ public SourceStampVerifier.Result verifySourceStamp() { return verifySourceStamp(null); } /** * Verifies the APK's source stamp signature, including verification that the SHA-256 digest of * the stamp signing certificate matches the {@code expectedCertDigest}, and returns the result * of the verification. * *

A value of {@code null} for the {@code expectedCertDigest} will verify the source stamp, * if present, without verifying the actual source stamp certificate used to sign the source * stamp. This can be used to verify an APK contains a properly signed source stamp without * verifying a particular signer. * * @see #verifySourceStamp() */ public SourceStampVerifier.Result verifySourceStamp(String expectedCertDigest) { Closeable in = null; try { DataSource apk; if (mApkDataSource != null) { apk = mApkDataSource; } else if (mApkFile != null) { RandomAccessFile f = new RandomAccessFile(mApkFile, "r"); in = f; apk = DataSources.asDataSource(f, 0, f.length()); } else { throw new IllegalStateException("APK not provided"); } return verifySourceStamp(apk, expectedCertDigest); } catch (IOException e) { Result result = new Result(); result.addVerificationError(ApkVerificationIssue.UNEXPECTED_EXCEPTION, e); return result; } finally { if (in != null) { try { in.close(); } catch (IOException ignored) { } } } } /** * Verifies the provided {@code apk}'s source stamp signature, including verification of the * SHA-256 digest of the stamp signing certificate matches the {@code expectedCertDigest}, and * returns the result of the verification. * * @see #verifySourceStamp(String) */ private SourceStampVerifier.Result verifySourceStamp(DataSource apk, String expectedCertDigest) { Result result = new Result(); try { ZipSections zipSections = ApkUtilsLite.findZipSections(apk); // Attempt to obtain the source stamp's certificate digest from the APK. List cdRecords = ZipUtils.parseZipCentralDirectory(apk, zipSections); CentralDirectoryRecord sourceStampCdRecord = null; for (CentralDirectoryRecord cdRecord : cdRecords) { if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) { sourceStampCdRecord = cdRecord; break; } } // If the source stamp's certificate digest is not available within the APK then the // source stamp cannot be verified; check if a source stamp signing block is in the // APK's signature block to determine the appropriate status to return. if (sourceStampCdRecord == null) { boolean stampSigningBlockFound; try { ApkSigningBlockUtilsLite.findSignature(apk, zipSections, SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID); stampSigningBlockFound = true; } catch (SignatureNotFoundException e) { stampSigningBlockFound = false; } result.addVerificationError(stampSigningBlockFound ? ApkVerificationIssue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST : ApkVerificationIssue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING); return result; } // Verify that the contents of the source stamp certificate digest match the expected // value, if provided. byte[] sourceStampCertificateDigest = LocalFileRecord.getUncompressedData( apk, sourceStampCdRecord, zipSections.getZipCentralDirectoryOffset()); if (expectedCertDigest != null) { String actualCertDigest = ApkSigningBlockUtilsLite.toHex( sourceStampCertificateDigest); if (!expectedCertDigest.equalsIgnoreCase(actualCertDigest)) { result.addVerificationError( ApkVerificationIssue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH, actualCertDigest, expectedCertDigest); return result; } } Map> signatureSchemeApkContentDigests = new HashMap<>(); if (mMaxSdkVersion >= AndroidSdkVersion.P) { SignatureInfo signatureInfo; try { signatureInfo = ApkSigningBlockUtilsLite.findSignature(apk, zipSections, V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID); } catch (SignatureNotFoundException e) { signatureInfo = null; } if (signatureInfo != null) { Map apkContentDigests = new EnumMap<>( ContentDigestAlgorithm.class); parseSigners(signatureInfo.signatureBlock, VERSION_APK_SIGNATURE_SCHEME_V3, apkContentDigests, result); signatureSchemeApkContentDigests.put( VERSION_APK_SIGNATURE_SCHEME_V3, apkContentDigests); } } if (mMaxSdkVersion >= AndroidSdkVersion.N && (mMinSdkVersion < AndroidSdkVersion.P || signatureSchemeApkContentDigests.isEmpty())) { SignatureInfo signatureInfo; try { signatureInfo = ApkSigningBlockUtilsLite.findSignature(apk, zipSections, V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID); } catch (SignatureNotFoundException e) { signatureInfo = null; } if (signatureInfo != null) { Map apkContentDigests = new EnumMap<>( ContentDigestAlgorithm.class); parseSigners(signatureInfo.signatureBlock, VERSION_APK_SIGNATURE_SCHEME_V2, apkContentDigests, result); signatureSchemeApkContentDigests.put( VERSION_APK_SIGNATURE_SCHEME_V2, apkContentDigests); } } if (mMinSdkVersion < AndroidSdkVersion.N || signatureSchemeApkContentDigests.isEmpty()) { Map apkContentDigests = getApkContentDigestFromV1SigningScheme(cdRecords, apk, zipSections, result); signatureSchemeApkContentDigests.put(VERSION_JAR_SIGNATURE_SCHEME, apkContentDigests); } ApkSigResult sourceStampResult = V2SourceStampVerifier.verify( apk, zipSections, sourceStampCertificateDigest, signatureSchemeApkContentDigests, mMinSdkVersion, mMaxSdkVersion); result.mergeFrom(sourceStampResult); return result; } catch (ApkFormatException | IOException | ZipFormatException e) { result.addVerificationError(ApkVerificationIssue.MALFORMED_APK, e); } catch (NoSuchAlgorithmException e) { result.addVerificationError(ApkVerificationIssue.UNEXPECTED_EXCEPTION, e); } catch (SignatureNotFoundException e) { result.addVerificationError(ApkVerificationIssue.SOURCE_STAMP_SIG_MISSING); } return result; } /** * Parses each signer in the provided APK V2 / V3 signature block and populates corresponding * {@code SignerInfo} of the provided {@code result} and their {@code apkContentDigests}. * *

This method adds one or more errors to the {@code result} if a verification error is * expected to be encountered on an Android platform version in the * {@code [minSdkVersion, maxSdkVersion]} range. */ public static void parseSigners( ByteBuffer apkSignatureSchemeBlock, int apkSigSchemeVersion, Map apkContentDigests, Result result) { boolean isV2Block = apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V2; // Both the V2 and V3 signature blocks contain the following: // * length-prefixed sequence of length-prefixed signers ByteBuffer signers; try { signers = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(apkSignatureSchemeBlock); } catch (ApkFormatException e) { result.addVerificationWarning(isV2Block ? ApkVerificationIssue.V2_SIG_MALFORMED_SIGNERS : ApkVerificationIssue.V3_SIG_MALFORMED_SIGNERS); return; } if (!signers.hasRemaining()) { result.addVerificationWarning(isV2Block ? ApkVerificationIssue.V2_SIG_NO_SIGNERS : ApkVerificationIssue.V3_SIG_NO_SIGNERS); return; } CertificateFactory certFactory; try { certFactory = CertificateFactory.getInstance("X.509"); } catch (CertificateException e) { throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); } while (signers.hasRemaining()) { Result.SignerInfo signerInfo = new Result.SignerInfo(); if (isV2Block) { result.addV2Signer(signerInfo); } else { result.addV3Signer(signerInfo); } try { ByteBuffer signer = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(signers); parseSigner( signer, apkSigSchemeVersion, certFactory, apkContentDigests, signerInfo); } catch (ApkFormatException | BufferUnderflowException e) { signerInfo.addVerificationWarning( isV2Block ? ApkVerificationIssue.V2_SIG_MALFORMED_SIGNER : ApkVerificationIssue.V3_SIG_MALFORMED_SIGNER); return; } } } /** * Parses the provided signer block and populates the {@code result}. * *

This verifies signatures over {@code signed-data} contained in this block but does not * verify the integrity of the rest of the APK. To facilitate APK integrity verification, this * method adds the {@code contentDigestsToVerify}. These digests can then be used to verify the * integrity of the APK. * *

This method adds one or more errors to the {@code result} if a verification error is * expected to be encountered on an Android platform version in the * {@code [minSdkVersion, maxSdkVersion]} range. */ private static void parseSigner( ByteBuffer signerBlock, int apkSigSchemeVersion, CertificateFactory certFactory, Map apkContentDigests, Result.SignerInfo signerInfo) throws ApkFormatException { boolean isV2Signer = apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V2; // Both the V2 and V3 signer blocks contain the following: // * length-prefixed signed data // * length-prefixed sequence of length-prefixed digests: // * uint32: signature algorithm ID // * length-prefixed bytes: digest of contents // * length-prefixed sequence of certificates: // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded). ByteBuffer signedData = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(signerBlock); ByteBuffer digests = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(signedData); ByteBuffer certificates = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(signedData); // Parse the digests block while (digests.hasRemaining()) { try { ByteBuffer digest = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(digests); int sigAlgorithmId = digest.getInt(); byte[] digestBytes = ApkSigningBlockUtilsLite.readLengthPrefixedByteArray(digest); SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId); if (signatureAlgorithm == null) { continue; } apkContentDigests.put(signatureAlgorithm.getContentDigestAlgorithm(), digestBytes); } catch (ApkFormatException | BufferUnderflowException e) { signerInfo.addVerificationWarning( isV2Signer ? ApkVerificationIssue.V2_SIG_MALFORMED_DIGEST : ApkVerificationIssue.V3_SIG_MALFORMED_DIGEST); return; } } // Parse the certificates block if (certificates.hasRemaining()) { byte[] encodedCert = ApkSigningBlockUtilsLite.readLengthPrefixedByteArray(certificates); X509Certificate certificate; try { certificate = (X509Certificate) certFactory.generateCertificate( new ByteArrayInputStream(encodedCert)); } catch (CertificateException e) { signerInfo.addVerificationWarning( isV2Signer ? ApkVerificationIssue.V2_SIG_MALFORMED_CERTIFICATE : ApkVerificationIssue.V3_SIG_MALFORMED_CERTIFICATE); return; } // Wrap the cert so that the result's getEncoded returns exactly the original encoded // form. Without this, getEncoded may return a different form from what was stored in // the signature. This is because some X509Certificate(Factory) implementations // re-encode certificates. certificate = new GuaranteedEncodedFormX509Certificate(certificate, encodedCert); signerInfo.setSigningCertificate(certificate); } if (signerInfo.getSigningCertificate() == null) { signerInfo.addVerificationWarning( isV2Signer ? ApkVerificationIssue.V2_SIG_NO_CERTIFICATES : ApkVerificationIssue.V3_SIG_NO_CERTIFICATES); return; } } /** * Returns a mapping of the {@link ContentDigestAlgorithm} to the {@code byte[]} digest of the * V1 / jar signing META-INF/MANIFEST.MF; if this file is not found then an empty {@code Map} is * returned. * *

If any errors are encountered while parsing the V1 signers the provided {@code result} * will be updated to include a warning, but the source stamp verification can still proceed. */ private static Map getApkContentDigestFromV1SigningScheme( List cdRecords, DataSource apk, ZipSections zipSections, Result result) throws IOException, ApkFormatException { CentralDirectoryRecord manifestCdRecord = null; List signatureBlockRecords = new ArrayList<>(1); Map v1ContentDigest = new EnumMap<>( ContentDigestAlgorithm.class); for (CentralDirectoryRecord cdRecord : cdRecords) { String cdRecordName = cdRecord.getName(); if (cdRecordName == null) { continue; } if (manifestCdRecord == null && MANIFEST_ENTRY_NAME.equals(cdRecordName)) { manifestCdRecord = cdRecord; continue; } if (cdRecordName.startsWith("META-INF/") && (cdRecordName.endsWith(".RSA") || cdRecordName.endsWith(".DSA") || cdRecordName.endsWith(".EC"))) { signatureBlockRecords.add(cdRecord); } } if (manifestCdRecord == null) { // No JAR signing manifest file found. For SourceStamp verification, returning an empty // digest is enough since this would affect the final digest signed by the stamp, and // thus an empty digest will invalidate that signature. return v1ContentDigest; } if (signatureBlockRecords.isEmpty()) { result.addVerificationWarning(ApkVerificationIssue.JAR_SIG_NO_SIGNATURES); } else { for (CentralDirectoryRecord signatureBlockRecord : signatureBlockRecords) { try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); byte[] signatureBlockBytes = LocalFileRecord.getUncompressedData(apk, signatureBlockRecord, zipSections.getZipCentralDirectoryOffset()); for (Certificate certificate : certFactory.generateCertificates( new ByteArrayInputStream(signatureBlockBytes))) { // If multiple certificates are found within the signature block only the // first is used as the signer of this block. if (certificate instanceof X509Certificate) { Result.SignerInfo signerInfo = new Result.SignerInfo(); signerInfo.setSigningCertificate((X509Certificate) certificate); result.addV1Signer(signerInfo); break; } } } catch (CertificateException e) { // Log a warning for the parsing exception but still proceed with the stamp // verification. result.addVerificationWarning(ApkVerificationIssue.JAR_SIG_PARSE_EXCEPTION, signatureBlockRecord.getName(), e); break; } catch (ZipFormatException e) { throw new ApkFormatException("Failed to read APK", e); } } } try { byte[] manifestBytes = LocalFileRecord.getUncompressedData( apk, manifestCdRecord, zipSections.getZipCentralDirectoryOffset()); v1ContentDigest.put( ContentDigestAlgorithm.SHA256, computeSha256DigestBytes(manifestBytes)); return v1ContentDigest; } catch (ZipFormatException e) { throw new ApkFormatException("Failed to read APK", e); } } /** * Result of verifying the APK's source stamp signature; this signature can only be considered * verified if {@link #isVerified()} returns true. */ public static class Result { private final List mV1SchemeSigners = new ArrayList<>(); private final List mV2SchemeSigners = new ArrayList<>(); private final List mV3SchemeSigners = new ArrayList<>(); private final List> mAllSchemeSigners = Arrays.asList(mV1SchemeSigners, mV2SchemeSigners, mV3SchemeSigners); private SourceStampInfo mSourceStampInfo; private final List mErrors = new ArrayList<>(); private final List mWarnings = new ArrayList<>(); private boolean mVerified; void addVerificationError(int errorId, Object... params) { mErrors.add(new ApkVerificationIssue(errorId, params)); } void addVerificationWarning(int warningId, Object... params) { mWarnings.add(new ApkVerificationIssue(warningId, params)); } private void addV1Signer(SignerInfo signerInfo) { mV1SchemeSigners.add(signerInfo); } private void addV2Signer(SignerInfo signerInfo) { mV2SchemeSigners.add(signerInfo); } private void addV3Signer(SignerInfo signerInfo) { mV3SchemeSigners.add(signerInfo); } /** * Returns {@code true} if the APK's source stamp signature */ public boolean isVerified() { return mVerified; } private void mergeFrom(ApkSigResult source) { switch (source.signatureSchemeVersion) { case Constants.VERSION_SOURCE_STAMP: mVerified = source.verified; if (!source.mSigners.isEmpty()) { mSourceStampInfo = new SourceStampInfo(source.mSigners.get(0)); } break; default: throw new IllegalArgumentException( "Unknown ApkSigResult Signing Block Scheme Id " + source.signatureSchemeVersion); } } /** * Returns a {@code List} of {@link SignerInfo} objects representing the V1 signers of the * provided APK. */ public List getV1SchemeSigners() { return mV1SchemeSigners; } /** * Returns a {@code List} of {@link SignerInfo} objects representing the V2 signers of the * provided APK. */ public List getV2SchemeSigners() { return mV2SchemeSigners; } /** * Returns a {@code List} of {@link SignerInfo} objects representing the V3 signers of the * provided APK. */ public List getV3SchemeSigners() { return mV3SchemeSigners; } /** * Returns the {@link SourceStampInfo} instance representing the source stamp signer for the * APK, or null if the source stamp signature verification failed before the stamp signature * block could be fully parsed. */ public SourceStampInfo getSourceStampInfo() { return mSourceStampInfo; } /** * Returns {@code true} if an error was encountered while verifying the APK. * *

Any error prevents the APK from being considered verified. */ public boolean containsErrors() { if (!mErrors.isEmpty()) { return true; } for (List signers : mAllSchemeSigners) { for (SignerInfo signer : signers) { if (signer.containsErrors()) { return true; } } } if (mSourceStampInfo != null) { if (mSourceStampInfo.containsErrors()) { return true; } } return false; } /** * Returns the errors encountered while verifying the APK's source stamp. */ public List getErrors() { return mErrors; } /** * Returns the warnings encountered while verifying the APK's source stamp. */ public List getWarnings() { return mWarnings; } /** * Returns all errors for this result, including any errors from signature scheme signers * and the source stamp. */ public List getAllErrors() { List errors = new ArrayList<>(); errors.addAll(mErrors); for (List signers : mAllSchemeSigners) { for (SignerInfo signer : signers) { errors.addAll(signer.getErrors()); } } if (mSourceStampInfo != null) { errors.addAll(mSourceStampInfo.getErrors()); } return errors; } /** * Returns all warnings for this result, including any warnings from signature scheme * signers and the source stamp. */ public List getAllWarnings() { List warnings = new ArrayList<>(); warnings.addAll(mWarnings); for (List signers : mAllSchemeSigners) { for (SignerInfo signer : signers) { warnings.addAll(signer.getWarnings()); } } if (mSourceStampInfo != null) { warnings.addAll(mSourceStampInfo.getWarnings()); } return warnings; } /** * Contains information about an APK's signer and any errors encountered while parsing the * corresponding signature block. */ public static class SignerInfo { private X509Certificate mSigningCertificate; private final List mErrors = new ArrayList<>(); private final List mWarnings = new ArrayList<>(); void setSigningCertificate(X509Certificate signingCertificate) { mSigningCertificate = signingCertificate; } void addVerificationError(int errorId, Object... params) { mErrors.add(new ApkVerificationIssue(errorId, params)); } void addVerificationWarning(int warningId, Object... params) { mWarnings.add(new ApkVerificationIssue(warningId, params)); } /** * Returns the current signing certificate used by this signer. */ public X509Certificate getSigningCertificate() { return mSigningCertificate; } /** * Returns a {@link List} of {@link ApkVerificationIssue} objects representing errors * encountered during processing of this signer's signature block. */ public List getErrors() { return mErrors; } /** * Returns a {@link List} of {@link ApkVerificationIssue} objects representing warnings * encountered during processing of this signer's signature block. */ public List getWarnings() { return mWarnings; } /** * Returns {@code true} if any errors were encountered while parsing this signer's * signature block. */ public boolean containsErrors() { return !mErrors.isEmpty(); } } /** * Contains information about an APK's source stamp and any errors encountered while * parsing the stamp signature block. */ public static class SourceStampInfo { private final List mCertificates; private final List mCertificateLineage; private final List mErrors = new ArrayList<>(); private final List mWarnings = new ArrayList<>(); private final List mInfoMessages = new ArrayList<>(); private final long mTimestamp; /* * Since this utility is intended just to verify the source stamp, and the source stamp * currently only logs warnings to prevent failing the APK signature verification, treat * all warnings as errors. If the stamp verification is updated to log errors this * should be set to false to ensure only errors trigger a failure verifying the source * stamp. */ private static final boolean mWarningsAsErrors = true; private SourceStampInfo(ApkSignerInfo result) { mCertificates = result.certs; mCertificateLineage = result.certificateLineage; mErrors.addAll(result.getErrors()); mWarnings.addAll(result.getWarnings()); mInfoMessages.addAll(result.getInfoMessages()); mTimestamp = result.timestamp; } /** * Returns the SourceStamp's signing certificate or {@code null} if not available. The * certificate is guaranteed to be available if no errors were encountered during * verification (see {@link #containsErrors()}. * *

This certificate contains the SourceStamp's public key. */ public X509Certificate getCertificate() { return mCertificates.isEmpty() ? null : mCertificates.get(0); } /** * Returns a {@code List} of {@link X509Certificate} instances representing the source * stamp signer's lineage with the oldest signer at element 0, or an empty {@code List} * if the stamp's signing certificate has not been rotated. */ public List getCertificatesInLineage() { return mCertificateLineage; } /** * Returns whether any errors were encountered during the source stamp verification. */ public boolean containsErrors() { return !mErrors.isEmpty() || (mWarningsAsErrors && !mWarnings.isEmpty()); } /** * Returns {@code true} if any info messages were encountered during verification of * this source stamp. */ public boolean containsInfoMessages() { return !mInfoMessages.isEmpty(); } /** * Returns a {@code List} of {@link ApkVerificationIssue} representing errors that were * encountered during source stamp verification. */ public List getErrors() { if (!mWarningsAsErrors) { return mErrors; } List result = new ArrayList<>(); result.addAll(mErrors); result.addAll(mWarnings); return result; } /** * Returns a {@code List} of {@link ApkVerificationIssue} representing warnings that * were encountered during source stamp verification. */ public List getWarnings() { return mWarnings; } /** * Returns a {@code List} of {@link ApkVerificationIssue} representing info messages * that were encountered during source stamp verification. */ public List getInfoMessages() { return mInfoMessages; } /** * Returns the epoch timestamp in seconds representing the time this source stamp block * was signed, or 0 if the timestamp is not available. */ public long getTimestampEpochSeconds() { return mTimestamp; } } } /** * Builder of {@link SourceStampVerifier} instances. * *

The resulting verifier, by default, checks whether the APK's source stamp signature will * verify on all platform versions. The APK's {@code android:minSdkVersion} attribute is not * queried to determine the APK's minimum supported level, so the caller should specify a lower * bound with {@link #setMinCheckedPlatformVersion(int)}. */ public static class Builder { private final File mApkFile; private final DataSource mApkDataSource; private int mMinSdkVersion = 1; private int mMaxSdkVersion = Integer.MAX_VALUE; /** * Constructs a new {@code Builder} for source stamp verification of the provided {@code * apk}. */ public Builder(File apk) { if (apk == null) { throw new NullPointerException("apk == null"); } mApkFile = apk; mApkDataSource = null; } /** * Constructs a new {@code Builder} for source stamp verification of the provided {@code * apk}. */ public Builder(DataSource apk) { if (apk == null) { throw new NullPointerException("apk == null"); } mApkDataSource = apk; mApkFile = null; } /** * Sets the oldest Android platform version for which the APK's source stamp is verified. * *

APK source stamp verification will confirm that the APK's stamp is expected to verify * on all Android platforms starting from the platform version with the provided {@code * minSdkVersion}. The upper end of the platform versions range can be modified via * {@link #setMaxCheckedPlatformVersion(int)}. * * @param minSdkVersion API Level of the oldest platform for which to verify the APK */ public SourceStampVerifier.Builder setMinCheckedPlatformVersion(int minSdkVersion) { mMinSdkVersion = minSdkVersion; return this; } /** * Sets the newest Android platform version for which the APK's source stamp is verified. * *

APK source stamp verification will confirm that the APK's stamp is expected to verify * on all platform versions up to and including the proviced {@code maxSdkVersion}. The * lower end of the platform versions range can be modified via {@link * #setMinCheckedPlatformVersion(int)}. * * @param maxSdkVersion API Level of the newest platform for which to verify the APK * @see #setMinCheckedPlatformVersion(int) */ public SourceStampVerifier.Builder setMaxCheckedPlatformVersion(int maxSdkVersion) { mMaxSdkVersion = maxSdkVersion; return this; } /** * Returns a {@link SourceStampVerifier} initialized according to the configuration of this * builder. */ public SourceStampVerifier build() { return new SourceStampVerifier( mApkFile, mApkDataSource, mMinSdkVersion, mMaxSdkVersion); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_apk_0100644 0000000 0000000 00000000034 14763776540 021731 xustar000000000 0000000 28 mtime=1741684064.5620000 src/main/java/com/android/apksig/apk/0040755 0000000 0000000 00000000000 14763776540 016516 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_apk_ApkFormatException.java0100644 0000000 0000000 00000000034 14763776540 026335 xustar000000000 0000000 28 mtime=1741684064.5630000 src/main/java/com/android/apksig/apk/ApkFormatException.java0100644 0000000 0000000 00000002363 14763776540 023125 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.apk; /** * Indicates that an APK is not well-formed. For example, this may indicate that the APK is not a * well-formed ZIP archive, in which case {@link #getCause()} will return a * {@link com.android.apksig.zip.ZipFormatException ZipFormatException}, or that the APK contains * multiple ZIP entries with the same name. */ public class ApkFormatException extends Exception { private static final long serialVersionUID = 1L; public ApkFormatException(String message) { super(message); } public ApkFormatException(String message, Throwable cause) { super(message, cause); } } ./PaxHeaders.X/src_main_java_com_android_apksig_apk_ApkSigningBlockNotFoundException.java0100644 0000000 0000000 00000000034 14763776540 031133 xustar000000000 0000000 28 mtime=1741684064.5630000 src/main/java/com/android/apksig/apk/ApkSigningBlockNotFoundException.java0100644 0000000 0000000 00000002035 14763776540 025717 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.apk; /** * Indicates that no APK Signing Block was found in an APK. */ public class ApkSigningBlockNotFoundException extends Exception { private static final long serialVersionUID = 1L; public ApkSigningBlockNotFoundException(String message) { super(message); } public ApkSigningBlockNotFoundException(String message, Throwable cause) { super(message, cause); } } ./PaxHeaders.X/src_main_java_com_android_apksig_apk_ApkUtils.java0100644 0000000 0000000 00000000034 14763776540 024326 xustar000000000 0000000 28 mtime=1741684064.5630000 src/main/java/com/android/apksig/apk/ApkUtils.java0100644 0000000 0000000 00000100261 14763776540 021112 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.apk; import com.android.apksig.internal.apk.AndroidBinXmlParser; import com.android.apksig.internal.apk.stamp.SourceStampConstants; import com.android.apksig.internal.apk.v1.V1SchemeVerifier; import com.android.apksig.internal.util.Pair; import com.android.apksig.internal.zip.CentralDirectoryRecord; import com.android.apksig.internal.zip.LocalFileRecord; import com.android.apksig.internal.zip.ZipUtils; import com.android.apksig.util.DataSource; import com.android.apksig.zip.ZipFormatException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Comparator; import java.util.List; /** * APK utilities. */ public abstract class ApkUtils { /** * Name of the Android manifest ZIP entry in APKs. */ public static final String ANDROID_MANIFEST_ZIP_ENTRY_NAME = "AndroidManifest.xml"; /** Name of the SourceStamp certificate hash ZIP entry in APKs. */ public static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = SourceStampConstants.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; private ApkUtils() {} /** * Finds the main ZIP sections of the provided APK. * * @throws IOException if an I/O error occurred while reading the APK * @throws ZipFormatException if the APK is malformed */ public static ZipSections findZipSections(DataSource apk) throws IOException, ZipFormatException { com.android.apksig.zip.ZipSections zipSections = ApkUtilsLite.findZipSections(apk); return new ZipSections( zipSections.getZipCentralDirectoryOffset(), zipSections.getZipCentralDirectorySizeBytes(), zipSections.getZipCentralDirectoryRecordCount(), zipSections.getZipEndOfCentralDirectoryOffset(), zipSections.getZipEndOfCentralDirectory()); } /** * Information about the ZIP sections of an APK. */ public static class ZipSections extends com.android.apksig.zip.ZipSections { public ZipSections( long centralDirectoryOffset, long centralDirectorySizeBytes, int centralDirectoryRecordCount, long eocdOffset, ByteBuffer eocd) { super(centralDirectoryOffset, centralDirectorySizeBytes, centralDirectoryRecordCount, eocdOffset, eocd); } } /** * Sets the offset of the start of the ZIP Central Directory in the APK's ZIP End of Central * Directory record. * * @param zipEndOfCentralDirectory APK's ZIP End of Central Directory record * @param offset offset of the ZIP Central Directory relative to the start of the archive. Must * be between {@code 0} and {@code 2^32 - 1} inclusive. */ public static void setZipEocdCentralDirectoryOffset( ByteBuffer zipEndOfCentralDirectory, long offset) { ByteBuffer eocd = zipEndOfCentralDirectory.slice(); eocd.order(ByteOrder.LITTLE_ENDIAN); ZipUtils.setZipEocdCentralDirectoryOffset(eocd, offset); } /** * Updates the length of EOCD comment. * * @param zipEndOfCentralDirectory APK's ZIP End of Central Directory record */ public static void updateZipEocdCommentLen(ByteBuffer zipEndOfCentralDirectory) { ByteBuffer eocd = zipEndOfCentralDirectory.slice(); eocd.order(ByteOrder.LITTLE_ENDIAN); ZipUtils.updateZipEocdCommentLen(eocd); } /** * Returns the APK Signing Block of the provided {@code apk}. * * @throws ApkFormatException if the APK is not a valid ZIP archive * @throws IOException if an I/O error occurs * @throws ApkSigningBlockNotFoundException if there is no APK Signing Block in the APK * * @see APK Signature Scheme v2 * */ public static ApkSigningBlock findApkSigningBlock(DataSource apk) throws ApkFormatException, IOException, ApkSigningBlockNotFoundException { ApkUtils.ZipSections inputZipSections; try { inputZipSections = ApkUtils.findZipSections(apk); } catch (ZipFormatException e) { throw new ApkFormatException("Malformed APK: not a ZIP archive", e); } return findApkSigningBlock(apk, inputZipSections); } /** * Returns the APK Signing Block of the provided APK. * * @throws IOException if an I/O error occurs * @throws ApkSigningBlockNotFoundException if there is no APK Signing Block in the APK * * @see APK Signature Scheme v2 * */ public static ApkSigningBlock findApkSigningBlock(DataSource apk, ZipSections zipSections) throws IOException, ApkSigningBlockNotFoundException { ApkUtilsLite.ApkSigningBlock apkSigningBlock = ApkUtilsLite.findApkSigningBlock(apk, zipSections); return new ApkSigningBlock(apkSigningBlock.getStartOffset(), apkSigningBlock.getContents()); } /** * Information about the location of the APK Signing Block inside an APK. */ public static class ApkSigningBlock extends ApkUtilsLite.ApkSigningBlock { /** * Constructs a new {@code ApkSigningBlock}. * * @param startOffsetInApk start offset (in bytes, relative to start of file) of the APK * Signing Block inside the APK file * @param contents contents of the APK Signing Block */ public ApkSigningBlock(long startOffsetInApk, DataSource contents) { super(startOffsetInApk, contents); } } /** * Returns the contents of the APK's {@code AndroidManifest.xml}. * * @throws IOException if an I/O error occurs while reading the APK * @throws ApkFormatException if the APK is malformed */ public static ByteBuffer getAndroidManifest(DataSource apk) throws IOException, ApkFormatException { ZipSections zipSections; try { zipSections = findZipSections(apk); } catch (ZipFormatException e) { throw new ApkFormatException("Not a valid ZIP archive", e); } List cdRecords = V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections); CentralDirectoryRecord androidManifestCdRecord = null; for (CentralDirectoryRecord cdRecord : cdRecords) { if (ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(cdRecord.getName())) { androidManifestCdRecord = cdRecord; break; } } if (androidManifestCdRecord == null) { throw new ApkFormatException("Missing " + ANDROID_MANIFEST_ZIP_ENTRY_NAME); } DataSource lfhSection = apk.slice(0, zipSections.getZipCentralDirectoryOffset()); try { return ByteBuffer.wrap( LocalFileRecord.getUncompressedData( lfhSection, androidManifestCdRecord, lfhSection.size())); } catch (ZipFormatException e) { throw new ApkFormatException("Failed to read " + ANDROID_MANIFEST_ZIP_ENTRY_NAME, e); } } /** * Android resource ID of the {@code android:minSdkVersion} attribute in AndroidManifest.xml. */ private static final int MIN_SDK_VERSION_ATTR_ID = 0x0101020c; /** * Android resource ID of the {@code android:debuggable} attribute in AndroidManifest.xml. */ private static final int DEBUGGABLE_ATTR_ID = 0x0101000f; /** * Android resource ID of the {@code android:targetSandboxVersion} attribute in * AndroidManifest.xml. */ private static final int TARGET_SANDBOX_VERSION_ATTR_ID = 0x0101054c; /** * Android resource ID of the {@code android:targetSdkVersion} attribute in * AndroidManifest.xml. */ private static final int TARGET_SDK_VERSION_ATTR_ID = 0x01010270; private static final String USES_SDK_ELEMENT_TAG = "uses-sdk"; /** * Android resource ID of the {@code android:versionCode} attribute in AndroidManifest.xml. */ private static final int VERSION_CODE_ATTR_ID = 0x0101021b; private static final String MANIFEST_ELEMENT_TAG = "manifest"; /** * Android resource ID of the {@code android:versionCodeMajor} attribute in AndroidManifest.xml. */ private static final int VERSION_CODE_MAJOR_ATTR_ID = 0x01010576; /** * Returns the lowest Android platform version (API Level) supported by an APK with the * provided {@code AndroidManifest.xml}. * * @param androidManifestContents contents of {@code AndroidManifest.xml} in binary Android * resource format * * @throws MinSdkVersionException if an error occurred while determining the API Level */ public static int getMinSdkVersionFromBinaryAndroidManifest( ByteBuffer androidManifestContents) throws MinSdkVersionException { // IMPLEMENTATION NOTE: Minimum supported Android platform version number is declared using // uses-sdk elements which are children of the top-level manifest element. uses-sdk element // declares the minimum supported platform version using the android:minSdkVersion attribute // whose default value is 1. // For each encountered uses-sdk element, the Android runtime checks that its minSdkVersion // is not higher than the runtime's API Level and rejects APKs if it is higher. Thus, the // effective minSdkVersion value is the maximum over the encountered minSdkVersion values. try { // If no uses-sdk elements are encountered, Android accepts the APK. We treat this // scenario as though the minimum supported API Level is 1. int result = 1; AndroidBinXmlParser parser = new AndroidBinXmlParser(androidManifestContents); int eventType = parser.getEventType(); while (eventType != AndroidBinXmlParser.EVENT_END_DOCUMENT) { if ((eventType == AndroidBinXmlParser.EVENT_START_ELEMENT) && (parser.getDepth() == 2) && ("uses-sdk".equals(parser.getName())) && (parser.getNamespace().isEmpty())) { // In each uses-sdk element, minSdkVersion defaults to 1 int minSdkVersion = 1; for (int i = 0; i < parser.getAttributeCount(); i++) { if (parser.getAttributeNameResourceId(i) == MIN_SDK_VERSION_ATTR_ID) { int valueType = parser.getAttributeValueType(i); switch (valueType) { case AndroidBinXmlParser.VALUE_TYPE_INT: minSdkVersion = parser.getAttributeIntValue(i); break; case AndroidBinXmlParser.VALUE_TYPE_STRING: minSdkVersion = getMinSdkVersionForCodename( parser.getAttributeStringValue(i)); break; default: throw new MinSdkVersionException( "Unable to determine APK's minimum supported Android" + ": unsupported value type in " + ANDROID_MANIFEST_ZIP_ENTRY_NAME + "'s" + " minSdkVersion" + ". Only integer values supported."); } break; } } result = Math.max(result, minSdkVersion); } eventType = parser.next(); } return result; } catch (AndroidBinXmlParser.XmlParserException e) { throw new MinSdkVersionException( "Unable to determine APK's minimum supported Android platform version" + ": malformed binary resource: " + ANDROID_MANIFEST_ZIP_ENTRY_NAME, e); } } private static class CodenamesLazyInitializer { /** * List of platform codename (first letter of) to API Level mappings. The list must be * sorted by the first letter. For codenames not in the list, the assumption is that the API * Level is incremented by one for every increase in the codename's first letter. */ @SuppressWarnings({"rawtypes", "unchecked"}) private static final Pair[] SORTED_CODENAMES_FIRST_CHAR_TO_API_LEVEL = new Pair[] { Pair.of('C', 2), Pair.of('D', 3), Pair.of('E', 4), Pair.of('F', 7), Pair.of('G', 8), Pair.of('H', 10), Pair.of('I', 13), Pair.of('J', 15), Pair.of('K', 18), Pair.of('L', 20), Pair.of('M', 22), Pair.of('N', 23), Pair.of('O', 25), }; private static final Comparator> CODENAME_FIRST_CHAR_COMPARATOR = new ByFirstComparator(); private static class ByFirstComparator implements Comparator> { @Override public int compare(Pair o1, Pair o2) { char c1 = o1.getFirst(); char c2 = o2.getFirst(); return c1 - c2; } } } /** * Returns the API Level corresponding to the provided platform codename. * *

This method is pessimistic. It returns a value one lower than the API Level with which the * platform is actually released (e.g., 23 for N which was released as API Level 24). This is * because new features which first appear in an API Level are not available in the early days * of that platform version's existence, when the platform only has a codename. Moreover, this * method currently doesn't differentiate between initial and MR releases, meaning API Level * returned for MR releases may be more than one lower than the API Level with which the * platform version is actually released. * * @throws CodenameMinSdkVersionException if the {@code codename} is not supported */ static int getMinSdkVersionForCodename(String codename) throws CodenameMinSdkVersionException { char firstChar = codename.isEmpty() ? ' ' : codename.charAt(0); // Codenames are case-sensitive. Only codenames starting with A-Z are supported for now. // We only look at the first letter of the codename as this is the most important letter. if ((firstChar >= 'A') && (firstChar <= 'Z')) { Pair[] sortedCodenamesFirstCharToApiLevel = CodenamesLazyInitializer.SORTED_CODENAMES_FIRST_CHAR_TO_API_LEVEL; int searchResult = Arrays.binarySearch( sortedCodenamesFirstCharToApiLevel, Pair.of(firstChar, null), // second element of the pair is ignored here CodenamesLazyInitializer.CODENAME_FIRST_CHAR_COMPARATOR); if (searchResult >= 0) { // Exact match -- searchResult is the index of the matching element return sortedCodenamesFirstCharToApiLevel[searchResult].getSecond(); } // Not an exact match -- searchResult is negative and is -(insertion index) - 1. // The element at insertionIndex - 1 (if present) is smaller than firstChar and the // element at insertionIndex (if present) is greater than firstChar. int insertionIndex = -1 - searchResult; // insertionIndex is in [0; array length] if (insertionIndex == 0) { // 'A' or 'B' -- never released to public return 1; } else { // The element at insertionIndex - 1 is the newest older codename. // API Level bumped by at least 1 for every change in the first letter of codename Pair newestOlderCodenameMapping = sortedCodenamesFirstCharToApiLevel[insertionIndex - 1]; char newestOlderCodenameFirstChar = newestOlderCodenameMapping.getFirst(); int newestOlderCodenameApiLevel = newestOlderCodenameMapping.getSecond(); return newestOlderCodenameApiLevel + (firstChar - newestOlderCodenameFirstChar); } } throw new CodenameMinSdkVersionException( "Unable to determine APK's minimum supported Android platform version" + " : Unsupported codename in " + ANDROID_MANIFEST_ZIP_ENTRY_NAME + "'s minSdkVersion: \"" + codename + "\"", codename); } /** * Returns {@code true} if the APK is debuggable according to its {@code AndroidManifest.xml}. * See the {@code android:debuggable} attribute of the {@code application} element. * * @param androidManifestContents contents of {@code AndroidManifest.xml} in binary Android * resource format * * @throws ApkFormatException if the manifest is malformed */ public static boolean getDebuggableFromBinaryAndroidManifest( ByteBuffer androidManifestContents) throws ApkFormatException { // IMPLEMENTATION NOTE: Whether the package is debuggable is declared using the first // "application" element which is a child of the top-level manifest element. The debuggable // attribute of this application element is coerced to a boolean value. If there is no // application element or if it doesn't declare the debuggable attribute, the package is // considered not debuggable. try { AndroidBinXmlParser parser = new AndroidBinXmlParser(androidManifestContents); int eventType = parser.getEventType(); while (eventType != AndroidBinXmlParser.EVENT_END_DOCUMENT) { if ((eventType == AndroidBinXmlParser.EVENT_START_ELEMENT) && (parser.getDepth() == 2) && ("application".equals(parser.getName())) && (parser.getNamespace().isEmpty())) { for (int i = 0; i < parser.getAttributeCount(); i++) { if (parser.getAttributeNameResourceId(i) == DEBUGGABLE_ATTR_ID) { int valueType = parser.getAttributeValueType(i); switch (valueType) { case AndroidBinXmlParser.VALUE_TYPE_BOOLEAN: case AndroidBinXmlParser.VALUE_TYPE_STRING: case AndroidBinXmlParser.VALUE_TYPE_INT: String value = parser.getAttributeStringValue(i); return ("true".equals(value)) || ("TRUE".equals(value)) || ("1".equals(value)); case AndroidBinXmlParser.VALUE_TYPE_REFERENCE: // References to resources are not supported on purpose. The // reason is that the resolved value depends on the resource // configuration (e.g, MNC/MCC, locale, screen density) used // at resolution time. As a result, the same APK may appear as // debuggable in one situation and as non-debuggable in another // situation. Such APKs may put users at risk. throw new ApkFormatException( "Unable to determine whether APK is debuggable" + ": " + ANDROID_MANIFEST_ZIP_ENTRY_NAME + "'s" + " android:debuggable attribute references a" + " resource. References are not supported for" + " security reasons. Only constant boolean," + " string and int values are supported."); default: throw new ApkFormatException( "Unable to determine whether APK is debuggable" + ": " + ANDROID_MANIFEST_ZIP_ENTRY_NAME + "'s" + " android:debuggable attribute uses" + " unsupported value type. Only boolean," + " string and int values are supported."); } } } // This application element does not declare the debuggable attribute return false; } eventType = parser.next(); } // No application element found return false; } catch (AndroidBinXmlParser.XmlParserException e) { throw new ApkFormatException( "Unable to determine whether APK is debuggable: malformed binary resource: " + ANDROID_MANIFEST_ZIP_ENTRY_NAME, e); } } /** * Returns the package name of the APK according to its {@code AndroidManifest.xml} or * {@code null} if package name is not declared. See the {@code package} attribute of the * {@code manifest} element. * * @param androidManifestContents contents of {@code AndroidManifest.xml} in binary Android * resource format * * @throws ApkFormatException if the manifest is malformed */ public static String getPackageNameFromBinaryAndroidManifest( ByteBuffer androidManifestContents) throws ApkFormatException { // IMPLEMENTATION NOTE: Package name is declared as the "package" attribute of the top-level // manifest element. Interestingly, as opposed to most other attributes, Android Package // Manager looks up this attribute by its name rather than by its resource ID. try { AndroidBinXmlParser parser = new AndroidBinXmlParser(androidManifestContents); int eventType = parser.getEventType(); while (eventType != AndroidBinXmlParser.EVENT_END_DOCUMENT) { if ((eventType == AndroidBinXmlParser.EVENT_START_ELEMENT) && (parser.getDepth() == 1) && ("manifest".equals(parser.getName())) && (parser.getNamespace().isEmpty())) { for (int i = 0; i < parser.getAttributeCount(); i++) { if ("package".equals(parser.getAttributeName(i)) && (parser.getNamespace().isEmpty())) { return parser.getAttributeStringValue(i); } } // No "package" attribute found return null; } eventType = parser.next(); } // No manifest element found return null; } catch (AndroidBinXmlParser.XmlParserException e) { throw new ApkFormatException( "Unable to determine APK package name: malformed binary resource: " + ANDROID_MANIFEST_ZIP_ENTRY_NAME, e); } } /** * Returns the security sandbox version targeted by an APK with the provided * {@code AndroidManifest.xml}. * *

If the security sandbox version is not specified in the manifest a default value of 1 is * returned. * * @param androidManifestContents contents of {@code AndroidManifest.xml} in binary Android * resource format */ public static int getTargetSandboxVersionFromBinaryAndroidManifest( ByteBuffer androidManifestContents) { try { return getAttributeValueFromBinaryAndroidManifest(androidManifestContents, MANIFEST_ELEMENT_TAG, TARGET_SANDBOX_VERSION_ATTR_ID); } catch (ApkFormatException e) { // An ApkFormatException indicates the target sandbox is not specified in the manifest; // return a default value of 1. return 1; } } /** * Returns the SDK version targeted by an APK with the provided {@code AndroidManifest.xml}. * *

If the targetSdkVersion is not specified the minimumSdkVersion is returned. If neither * value is specified then a value of 1 is returned. * * @param androidManifestContents contents of {@code AndroidManifest.xml} in binary Android * resource format */ public static int getTargetSdkVersionFromBinaryAndroidManifest( ByteBuffer androidManifestContents) { // If the targetSdkVersion is not specified then the platform will use the value of the // minSdkVersion; if neither is specified then the platform will use a value of 1. int minSdkVersion = 1; try { return getAttributeValueFromBinaryAndroidManifest(androidManifestContents, USES_SDK_ELEMENT_TAG, TARGET_SDK_VERSION_ATTR_ID); } catch (ApkFormatException e) { // Expected if the APK does not contain a targetSdkVersion attribute or the uses-sdk // element is not specified at all. } androidManifestContents.rewind(); try { minSdkVersion = getMinSdkVersionFromBinaryAndroidManifest(androidManifestContents); } catch (ApkFormatException e) { // Similar to above, expected if the APK does not contain a minSdkVersion attribute, or // the uses-sdk element is not specified at all. } return minSdkVersion; } /** * Returns the versionCode of the APK according to its {@code AndroidManifest.xml}. * *

If the versionCode is not specified in the {@code AndroidManifest.xml} or is not a valid * integer an ApkFormatException is thrown. * * @param androidManifestContents contents of {@code AndroidManifest.xml} in binary Android * resource format * @throws ApkFormatException if an error occurred while determining the versionCode, or if the * versionCode attribute value is not available. */ public static int getVersionCodeFromBinaryAndroidManifest(ByteBuffer androidManifestContents) throws ApkFormatException { return getAttributeValueFromBinaryAndroidManifest(androidManifestContents, MANIFEST_ELEMENT_TAG, VERSION_CODE_ATTR_ID); } /** * Returns the versionCode and versionCodeMajor of the APK according to its {@code * AndroidManifest.xml} combined together as a single long value. * *

The versionCodeMajor is placed in the upper 32 bits, and the versionCode is in the lower * 32 bits. If the versionCodeMajor is not specified then the versionCode is returned. * * @param androidManifestContents contents of {@code AndroidManifest.xml} in binary Android * resource format * @throws ApkFormatException if an error occurred while determining the version, or if the * versionCode attribute value is not available. */ public static long getLongVersionCodeFromBinaryAndroidManifest( ByteBuffer androidManifestContents) throws ApkFormatException { // If the versionCode is not found then allow the ApkFormatException to be thrown to notify // the caller that the versionCode is not available. int versionCode = getVersionCodeFromBinaryAndroidManifest(androidManifestContents); long versionCodeMajor = 0; try { androidManifestContents.rewind(); versionCodeMajor = getAttributeValueFromBinaryAndroidManifest(androidManifestContents, MANIFEST_ELEMENT_TAG, VERSION_CODE_MAJOR_ATTR_ID); } catch (ApkFormatException e) { // This is expected if the versionCodeMajor has not been defined for the APK; in this // case the return value is just the versionCode. } return (versionCodeMajor << 32) | versionCode; } /** * Returns the integer value of the requested {@code attributeId} in the specified {@code * elementName} from the provided {@code androidManifestContents} in binary Android resource * format. * * @throws ApkFormatException if an error occurred while attempting to obtain the attribute, or * if the requested attribute is not found. */ private static int getAttributeValueFromBinaryAndroidManifest( ByteBuffer androidManifestContents, String elementName, int attributeId) throws ApkFormatException { if (elementName == null) { throw new NullPointerException("elementName cannot be null"); } try { AndroidBinXmlParser parser = new AndroidBinXmlParser(androidManifestContents); int eventType = parser.getEventType(); while (eventType != AndroidBinXmlParser.EVENT_END_DOCUMENT) { if ((eventType == AndroidBinXmlParser.EVENT_START_ELEMENT) && (elementName.equals(parser.getName()))) { for (int i = 0; i < parser.getAttributeCount(); i++) { if (parser.getAttributeNameResourceId(i) == attributeId) { int valueType = parser.getAttributeValueType(i); switch (valueType) { case AndroidBinXmlParser.VALUE_TYPE_INT: case AndroidBinXmlParser.VALUE_TYPE_STRING: return parser.getAttributeIntValue(i); default: throw new ApkFormatException( "Unsupported value type, " + valueType + ", for attribute " + String.format("0x%08X", attributeId) + " under element " + elementName); } } } } eventType = parser.next(); } throw new ApkFormatException( "Failed to determine APK's " + elementName + " attribute " + String.format("0x%08X", attributeId) + " value"); } catch (AndroidBinXmlParser.XmlParserException e) { throw new ApkFormatException( "Unable to determine value for attribute " + String.format("0x%08X", attributeId) + " under element " + elementName + "; malformed binary resource: " + ANDROID_MANIFEST_ZIP_ENTRY_NAME, e); } } public static byte[] computeSha256DigestBytes(byte[] data) { return ApkUtilsLite.computeSha256DigestBytes(data); } } ./PaxHeaders.X/src_main_java_com_android_apksig_apk_ApkUtilsLite.java0100644 0000000 0000000 00000000034 14763776540 025144 xustar000000000 0000000 28 mtime=1741684064.5640000 src/main/java/com/android/apksig/apk/ApkUtilsLite.java0100644 0000000 0000000 00000020420 14763776540 021726 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.apk; import com.android.apksig.internal.util.Pair; import com.android.apksig.internal.zip.ZipUtils; import com.android.apksig.util.DataSource; import com.android.apksig.zip.ZipFormatException; import com.android.apksig.zip.ZipSections; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * Lightweight version of the ApkUtils for clients that only require a subset of the utility * functionality. */ public class ApkUtilsLite { private ApkUtilsLite() {} /** * Finds the main ZIP sections of the provided APK. * * @throws IOException if an I/O error occurred while reading the APK * @throws ZipFormatException if the APK is malformed */ public static ZipSections findZipSections(DataSource apk) throws IOException, ZipFormatException { Pair eocdAndOffsetInFile = ZipUtils.findZipEndOfCentralDirectoryRecord(apk); if (eocdAndOffsetInFile == null) { throw new ZipFormatException("ZIP End of Central Directory record not found"); } ByteBuffer eocdBuf = eocdAndOffsetInFile.getFirst(); long eocdOffset = eocdAndOffsetInFile.getSecond(); eocdBuf.order(ByteOrder.LITTLE_ENDIAN); long cdStartOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocdBuf); if (cdStartOffset > eocdOffset) { throw new ZipFormatException( "ZIP Central Directory start offset out of range: " + cdStartOffset + ". ZIP End of Central Directory offset: " + eocdOffset); } long cdSizeBytes = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocdBuf); long cdEndOffset = cdStartOffset + cdSizeBytes; if (cdEndOffset > eocdOffset) { throw new ZipFormatException( "ZIP Central Directory overlaps with End of Central Directory" + ". CD end: " + cdEndOffset + ", EoCD start: " + eocdOffset); } int cdRecordCount = ZipUtils.getZipEocdCentralDirectoryTotalRecordCount(eocdBuf); return new ZipSections( cdStartOffset, cdSizeBytes, cdRecordCount, eocdOffset, eocdBuf); } // See https://source.android.com/security/apksigning/v2.html private static final long APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42L; private static final long APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041L; private static final int APK_SIG_BLOCK_MIN_SIZE = 32; /** * Returns the APK Signing Block of the provided APK. * * @throws IOException if an I/O error occurs * @throws ApkSigningBlockNotFoundException if there is no APK Signing Block in the APK * * @see APK Signature Scheme v2 * */ public static ApkSigningBlock findApkSigningBlock(DataSource apk, ZipSections zipSections) throws IOException, ApkSigningBlockNotFoundException { // FORMAT (see https://source.android.com/security/apksigning/v2.html): // OFFSET DATA TYPE DESCRIPTION // * @+0 bytes uint64: size in bytes (excluding this field) // * @+8 bytes payload // * @-24 bytes uint64: size in bytes (same as the one above) // * @-16 bytes uint128: magic long centralDirStartOffset = zipSections.getZipCentralDirectoryOffset(); long centralDirEndOffset = centralDirStartOffset + zipSections.getZipCentralDirectorySizeBytes(); long eocdStartOffset = zipSections.getZipEndOfCentralDirectoryOffset(); if (centralDirEndOffset != eocdStartOffset) { throw new ApkSigningBlockNotFoundException( "ZIP Central Directory is not immediately followed by End of Central Directory" + ". CD end: " + centralDirEndOffset + ", EoCD start: " + eocdStartOffset); } if (centralDirStartOffset < APK_SIG_BLOCK_MIN_SIZE) { throw new ApkSigningBlockNotFoundException( "APK too small for APK Signing Block. ZIP Central Directory offset: " + centralDirStartOffset); } // Read the magic and offset in file from the footer section of the block: // * uint64: size of block // * 16 bytes: magic ByteBuffer footer = apk.getByteBuffer(centralDirStartOffset - 24, 24); footer.order(ByteOrder.LITTLE_ENDIAN); if ((footer.getLong(8) != APK_SIG_BLOCK_MAGIC_LO) || (footer.getLong(16) != APK_SIG_BLOCK_MAGIC_HI)) { throw new ApkSigningBlockNotFoundException( "No APK Signing Block before ZIP Central Directory"); } // Read and compare size fields long apkSigBlockSizeInFooter = footer.getLong(0); if ((apkSigBlockSizeInFooter < footer.capacity()) || (apkSigBlockSizeInFooter > Integer.MAX_VALUE - 8)) { throw new ApkSigningBlockNotFoundException( "APK Signing Block size out of range: " + apkSigBlockSizeInFooter); } int totalSize = (int) (apkSigBlockSizeInFooter + 8); long apkSigBlockOffset = centralDirStartOffset - totalSize; if (apkSigBlockOffset < 0) { throw new ApkSigningBlockNotFoundException( "APK Signing Block offset out of range: " + apkSigBlockOffset); } ByteBuffer apkSigBlock = apk.getByteBuffer(apkSigBlockOffset, 8); apkSigBlock.order(ByteOrder.LITTLE_ENDIAN); long apkSigBlockSizeInHeader = apkSigBlock.getLong(0); if (apkSigBlockSizeInHeader != apkSigBlockSizeInFooter) { throw new ApkSigningBlockNotFoundException( "APK Signing Block sizes in header and footer do not match: " + apkSigBlockSizeInHeader + " vs " + apkSigBlockSizeInFooter); } return new ApkSigningBlock(apkSigBlockOffset, apk.slice(apkSigBlockOffset, totalSize)); } /** * Information about the location of the APK Signing Block inside an APK. */ public static class ApkSigningBlock { private final long mStartOffsetInApk; private final DataSource mContents; /** * Constructs a new {@code ApkSigningBlock}. * * @param startOffsetInApk start offset (in bytes, relative to start of file) of the APK * Signing Block inside the APK file * @param contents contents of the APK Signing Block */ public ApkSigningBlock(long startOffsetInApk, DataSource contents) { mStartOffsetInApk = startOffsetInApk; mContents = contents; } /** * Returns the start offset (in bytes, relative to start of file) of the APK Signing Block. */ public long getStartOffset() { return mStartOffsetInApk; } /** * Returns the data source which provides the full contents of the APK Signing Block, * including its footer. */ public DataSource getContents() { return mContents; } } public static byte[] computeSha256DigestBytes(byte[] data) { MessageDigest messageDigest; try { messageDigest = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("SHA-256 is not found", e); } messageDigest.update(data); return messageDigest.digest(); } } ./PaxHeaders.X/src_main_java_com_android_apksig_apk_CodenameMinSdkVersionException.java0100644 0000000 0000000 00000000034 14763776540 030640 xustar000000000 0000000 28 mtime=1741684064.5650000 src/main/java/com/android/apksig/apk/CodenameMinSdkVersionException.java0100644 0000000 0000000 00000002707 14763776540 025432 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.apk; /** * Indicates that there was an issue determining the minimum Android platform version supported by * an APK because the version is specified as a codename, rather than as API Level number, and the * codename is in an unexpected format. */ public class CodenameMinSdkVersionException extends MinSdkVersionException { private static final long serialVersionUID = 1L; /** Encountered codename. */ private final String mCodename; /** * Constructs a new {@code MinSdkVersionCodenameException} with the provided message and * codename. */ public CodenameMinSdkVersionException(String message, String codename) { super(message); mCodename = codename; } /** * Returns the codename. */ public String getCodename() { return mCodename; } } ./PaxHeaders.X/src_main_java_com_android_apksig_apk_MinSdkVersionException.java0100644 0000000 0000000 00000000034 14763776540 027204 xustar000000000 0000000 28 mtime=1741684064.5650000 src/main/java/com/android/apksig/apk/MinSdkVersionException.java0100644 0000000 0000000 00000002411 14763776540 023766 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.apk; /** * Indicates that there was an issue determining the minimum Android platform version supported by * an APK. */ public class MinSdkVersionException extends ApkFormatException { private static final long serialVersionUID = 1L; /** * Constructs a new {@code MinSdkVersionException} with the provided message. */ public MinSdkVersionException(String message) { super(message); } /** * Constructs a new {@code MinSdkVersionException} with the provided message and cause. */ public MinSdkVersionException(String message, Throwable cause) { super(message, cause); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_0100644 0000000 0000000 00000000034 14763776540 022772 xustar000000000 0000000 28 mtime=1741684064.5650000 src/main/java/com/android/apksig/internal/0040755 0000000 0000000 00000000000 14763776540 017557 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_0100644 0000000 0000000 00000000034 14763776540 023625 xustar000000000 0000000 28 mtime=1741684064.5650000 src/main/java/com/android/apksig/internal/apk/0040755 0000000 0000000 00000000000 14763776540 020332 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_AndroidBinXmlParser.java0100644 0000000 0000000 00000000034 14763776540 030335 xustar000000000 0000000 28 mtime=1741684064.5660000 src/main/java/com/android/apksig/internal/apk/AndroidBinXmlParser.java0100644 0000000 0000000 00000104674 14763776540 025055 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * XML pull style parser of Android binary XML resources, such as {@code AndroidManifest.xml}. * *

For an input document, the parser outputs an event stream (see {@code EVENT_... constants} via * {@link #getEventType()} and {@link #next()} methods. Additional information about the current * event can be obtained via an assortment of getters, for example, {@link #getName()} or * {@link #getAttributeNameResourceId(int)}. */ public class AndroidBinXmlParser { /** Event: start of document. */ public static final int EVENT_START_DOCUMENT = 1; /** Event: end of document. */ public static final int EVENT_END_DOCUMENT = 2; /** Event: start of an element. */ public static final int EVENT_START_ELEMENT = 3; /** Event: end of an document. */ public static final int EVENT_END_ELEMENT = 4; /** Attribute value type is not supported by this parser. */ public static final int VALUE_TYPE_UNSUPPORTED = 0; /** Attribute value is a string. Use {@link #getAttributeStringValue(int)} to obtain it. */ public static final int VALUE_TYPE_STRING = 1; /** Attribute value is an integer. Use {@link #getAttributeIntValue(int)} to obtain it. */ public static final int VALUE_TYPE_INT = 2; /** * Attribute value is a resource reference. Use {@link #getAttributeIntValue(int)} to obtain it. */ public static final int VALUE_TYPE_REFERENCE = 3; /** Attribute value is a boolean. Use {@link #getAttributeBooleanValue(int)} to obtain it. */ public static final int VALUE_TYPE_BOOLEAN = 4; private static final long NO_NAMESPACE = 0xffffffffL; private final ByteBuffer mXml; private StringPool mStringPool; private ResourceMap mResourceMap; private int mDepth; private int mCurrentEvent = EVENT_START_DOCUMENT; private String mCurrentElementName; private String mCurrentElementNamespace; private int mCurrentElementAttributeCount; private List mCurrentElementAttributes; private ByteBuffer mCurrentElementAttributesContents; private int mCurrentElementAttrSizeBytes; /** * Constructs a new parser for the provided document. */ public AndroidBinXmlParser(ByteBuffer xml) throws XmlParserException { xml.order(ByteOrder.LITTLE_ENDIAN); Chunk resXmlChunk = null; while (xml.hasRemaining()) { Chunk chunk = Chunk.get(xml); if (chunk == null) { break; } if (chunk.getType() == Chunk.TYPE_RES_XML) { resXmlChunk = chunk; break; } } if (resXmlChunk == null) { throw new XmlParserException("No XML chunk in file"); } mXml = resXmlChunk.getContents(); } /** * Returns the depth of the current element. Outside of the root of the document the depth is * {@code 0}. The depth is incremented by {@code 1} before each {@code start element} event and * is decremented by {@code 1} after each {@code end element} event. */ public int getDepth() { return mDepth; } /** * Returns the type of the current event. See {@code EVENT_...} constants. */ public int getEventType() { return mCurrentEvent; } /** * Returns the local name of the current element or {@code null} if the current event does not * pertain to an element. */ public String getName() { if ((mCurrentEvent != EVENT_START_ELEMENT) && (mCurrentEvent != EVENT_END_ELEMENT)) { return null; } return mCurrentElementName; } /** * Returns the namespace of the current element or {@code null} if the current event does not * pertain to an element. Returns an empty string if the element is not associated with a * namespace. */ public String getNamespace() { if ((mCurrentEvent != EVENT_START_ELEMENT) && (mCurrentEvent != EVENT_END_ELEMENT)) { return null; } return mCurrentElementNamespace; } /** * Returns the number of attributes of the element associated with the current event or * {@code -1} if no element is associated with the current event. */ public int getAttributeCount() { if (mCurrentEvent != EVENT_START_ELEMENT) { return -1; } return mCurrentElementAttributeCount; } /** * Returns the resource ID corresponding to the name of the specified attribute of the current * element or {@code 0} if the name is not associated with a resource ID. * * @throws IndexOutOfBoundsException if the index is out of range or the current event is not a * {@code start element} event * @throws XmlParserException if a parsing error is occurred */ public int getAttributeNameResourceId(int index) throws XmlParserException { return getAttribute(index).getNameResourceId(); } /** * Returns the name of the specified attribute of the current element. * * @throws IndexOutOfBoundsException if the index is out of range or the current event is not a * {@code start element} event * @throws XmlParserException if a parsing error is occurred */ public String getAttributeName(int index) throws XmlParserException { return getAttribute(index).getName(); } /** * Returns the name of the specified attribute of the current element or an empty string if * the attribute is not associated with a namespace. * * @throws IndexOutOfBoundsException if the index is out of range or the current event is not a * {@code start element} event * @throws XmlParserException if a parsing error is occurred */ public String getAttributeNamespace(int index) throws XmlParserException { return getAttribute(index).getNamespace(); } /** * Returns the value type of the specified attribute of the current element. See * {@code VALUE_TYPE_...} constants. * * @throws IndexOutOfBoundsException if the index is out of range or the current event is not a * {@code start element} event * @throws XmlParserException if a parsing error is occurred */ public int getAttributeValueType(int index) throws XmlParserException { int type = getAttribute(index).getValueType(); switch (type) { case Attribute.TYPE_STRING: return VALUE_TYPE_STRING; case Attribute.TYPE_INT_DEC: case Attribute.TYPE_INT_HEX: return VALUE_TYPE_INT; case Attribute.TYPE_REFERENCE: return VALUE_TYPE_REFERENCE; case Attribute.TYPE_INT_BOOLEAN: return VALUE_TYPE_BOOLEAN; default: return VALUE_TYPE_UNSUPPORTED; } } /** * Returns the integer value of the specified attribute of the current element. See * {@code VALUE_TYPE_...} constants. * * @throws IndexOutOfBoundsException if the index is out of range or the current event is not a * {@code start element} event. * @throws XmlParserException if a parsing error is occurred */ public int getAttributeIntValue(int index) throws XmlParserException { return getAttribute(index).getIntValue(); } /** * Returns the boolean value of the specified attribute of the current element. See * {@code VALUE_TYPE_...} constants. * * @throws IndexOutOfBoundsException if the index is out of range or the current event is not a * {@code start element} event. * @throws XmlParserException if a parsing error is occurred */ public boolean getAttributeBooleanValue(int index) throws XmlParserException { return getAttribute(index).getBooleanValue(); } /** * Returns the string value of the specified attribute of the current element. See * {@code VALUE_TYPE_...} constants. * * @throws IndexOutOfBoundsException if the index is out of range or the current event is not a * {@code start element} event. * @throws XmlParserException if a parsing error is occurred */ public String getAttributeStringValue(int index) throws XmlParserException { return getAttribute(index).getStringValue(); } private Attribute getAttribute(int index) { if (mCurrentEvent != EVENT_START_ELEMENT) { throw new IndexOutOfBoundsException("Current event not a START_ELEMENT"); } if (index < 0) { throw new IndexOutOfBoundsException("index must be >= 0"); } if (index >= mCurrentElementAttributeCount) { throw new IndexOutOfBoundsException( "index must be <= attr count (" + mCurrentElementAttributeCount + ")"); } parseCurrentElementAttributesIfNotParsed(); return mCurrentElementAttributes.get(index); } /** * Advances to the next parsing event and returns its type. See {@code EVENT_...} constants. */ public int next() throws XmlParserException { // Decrement depth if the previous event was "end element". if (mCurrentEvent == EVENT_END_ELEMENT) { mDepth--; } // Read events from document, ignoring events that we don't report to caller. Stop at the // earliest event which we report to caller. while (mXml.hasRemaining()) { Chunk chunk = Chunk.get(mXml); if (chunk == null) { break; } switch (chunk.getType()) { case Chunk.TYPE_STRING_POOL: if (mStringPool != null) { throw new XmlParserException("Multiple string pools not supported"); } mStringPool = new StringPool(chunk); break; case Chunk.RES_XML_TYPE_START_ELEMENT: { if (mStringPool == null) { throw new XmlParserException( "Named element encountered before string pool"); } ByteBuffer contents = chunk.getContents(); if (contents.remaining() < 20) { throw new XmlParserException( "Start element chunk too short. Need at least 20 bytes. Available: " + contents.remaining() + " bytes"); } long nsId = getUnsignedInt32(contents); long nameId = getUnsignedInt32(contents); int attrStartOffset = getUnsignedInt16(contents); int attrSizeBytes = getUnsignedInt16(contents); int attrCount = getUnsignedInt16(contents); long attrEndOffset = attrStartOffset + ((long) attrCount) * attrSizeBytes; contents.position(0); if (attrStartOffset > contents.remaining()) { throw new XmlParserException( "Attributes start offset out of bounds: " + attrStartOffset + ", max: " + contents.remaining()); } if (attrEndOffset > contents.remaining()) { throw new XmlParserException( "Attributes end offset out of bounds: " + attrEndOffset + ", max: " + contents.remaining()); } mCurrentElementName = mStringPool.getString(nameId); mCurrentElementNamespace = (nsId == NO_NAMESPACE) ? "" : mStringPool.getString(nsId); mCurrentElementAttributeCount = attrCount; mCurrentElementAttributes = null; mCurrentElementAttrSizeBytes = attrSizeBytes; mCurrentElementAttributesContents = sliceFromTo(contents, attrStartOffset, attrEndOffset); mDepth++; mCurrentEvent = EVENT_START_ELEMENT; return mCurrentEvent; } case Chunk.RES_XML_TYPE_END_ELEMENT: { if (mStringPool == null) { throw new XmlParserException( "Named element encountered before string pool"); } ByteBuffer contents = chunk.getContents(); if (contents.remaining() < 8) { throw new XmlParserException( "End element chunk too short. Need at least 8 bytes. Available: " + contents.remaining() + " bytes"); } long nsId = getUnsignedInt32(contents); long nameId = getUnsignedInt32(contents); mCurrentElementName = mStringPool.getString(nameId); mCurrentElementNamespace = (nsId == NO_NAMESPACE) ? "" : mStringPool.getString(nsId); mCurrentEvent = EVENT_END_ELEMENT; mCurrentElementAttributes = null; mCurrentElementAttributesContents = null; return mCurrentEvent; } case Chunk.RES_XML_TYPE_RESOURCE_MAP: if (mResourceMap != null) { throw new XmlParserException("Multiple resource maps not supported"); } mResourceMap = new ResourceMap(chunk); break; default: // Unknown chunk type -- ignore break; } } mCurrentEvent = EVENT_END_DOCUMENT; return mCurrentEvent; } private void parseCurrentElementAttributesIfNotParsed() { if (mCurrentElementAttributes != null) { return; } mCurrentElementAttributes = new ArrayList<>(mCurrentElementAttributeCount); for (int i = 0; i < mCurrentElementAttributeCount; i++) { int startPosition = i * mCurrentElementAttrSizeBytes; ByteBuffer attr = sliceFromTo( mCurrentElementAttributesContents, startPosition, startPosition + mCurrentElementAttrSizeBytes); long nsId = getUnsignedInt32(attr); long nameId = getUnsignedInt32(attr); attr.position(attr.position() + 7); // skip ignored fields int valueType = getUnsignedInt8(attr); long valueData = getUnsignedInt32(attr); mCurrentElementAttributes.add( new Attribute( nsId, nameId, valueType, (int) valueData, mStringPool, mResourceMap)); } } private static class Attribute { private static final int TYPE_REFERENCE = 1; private static final int TYPE_STRING = 3; private static final int TYPE_INT_DEC = 0x10; private static final int TYPE_INT_HEX = 0x11; private static final int TYPE_INT_BOOLEAN = 0x12; private final long mNsId; private final long mNameId; private final int mValueType; private final int mValueData; private final StringPool mStringPool; private final ResourceMap mResourceMap; private Attribute( long nsId, long nameId, int valueType, int valueData, StringPool stringPool, ResourceMap resourceMap) { mNsId = nsId; mNameId = nameId; mValueType = valueType; mValueData = valueData; mStringPool = stringPool; mResourceMap = resourceMap; } public int getNameResourceId() { return (mResourceMap != null) ? mResourceMap.getResourceId(mNameId) : 0; } public String getName() throws XmlParserException { return mStringPool.getString(mNameId); } public String getNamespace() throws XmlParserException { return (mNsId != NO_NAMESPACE) ? mStringPool.getString(mNsId) : ""; } public int getValueType() { return mValueType; } public int getIntValue() throws XmlParserException { switch (mValueType) { case TYPE_REFERENCE: case TYPE_INT_DEC: case TYPE_INT_HEX: case TYPE_INT_BOOLEAN: return mValueData; default: throw new XmlParserException("Cannot coerce to int: value type " + mValueType); } } public boolean getBooleanValue() throws XmlParserException { switch (mValueType) { case TYPE_INT_BOOLEAN: return mValueData != 0; default: throw new XmlParserException( "Cannot coerce to boolean: value type " + mValueType); } } public String getStringValue() throws XmlParserException { switch (mValueType) { case TYPE_STRING: return mStringPool.getString(mValueData & 0xffffffffL); case TYPE_INT_DEC: return Integer.toString(mValueData); case TYPE_INT_HEX: return "0x" + Integer.toHexString(mValueData); case TYPE_INT_BOOLEAN: return Boolean.toString(mValueData != 0); case TYPE_REFERENCE: return "@" + Integer.toHexString(mValueData); default: throw new XmlParserException( "Cannot coerce to string: value type " + mValueType); } } } /** * Chunk of a document. Each chunk is tagged with a type and consists of a header followed by * contents. */ private static class Chunk { public static final int TYPE_STRING_POOL = 1; public static final int TYPE_RES_XML = 3; public static final int RES_XML_TYPE_START_ELEMENT = 0x0102; public static final int RES_XML_TYPE_END_ELEMENT = 0x0103; public static final int RES_XML_TYPE_RESOURCE_MAP = 0x0180; static final int HEADER_MIN_SIZE_BYTES = 8; private final int mType; private final ByteBuffer mHeader; private final ByteBuffer mContents; public Chunk(int type, ByteBuffer header, ByteBuffer contents) { mType = type; mHeader = header; mContents = contents; } public ByteBuffer getContents() { ByteBuffer result = mContents.slice(); result.order(mContents.order()); return result; } public ByteBuffer getHeader() { ByteBuffer result = mHeader.slice(); result.order(mHeader.order()); return result; } public int getType() { return mType; } /** * Consumes the chunk located at the current position of the input and returns the chunk * or {@code null} if there is no chunk left in the input. * * @throws XmlParserException if the chunk is malformed */ public static Chunk get(ByteBuffer input) throws XmlParserException { if (input.remaining() < HEADER_MIN_SIZE_BYTES) { // Android ignores the last chunk if its header is too big to fit into the file input.position(input.limit()); return null; } int originalPosition = input.position(); int type = getUnsignedInt16(input); int headerSize = getUnsignedInt16(input); long chunkSize = getUnsignedInt32(input); long chunkRemaining = chunkSize - 8; if (chunkRemaining > input.remaining()) { // Android ignores the last chunk if it's too big to fit into the file input.position(input.limit()); return null; } if (headerSize < HEADER_MIN_SIZE_BYTES) { throw new XmlParserException( "Malformed chunk: header too short: " + headerSize + " bytes"); } else if (headerSize > chunkSize) { throw new XmlParserException( "Malformed chunk: header too long: " + headerSize + " bytes. Chunk size: " + chunkSize + " bytes"); } int contentStartPosition = originalPosition + headerSize; long chunkEndPosition = originalPosition + chunkSize; Chunk chunk = new Chunk( type, sliceFromTo(input, originalPosition, contentStartPosition), sliceFromTo(input, contentStartPosition, chunkEndPosition)); input.position((int) chunkEndPosition); return chunk; } } /** * String pool of a document. Strings are referenced by their {@code 0}-based index in the pool. */ private static class StringPool { private static final int FLAG_UTF8 = 1 << 8; private final ByteBuffer mChunkContents; private final ByteBuffer mStringsSection; private final int mStringCount; private final boolean mUtf8Encoded; private final Map mCachedStrings = new HashMap<>(); /** * Constructs a new string pool from the provided chunk. * * @throws XmlParserException if a parsing error occurred */ public StringPool(Chunk chunk) throws XmlParserException { ByteBuffer header = chunk.getHeader(); int headerSizeBytes = header.remaining(); header.position(Chunk.HEADER_MIN_SIZE_BYTES); if (header.remaining() < 20) { throw new XmlParserException( "XML chunk's header too short. Required at least 20 bytes. Available: " + header.remaining() + " bytes"); } long stringCount = getUnsignedInt32(header); if (stringCount > Integer.MAX_VALUE) { throw new XmlParserException("Too many strings: " + stringCount); } mStringCount = (int) stringCount; long styleCount = getUnsignedInt32(header); if (styleCount > Integer.MAX_VALUE) { throw new XmlParserException("Too many styles: " + styleCount); } long flags = getUnsignedInt32(header); long stringsStartOffset = getUnsignedInt32(header); long stylesStartOffset = getUnsignedInt32(header); ByteBuffer contents = chunk.getContents(); if (mStringCount > 0) { int stringsSectionStartOffsetInContents = (int) (stringsStartOffset - headerSizeBytes); int stringsSectionEndOffsetInContents; if (styleCount > 0) { // Styles section follows the strings section if (stylesStartOffset < stringsStartOffset) { throw new XmlParserException( "Styles offset (" + stylesStartOffset + ") < strings offset (" + stringsStartOffset + ")"); } stringsSectionEndOffsetInContents = (int) (stylesStartOffset - headerSizeBytes); } else { stringsSectionEndOffsetInContents = contents.remaining(); } mStringsSection = sliceFromTo( contents, stringsSectionStartOffsetInContents, stringsSectionEndOffsetInContents); } else { mStringsSection = ByteBuffer.allocate(0); } mUtf8Encoded = (flags & FLAG_UTF8) != 0; mChunkContents = contents; } /** * Returns the string located at the specified {@code 0}-based index in this pool. * * @throws XmlParserException if the string does not exist or cannot be decoded */ public String getString(long index) throws XmlParserException { if (index < 0) { throw new XmlParserException("Unsuported string index: " + index); } else if (index >= mStringCount) { throw new XmlParserException( "Unsuported string index: " + index + ", max: " + (mStringCount - 1)); } int idx = (int) index; String result = mCachedStrings.get(idx); if (result != null) { return result; } long offsetInStringsSection = getUnsignedInt32(mChunkContents, idx * 4); if (offsetInStringsSection >= mStringsSection.capacity()) { throw new XmlParserException( "Offset of string idx " + idx + " out of bounds: " + offsetInStringsSection + ", max: " + (mStringsSection.capacity() - 1)); } mStringsSection.position((int) offsetInStringsSection); result = (mUtf8Encoded) ? getLengthPrefixedUtf8EncodedString(mStringsSection) : getLengthPrefixedUtf16EncodedString(mStringsSection); mCachedStrings.put(idx, result); return result; } private static String getLengthPrefixedUtf16EncodedString(ByteBuffer encoded) throws XmlParserException { // If the length (in uint16s) is 0x7fff or lower, it is stored as a single uint16. // Otherwise, it is stored as a big-endian uint32 with highest bit set. Thus, the range // of supported values is 0 to 0x7fffffff inclusive. int lengthChars = getUnsignedInt16(encoded); if ((lengthChars & 0x8000) != 0) { lengthChars = ((lengthChars & 0x7fff) << 16) | getUnsignedInt16(encoded); } if (lengthChars > Integer.MAX_VALUE / 2) { throw new XmlParserException("String too long: " + lengthChars + " uint16s"); } int lengthBytes = lengthChars * 2; byte[] arr; int arrOffset; if (encoded.hasArray()) { arr = encoded.array(); arrOffset = encoded.arrayOffset() + encoded.position(); encoded.position(encoded.position() + lengthBytes); } else { arr = new byte[lengthBytes]; arrOffset = 0; encoded.get(arr); } // Reproduce the behavior of Android runtime which requires that the UTF-16 encoded // array of bytes is NULL terminated. if ((arr[arrOffset + lengthBytes] != 0) || (arr[arrOffset + lengthBytes + 1] != 0)) { throw new XmlParserException("UTF-16 encoded form of string not NULL terminated"); } try { return new String(arr, arrOffset, lengthBytes, "UTF-16LE"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UTF-16LE character encoding not supported", e); } } private static String getLengthPrefixedUtf8EncodedString(ByteBuffer encoded) throws XmlParserException { // If the length (in bytes) is 0x7f or lower, it is stored as a single uint8. Otherwise, // it is stored as a big-endian uint16 with highest bit set. Thus, the range of // supported values is 0 to 0x7fff inclusive. // Skip UTF-16 encoded length (in uint16s) int lengthBytes = getUnsignedInt8(encoded); if ((lengthBytes & 0x80) != 0) { lengthBytes = ((lengthBytes & 0x7f) << 8) | getUnsignedInt8(encoded); } // Read UTF-8 encoded length (in bytes) lengthBytes = getUnsignedInt8(encoded); if ((lengthBytes & 0x80) != 0) { lengthBytes = ((lengthBytes & 0x7f) << 8) | getUnsignedInt8(encoded); } byte[] arr; int arrOffset; if (encoded.hasArray()) { arr = encoded.array(); arrOffset = encoded.arrayOffset() + encoded.position(); encoded.position(encoded.position() + lengthBytes); } else { arr = new byte[lengthBytes]; arrOffset = 0; encoded.get(arr); } // Reproduce the behavior of Android runtime which requires that the UTF-8 encoded array // of bytes is NULL terminated. if (arr[arrOffset + lengthBytes] != 0) { throw new XmlParserException("UTF-8 encoded form of string not NULL terminated"); } try { return new String(arr, arrOffset, lengthBytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UTF-8 character encoding not supported", e); } } } /** * Resource map of a document. Resource IDs are referenced by their {@code 0}-based index in the * map. */ private static class ResourceMap { private final ByteBuffer mChunkContents; private final int mEntryCount; /** * Constructs a new resource map from the provided chunk. * * @throws XmlParserException if a parsing error occurred */ public ResourceMap(Chunk chunk) throws XmlParserException { mChunkContents = chunk.getContents().slice(); mChunkContents.order(chunk.getContents().order()); // Each entry of the map is four bytes long, containing the int32 resource ID. mEntryCount = mChunkContents.remaining() / 4; } /** * Returns the resource ID located at the specified {@code 0}-based index in this pool or * {@code 0} if the index is out of range. */ public int getResourceId(long index) { if ((index < 0) || (index >= mEntryCount)) { return 0; } int idx = (int) index; // Each entry of the map is four bytes long, containing the int32 resource ID. return mChunkContents.getInt(idx * 4); } } /** * Returns new byte buffer whose content is a shared subsequence of this buffer's content * between the specified start (inclusive) and end (exclusive) positions. As opposed to * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source * buffer's byte order. */ private static ByteBuffer sliceFromTo(ByteBuffer source, long start, long end) { if (start < 0) { throw new IllegalArgumentException("start: " + start); } if (end < start) { throw new IllegalArgumentException("end < start: " + end + " < " + start); } int capacity = source.capacity(); if (end > source.capacity()) { throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity); } return sliceFromTo(source, (int) start, (int) end); } /** * Returns new byte buffer whose content is a shared subsequence of this buffer's content * between the specified start (inclusive) and end (exclusive) positions. As opposed to * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source * buffer's byte order. */ private static ByteBuffer sliceFromTo(ByteBuffer source, int start, int end) { if (start < 0) { throw new IllegalArgumentException("start: " + start); } if (end < start) { throw new IllegalArgumentException("end < start: " + end + " < " + start); } int capacity = source.capacity(); if (end > source.capacity()) { throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity); } int originalLimit = source.limit(); int originalPosition = source.position(); try { source.position(0); source.limit(end); source.position(start); ByteBuffer result = source.slice(); result.order(source.order()); return result; } finally { source.position(0); source.limit(originalLimit); source.position(originalPosition); } } private static int getUnsignedInt8(ByteBuffer buffer) { return buffer.get() & 0xff; } private static int getUnsignedInt16(ByteBuffer buffer) { return buffer.getShort() & 0xffff; } private static long getUnsignedInt32(ByteBuffer buffer) { return buffer.getInt() & 0xffffffffL; } private static long getUnsignedInt32(ByteBuffer buffer, int position) { return buffer.getInt(position) & 0xffffffffL; } /** * Indicates that an error occurred while parsing a document. */ public static class XmlParserException extends Exception { private static final long serialVersionUID = 1L; public XmlParserException(String message) { super(message); } public XmlParserException(String message, Throwable cause) { super(message, cause); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_ApkSigResult.java0100644 0000000 0000000 00000000034 14763776540 027043 xustar000000000 0000000 28 mtime=1741684064.5680000 src/main/java/com/android/apksig/internal/apk/ApkSigResult.java0100644 0000000 0000000 00000006141 14763776540 023551 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; import com.android.apksig.ApkVerificationIssue; import java.util.ArrayList; import java.util.List; /** * Base implementation of an APK signature verification result. */ public class ApkSigResult { public final int signatureSchemeVersion; /** Whether the APK's Signature Scheme signature verifies. */ public boolean verified; public final List mSigners = new ArrayList<>(); private final List mWarnings = new ArrayList<>(); private final List mErrors = new ArrayList<>(); public ApkSigResult(int signatureSchemeVersion) { this.signatureSchemeVersion = signatureSchemeVersion; } /** * Returns {@code true} if this result encountered errors during verification. */ public boolean containsErrors() { if (!mErrors.isEmpty()) { return true; } if (!mSigners.isEmpty()) { for (ApkSignerInfo signer : mSigners) { if (signer.containsErrors()) { return true; } } } return false; } /** * Returns {@code true} if this result encountered warnings during verification. */ public boolean containsWarnings() { if (!mWarnings.isEmpty()) { return true; } if (!mSigners.isEmpty()) { for (ApkSignerInfo signer : mSigners) { if (signer.containsWarnings()) { return true; } } } return false; } /** * Adds a new {@link ApkVerificationIssue} as an error to this result using the provided {@code * issueId} and {@code params}. */ public void addError(int issueId, Object... parameters) { mErrors.add(new ApkVerificationIssue(issueId, parameters)); } /** * Adds a new {@link ApkVerificationIssue} as a warning to this result using the provided {@code * issueId} and {@code params}. */ public void addWarning(int issueId, Object... parameters) { mWarnings.add(new ApkVerificationIssue(issueId, parameters)); } /** * Returns the errors encountered during verification. */ public List getErrors() { return mErrors; } /** * Returns the warnings encountered during verification. */ public List getWarnings() { return mWarnings; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_ApkSignerInfo.java0100644 0000000 0000000 00000000034 14763776540 027165 xustar000000000 0000000 28 mtime=1741684064.5680000 src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java0100644 0000000 0000000 00000006435 14763776540 023701 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; import com.android.apksig.ApkVerificationIssue; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; /** * Base implementation of an APK signer. */ public class ApkSignerInfo { public int index; public long timestamp; public List certs = new ArrayList<>(); public List certificateLineage = new ArrayList<>(); private final List mInfoMessages = new ArrayList<>(); private final List mWarnings = new ArrayList<>(); private final List mErrors = new ArrayList<>(); /** * Adds a new {@link ApkVerificationIssue} as an error to this signer using the provided {@code * issueId} and {@code params}. */ public void addError(int issueId, Object... params) { mErrors.add(new ApkVerificationIssue(issueId, params)); } /** * Adds a new {@link ApkVerificationIssue} as a warning to this signer using the provided {@code * issueId} and {@code params}. */ public void addWarning(int issueId, Object... params) { mWarnings.add(new ApkVerificationIssue(issueId, params)); } /** * Adds a new {@link ApkVerificationIssue} as an info message to this signer config using the * provided {@code issueId} and {@code params}. */ public void addInfoMessage(int issueId, Object... params) { mInfoMessages.add(new ApkVerificationIssue(issueId, params)); } /** * Returns {@code true} if any errors were encountered during verification for this signer. */ public boolean containsErrors() { return !mErrors.isEmpty(); } /** * Returns {@code true} if any warnings were encountered during verification for this signer. */ public boolean containsWarnings() { return !mWarnings.isEmpty(); } /** * Returns {@code true} if any info messages were encountered during verification of this * signer. */ public boolean containsInfoMessages() { return !mInfoMessages.isEmpty(); } /** * Returns the errors encountered during verification for this signer. */ public List getErrors() { return mErrors; } /** * Returns the warnings encountered during verification for this signer. */ public List getWarnings() { return mWarnings; } /** * Returns the info messages encountered during verification of this signer. */ public List getInfoMessages() { return mInfoMessages; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_ApkSigningBlockUtils.java0100644 0000000 0000000 00000000034 14763776540 030514 xustar000000000 0000000 28 mtime=1741684064.5690000 src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java0100644 0000000 0000000 00000205350 14763776540 025225 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; import static com.android.apksig.Constants.OID_RSA_ENCRYPTION; import static com.android.apksig.internal.apk.ContentDigestAlgorithm.CHUNKED_SHA256; import static com.android.apksig.internal.apk.ContentDigestAlgorithm.CHUNKED_SHA512; import static com.android.apksig.internal.apk.ContentDigestAlgorithm.VERITY_CHUNKED_SHA256; import com.android.apksig.ApkVerifier; import com.android.apksig.KeyConfig; import com.android.apksig.SignerEngineFactory; import com.android.apksig.SigningCertificateLineage; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.asn1.Asn1BerParser; import com.android.apksig.internal.asn1.Asn1DecodingException; import com.android.apksig.internal.asn1.Asn1DerEncoder; import com.android.apksig.internal.asn1.Asn1EncodingException; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.pkcs7.AlgorithmIdentifier; import com.android.apksig.internal.pkcs7.ContentInfo; import com.android.apksig.internal.pkcs7.EncapsulatedContentInfo; import com.android.apksig.internal.pkcs7.IssuerAndSerialNumber; import com.android.apksig.internal.pkcs7.Pkcs7Constants; import com.android.apksig.internal.pkcs7.SignedData; import com.android.apksig.internal.pkcs7.SignerIdentifier; import com.android.apksig.internal.pkcs7.SignerInfo; import com.android.apksig.internal.util.ByteBufferDataSource; import com.android.apksig.internal.util.ChainedDataSource; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import com.android.apksig.internal.util.Pair; import com.android.apksig.internal.util.VerityTreeBuilder; import com.android.apksig.internal.util.X509CertificateUtils; import com.android.apksig.internal.x509.RSAPublicKey; import com.android.apksig.internal.x509.SubjectPublicKeyInfo; import com.android.apksig.internal.zip.ZipUtils; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSinks; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import com.android.apksig.util.RunnablesExecutor; import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.DigestException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import javax.security.auth.x500.X500Principal; public class ApkSigningBlockUtils { private static final long CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES = 1024 * 1024; public static final int ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096; private static final byte[] APK_SIGNING_BLOCK_MAGIC = new byte[] { 0x41, 0x50, 0x4b, 0x20, 0x53, 0x69, 0x67, 0x20, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x34, 0x32, }; public static final int VERITY_PADDING_BLOCK_ID = 0x42726577; private static final ContentDigestAlgorithm[] V4_CONTENT_DIGEST_ALGORITHMS = {CHUNKED_SHA512, VERITY_CHUNKED_SHA256, CHUNKED_SHA256}; public static final int VERSION_SOURCE_STAMP = 0; public static final int VERSION_JAR_SIGNATURE_SCHEME = 1; public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2; public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3; public static final int VERSION_APK_SIGNATURE_SCHEME_V31 = 31; public static final int VERSION_APK_SIGNATURE_SCHEME_V4 = 4; /** * Returns positive number if {@code alg1} is preferred over {@code alg2}, {@code -1} if * {@code alg2} is preferred over {@code alg1}, and {@code 0} if there is no preference. */ public static int compareSignatureAlgorithm(SignatureAlgorithm alg1, SignatureAlgorithm alg2) { return ApkSigningBlockUtilsLite.compareSignatureAlgorithm(alg1, alg2); } /** * Verifies integrity of the APK outside of the APK Signing Block by computing digests of the * APK and comparing them against the digests listed in APK Signing Block. The expected digests * are taken from {@code SignerInfos} of the provided {@code result}. * *

This method adds one or more errors to the {@code result} if a verification error is * expected to be encountered on Android. No errors are added to the {@code result} if the APK's * integrity is expected to verify on Android for each algorithm in * {@code contentDigestAlgorithms}. * *

The reason this method is currently not parameterized by a * {@code [minSdkVersion, maxSdkVersion]} range is that up until now content digest algorithms * exhibit the same behavior on all Android platform versions. */ public static void verifyIntegrity( RunnablesExecutor executor, DataSource beforeApkSigningBlock, DataSource centralDir, ByteBuffer eocd, Set contentDigestAlgorithms, Result result) throws IOException, NoSuchAlgorithmException { if (contentDigestAlgorithms.isEmpty()) { // This should never occur because this method is invoked once at least one signature // is verified, meaning at least one content digest is known. throw new RuntimeException("No content digests found"); } // For the purposes of verifying integrity, ZIP End of Central Directory (EoCD) must be // treated as though its Central Directory offset points to the start of APK Signing Block. // We thus modify the EoCD accordingly. ByteBuffer modifiedEocd = ByteBuffer.allocate(eocd.remaining()); int eocdSavedPos = eocd.position(); modifiedEocd.order(ByteOrder.LITTLE_ENDIAN); modifiedEocd.put(eocd); modifiedEocd.flip(); // restore eocd to position prior to modification in case it is to be used elsewhere eocd.position(eocdSavedPos); ZipUtils.setZipEocdCentralDirectoryOffset(modifiedEocd, beforeApkSigningBlock.size()); Map actualContentDigests; try { actualContentDigests = computeContentDigests( executor, contentDigestAlgorithms, beforeApkSigningBlock, centralDir, new ByteBufferDataSource(modifiedEocd)); // Special checks for the verity algorithm requirements. if (actualContentDigests.containsKey(VERITY_CHUNKED_SHA256)) { if ((beforeApkSigningBlock.size() % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES != 0)) { throw new RuntimeException( "APK Signing Block is not aligned on 4k boundary: " + beforeApkSigningBlock.size()); } long centralDirOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocd); long signingBlockSize = centralDirOffset - beforeApkSigningBlock.size(); if (signingBlockSize % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES != 0) { throw new RuntimeException( "APK Signing Block size is not multiple of page size: " + signingBlockSize); } } } catch (DigestException e) { throw new RuntimeException("Failed to compute content digests", e); } if (!contentDigestAlgorithms.equals(actualContentDigests.keySet())) { throw new RuntimeException( "Mismatch between sets of requested and computed content digests" + " . Requested: " + contentDigestAlgorithms + ", computed: " + actualContentDigests.keySet()); } // Compare digests computed over the rest of APK against the corresponding expected digests // in signer blocks. for (Result.SignerInfo signerInfo : result.signers) { for (Result.SignerInfo.ContentDigest expected : signerInfo.contentDigests) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(expected.getSignatureAlgorithmId()); if (signatureAlgorithm == null) { continue; } ContentDigestAlgorithm contentDigestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm(); // if the current digest algorithm is not in the list provided by the caller then // ignore it; the signer may contain digests not recognized by the specified SDK // range. if (!contentDigestAlgorithms.contains(contentDigestAlgorithm)) { continue; } byte[] expectedDigest = expected.getValue(); byte[] actualDigest = actualContentDigests.get(contentDigestAlgorithm); if (!Arrays.equals(expectedDigest, actualDigest)) { if (result.signatureSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V2) { signerInfo.addError( ApkVerifier.Issue.V2_SIG_APK_DIGEST_DID_NOT_VERIFY, contentDigestAlgorithm, toHex(expectedDigest), toHex(actualDigest)); } else if (result.signatureSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V3) { signerInfo.addError( ApkVerifier.Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY, contentDigestAlgorithm, toHex(expectedDigest), toHex(actualDigest)); } continue; } signerInfo.verifiedContentDigests.put(contentDigestAlgorithm, actualDigest); } } } public static ByteBuffer findApkSignatureSchemeBlock( ByteBuffer apkSigningBlock, int blockId, Result result) throws SignatureNotFoundException { try { return ApkSigningBlockUtilsLite.findApkSignatureSchemeBlock(apkSigningBlock, blockId); } catch (com.android.apksig.internal.apk.SignatureNotFoundException e) { throw new SignatureNotFoundException(e.getMessage()); } } public static void checkByteOrderLittleEndian(ByteBuffer buffer) { ApkSigningBlockUtilsLite.checkByteOrderLittleEndian(buffer); } public static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws ApkFormatException { return ApkSigningBlockUtilsLite.getLengthPrefixedSlice(source); } public static byte[] readLengthPrefixedByteArray(ByteBuffer buf) throws ApkFormatException { return ApkSigningBlockUtilsLite.readLengthPrefixedByteArray(buf); } public static String toHex(byte[] value) { return ApkSigningBlockUtilsLite.toHex(value); } public static Map computeContentDigests( RunnablesExecutor executor, Set digestAlgorithms, DataSource beforeCentralDir, DataSource centralDir, DataSource eocd) throws IOException, NoSuchAlgorithmException, DigestException { Map contentDigests = new HashMap<>(); Set oneMbChunkBasedAlgorithm = new HashSet<>(); for (ContentDigestAlgorithm digestAlgorithm : digestAlgorithms) { if (digestAlgorithm == ContentDigestAlgorithm.CHUNKED_SHA256 || digestAlgorithm == ContentDigestAlgorithm.CHUNKED_SHA512) { oneMbChunkBasedAlgorithm.add(digestAlgorithm); } } computeOneMbChunkContentDigests( executor, oneMbChunkBasedAlgorithm, new DataSource[] { beforeCentralDir, centralDir, eocd }, contentDigests); if (digestAlgorithms.contains(VERITY_CHUNKED_SHA256)) { computeApkVerityDigest(beforeCentralDir, centralDir, eocd, contentDigests); } return contentDigests; } static void computeOneMbChunkContentDigests( Set digestAlgorithms, DataSource[] contents, Map outputContentDigests) throws IOException, NoSuchAlgorithmException, DigestException { // For each digest algorithm the result is computed as follows: // 1. Each segment of contents is split into consecutive chunks of 1 MB in size. // The final chunk will be shorter iff the length of segment is not a multiple of 1 MB. // No chunks are produced for empty (zero length) segments. // 2. The digest of each chunk is computed over the concatenation of byte 0xa5, the chunk's // length in bytes (uint32 little-endian) and the chunk's contents. // 3. The output digest is computed over the concatenation of the byte 0x5a, the number of // chunks (uint32 little-endian) and the concatenation of digests of chunks of all // segments in-order. long chunkCountLong = 0; for (DataSource input : contents) { chunkCountLong += getChunkCount(input.size(), CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES); } if (chunkCountLong > Integer.MAX_VALUE) { throw new DigestException("Input too long: " + chunkCountLong + " chunks"); } int chunkCount = (int) chunkCountLong; ContentDigestAlgorithm[] digestAlgorithmsArray = digestAlgorithms.toArray(new ContentDigestAlgorithm[digestAlgorithms.size()]); MessageDigest[] mds = new MessageDigest[digestAlgorithmsArray.length]; byte[][] digestsOfChunks = new byte[digestAlgorithmsArray.length][]; int[] digestOutputSizes = new int[digestAlgorithmsArray.length]; for (int i = 0; i < digestAlgorithmsArray.length; i++) { ContentDigestAlgorithm digestAlgorithm = digestAlgorithmsArray[i]; int digestOutputSizeBytes = digestAlgorithm.getChunkDigestOutputSizeBytes(); digestOutputSizes[i] = digestOutputSizeBytes; byte[] concatenationOfChunkCountAndChunkDigests = new byte[5 + chunkCount * digestOutputSizeBytes]; concatenationOfChunkCountAndChunkDigests[0] = 0x5a; setUnsignedInt32LittleEndian( chunkCount, concatenationOfChunkCountAndChunkDigests, 1); digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests; String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm(); mds[i] = MessageDigest.getInstance(jcaAlgorithm); } DataSink mdSink = DataSinks.asDataSink(mds); byte[] chunkContentPrefix = new byte[5]; chunkContentPrefix[0] = (byte) 0xa5; int chunkIndex = 0; // Optimization opportunity: digests of chunks can be computed in parallel. However, // determining the number of computations to be performed in parallel is non-trivial. This // depends on a wide range of factors, such as data source type (e.g., in-memory or fetched // from file), CPU/memory/disk cache bandwidth and latency, interconnect architecture of CPU // cores, load on the system from other threads of execution and other processes, size of // input. // For now, we compute these digests sequentially and thus have the luxury of improving // performance by writing the digest of each chunk into a pre-allocated buffer at exactly // the right position. This avoids unnecessary allocations, copying, and enables the final // digest to be more efficient because it's presented with all of its input in one go. for (DataSource input : contents) { long inputOffset = 0; long inputRemaining = input.size(); while (inputRemaining > 0) { int chunkSize = (int) Math.min(inputRemaining, CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES); setUnsignedInt32LittleEndian(chunkSize, chunkContentPrefix, 1); for (int i = 0; i < mds.length; i++) { mds[i].update(chunkContentPrefix); } try { input.feed(inputOffset, chunkSize, mdSink); } catch (IOException e) { throw new IOException("Failed to read chunk #" + chunkIndex, e); } for (int i = 0; i < digestAlgorithmsArray.length; i++) { MessageDigest md = mds[i]; byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i]; int expectedDigestSizeBytes = digestOutputSizes[i]; int actualDigestSizeBytes = md.digest( concatenationOfChunkCountAndChunkDigests, 5 + chunkIndex * expectedDigestSizeBytes, expectedDigestSizeBytes); if (actualDigestSizeBytes != expectedDigestSizeBytes) { throw new RuntimeException( "Unexpected output size of " + md.getAlgorithm() + " digest: " + actualDigestSizeBytes); } } inputOffset += chunkSize; inputRemaining -= chunkSize; chunkIndex++; } } for (int i = 0; i < digestAlgorithmsArray.length; i++) { ContentDigestAlgorithm digestAlgorithm = digestAlgorithmsArray[i]; byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i]; MessageDigest md = mds[i]; byte[] digest = md.digest(concatenationOfChunkCountAndChunkDigests); outputContentDigests.put(digestAlgorithm, digest); } } static void computeOneMbChunkContentDigests( RunnablesExecutor executor, Set digestAlgorithms, DataSource[] contents, Map outputContentDigests) throws NoSuchAlgorithmException, DigestException { long chunkCountLong = 0; for (DataSource input : contents) { chunkCountLong += getChunkCount(input.size(), CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES); } if (chunkCountLong > Integer.MAX_VALUE) { throw new DigestException("Input too long: " + chunkCountLong + " chunks"); } int chunkCount = (int) chunkCountLong; List chunkDigestsList = new ArrayList<>(digestAlgorithms.size()); for (ContentDigestAlgorithm algorithms : digestAlgorithms) { chunkDigestsList.add(new ChunkDigests(algorithms, chunkCount)); } ChunkSupplier chunkSupplier = new ChunkSupplier(contents); executor.execute(() -> new ChunkDigester(chunkSupplier, chunkDigestsList)); // Compute and write out final digest for each algorithm. for (ChunkDigests chunkDigests : chunkDigestsList) { MessageDigest messageDigest = chunkDigests.createMessageDigest(); outputContentDigests.put( chunkDigests.algorithm, messageDigest.digest(chunkDigests.concatOfDigestsOfChunks)); } } private static class ChunkDigests { private final ContentDigestAlgorithm algorithm; private final int digestOutputSize; private final byte[] concatOfDigestsOfChunks; private ChunkDigests(ContentDigestAlgorithm algorithm, int chunkCount) { this.algorithm = algorithm; digestOutputSize = this.algorithm.getChunkDigestOutputSizeBytes(); concatOfDigestsOfChunks = new byte[1 + 4 + chunkCount * digestOutputSize]; // Fill the initial values of the concatenated digests of chunks, which is // {0x5a, 4-bytes-of-little-endian-chunk-count, digests*...}. concatOfDigestsOfChunks[0] = 0x5a; setUnsignedInt32LittleEndian(chunkCount, concatOfDigestsOfChunks, 1); } private MessageDigest createMessageDigest() throws NoSuchAlgorithmException { return MessageDigest.getInstance(algorithm.getJcaMessageDigestAlgorithm()); } private int getOffset(int chunkIndex) { return 1 + 4 + chunkIndex * digestOutputSize; } } /** * A per-thread digest worker. */ private static class ChunkDigester implements Runnable { private final ChunkSupplier dataSupplier; private final List chunkDigests; private final List messageDigests; private final DataSink mdSink; private ChunkDigester(ChunkSupplier dataSupplier, List chunkDigests) { this.dataSupplier = dataSupplier; this.chunkDigests = chunkDigests; messageDigests = new ArrayList<>(chunkDigests.size()); for (ChunkDigests chunkDigest : chunkDigests) { try { messageDigests.add(chunkDigest.createMessageDigest()); } catch (NoSuchAlgorithmException ex) { throw new RuntimeException(ex); } } mdSink = DataSinks.asDataSink(messageDigests.toArray(new MessageDigest[0])); } @Override public void run() { byte[] chunkContentPrefix = new byte[5]; chunkContentPrefix[0] = (byte) 0xa5; try { for (ChunkSupplier.Chunk chunk = dataSupplier.get(); chunk != null; chunk = dataSupplier.get()) { int size = chunk.size; if (size > CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES) { throw new RuntimeException("Chunk size greater than expected: " + size); } // First update with the chunk prefix. setUnsignedInt32LittleEndian(size, chunkContentPrefix, 1); mdSink.consume(chunkContentPrefix, 0, chunkContentPrefix.length); // Then update with the chunk data. mdSink.consume(chunk.data); // Now finalize chunk for all algorithms. for (int i = 0; i < chunkDigests.size(); i++) { ChunkDigests chunkDigest = chunkDigests.get(i); int actualDigestSize = messageDigests.get(i).digest( chunkDigest.concatOfDigestsOfChunks, chunkDigest.getOffset(chunk.chunkIndex), chunkDigest.digestOutputSize); if (actualDigestSize != chunkDigest.digestOutputSize) { throw new RuntimeException( "Unexpected output size of " + chunkDigest.algorithm + " digest: " + actualDigestSize); } } } } catch (IOException | DigestException e) { throw new RuntimeException(e); } } } /** * Thread-safe 1MB DataSource chunk supplier. When bounds are met in a * supplied {@link DataSource}, the data from the next {@link DataSource} * are NOT concatenated. Only the next call to get() will fetch from the * next {@link DataSource} in the input {@link DataSource} array. */ private static class ChunkSupplier implements Supplier { private final DataSource[] dataSources; private final int[] chunkCounts; private final int totalChunkCount; private final AtomicInteger nextIndex; private ChunkSupplier(DataSource[] dataSources) { this.dataSources = dataSources; chunkCounts = new int[dataSources.length]; int totalChunkCount = 0; for (int i = 0; i < dataSources.length; i++) { long chunkCount = getChunkCount(dataSources[i].size(), CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES); if (chunkCount > Integer.MAX_VALUE) { throw new RuntimeException( String.format( "Number of chunks in dataSource[%d] is greater than max int.", i)); } chunkCounts[i] = (int)chunkCount; totalChunkCount = (int) (totalChunkCount + chunkCount); } this.totalChunkCount = totalChunkCount; nextIndex = new AtomicInteger(0); } /** * We map an integer index to the termination-adjusted dataSources 1MB chunks. * Note that {@link Chunk}s could be less than 1MB, namely the last 1MB-aligned * blocks in each input {@link DataSource} (unless the DataSource itself is * 1MB-aligned). */ @Override public ChunkSupplier.Chunk get() { int index = nextIndex.getAndIncrement(); if (index < 0 || index >= totalChunkCount) { return null; } int dataSourceIndex = 0; long dataSourceChunkOffset = index; for (; dataSourceIndex < dataSources.length; dataSourceIndex++) { if (dataSourceChunkOffset < chunkCounts[dataSourceIndex]) { break; } dataSourceChunkOffset -= chunkCounts[dataSourceIndex]; } long remainingSize = Math.min( dataSources[dataSourceIndex].size() - dataSourceChunkOffset * CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES, CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES); final int size = (int)remainingSize; final ByteBuffer buffer = ByteBuffer.allocate(size); try { dataSources[dataSourceIndex].copyTo( dataSourceChunkOffset * CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES, size, buffer); } catch (IOException e) { throw new IllegalStateException("Failed to read chunk", e); } buffer.rewind(); return new Chunk(index, buffer, size); } static class Chunk { private final int chunkIndex; private final ByteBuffer data; private final int size; private Chunk(int chunkIndex, ByteBuffer data, int size) { this.chunkIndex = chunkIndex; this.data = data; this.size = size; } } } @SuppressWarnings("ByteBufferBackingArray") private static void computeApkVerityDigest(DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, Map outputContentDigests) throws IOException, NoSuchAlgorithmException { ByteBuffer encoded = createVerityDigestBuffer(true); // Use 0s as salt for now. This also needs to be consistent in the fsverify header for // kernel to use. try (VerityTreeBuilder builder = new VerityTreeBuilder(new byte[8])) { byte[] rootHash = builder.generateVerityTreeRootHash(beforeCentralDir, centralDir, eocd); encoded.put(rootHash); encoded.putLong(beforeCentralDir.size() + centralDir.size() + eocd.size()); outputContentDigests.put(VERITY_CHUNKED_SHA256, encoded.array()); } } private static ByteBuffer createVerityDigestBuffer(boolean includeSourceDataSize) { // FORMAT: // OFFSET DATA TYPE DESCRIPTION // * @+0 bytes uint8[32] Merkle tree root hash of SHA-256 // * @+32 bytes int64 (optional) Length of source data int backBufferSize = VERITY_CHUNKED_SHA256.getChunkDigestOutputSizeBytes(); if (includeSourceDataSize) { backBufferSize += Long.SIZE / Byte.SIZE; } ByteBuffer encoded = ByteBuffer.allocate(backBufferSize); encoded.order(ByteOrder.LITTLE_ENDIAN); return encoded; } public static class VerityTreeAndDigest { public final ContentDigestAlgorithm contentDigestAlgorithm; public final byte[] rootHash; public final byte[] tree; VerityTreeAndDigest(ContentDigestAlgorithm contentDigestAlgorithm, byte[] rootHash, byte[] tree) { this.contentDigestAlgorithm = contentDigestAlgorithm; this.rootHash = rootHash; this.tree = tree; } } @SuppressWarnings("ByteBufferBackingArray") public static VerityTreeAndDigest computeChunkVerityTreeAndDigest(DataSource dataSource) throws IOException, NoSuchAlgorithmException { ByteBuffer encoded = createVerityDigestBuffer(false); // Use 0s as salt for now. This also needs to be consistent in the fsverify header for // kernel to use. try (VerityTreeBuilder builder = new VerityTreeBuilder(null)) { ByteBuffer tree = builder.generateVerityTree(dataSource); byte[] rootHash = builder.getRootHashFromTree(tree); encoded.put(rootHash); return new VerityTreeAndDigest(VERITY_CHUNKED_SHA256, encoded.array(), tree.array()); } } private static long getChunkCount(long inputSize, long chunkSize) { return (inputSize + chunkSize - 1) / chunkSize; } private static void setUnsignedInt32LittleEndian(int value, byte[] result, int offset) { result[offset] = (byte) (value & 0xff); result[offset + 1] = (byte) ((value >> 8) & 0xff); result[offset + 2] = (byte) ((value >> 16) & 0xff); result[offset + 3] = (byte) ((value >> 24) & 0xff); } public static byte[] encodePublicKey(PublicKey publicKey) throws InvalidKeyException, NoSuchAlgorithmException { byte[] encodedPublicKey = null; if ("X.509".equals(publicKey.getFormat())) { encodedPublicKey = publicKey.getEncoded(); // if the key is an RSA key check for a negative modulus String keyAlgorithm = publicKey.getAlgorithm(); if ("RSA".equals(keyAlgorithm) || OID_RSA_ENCRYPTION.equals(keyAlgorithm)) { try { // Parse the encoded public key into the separate elements of the // SubjectPublicKeyInfo to obtain the SubjectPublicKey. ByteBuffer encodedPublicKeyBuffer = ByteBuffer.wrap(encodedPublicKey); SubjectPublicKeyInfo subjectPublicKeyInfo = Asn1BerParser.parse( encodedPublicKeyBuffer, SubjectPublicKeyInfo.class); // The SubjectPublicKey is encoded as a bit string within the // SubjectPublicKeyInfo. The first byte of the encoding is the number of padding // bits; store this and decode the rest of the bit string into the RSA modulus // and exponent. ByteBuffer subjectPublicKeyBuffer = subjectPublicKeyInfo.subjectPublicKey; byte padding = subjectPublicKeyBuffer.get(); RSAPublicKey rsaPublicKey = Asn1BerParser.parse(subjectPublicKeyBuffer, RSAPublicKey.class); // if the modulus is negative then attempt to reencode it with a leading 0 sign // byte. if (rsaPublicKey.modulus.compareTo(BigInteger.ZERO) < 0) { // A negative modulus indicates the leading bit in the integer is 1. Per // ASN.1 encoding rules to encode a positive integer with the leading bit // set to 1 a byte containing all zeros should precede the integer encoding. byte[] encodedModulus = rsaPublicKey.modulus.toByteArray(); byte[] reencodedModulus = new byte[encodedModulus.length + 1]; reencodedModulus[0] = 0; System.arraycopy(encodedModulus, 0, reencodedModulus, 1, encodedModulus.length); rsaPublicKey.modulus = new BigInteger(reencodedModulus); // Once the modulus has been corrected reencode the RSAPublicKey, then // restore the padding value in the bit string and reencode the entire // SubjectPublicKeyInfo to be returned to the caller. byte[] reencodedRSAPublicKey = Asn1DerEncoder.encode(rsaPublicKey); byte[] reencodedSubjectPublicKey = new byte[reencodedRSAPublicKey.length + 1]; reencodedSubjectPublicKey[0] = padding; System.arraycopy(reencodedRSAPublicKey, 0, reencodedSubjectPublicKey, 1, reencodedRSAPublicKey.length); subjectPublicKeyInfo.subjectPublicKey = ByteBuffer.wrap( reencodedSubjectPublicKey); encodedPublicKey = Asn1DerEncoder.encode(subjectPublicKeyInfo); } } catch (Asn1DecodingException | Asn1EncodingException e) { System.out.println("Caught a exception encoding the public key: " + e); e.printStackTrace(); encodedPublicKey = null; } } } if (encodedPublicKey == null) { try { encodedPublicKey = KeyFactory.getInstance(publicKey.getAlgorithm()) .getKeySpec(publicKey, X509EncodedKeySpec.class) .getEncoded(); } catch (InvalidKeySpecException e) { throw new InvalidKeyException( "Failed to obtain X.509 encoded form of public key " + publicKey + " of class " + publicKey.getClass().getName(), e); } } if ((encodedPublicKey == null) || (encodedPublicKey.length == 0)) { throw new InvalidKeyException( "Failed to obtain X.509 encoded form of public key " + publicKey + " of class " + publicKey.getClass().getName()); } return encodedPublicKey; } public static List encodeCertificates(List certificates) throws CertificateEncodingException { List result = new ArrayList<>(certificates.size()); for (X509Certificate certificate : certificates) { result.add(certificate.getEncoded()); } return result; } public static byte[] encodeAsLengthPrefixedElement(byte[] bytes) { byte[][] adapterBytes = new byte[1][]; adapterBytes[0] = bytes; return encodeAsSequenceOfLengthPrefixedElements(adapterBytes); } public static byte[] encodeAsSequenceOfLengthPrefixedElements(List sequence) { return encodeAsSequenceOfLengthPrefixedElements( sequence.toArray(new byte[sequence.size()][])); } public static byte[] encodeAsSequenceOfLengthPrefixedElements(byte[][] sequence) { int payloadSize = 0; for (byte[] element : sequence) { payloadSize += 4 + element.length; } ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); for (byte[] element : sequence) { result.putInt(element.length); result.put(element); } return result.array(); } public static byte[] encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( List> sequence) { return ApkSigningBlockUtilsLite .encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(sequence); } /** * Returns the APK Signature Scheme block contained in the provided APK file for the given ID * and the additional information relevant for verifying the block against the file. * * @param blockId the ID value in the APK Signing Block's sequence of ID-value pairs * identifying the appropriate block to find, e.g. the APK Signature Scheme v2 * block ID. * * @throws SignatureNotFoundException if the APK is not signed using given APK Signature Scheme * @throws IOException if an I/O error occurs while reading the APK */ public static SignatureInfo findSignature( DataSource apk, ApkUtils.ZipSections zipSections, int blockId, Result result) throws IOException, SignatureNotFoundException { try { return ApkSigningBlockUtilsLite.findSignature(apk, zipSections, blockId); } catch (com.android.apksig.internal.apk.SignatureNotFoundException e) { throw new SignatureNotFoundException(e.getMessage()); } } /** * Generates a new DataSource representing the APK contents before the Central Directory with * padding, if padding is requested. If the existing data entries before the Central Directory * are already aligned, or no padding is requested, the original DataSource is used. This * padding is used to allow for verity-based APK verification. * * @return {@code Pair} containing the potentially new {@code DataSource} and the amount of * padding used. */ public static Pair generateApkSigningBlockPadding( DataSource beforeCentralDir, boolean apkSigningBlockPaddingSupported) { // Ensure APK Signing Block starts from page boundary. int padSizeBeforeSigningBlock = 0; if (apkSigningBlockPaddingSupported && (beforeCentralDir.size() % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES != 0)) { padSizeBeforeSigningBlock = (int) ( ANDROID_COMMON_PAGE_ALIGNMENT_BYTES - beforeCentralDir.size() % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES); beforeCentralDir = new ChainedDataSource( beforeCentralDir, DataSources.asDataSource( ByteBuffer.allocate(padSizeBeforeSigningBlock))); } return Pair.of(beforeCentralDir, padSizeBeforeSigningBlock); } public static DataSource copyWithModifiedCDOffset( DataSource beforeCentralDir, DataSource eocd) throws IOException { // Ensure that, when digesting, ZIP End of Central Directory record's Central Directory // offset field is treated as pointing to the offset at which the APK Signing Block will // start. long centralDirOffsetForDigesting = beforeCentralDir.size(); ByteBuffer eocdBuf = ByteBuffer.allocate((int) eocd.size()); eocdBuf.order(ByteOrder.LITTLE_ENDIAN); eocd.copyTo(0, (int) eocd.size(), eocdBuf); eocdBuf.flip(); ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, centralDirOffsetForDigesting); return DataSources.asDataSource(eocdBuf); } public static byte[] generateApkSigningBlock( List> apkSignatureSchemeBlockPairs) { // FORMAT: // uint64: size (excluding this field) // repeated ID-value pairs: // uint64: size (excluding this field) // uint32: ID // (size - 4) bytes: value // (extra verity ID-value for padding to make block size a multiple of 4096 bytes) // uint64: size (same as the one above) // uint128: magic int blocksSize = 0; for (Pair schemeBlockPair : apkSignatureSchemeBlockPairs) { blocksSize += 8 + 4 + schemeBlockPair.getFirst().length; // size + id + value } int resultSize = 8 // size + blocksSize + 8 // size + 16 // magic ; ByteBuffer paddingPair = null; if (resultSize % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES != 0) { int padding = ANDROID_COMMON_PAGE_ALIGNMENT_BYTES - (resultSize % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES); if (padding < 12) { // minimum size of an ID-value pair padding += ANDROID_COMMON_PAGE_ALIGNMENT_BYTES; } paddingPair = ByteBuffer.allocate(padding).order(ByteOrder.LITTLE_ENDIAN); paddingPair.putLong(padding - 8); paddingPair.putInt(VERITY_PADDING_BLOCK_ID); paddingPair.rewind(); resultSize += padding; } ByteBuffer result = ByteBuffer.allocate(resultSize); result.order(ByteOrder.LITTLE_ENDIAN); long blockSizeFieldValue = resultSize - 8L; result.putLong(blockSizeFieldValue); for (Pair schemeBlockPair : apkSignatureSchemeBlockPairs) { byte[] apkSignatureSchemeBlock = schemeBlockPair.getFirst(); int apkSignatureSchemeId = schemeBlockPair.getSecond(); long pairSizeFieldValue = 4L + apkSignatureSchemeBlock.length; result.putLong(pairSizeFieldValue); result.putInt(apkSignatureSchemeId); result.put(apkSignatureSchemeBlock); } if (paddingPair != null) { result.put(paddingPair); } result.putLong(blockSizeFieldValue); result.put(APK_SIGNING_BLOCK_MAGIC); return result.array(); } /** * Returns the individual APK signature blocks within the provided {@code apkSigningBlock} in a * {@code List} of {@code Pair} instances where the first element in the {@code Pair} is the * contents / value of the signature block and the second element is the ID of the block. * * @throws IOException if an error is encountered reading the provided {@code apkSigningBlock} */ public static List> getApkSignatureBlocks( DataSource apkSigningBlock) throws IOException { // FORMAT: // uint64: size (excluding this field) // repeated ID-value pairs: // uint64: size (excluding this field) // uint32: ID // (size - 4) bytes: value // (extra verity ID-value for padding to make block size a multiple of 4096 bytes) // uint64: size (same as the one above) // uint128: magic long apkSigningBlockSize = apkSigningBlock.size(); if (apkSigningBlock.size() > Integer.MAX_VALUE || apkSigningBlockSize < 32) { throw new IllegalArgumentException( "APK signing block size out of range: " + apkSigningBlockSize); } // Remove the header and footer from the signing block to iterate over only the repeated // ID-value pairs. ByteBuffer apkSigningBlockBuffer = apkSigningBlock.getByteBuffer(8, (int) apkSigningBlock.size() - 32); apkSigningBlockBuffer.order(ByteOrder.LITTLE_ENDIAN); List> signatureBlocks = new ArrayList<>(); while (apkSigningBlockBuffer.hasRemaining()) { long blockLength = apkSigningBlockBuffer.getLong(); if (blockLength > Integer.MAX_VALUE || blockLength < 4) { throw new IllegalArgumentException( "Block index " + (signatureBlocks.size() + 1) + " size out of range: " + blockLength); } int blockId = apkSigningBlockBuffer.getInt(); // Since the block ID has already been read from the signature block read the next // blockLength - 4 bytes as the value. byte[] blockValue = new byte[(int) blockLength - 4]; apkSigningBlockBuffer.get(blockValue); signatureBlocks.add(Pair.of(blockValue, blockId)); } return signatureBlocks; } /** * Returns the individual APK signers within the provided {@code signatureBlock} in a {@code * List} of {@code Pair} instances where the first element is a {@code List} of {@link * X509Certificate}s and the second element is a byte array of the individual signer's block. * *

This method supports any signature block that adheres to the following format up to the * signing certificate(s): *

     * * length-prefixed sequence of length-prefixed signers
     *   * length-prefixed signed data
     *     * length-prefixed sequence of length-prefixed digests:
     *       * uint32: signature algorithm ID
     *       * length-prefixed bytes: digest of contents
     *     * length-prefixed sequence of certificates:
     *       * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded).
     * 
* *

Note, this is a convenience method to obtain any signers from an existing signature block; * the signature of each signer will not be verified. * * @throws ApkFormatException if an error is encountered while parsing the provided {@code * signatureBlock} * @throws CertificateException if the signing certificate(s) within an individual signer block * cannot be parsed */ public static List, byte[]>> getApkSignatureBlockSigners( byte[] signatureBlock) throws ApkFormatException, CertificateException { ByteBuffer signatureBlockBuffer = ByteBuffer.wrap(signatureBlock); signatureBlockBuffer.order(ByteOrder.LITTLE_ENDIAN); ByteBuffer signersBuffer = getLengthPrefixedSlice(signatureBlockBuffer); List, byte[]>> signers = new ArrayList<>(); while (signersBuffer.hasRemaining()) { // Parse the next signer block, save all of its bytes for the resulting List, and // rewind the buffer to allow the signing certificate(s) to be parsed. ByteBuffer signer = getLengthPrefixedSlice(signersBuffer); byte[] signerBytes = new byte[signer.remaining()]; signer.get(signerBytes); signer.rewind(); ByteBuffer signedData = getLengthPrefixedSlice(signer); // The first length prefixed slice is the sequence of digests which are not required // when obtaining the signing certificate(s). getLengthPrefixedSlice(signedData); ByteBuffer certificatesBuffer = getLengthPrefixedSlice(signedData); List certificates = new ArrayList<>(); while (certificatesBuffer.hasRemaining()) { int certLength = certificatesBuffer.getInt(); byte[] certBytes = new byte[certLength]; if (certLength > certificatesBuffer.remaining()) { throw new IllegalArgumentException( "Cert index " + (certificates.size() + 1) + " under signer index " + (signers.size() + 1) + " size out of range: " + certLength); } certificatesBuffer.get(certBytes); GuaranteedEncodedFormX509Certificate signerCert = new GuaranteedEncodedFormX509Certificate( X509CertificateUtils.generateCertificate(certBytes), certBytes); certificates.add(signerCert); } signers.add(Pair.of(certificates, signerBytes)); } return signers; } /** * Computes the digests of the given APK components according to the algorithms specified in the * given SignerConfigs. * * @param signerConfigs signer configurations, one for each signer At least one signer config * must be provided. * * @throws IOException if an I/O error occurs * @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is * missing * @throws SignatureException if an error occurs when computing digests of generating * signatures */ public static Pair, Map> computeContentDigests( RunnablesExecutor executor, DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, List signerConfigs) throws IOException, NoSuchAlgorithmException, SignatureException { if (signerConfigs.isEmpty()) { throw new IllegalArgumentException( "No signer configs provided. At least one is required"); } // Figure out which digest(s) to use for APK contents. Set contentDigestAlgorithms = new HashSet<>(1); for (SignerConfig signerConfig : signerConfigs) { for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) { contentDigestAlgorithms.add(signatureAlgorithm.getContentDigestAlgorithm()); } } // Compute digests of APK contents. Map contentDigests; // digest algorithm ID -> digest try { contentDigests = computeContentDigests( executor, contentDigestAlgorithms, beforeCentralDir, centralDir, eocd); } catch (IOException e) { throw new IOException("Failed to read APK being signed", e); } catch (DigestException e) { throw new SignatureException("Failed to compute digests of APK", e); } // Sign the digests and wrap the signatures and signer info into an APK Signing Block. return Pair.of(signerConfigs, contentDigests); } /** * Returns the subset of signatures which are expected to be verified by at least one Android * platform version in the {@code [minSdkVersion, maxSdkVersion]} range. The returned result is * guaranteed to contain at least one signature. * *

Each Android platform version typically verifies exactly one signature from the provided * {@code signatures} set. This method returns the set of these signatures collected over all * requested platform versions. As a result, the result may contain more than one signature. * * @throws NoSupportedSignaturesException if no supported signatures were * found for an Android platform version in the range. */ public static List getSignaturesToVerify( List signatures, int minSdkVersion, int maxSdkVersion) throws NoSupportedSignaturesException { return getSignaturesToVerify(signatures, minSdkVersion, maxSdkVersion, false); } /** * Returns the subset of signatures which are expected to be verified by at least one Android * platform version in the {@code [minSdkVersion, maxSdkVersion]} range. The returned result is * guaranteed to contain at least one signature. * *

{@code onlyRequireJcaSupport} can be set to true for cases that only require verifying a * signature within the signing block using the standard JCA. * *

Each Android platform version typically verifies exactly one signature from the provided * {@code signatures} set. This method returns the set of these signatures collected over all * requested platform versions. As a result, the result may contain more than one signature. * * @throws NoSupportedSignaturesException if no supported signatures were * found for an Android platform version in the range. */ public static List getSignaturesToVerify( List signatures, int minSdkVersion, int maxSdkVersion, boolean onlyRequireJcaSupport) throws NoSupportedSignaturesException { try { return ApkSigningBlockUtilsLite.getSignaturesToVerify(signatures, minSdkVersion, maxSdkVersion, onlyRequireJcaSupport); } catch (NoApkSupportedSignaturesException e) { throw new NoSupportedSignaturesException(e.getMessage()); } } public static class NoSupportedSignaturesException extends NoApkSupportedSignaturesException { public NoSupportedSignaturesException(String message) { super(message); } } public static class SignatureNotFoundException extends Exception { private static final long serialVersionUID = 1L; public SignatureNotFoundException(String message) { super(message); } public SignatureNotFoundException(String message, Throwable cause) { super(message, cause); } } /** * uses the SignatureAlgorithms in the provided signerConfig to sign the provided data * * @return list of signature algorithm IDs and their corresponding signatures over the data. */ public static List> generateSignaturesOverData( SignerConfig signerConfig, byte[] data) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException { List> signatures = new ArrayList<>(signerConfig.signatureAlgorithms.size()); PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey(); for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) { Pair sigAlgAndParams = signatureAlgorithm.getJcaSignatureAlgorithmAndParams(); String jcaSignatureAlgorithm = sigAlgAndParams.getFirst(); AlgorithmParameterSpec jcaSignatureAlgorithmParams = sigAlgAndParams.getSecond(); byte[] signatureBytes; try { signatureBytes = SignerEngineFactory.getImplementation( signerConfig.keyConfig, jcaSignatureAlgorithm, jcaSignatureAlgorithmParams) .sign(data); } catch (InvalidKeyException e) { throw new InvalidKeyException("Failed to sign using " + jcaSignatureAlgorithm, e); } catch (InvalidAlgorithmParameterException | SignatureException e) { throw new SignatureException("Failed to sign using " + jcaSignatureAlgorithm, e); } try { Signature signature = Signature.getInstance(jcaSignatureAlgorithm); signature.initVerify(publicKey); if (jcaSignatureAlgorithmParams != null) { signature.setParameter(jcaSignatureAlgorithmParams); } signature.update(data); if (!signature.verify(signatureBytes)) { throw new SignatureException("Failed to verify generated " + jcaSignatureAlgorithm + " signature using public key from certificate"); } } catch (InvalidKeyException e) { throw new InvalidKeyException( "Failed to verify generated " + jcaSignatureAlgorithm + " signature using" + " public key from certificate", e); } catch (InvalidAlgorithmParameterException | SignatureException e) { throw new SignatureException( "Failed to verify generated " + jcaSignatureAlgorithm + " signature using" + " public key from certificate", e); } signatures.add(Pair.of(signatureAlgorithm.getId(), signatureBytes)); } return signatures; } /** * Wrap the signature according to CMS PKCS #7 RFC 5652. * The high-level simplified structure is as follows: * // ContentInfo * // digestAlgorithm * // SignedData * // bag of certificates * // SignerInfo * // signing cert issuer and serial number (for locating the cert in the above bag) * // digestAlgorithm * // signatureAlgorithm * // signature * * @throws Asn1EncodingException if the ASN.1 structure could not be encoded */ public static byte[] generatePkcs7DerEncodedMessage( byte[] signatureBytes, ByteBuffer data, List signerCerts, AlgorithmIdentifier digestAlgorithmId, AlgorithmIdentifier signatureAlgorithmId) throws Asn1EncodingException, CertificateEncodingException { SignerInfo signerInfo = new SignerInfo(); signerInfo.version = 1; X509Certificate signingCert = signerCerts.get(0); X500Principal signerCertIssuer = signingCert.getIssuerX500Principal(); signerInfo.sid = new SignerIdentifier( new IssuerAndSerialNumber( new Asn1OpaqueObject(signerCertIssuer.getEncoded()), signingCert.getSerialNumber())); signerInfo.digestAlgorithm = digestAlgorithmId; signerInfo.signatureAlgorithm = signatureAlgorithmId; signerInfo.signature = ByteBuffer.wrap(signatureBytes); SignedData signedData = new SignedData(); signedData.certificates = new ArrayList<>(signerCerts.size()); for (X509Certificate cert : signerCerts) { signedData.certificates.add(new Asn1OpaqueObject(cert.getEncoded())); } signedData.version = 1; signedData.digestAlgorithms = Collections.singletonList(digestAlgorithmId); signedData.encapContentInfo = new EncapsulatedContentInfo(Pkcs7Constants.OID_DATA); // If data is not null, data will be embedded as is in the result -- an attached pcsk7 signedData.encapContentInfo.content = data; signedData.signerInfos = Collections.singletonList(signerInfo); ContentInfo contentInfo = new ContentInfo(); contentInfo.contentType = Pkcs7Constants.OID_SIGNED_DATA; contentInfo.content = new Asn1OpaqueObject(Asn1DerEncoder.encode(signedData)); return Asn1DerEncoder.encode(contentInfo); } /** * Picks the correct v2/v3 digest for v4 signature verification. * * Keep in sync with pickBestDigestForV4 in framework's ApkSigningBlockUtils. */ public static byte[] pickBestDigestForV4(Map contentDigests) { for (ContentDigestAlgorithm algo : V4_CONTENT_DIGEST_ALGORITHMS) { if (contentDigests.containsKey(algo)) { return contentDigests.get(algo); } } return null; } /** Signer configuration. */ public static class SignerConfig { /** * Private key. * * @deprecated all internal usage has migrated to use {@link #keyConfig}. This field is not * removed so that compilation is not broken for clients referencing it, but using this * field may lead to unexpected errors. */ @Deprecated public PrivateKey privateKey; /** Signing key config. */ public KeyConfig keyConfig; /** * Certificates, with the first certificate containing the public key corresponding to * {@link #keyConfig}. */ public List certificates; /** * List of signature algorithms with which to sign. */ public List signatureAlgorithms; public int minSdkVersion; public int maxSdkVersion; public boolean signerTargetsDevRelease; public SigningCertificateLineage signingCertificateLineage; } public static class Result extends ApkSigResult { public SigningCertificateLineage signingCertificateLineage = null; public final List signers = new ArrayList<>(); private final List mWarnings = new ArrayList<>(); private final List mErrors = new ArrayList<>(); public Result(int signatureSchemeVersion) { super(signatureSchemeVersion); } public boolean containsErrors() { if (!mErrors.isEmpty()) { return true; } if (!signers.isEmpty()) { for (Result.SignerInfo signer : signers) { if (signer.containsErrors()) { return true; } } } return false; } public boolean containsWarnings() { if (!mWarnings.isEmpty()) { return true; } if (!signers.isEmpty()) { for (Result.SignerInfo signer : signers) { if (signer.containsWarnings()) { return true; } } } return false; } public void addError(ApkVerifier.Issue msg, Object... parameters) { mErrors.add(new ApkVerifier.IssueWithParams(msg, parameters)); } public void addWarning(ApkVerifier.Issue msg, Object... parameters) { mWarnings.add(new ApkVerifier.IssueWithParams(msg, parameters)); } @Override public List getErrors() { return mErrors; } @Override public List getWarnings() { return mWarnings; } public static class SignerInfo extends ApkSignerInfo { public List contentDigests = new ArrayList<>(); public Map verifiedContentDigests = new HashMap<>(); public List signatures = new ArrayList<>(); public Map verifiedSignatures = new HashMap<>(); public List additionalAttributes = new ArrayList<>(); public byte[] signedData; public int minSdkVersion; public int maxSdkVersion; public SigningCertificateLineage signingCertificateLineage; private final List mWarnings = new ArrayList<>(); private final List mErrors = new ArrayList<>(); public void addError(ApkVerifier.Issue msg, Object... parameters) { mErrors.add(new ApkVerifier.IssueWithParams(msg, parameters)); } public void addWarning(ApkVerifier.Issue msg, Object... parameters) { mWarnings.add(new ApkVerifier.IssueWithParams(msg, parameters)); } public boolean containsErrors() { return !mErrors.isEmpty(); } public boolean containsWarnings() { return !mWarnings.isEmpty(); } public List getErrors() { return mErrors; } public List getWarnings() { return mWarnings; } public static class ContentDigest { private final int mSignatureAlgorithmId; private final byte[] mValue; public ContentDigest(int signatureAlgorithmId, byte[] value) { mSignatureAlgorithmId = signatureAlgorithmId; mValue = value; } public int getSignatureAlgorithmId() { return mSignatureAlgorithmId; } public byte[] getValue() { return mValue; } } public static class Signature { private final int mAlgorithmId; private final byte[] mValue; public Signature(int algorithmId, byte[] value) { mAlgorithmId = algorithmId; mValue = value; } public int getAlgorithmId() { return mAlgorithmId; } public byte[] getValue() { return mValue; } } public static class AdditionalAttribute { private final int mId; private final byte[] mValue; public AdditionalAttribute(int id, byte[] value) { mId = id; mValue = value.clone(); } public int getId() { return mId; } public byte[] getValue() { return mValue.clone(); } } } } public static class SupportedSignature extends ApkSupportedSignature { public SupportedSignature(SignatureAlgorithm algorithm, byte[] signature) { super(algorithm, signature); } } public static class SigningSchemeBlockAndDigests { public final Pair signingSchemeBlock; public final Map digestInfo; public SigningSchemeBlockAndDigests( Pair signingSchemeBlock, Map digestInfo) { this.signingSchemeBlock = signingSchemeBlock; this.digestInfo = digestInfo; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_ApkSigningBlockUtilsLite.java0100644 0000000 0000000 00000000034 14763776540 031332 xustar000000000 0000000 28 mtime=1741684064.5720000 src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtilsLite.java0100644 0000000 0000000 00000041705 14763776540 026045 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkSigningBlockNotFoundException; import com.android.apksig.apk.ApkUtilsLite; import com.android.apksig.internal.util.Pair; import com.android.apksig.util.DataSource; import com.android.apksig.zip.ZipSections; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Lightweight version of the ApkSigningBlockUtils for clients that only require a subset of the * utility functionality. */ public class ApkSigningBlockUtilsLite { private ApkSigningBlockUtilsLite() {} private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); /** * Returns the APK Signature Scheme block contained in the provided APK file for the given ID * and the additional information relevant for verifying the block against the file. * * @param blockId the ID value in the APK Signing Block's sequence of ID-value pairs * identifying the appropriate block to find, e.g. the APK Signature Scheme v2 * block ID. * * @throws SignatureNotFoundException if the APK is not signed using given APK Signature Scheme * @throws IOException if an I/O error occurs while reading the APK */ public static SignatureInfo findSignature( DataSource apk, ZipSections zipSections, int blockId) throws IOException, SignatureNotFoundException { // Find the APK Signing Block. DataSource apkSigningBlock; long apkSigningBlockOffset; try { ApkUtilsLite.ApkSigningBlock apkSigningBlockInfo = ApkUtilsLite.findApkSigningBlock(apk, zipSections); apkSigningBlockOffset = apkSigningBlockInfo.getStartOffset(); apkSigningBlock = apkSigningBlockInfo.getContents(); } catch (ApkSigningBlockNotFoundException e) { throw new SignatureNotFoundException(e.getMessage(), e); } ByteBuffer apkSigningBlockBuf = apkSigningBlock.getByteBuffer(0, (int) apkSigningBlock.size()); apkSigningBlockBuf.order(ByteOrder.LITTLE_ENDIAN); // Find the APK Signature Scheme Block inside the APK Signing Block. ByteBuffer apkSignatureSchemeBlock = findApkSignatureSchemeBlock(apkSigningBlockBuf, blockId); return new SignatureInfo( apkSignatureSchemeBlock, apkSigningBlockOffset, zipSections.getZipCentralDirectoryOffset(), zipSections.getZipEndOfCentralDirectoryOffset(), zipSections.getZipEndOfCentralDirectory()); } public static ByteBuffer findApkSignatureSchemeBlock( ByteBuffer apkSigningBlock, int blockId) throws SignatureNotFoundException { checkByteOrderLittleEndian(apkSigningBlock); // FORMAT: // OFFSET DATA TYPE DESCRIPTION // * @+0 bytes uint64: size in bytes (excluding this field) // * @+8 bytes pairs // * @-24 bytes uint64: size in bytes (same as the one above) // * @-16 bytes uint128: magic ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24); int entryCount = 0; while (pairs.hasRemaining()) { entryCount++; if (pairs.remaining() < 8) { throw new SignatureNotFoundException( "Insufficient data to read size of APK Signing Block entry #" + entryCount); } long lenLong = pairs.getLong(); if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) { throw new SignatureNotFoundException( "APK Signing Block entry #" + entryCount + " size out of range: " + lenLong); } int len = (int) lenLong; int nextEntryPos = pairs.position() + len; if (len > pairs.remaining()) { throw new SignatureNotFoundException( "APK Signing Block entry #" + entryCount + " size out of range: " + len + ", available: " + pairs.remaining()); } int id = pairs.getInt(); if (id == blockId) { return getByteBuffer(pairs, len - 4); } pairs.position(nextEntryPos); } throw new SignatureNotFoundException( "No APK Signature Scheme block in APK Signing Block with ID: " + blockId); } public static void checkByteOrderLittleEndian(ByteBuffer buffer) { if (buffer.order() != ByteOrder.LITTLE_ENDIAN) { throw new IllegalArgumentException("ByteBuffer byte order must be little endian"); } } /** * Returns the subset of signatures which are expected to be verified by at least one Android * platform version in the {@code [minSdkVersion, maxSdkVersion]} range. The returned result is * guaranteed to contain at least one signature. * *

Each Android platform version typically verifies exactly one signature from the provided * {@code signatures} set. This method returns the set of these signatures collected over all * requested platform versions. As a result, the result may contain more than one signature. * * @throws NoApkSupportedSignaturesException if no supported signatures were * found for an Android platform version in the range. */ public static List getSignaturesToVerify( List signatures, int minSdkVersion, int maxSdkVersion) throws NoApkSupportedSignaturesException { return getSignaturesToVerify(signatures, minSdkVersion, maxSdkVersion, false); } /** * Returns the subset of signatures which are expected to be verified by at least one Android * platform version in the {@code [minSdkVersion, maxSdkVersion]} range. The returned result is * guaranteed to contain at least one signature. * *

{@code onlyRequireJcaSupport} can be set to true for cases that only require verifying a * signature within the signing block using the standard JCA. * *

Each Android platform version typically verifies exactly one signature from the provided * {@code signatures} set. This method returns the set of these signatures collected over all * requested platform versions. As a result, the result may contain more than one signature. * * @throws NoApkSupportedSignaturesException if no supported signatures were * found for an Android platform version in the range. */ public static List getSignaturesToVerify( List signatures, int minSdkVersion, int maxSdkVersion, boolean onlyRequireJcaSupport) throws NoApkSupportedSignaturesException { // Pick the signature with the strongest algorithm at all required SDK versions, to mimic // Android's behavior on those versions. // // Here we assume that, once introduced, a signature algorithm continues to be supported in // all future Android versions. We also assume that the better-than relationship between // algorithms is exactly the same on all Android platform versions (except that older // platforms might support fewer algorithms). If these assumption are no longer true, the // logic here will need to change accordingly. Map bestSigAlgorithmOnSdkVersion = new HashMap<>(); int minProvidedSignaturesVersion = Integer.MAX_VALUE; for (T sig : signatures) { SignatureAlgorithm sigAlgorithm = sig.algorithm; int sigMinSdkVersion = onlyRequireJcaSupport ? sigAlgorithm.getJcaSigAlgMinSdkVersion() : sigAlgorithm.getMinSdkVersion(); if (sigMinSdkVersion > maxSdkVersion) { continue; } if (sigMinSdkVersion < minProvidedSignaturesVersion) { minProvidedSignaturesVersion = sigMinSdkVersion; } T candidate = bestSigAlgorithmOnSdkVersion.get(sigMinSdkVersion); if ((candidate == null) || (compareSignatureAlgorithm( sigAlgorithm, candidate.algorithm) > 0)) { bestSigAlgorithmOnSdkVersion.put(sigMinSdkVersion, sig); } } // Must have some supported signature algorithms for minSdkVersion. if (minSdkVersion < minProvidedSignaturesVersion) { throw new NoApkSupportedSignaturesException( "Minimum provided signature version " + minProvidedSignaturesVersion + " > minSdkVersion " + minSdkVersion); } if (bestSigAlgorithmOnSdkVersion.isEmpty()) { throw new NoApkSupportedSignaturesException("No supported signature"); } List signaturesToVerify = new ArrayList<>(bestSigAlgorithmOnSdkVersion.values()); Collections.sort( signaturesToVerify, (sig1, sig2) -> Integer.compare(sig1.algorithm.getId(), sig2.algorithm.getId())); return signaturesToVerify; } /** * Returns positive number if {@code alg1} is preferred over {@code alg2}, {@code -1} if * {@code alg2} is preferred over {@code alg1}, and {@code 0} if there is no preference. */ public static int compareSignatureAlgorithm(SignatureAlgorithm alg1, SignatureAlgorithm alg2) { ContentDigestAlgorithm digestAlg1 = alg1.getContentDigestAlgorithm(); ContentDigestAlgorithm digestAlg2 = alg2.getContentDigestAlgorithm(); return compareContentDigestAlgorithm(digestAlg1, digestAlg2); } /** * Returns a positive number if {@code alg1} is preferred over {@code alg2}, a negative number * if {@code alg2} is preferred over {@code alg1}, or {@code 0} if there is no preference. */ private static int compareContentDigestAlgorithm( ContentDigestAlgorithm alg1, ContentDigestAlgorithm alg2) { switch (alg1) { case CHUNKED_SHA256: switch (alg2) { case CHUNKED_SHA256: return 0; case CHUNKED_SHA512: case VERITY_CHUNKED_SHA256: return -1; default: throw new IllegalArgumentException("Unknown alg2: " + alg2); } case CHUNKED_SHA512: switch (alg2) { case CHUNKED_SHA256: case VERITY_CHUNKED_SHA256: return 1; case CHUNKED_SHA512: return 0; default: throw new IllegalArgumentException("Unknown alg2: " + alg2); } case VERITY_CHUNKED_SHA256: switch (alg2) { case CHUNKED_SHA256: return 1; case VERITY_CHUNKED_SHA256: return 0; case CHUNKED_SHA512: return -1; default: throw new IllegalArgumentException("Unknown alg2: " + alg2); } default: throw new IllegalArgumentException("Unknown alg1: " + alg1); } } /** * Returns new byte buffer whose content is a shared subsequence of this buffer's content * between the specified start (inclusive) and end (exclusive) positions. As opposed to * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source * buffer's byte order. */ private static ByteBuffer sliceFromTo(ByteBuffer source, int start, int end) { if (start < 0) { throw new IllegalArgumentException("start: " + start); } if (end < start) { throw new IllegalArgumentException("end < start: " + end + " < " + start); } int capacity = source.capacity(); if (end > source.capacity()) { throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity); } int originalLimit = source.limit(); int originalPosition = source.position(); try { source.position(0); source.limit(end); source.position(start); ByteBuffer result = source.slice(); result.order(source.order()); return result; } finally { source.position(0); source.limit(originalLimit); source.position(originalPosition); } } /** * Relative get method for reading {@code size} number of bytes from the current * position of this buffer. * *

This method reads the next {@code size} bytes at this buffer's current position, * returning them as a {@code ByteBuffer} with start set to 0, limit and capacity set to * {@code size}, byte order set to this buffer's byte order; and then increments the position by * {@code size}. */ private static ByteBuffer getByteBuffer(ByteBuffer source, int size) { if (size < 0) { throw new IllegalArgumentException("size: " + size); } int originalLimit = source.limit(); int position = source.position(); int limit = position + size; if ((limit < position) || (limit > originalLimit)) { throw new BufferUnderflowException(); } source.limit(limit); try { ByteBuffer result = source.slice(); result.order(source.order()); source.position(limit); return result; } finally { source.limit(originalLimit); } } public static String toHex(byte[] value) { StringBuilder sb = new StringBuilder(value.length * 2); int len = value.length; for (int i = 0; i < len; i++) { int hi = (value[i] & 0xff) >>> 4; int lo = value[i] & 0x0f; sb.append(HEX_DIGITS[hi]).append(HEX_DIGITS[lo]); } return sb.toString(); } public static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws ApkFormatException { if (source.remaining() < 4) { throw new ApkFormatException( "Remaining buffer too short to contain length of length-prefixed field" + ". Remaining: " + source.remaining()); } int len = source.getInt(); if (len < 0) { throw new IllegalArgumentException("Negative length"); } else if (len > source.remaining()) { throw new ApkFormatException( "Length-prefixed field longer than remaining buffer" + ". Field length: " + len + ", remaining: " + source.remaining()); } return getByteBuffer(source, len); } public static byte[] readLengthPrefixedByteArray(ByteBuffer buf) throws ApkFormatException { int len = buf.getInt(); if (len < 0) { throw new ApkFormatException("Negative length"); } else if (len > buf.remaining()) { throw new ApkFormatException( "Underflow while reading length-prefixed value. Length: " + len + ", available: " + buf.remaining()); } byte[] result = new byte[len]; buf.get(result); return result; } public static byte[] encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( List> sequence) { int resultSize = 0; for (Pair element : sequence) { resultSize += 12 + element.getSecond().length; } ByteBuffer result = ByteBuffer.allocate(resultSize); result.order(ByteOrder.LITTLE_ENDIAN); for (Pair element : sequence) { byte[] second = element.getSecond(); result.putInt(8 + second.length); result.putInt(element.getFirst()); result.putInt(second.length); result.put(second); } return result.array(); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_ApkSupportedSignature.java0100644 0000000 0000000 00000000034 14763776540 030771 xustar000000000 0000000 28 mtime=1741684064.5720000 src/main/java/com/android/apksig/internal/apk/ApkSupportedSignature.java0100644 0000000 0000000 00000002174 14763776540 025501 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; /** * Base implementation of a supported signature for an APK. */ public class ApkSupportedSignature { public final SignatureAlgorithm algorithm; public final byte[] signature; /** * Constructs a new supported signature using the provided {@code algorithm} and {@code * signature} bytes. */ public ApkSupportedSignature(SignatureAlgorithm algorithm, byte[] signature) { this.algorithm = algorithm; this.signature = signature; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_ContentDigestAlgorithm.java0100644 0000000 0000000 00000000034 14763776540 031107 xustar000000000 0000000 28 mtime=1741684064.5730000 src/main/java/com/android/apksig/internal/apk/ContentDigestAlgorithm.java0100644 0000000 0000000 00000003775 14763776540 025627 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; /** APK Signature Scheme v2 content digest algorithm. */ public enum ContentDigestAlgorithm { /** SHA2-256 over 1 MB chunks. */ CHUNKED_SHA256(1, "SHA-256", 256 / 8), /** SHA2-512 over 1 MB chunks. */ CHUNKED_SHA512(2, "SHA-512", 512 / 8), /** SHA2-256 over 4 KB chunks for APK verity. */ VERITY_CHUNKED_SHA256(3, "SHA-256", 256 / 8), /** Non-chunk SHA2-256. */ SHA256(4, "SHA-256", 256 / 8); private final int mId; private final String mJcaMessageDigestAlgorithm; private final int mChunkDigestOutputSizeBytes; private ContentDigestAlgorithm( int id, String jcaMessageDigestAlgorithm, int chunkDigestOutputSizeBytes) { mId = id; mJcaMessageDigestAlgorithm = jcaMessageDigestAlgorithm; mChunkDigestOutputSizeBytes = chunkDigestOutputSizeBytes; } /** Returns the ID of the digest algorithm used on the APK. */ public int getId() { return mId; } /** * Returns the {@link java.security.MessageDigest} algorithm used for computing digests of * chunks by this content digest algorithm. */ String getJcaMessageDigestAlgorithm() { return mJcaMessageDigestAlgorithm; } /** Returns the size (in bytes) of the digest of a chunk of content. */ int getChunkDigestOutputSizeBytes() { return mChunkDigestOutputSizeBytes; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_NoApkSupportedSignaturesException.java0100644 0000000 0000000 00000000034 14763776540 033330 xustar000000000 0000000 28 mtime=1741684064.5730000 src/main/java/com/android/apksig/internal/apk/NoApkSupportedSignaturesException.java0100644 0000000 0000000 00000001676 14763776540 030046 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; /** * Base exception that is thrown when there are no signatures that support the full range of * requested platform versions. */ public class NoApkSupportedSignaturesException extends Exception { public NoApkSupportedSignaturesException(String message) { super(message); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_SignatureAlgorithm.java0100644 0000000 0000000 00000000034 14763776540 030276 xustar000000000 0000000 28 mtime=1741684064.5730000 src/main/java/com/android/apksig/internal/apk/SignatureAlgorithm.java0100644 0000000 0000000 00000017446 14763776540 025016 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.Pair; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.MGF1ParameterSpec; import java.security.spec.PSSParameterSpec; /** * APK Signing Block signature algorithm. */ public enum SignatureAlgorithm { // TODO reserve the 0x0000 ID to mean null /** * RSASSA-PSS with SHA2-256 digest, SHA2-256 MGF1, 32 bytes of salt, trailer: 0xbc, content * digested using SHA2-256 in 1 MB chunks. */ RSA_PSS_WITH_SHA256( 0x0101, ContentDigestAlgorithm.CHUNKED_SHA256, "RSA", Pair.of("SHA256withRSA/PSS", new PSSParameterSpec( "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 256 / 8, 1)), AndroidSdkVersion.N, AndroidSdkVersion.M), /** * RSASSA-PSS with SHA2-512 digest, SHA2-512 MGF1, 64 bytes of salt, trailer: 0xbc, content * digested using SHA2-512 in 1 MB chunks. */ RSA_PSS_WITH_SHA512( 0x0102, ContentDigestAlgorithm.CHUNKED_SHA512, "RSA", Pair.of( "SHA512withRSA/PSS", new PSSParameterSpec( "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1)), AndroidSdkVersion.N, AndroidSdkVersion.M), /** RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. */ RSA_PKCS1_V1_5_WITH_SHA256( 0x0103, ContentDigestAlgorithm.CHUNKED_SHA256, "RSA", Pair.of("SHA256withRSA", null), AndroidSdkVersion.N, AndroidSdkVersion.INITIAL_RELEASE), /** RSASSA-PKCS1-v1_5 with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks. */ RSA_PKCS1_V1_5_WITH_SHA512( 0x0104, ContentDigestAlgorithm.CHUNKED_SHA512, "RSA", Pair.of("SHA512withRSA", null), AndroidSdkVersion.N, AndroidSdkVersion.INITIAL_RELEASE), /** ECDSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. */ ECDSA_WITH_SHA256( 0x0201, ContentDigestAlgorithm.CHUNKED_SHA256, "EC", Pair.of("SHA256withECDSA", null), AndroidSdkVersion.N, AndroidSdkVersion.HONEYCOMB), /** ECDSA with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks. */ ECDSA_WITH_SHA512( 0x0202, ContentDigestAlgorithm.CHUNKED_SHA512, "EC", Pair.of("SHA512withECDSA", null), AndroidSdkVersion.N, AndroidSdkVersion.HONEYCOMB), /** DSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. */ DSA_WITH_SHA256( 0x0301, ContentDigestAlgorithm.CHUNKED_SHA256, "DSA", Pair.of("SHA256withDSA", null), AndroidSdkVersion.N, AndroidSdkVersion.INITIAL_RELEASE), /** * DSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. Signing is done * deterministically according to RFC 6979. */ DETDSA_WITH_SHA256( 0x0301, ContentDigestAlgorithm.CHUNKED_SHA256, "DSA", Pair.of("SHA256withDetDSA", null), AndroidSdkVersion.N, AndroidSdkVersion.INITIAL_RELEASE), /** * RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 4 KB chunks, in * the same way fsverity operates. This digest and the content length (before digestion, 8 bytes * in little endian) construct the final digest. */ VERITY_RSA_PKCS1_V1_5_WITH_SHA256( 0x0421, ContentDigestAlgorithm.VERITY_CHUNKED_SHA256, "RSA", Pair.of("SHA256withRSA", null), AndroidSdkVersion.P, AndroidSdkVersion.INITIAL_RELEASE), /** * ECDSA with SHA2-256 digest, content digested using SHA2-256 in 4 KB chunks, in the same way * fsverity operates. This digest and the content length (before digestion, 8 bytes in little * endian) construct the final digest. */ VERITY_ECDSA_WITH_SHA256( 0x0423, ContentDigestAlgorithm.VERITY_CHUNKED_SHA256, "EC", Pair.of("SHA256withECDSA", null), AndroidSdkVersion.P, AndroidSdkVersion.HONEYCOMB), /** * DSA with SHA2-256 digest, content digested using SHA2-256 in 4 KB chunks, in the same way * fsverity operates. This digest and the content length (before digestion, 8 bytes in little * endian) construct the final digest. */ VERITY_DSA_WITH_SHA256( 0x0425, ContentDigestAlgorithm.VERITY_CHUNKED_SHA256, "DSA", Pair.of("SHA256withDSA", null), AndroidSdkVersion.P, AndroidSdkVersion.INITIAL_RELEASE); private final int mId; private final String mJcaKeyAlgorithm; private final ContentDigestAlgorithm mContentDigestAlgorithm; private final Pair mJcaSignatureAlgAndParams; private final int mMinSdkVersion; private final int mJcaSigAlgMinSdkVersion; SignatureAlgorithm(int id, ContentDigestAlgorithm contentDigestAlgorithm, String jcaKeyAlgorithm, Pair jcaSignatureAlgAndParams, int minSdkVersion, int jcaSigAlgMinSdkVersion) { mId = id; mContentDigestAlgorithm = contentDigestAlgorithm; mJcaKeyAlgorithm = jcaKeyAlgorithm; mJcaSignatureAlgAndParams = jcaSignatureAlgAndParams; mMinSdkVersion = minSdkVersion; mJcaSigAlgMinSdkVersion = jcaSigAlgMinSdkVersion; } /** * Returns the ID of this signature algorithm as used in APK Signature Scheme v2 wire format. */ public int getId() { return mId; } /** * Returns the content digest algorithm associated with this signature algorithm. */ public ContentDigestAlgorithm getContentDigestAlgorithm() { return mContentDigestAlgorithm; } /** * Returns the JCA {@link java.security.Key} algorithm used by this signature scheme. */ public String getJcaKeyAlgorithm() { return mJcaKeyAlgorithm; } /** * Returns the {@link java.security.Signature} algorithm and the {@link AlgorithmParameterSpec} * (or null if not needed) to parameterize the {@code Signature}. */ public Pair getJcaSignatureAlgorithmAndParams() { return mJcaSignatureAlgAndParams; } public int getMinSdkVersion() { return mMinSdkVersion; } /** * Returns the minimum SDK version that supports the JCA signature algorithm. */ public int getJcaSigAlgMinSdkVersion() { return mJcaSigAlgMinSdkVersion; } public static SignatureAlgorithm findById(int id) { for (SignatureAlgorithm alg : SignatureAlgorithm.values()) { if (alg.getId() == id) { return alg; } } return null; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_SignatureInfo.java0100644 0000000 0000000 00000000034 14763776540 027243 xustar000000000 0000000 28 mtime=1741684064.5730000 src/main/java/com/android/apksig/internal/apk/SignatureInfo.java0100644 0000000 0000000 00000003430 14763776540 023747 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; import java.nio.ByteBuffer; /** * APK Signature Scheme block and additional information relevant to verifying the signatures * contained in the block against the file. */ public class SignatureInfo { /** Contents of APK Signature Scheme block. */ public final ByteBuffer signatureBlock; /** Position of the APK Signing Block in the file. */ public final long apkSigningBlockOffset; /** Position of the ZIP Central Directory in the file. */ public final long centralDirOffset; /** Position of the ZIP End of Central Directory (EoCD) in the file. */ public final long eocdOffset; /** Contents of ZIP End of Central Directory (EoCD) of the file. */ public final ByteBuffer eocd; public SignatureInfo( ByteBuffer signatureBlock, long apkSigningBlockOffset, long centralDirOffset, long eocdOffset, ByteBuffer eocd) { this.signatureBlock = signatureBlock; this.apkSigningBlockOffset = apkSigningBlockOffset; this.centralDirOffset = centralDirOffset; this.eocdOffset = eocdOffset; this.eocd = eocd; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_SignatureNotFoundException.java0100644 0000000 0000000 00000000034 14763776540 031763 xustar000000000 0000000 28 mtime=1741684064.5730000 src/main/java/com/android/apksig/internal/apk/SignatureNotFoundException.java0100644 0000000 0000000 00000002002 14763776540 026461 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk; /** * Base exception that is thrown when the APK is not signed with the requested signature scheme. */ public class SignatureNotFoundException extends Exception { public SignatureNotFoundException(String message) { super(message); } public SignatureNotFoundException(String message, Throwable cause) { super(message, cause); } }./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_stamp_0100644 0000000 0000000 00000000034 14763776540 025031 xustar000000000 0000000 28 mtime=1741684064.5730000 src/main/java/com/android/apksig/internal/apk/stamp/0040755 0000000 0000000 00000000000 14763776540 021456 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_stamp_SourceStampCertificateLineage.ja0100644 0000000 0000000 00000000034 14763776540 033240 xustar000000000 0000000 28 mtime=1741684064.5730000 src/main/java/com/android/apksig/internal/apk/stamp/SourceStampCertificateLineage.java0100644 0000000 0000000 00000025116 14763776540 030220 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.stamp; import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.getLengthPrefixedSlice; import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.readLengthPrefixedByteArray; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.internal.apk.ApkSigningBlockUtilsLite; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; /** Lightweight version of the V3SigningCertificateLineage to be used for source stamps. */ public class SourceStampCertificateLineage { private final static int FIRST_VERSION = 1; private final static int CURRENT_VERSION = FIRST_VERSION; /** * Deserializes the binary representation of a SourceStampCertificateLineage. Also * verifies that the structure is well-formed, e.g. that the signature for each node is from its * parent. */ public static List readSigningCertificateLineage(ByteBuffer inputBytes) throws IOException { List result = new ArrayList<>(); int nodeCount = 0; if (inputBytes == null || !inputBytes.hasRemaining()) { return null; } ApkSigningBlockUtilsLite.checkByteOrderLittleEndian(inputBytes); CertificateFactory certFactory; try { certFactory = CertificateFactory.getInstance("X.509"); } catch (CertificateException e) { throw new IllegalStateException("Failed to obtain X.509 CertificateFactory", e); } // FORMAT (little endian): // * uint32: version code // * sequence of length-prefixed (uint32): nodes // * length-prefixed bytes: signed data // * length-prefixed bytes: certificate // * uint32: signature algorithm id // * uint32: flags // * uint32: signature algorithm id (used by to sign next cert in lineage) // * length-prefixed bytes: signature over above signed data X509Certificate lastCert = null; int lastSigAlgorithmId = 0; try { int version = inputBytes.getInt(); if (version != CURRENT_VERSION) { // we only have one version to worry about right now, so just check it throw new IllegalArgumentException("Encoded SigningCertificateLineage has a version" + " different than any of which we are aware"); } HashSet certHistorySet = new HashSet<>(); while (inputBytes.hasRemaining()) { nodeCount++; ByteBuffer nodeBytes = getLengthPrefixedSlice(inputBytes); ByteBuffer signedData = getLengthPrefixedSlice(nodeBytes); int flags = nodeBytes.getInt(); int sigAlgorithmId = nodeBytes.getInt(); SignatureAlgorithm sigAlgorithm = SignatureAlgorithm.findById(lastSigAlgorithmId); byte[] signature = readLengthPrefixedByteArray(nodeBytes); if (lastCert != null) { // Use previous level cert to verify current level String jcaSignatureAlgorithm = sigAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst(); AlgorithmParameterSpec jcaSignatureAlgorithmParams = sigAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond(); PublicKey publicKey = lastCert.getPublicKey(); Signature sig = Signature.getInstance(jcaSignatureAlgorithm); sig.initVerify(publicKey); if (jcaSignatureAlgorithmParams != null) { sig.setParameter(jcaSignatureAlgorithmParams); } sig.update(signedData); if (!sig.verify(signature)) { throw new SecurityException("Unable to verify signature of certificate #" + nodeCount + " using " + jcaSignatureAlgorithm + " when verifying" + " SourceStampCertificateLineage object"); } } signedData.rewind(); byte[] encodedCert = readLengthPrefixedByteArray(signedData); int signedSigAlgorithm = signedData.getInt(); if (lastCert != null && lastSigAlgorithmId != signedSigAlgorithm) { throw new SecurityException("Signing algorithm ID mismatch for certificate #" + nodeBytes + " when verifying SourceStampCertificateLineage object"); } lastCert = (X509Certificate) certFactory.generateCertificate( new ByteArrayInputStream(encodedCert)); lastCert = new GuaranteedEncodedFormX509Certificate(lastCert, encodedCert); if (certHistorySet.contains(lastCert)) { throw new SecurityException("Encountered duplicate entries in " + "SigningCertificateLineage at certificate #" + nodeCount + ". All " + "signing certificates should be unique"); } certHistorySet.add(lastCert); lastSigAlgorithmId = sigAlgorithmId; result.add(new SigningCertificateNode( lastCert, SignatureAlgorithm.findById(signedSigAlgorithm), SignatureAlgorithm.findById(sigAlgorithmId), signature, flags)); } } catch(ApkFormatException | BufferUnderflowException e){ throw new IOException("Failed to parse SourceStampCertificateLineage object", e); } catch(NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e){ throw new SecurityException( "Failed to verify signature over signed data for certificate #" + nodeCount + " when parsing SourceStampCertificateLineage object", e); } catch(CertificateException e){ throw new SecurityException("Failed to decode certificate #" + nodeCount + " when parsing SourceStampCertificateLineage object", e); } return result; } /** * Represents one signing certificate in the SourceStampCertificateLineage, which * generally means it is/was used at some point to sign source stamps. */ public static class SigningCertificateNode { public SigningCertificateNode( X509Certificate signingCert, SignatureAlgorithm parentSigAlgorithm, SignatureAlgorithm sigAlgorithm, byte[] signature, int flags) { this.signingCert = signingCert; this.parentSigAlgorithm = parentSigAlgorithm; this.sigAlgorithm = sigAlgorithm; this.signature = signature; this.flags = flags; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof SigningCertificateNode)) return false; SigningCertificateNode that = (SigningCertificateNode) o; if (!signingCert.equals(that.signingCert)) return false; if (parentSigAlgorithm != that.parentSigAlgorithm) return false; if (sigAlgorithm != that.sigAlgorithm) return false; if (!Arrays.equals(signature, that.signature)) return false; if (flags != that.flags) return false; // we made it return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((signingCert == null) ? 0 : signingCert.hashCode()); result = prime * result + ((parentSigAlgorithm == null) ? 0 : parentSigAlgorithm.hashCode()); result = prime * result + ((sigAlgorithm == null) ? 0 : sigAlgorithm.hashCode()); result = prime * result + Arrays.hashCode(signature); result = prime * result + flags; return result; } /** * the signing cert for this node. This is part of the data signed by the parent node. */ public final X509Certificate signingCert; /** * the algorithm used by this node's parent to bless this data. Its ID value is part of * the data signed by the parent node. {@code null} for first node. */ public final SignatureAlgorithm parentSigAlgorithm; /** * the algorithm used by this node to bless the next node's data. Its ID value is part * of the signed data of the next node. {@code null} for the last node. */ public SignatureAlgorithm sigAlgorithm; /** * signature over the signed data (above). The signature is from this node's parent * signing certificate, which should correspond to the signing certificate used to sign an * APK before rotating to this one, and is formed using {@code signatureAlgorithm}. */ public final byte[] signature; /** * the flags detailing how the platform should treat this signing cert */ public int flags; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_stamp_SourceStampConstants.java0100644 0000000 0000000 00000000034 14763776540 032034 xustar000000000 0000000 28 mtime=1741684064.5740000 src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java0100644 0000000 0000000 00000002741 14763776540 026464 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.stamp; /** Constants used for source stamp signing and verification. */ public class SourceStampConstants { private SourceStampConstants() {} public static final int V1_SOURCE_STAMP_BLOCK_ID = 0x2b09189e; public static final int V2_SOURCE_STAMP_BLOCK_ID = 0x6dff800d; public static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = "stamp-cert-sha256"; public static final int PROOF_OF_ROTATION_ATTR_ID = 0x9d6303f7; /** * The source stamp timestamp attribute value is an 8-byte little-endian encoded long * representing the epoch time in seconds when the stamp block was signed. The first 8 bytes * of the attribute value buffer will be used to read the timestamp, and any additional buffer * space will be ignored. */ public static final int STAMP_TIME_ATTR_ID = 0xe43c5946; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_stamp_SourceStampVerifier.java0100644 0000000 0000000 00000000034 14763776540 031633 xustar000000000 0000000 28 mtime=1741684064.5740000 src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java0100644 0000000 0000000 00000041505 14763776540 026264 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.stamp; import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.getLengthPrefixedSlice; import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.getSignaturesToVerify; import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.readLengthPrefixedByteArray; import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.toHex; import com.android.apksig.ApkVerificationIssue; import com.android.apksig.Constants; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.internal.apk.ApkSignerInfo; import com.android.apksig.internal.apk.ApkSupportedSignature; import com.android.apksig.internal.apk.NoApkSupportedSignaturesException; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.util.ByteBufferUtils; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import java.io.ByteArrayInputStream; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Source Stamp verifier. * *

SourceStamp improves traceability of apps with respect to unauthorized distribution. * *

The stamp is part of the APK that is protected by the signing block. * *

The APK contents hash is signed using the stamp key, and is saved as part of the signing * block. */ class SourceStampVerifier { /** Hidden constructor to prevent instantiation. */ private SourceStampVerifier() { } /** * Parses the SourceStamp block and populates the {@code result}. * *

This verifies signatures over digest provided. * *

This method adds one or more errors to the {@code result} if a verification error is * expected to be encountered on an Android platform version in the {@code [minSdkVersion, * maxSdkVersion]} range. */ public static void verifyV1SourceStamp( ByteBuffer sourceStampBlockData, CertificateFactory certFactory, ApkSignerInfo result, byte[] apkDigest, byte[] sourceStampCertificateDigest, int minSdkVersion, int maxSdkVersion) throws ApkFormatException, NoSuchAlgorithmException { X509Certificate sourceStampCertificate = verifySourceStampCertificate( sourceStampBlockData, certFactory, sourceStampCertificateDigest, result); if (result.containsWarnings() || result.containsErrors()) { return; } ByteBuffer apkDigestSignatures = getLengthPrefixedSlice(sourceStampBlockData); verifySourceStampSignature( apkDigest, minSdkVersion, maxSdkVersion, sourceStampCertificate, apkDigestSignatures, result); } /** * Parses the SourceStamp block and populates the {@code result}. * *

This verifies signatures over digest of multiple signature schemes provided. * *

This method adds one or more errors to the {@code result} if a verification error is * expected to be encountered on an Android platform version in the {@code [minSdkVersion, * maxSdkVersion]} range. */ public static void verifyV2SourceStamp( ByteBuffer sourceStampBlockData, CertificateFactory certFactory, ApkSignerInfo result, Map signatureSchemeApkDigests, byte[] sourceStampCertificateDigest, int minSdkVersion, int maxSdkVersion) throws ApkFormatException, NoSuchAlgorithmException { X509Certificate sourceStampCertificate = verifySourceStampCertificate( sourceStampBlockData, certFactory, sourceStampCertificateDigest, result); if (result.containsWarnings() || result.containsErrors()) { return; } // Parse signed signature schemes block. ByteBuffer signedSignatureSchemes = getLengthPrefixedSlice(sourceStampBlockData); Map signedSignatureSchemeData = new HashMap<>(); while (signedSignatureSchemes.hasRemaining()) { ByteBuffer signedSignatureScheme = getLengthPrefixedSlice(signedSignatureSchemes); int signatureSchemeId = signedSignatureScheme.getInt(); ByteBuffer apkDigestSignatures = getLengthPrefixedSlice(signedSignatureScheme); signedSignatureSchemeData.put(signatureSchemeId, apkDigestSignatures); } for (Map.Entry signatureSchemeApkDigest : signatureSchemeApkDigests.entrySet()) { // TODO(b/192301300): Should the new v3.1 be included in the source stamp, or since a // v3.0 block must always be present with a v3.1 block is it sufficient to just use the // v3.0 block? if (signatureSchemeApkDigest.getKey() == Constants.VERSION_APK_SIGNATURE_SCHEME_V31) { continue; } if (!signedSignatureSchemeData.containsKey(signatureSchemeApkDigest.getKey())) { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_NO_SIGNATURE); return; } verifySourceStampSignature( signatureSchemeApkDigest.getValue(), minSdkVersion, maxSdkVersion, sourceStampCertificate, signedSignatureSchemeData.get(signatureSchemeApkDigest.getKey()), result); if (result.containsWarnings() || result.containsErrors()) { return; } } if (sourceStampBlockData.hasRemaining()) { // The stamp block contains some additional attributes. ByteBuffer stampAttributeData = getLengthPrefixedSlice(sourceStampBlockData); ByteBuffer stampAttributeDataSignatures = getLengthPrefixedSlice(sourceStampBlockData); byte[] stampAttributeBytes = new byte[stampAttributeData.remaining()]; stampAttributeData.get(stampAttributeBytes); stampAttributeData.flip(); verifySourceStampSignature(stampAttributeBytes, minSdkVersion, maxSdkVersion, sourceStampCertificate, stampAttributeDataSignatures, result); if (result.containsErrors() || result.containsWarnings()) { return; } parseStampAttributes(stampAttributeData, sourceStampCertificate, result); } } private static X509Certificate verifySourceStampCertificate( ByteBuffer sourceStampBlockData, CertificateFactory certFactory, byte[] sourceStampCertificateDigest, ApkSignerInfo result) throws NoSuchAlgorithmException, ApkFormatException { // Parse the SourceStamp certificate. byte[] sourceStampEncodedCertificate = readLengthPrefixedByteArray(sourceStampBlockData); X509Certificate sourceStampCertificate; try { sourceStampCertificate = (X509Certificate) certFactory.generateCertificate( new ByteArrayInputStream(sourceStampEncodedCertificate)); } catch (CertificateException e) { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_CERTIFICATE, e); return null; } // Wrap the cert so that the result's getEncoded returns exactly the original encoded // form. Without this, getEncoded may return a different form from what was stored in // the signature. This is because some X509Certificate(Factory) implementations // re-encode certificates. sourceStampCertificate = new GuaranteedEncodedFormX509Certificate( sourceStampCertificate, sourceStampEncodedCertificate); result.certs.add(sourceStampCertificate); // Verify the SourceStamp certificate found in the signing block is the same as the // SourceStamp certificate found in the APK. MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(sourceStampEncodedCertificate); byte[] sourceStampBlockCertificateDigest = messageDigest.digest(); if (!Arrays.equals(sourceStampCertificateDigest, sourceStampBlockCertificateDigest)) { result.addWarning( ApkVerificationIssue .SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK, toHex(sourceStampBlockCertificateDigest), toHex(sourceStampCertificateDigest)); return null; } return sourceStampCertificate; } private static void verifySourceStampSignature( byte[] data, int minSdkVersion, int maxSdkVersion, X509Certificate sourceStampCertificate, ByteBuffer signatures, ApkSignerInfo result) { // Parse the signatures block and identify supported signatures int signatureCount = 0; List supportedSignatures = new ArrayList<>(1); while (signatures.hasRemaining()) { signatureCount++; try { ByteBuffer signature = getLengthPrefixedSlice(signatures); int sigAlgorithmId = signature.getInt(); byte[] sigBytes = readLengthPrefixedByteArray(signature); SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId); if (signatureAlgorithm == null) { result.addInfoMessage( ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM, sigAlgorithmId); continue; } supportedSignatures.add( new ApkSupportedSignature(signatureAlgorithm, sigBytes)); } catch (ApkFormatException | BufferUnderflowException e) { result.addWarning( ApkVerificationIssue.SOURCE_STAMP_MALFORMED_SIGNATURE, signatureCount); return; } } if (supportedSignatures.isEmpty()) { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_NO_SIGNATURE); return; } // Verify signatures over digests using the SourceStamp's certificate. List signaturesToVerify; try { signaturesToVerify = getSignaturesToVerify( supportedSignatures, minSdkVersion, maxSdkVersion, true); } catch (NoApkSupportedSignaturesException e) { // To facilitate debugging capture the signature algorithms and resulting exception in // the warning. StringBuilder signatureAlgorithms = new StringBuilder(); for (ApkSupportedSignature supportedSignature : supportedSignatures) { if (signatureAlgorithms.length() > 0) { signatureAlgorithms.append(", "); } signatureAlgorithms.append(supportedSignature.algorithm); } result.addWarning(ApkVerificationIssue.SOURCE_STAMP_NO_SUPPORTED_SIGNATURE, signatureAlgorithms.toString(), e); return; } for (ApkSupportedSignature signature : signaturesToVerify) { SignatureAlgorithm signatureAlgorithm = signature.algorithm; String jcaSignatureAlgorithm = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst(); AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond(); PublicKey publicKey = sourceStampCertificate.getPublicKey(); try { Signature sig = Signature.getInstance(jcaSignatureAlgorithm); sig.initVerify(publicKey); if (jcaSignatureAlgorithmParams != null) { sig.setParameter(jcaSignatureAlgorithmParams); } sig.update(data); byte[] sigBytes = signature.signature; if (!sig.verify(sigBytes)) { result.addWarning( ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY, signatureAlgorithm); return; } } catch (InvalidKeyException | InvalidAlgorithmParameterException | SignatureException | NoSuchAlgorithmException e) { result.addWarning( ApkVerificationIssue.SOURCE_STAMP_VERIFY_EXCEPTION, signatureAlgorithm, e); return; } } } private static void parseStampAttributes(ByteBuffer stampAttributeData, X509Certificate sourceStampCertificate, ApkSignerInfo result) throws ApkFormatException { ByteBuffer stampAttributes = getLengthPrefixedSlice(stampAttributeData); int stampAttributeCount = 0; while (stampAttributes.hasRemaining()) { stampAttributeCount++; try { ByteBuffer attribute = getLengthPrefixedSlice(stampAttributes); int id = attribute.getInt(); byte[] value = ByteBufferUtils.toByteArray(attribute); if (id == SourceStampConstants.PROOF_OF_ROTATION_ATTR_ID) { readStampCertificateLineage(value, sourceStampCertificate, result); } else if (id == SourceStampConstants.STAMP_TIME_ATTR_ID) { long timestamp = ByteBuffer.wrap(value).order( ByteOrder.LITTLE_ENDIAN).getLong(); if (timestamp > 0) { result.timestamp = timestamp; } else { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP, timestamp); } } else { result.addInfoMessage(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE, id); } } catch (ApkFormatException | BufferUnderflowException e) { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_ATTRIBUTE, stampAttributeCount); return; } } } private static void readStampCertificateLineage(byte[] lineageBytes, X509Certificate sourceStampCertificate, ApkSignerInfo result) { try { // SourceStampCertificateLineage is verified when built List nodes = SourceStampCertificateLineage.readSigningCertificateLineage( ByteBuffer.wrap(lineageBytes).order(ByteOrder.LITTLE_ENDIAN)); for (int i = 0; i < nodes.size(); i++) { result.certificateLineage.add(nodes.get(i).signingCert); } // Make sure that the last cert in the chain matches this signer cert if (!sourceStampCertificate.equals( result.certificateLineage.get(result.certificateLineage.size() - 1))) { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_POR_CERT_MISMATCH); } } catch (SecurityException e) { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_POR_DID_NOT_VERIFY); } catch (IllegalArgumentException e) { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_POR_CERT_MISMATCH); } catch (Exception e) { result.addWarning(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_LINEAGE); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_stamp_V1SourceStampSigner.java0100644 0000000 0000000 00000000034 14763776540 031516 xustar000000000 0000000 28 mtime=1741684064.5750000 src/main/java/com/android/apksig/internal/apk/stamp/V1SourceStampSigner.java0100644 0000000 0000000 00000011104 14763776540 026137 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.stamp; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsLengthPrefixedElement; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedElements; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignerConfig; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.util.Pair; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; /** * SourceStamp signer. * *

SourceStamp improves traceability of apps with respect to unauthorized distribution. * *

The stamp is part of the APK that is protected by the signing block. * *

The APK contents hash is signed using the stamp key, and is saved as part of the signing * block. * *

V1 of the source stamp allows signing the digest of at most one signature scheme only. */ public abstract class V1SourceStampSigner { public static final int V1_SOURCE_STAMP_BLOCK_ID = SourceStampConstants.V1_SOURCE_STAMP_BLOCK_ID; /** Hidden constructor to prevent instantiation. */ private V1SourceStampSigner() {} public static Pair generateSourceStampBlock( SignerConfig sourceStampSignerConfig, Map digestInfo) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { if (sourceStampSignerConfig.certificates.isEmpty()) { throw new SignatureException("No certificates configured for signer"); } List> digests = new ArrayList<>(); for (Map.Entry digest : digestInfo.entrySet()) { digests.add(Pair.of(digest.getKey().getId(), digest.getValue())); } Collections.sort(digests, Comparator.comparing(Pair::getFirst)); SourceStampBlock sourceStampBlock = new SourceStampBlock(); try { sourceStampBlock.stampCertificate = sourceStampSignerConfig.certificates.get(0).getEncoded(); } catch (CertificateEncodingException e) { throw new SignatureException( "Retrieving the encoded form of the stamp certificate failed", e); } byte[] digestBytes = encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(digests); sourceStampBlock.signedDigests = ApkSigningBlockUtils.generateSignaturesOverData( sourceStampSignerConfig, digestBytes); // FORMAT: // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded) // * length-prefixed sequence of length-prefixed signatures: // * uint32: signature algorithm ID // * length-prefixed bytes: signature of signed data byte[] sourceStampSignerBlock = encodeAsSequenceOfLengthPrefixedElements( new byte[][] { sourceStampBlock.stampCertificate, encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( sourceStampBlock.signedDigests), }); // FORMAT: // * length-prefixed stamp block. return Pair.of(encodeAsLengthPrefixedElement(sourceStampSignerBlock), SourceStampConstants.V1_SOURCE_STAMP_BLOCK_ID); } private static final class SourceStampBlock { public byte[] stampCertificate; public List> signedDigests; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_stamp_V1SourceStampVerifier.java0100644 0000000 0000000 00000000034 14763776540 032042 xustar000000000 0000000 28 mtime=1741684064.5760000 src/main/java/com/android/apksig/internal/apk/stamp/V1SourceStampVerifier.java0100644 0000000 0000000 00000014037 14763776540 026473 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.stamp; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes; import static com.android.apksig.internal.apk.stamp.SourceStampConstants.V1_SOURCE_STAMP_BLOCK_ID; import com.android.apksig.ApkVerifier; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.util.Pair; import com.android.apksig.util.DataSource; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; /** * Source Stamp verifier. * *

V1 of the source stamp verifies the stamp signature of at most one signature scheme. */ public abstract class V1SourceStampVerifier { /** Hidden constructor to prevent instantiation. */ private V1SourceStampVerifier() {} /** * Verifies the provided APK's SourceStamp signatures and returns the result of verification. * The APK must be considered verified only if {@link ApkSigningBlockUtils.Result#verified} is * {@code true}. If verification fails, the result will contain errors -- see {@link * ApkSigningBlockUtils.Result#getErrors()}. * * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a * required cryptographic algorithm implementation is missing * @throws ApkSigningBlockUtils.SignatureNotFoundException if no SourceStamp signatures are * found * @throws IOException if an I/O error occurs when reading the APK */ public static ApkSigningBlockUtils.Result verify( DataSource apk, ApkUtils.ZipSections zipSections, byte[] sourceStampCertificateDigest, Map apkContentDigests, int minSdkVersion, int maxSdkVersion) throws IOException, NoSuchAlgorithmException, ApkSigningBlockUtils.SignatureNotFoundException { ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(ApkSigningBlockUtils.VERSION_SOURCE_STAMP); SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature( apk, zipSections, V1_SOURCE_STAMP_BLOCK_ID, result); verify( signatureInfo.signatureBlock, sourceStampCertificateDigest, apkContentDigests, minSdkVersion, maxSdkVersion, result); return result; } /** * Verifies the provided APK's SourceStamp signatures and outputs the results into the provided * {@code result}. APK is considered verified only if there are no errors reported in the {@code * result}. See {@link #verify(DataSource, ApkUtils.ZipSections, byte[], Map, int, int)} for * more information about the contract of this method. */ private static void verify( ByteBuffer sourceStampBlock, byte[] sourceStampCertificateDigest, Map apkContentDigests, int minSdkVersion, int maxSdkVersion, ApkSigningBlockUtils.Result result) throws NoSuchAlgorithmException { ApkSigningBlockUtils.Result.SignerInfo signerInfo = new ApkSigningBlockUtils.Result.SignerInfo(); result.signers.add(signerInfo); try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); ByteBuffer sourceStampBlockData = ApkSigningBlockUtils.getLengthPrefixedSlice(sourceStampBlock); byte[] digestBytes = encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( getApkDigests(apkContentDigests)); SourceStampVerifier.verifyV1SourceStamp( sourceStampBlockData, certFactory, signerInfo, digestBytes, sourceStampCertificateDigest, minSdkVersion, maxSdkVersion); result.verified = !result.containsErrors() && !result.containsWarnings(); } catch (CertificateException e) { throw new IllegalStateException("Failed to obtain X.509 CertificateFactory", e); } catch (ApkFormatException | BufferUnderflowException e) { signerInfo.addWarning(ApkVerifier.Issue.SOURCE_STAMP_MALFORMED_SIGNATURE); } } private static List> getApkDigests( Map apkContentDigests) { List> digests = new ArrayList<>(); for (Map.Entry apkContentDigest : apkContentDigests.entrySet()) { digests.add(Pair.of(apkContentDigest.getKey().getId(), apkContentDigest.getValue())); } Collections.sort(digests, Comparator.comparing(Pair::getFirst)); return digests; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_stamp_V2SourceStampSigner.java0100644 0000000 0000000 00000000034 14763776540 031517 xustar000000000 0000000 28 mtime=1741684064.5760000 src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java0100644 0000000 0000000 00000031647 14763776540 026156 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.stamp; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsLengthPrefixedElement; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedElements; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes; import com.android.apksig.SigningCertificateLineage; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignerConfig; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.util.Pair; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * SourceStamp signer. * *

SourceStamp improves traceability of apps with respect to unauthorized distribution. * *

The stamp is part of the APK that is protected by the signing block. * *

The APK contents hash is signed using the stamp key, and is saved as part of the signing * block. * *

V2 of the source stamp allows signing the digests of more than one signature schemes. */ public class V2SourceStampSigner { public static final int V2_SOURCE_STAMP_BLOCK_ID = SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID; private final SignerConfig mSourceStampSignerConfig; private final Map> mSignatureSchemeDigestInfos; private final boolean mSourceStampTimestampEnabled; /** Hidden constructor to prevent instantiation. */ private V2SourceStampSigner(Builder builder) { mSourceStampSignerConfig = builder.mSourceStampSignerConfig; mSignatureSchemeDigestInfos = builder.mSignatureSchemeDigestInfos; mSourceStampTimestampEnabled = builder.mSourceStampTimestampEnabled; } public static Pair generateSourceStampBlock( SignerConfig sourceStampSignerConfig, Map> signatureSchemeDigestInfos) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { return new Builder(sourceStampSignerConfig, signatureSchemeDigestInfos).build().generateSourceStampBlock(); } public Pair generateSourceStampBlock() throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { if (mSourceStampSignerConfig.certificates.isEmpty()) { throw new SignatureException("No certificates configured for signer"); } // Extract the digests for signature schemes. List> signatureSchemeDigests = new ArrayList<>(); getSignedDigestsFor( VERSION_APK_SIGNATURE_SCHEME_V3, mSignatureSchemeDigestInfos, mSourceStampSignerConfig, signatureSchemeDigests); getSignedDigestsFor( VERSION_APK_SIGNATURE_SCHEME_V2, mSignatureSchemeDigestInfos, mSourceStampSignerConfig, signatureSchemeDigests); getSignedDigestsFor( VERSION_JAR_SIGNATURE_SCHEME, mSignatureSchemeDigestInfos, mSourceStampSignerConfig, signatureSchemeDigests); Collections.sort(signatureSchemeDigests, Comparator.comparing(Pair::getFirst)); SourceStampBlock sourceStampBlock = new SourceStampBlock(); try { sourceStampBlock.stampCertificate = mSourceStampSignerConfig.certificates.get(0).getEncoded(); } catch (CertificateEncodingException e) { throw new SignatureException( "Retrieving the encoded form of the stamp certificate failed", e); } sourceStampBlock.signedDigests = signatureSchemeDigests; sourceStampBlock.stampAttributes = encodeStampAttributes( generateStampAttributes(mSourceStampSignerConfig.signingCertificateLineage)); sourceStampBlock.signedStampAttributes = ApkSigningBlockUtils.generateSignaturesOverData(mSourceStampSignerConfig, sourceStampBlock.stampAttributes); // FORMAT: // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded) // * length-prefixed sequence of length-prefixed signed signature scheme digests: // * uint32: signature scheme id // * length-prefixed bytes: signed digests for the respective signature scheme // * length-prefixed bytes: encoded stamp attributes // * length-prefixed sequence of length-prefixed signed stamp attributes: // * uint32: signature algorithm id // * length-prefixed bytes: signed stamp attributes for the respective signature algorithm byte[] sourceStampSignerBlock = encodeAsSequenceOfLengthPrefixedElements( new byte[][]{ sourceStampBlock.stampCertificate, encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( sourceStampBlock.signedDigests), sourceStampBlock.stampAttributes, encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( sourceStampBlock.signedStampAttributes), }); // FORMAT: // * length-prefixed stamp block. return Pair.of(encodeAsLengthPrefixedElement(sourceStampSignerBlock), SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID); } private static void getSignedDigestsFor( int signatureSchemeVersion, Map> mSignatureSchemeDigestInfos, SignerConfig mSourceStampSignerConfig, List> signatureSchemeDigests) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { if (!mSignatureSchemeDigestInfos.containsKey(signatureSchemeVersion)) { return; } Map digestInfo = mSignatureSchemeDigestInfos.get(signatureSchemeVersion); List> digests = new ArrayList<>(); for (Map.Entry digest : digestInfo.entrySet()) { digests.add(Pair.of(digest.getKey().getId(), digest.getValue())); } Collections.sort(digests, Comparator.comparing(Pair::getFirst)); // FORMAT: // * length-prefixed sequence of length-prefixed digests: // * uint32: digest algorithm id // * length-prefixed bytes: digest of the respective digest algorithm byte[] digestBytes = encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(digests); // FORMAT: // * length-prefixed sequence of length-prefixed signed digests: // * uint32: signature algorithm id // * length-prefixed bytes: signed digest for the respective signature algorithm List> signedDigest = ApkSigningBlockUtils.generateSignaturesOverData( mSourceStampSignerConfig, digestBytes); // FORMAT: // * length-prefixed sequence of length-prefixed signed signature scheme digests: // * uint32: signature scheme id // * length-prefixed bytes: signed digests for the respective signature scheme signatureSchemeDigests.add( Pair.of( signatureSchemeVersion, encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( signedDigest))); } private static byte[] encodeStampAttributes(Map stampAttributes) { int payloadSize = 0; for (byte[] attributeValue : stampAttributes.values()) { // Pair size + Attribute ID + Attribute value payloadSize += 4 + 4 + attributeValue.length; } // FORMAT (little endian): // * length-prefixed bytes: pair // * uint32: ID // * bytes: value ByteBuffer result = ByteBuffer.allocate(4 + payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.putInt(payloadSize); for (Map.Entry stampAttribute : stampAttributes.entrySet()) { // Pair size result.putInt(4 + stampAttribute.getValue().length); result.putInt(stampAttribute.getKey()); result.put(stampAttribute.getValue()); } return result.array(); } private Map generateStampAttributes(SigningCertificateLineage lineage) { HashMap stampAttributes = new HashMap<>(); if (mSourceStampTimestampEnabled) { // Write the current epoch time as the timestamp for the source stamp. long timestamp = Instant.now().getEpochSecond(); if (timestamp > 0) { ByteBuffer attributeBuffer = ByteBuffer.allocate(8); attributeBuffer.order(ByteOrder.LITTLE_ENDIAN); attributeBuffer.putLong(timestamp); stampAttributes.put(SourceStampConstants.STAMP_TIME_ATTR_ID, attributeBuffer.array()); } else { // The epoch time should never be <= 0, and since security decisions can potentially // be made based on the value in the timestamp, throw an Exception to ensure the // issues with the environment are resolved before allowing the signing. throw new IllegalStateException( "Received an invalid value from Instant#getTimestamp: " + timestamp); } } if (lineage != null) { stampAttributes.put(SourceStampConstants.PROOF_OF_ROTATION_ATTR_ID, lineage.encodeSigningCertificateLineage()); } return stampAttributes; } private static final class SourceStampBlock { public byte[] stampCertificate; public List> signedDigests; // Optional stamp attributes that are not required for verification. public byte[] stampAttributes; public List> signedStampAttributes; } /** Builder of {@link V2SourceStampSigner} instances. */ public static class Builder { private final SignerConfig mSourceStampSignerConfig; private final Map> mSignatureSchemeDigestInfos; private boolean mSourceStampTimestampEnabled = true; /** * Instantiates a new {@code Builder} with the provided {@code sourceStampSignerConfig} * and the {@code signatureSchemeDigestInfos}. */ public Builder(SignerConfig sourceStampSignerConfig, Map> signatureSchemeDigestInfos) { mSourceStampSignerConfig = sourceStampSignerConfig; mSignatureSchemeDigestInfos = signatureSchemeDigestInfos; } /** * Sets whether the source stamp should contain the timestamp attribute with the time * at which the source stamp was signed. */ public Builder setSourceStampTimestampEnabled(boolean value) { mSourceStampTimestampEnabled = value; return this; } /** * Builds a new V2SourceStampSigner that can be used to generate a new source stamp * block signed with the specified signing config. */ public V2SourceStampSigner build() { return new V2SourceStampSigner(this); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_stamp_V2SourceStampVerifier.java0100644 0000000 0000000 00000000034 14763776540 032043 xustar000000000 0000000 28 mtime=1741684064.5760000 src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampVerifier.java0100644 0000000 0000000 00000015650 14763776540 026476 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.stamp; import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes; import static com.android.apksig.internal.apk.stamp.SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID; import com.android.apksig.ApkVerificationIssue; import com.android.apksig.Constants; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.internal.apk.ApkSigResult; import com.android.apksig.internal.apk.ApkSignerInfo; import com.android.apksig.internal.apk.ApkSigningBlockUtilsLite; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.apk.SignatureNotFoundException; import com.android.apksig.internal.util.Pair; import com.android.apksig.util.DataSource; import com.android.apksig.zip.ZipSections; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Source Stamp verifier. * *

V2 of the source stamp verifies the stamp signature of more than one signature schemes. */ public abstract class V2SourceStampVerifier { /** Hidden constructor to prevent instantiation. */ private V2SourceStampVerifier() {} /** * Verifies the provided APK's SourceStamp signatures and returns the result of verification. * The APK must be considered verified only if {@link ApkSigResult#verified} is * {@code true}. If verification fails, the result will contain errors -- see {@link * ApkSigResult#getErrors()}. * * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a * required cryptographic algorithm implementation is missing * @throws SignatureNotFoundException if no SourceStamp signatures are * found * @throws IOException if an I/O error occurs when reading the APK */ public static ApkSigResult verify( DataSource apk, ZipSections zipSections, byte[] sourceStampCertificateDigest, Map> signatureSchemeApkContentDigests, int minSdkVersion, int maxSdkVersion) throws IOException, NoSuchAlgorithmException, SignatureNotFoundException { ApkSigResult result = new ApkSigResult(Constants.VERSION_SOURCE_STAMP); SignatureInfo signatureInfo = ApkSigningBlockUtilsLite.findSignature( apk, zipSections, V2_SOURCE_STAMP_BLOCK_ID); verify( signatureInfo.signatureBlock, sourceStampCertificateDigest, signatureSchemeApkContentDigests, minSdkVersion, maxSdkVersion, result); return result; } /** * Verifies the provided APK's SourceStamp signatures and outputs the results into the provided * {@code result}. APK is considered verified only if there are no errors reported in the {@code * result}. See {@link #verify(DataSource, ZipSections, byte[], Map, int, int)} for * more information about the contract of this method. */ private static void verify( ByteBuffer sourceStampBlock, byte[] sourceStampCertificateDigest, Map> signatureSchemeApkContentDigests, int minSdkVersion, int maxSdkVersion, ApkSigResult result) throws NoSuchAlgorithmException { ApkSignerInfo signerInfo = new ApkSignerInfo(); result.mSigners.add(signerInfo); try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); ByteBuffer sourceStampBlockData = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(sourceStampBlock); SourceStampVerifier.verifyV2SourceStamp( sourceStampBlockData, certFactory, signerInfo, getSignatureSchemeDigests(signatureSchemeApkContentDigests), sourceStampCertificateDigest, minSdkVersion, maxSdkVersion); result.verified = !result.containsErrors() && !result.containsWarnings(); } catch (CertificateException e) { throw new IllegalStateException("Failed to obtain X.509 CertificateFactory", e); } catch (ApkFormatException | BufferUnderflowException e) { signerInfo.addWarning(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_SIGNATURE); } } private static Map getSignatureSchemeDigests( Map> signatureSchemeApkContentDigests) { Map digests = new HashMap<>(); for (Map.Entry> signatureSchemeApkContentDigest : signatureSchemeApkContentDigests.entrySet()) { List> apkDigests = getApkDigests(signatureSchemeApkContentDigest.getValue()); digests.put( signatureSchemeApkContentDigest.getKey(), encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(apkDigests)); } return digests; } private static List> getApkDigests( Map apkContentDigests) { List> digests = new ArrayList<>(); for (Map.Entry apkContentDigest : apkContentDigests.entrySet()) { digests.add(Pair.of(apkContentDigest.getKey().getId(), apkContentDigest.getValue())); } Collections.sort(digests, new Comparator>() { @Override public int compare(Pair pair1, Pair pair2) { return pair1.getFirst() - pair2.getFirst(); } }); return digests; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v1_0100644 0000000 0000000 00000000034 14763776540 024233 xustar000000000 0000000 28 mtime=1741684064.5760000 src/main/java/com/android/apksig/internal/apk/v1/0040755 0000000 0000000 00000000000 14763776540 020660 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v1_DigestAlgorithm.java0100644 0000000 0000000 00000000034 14763776540 030162 xustar000000000 0000000 28 mtime=1741684064.5770000 src/main/java/com/android/apksig/internal/apk/v1/DigestAlgorithm.java0100644 0000000 0000000 00000004455 14763776540 024616 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v1; import java.util.Comparator; /** * Digest algorithm used with JAR signing (aka v1 signing scheme). */ public enum DigestAlgorithm { /** SHA-1 */ SHA1("SHA-1"), /** SHA2-256 */ SHA256("SHA-256"); private final String mJcaMessageDigestAlgorithm; private DigestAlgorithm(String jcaMessageDigestAlgoritm) { mJcaMessageDigestAlgorithm = jcaMessageDigestAlgoritm; } /** * Returns the {@link java.security.MessageDigest} algorithm represented by this digest * algorithm. */ String getJcaMessageDigestAlgorithm() { return mJcaMessageDigestAlgorithm; } public static Comparator BY_STRENGTH_COMPARATOR = new StrengthComparator(); private static class StrengthComparator implements Comparator { @Override public int compare(DigestAlgorithm a1, DigestAlgorithm a2) { switch (a1) { case SHA1: switch (a2) { case SHA1: return 0; case SHA256: return -1; } throw new RuntimeException("Unsupported algorithm: " + a2); case SHA256: switch (a2) { case SHA1: return 1; case SHA256: return 0; } throw new RuntimeException("Unsupported algorithm: " + a2); default: throw new RuntimeException("Unsupported algorithm: " + a1); } } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v1_V1SchemeConstants.java0100644 0000000 0000000 00000000034 14763776540 030404 xustar000000000 0000000 28 mtime=1741684064.5770000 src/main/java/com/android/apksig/internal/apk/v1/V1SchemeConstants.java0100644 0000000 0000000 00000001772 14763776540 025037 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v1; /** Constants used by the Jar Signing / V1 Signature Scheme signing and verification. */ public class V1SchemeConstants { private V1SchemeConstants() {} public static final String MANIFEST_ENTRY_NAME = "META-INF/MANIFEST.MF"; public static final String SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR = "X-Android-APK-Signed"; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v1_V1SchemeSigner.java0100644 0000000 0000000 00000000034 14763776540 027657 xustar000000000 0000000 28 mtime=1741684064.5770000 src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java0100644 0000000 0000000 00000064004 14763776540 024307 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v1; import static com.android.apksig.Constants.MAX_APK_SIGNERS; import static com.android.apksig.Constants.OID_RSA_ENCRYPTION; import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoDigestAlgorithmOid; import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoSignatureAlgorithm; import com.android.apksig.KeyConfig; import com.android.apksig.SignerEngineFactory; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.asn1.Asn1EncodingException; import com.android.apksig.internal.jar.ManifestWriter; import com.android.apksig.internal.jar.SignatureFileWriter; import com.android.apksig.internal.pkcs7.AlgorithmIdentifier; import com.android.apksig.internal.util.Pair; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Base64; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.jar.Attributes; import java.util.jar.Manifest; /** * APK signer which uses JAR signing (aka v1 signing scheme). * * @see Signed JAR File */ public abstract class V1SchemeSigner { public static final String MANIFEST_ENTRY_NAME = V1SchemeConstants.MANIFEST_ENTRY_NAME; private static final Attributes.Name ATTRIBUTE_NAME_CREATED_BY = new Attributes.Name("Created-By"); private static final String ATTRIBUTE_VALUE_MANIFEST_VERSION = "1.0"; private static final String ATTRIBUTE_VALUE_SIGNATURE_VERSION = "1.0"; private static final Attributes.Name SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME = new Attributes.Name(V1SchemeConstants.SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR); /** * Signer configuration. */ public static class SignerConfig { /** Name. */ public String name; /** * Private key. * * @deprecated all internal usage has migrated to use {@link #keyConfig}. This field is not * removed so that compilation is not broken for clients referencing it, but using this * field may lead to unexpected errors. */ public PrivateKey privateKey; /** Signing key configuration */ public KeyConfig keyConfig; /** * Certificates, with the first certificate containing the public key corresponding to * {@link #keyConfig}. */ public List certificates; /** * Digest algorithm used for the signature. */ public DigestAlgorithm signatureDigestAlgorithm; /** * If DSA is the signing algorithm, whether or not deterministic DSA signing should be used. */ public boolean deterministicDsaSigning; } /** Hidden constructor to prevent instantiation. */ private V1SchemeSigner() {} /** * Gets the JAR signing digest algorithm to be used for signing an APK using the provided key. * * @param minSdkVersion minimum API Level of the platform on which the APK may be installed (see * AndroidManifest.xml minSdkVersion attribute) * * @throws InvalidKeyException if the provided key is not suitable for signing APKs using * JAR signing (aka v1 signature scheme) */ public static DigestAlgorithm getSuggestedSignatureDigestAlgorithm( PublicKey signingKey, int minSdkVersion) throws InvalidKeyException { String keyAlgorithm = signingKey.getAlgorithm(); if ("RSA".equalsIgnoreCase(keyAlgorithm) || OID_RSA_ENCRYPTION.equals((keyAlgorithm))) { // Prior to API Level 18, only SHA-1 can be used with RSA. if (minSdkVersion < 18) { return DigestAlgorithm.SHA1; } return DigestAlgorithm.SHA256; } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) { // Prior to API Level 21, only SHA-1 can be used with DSA if (minSdkVersion < 21) { return DigestAlgorithm.SHA1; } else { return DigestAlgorithm.SHA256; } } else if ("EC".equalsIgnoreCase(keyAlgorithm)) { if (minSdkVersion < 18) { throw new InvalidKeyException( "ECDSA signatures only supported for minSdkVersion 18 and higher"); } return DigestAlgorithm.SHA256; } else { throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm); } } /** * Returns a safe version of the provided signer name. */ public static String getSafeSignerName(String name) { if (name.isEmpty()) { throw new IllegalArgumentException("Empty name"); } // According to https://docs.oracle.com/javase/tutorial/deployment/jar/signing.html, the // name must not be longer than 8 characters and may contain only A-Z, 0-9, _, and -. StringBuilder result = new StringBuilder(); char[] nameCharsUpperCase = name.toUpperCase(Locale.US).toCharArray(); for (int i = 0; i < Math.min(nameCharsUpperCase.length, 8); i++) { char c = nameCharsUpperCase[i]; if (((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || (c == '-') || (c == '_')) { result.append(c); } else { result.append('_'); } } return result.toString(); } /** * Returns a new {@link MessageDigest} instance corresponding to the provided digest algorithm. */ private static MessageDigest getMessageDigestInstance(DigestAlgorithm digestAlgorithm) throws NoSuchAlgorithmException { String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm(); return MessageDigest.getInstance(jcaAlgorithm); } /** * Returns the JCA {@link MessageDigest} algorithm corresponding to the provided digest * algorithm. */ public static String getJcaMessageDigestAlgorithm(DigestAlgorithm digestAlgorithm) { return digestAlgorithm.getJcaMessageDigestAlgorithm(); } /** * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's * manifest. */ public static boolean isJarEntryDigestNeededInManifest(String entryName) { // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File // Entries which represent directories sould not be listed in the manifest. if (entryName.endsWith("/")) { return false; } // Entries outside of META-INF must be listed in the manifest. if (!entryName.startsWith("META-INF/")) { return true; } // Entries in subdirectories of META-INF must be listed in the manifest. if (entryName.indexOf('/', "META-INF/".length()) != -1) { return true; } // Ignored file names (case-insensitive) in META-INF directory: // MANIFEST.MF // *.SF // *.RSA // *.DSA // *.EC // SIG-* String fileNameLowerCase = entryName.substring("META-INF/".length()).toLowerCase(Locale.US); if (("manifest.mf".equals(fileNameLowerCase)) || (fileNameLowerCase.endsWith(".sf")) || (fileNameLowerCase.endsWith(".rsa")) || (fileNameLowerCase.endsWith(".dsa")) || (fileNameLowerCase.endsWith(".ec")) || (fileNameLowerCase.startsWith("sig-"))) { return false; } return true; } /** * Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of * JAR entries which need to be added to the APK as part of the signature. * * @param signerConfigs signer configurations, one for each signer. At least one signer config * must be provided. * * @throws ApkFormatException if the source manifest is malformed * @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is * missing * @throws InvalidKeyException if a signing key is not suitable for this signature scheme or * cannot be used in general * @throws SignatureException if an error occurs when computing digests of generating * signatures */ public static List> sign( List signerConfigs, DigestAlgorithm jarEntryDigestAlgorithm, Map jarEntryDigests, List apkSigningSchemeIds, byte[] sourceManifestBytes, String createdBy) throws NoSuchAlgorithmException, ApkFormatException, InvalidKeyException, CertificateException, SignatureException { if (signerConfigs.isEmpty()) { throw new IllegalArgumentException("At least one signer config must be provided"); } if (signerConfigs.size() > MAX_APK_SIGNERS) { throw new IllegalArgumentException( "APK Signature Scheme v1 only supports a maximum of " + MAX_APK_SIGNERS + ", " + signerConfigs.size() + " provided"); } OutputManifestFile manifest = generateManifestFile( jarEntryDigestAlgorithm, jarEntryDigests, sourceManifestBytes); return signManifest( signerConfigs, jarEntryDigestAlgorithm, apkSigningSchemeIds, createdBy, manifest); } /** * Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of * JAR entries which need to be added to the APK as part of the signature. * * @param signerConfigs signer configurations, one for each signer. At least one signer config * must be provided. * * @throws InvalidKeyException if a signing key is not suitable for this signature scheme or * cannot be used in general * @throws SignatureException if an error occurs when computing digests of generating * signatures */ public static List> signManifest( List signerConfigs, DigestAlgorithm digestAlgorithm, List apkSigningSchemeIds, String createdBy, OutputManifestFile manifest) throws NoSuchAlgorithmException, InvalidKeyException, CertificateException, SignatureException { if (signerConfigs.isEmpty()) { throw new IllegalArgumentException("At least one signer config must be provided"); } // For each signer output .SF and .(RSA|DSA|EC) file, then output MANIFEST.MF. List> signatureJarEntries = new ArrayList<>(2 * signerConfigs.size() + 1); byte[] sfBytes = generateSignatureFile(apkSigningSchemeIds, digestAlgorithm, createdBy, manifest); for (SignerConfig signerConfig : signerConfigs) { String signerName = signerConfig.name; byte[] signatureBlock; try { signatureBlock = generateSignatureBlock(signerConfig, sfBytes); } catch (InvalidKeyException e) { throw new InvalidKeyException( "Failed to sign using signer \"" + signerName + "\"", e); } catch (CertificateException e) { throw new CertificateException( "Failed to sign using signer \"" + signerName + "\"", e); } catch (SignatureException e) { throw new SignatureException( "Failed to sign using signer \"" + signerName + "\"", e); } signatureJarEntries.add(Pair.of("META-INF/" + signerName + ".SF", sfBytes)); PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey(); String signatureBlockFileName = "META-INF/" + signerName + "." + publicKey.getAlgorithm().toUpperCase(Locale.US); signatureJarEntries.add( Pair.of(signatureBlockFileName, signatureBlock)); } signatureJarEntries.add(Pair.of(V1SchemeConstants.MANIFEST_ENTRY_NAME, manifest.contents)); return signatureJarEntries; } /** * Returns the names of JAR entries which this signer will produce as part of v1 signature. */ public static Set getOutputEntryNames(List signerConfigs) { Set result = new HashSet<>(2 * signerConfigs.size() + 1); for (SignerConfig signerConfig : signerConfigs) { String signerName = signerConfig.name; result.add("META-INF/" + signerName + ".SF"); PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey(); String signatureBlockFileName = "META-INF/" + signerName + "." + publicKey.getAlgorithm().toUpperCase(Locale.US); result.add(signatureBlockFileName); } result.add(V1SchemeConstants.MANIFEST_ENTRY_NAME); return result; } /** * Generated and returns the {@code META-INF/MANIFEST.MF} file based on the provided (optional) * input {@code MANIFEST.MF} and digests of JAR entries covered by the manifest. */ public static OutputManifestFile generateManifestFile( DigestAlgorithm jarEntryDigestAlgorithm, Map jarEntryDigests, byte[] sourceManifestBytes) throws ApkFormatException { Manifest sourceManifest = null; if (sourceManifestBytes != null) { try { sourceManifest = new Manifest(new ByteArrayInputStream(sourceManifestBytes)); } catch (IOException e) { throw new ApkFormatException("Malformed source META-INF/MANIFEST.MF", e); } } ByteArrayOutputStream manifestOut = new ByteArrayOutputStream(); Attributes mainAttrs = new Attributes(); // Copy the main section from the source manifest (if provided). Otherwise use defaults. // NOTE: We don't output our own Created-By header because this signer did not create the // JAR/APK being signed -- the signer only adds signatures to the already existing // JAR/APK. if (sourceManifest != null) { mainAttrs.putAll(sourceManifest.getMainAttributes()); } else { mainAttrs.put(Attributes.Name.MANIFEST_VERSION, ATTRIBUTE_VALUE_MANIFEST_VERSION); } try { ManifestWriter.writeMainSection(manifestOut, mainAttrs); } catch (IOException e) { throw new RuntimeException("Failed to write in-memory MANIFEST.MF", e); } List sortedEntryNames = new ArrayList<>(jarEntryDigests.keySet()); Collections.sort(sortedEntryNames); SortedMap invidualSectionsContents = new TreeMap<>(); String entryDigestAttributeName = getEntryDigestAttributeName(jarEntryDigestAlgorithm); for (String entryName : sortedEntryNames) { checkEntryNameValid(entryName); byte[] entryDigest = jarEntryDigests.get(entryName); Attributes entryAttrs = new Attributes(); entryAttrs.putValue( entryDigestAttributeName, Base64.getEncoder().encodeToString(entryDigest)); ByteArrayOutputStream sectionOut = new ByteArrayOutputStream(); byte[] sectionBytes; try { ManifestWriter.writeIndividualSection(sectionOut, entryName, entryAttrs); sectionBytes = sectionOut.toByteArray(); manifestOut.write(sectionBytes); } catch (IOException e) { throw new RuntimeException("Failed to write in-memory MANIFEST.MF", e); } invidualSectionsContents.put(entryName, sectionBytes); } OutputManifestFile result = new OutputManifestFile(); result.contents = manifestOut.toByteArray(); result.mainSectionAttributes = mainAttrs; result.individualSectionsContents = invidualSectionsContents; return result; } private static void checkEntryNameValid(String name) throws ApkFormatException { // JAR signing spec says CR, LF, and NUL are not permitted in entry names // CR or LF in entry names will result in malformed MANIFEST.MF and .SF files because there // is no way to escape characters in MANIFEST.MF and .SF files. NUL can, presumably, cause // issues when parsing using C and C++ like languages. for (char c : name.toCharArray()) { if ((c == '\r') || (c == '\n') || (c == 0)) { throw new ApkFormatException( String.format( "Unsupported character 0x%1$02x in ZIP entry name \"%2$s\"", (int) c, name)); } } } public static class OutputManifestFile { public byte[] contents; public SortedMap individualSectionsContents; public Attributes mainSectionAttributes; } private static byte[] generateSignatureFile( List apkSignatureSchemeIds, DigestAlgorithm manifestDigestAlgorithm, String createdBy, OutputManifestFile manifest) throws NoSuchAlgorithmException { Manifest sf = new Manifest(); Attributes mainAttrs = sf.getMainAttributes(); mainAttrs.put(Attributes.Name.SIGNATURE_VERSION, ATTRIBUTE_VALUE_SIGNATURE_VERSION); mainAttrs.put(ATTRIBUTE_NAME_CREATED_BY, createdBy); if (!apkSignatureSchemeIds.isEmpty()) { // Add APK Signature Scheme v2 (and newer) signature stripping protection. // This attribute indicates that this APK is supposed to have been signed using one or // more APK-specific signature schemes in addition to the standard JAR signature scheme // used by this code. APK signature verifier should reject the APK if it does not // contain a signature for the signature scheme the verifier prefers out of this set. StringBuilder attrValue = new StringBuilder(); for (int id : apkSignatureSchemeIds) { if (attrValue.length() > 0) { attrValue.append(", "); } attrValue.append(String.valueOf(id)); } mainAttrs.put( SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME, attrValue.toString()); } // Add main attribute containing the digest of MANIFEST.MF. MessageDigest md = getMessageDigestInstance(manifestDigestAlgorithm); mainAttrs.putValue( getManifestDigestAttributeName(manifestDigestAlgorithm), Base64.getEncoder().encodeToString(md.digest(manifest.contents))); ByteArrayOutputStream out = new ByteArrayOutputStream(); try { SignatureFileWriter.writeMainSection(out, mainAttrs); } catch (IOException e) { throw new RuntimeException("Failed to write in-memory .SF file", e); } String entryDigestAttributeName = getEntryDigestAttributeName(manifestDigestAlgorithm); for (Map.Entry manifestSection : manifest.individualSectionsContents.entrySet()) { String sectionName = manifestSection.getKey(); byte[] sectionContents = manifestSection.getValue(); byte[] sectionDigest = md.digest(sectionContents); Attributes attrs = new Attributes(); attrs.putValue( entryDigestAttributeName, Base64.getEncoder().encodeToString(sectionDigest)); try { SignatureFileWriter.writeIndividualSection(out, sectionName, attrs); } catch (IOException e) { throw new RuntimeException("Failed to write in-memory .SF file", e); } } // A bug in the java.util.jar implementation of Android platforms up to version 1.6 will // cause a spurious IOException to be thrown if the length of the signature file is a // multiple of 1024 bytes. As a workaround, add an extra CRLF in this case. if ((out.size() > 0) && ((out.size() % 1024) == 0)) { try { SignatureFileWriter.writeSectionDelimiter(out); } catch (IOException e) { throw new RuntimeException("Failed to write to ByteArrayOutputStream", e); } } return out.toByteArray(); } /** * Generates the CMS PKCS #7 signature block corresponding to the provided signature file and * signing configuration. */ private static byte[] generateSignatureBlock( SignerConfig signerConfig, byte[] signatureFileBytes) throws NoSuchAlgorithmException, InvalidKeyException, CertificateException, SignatureException { // Obtain relevant bits of signing configuration List signerCerts = signerConfig.certificates; X509Certificate signingCert = signerCerts.get(0); PublicKey publicKey = signingCert.getPublicKey(); DigestAlgorithm digestAlgorithm = signerConfig.signatureDigestAlgorithm; Pair signatureAlgs = getSignerInfoSignatureAlgorithm(publicKey, digestAlgorithm, signerConfig.deterministicDsaSigning); String jcaSignatureAlgorithm = signatureAlgs.getFirst(); // Generate the cryptographic signature of the signature file byte[] signatureBytes; try { signatureBytes = SignerEngineFactory.getImplementation( signerConfig.keyConfig, jcaSignatureAlgorithm, null) .sign(signatureFileBytes); } catch (InvalidKeyException e) { throw new InvalidKeyException("Failed to sign using " + jcaSignatureAlgorithm, e); } catch (InvalidAlgorithmParameterException | SignatureException e) { throw new SignatureException("Failed to sign using " + jcaSignatureAlgorithm, e); } // Verify the signature against the public key in the signing certificate try { Signature signature = Signature.getInstance(jcaSignatureAlgorithm); signature.initVerify(publicKey); signature.update(signatureFileBytes); if (!signature.verify(signatureBytes)) { throw new SignatureException("Signature did not verify"); } } catch (InvalidKeyException e) { throw new InvalidKeyException( "Failed to verify generated " + jcaSignatureAlgorithm + " signature using" + " public key from certificate", e); } catch (SignatureException e) { throw new SignatureException( "Failed to verify generated " + jcaSignatureAlgorithm + " signature using" + " public key from certificate", e); } AlgorithmIdentifier digestAlgorithmId = getSignerInfoDigestAlgorithmOid(digestAlgorithm); AlgorithmIdentifier signatureAlgorithmId = signatureAlgs.getSecond(); try { return ApkSigningBlockUtils.generatePkcs7DerEncodedMessage( signatureBytes, null, signerCerts, digestAlgorithmId, signatureAlgorithmId); } catch (Asn1EncodingException | CertificateEncodingException ex) { throw new SignatureException("Failed to encode signature block"); } } private static String getEntryDigestAttributeName(DigestAlgorithm digestAlgorithm) { switch (digestAlgorithm) { case SHA1: return "SHA1-Digest"; case SHA256: return "SHA-256-Digest"; default: throw new IllegalArgumentException( "Unexpected content digest algorithm: " + digestAlgorithm); } } private static String getManifestDigestAttributeName(DigestAlgorithm digestAlgorithm) { switch (digestAlgorithm) { case SHA1: return "SHA1-Digest-Manifest"; case SHA256: return "SHA-256-Digest-Manifest"; default: throw new IllegalArgumentException( "Unexpected content digest algorithm: " + digestAlgorithm); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v1_V1SchemeVerifier.java0100644 0000000 0000000 00000000034 14763776540 030203 xustar000000000 0000000 28 mtime=1741684064.5780000 src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java0100644 0000000 0000000 00000215022 14763776540 024631 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v1; import static com.android.apksig.Constants.MAX_APK_SIGNERS; import static com.android.apksig.internal.oid.OidConstants.getSigAlgSupportedApiLevels; import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getJcaDigestAlgorithm; import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getJcaSignatureAlgorithm; import static com.android.apksig.internal.x509.Certificate.findCertificate; import static com.android.apksig.internal.x509.Certificate.parseCertificates; import com.android.apksig.ApkVerifier.Issue; import com.android.apksig.ApkVerifier.IssueWithParams; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.asn1.Asn1BerParser; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1DecodingException; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.jar.ManifestParser; import com.android.apksig.internal.oid.OidConstants; import com.android.apksig.internal.pkcs7.Attribute; import com.android.apksig.internal.pkcs7.ContentInfo; import com.android.apksig.internal.pkcs7.Pkcs7Constants; import com.android.apksig.internal.pkcs7.Pkcs7DecodingException; import com.android.apksig.internal.pkcs7.SignedData; import com.android.apksig.internal.pkcs7.SignerInfo; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.ByteBufferUtils; import com.android.apksig.internal.util.InclusiveIntRange; import com.android.apksig.internal.util.Pair; import com.android.apksig.internal.zip.CentralDirectoryRecord; import com.android.apksig.internal.zip.LocalFileRecord; import com.android.apksig.internal.zip.ZipUtils; import com.android.apksig.util.DataSinks; import com.android.apksig.util.DataSource; import com.android.apksig.zip.ZipFormatException; import java.io.IOException; import java.nio.ByteBuffer; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.Base64.Decoder; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.jar.Attributes; /** * APK verifier which uses JAR signing (aka v1 signing scheme). * * @see Signed JAR File */ public abstract class V1SchemeVerifier { private V1SchemeVerifier() {} /** * Verifies the provided APK's JAR signatures and returns the result of verification. APK is * considered verified only if {@link Result#verified} is {@code true}. If verification fails, * the result will contain errors -- see {@link Result#getErrors()}. * *

Verification succeeds iff the APK's JAR signatures are expected to verify on all Android * platform versions in the {@code [minSdkVersion, maxSdkVersion]} range. If the APK's signature * is expected to not verify on any of the specified platform versions, this method returns a * result with one or more errors and whose {@code Result.verified == false}, or this method * throws an exception. * * @throws ApkFormatException if the APK is malformed * @throws IOException if an I/O error occurs when reading the APK * @throws NoSuchAlgorithmException if the APK's JAR signatures cannot be verified because a * required cryptographic algorithm implementation is missing */ public static Result verify( DataSource apk, ApkUtils.ZipSections apkSections, Map supportedApkSigSchemeNames, Set foundApkSigSchemeIds, int minSdkVersion, int maxSdkVersion) throws IOException, ApkFormatException, NoSuchAlgorithmException { if (minSdkVersion > maxSdkVersion) { throw new IllegalArgumentException( "minSdkVersion (" + minSdkVersion + ") > maxSdkVersion (" + maxSdkVersion + ")"); } Result result = new Result(); // Parse the ZIP Central Directory and check that there are no entries with duplicate names. List cdRecords = parseZipCentralDirectory(apk, apkSections); Set cdEntryNames = checkForDuplicateEntries(cdRecords, result); if (result.containsErrors()) { return result; } // Verify JAR signature(s). Signers.verify( apk, apkSections.getZipCentralDirectoryOffset(), cdRecords, cdEntryNames, supportedApkSigSchemeNames, foundApkSigSchemeIds, minSdkVersion, maxSdkVersion, result); return result; } /** * Returns the set of entry names and reports any duplicate entry names in the {@code result} * as errors. */ private static Set checkForDuplicateEntries( List cdRecords, Result result) { Set cdEntryNames = new HashSet<>(cdRecords.size()); Set duplicateCdEntryNames = null; for (CentralDirectoryRecord cdRecord : cdRecords) { String entryName = cdRecord.getName(); if (!cdEntryNames.add(entryName)) { // This is an error. Report this once per duplicate name. if (duplicateCdEntryNames == null) { duplicateCdEntryNames = new HashSet<>(); } if (duplicateCdEntryNames.add(entryName)) { result.addError(Issue.JAR_SIG_DUPLICATE_ZIP_ENTRY, entryName); } } } return cdEntryNames; } /** * Parses raw representation of MANIFEST.MF file into a pair of main entry manifest section * representation and a mapping between entry name and its manifest section representation. * * @param manifestBytes raw representation of Manifest.MF * @param cdEntryNames expected set of entry names * @param result object to keep track of errors that happened during the parsing * @return a pair of main entry manifest section representation and a mapping between entry name * and its manifest section representation */ public static Pair> parseManifest( byte[] manifestBytes, Set cdEntryNames, Result result) { ManifestParser manifest = new ManifestParser(manifestBytes); ManifestParser.Section manifestMainSection = manifest.readSection(); List manifestIndividualSections = manifest.readAllSections(); Map entryNameToManifestSection = new HashMap<>(manifestIndividualSections.size()); int manifestSectionNumber = 0; for (ManifestParser.Section manifestSection : manifestIndividualSections) { manifestSectionNumber++; String entryName = manifestSection.getName(); if (entryName == null) { result.addError(Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION, manifestSectionNumber); continue; } if (entryNameToManifestSection.put(entryName, manifestSection) != null) { result.addError(Issue.JAR_SIG_DUPLICATE_MANIFEST_SECTION, entryName); continue; } if (!cdEntryNames.contains(entryName)) { result.addError( Issue.JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST, entryName); continue; } } return Pair.of(manifestMainSection, entryNameToManifestSection); } /** * All JAR signers of an APK. */ private static class Signers { /** * Verifies JAR signatures of the provided APK and populates the provided result container * with errors, warnings, and information about signers. The APK is considered verified if * the {@link Result#verified} is {@code true}. */ private static void verify( DataSource apk, long cdStartOffset, List cdRecords, Set cdEntryNames, Map supportedApkSigSchemeNames, Set foundApkSigSchemeIds, int minSdkVersion, int maxSdkVersion, Result result) throws ApkFormatException, IOException, NoSuchAlgorithmException { // Find JAR manifest and signature block files. CentralDirectoryRecord manifestEntry = null; Map sigFileEntries = new HashMap<>(1); List sigBlockEntries = new ArrayList<>(1); for (CentralDirectoryRecord cdRecord : cdRecords) { String entryName = cdRecord.getName(); if (!entryName.startsWith("META-INF/")) { continue; } if ((manifestEntry == null) && (V1SchemeConstants.MANIFEST_ENTRY_NAME.equals( entryName))) { manifestEntry = cdRecord; continue; } if (entryName.endsWith(".SF")) { sigFileEntries.put(entryName, cdRecord); continue; } if ((entryName.endsWith(".RSA")) || (entryName.endsWith(".DSA")) || (entryName.endsWith(".EC"))) { sigBlockEntries.add(cdRecord); continue; } } if (manifestEntry == null) { result.addError(Issue.JAR_SIG_NO_MANIFEST); return; } // Parse the JAR manifest and check that all JAR entries it references exist in the APK. byte[] manifestBytes; try { manifestBytes = LocalFileRecord.getUncompressedData(apk, manifestEntry, cdStartOffset); } catch (ZipFormatException e) { throw new ApkFormatException("Malformed ZIP entry: " + manifestEntry.getName(), e); } Pair> manifestSections = parseManifest(manifestBytes, cdEntryNames, result); if (result.containsErrors()) { return; } ManifestParser.Section manifestMainSection = manifestSections.getFirst(); Map entryNameToManifestSection = manifestSections.getSecond(); // STATE OF AFFAIRS: // * All JAR entries listed in JAR manifest are present in the APK. // Identify signers List signers = new ArrayList<>(sigBlockEntries.size()); for (CentralDirectoryRecord sigBlockEntry : sigBlockEntries) { String sigBlockEntryName = sigBlockEntry.getName(); int extensionDelimiterIndex = sigBlockEntryName.lastIndexOf('.'); if (extensionDelimiterIndex == -1) { throw new RuntimeException( "Signature block file name does not contain extension: " + sigBlockEntryName); } String sigFileEntryName = sigBlockEntryName.substring(0, extensionDelimiterIndex) + ".SF"; CentralDirectoryRecord sigFileEntry = sigFileEntries.get(sigFileEntryName); if (sigFileEntry == null) { result.addWarning( Issue.JAR_SIG_MISSING_FILE, sigBlockEntryName, sigFileEntryName); continue; } String signerName = sigBlockEntryName.substring("META-INF/".length()); Result.SignerInfo signerInfo = new Result.SignerInfo( signerName, sigBlockEntryName, sigFileEntry.getName()); Signer signer = new Signer(signerName, sigBlockEntry, sigFileEntry, signerInfo); signers.add(signer); } if (signers.isEmpty()) { result.addError(Issue.JAR_SIG_NO_SIGNATURES); return; } if (signers.size() > MAX_APK_SIGNERS) { result.addError(Issue.JAR_SIG_MAX_SIGNATURES_EXCEEDED, MAX_APK_SIGNERS, signers.size()); return; } // Verify each signer's signature block file .(RSA|DSA|EC) against the corresponding // signature file .SF. Any error encountered for any signer terminates verification, to // mimic Android's behavior. for (Signer signer : signers) { signer.verifySigBlockAgainstSigFile( apk, cdStartOffset, minSdkVersion, maxSdkVersion); if (signer.getResult().containsErrors()) { result.signers.add(signer.getResult()); } } if (result.containsErrors()) { return; } // STATE OF AFFAIRS: // * All JAR entries listed in JAR manifest are present in the APK. // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC). // Verify each signer's signature file (.SF) against the JAR manifest. List remainingSigners = new ArrayList<>(signers.size()); for (Signer signer : signers) { signer.verifySigFileAgainstManifest( manifestBytes, manifestMainSection, entryNameToManifestSection, supportedApkSigSchemeNames, foundApkSigSchemeIds, minSdkVersion, maxSdkVersion); if (signer.isIgnored()) { result.ignoredSigners.add(signer.getResult()); } else { if (signer.getResult().containsErrors()) { result.signers.add(signer.getResult()); } else { remainingSigners.add(signer); } } } if (result.containsErrors()) { return; } signers = remainingSigners; if (signers.isEmpty()) { result.addError(Issue.JAR_SIG_NO_SIGNATURES); return; } // STATE OF AFFAIRS: // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC). // * Contents of all JAR manifest sections listed in .SF files verify against .SF files. // * All JAR entries listed in JAR manifest are present in the APK. // Verify data of JAR entries against JAR manifest and .SF files. On Android, an APK's // JAR entry is considered signed by signers associated with an .SF file iff the entry // is mentioned in the .SF file and the entry's digest(s) mentioned in the JAR manifest // match theentry's uncompressed data. Android requires that all such JAR entries are // signed by the same set of signers. This set may be smaller than the set of signers // we've identified so far. Set apkSigners = verifyJarEntriesAgainstManifestAndSigners( apk, cdStartOffset, cdRecords, entryNameToManifestSection, signers, minSdkVersion, maxSdkVersion, result); if (result.containsErrors()) { return; } // STATE OF AFFAIRS: // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC). // * Contents of all JAR manifest sections listed in .SF files verify against .SF files. // * All JAR entries listed in JAR manifest are present in the APK. // * All JAR entries present in the APK and supposed to be covered by JAR signature // (i.e., reside outside of META-INF/) are covered by signatures from the same set // of signers. // Report any JAR entries which aren't covered by signature. Set signatureEntryNames = new HashSet<>(1 + result.signers.size() * 2); signatureEntryNames.add(manifestEntry.getName()); for (Signer signer : apkSigners) { signatureEntryNames.add(signer.getSignatureBlockEntryName()); signatureEntryNames.add(signer.getSignatureFileEntryName()); } for (CentralDirectoryRecord cdRecord : cdRecords) { String entryName = cdRecord.getName(); if ((entryName.startsWith("META-INF/")) && (!entryName.endsWith("/")) && (!signatureEntryNames.contains(entryName))) { result.addWarning(Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY, entryName); } } // Reflect the sets of used signers and ignored signers in the result. for (Signer signer : signers) { if (apkSigners.contains(signer)) { result.signers.add(signer.getResult()); } else { result.ignoredSigners.add(signer.getResult()); } } result.verified = true; } } static class Signer { private final String mName; private final Result.SignerInfo mResult; private final CentralDirectoryRecord mSignatureFileEntry; private final CentralDirectoryRecord mSignatureBlockEntry; private boolean mIgnored; private byte[] mSigFileBytes; private Set mSigFileEntryNames; private Signer( String name, CentralDirectoryRecord sigBlockEntry, CentralDirectoryRecord sigFileEntry, Result.SignerInfo result) { mName = name; mResult = result; mSignatureBlockEntry = sigBlockEntry; mSignatureFileEntry = sigFileEntry; } public String getName() { return mName; } public String getSignatureFileEntryName() { return mSignatureFileEntry.getName(); } public String getSignatureBlockEntryName() { return mSignatureBlockEntry.getName(); } void setIgnored() { mIgnored = true; } public boolean isIgnored() { return mIgnored; } public Set getSigFileEntryNames() { return mSigFileEntryNames; } public Result.SignerInfo getResult() { return mResult; } public void verifySigBlockAgainstSigFile( DataSource apk, long cdStartOffset, int minSdkVersion, int maxSdkVersion) throws IOException, ApkFormatException, NoSuchAlgorithmException { // Obtain the signature block from the APK byte[] sigBlockBytes; try { sigBlockBytes = LocalFileRecord.getUncompressedData( apk, mSignatureBlockEntry, cdStartOffset); } catch (ZipFormatException e) { throw new ApkFormatException( "Malformed ZIP entry: " + mSignatureBlockEntry.getName(), e); } // Obtain the signature file from the APK try { mSigFileBytes = LocalFileRecord.getUncompressedData( apk, mSignatureFileEntry, cdStartOffset); } catch (ZipFormatException e) { throw new ApkFormatException( "Malformed ZIP entry: " + mSignatureFileEntry.getName(), e); } // Extract PKCS #7 SignedData from the signature block SignedData signedData; try { ContentInfo contentInfo = Asn1BerParser.parse(ByteBuffer.wrap(sigBlockBytes), ContentInfo.class); if (!Pkcs7Constants.OID_SIGNED_DATA.equals(contentInfo.contentType)) { throw new Asn1DecodingException( "Unsupported ContentInfo.contentType: " + contentInfo.contentType); } signedData = Asn1BerParser.parse(contentInfo.content.getEncoded(), SignedData.class); } catch (Asn1DecodingException e) { e.printStackTrace(); mResult.addError( Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e); return; } if (signedData.signerInfos.isEmpty()) { mResult.addError(Issue.JAR_SIG_NO_SIGNERS, mSignatureBlockEntry.getName()); return; } // Find the first SignedData.SignerInfos element which verifies against the signature // file SignerInfo firstVerifiedSignerInfo = null; X509Certificate firstVerifiedSignerInfoSigningCertificate = null; // Prior to Android N, Android attempts to verify only the first SignerInfo. From N // onwards, Android attempts to verify all SignerInfos and then picks the first verified // SignerInfo. List unverifiedSignerInfosToTry; if (minSdkVersion < AndroidSdkVersion.N) { unverifiedSignerInfosToTry = Collections.singletonList(signedData.signerInfos.get(0)); } else { unverifiedSignerInfosToTry = signedData.signerInfos; } List signedDataCertificates = null; for (SignerInfo unverifiedSignerInfo : unverifiedSignerInfosToTry) { // Parse SignedData.certificates -- they are needed to verify SignerInfo if (signedDataCertificates == null) { try { signedDataCertificates = parseCertificates(signedData.certificates); } catch (CertificateException e) { mResult.addError( Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e); return; } } // Verify SignerInfo X509Certificate signingCertificate; try { signingCertificate = verifySignerInfoAgainstSigFile( signedData, signedDataCertificates, unverifiedSignerInfo, mSigFileBytes, minSdkVersion, maxSdkVersion); if (mResult.containsErrors()) { return; } if (signingCertificate != null) { // SignerInfo verified if (firstVerifiedSignerInfo == null) { firstVerifiedSignerInfo = unverifiedSignerInfo; firstVerifiedSignerInfoSigningCertificate = signingCertificate; } } } catch (Pkcs7DecodingException e) { mResult.addError( Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e); return; } catch (InvalidKeyException | SignatureException e) { mResult.addError( Issue.JAR_SIG_VERIFY_EXCEPTION, mSignatureBlockEntry.getName(), mSignatureFileEntry.getName(), e); return; } } if (firstVerifiedSignerInfo == null) { // No SignerInfo verified mResult.addError( Issue.JAR_SIG_DID_NOT_VERIFY, mSignatureBlockEntry.getName(), mSignatureFileEntry.getName()); return; } // Verified List signingCertChain = getCertificateChain( signedDataCertificates, firstVerifiedSignerInfoSigningCertificate); mResult.certChain.clear(); mResult.certChain.addAll(signingCertChain); } /** * Returns the signing certificate if the provided {@link SignerInfo} verifies against the * contents of the provided signature file, or {@code null} if it does not verify. */ private X509Certificate verifySignerInfoAgainstSigFile( SignedData signedData, Collection signedDataCertificates, SignerInfo signerInfo, byte[] signatureFile, int minSdkVersion, int maxSdkVersion) throws Pkcs7DecodingException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { String digestAlgorithmOid = signerInfo.digestAlgorithm.algorithm; String signatureAlgorithmOid = signerInfo.signatureAlgorithm.algorithm; InclusiveIntRange desiredApiLevels = InclusiveIntRange.fromTo(minSdkVersion, maxSdkVersion); List apiLevelsWhereDigestAndSigAlgorithmSupported = getSigAlgSupportedApiLevels(digestAlgorithmOid, signatureAlgorithmOid); List apiLevelsWhereDigestAlgorithmNotSupported = desiredApiLevels.getValuesNotIn(apiLevelsWhereDigestAndSigAlgorithmSupported); if (!apiLevelsWhereDigestAlgorithmNotSupported.isEmpty()) { String digestAlgorithmUserFriendly = OidConstants.OidToUserFriendlyNameMapper.getUserFriendlyNameForOid( digestAlgorithmOid); if (digestAlgorithmUserFriendly == null) { digestAlgorithmUserFriendly = digestAlgorithmOid; } String signatureAlgorithmUserFriendly = OidConstants.OidToUserFriendlyNameMapper.getUserFriendlyNameForOid( signatureAlgorithmOid); if (signatureAlgorithmUserFriendly == null) { signatureAlgorithmUserFriendly = signatureAlgorithmOid; } StringBuilder apiLevelsUserFriendly = new StringBuilder(); for (InclusiveIntRange range : apiLevelsWhereDigestAlgorithmNotSupported) { if (apiLevelsUserFriendly.length() > 0) { apiLevelsUserFriendly.append(", "); } if (range.getMin() == range.getMax()) { apiLevelsUserFriendly.append(String.valueOf(range.getMin())); } else if (range.getMax() == Integer.MAX_VALUE) { apiLevelsUserFriendly.append(range.getMin() + "+"); } else { apiLevelsUserFriendly.append(range.getMin() + "-" + range.getMax()); } } mResult.addError( Issue.JAR_SIG_UNSUPPORTED_SIG_ALG, mSignatureBlockEntry.getName(), digestAlgorithmOid, signatureAlgorithmOid, apiLevelsUserFriendly.toString(), digestAlgorithmUserFriendly, signatureAlgorithmUserFriendly); return null; } // From the bag of certs, obtain the certificate referenced by the SignerInfo, // and verify the cryptographic signature in the SignerInfo against the certificate. // Locate the signing certificate referenced by the SignerInfo X509Certificate signingCertificate = findCertificate(signedDataCertificates, signerInfo.sid); if (signingCertificate == null) { throw new SignatureException( "Signing certificate referenced in SignerInfo not found in" + " SignedData"); } // Check whether the signing certificate is acceptable. Android performs these // checks explicitly, instead of delegating this to // Signature.initVerify(Certificate). if (signingCertificate.hasUnsupportedCriticalExtension()) { throw new SignatureException( "Signing certificate has unsupported critical extensions"); } boolean[] keyUsageExtension = signingCertificate.getKeyUsage(); if (keyUsageExtension != null) { boolean digitalSignature = (keyUsageExtension.length >= 1) && (keyUsageExtension[0]); boolean nonRepudiation = (keyUsageExtension.length >= 2) && (keyUsageExtension[1]); if ((!digitalSignature) && (!nonRepudiation)) { throw new SignatureException( "Signing certificate not authorized for use in digital signatures" + ": keyUsage extension missing digitalSignature and" + " nonRepudiation"); } } // Verify the cryptographic signature in SignerInfo against the certificate's // public key String jcaSignatureAlgorithm = getJcaSignatureAlgorithm(digestAlgorithmOid, signatureAlgorithmOid); Signature s = Signature.getInstance(jcaSignatureAlgorithm); PublicKey publicKey = signingCertificate.getPublicKey(); try { s.initVerify(publicKey); } catch (InvalidKeyException e) { // An InvalidKeyException could be caught if the PublicKey in the certificate is not // properly encoded; attempt to resolve any encoding errors, generate a new public // key, and reattempt the initVerify with the newly encoded key. try { byte[] encodedPublicKey = ApkSigningBlockUtils.encodePublicKey(publicKey); publicKey = KeyFactory.getInstance(publicKey.getAlgorithm()).generatePublic( new X509EncodedKeySpec(encodedPublicKey)); } catch (InvalidKeySpecException ikse) { // If an InvalidKeySpecException is caught then throw the original Exception // since the key couldn't be properly re-encoded, and the original Exception // will have more useful debugging info. throw e; } s = Signature.getInstance(jcaSignatureAlgorithm); s.initVerify(publicKey); } if (signerInfo.signedAttrs != null) { // Signed attributes present -- verify signature against the ASN.1 DER encoded form // of signed attributes. This verifies integrity of the signature file because // signed attributes must contain the digest of the signature file. if (minSdkVersion < AndroidSdkVersion.KITKAT) { // Prior to Android KitKat, APKs with signed attributes are unsafe: // * The APK's contents are not protected by the JAR signature because the // digest in signed attributes is not verified. This means an attacker can // arbitrarily modify the APK without invalidating its signature. // * Luckily, the signature over signed attributes was verified incorrectly // (over the verbatim IMPLICIT [0] form rather than over re-encoded // UNIVERSAL SET form) which means that JAR signatures which would verify on // pre-KitKat Android and yet do not protect the APK from modification could // be generated only by broken tools or on purpose by the entity signing the // APK. // // We thus reject such unsafe APKs, even if they verify on platforms before // KitKat. throw new SignatureException( "APKs with Signed Attributes broken on platforms with API Level < " + AndroidSdkVersion.KITKAT); } try { List signedAttributes = Asn1BerParser.parseImplicitSetOf( signerInfo.signedAttrs.getEncoded(), Attribute.class); SignedAttributes signedAttrs = new SignedAttributes(signedAttributes); if (maxSdkVersion >= AndroidSdkVersion.N) { // Content Type attribute is checked only on Android N and newer String contentType = signedAttrs.getSingleObjectIdentifierValue( Pkcs7Constants.OID_CONTENT_TYPE); if (contentType == null) { throw new SignatureException("No Content Type in signed attributes"); } if (!contentType.equals(signedData.encapContentInfo.contentType)) { // Did not verify: Content type signed attribute does not match // SignedData.encapContentInfo.eContentType. This fails verification of // this SignerInfo but should not prevent verification of other // SignerInfos. Hence, no exception is thrown. return null; } } byte[] expectedSignatureFileDigest = signedAttrs.getSingleOctetStringValue( Pkcs7Constants.OID_MESSAGE_DIGEST); if (expectedSignatureFileDigest == null) { throw new SignatureException("No content digest in signed attributes"); } byte[] actualSignatureFileDigest = MessageDigest.getInstance( getJcaDigestAlgorithm(digestAlgorithmOid)) .digest(signatureFile); if (!Arrays.equals( expectedSignatureFileDigest, actualSignatureFileDigest)) { // Skip verification: signature file digest in signed attributes does not // match the signature file. This fails verification of // this SignerInfo but should not prevent verification of other // SignerInfos. Hence, no exception is thrown. return null; } } catch (Asn1DecodingException e) { throw new SignatureException("Failed to parse signed attributes", e); } // PKCS #7 requires that signature is over signed attributes re-encoded as // ASN.1 DER. However, Android does not re-encode except for changing the // first byte of encoded form from IMPLICIT [0] to UNIVERSAL SET. We do the // same for maximum compatibility. ByteBuffer signedAttrsOriginalEncoding = signerInfo.signedAttrs.getEncoded(); s.update((byte) 0x31); // UNIVERSAL SET signedAttrsOriginalEncoding.position(1); s.update(signedAttrsOriginalEncoding); } else { // No signed attributes present -- verify signature against the contents of the // signature file s.update(signatureFile); } byte[] sigBytes = ByteBufferUtils.toByteArray(signerInfo.signature.slice()); if (!s.verify(sigBytes)) { // Cryptographic signature did not verify. This fails verification of this // SignerInfo but should not prevent verification of other SignerInfos. Hence, no // exception is thrown. return null; } // Cryptographic signature verified return signingCertificate; } public static List getCertificateChain( List certs, X509Certificate leaf) { List unusedCerts = new ArrayList<>(certs); List result = new ArrayList<>(1); result.add(leaf); unusedCerts.remove(leaf); X509Certificate root = leaf; while (!root.getSubjectDN().equals(root.getIssuerDN())) { Principal targetDn = root.getIssuerDN(); boolean issuerFound = false; for (int i = 0; i < unusedCerts.size(); i++) { X509Certificate unusedCert = unusedCerts.get(i); if (targetDn.equals(unusedCert.getSubjectDN())) { issuerFound = true; unusedCerts.remove(i); result.add(unusedCert); root = unusedCert; break; } } if (!issuerFound) { break; } } return result; } public void verifySigFileAgainstManifest( byte[] manifestBytes, ManifestParser.Section manifestMainSection, Map entryNameToManifestSection, Map supportedApkSigSchemeNames, Set foundApkSigSchemeIds, int minSdkVersion, int maxSdkVersion) throws NoSuchAlgorithmException { // Inspect the main section of the .SF file. ManifestParser sf = new ManifestParser(mSigFileBytes); ManifestParser.Section sfMainSection = sf.readSection(); if (sfMainSection.getAttributeValue(Attributes.Name.SIGNATURE_VERSION) == null) { mResult.addError( Issue.JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE, mSignatureFileEntry.getName()); setIgnored(); return; } if (maxSdkVersion >= AndroidSdkVersion.N) { // Android N and newer rejects APKs whose .SF file says they were supposed to be // signed with APK Signature Scheme v2 (or newer) and yet no such signature was // found. checkForStrippedApkSignatures( sfMainSection, supportedApkSigSchemeNames, foundApkSigSchemeIds); if (mResult.containsErrors()) { return; } } boolean createdBySigntool = false; String createdBy = sfMainSection.getAttributeValue("Created-By"); if (createdBy != null) { createdBySigntool = createdBy.indexOf("signtool") != -1; } boolean manifestDigestVerified = verifyManifestDigest( sfMainSection, createdBySigntool, manifestBytes, minSdkVersion, maxSdkVersion); if (!createdBySigntool) { verifyManifestMainSectionDigest( sfMainSection, manifestMainSection, manifestBytes, minSdkVersion, maxSdkVersion); } if (mResult.containsErrors()) { return; } // Inspect per-entry sections of .SF file. Technically, if the digest of JAR manifest // verifies, per-entry sections should be ignored. However, most Android platform // implementations require that such sections exist. List sfSections = sf.readAllSections(); Set sfEntryNames = new HashSet<>(sfSections.size()); int sfSectionNumber = 0; for (ManifestParser.Section sfSection : sfSections) { sfSectionNumber++; String entryName = sfSection.getName(); if (entryName == null) { mResult.addError( Issue.JAR_SIG_UNNNAMED_SIG_FILE_SECTION, mSignatureFileEntry.getName(), sfSectionNumber); setIgnored(); return; } if (!sfEntryNames.add(entryName)) { mResult.addError( Issue.JAR_SIG_DUPLICATE_SIG_FILE_SECTION, mSignatureFileEntry.getName(), entryName); setIgnored(); return; } if (manifestDigestVerified) { // No need to verify this entry's corresponding JAR manifest entry because the // JAR manifest verifies in full. continue; } // Whole-file digest of JAR manifest hasn't been verified. Thus, we need to verify // the digest of the JAR manifest section corresponding to this .SF section. ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName); if (manifestSection == null) { mResult.addError( Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE, entryName, mSignatureFileEntry.getName()); setIgnored(); continue; } verifyManifestIndividualSectionDigest( sfSection, createdBySigntool, manifestSection, manifestBytes, minSdkVersion, maxSdkVersion); } mSigFileEntryNames = sfEntryNames; } /** * Returns {@code true} if the whole-file digest of the manifest against the main section of * the .SF file. */ private boolean verifyManifestDigest( ManifestParser.Section sfMainSection, boolean createdBySigntool, byte[] manifestBytes, int minSdkVersion, int maxSdkVersion) throws NoSuchAlgorithmException { Collection expectedDigests = getDigestsToVerify( sfMainSection, ((createdBySigntool) ? "-Digest" : "-Digest-Manifest"), minSdkVersion, maxSdkVersion); boolean digestFound = !expectedDigests.isEmpty(); if (!digestFound) { mResult.addWarning( Issue.JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE, mSignatureFileEntry.getName()); return false; } boolean verified = true; for (NamedDigest expectedDigest : expectedDigests) { String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm; byte[] actual = digest(jcaDigestAlgorithm, manifestBytes); byte[] expected = expectedDigest.digest; if (!Arrays.equals(expected, actual)) { mResult.addWarning( Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY, V1SchemeConstants.MANIFEST_ENTRY_NAME, jcaDigestAlgorithm, mSignatureFileEntry.getName(), Base64.getEncoder().encodeToString(actual), Base64.getEncoder().encodeToString(expected)); verified = false; } } return verified; } /** * Verifies the digest of the manifest's main section against the main section of the .SF * file. */ private void verifyManifestMainSectionDigest( ManifestParser.Section sfMainSection, ManifestParser.Section manifestMainSection, byte[] manifestBytes, int minSdkVersion, int maxSdkVersion) throws NoSuchAlgorithmException { Collection expectedDigests = getDigestsToVerify( sfMainSection, "-Digest-Manifest-Main-Attributes", minSdkVersion, maxSdkVersion); if (expectedDigests.isEmpty()) { return; } for (NamedDigest expectedDigest : expectedDigests) { String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm; byte[] actual = digest( jcaDigestAlgorithm, manifestBytes, manifestMainSection.getStartOffset(), manifestMainSection.getSizeBytes()); byte[] expected = expectedDigest.digest; if (!Arrays.equals(expected, actual)) { mResult.addError( Issue.JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY, jcaDigestAlgorithm, mSignatureFileEntry.getName(), Base64.getEncoder().encodeToString(actual), Base64.getEncoder().encodeToString(expected)); } } } /** * Verifies the digest of the manifest's individual section against the corresponding * individual section of the .SF file. */ private void verifyManifestIndividualSectionDigest( ManifestParser.Section sfIndividualSection, boolean createdBySigntool, ManifestParser.Section manifestIndividualSection, byte[] manifestBytes, int minSdkVersion, int maxSdkVersion) throws NoSuchAlgorithmException { String entryName = sfIndividualSection.getName(); Collection expectedDigests = getDigestsToVerify( sfIndividualSection, "-Digest", minSdkVersion, maxSdkVersion); if (expectedDigests.isEmpty()) { mResult.addError( Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE, entryName, mSignatureFileEntry.getName()); return; } int sectionStartIndex = manifestIndividualSection.getStartOffset(); int sectionSizeBytes = manifestIndividualSection.getSizeBytes(); if (createdBySigntool) { int sectionEndIndex = sectionStartIndex + sectionSizeBytes; if ((manifestBytes[sectionEndIndex - 1] == '\n') && (manifestBytes[sectionEndIndex - 2] == '\n')) { sectionSizeBytes--; } } for (NamedDigest expectedDigest : expectedDigests) { String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm; byte[] actual = digest( jcaDigestAlgorithm, manifestBytes, sectionStartIndex, sectionSizeBytes); byte[] expected = expectedDigest.digest; if (!Arrays.equals(expected, actual)) { mResult.addError( Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY, entryName, jcaDigestAlgorithm, mSignatureFileEntry.getName(), Base64.getEncoder().encodeToString(actual), Base64.getEncoder().encodeToString(expected)); } } } private void checkForStrippedApkSignatures( ManifestParser.Section sfMainSection, Map supportedApkSigSchemeNames, Set foundApkSigSchemeIds) { String signedWithApkSchemes = sfMainSection.getAttributeValue( V1SchemeConstants.SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR); // This field contains a comma-separated list of APK signature scheme IDs which were // used to sign this APK. Android rejects APKs where an ID is known to the platform but // the APK didn't verify using that scheme. if (signedWithApkSchemes == null) { // APK signature (e.g., v2 scheme) stripping protections not enabled. if (!foundApkSigSchemeIds.isEmpty()) { // APK is signed with an APK signature scheme such as v2 scheme. mResult.addWarning( Issue.JAR_SIG_NO_APK_SIG_STRIP_PROTECTION, mSignatureFileEntry.getName()); } return; } if (supportedApkSigSchemeNames.isEmpty()) { return; } Set supportedApkSigSchemeIds = supportedApkSigSchemeNames.keySet(); Set supportedExpectedApkSigSchemeIds = new HashSet<>(1); StringTokenizer tokenizer = new StringTokenizer(signedWithApkSchemes, ","); while (tokenizer.hasMoreTokens()) { String idText = tokenizer.nextToken().trim(); if (idText.isEmpty()) { continue; } int id; try { id = Integer.parseInt(idText); } catch (Exception ignored) { continue; } // This APK was supposed to be signed with the APK signature scheme having // this ID. if (supportedApkSigSchemeIds.contains(id)) { supportedExpectedApkSigSchemeIds.add(id); } else { mResult.addWarning( Issue.JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID, mSignatureFileEntry.getName(), id); } } for (int id : supportedExpectedApkSigSchemeIds) { if (!foundApkSigSchemeIds.contains(id)) { String apkSigSchemeName = supportedApkSigSchemeNames.get(id); mResult.addError( Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED, mSignatureFileEntry.getName(), id, apkSigSchemeName); } } } } public static Collection getDigestsToVerify( ManifestParser.Section section, String digestAttrSuffix, int minSdkVersion, int maxSdkVersion) { Decoder base64Decoder = Base64.getDecoder(); List result = new ArrayList<>(1); if (minSdkVersion < AndroidSdkVersion.JELLY_BEAN_MR2) { // Prior to JB MR2, Android platform's logic for picking a digest algorithm to verify is // to rely on the ancient Digest-Algorithms attribute which contains // whitespace-separated list of digest algorithms (defaulting to SHA-1) to try. The // first digest attribute (with supported digest algorithm) found using the list is // used. String algs = section.getAttributeValue("Digest-Algorithms"); if (algs == null) { algs = "SHA SHA1"; } StringTokenizer tokens = new StringTokenizer(algs); while (tokens.hasMoreTokens()) { String alg = tokens.nextToken(); String attrName = alg + digestAttrSuffix; String digestBase64 = section.getAttributeValue(attrName); if (digestBase64 == null) { // Attribute not found continue; } alg = getCanonicalJcaMessageDigestAlgorithm(alg); if ((alg == null) || (getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile(alg) > minSdkVersion)) { // Unsupported digest algorithm continue; } // Supported digest algorithm result.add(new NamedDigest(alg, base64Decoder.decode(digestBase64))); break; } // No supported digests found -- this will fail to verify on pre-JB MR2 Androids. if (result.isEmpty()) { return result; } } if (maxSdkVersion >= AndroidSdkVersion.JELLY_BEAN_MR2) { // On JB MR2 and newer, Android platform picks the strongest algorithm out of: // SHA-512, SHA-384, SHA-256, SHA-1. for (String alg : JB_MR2_AND_NEWER_DIGEST_ALGS) { String attrName = getJarDigestAttributeName(alg, digestAttrSuffix); String digestBase64 = section.getAttributeValue(attrName); if (digestBase64 == null) { // Attribute not found continue; } byte[] digest = base64Decoder.decode(digestBase64); byte[] digestInResult = getDigest(result, alg); if ((digestInResult == null) || (!Arrays.equals(digestInResult, digest))) { result.add(new NamedDigest(alg, digest)); } break; } } return result; } private static final String[] JB_MR2_AND_NEWER_DIGEST_ALGS = { "SHA-512", "SHA-384", "SHA-256", "SHA-1", }; private static String getCanonicalJcaMessageDigestAlgorithm(String algorithm) { return UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.get(algorithm.toUpperCase(Locale.US)); } public static int getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile( String jcaAlgorithmName) { Integer result = MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.get( jcaAlgorithmName.toUpperCase(Locale.US)); return (result != null) ? result : Integer.MAX_VALUE; } private static String getJarDigestAttributeName( String jcaDigestAlgorithm, String attrNameSuffix) { if ("SHA-1".equalsIgnoreCase(jcaDigestAlgorithm)) { return "SHA1" + attrNameSuffix; } else { return jcaDigestAlgorithm + attrNameSuffix; } } private static final Map UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL; static { UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL = new HashMap<>(8); UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("MD5", "MD5"); UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA", "SHA-1"); UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA1", "SHA-1"); UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-1", "SHA-1"); UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-256", "SHA-256"); UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-384", "SHA-384"); UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-512", "SHA-512"); } private static final Map MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST; static { MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST = new HashMap<>(5); MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("MD5", 0); MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-1", 0); MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-256", 0); MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put( "SHA-384", AndroidSdkVersion.GINGERBREAD); MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put( "SHA-512", AndroidSdkVersion.GINGERBREAD); } private static byte[] getDigest(Collection digests, String jcaDigestAlgorithm) { for (NamedDigest digest : digests) { if (digest.jcaDigestAlgorithm.equalsIgnoreCase(jcaDigestAlgorithm)) { return digest.digest; } } return null; } public static List parseZipCentralDirectory( DataSource apk, ApkUtils.ZipSections apkSections) throws IOException, ApkFormatException { return ZipUtils.parseZipCentralDirectory(apk, apkSections); } /** * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's * manifest for the APK to verify on Android. */ private static boolean isJarEntryDigestNeededInManifest(String entryName) { // NOTE: This logic is different from what's required by the JAR signing scheme. This is // because Android's APK verification logic differs from that spec. In particular, JAR // signing spec includes into JAR manifest all files in subdirectories of META-INF and // any files inside META-INF not related to signatures. if (entryName.startsWith("META-INF/")) { return false; } return !entryName.endsWith("/"); } private static Set verifyJarEntriesAgainstManifestAndSigners( DataSource apk, long cdOffsetInApk, Collection cdRecords, Map entryNameToManifestSection, List signers, int minSdkVersion, int maxSdkVersion, Result result) throws ApkFormatException, IOException, NoSuchAlgorithmException { // Iterate over APK contents as sequentially as possible to improve performance. List cdRecordsSortedByLocalFileHeaderOffset = new ArrayList<>(cdRecords); Collections.sort( cdRecordsSortedByLocalFileHeaderOffset, CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR); List firstSignedEntrySigners = null; String firstSignedEntryName = null; for (CentralDirectoryRecord cdRecord : cdRecordsSortedByLocalFileHeaderOffset) { String entryName = cdRecord.getName(); if (!isJarEntryDigestNeededInManifest(entryName)) { continue; } ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName); if (manifestSection == null) { result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName); continue; } List entrySigners = new ArrayList<>(signers.size()); for (Signer signer : signers) { if (signer.getSigFileEntryNames().contains(entryName)) { entrySigners.add(signer); } } if (entrySigners.isEmpty()) { result.addError(Issue.JAR_SIG_ZIP_ENTRY_NOT_SIGNED, entryName); continue; } if (firstSignedEntrySigners == null) { firstSignedEntrySigners = entrySigners; firstSignedEntryName = entryName; } else if (!entrySigners.equals(firstSignedEntrySigners)) { result.addError( Issue.JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH, firstSignedEntryName, getSignerNames(firstSignedEntrySigners), entryName, getSignerNames(entrySigners)); continue; } List expectedDigests = new ArrayList<>( getDigestsToVerify( manifestSection, "-Digest", minSdkVersion, maxSdkVersion)); if (expectedDigests.isEmpty()) { result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName); continue; } MessageDigest[] mds = new MessageDigest[expectedDigests.size()]; for (int i = 0; i < expectedDigests.size(); i++) { mds[i] = getMessageDigest(expectedDigests.get(i).jcaDigestAlgorithm); } try { LocalFileRecord.outputUncompressedData( apk, cdRecord, cdOffsetInApk, DataSinks.asDataSink(mds)); } catch (ZipFormatException e) { throw new ApkFormatException("Malformed ZIP entry: " + entryName, e); } catch (IOException e) { throw new IOException("Failed to read entry: " + entryName, e); } for (int i = 0; i < expectedDigests.size(); i++) { NamedDigest expectedDigest = expectedDigests.get(i); byte[] actualDigest = mds[i].digest(); if (!Arrays.equals(expectedDigest.digest, actualDigest)) { result.addError( Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY, entryName, expectedDigest.jcaDigestAlgorithm, V1SchemeConstants.MANIFEST_ENTRY_NAME, Base64.getEncoder().encodeToString(actualDigest), Base64.getEncoder().encodeToString(expectedDigest.digest)); } } } if (firstSignedEntrySigners == null) { result.addError(Issue.JAR_SIG_NO_SIGNED_ZIP_ENTRIES); return Collections.emptySet(); } else { return new HashSet<>(firstSignedEntrySigners); } } private static List getSignerNames(List signers) { if (signers.isEmpty()) { return Collections.emptyList(); } List result = new ArrayList<>(signers.size()); for (Signer signer : signers) { result.add(signer.getName()); } return result; } private static MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException { return MessageDigest.getInstance(algorithm); } private static byte[] digest(String algorithm, byte[] data, int offset, int length) throws NoSuchAlgorithmException { MessageDigest md = getMessageDigest(algorithm); md.update(data, offset, length); return md.digest(); } private static byte[] digest(String algorithm, byte[] data) throws NoSuchAlgorithmException { return getMessageDigest(algorithm).digest(data); } public static class NamedDigest { public final String jcaDigestAlgorithm; public final byte[] digest; private NamedDigest(String jcaDigestAlgorithm, byte[] digest) { this.jcaDigestAlgorithm = jcaDigestAlgorithm; this.digest = digest; } } public static class Result { /** Whether the APK's JAR signature verifies. */ public boolean verified; /** List of APK's signers. These signers are used by Android. */ public final List signers = new ArrayList<>(); /** * Signers encountered in the APK but not included in the set of the APK's signers. These * signers are ignored by Android. */ public final List ignoredSigners = new ArrayList<>(); private final List mWarnings = new ArrayList<>(); private final List mErrors = new ArrayList<>(); private boolean containsErrors() { if (!mErrors.isEmpty()) { return true; } for (SignerInfo signer : signers) { if (signer.containsErrors()) { return true; } } return false; } private void addError(Issue msg, Object... parameters) { mErrors.add(new IssueWithParams(msg, parameters)); } private void addWarning(Issue msg, Object... parameters) { mWarnings.add(new IssueWithParams(msg, parameters)); } public List getErrors() { return mErrors; } public List getWarnings() { return mWarnings; } public static class SignerInfo { public final String name; public final String signatureFileName; public final String signatureBlockFileName; public final List certChain = new ArrayList<>(); private final List mWarnings = new ArrayList<>(); private final List mErrors = new ArrayList<>(); private SignerInfo( String name, String signatureBlockFileName, String signatureFileName) { this.name = name; this.signatureBlockFileName = signatureBlockFileName; this.signatureFileName = signatureFileName; } private boolean containsErrors() { return !mErrors.isEmpty(); } private void addError(Issue msg, Object... parameters) { mErrors.add(new IssueWithParams(msg, parameters)); } private void addWarning(Issue msg, Object... parameters) { mWarnings.add(new IssueWithParams(msg, parameters)); } public List getErrors() { return mErrors; } public List getWarnings() { return mWarnings; } } } private static class SignedAttributes { private Map> mAttrs; public SignedAttributes(Collection attrs) throws Pkcs7DecodingException { Map> result = new HashMap<>(attrs.size()); for (Attribute attr : attrs) { if (result.put(attr.attrType, attr.attrValues) != null) { throw new Pkcs7DecodingException("Duplicate signed attribute: " + attr.attrType); } } mAttrs = result; } private Asn1OpaqueObject getSingleValue(String attrOid) throws Pkcs7DecodingException { List values = mAttrs.get(attrOid); if ((values == null) || (values.isEmpty())) { return null; } if (values.size() > 1) { throw new Pkcs7DecodingException("Attribute " + attrOid + " has multiple values"); } return values.get(0); } public String getSingleObjectIdentifierValue(String attrOid) throws Pkcs7DecodingException { Asn1OpaqueObject value = getSingleValue(attrOid); if (value == null) { return null; } try { return Asn1BerParser.parse(value.getEncoded(), ObjectIdentifierChoice.class).value; } catch (Asn1DecodingException e) { throw new Pkcs7DecodingException("Failed to decode OBJECT IDENTIFIER", e); } } public byte[] getSingleOctetStringValue(String attrOid) throws Pkcs7DecodingException { Asn1OpaqueObject value = getSingleValue(attrOid); if (value == null) { return null; } try { return Asn1BerParser.parse(value.getEncoded(), OctetStringChoice.class).value; } catch (Asn1DecodingException e) { throw new Pkcs7DecodingException("Failed to decode OBJECT IDENTIFIER", e); } } } @Asn1Class(type = Asn1Type.CHOICE) public static class OctetStringChoice { @Asn1Field(type = Asn1Type.OCTET_STRING) public byte[] value; } @Asn1Class(type = Asn1Type.CHOICE) public static class ObjectIdentifierChoice { @Asn1Field(type = Asn1Type.OBJECT_IDENTIFIER) public String value; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v2_0100644 0000000 0000000 00000000034 14763776540 024234 xustar000000000 0000000 28 mtime=1741684064.5810000 src/main/java/com/android/apksig/internal/apk/v2/0040755 0000000 0000000 00000000000 14763776540 020661 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v2_V2SchemeConstants.java0100644 0000000 0000000 00000000034 14763776540 030406 xustar000000000 0000000 28 mtime=1741684064.5810000 src/main/java/com/android/apksig/internal/apk/v2/V2SchemeConstants.java0100644 0000000 0000000 00000001676 14763776540 025044 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v2; /** Constants used by the V2 Signature Scheme signing and verification. */ public class V2SchemeConstants { private V2SchemeConstants() {} public static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; public static final int STRIPPING_PROTECTION_ATTR_ID = 0xbeeff00d; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v2_V2SchemeSigner.java0100644 0000000 0000000 00000000034 14763776540 027661 xustar000000000 0000000 28 mtime=1741684064.5820000 src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java0100644 0000000 0000000 00000037230 14763776540 024312 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v2; import static com.android.apksig.Constants.MAX_APK_SIGNERS; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedElements; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeCertificates; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodePublicKey; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignerConfig; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.util.Pair; import com.android.apksig.util.DataSource; import com.android.apksig.util.RunnablesExecutor; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.interfaces.ECKey; import java.security.interfaces.RSAKey; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; /** * APK Signature Scheme v2 signer. * *

APK Signature Scheme v2 is a whole-file signature scheme which aims to protect every single * bit of the APK, as opposed to the JAR Signature Scheme which protects only the names and * uncompressed contents of ZIP entries. * * @see APK Signature Scheme v2 */ public abstract class V2SchemeSigner { /* * The two main goals of APK Signature Scheme v2 are: * 1. Detect any unauthorized modifications to the APK. This is achieved by making the signature * cover every byte of the APK being signed. * 2. Enable much faster signature and integrity verification. This is achieved by requiring * only a minimal amount of APK parsing before the signature is verified, thus completely * bypassing ZIP entry decompression and by making integrity verification parallelizable by * employing a hash tree. * * The generated signature block is wrapped into an APK Signing Block and inserted into the * original APK immediately before the start of ZIP Central Directory. This is to ensure that * JAR and ZIP parsers continue to work on the signed APK. The APK Signing Block is designed for * extensibility. For example, a future signature scheme could insert its signatures there as * well. The contract of the APK Signing Block is that all contents outside of the block must be * protected by signatures inside the block. */ public static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID; /** Hidden constructor to prevent instantiation. */ private V2SchemeSigner() {} /** * Gets the APK Signature Scheme v2 signature algorithms to be used for signing an APK using the * provided key. * * @param minSdkVersion minimum API Level of the platform on which the APK may be installed (see * AndroidManifest.xml minSdkVersion attribute). * @throws InvalidKeyException if the provided key is not suitable for signing APKs using APK * Signature Scheme v2 */ public static List getSuggestedSignatureAlgorithms(PublicKey signingKey, int minSdkVersion, boolean verityEnabled, boolean deterministicDsaSigning) throws InvalidKeyException { String keyAlgorithm = signingKey.getAlgorithm(); if ("RSA".equalsIgnoreCase(keyAlgorithm)) { // Use RSASSA-PKCS1-v1_5 signature scheme instead of RSASSA-PSS to guarantee // deterministic signatures which make life easier for OTA updates (fewer files // changed when deterministic signature schemes are used). // Pick a digest which is no weaker than the key. int modulusLengthBits = ((RSAKey) signingKey).getModulus().bitLength(); if (modulusLengthBits <= 3072) { // 3072-bit RSA is roughly 128-bit strong, meaning SHA-256 is a good fit. List algorithms = new ArrayList<>(); algorithms.add(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA256); if (verityEnabled) { algorithms.add(SignatureAlgorithm.VERITY_RSA_PKCS1_V1_5_WITH_SHA256); } return algorithms; } else { // Keys longer than 3072 bit need to be paired with a stronger digest to avoid the // digest being the weak link. SHA-512 is the next strongest supported digest. return Collections.singletonList(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA512); } } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) { // DSA is supported only with SHA-256. List algorithms = new ArrayList<>(); algorithms.add( deterministicDsaSigning ? SignatureAlgorithm.DETDSA_WITH_SHA256 : SignatureAlgorithm.DSA_WITH_SHA256); if (verityEnabled) { algorithms.add(SignatureAlgorithm.VERITY_DSA_WITH_SHA256); } return algorithms; } else if ("EC".equalsIgnoreCase(keyAlgorithm)) { // Pick a digest which is no weaker than the key. int keySizeBits = ((ECKey) signingKey).getParams().getOrder().bitLength(); if (keySizeBits <= 256) { // 256-bit Elliptic Curve is roughly 128-bit strong, meaning SHA-256 is a good fit. List algorithms = new ArrayList<>(); algorithms.add(SignatureAlgorithm.ECDSA_WITH_SHA256); if (verityEnabled) { algorithms.add(SignatureAlgorithm.VERITY_ECDSA_WITH_SHA256); } return algorithms; } else { // Keys longer than 256 bit need to be paired with a stronger digest to avoid the // digest being the weak link. SHA-512 is the next strongest supported digest. return Collections.singletonList(SignatureAlgorithm.ECDSA_WITH_SHA512); } } else { throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm); } } public static ApkSigningBlockUtils.SigningSchemeBlockAndDigests generateApkSignatureSchemeV2Block(RunnablesExecutor executor, DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, List signerConfigs, boolean v3SigningEnabled) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { return generateApkSignatureSchemeV2Block(executor, beforeCentralDir, centralDir, eocd, signerConfigs, v3SigningEnabled, null); } public static ApkSigningBlockUtils.SigningSchemeBlockAndDigests generateApkSignatureSchemeV2Block( RunnablesExecutor executor, DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, List signerConfigs, boolean v3SigningEnabled, List preservedV2SignerBlocks) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { Pair, Map> digestInfo = ApkSigningBlockUtils.computeContentDigests( executor, beforeCentralDir, centralDir, eocd, signerConfigs); return new ApkSigningBlockUtils.SigningSchemeBlockAndDigests( generateApkSignatureSchemeV2Block( digestInfo.getFirst(), digestInfo.getSecond(), v3SigningEnabled, preservedV2SignerBlocks), digestInfo.getSecond()); } private static Pair generateApkSignatureSchemeV2Block( List signerConfigs, Map contentDigests, boolean v3SigningEnabled, List preservedV2SignerBlocks) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { // FORMAT: // * length-prefixed sequence of length-prefixed signer blocks. if (signerConfigs.size() > MAX_APK_SIGNERS) { throw new IllegalArgumentException( "APK Signature Scheme v2 only supports a maximum of " + MAX_APK_SIGNERS + ", " + signerConfigs.size() + " provided"); } List signerBlocks = new ArrayList<>(signerConfigs.size()); if (preservedV2SignerBlocks != null && preservedV2SignerBlocks.size() > 0) { signerBlocks.addAll(preservedV2SignerBlocks); } int signerNumber = 0; for (SignerConfig signerConfig : signerConfigs) { signerNumber++; byte[] signerBlock; try { signerBlock = generateSignerBlock(signerConfig, contentDigests, v3SigningEnabled); } catch (InvalidKeyException e) { throw new InvalidKeyException("Signer #" + signerNumber + " failed", e); } catch (SignatureException e) { throw new SignatureException("Signer #" + signerNumber + " failed", e); } signerBlocks.add(signerBlock); } return Pair.of( encodeAsSequenceOfLengthPrefixedElements( new byte[][] { encodeAsSequenceOfLengthPrefixedElements(signerBlocks), }), V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID); } private static byte[] generateSignerBlock( SignerConfig signerConfig, Map contentDigests, boolean v3SigningEnabled) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { if (signerConfig.certificates.isEmpty()) { throw new SignatureException("No certificates configured for signer"); } PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey(); byte[] encodedPublicKey = encodePublicKey(publicKey); V2SignatureSchemeBlock.SignedData signedData = new V2SignatureSchemeBlock.SignedData(); try { signedData.certificates = encodeCertificates(signerConfig.certificates); } catch (CertificateEncodingException e) { throw new SignatureException("Failed to encode certificates", e); } List> digests = new ArrayList<>(signerConfig.signatureAlgorithms.size()); for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) { ContentDigestAlgorithm contentDigestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm(); byte[] contentDigest = contentDigests.get(contentDigestAlgorithm); if (contentDigest == null) { throw new RuntimeException( contentDigestAlgorithm + " content digest for " + signatureAlgorithm + " not computed"); } digests.add(Pair.of(signatureAlgorithm.getId(), contentDigest)); } signedData.digests = digests; signedData.additionalAttributes = generateAdditionalAttributes(v3SigningEnabled); V2SignatureSchemeBlock.Signer signer = new V2SignatureSchemeBlock.Signer(); // FORMAT: // * length-prefixed sequence of length-prefixed digests: // * uint32: signature algorithm ID // * length-prefixed bytes: digest of contents // * length-prefixed sequence of certificates: // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded). // * length-prefixed sequence of length-prefixed additional attributes: // * uint32: ID // * (length - 4) bytes: value signer.signedData = encodeAsSequenceOfLengthPrefixedElements( new byte[][] { encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( signedData.digests), encodeAsSequenceOfLengthPrefixedElements(signedData.certificates), signedData.additionalAttributes, new byte[0], }); signer.publicKey = encodedPublicKey; signer.signatures = new ArrayList<>(); signer.signatures = ApkSigningBlockUtils.generateSignaturesOverData(signerConfig, signer.signedData); // FORMAT: // * length-prefixed signed data // * length-prefixed sequence of length-prefixed signatures: // * uint32: signature algorithm ID // * length-prefixed bytes: signature of signed data // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded) return encodeAsSequenceOfLengthPrefixedElements( new byte[][] { signer.signedData, encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( signer.signatures), signer.publicKey, }); } private static byte[] generateAdditionalAttributes(boolean v3SigningEnabled) { if (v3SigningEnabled) { // FORMAT (little endian): // * length-prefixed bytes: attribute pair // * uint32: ID - STRIPPING_PROTECTION_ATTR_ID in this case // * uint32: value - 3 (v3 signature scheme id) in this case int payloadSize = 4 + 4 + 4; ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.putInt(payloadSize - 4); result.putInt(V2SchemeConstants.STRIPPING_PROTECTION_ATTR_ID); result.putInt(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); return result.array(); } else { return new byte[0]; } } private static final class V2SignatureSchemeBlock { private static final class Signer { public byte[] signedData; public List> signatures; public byte[] publicKey; } private static final class SignedData { public List> digests; public List certificates; public byte[] additionalAttributes; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v2_V2SchemeVerifier.java0100644 0000000 0000000 00000000034 14763776540 030205 xustar000000000 0000000 28 mtime=1741684064.5830000 src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java0100644 0000000 0000000 00000053251 14763776540 024637 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v2; import static com.android.apksig.Constants.MAX_APK_SIGNERS; import com.android.apksig.ApkVerifier.Issue; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.util.ByteBufferUtils; import com.android.apksig.internal.util.X509CertificateUtils; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import com.android.apksig.util.DataSource; import com.android.apksig.util.RunnablesExecutor; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * APK Signature Scheme v2 verifier. * *

APK Signature Scheme v2 is a whole-file signature scheme which aims to protect every single * bit of the APK, as opposed to the JAR Signature Scheme which protects only the names and * uncompressed contents of ZIP entries. * * @see APK Signature Scheme v2 */ public abstract class V2SchemeVerifier { /** Hidden constructor to prevent instantiation. */ private V2SchemeVerifier() {} /** * Verifies the provided APK's APK Signature Scheme v2 signatures and returns the result of * verification. The APK must be considered verified only if * {@link ApkSigningBlockUtils.Result#verified} is * {@code true}. If verification fails, the result will contain errors -- see * {@link ApkSigningBlockUtils.Result#getErrors()}. * *

Verification succeeds iff the APK's APK Signature Scheme v2 signatures are expected to * verify on all Android platform versions in the {@code [minSdkVersion, maxSdkVersion]} range. * If the APK's signature is expected to not verify on any of the specified platform versions, * this method returns a result with one or more errors and whose * {@code Result.verified == false}, or this method throws an exception. * * @throws ApkFormatException if the APK is malformed * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a * required cryptographic algorithm implementation is missing * @throws ApkSigningBlockUtils.SignatureNotFoundException if no APK Signature Scheme v2 * signatures are found * @throws IOException if an I/O error occurs when reading the APK */ public static ApkSigningBlockUtils.Result verify( RunnablesExecutor executor, DataSource apk, ApkUtils.ZipSections zipSections, Map supportedApkSigSchemeNames, Set foundSigSchemeIds, int minSdkVersion, int maxSdkVersion) throws IOException, ApkFormatException, NoSuchAlgorithmException, ApkSigningBlockUtils.SignatureNotFoundException { ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections, V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID , result); DataSource beforeApkSigningBlock = apk.slice(0, signatureInfo.apkSigningBlockOffset); DataSource centralDir = apk.slice( signatureInfo.centralDirOffset, signatureInfo.eocdOffset - signatureInfo.centralDirOffset); ByteBuffer eocd = signatureInfo.eocd; verify(executor, beforeApkSigningBlock, signatureInfo.signatureBlock, centralDir, eocd, supportedApkSigSchemeNames, foundSigSchemeIds, minSdkVersion, maxSdkVersion, result); return result; } /** * Verifies the provided APK's v2 signatures and outputs the results into the provided * {@code result}. APK is considered verified only if there are no errors reported in the * {@code result}. See {@link #verify(RunnablesExecutor, DataSource, ApkUtils.ZipSections, Map, * Set, int, int)} for more information about the contract of this method. * * @param result result populated by this method with interesting information about the APK, * such as information about signers, and verification errors and warnings. */ private static void verify( RunnablesExecutor executor, DataSource beforeApkSigningBlock, ByteBuffer apkSignatureSchemeV2Block, DataSource centralDir, ByteBuffer eocd, Map supportedApkSigSchemeNames, Set foundSigSchemeIds, int minSdkVersion, int maxSdkVersion, ApkSigningBlockUtils.Result result) throws IOException, NoSuchAlgorithmException { Set contentDigestsToVerify = new HashSet<>(1); parseSigners( apkSignatureSchemeV2Block, contentDigestsToVerify, supportedApkSigSchemeNames, foundSigSchemeIds, minSdkVersion, maxSdkVersion, result); if (result.containsErrors()) { return; } ApkSigningBlockUtils.verifyIntegrity( executor, beforeApkSigningBlock, centralDir, eocd, contentDigestsToVerify, result); if (!result.containsErrors()) { result.verified = true; } } /** * Parses each signer in the provided APK Signature Scheme v2 block and populates corresponding * {@code signerInfos} of the provided {@code result}. * *

This verifies signatures over {@code signed-data} block contained in each signer block. * However, this does not verify the integrity of the rest of the APK but rather simply reports * the expected digests of the rest of the APK (see {@code contentDigestsToVerify}). * *

This method adds one or more errors to the {@code result} if a verification error is * expected to be encountered on an Android platform version in the * {@code [minSdkVersion, maxSdkVersion]} range. */ public static void parseSigners( ByteBuffer apkSignatureSchemeV2Block, Set contentDigestsToVerify, Map supportedApkSigSchemeNames, Set foundApkSigSchemeIds, int minSdkVersion, int maxSdkVersion, ApkSigningBlockUtils.Result result) throws NoSuchAlgorithmException { ByteBuffer signers; try { signers = ApkSigningBlockUtils.getLengthPrefixedSlice(apkSignatureSchemeV2Block); } catch (ApkFormatException e) { result.addError(Issue.V2_SIG_MALFORMED_SIGNERS); return; } if (!signers.hasRemaining()) { result.addError(Issue.V2_SIG_NO_SIGNERS); return; } CertificateFactory certFactory; try { certFactory = CertificateFactory.getInstance("X.509"); } catch (CertificateException e) { throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); } int signerCount = 0; while (signers.hasRemaining()) { int signerIndex = signerCount; signerCount++; ApkSigningBlockUtils.Result.SignerInfo signerInfo = new ApkSigningBlockUtils.Result.SignerInfo(); signerInfo.index = signerIndex; result.signers.add(signerInfo); try { ByteBuffer signer = ApkSigningBlockUtils.getLengthPrefixedSlice(signers); parseSigner( signer, certFactory, signerInfo, contentDigestsToVerify, supportedApkSigSchemeNames, foundApkSigSchemeIds, minSdkVersion, maxSdkVersion); } catch (ApkFormatException | BufferUnderflowException e) { signerInfo.addError(Issue.V2_SIG_MALFORMED_SIGNER); return; } } if (signerCount > MAX_APK_SIGNERS) { result.addError(Issue.V2_SIG_MAX_SIGNATURES_EXCEEDED, MAX_APK_SIGNERS, signerCount); } } /** * Parses the provided signer block and populates the {@code result}. * *

This verifies signatures over {@code signed-data} contained in this block but does not * verify the integrity of the rest of the APK. To facilitate APK integrity verification, this * method adds the {@code contentDigestsToVerify}. These digests can then be used to verify the * integrity of the APK. * *

This method adds one or more errors to the {@code result} if a verification error is * expected to be encountered on an Android platform version in the * {@code [minSdkVersion, maxSdkVersion]} range. */ private static void parseSigner( ByteBuffer signerBlock, CertificateFactory certFactory, ApkSigningBlockUtils.Result.SignerInfo result, Set contentDigestsToVerify, Map supportedApkSigSchemeNames, Set foundApkSigSchemeIds, int minSdkVersion, int maxSdkVersion) throws ApkFormatException, NoSuchAlgorithmException { ByteBuffer signedData = ApkSigningBlockUtils.getLengthPrefixedSlice(signerBlock); byte[] signedDataBytes = new byte[signedData.remaining()]; signedData.get(signedDataBytes); signedData.flip(); result.signedData = signedDataBytes; ByteBuffer signatures = ApkSigningBlockUtils.getLengthPrefixedSlice(signerBlock); byte[] publicKeyBytes = ApkSigningBlockUtils.readLengthPrefixedByteArray(signerBlock); // Parse the signatures block and identify supported signatures int signatureCount = 0; List supportedSignatures = new ArrayList<>(1); while (signatures.hasRemaining()) { signatureCount++; try { ByteBuffer signature = ApkSigningBlockUtils.getLengthPrefixedSlice(signatures); int sigAlgorithmId = signature.getInt(); byte[] sigBytes = ApkSigningBlockUtils.readLengthPrefixedByteArray(signature); result.signatures.add( new ApkSigningBlockUtils.Result.SignerInfo.Signature( sigAlgorithmId, sigBytes)); SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId); if (signatureAlgorithm == null) { result.addWarning(Issue.V2_SIG_UNKNOWN_SIG_ALGORITHM, sigAlgorithmId); continue; } supportedSignatures.add( new ApkSigningBlockUtils.SupportedSignature(signatureAlgorithm, sigBytes)); } catch (ApkFormatException | BufferUnderflowException e) { result.addError(Issue.V2_SIG_MALFORMED_SIGNATURE, signatureCount); return; } } if (result.signatures.isEmpty()) { result.addError(Issue.V2_SIG_NO_SIGNATURES); return; } // Verify signatures over signed-data block using the public key List signaturesToVerify = null; try { signaturesToVerify = ApkSigningBlockUtils.getSignaturesToVerify( supportedSignatures, minSdkVersion, maxSdkVersion); } catch (ApkSigningBlockUtils.NoSupportedSignaturesException e) { result.addError(Issue.V2_SIG_NO_SUPPORTED_SIGNATURES, e); return; } for (ApkSigningBlockUtils.SupportedSignature signature : signaturesToVerify) { SignatureAlgorithm signatureAlgorithm = signature.algorithm; String jcaSignatureAlgorithm = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst(); AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond(); String keyAlgorithm = signatureAlgorithm.getJcaKeyAlgorithm(); PublicKey publicKey; try { publicKey = KeyFactory.getInstance(keyAlgorithm).generatePublic( new X509EncodedKeySpec(publicKeyBytes)); } catch (Exception e) { result.addError(Issue.V2_SIG_MALFORMED_PUBLIC_KEY, e); return; } try { Signature sig = Signature.getInstance(jcaSignatureAlgorithm); sig.initVerify(publicKey); if (jcaSignatureAlgorithmParams != null) { sig.setParameter(jcaSignatureAlgorithmParams); } signedData.position(0); sig.update(signedData); byte[] sigBytes = signature.signature; if (!sig.verify(sigBytes)) { result.addError(Issue.V2_SIG_DID_NOT_VERIFY, signatureAlgorithm); return; } result.verifiedSignatures.put(signatureAlgorithm, sigBytes); contentDigestsToVerify.add(signatureAlgorithm.getContentDigestAlgorithm()); } catch (InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) { result.addError(Issue.V2_SIG_VERIFY_EXCEPTION, signatureAlgorithm, e); return; } } // At least one signature over signedData has verified. We can now parse signed-data. signedData.position(0); ByteBuffer digests = ApkSigningBlockUtils.getLengthPrefixedSlice(signedData); ByteBuffer certificates = ApkSigningBlockUtils.getLengthPrefixedSlice(signedData); ByteBuffer additionalAttributes = ApkSigningBlockUtils.getLengthPrefixedSlice(signedData); // Parse the certificates block int certificateIndex = -1; while (certificates.hasRemaining()) { certificateIndex++; byte[] encodedCert = ApkSigningBlockUtils.readLengthPrefixedByteArray(certificates); X509Certificate certificate; try { certificate = X509CertificateUtils.generateCertificate(encodedCert, certFactory); } catch (CertificateException e) { result.addError( Issue.V2_SIG_MALFORMED_CERTIFICATE, certificateIndex, certificateIndex + 1, e); return; } // Wrap the cert so that the result's getEncoded returns exactly the original encoded // form. Without this, getEncoded may return a different form from what was stored in // the signature. This is because some X509Certificate(Factory) implementations // re-encode certificates. certificate = new GuaranteedEncodedFormX509Certificate(certificate, encodedCert); result.certs.add(certificate); } if (result.certs.isEmpty()) { result.addError(Issue.V2_SIG_NO_CERTIFICATES); return; } X509Certificate mainCertificate = result.certs.get(0); byte[] certificatePublicKeyBytes; try { certificatePublicKeyBytes = ApkSigningBlockUtils.encodePublicKey( mainCertificate.getPublicKey()); } catch (InvalidKeyException e) { System.out.println("Caught an exception encoding the public key: " + e); e.printStackTrace(); certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded(); } if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) { result.addError( Issue.V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD, ApkSigningBlockUtils.toHex(certificatePublicKeyBytes), ApkSigningBlockUtils.toHex(publicKeyBytes)); return; } // Parse the digests block int digestCount = 0; while (digests.hasRemaining()) { digestCount++; try { ByteBuffer digest = ApkSigningBlockUtils.getLengthPrefixedSlice(digests); int sigAlgorithmId = digest.getInt(); byte[] digestBytes = ApkSigningBlockUtils.readLengthPrefixedByteArray(digest); result.contentDigests.add( new ApkSigningBlockUtils.Result.SignerInfo.ContentDigest( sigAlgorithmId, digestBytes)); } catch (ApkFormatException | BufferUnderflowException e) { result.addError(Issue.V2_SIG_MALFORMED_DIGEST, digestCount); return; } } List sigAlgsFromSignaturesRecord = new ArrayList<>(result.signatures.size()); for (ApkSigningBlockUtils.Result.SignerInfo.Signature signature : result.signatures) { sigAlgsFromSignaturesRecord.add(signature.getAlgorithmId()); } List sigAlgsFromDigestsRecord = new ArrayList<>(result.contentDigests.size()); for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest digest : result.contentDigests) { sigAlgsFromDigestsRecord.add(digest.getSignatureAlgorithmId()); } if (!sigAlgsFromSignaturesRecord.equals(sigAlgsFromDigestsRecord)) { result.addError( Issue.V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS, sigAlgsFromSignaturesRecord, sigAlgsFromDigestsRecord); return; } // Parse the additional attributes block. int additionalAttributeCount = 0; Set supportedApkSigSchemeIds = supportedApkSigSchemeNames.keySet(); Set supportedExpectedApkSigSchemeIds = new HashSet<>(1); while (additionalAttributes.hasRemaining()) { additionalAttributeCount++; try { ByteBuffer attribute = ApkSigningBlockUtils.getLengthPrefixedSlice(additionalAttributes); int id = attribute.getInt(); byte[] value = ByteBufferUtils.toByteArray(attribute); result.additionalAttributes.add( new ApkSigningBlockUtils.Result.SignerInfo.AdditionalAttribute(id, value)); switch (id) { case V2SchemeConstants.STRIPPING_PROTECTION_ATTR_ID: // stripping protection added when signing with a newer scheme int foundId = ByteBuffer.wrap(value).order( ByteOrder.LITTLE_ENDIAN).getInt(); if (supportedApkSigSchemeIds.contains(foundId)) { supportedExpectedApkSigSchemeIds.add(foundId); } else { result.addWarning( Issue.V2_SIG_UNKNOWN_APK_SIG_SCHEME_ID, result.index, foundId); } break; default: result.addWarning(Issue.V2_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE, id); } } catch (ApkFormatException | BufferUnderflowException e) { result.addError( Issue.V2_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE, additionalAttributeCount); return; } } // make sure that all known IDs indicated in stripping protection have already verified for (int id : supportedExpectedApkSigSchemeIds) { if (!foundApkSigSchemeIds.contains(id)) { String apkSigSchemeName = supportedApkSigSchemeNames.get(id); result.addError( Issue.V2_SIG_MISSING_APK_SIG_REFERENCED, result.index, apkSigSchemeName); } } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v3_0100644 0000000 0000000 00000000034 14763776540 024235 xustar000000000 0000000 28 mtime=1741684064.5840000 src/main/java/com/android/apksig/internal/apk/v3/0040755 0000000 0000000 00000000000 14763776540 020662 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v3_V3SchemeConstants.java0100644 0000000 0000000 00000000034 14763776540 030410 xustar000000000 0000000 28 mtime=1741684064.5840000 src/main/java/com/android/apksig/internal/apk/v3/V3SchemeConstants.java0100644 0000000 0000000 00000005762 14763776540 025046 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v3; import com.android.apksig.internal.util.AndroidSdkVersion; /** Constants used by the V3 Signature Scheme signing and verification. */ public class V3SchemeConstants { private V3SchemeConstants() {} public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; public static final int APK_SIGNATURE_SCHEME_V31_BLOCK_ID = 0x1b93ad61; public static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c; public static final int MIN_SDK_WITH_V3_SUPPORT = AndroidSdkVersion.P; public static final int MIN_SDK_WITH_V31_SUPPORT = AndroidSdkVersion.T; /** * By default, APK signing key rotation will target T, but packages that have previously * rotated can continue rotating on pre-T by specifying an SDK version <= 32 as the * --rotation-min-sdk-version parameter when using apksigner or when invoking * {@link com.android.apksig.ApkSigner.Builder#setMinSdkVersionForRotation(int)}. */ public static final int DEFAULT_ROTATION_MIN_SDK_VERSION = AndroidSdkVersion.T; /** * This attribute is intended to be written to the V3.0 signer block as an additional attribute * whose value is the minimum SDK version supported for rotation by the V3.1 signing block. If * this value is set to X and a v3.1 signing block does not exist, or the minimum SDK version * for rotation in the v3.1 signing block is not X, then the APK should be rejected. */ public static final int ROTATION_MIN_SDK_VERSION_ATTR_ID = 0x559f8b02; /** * This attribute is written to the V3.1 signer block as an additional attribute to signify that * the rotation-min-sdk-version is targeting a development release. This is required to support * testing rotation on new development releases as the previous platform release SDK version * is used as the development release SDK version until the development release SDK is * finalized. */ public static final int ROTATION_ON_DEV_RELEASE_ATTR_ID = 0xc2a6b3ba; /** * The current development release; rotation / signing configs targeting this release should * be written with the {@link #PROD_RELEASE} SDK version and the dev release attribute. */ public static final int DEV_RELEASE = AndroidSdkVersion.U; /** * The current production release. */ public static final int PROD_RELEASE = AndroidSdkVersion.T; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v3_V3SchemeSigner.java0100644 0000000 0000000 00000000034 14763776540 027663 xustar000000000 0000000 28 mtime=1741684064.5850000 src/main/java/com/android/apksig/internal/apk/v3/V3SchemeSigner.java0100644 0000000 0000000 00000060060 14763776540 024311 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v3; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsLengthPrefixedElement; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedElements; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeCertificates; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodePublicKey; import com.android.apksig.SigningCertificateLineage; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils.SigningSchemeBlockAndDigests; import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignerConfig; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.util.Pair; import com.android.apksig.util.DataSource; import com.android.apksig.util.RunnablesExecutor; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.interfaces.ECKey; import java.security.interfaces.RSAKey; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.OptionalInt; /** * APK Signature Scheme v3 signer. * *

APK Signature Scheme v3 builds upon APK Signature Scheme v3, and maintains all of the APK * Signature Scheme v2 goals. * * @see APK Signature Scheme v2 *

The main contribution of APK Signature Scheme v3 is the introduction of the {@link * SigningCertificateLineage}, which enables an APK to change its signing certificate as long as * it can prove the new siging certificate was signed by the old. */ public class V3SchemeSigner { public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; public static final int PROOF_OF_ROTATION_ATTR_ID = V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID; private final RunnablesExecutor mExecutor; private final DataSource mBeforeCentralDir; private final DataSource mCentralDir; private final DataSource mEocd; private final List mSignerConfigs; private final int mBlockId; private final OptionalInt mOptionalV31MinSdkVersion; private final boolean mRotationTargetsDevRelease; private V3SchemeSigner(DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, List signerConfigs, RunnablesExecutor executor, int blockId, OptionalInt optionalV31MinSdkVersion, boolean rotationTargetsDevRelease) { mBeforeCentralDir = beforeCentralDir; mCentralDir = centralDir; mEocd = eocd; mSignerConfigs = signerConfigs; mExecutor = executor; mBlockId = blockId; mOptionalV31MinSdkVersion = optionalV31MinSdkVersion; mRotationTargetsDevRelease = rotationTargetsDevRelease; } /** * Gets the APK Signature Scheme v3 signature algorithms to be used for signing an APK using the * provided key. * * @param minSdkVersion minimum API Level of the platform on which the APK may be installed (see * AndroidManifest.xml minSdkVersion attribute). * @throws InvalidKeyException if the provided key is not suitable for signing APKs using APK * Signature Scheme v3 */ public static List getSuggestedSignatureAlgorithms(PublicKey signingKey, int minSdkVersion, boolean verityEnabled, boolean deterministicDsaSigning) throws InvalidKeyException { String keyAlgorithm = signingKey.getAlgorithm(); if ("RSA".equalsIgnoreCase(keyAlgorithm)) { // Use RSASSA-PKCS1-v1_5 signature scheme instead of RSASSA-PSS to guarantee // deterministic signatures which make life easier for OTA updates (fewer files // changed when deterministic signature schemes are used). // Pick a digest which is no weaker than the key. int modulusLengthBits = ((RSAKey) signingKey).getModulus().bitLength(); if (modulusLengthBits <= 3072) { // 3072-bit RSA is roughly 128-bit strong, meaning SHA-256 is a good fit. List algorithms = new ArrayList<>(); algorithms.add(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA256); if (verityEnabled) { algorithms.add(SignatureAlgorithm.VERITY_RSA_PKCS1_V1_5_WITH_SHA256); } return algorithms; } else { // Keys longer than 3072 bit need to be paired with a stronger digest to avoid the // digest being the weak link. SHA-512 is the next strongest supported digest. return Collections.singletonList(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA512); } } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) { // DSA is supported only with SHA-256. List algorithms = new ArrayList<>(); algorithms.add( deterministicDsaSigning ? SignatureAlgorithm.DETDSA_WITH_SHA256 : SignatureAlgorithm.DSA_WITH_SHA256); if (verityEnabled) { algorithms.add(SignatureAlgorithm.VERITY_DSA_WITH_SHA256); } return algorithms; } else if ("EC".equalsIgnoreCase(keyAlgorithm)) { // Pick a digest which is no weaker than the key. int keySizeBits = ((ECKey) signingKey).getParams().getOrder().bitLength(); if (keySizeBits <= 256) { // 256-bit Elliptic Curve is roughly 128-bit strong, meaning SHA-256 is a good fit. List algorithms = new ArrayList<>(); algorithms.add(SignatureAlgorithm.ECDSA_WITH_SHA256); if (verityEnabled) { algorithms.add(SignatureAlgorithm.VERITY_ECDSA_WITH_SHA256); } return algorithms; } else { // Keys longer than 256 bit need to be paired with a stronger digest to avoid the // digest being the weak link. SHA-512 is the next strongest supported digest. return Collections.singletonList(SignatureAlgorithm.ECDSA_WITH_SHA512); } } else { throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm); } } public static SigningSchemeBlockAndDigests generateApkSignatureSchemeV3Block( RunnablesExecutor executor, DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, List signerConfigs) throws IOException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { return new V3SchemeSigner.Builder(beforeCentralDir, centralDir, eocd, signerConfigs) .setRunnablesExecutor(executor) .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID) .build() .generateApkSignatureSchemeV3BlockAndDigests(); } public static byte[] generateV3SignerAttribute( SigningCertificateLineage signingCertificateLineage) { // FORMAT (little endian): // * length-prefixed bytes: attribute pair // * uint32: ID // * bytes: value - encoded V3 SigningCertificateLineage byte[] encodedLineage = signingCertificateLineage.encodeSigningCertificateLineage(); int payloadSize = 4 + 4 + encodedLineage.length; ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.putInt(4 + encodedLineage.length); result.putInt(V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID); result.put(encodedLineage); return result.array(); } private static byte[] generateV3RotationMinSdkVersionStrippingProtectionAttribute( int rotationMinSdkVersion) { // FORMAT (little endian): // * length-prefixed bytes: attribute pair // * uint32: ID // * bytes: value - int32 representing minimum SDK version for rotation int payloadSize = 4 + 4 + 4; ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.putInt(payloadSize - 4); result.putInt(V3SchemeConstants.ROTATION_MIN_SDK_VERSION_ATTR_ID); result.putInt(rotationMinSdkVersion); return result.array(); } private static byte[] generateV31RotationTargetsDevReleaseAttribute() { // FORMAT (little endian): // * length-prefixed bytes: attribute pair // * uint32: ID // * bytes: value - No value is used for this attribute int payloadSize = 4 + 4; ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.putInt(payloadSize - 4); result.putInt(V3SchemeConstants.ROTATION_ON_DEV_RELEASE_ATTR_ID); return result.array(); } /** * Generates and returns a new {@link SigningSchemeBlockAndDigests} containing the V3.x * signing scheme block and digests based on the parameters provided to the {@link Builder}. * * @throws IOException if an I/O error occurs * @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is * missing * @throws InvalidKeyException if the X.509 encoded form of the public key cannot be obtained * @throws SignatureException if an error occurs when computing digests or generating * signatures */ public SigningSchemeBlockAndDigests generateApkSignatureSchemeV3BlockAndDigests() throws IOException, InvalidKeyException, NoSuchAlgorithmException, SignatureException { Pair, Map> digestInfo = ApkSigningBlockUtils.computeContentDigests( mExecutor, mBeforeCentralDir, mCentralDir, mEocd, mSignerConfigs); return new SigningSchemeBlockAndDigests( generateApkSignatureSchemeV3Block(digestInfo.getSecond()), digestInfo.getSecond()); } private Pair generateApkSignatureSchemeV3Block( Map contentDigests) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { // FORMAT: // * length-prefixed sequence of length-prefixed signer blocks. List signerBlocks = new ArrayList<>(mSignerConfigs.size()); int signerNumber = 0; for (SignerConfig signerConfig : mSignerConfigs) { signerNumber++; byte[] signerBlock; try { signerBlock = generateSignerBlock(signerConfig, contentDigests); } catch (InvalidKeyException e) { throw new InvalidKeyException("Signer #" + signerNumber + " failed", e); } catch (SignatureException e) { throw new SignatureException("Signer #" + signerNumber + " failed", e); } signerBlocks.add(signerBlock); } return Pair.of( encodeAsSequenceOfLengthPrefixedElements( new byte[][] { encodeAsSequenceOfLengthPrefixedElements(signerBlocks), }), mBlockId); } private byte[] generateSignerBlock( SignerConfig signerConfig, Map contentDigests) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { if (signerConfig.certificates.isEmpty()) { throw new SignatureException("No certificates configured for signer"); } PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey(); byte[] encodedPublicKey = encodePublicKey(publicKey); V3SignatureSchemeBlock.SignedData signedData = new V3SignatureSchemeBlock.SignedData(); try { signedData.certificates = encodeCertificates(signerConfig.certificates); } catch (CertificateEncodingException e) { throw new SignatureException("Failed to encode certificates", e); } List> digests = new ArrayList<>(signerConfig.signatureAlgorithms.size()); for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) { ContentDigestAlgorithm contentDigestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm(); byte[] contentDigest = contentDigests.get(contentDigestAlgorithm); if (contentDigest == null) { throw new RuntimeException( contentDigestAlgorithm + " content digest for " + signatureAlgorithm + " not computed"); } digests.add(Pair.of(signatureAlgorithm.getId(), contentDigest)); } signedData.digests = digests; signedData.minSdkVersion = signerConfig.minSdkVersion; signedData.maxSdkVersion = signerConfig.maxSdkVersion; signedData.additionalAttributes = generateAdditionalAttributes(signerConfig); V3SignatureSchemeBlock.Signer signer = new V3SignatureSchemeBlock.Signer(); signer.signedData = encodeSignedData(signedData); signer.minSdkVersion = signerConfig.minSdkVersion; signer.maxSdkVersion = signerConfig.maxSdkVersion; signer.publicKey = encodedPublicKey; signer.signatures = ApkSigningBlockUtils.generateSignaturesOverData(signerConfig, signer.signedData); return encodeSigner(signer); } private byte[] encodeSigner(V3SignatureSchemeBlock.Signer signer) { byte[] signedData = encodeAsLengthPrefixedElement(signer.signedData); byte[] signatures = encodeAsLengthPrefixedElement( encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( signer.signatures)); byte[] publicKey = encodeAsLengthPrefixedElement(signer.publicKey); // FORMAT: // * length-prefixed signed data // * uint32: minSdkVersion // * uint32: maxSdkVersion // * length-prefixed sequence of length-prefixed signatures: // * uint32: signature algorithm ID // * length-prefixed bytes: signature of signed data // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded) int payloadSize = signedData.length + 4 + 4 + signatures.length + publicKey.length; ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.put(signedData); result.putInt(signer.minSdkVersion); result.putInt(signer.maxSdkVersion); result.put(signatures); result.put(publicKey); return result.array(); } private byte[] encodeSignedData(V3SignatureSchemeBlock.SignedData signedData) { byte[] digests = encodeAsLengthPrefixedElement( encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( signedData.digests)); byte[] certs = encodeAsLengthPrefixedElement( encodeAsSequenceOfLengthPrefixedElements(signedData.certificates)); byte[] attributes = encodeAsLengthPrefixedElement(signedData.additionalAttributes); // FORMAT: // * length-prefixed sequence of length-prefixed digests: // * uint32: signature algorithm ID // * length-prefixed bytes: digest of contents // * length-prefixed sequence of certificates: // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded). // * uint-32: minSdkVersion // * uint-32: maxSdkVersion // * length-prefixed sequence of length-prefixed additional attributes: // * uint32: ID // * (length - 4) bytes: value // * uint32: Proof-of-rotation ID: 0x3ba06f8c // * length-prefixed roof-of-rotation structure int payloadSize = digests.length + certs.length + 4 + 4 + attributes.length; ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.put(digests); result.put(certs); result.putInt(signedData.minSdkVersion); result.putInt(signedData.maxSdkVersion); result.put(attributes); return result.array(); } private byte[] generateAdditionalAttributes(SignerConfig signerConfig) { List attributes = new ArrayList<>(); if (signerConfig.signingCertificateLineage != null) { attributes.add(generateV3SignerAttribute(signerConfig.signingCertificateLineage)); } if ((mRotationTargetsDevRelease || signerConfig.signerTargetsDevRelease) && mBlockId == V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID) { attributes.add(generateV31RotationTargetsDevReleaseAttribute()); } if (mOptionalV31MinSdkVersion.isPresent() && mBlockId == V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID) { attributes.add(generateV3RotationMinSdkVersionStrippingProtectionAttribute( mOptionalV31MinSdkVersion.getAsInt())); } int attributesSize = attributes.stream().mapToInt(attribute -> attribute.length).sum(); byte[] attributesBuffer = new byte[attributesSize]; if (attributesSize == 0) { return new byte[0]; } int index = 0; for (byte[] attribute : attributes) { System.arraycopy(attribute, 0, attributesBuffer, index, attribute.length); index += attribute.length; } return attributesBuffer; } private static final class V3SignatureSchemeBlock { private static final class Signer { public byte[] signedData; public int minSdkVersion; public int maxSdkVersion; public List> signatures; public byte[] publicKey; } private static final class SignedData { public List> digests; public List certificates; public int minSdkVersion; public int maxSdkVersion; public byte[] additionalAttributes; } } /** Builder of {@link V3SchemeSigner} instances. */ public static class Builder { private final DataSource mBeforeCentralDir; private final DataSource mCentralDir; private final DataSource mEocd; private final List mSignerConfigs; private RunnablesExecutor mExecutor = RunnablesExecutor.MULTI_THREADED; private int mBlockId = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; private OptionalInt mOptionalV31MinSdkVersion = OptionalInt.empty(); private boolean mRotationTargetsDevRelease = false; /** * Instantiates a new {@code Builder} with an APK's {@code beforeCentralDir}, {@code * centralDir}, and {@code eocd}, along with a {@link List} of {@code signerConfigs} to * be used to sign the APK. */ public Builder(DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, List signerConfigs) { mBeforeCentralDir = beforeCentralDir; mCentralDir = centralDir; mEocd = eocd; mSignerConfigs = signerConfigs; } /** * Sets the {@link RunnablesExecutor} to be used when computing the APK's content digests. */ public Builder setRunnablesExecutor(RunnablesExecutor executor) { mExecutor = executor; return this; } /** * Sets the {@code blockId} to be used for the V3 signature block. * *

This {@code V3SchemeSigner} currently supports the block IDs for the {@link * V3SchemeConstants#APK_SIGNATURE_SCHEME_V3_BLOCK_ID v3.0} and {@link * V3SchemeConstants#APK_SIGNATURE_SCHEME_V31_BLOCK_ID v3.1} signature schemes. */ public Builder setBlockId(int blockId) { mBlockId = blockId; return this; } /** * Sets the {@code rotationMinSdkVersion} to be written as an additional attribute in each * signer's block. * *

This value provides stripping protection to ensure a v3.1 signing block with rotation * is not modified or removed from the APK's signature block. */ public Builder setRotationMinSdkVersion(int rotationMinSdkVersion) { return setMinSdkVersionForV31(rotationMinSdkVersion); } /** * Sets the {@code minSdkVersion} to be written as an additional attribute in each * signer's block. * *

This value provides the stripping protection to ensure a v3.1 signing block is not * modified or removed from the APK's signature block. */ public Builder setMinSdkVersionForV31(int minSdkVersion) { if (minSdkVersion == V3SchemeConstants.DEV_RELEASE) { minSdkVersion = V3SchemeConstants.PROD_RELEASE; } mOptionalV31MinSdkVersion = OptionalInt.of(minSdkVersion); return this; } /** * Sets whether the minimum SDK version of a signer is intended to target a development * release; this is primarily required after the T SDK is finalized, and an APK needs to * target U during its development cycle for rotation. * *

This is only required after the T SDK is finalized since S and earlier releases do * not know about the V3.1 block ID, but once T is released and work begins on U, U will * use the SDK version of T during development. A signer with a minimum SDK version of T's * SDK version along with setting {@code enabled} to true will allow an APK to use the * rotated key on a device running U while causing this to be bypassed for T. * *

Note:If the rotation-min-sdk-version is less than or equal to 32 (Android * Sv2), then the rotated signing key will be used in the v3.0 signing block and this call * will be a noop. */ public Builder setRotationTargetsDevRelease(boolean enabled) { mRotationTargetsDevRelease = enabled; return this; } /** * Returns a new {@link V3SchemeSigner} built with the configuration provided to this * {@code Builder}. */ public V3SchemeSigner build() { return new V3SchemeSigner(mBeforeCentralDir, mCentralDir, mEocd, mSignerConfigs, mExecutor, mBlockId, mOptionalV31MinSdkVersion, mRotationTargetsDevRelease); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v3_V3SchemeVerifier.java0100644 0000000 0000000 00000000034 14763776540 030207 xustar000000000 0000000 28 mtime=1741684064.5870000 src/main/java/com/android/apksig/internal/apk/v3/V3SchemeVerifier.java0100644 0000000 0000000 00000111246 14763776540 024640 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v3; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import com.android.apksig.ApkVerificationIssue; import com.android.apksig.ApkVerifier.Issue; import com.android.apksig.SigningCertificateLineage; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignatureNotFoundException; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.util.ByteBufferUtils; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import com.android.apksig.internal.util.X509CertificateUtils; import com.android.apksig.util.DataSource; import com.android.apksig.util.RunnablesExecutor; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.OptionalInt; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; /** * APK Signature Scheme v3 verifier. * *

APK Signature Scheme v3, like v2 is a whole-file signature scheme which aims to protect every * single bit of the APK, as opposed to the JAR Signature Scheme which protects only the names and * uncompressed contents of ZIP entries. * * @see APK Signature Scheme v2 */ public class V3SchemeVerifier { private final RunnablesExecutor mExecutor; private final DataSource mApk; private final ApkUtils.ZipSections mZipSections; private final ApkSigningBlockUtils.Result mResult; private final Set mContentDigestsToVerify; private final int mMinSdkVersion; private final int mMaxSdkVersion; private final int mBlockId; private final OptionalInt mOptionalRotationMinSdkVersion; private final boolean mFullVerification; private ByteBuffer mApkSignatureSchemeV3Block; private V3SchemeVerifier( RunnablesExecutor executor, DataSource apk, ApkUtils.ZipSections zipSections, Set contentDigestsToVerify, ApkSigningBlockUtils.Result result, int minSdkVersion, int maxSdkVersion, int blockId, OptionalInt optionalRotationMinSdkVersion, boolean fullVerification) { mExecutor = executor; mApk = apk; mZipSections = zipSections; mContentDigestsToVerify = contentDigestsToVerify; mResult = result; mMinSdkVersion = minSdkVersion; mMaxSdkVersion = maxSdkVersion; mBlockId = blockId; mOptionalRotationMinSdkVersion = optionalRotationMinSdkVersion; mFullVerification = fullVerification; } /** * Verifies the provided APK's APK Signature Scheme v3 signatures and returns the result of * verification. The APK must be considered verified only if * {@link ApkSigningBlockUtils.Result#verified} is * {@code true}. If verification fails, the result will contain errors -- see * {@link ApkSigningBlockUtils.Result#getErrors()}. * *

Verification succeeds iff the APK's APK Signature Scheme v3 signatures are expected to * verify on all Android platform versions in the {@code [minSdkVersion, maxSdkVersion]} range. * If the APK's signature is expected to not verify on any of the specified platform versions, * this method returns a result with one or more errors and whose * {@code Result.verified == false}, or this method throws an exception. * *

This method only verifies the v3.0 signing block without platform targeted rotation from * a v3.1 signing block. To verify a v3.1 signing block, or a v3.0 signing block in the presence * of a v3.1 block, configure a new {@link V3SchemeVerifier} using the {@code Builder}. * * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a * required cryptographic algorithm implementation is missing * @throws SignatureNotFoundException if no APK Signature Scheme v3 * signatures are found * @throws IOException if an I/O error occurs when reading the APK */ public static ApkSigningBlockUtils.Result verify( RunnablesExecutor executor, DataSource apk, ApkUtils.ZipSections zipSections, int minSdkVersion, int maxSdkVersion) throws IOException, NoSuchAlgorithmException, SignatureNotFoundException { return new V3SchemeVerifier.Builder(apk, zipSections, minSdkVersion, maxSdkVersion) .setRunnablesExecutor(executor) .setBlockId(V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID) .build() .verify(); } /** * Verifies the provided APK's v3 signatures and outputs the results into the provided * {@code result}. APK is considered verified only if there are no errors reported in the * {@code result}. See {@link #verify(RunnablesExecutor, DataSource, ApkUtils.ZipSections, int, * int)} for more information about the contract of this method. * * @return {@link ApkSigningBlockUtils.Result} populated with interesting information about the * APK, such as information about signers, and verification errors and warnings */ public ApkSigningBlockUtils.Result verify() throws IOException, NoSuchAlgorithmException, SignatureNotFoundException { if (mApk == null || mZipSections == null) { throw new IllegalStateException( "A non-null apk and zip sections must be specified to verify an APK's v3 " + "signatures"); } SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(mApk, mZipSections, mBlockId, mResult); mApkSignatureSchemeV3Block = signatureInfo.signatureBlock; DataSource beforeApkSigningBlock = mApk.slice(0, signatureInfo.apkSigningBlockOffset); DataSource centralDir = mApk.slice( signatureInfo.centralDirOffset, signatureInfo.eocdOffset - signatureInfo.centralDirOffset); ByteBuffer eocd = signatureInfo.eocd; parseSigners(); if (mResult.containsErrors()) { return mResult; } ApkSigningBlockUtils.verifyIntegrity(mExecutor, beforeApkSigningBlock, centralDir, eocd, mContentDigestsToVerify, mResult); // make sure that the v3 signers cover the entire targeted sdk version ranges and that the // longest SigningCertificateHistory, if present, corresponds to the newest platform // versions SortedMap sortedSigners = new TreeMap<>(); for (ApkSigningBlockUtils.Result.SignerInfo signer : mResult.signers) { sortedSigners.put(signer.maxSdkVersion, signer); } // first make sure there is neither overlap nor holes int firstMin = 0; int lastMax = 0; int lastLineageSize = 0; // while we're iterating through the signers, build up the list of lineages List lineages = new ArrayList<>(mResult.signers.size()); for (ApkSigningBlockUtils.Result.SignerInfo signer : sortedSigners.values()) { int currentMin = signer.minSdkVersion; int currentMax = signer.maxSdkVersion; if (firstMin == 0) { // first round sets up our basis firstMin = currentMin; } else { // A signer's minimum SDK can equal the previous signer's maximum SDK if this signer // is targeting a development release. if (currentMin != (lastMax + 1) && !(currentMin == lastMax && signerTargetsDevRelease(signer))) { mResult.addError(Issue.V3_INCONSISTENT_SDK_VERSIONS); break; } } lastMax = currentMax; // also, while we're here, make sure that the lineage sizes only increase if (signer.signingCertificateLineage != null) { int currLineageSize = signer.signingCertificateLineage.size(); if (currLineageSize < lastLineageSize) { mResult.addError(Issue.V3_INCONSISTENT_LINEAGES); break; } lastLineageSize = currLineageSize; lineages.add(signer.signingCertificateLineage); } } // make sure we support our desired sdk ranges; if rotation is present in a v3.1 block // then the max level only needs to support up to that sdk version for rotation. if (firstMin > mMinSdkVersion || lastMax < (mOptionalRotationMinSdkVersion.isPresent() ? mOptionalRotationMinSdkVersion.getAsInt() - 1 : mMaxSdkVersion)) { mResult.addError(Issue.V3_MISSING_SDK_VERSIONS, firstMin, lastMax); } try { mResult.signingCertificateLineage = SigningCertificateLineage.consolidateLineages(lineages); } catch (IllegalArgumentException e) { mResult.addError(Issue.V3_INCONSISTENT_LINEAGES); } if (!mResult.containsErrors()) { mResult.verified = true; } return mResult; } /** * Parses each signer in the provided APK Signature Scheme v3 block and populates corresponding * {@code signerInfos} of the provided {@code result}. * *

This verifies signatures over {@code signed-data} block contained in each signer block. * However, this does not verify the integrity of the rest of the APK but rather simply reports * the expected digests of the rest of the APK (see {@code contentDigestsToVerify}). * *

This method adds one or more errors to the {@code result} if a verification error is * expected to be encountered on an Android platform version in the * {@code [minSdkVersion, maxSdkVersion]} range. */ public static void parseSigners( ByteBuffer apkSignatureSchemeV3Block, Set contentDigestsToVerify, ApkSigningBlockUtils.Result result) throws NoSuchAlgorithmException { try { new V3SchemeVerifier.Builder(apkSignatureSchemeV3Block) .setResult(result) .setContentDigestsToVerify(contentDigestsToVerify) .setFullVerification(false) .build() .parseSigners(); } catch (IOException | SignatureNotFoundException e) { // This should never occur since the apkSignatureSchemeV3Block was already provided. throw new IllegalStateException("An exception was encountered when attempting to parse" + " the signers from the provided APK Signature Scheme v3 block", e); } } /** * Parses each signer in the APK Signature Scheme v3 block and populates corresponding * {@link ApkSigningBlockUtils.Result.SignerInfo} instances in the * returned {@link ApkSigningBlockUtils.Result}. * *

This verifies signatures over {@code signed-data} block contained in each signer block. * However, this does not verify the integrity of the rest of the APK but rather simply reports * the expected digests of the rest of the APK (see {@link Builder#setContentDigestsToVerify}). * *

This method adds one or more errors to the returned {@code Result} if a verification error * is encountered when parsing the signers. */ public ApkSigningBlockUtils.Result parseSigners() throws IOException, NoSuchAlgorithmException, SignatureNotFoundException { ByteBuffer signers; try { if (mApkSignatureSchemeV3Block == null) { SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(mApk, mZipSections, mBlockId, mResult); mApkSignatureSchemeV3Block = signatureInfo.signatureBlock; } signers = getLengthPrefixedSlice(mApkSignatureSchemeV3Block); } catch (ApkFormatException e) { mResult.addError(Issue.V3_SIG_MALFORMED_SIGNERS); return mResult; } if (!signers.hasRemaining()) { mResult.addError(Issue.V3_SIG_NO_SIGNERS); return mResult; } CertificateFactory certFactory; try { certFactory = CertificateFactory.getInstance("X.509"); } catch (CertificateException e) { throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); } int signerCount = 0; while (signers.hasRemaining()) { int signerIndex = signerCount; signerCount++; ApkSigningBlockUtils.Result.SignerInfo signerInfo = new ApkSigningBlockUtils.Result.SignerInfo(); signerInfo.index = signerIndex; mResult.signers.add(signerInfo); try { ByteBuffer signer = getLengthPrefixedSlice(signers); parseSigner(signer, certFactory, signerInfo); } catch (ApkFormatException | BufferUnderflowException e) { signerInfo.addError(Issue.V3_SIG_MALFORMED_SIGNER); return mResult; } } return mResult; } /** * Parses the provided signer block and populates the {@code result}. * *

This verifies signatures over {@code signed-data} contained in this block, as well as * the data contained therein, but does not verify the integrity of the rest of the APK. To * facilitate APK integrity verification, this method adds the {@code contentDigestsToVerify}. * These digests can then be used to verify the integrity of the APK. * *

This method adds one or more errors to the {@code result} if a verification error is * expected to be encountered on an Android platform version in the * {@code [minSdkVersion, maxSdkVersion]} range. */ private void parseSigner(ByteBuffer signerBlock, CertificateFactory certFactory, ApkSigningBlockUtils.Result.SignerInfo result) throws ApkFormatException, NoSuchAlgorithmException { ByteBuffer signedData = getLengthPrefixedSlice(signerBlock); byte[] signedDataBytes = new byte[signedData.remaining()]; signedData.get(signedDataBytes); signedData.flip(); result.signedData = signedDataBytes; int parsedMinSdkVersion = signerBlock.getInt(); int parsedMaxSdkVersion = signerBlock.getInt(); result.minSdkVersion = parsedMinSdkVersion; result.maxSdkVersion = parsedMaxSdkVersion; if (parsedMinSdkVersion < 0 || parsedMinSdkVersion > parsedMaxSdkVersion) { result.addError( Issue.V3_SIG_INVALID_SDK_VERSIONS, parsedMinSdkVersion, parsedMaxSdkVersion); } ByteBuffer signatures = getLengthPrefixedSlice(signerBlock); byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock); // Parse the signatures block and identify supported signatures int signatureCount = 0; List supportedSignatures = new ArrayList<>(1); while (signatures.hasRemaining()) { signatureCount++; try { ByteBuffer signature = getLengthPrefixedSlice(signatures); int sigAlgorithmId = signature.getInt(); byte[] sigBytes = readLengthPrefixedByteArray(signature); result.signatures.add( new ApkSigningBlockUtils.Result.SignerInfo.Signature( sigAlgorithmId, sigBytes)); SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId); if (signatureAlgorithm == null) { result.addWarning(Issue.V3_SIG_UNKNOWN_SIG_ALGORITHM, sigAlgorithmId); continue; } // TODO consider dropping deprecated signatures for v3 or modifying // getSignaturesToVerify (called below) supportedSignatures.add( new ApkSigningBlockUtils.SupportedSignature(signatureAlgorithm, sigBytes)); } catch (ApkFormatException | BufferUnderflowException e) { result.addError(Issue.V3_SIG_MALFORMED_SIGNATURE, signatureCount); return; } } if (result.signatures.isEmpty()) { result.addError(Issue.V3_SIG_NO_SIGNATURES); return; } // Verify signatures over signed-data block using the public key List signaturesToVerify = null; try { signaturesToVerify = ApkSigningBlockUtils.getSignaturesToVerify( supportedSignatures, result.minSdkVersion, result.maxSdkVersion); } catch (ApkSigningBlockUtils.NoSupportedSignaturesException e) { result.addError(Issue.V3_SIG_NO_SUPPORTED_SIGNATURES); return; } for (ApkSigningBlockUtils.SupportedSignature signature : signaturesToVerify) { SignatureAlgorithm signatureAlgorithm = signature.algorithm; String jcaSignatureAlgorithm = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst(); AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond(); String keyAlgorithm = signatureAlgorithm.getJcaKeyAlgorithm(); PublicKey publicKey; try { publicKey = KeyFactory.getInstance(keyAlgorithm).generatePublic( new X509EncodedKeySpec(publicKeyBytes)); } catch (Exception e) { result.addError(Issue.V3_SIG_MALFORMED_PUBLIC_KEY, e); return; } try { Signature sig = Signature.getInstance(jcaSignatureAlgorithm); sig.initVerify(publicKey); if (jcaSignatureAlgorithmParams != null) { sig.setParameter(jcaSignatureAlgorithmParams); } signedData.position(0); sig.update(signedData); byte[] sigBytes = signature.signature; if (!sig.verify(sigBytes)) { result.addError(Issue.V3_SIG_DID_NOT_VERIFY, signatureAlgorithm); return; } result.verifiedSignatures.put(signatureAlgorithm, sigBytes); mContentDigestsToVerify.add(signatureAlgorithm.getContentDigestAlgorithm()); } catch (InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) { result.addError(Issue.V3_SIG_VERIFY_EXCEPTION, signatureAlgorithm, e); return; } } // At least one signature over signedData has verified. We can now parse signed-data. signedData.position(0); ByteBuffer digests = getLengthPrefixedSlice(signedData); ByteBuffer certificates = getLengthPrefixedSlice(signedData); int signedMinSdkVersion = signedData.getInt(); if (signedMinSdkVersion != parsedMinSdkVersion) { result.addError( Issue.V3_MIN_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD, parsedMinSdkVersion, signedMinSdkVersion); } int signedMaxSdkVersion = signedData.getInt(); if (signedMaxSdkVersion != parsedMaxSdkVersion) { result.addError( Issue.V3_MAX_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD, parsedMaxSdkVersion, signedMaxSdkVersion); } ByteBuffer additionalAttributes = getLengthPrefixedSlice(signedData); // Parse the certificates block int certificateIndex = -1; while (certificates.hasRemaining()) { certificateIndex++; byte[] encodedCert = readLengthPrefixedByteArray(certificates); X509Certificate certificate; try { certificate = X509CertificateUtils.generateCertificate(encodedCert, certFactory); } catch (CertificateException e) { result.addError( Issue.V3_SIG_MALFORMED_CERTIFICATE, certificateIndex, certificateIndex + 1, e); return; } // Wrap the cert so that the result's getEncoded returns exactly the original encoded // form. Without this, getEncoded may return a different form from what was stored in // the signature. This is because some X509Certificate(Factory) implementations // re-encode certificates. certificate = new GuaranteedEncodedFormX509Certificate(certificate, encodedCert); result.certs.add(certificate); } if (result.certs.isEmpty()) { result.addError(Issue.V3_SIG_NO_CERTIFICATES); return; } X509Certificate mainCertificate = result.certs.get(0); byte[] certificatePublicKeyBytes; try { certificatePublicKeyBytes = ApkSigningBlockUtils.encodePublicKey( mainCertificate.getPublicKey()); } catch (InvalidKeyException e) { System.out.println("Caught an exception encoding the public key: " + e); e.printStackTrace(); certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded(); } if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) { result.addError( Issue.V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD, ApkSigningBlockUtils.toHex(certificatePublicKeyBytes), ApkSigningBlockUtils.toHex(publicKeyBytes)); return; } // Parse the digests block int digestCount = 0; while (digests.hasRemaining()) { digestCount++; try { ByteBuffer digest = getLengthPrefixedSlice(digests); int sigAlgorithmId = digest.getInt(); byte[] digestBytes = readLengthPrefixedByteArray(digest); result.contentDigests.add( new ApkSigningBlockUtils.Result.SignerInfo.ContentDigest( sigAlgorithmId, digestBytes)); } catch (ApkFormatException | BufferUnderflowException e) { result.addError(Issue.V3_SIG_MALFORMED_DIGEST, digestCount); return; } } List sigAlgsFromSignaturesRecord = new ArrayList<>(result.signatures.size()); for (ApkSigningBlockUtils.Result.SignerInfo.Signature signature : result.signatures) { sigAlgsFromSignaturesRecord.add(signature.getAlgorithmId()); } List sigAlgsFromDigestsRecord = new ArrayList<>(result.contentDigests.size()); for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest digest : result.contentDigests) { sigAlgsFromDigestsRecord.add(digest.getSignatureAlgorithmId()); } if (!sigAlgsFromSignaturesRecord.equals(sigAlgsFromDigestsRecord)) { result.addError( Issue.V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS, sigAlgsFromSignaturesRecord, sigAlgsFromDigestsRecord); return; } // Parse the additional attributes block. int additionalAttributeCount = 0; boolean rotationAttrFound = false; while (additionalAttributes.hasRemaining()) { additionalAttributeCount++; try { ByteBuffer attribute = getLengthPrefixedSlice(additionalAttributes); int id = attribute.getInt(); byte[] value = ByteBufferUtils.toByteArray(attribute); result.additionalAttributes.add( new ApkSigningBlockUtils.Result.SignerInfo.AdditionalAttribute(id, value)); if (id == V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID) { try { // SigningCertificateLineage is verified when built result.signingCertificateLineage = SigningCertificateLineage.readFromV3AttributeValue(value); // make sure that the last cert in the chain matches this signer cert SigningCertificateLineage subLineage = result.signingCertificateLineage.getSubLineage(result.certs.get(0)); if (result.signingCertificateLineage.size() != subLineage.size()) { result.addError(Issue.V3_SIG_POR_CERT_MISMATCH); } } catch (SecurityException e) { result.addError(Issue.V3_SIG_POR_DID_NOT_VERIFY); } catch (IllegalArgumentException e) { result.addError(Issue.V3_SIG_POR_CERT_MISMATCH); } catch (Exception e) { result.addError(Issue.V3_SIG_MALFORMED_LINEAGE); } } else if (id == V3SchemeConstants.ROTATION_MIN_SDK_VERSION_ATTR_ID) { rotationAttrFound = true; // API targeting for rotation was added with V3.1; if the maxSdkVersion // does not support v3.1 then ignore this attribute. if (mMaxSdkVersion >= V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT && mFullVerification) { int attrRotationMinSdkVersion = ByteBuffer.wrap(value) .order(ByteOrder.LITTLE_ENDIAN).getInt(); if (mOptionalRotationMinSdkVersion.isPresent()) { int rotationMinSdkVersion = mOptionalRotationMinSdkVersion.getAsInt(); if (attrRotationMinSdkVersion != rotationMinSdkVersion) { result.addError(Issue.V31_ROTATION_MIN_SDK_MISMATCH, attrRotationMinSdkVersion, rotationMinSdkVersion); } } else { result.addError(Issue.V31_BLOCK_MISSING, attrRotationMinSdkVersion); } } } else if (id == V3SchemeConstants.ROTATION_ON_DEV_RELEASE_ATTR_ID) { // This attribute should only be used by a v3.1 signer to indicate rotation // is targeting the development release that is using the SDK version of the // previously released platform version. if (mBlockId != V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID) { result.addWarning(Issue.V31_ROTATION_TARGETS_DEV_RELEASE_ATTR_ON_V3_SIGNER); } } else { result.addWarning(Issue.V3_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE, id); } } catch (ApkFormatException | BufferUnderflowException e) { result.addError( Issue.V3_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE, additionalAttributeCount); return; } } if (mFullVerification && mOptionalRotationMinSdkVersion.isPresent() && !rotationAttrFound) { result.addWarning(Issue.V31_ROTATION_MIN_SDK_ATTR_MISSING, mOptionalRotationMinSdkVersion.getAsInt()); } } /** * Returns whether the specified {@code signerInfo} is targeting a development release. */ public static boolean signerTargetsDevRelease( ApkSigningBlockUtils.Result.SignerInfo signerInfo) { boolean result = signerInfo.additionalAttributes.stream() .mapToInt(attribute -> attribute.getId()) .anyMatch(attrId -> attrId == V3SchemeConstants.ROTATION_ON_DEV_RELEASE_ATTR_ID); return result; } /** Builder of {@link V3SchemeVerifier} instances. */ public static class Builder { private RunnablesExecutor mExecutor = RunnablesExecutor.SINGLE_THREADED; private DataSource mApk; private ApkUtils.ZipSections mZipSections; private ByteBuffer mApkSignatureSchemeV3Block; private Set mContentDigestsToVerify; private ApkSigningBlockUtils.Result mResult; private int mMinSdkVersion; private int mMaxSdkVersion; private int mBlockId = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; private boolean mFullVerification = true; private OptionalInt mOptionalRotationMinSdkVersion = OptionalInt.empty(); /** * Instantiates a new {@code Builder} for a {@code V3SchemeVerifier} that can be used to * verify the V3 signing block of the provided {@code apk} with the specified {@code * zipSections} over the range from {@code minSdkVersion} to {@code maxSdkVersion}. */ public Builder(DataSource apk, ApkUtils.ZipSections zipSections, int minSdkVersion, int maxSdkVersion) { mApk = apk; mZipSections = zipSections; mMinSdkVersion = minSdkVersion; mMaxSdkVersion = maxSdkVersion; } /** * Instantiates a new {@code Builder} for a {@code V3SchemeVerifier} that can be used to * parse the {@link ApkSigningBlockUtils.Result.SignerInfo} instances from the {@code * apkSignatureSchemeV3Block}. * * Full verification of the v3 signature is not possible when instantiating a new * {@code V3SchemeVerifier} with this method. */ public Builder(ByteBuffer apkSignatureSchemeV3Block) { mApkSignatureSchemeV3Block = apkSignatureSchemeV3Block; } /** * Sets the {@link RunnablesExecutor} to be used when verifying the APK's content digests. */ public Builder setRunnablesExecutor(RunnablesExecutor executor) { mExecutor = executor; return this; } /** * Sets the V3 {code blockId} to be verified in the provided APK. * *

This {@code V3SchemeVerifier} currently supports the block IDs for the {@link * V3SchemeConstants#APK_SIGNATURE_SCHEME_V3_BLOCK_ID v3.0} and {@link * V3SchemeConstants#APK_SIGNATURE_SCHEME_V31_BLOCK_ID v3.1} signature schemes. */ public Builder setBlockId(int blockId) { mBlockId = blockId; return this; } /** * Sets the {@code rotationMinSdkVersion} to be verified in the v3.0 signer's additional * attribute. * *

This value can be obtained from the signers returned when verifying the v3.1 signing * block of an APK; in the case of multiple signers targeting different SDK versions in the * v3.1 signing block, the minimum SDK version from all the signers should be used. */ public Builder setRotationMinSdkVersion(int rotationMinSdkVersion) { mOptionalRotationMinSdkVersion = OptionalInt.of(rotationMinSdkVersion); return this; } /** * Sets the {@code result} instance to be used when returning verification results. * *

This method can be used when the caller already has a {@link * ApkSigningBlockUtils.Result} and wants to store the verification results in this * instance. */ public Builder setResult(ApkSigningBlockUtils.Result result) { mResult = result; return this; } /** * Sets the instance to be used to store the {@code contentDigestsToVerify}. * *

This method can be used when the caller needs access to the {@code * contentDigestsToVerify} computed by this {@code V3SchemeVerifier}. */ public Builder setContentDigestsToVerify( Set contentDigestsToVerify) { mContentDigestsToVerify = contentDigestsToVerify; return this; } /** * Sets whether full verification should be performed by the {@code V3SchemeVerifier} built * from this instance. * * {@link #verify()} will always verify the content digests for the APK, but this * allows verification of the rotation minimum SDK version stripping attribute to be skipped * for scenarios where this value may not have been parsed from a V3.1 signing block (such * as when only {@link #parseSigners()} will be invoked. */ public Builder setFullVerification(boolean fullVerification) { mFullVerification = fullVerification; return this; } /** * Returns a new {@link V3SchemeVerifier} built with the configuration provided to this * {@code Builder}. */ public V3SchemeVerifier build() { int sigSchemeVersion; switch (mBlockId) { case V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID: sigSchemeVersion = ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3; mMinSdkVersion = Math.max(mMinSdkVersion, V3SchemeConstants.MIN_SDK_WITH_V3_SUPPORT); break; case V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID: sigSchemeVersion = ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31; // V3.1 supports targeting an SDK version later than that of the initial release // in which it is supported; allow any range for V3.1 as long as V3.0 covers the // rest of the range. mMinSdkVersion = mMaxSdkVersion; break; default: throw new IllegalArgumentException( String.format("Unsupported APK Signature Scheme V3 block ID: 0x%08x", mBlockId)); } if (mResult == null) { mResult = new ApkSigningBlockUtils.Result(sigSchemeVersion); } if (mContentDigestsToVerify == null) { mContentDigestsToVerify = new HashSet<>(1); } V3SchemeVerifier verifier = new V3SchemeVerifier( mExecutor, mApk, mZipSections, mContentDigestsToVerify, mResult, mMinSdkVersion, mMaxSdkVersion, mBlockId, mOptionalRotationMinSdkVersion, mFullVerification); if (mApkSignatureSchemeV3Block != null) { verifier.mApkSignatureSchemeV3Block = mApkSignatureSchemeV3Block; } return verifier; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v3_V3SigningCertificateLineage.java0100644 0000000 0000000 00000000034 14763776540 032335 xustar000000000 0000000 28 mtime=1741684064.5880000 src/main/java/com/android/apksig/internal/apk/v3/V3SigningCertificateLineage.java0100644 0000000 0000000 00000034513 14763776540 026767 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v3; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsLengthPrefixedElement; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedElements; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import com.android.apksig.internal.util.X509CertificateUtils; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; /** * APK Signer Lineage. * *

The signer lineage contains a history of signing certificates with each ancestor attesting to * the validity of its descendant. Each additional descendant represents a new identity that can be * used to sign an APK, and each generation has accompanying attributes which represent how the * APK would like to view the older signing certificates, specifically how they should be trusted in * certain situations. * *

Its primary use is to enable APK Signing Certificate Rotation. The Android platform verifies * the APK Signer Lineage, and if the current signing certificate for the APK is in the Signer * Lineage, and the Lineage contains the certificate the platform associates with the APK, it will * allow upgrades to the new certificate. * * @see Application Signing */ public class V3SigningCertificateLineage { private final static int FIRST_VERSION = 1; private final static int CURRENT_VERSION = FIRST_VERSION; /** * Deserializes the binary representation of an {@link V3SigningCertificateLineage}. Also * verifies that the structure is well-formed, e.g. that the signature for each node is from its * parent. */ public static List readSigningCertificateLineage(ByteBuffer inputBytes) throws IOException { List result = new ArrayList<>(); int nodeCount = 0; if (inputBytes == null || !inputBytes.hasRemaining()) { return null; } ApkSigningBlockUtils.checkByteOrderLittleEndian(inputBytes); // FORMAT (little endian): // * uint32: version code // * sequence of length-prefixed (uint32): nodes // * length-prefixed bytes: signed data // * length-prefixed bytes: certificate // * uint32: signature algorithm id // * uint32: flags // * uint32: signature algorithm id (used by to sign next cert in lineage) // * length-prefixed bytes: signature over above signed data X509Certificate lastCert = null; int lastSigAlgorithmId = 0; try { int version = inputBytes.getInt(); if (version != CURRENT_VERSION) { // we only have one version to worry about right now, so just check it throw new IllegalArgumentException("Encoded SigningCertificateLineage has a version" + " different than any of which we are aware"); } HashSet certHistorySet = new HashSet<>(); while (inputBytes.hasRemaining()) { nodeCount++; ByteBuffer nodeBytes = getLengthPrefixedSlice(inputBytes); ByteBuffer signedData = getLengthPrefixedSlice(nodeBytes); int flags = nodeBytes.getInt(); int sigAlgorithmId = nodeBytes.getInt(); SignatureAlgorithm sigAlgorithm = SignatureAlgorithm.findById(lastSigAlgorithmId); byte[] signature = readLengthPrefixedByteArray(nodeBytes); if (lastCert != null) { // Use previous level cert to verify current level String jcaSignatureAlgorithm = sigAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst(); AlgorithmParameterSpec jcaSignatureAlgorithmParams = sigAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond(); PublicKey publicKey = lastCert.getPublicKey(); Signature sig = Signature.getInstance(jcaSignatureAlgorithm); sig.initVerify(publicKey); if (jcaSignatureAlgorithmParams != null) { sig.setParameter(jcaSignatureAlgorithmParams); } sig.update(signedData); if (!sig.verify(signature)) { throw new SecurityException("Unable to verify signature of certificate #" + nodeCount + " using " + jcaSignatureAlgorithm + " when verifying" + " V3SigningCertificateLineage object"); } } signedData.rewind(); byte[] encodedCert = readLengthPrefixedByteArray(signedData); int signedSigAlgorithm = signedData.getInt(); if (lastCert != null && lastSigAlgorithmId != signedSigAlgorithm) { throw new SecurityException("Signing algorithm ID mismatch for certificate #" + nodeBytes + " when verifying V3SigningCertificateLineage object"); } lastCert = X509CertificateUtils.generateCertificate(encodedCert); lastCert = new GuaranteedEncodedFormX509Certificate(lastCert, encodedCert); if (certHistorySet.contains(lastCert)) { throw new SecurityException("Encountered duplicate entries in " + "SigningCertificateLineage at certificate #" + nodeCount + ". All " + "signing certificates should be unique"); } certHistorySet.add(lastCert); lastSigAlgorithmId = sigAlgorithmId; result.add(new SigningCertificateNode( lastCert, SignatureAlgorithm.findById(signedSigAlgorithm), SignatureAlgorithm.findById(sigAlgorithmId), signature, flags)); } } catch(ApkFormatException | BufferUnderflowException e){ throw new IOException("Failed to parse V3SigningCertificateLineage object", e); } catch(NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e){ throw new SecurityException( "Failed to verify signature over signed data for certificate #" + nodeCount + " when parsing V3SigningCertificateLineage object", e); } catch(CertificateException e){ throw new SecurityException("Failed to decode certificate #" + nodeCount + " when parsing V3SigningCertificateLineage object", e); } return result; } /** * encode the in-memory representation of this {@code V3SigningCertificateLineage} */ public static byte[] encodeSigningCertificateLineage( List signingCertificateLineage) { // FORMAT (little endian): // * version code // * sequence of length-prefixed (uint32): nodes // * length-prefixed bytes: signed data // * length-prefixed bytes: certificate // * uint32: signature algorithm id // * uint32: flags // * uint32: signature algorithm id (used by to sign next cert in lineage) List nodes = new ArrayList<>(); for (SigningCertificateNode node : signingCertificateLineage) { nodes.add(encodeSigningCertificateNode(node)); } byte [] encodedSigningCertificateLineage = encodeAsSequenceOfLengthPrefixedElements(nodes); // add the version code (uint32) on top of the encoded nodes int payloadSize = 4 + encodedSigningCertificateLineage.length; ByteBuffer encodedWithVersion = ByteBuffer.allocate(payloadSize); encodedWithVersion.order(ByteOrder.LITTLE_ENDIAN); encodedWithVersion.putInt(CURRENT_VERSION); encodedWithVersion.put(encodedSigningCertificateLineage); return encodedWithVersion.array(); } public static byte[] encodeSigningCertificateNode(SigningCertificateNode node) { // FORMAT (little endian): // * length-prefixed bytes: signed data // * length-prefixed bytes: certificate // * uint32: signature algorithm id // * uint32: flags // * uint32: signature algorithm id (used by to sign next cert in lineage) // * length-prefixed bytes: signature over signed data int parentSigAlgorithmId = 0; if (node.parentSigAlgorithm != null) { parentSigAlgorithmId = node.parentSigAlgorithm.getId(); } int sigAlgorithmId = 0; if (node.sigAlgorithm != null) { sigAlgorithmId = node.sigAlgorithm.getId(); } byte[] prefixedSignedData = encodeSignedData(node.signingCert, parentSigAlgorithmId); byte[] prefixedSignature = encodeAsLengthPrefixedElement(node.signature); int payloadSize = prefixedSignedData.length + 4 + 4 + prefixedSignature.length; ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.put(prefixedSignedData); result.putInt(node.flags); result.putInt(sigAlgorithmId); result.put(prefixedSignature); return result.array(); } public static byte[] encodeSignedData(X509Certificate certificate, int flags) { try { byte[] prefixedCertificate = encodeAsLengthPrefixedElement(certificate.getEncoded()); int payloadSize = 4 + prefixedCertificate.length; ByteBuffer result = ByteBuffer.allocate(payloadSize); result.order(ByteOrder.LITTLE_ENDIAN); result.put(prefixedCertificate); result.putInt(flags); return encodeAsLengthPrefixedElement(result.array()); } catch (CertificateEncodingException e) { throw new RuntimeException( "Failed to encode V3SigningCertificateLineage certificate", e); } } /** * Represents one signing certificate in the {@link V3SigningCertificateLineage}, which * generally means it is/was used at some point to sign the same APK of the others in the * lineage. */ public static class SigningCertificateNode { public SigningCertificateNode( X509Certificate signingCert, SignatureAlgorithm parentSigAlgorithm, SignatureAlgorithm sigAlgorithm, byte[] signature, int flags) { this.signingCert = signingCert; this.parentSigAlgorithm = parentSigAlgorithm; this.sigAlgorithm = sigAlgorithm; this.signature = signature; this.flags = flags; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof SigningCertificateNode)) return false; SigningCertificateNode that = (SigningCertificateNode) o; if (!signingCert.equals(that.signingCert)) return false; if (parentSigAlgorithm != that.parentSigAlgorithm) return false; if (sigAlgorithm != that.sigAlgorithm) return false; if (!Arrays.equals(signature, that.signature)) return false; if (flags != that.flags) return false; // we made it return true; } @Override public int hashCode() { int result = Objects.hash(signingCert, parentSigAlgorithm, sigAlgorithm, flags); result = 31 * result + Arrays.hashCode(signature); return result; } /** * the signing cert for this node. This is part of the data signed by the parent node. */ public final X509Certificate signingCert; /** * the algorithm used by the this node's parent to bless this data. Its ID value is part of * the data signed by the parent node. {@code null} for first node. */ public final SignatureAlgorithm parentSigAlgorithm; /** * the algorithm used by the this nodeto bless the next node's data. Its ID value is part * of the signed data of the next node. {@code null} for the last node. */ public SignatureAlgorithm sigAlgorithm; /** * signature over the signed data (above). The signature is from this node's parent * signing certificate, which should correspond to the signing certificate used to sign an * APK before rotating to this one, and is formed using {@code signatureAlgorithm}. */ public final byte[] signature; /** * the flags detailing how the platform should treat this signing cert */ public int flags; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v4_0100644 0000000 0000000 00000000034 14763776540 024236 xustar000000000 0000000 28 mtime=1741684064.5890000 src/main/java/com/android/apksig/internal/apk/v4/0040755 0000000 0000000 00000000000 14763776540 020663 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v4_V4SchemeSigner.java0100644 0000000 0000000 00000000034 14763776540 027665 xustar000000000 0000000 28 mtime=1741684064.5890000 src/main/java/com/android/apksig/internal/apk/v4/V4SchemeSigner.java0100644 0000000 0000000 00000046471 14763776540 024325 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v4; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeCertificates; import static com.android.apksig.internal.apk.v2.V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID; import static com.android.apksig.internal.apk.v3.V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID; import static com.android.apksig.internal.apk.v3.V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.apk.v2.V2SchemeVerifier; import com.android.apksig.internal.apk.v3.V3SchemeSigner; import com.android.apksig.internal.apk.v3.V3SchemeVerifier; import com.android.apksig.internal.util.Pair; import com.android.apksig.util.DataSource; import com.android.apksig.zip.ZipFormatException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * APK Signature Scheme V4 signer. V4 scheme file contains 2 mandatory fields - used during * installation. And optional verity tree - has to be present during session commit. *

* The fields: *

* 1. hashingInfo - verity root hash and hashing info, * 2. signingInfo - certificate, public key and signature, * For more details see V4Signature. *

* (optional) verityTree: integer size prepended bytes of the verity hash tree. *

*/ public abstract class V4SchemeSigner { /** * Hidden constructor to prevent instantiation. */ private V4SchemeSigner() { } public static class SignerConfig { final public ApkSigningBlockUtils.SignerConfig v4Config; final public ApkSigningBlockUtils.SignerConfig v41Config; public SignerConfig(List v4Configs, List v41Configs) throws InvalidKeyException { if (v4Configs == null || v4Configs.size() != 1) { throw new InvalidKeyException("Only accepting one signer config for V4 Signature."); } if (v41Configs != null && v41Configs.size() != 1) { throw new InvalidKeyException("Only accepting one signer config for V4.1 Signature."); } this.v4Config = v4Configs.get(0); this.v41Config = v41Configs != null ? v41Configs.get(0) : null; } } /** * Based on a public key, return a signing algorithm that supports verity. */ public static List getSuggestedSignatureAlgorithms(PublicKey signingKey, int minSdkVersion, boolean apkSigningBlockPaddingSupported, boolean deterministicDsaSigning) throws InvalidKeyException { List algorithms = V3SchemeSigner.getSuggestedSignatureAlgorithms( signingKey, minSdkVersion, apkSigningBlockPaddingSupported, deterministicDsaSigning); // Keeping only supported algorithms. for (Iterator iter = algorithms.listIterator(); iter.hasNext(); ) { final SignatureAlgorithm algorithm = iter.next(); if (!isSupported(algorithm.getContentDigestAlgorithm(), false)) { iter.remove(); } } return algorithms; } /** * Compute hash tree and generate v4 signature for a given APK. Write the serialized data to * output file. */ public static void generateV4Signature( DataSource apkContent, SignerConfig signerConfig, File outputFile) throws IOException, InvalidKeyException, NoSuchAlgorithmException { Pair pair = generateV4Signature(apkContent, signerConfig); try (final OutputStream output = new FileOutputStream(outputFile)) { pair.getFirst().writeTo(output); V4Signature.writeBytes(output, pair.getSecond()); } catch (IOException e) { outputFile.delete(); throw e; } } /** Generate v4 signature and hash tree for a given APK. */ public static Pair generateV4Signature( DataSource apkContent, SignerConfig signerConfig) throws IOException, InvalidKeyException, NoSuchAlgorithmException { // Salt has to stay empty for fs-verity compatibility. final byte[] salt = null; // Not used by apksigner. final byte[] additionalData = null; final long fileSize = apkContent.size(); // Obtaining the strongest supported digest for each of the v2/v3/v3.1 blocks // (CHUNKED_SHA256 or CHUNKED_SHA512). final Map apkDigests = getApkDigests(apkContent); // Obtaining the merkle tree and the root hash in verity format. ApkSigningBlockUtils.VerityTreeAndDigest verityContentDigestInfo = ApkSigningBlockUtils.computeChunkVerityTreeAndDigest(apkContent); final ContentDigestAlgorithm verityContentDigestAlgorithm = verityContentDigestInfo.contentDigestAlgorithm; final byte[] rootHash = verityContentDigestInfo.rootHash; final byte[] tree = verityContentDigestInfo.tree; final Pair hashingAlgorithmBlockSizePair = convertToV4HashingInfo( verityContentDigestAlgorithm); final V4Signature.HashingInfo hashingInfo = new V4Signature.HashingInfo( hashingAlgorithmBlockSizePair.getFirst(), hashingAlgorithmBlockSizePair.getSecond(), salt, rootHash); // Generating SigningInfo and combining everything into V4Signature. final V4Signature signature; try { signature = generateSignature(signerConfig, hashingInfo, apkDigests, additionalData, fileSize); } catch (InvalidKeyException | SignatureException | CertificateEncodingException e) { throw new InvalidKeyException("Signer failed", e); } return Pair.of(signature, tree); } private static V4Signature.SigningInfo generateSigningInfo( ApkSigningBlockUtils.SignerConfig signerConfig, V4Signature.HashingInfo hashingInfo, byte[] apkDigest, byte[] additionalData, long fileSize) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, CertificateEncodingException { if (signerConfig.certificates.isEmpty()) { throw new SignatureException("No certificates configured for signer"); } if (signerConfig.certificates.size() != 1) { throw new CertificateEncodingException("Should only have one certificate"); } // Collecting data for signing. final PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey(); final List encodedCertificates = encodeCertificates(signerConfig.certificates); final byte[] encodedCertificate = encodedCertificates.get(0); final V4Signature.SigningInfo signingInfoNoSignature = new V4Signature.SigningInfo(apkDigest, encodedCertificate, additionalData, publicKey.getEncoded(), -1, null); final byte[] data = V4Signature.getSignedData(fileSize, hashingInfo, signingInfoNoSignature); // Signing. final List> signatures = ApkSigningBlockUtils.generateSignaturesOverData(signerConfig, data); if (signatures.size() != 1) { throw new SignatureException("Should only be one signature generated"); } final int signatureAlgorithmId = signatures.get(0).getFirst(); final byte[] signature = signatures.get(0).getSecond(); return new V4Signature.SigningInfo(apkDigest, encodedCertificate, additionalData, publicKey.getEncoded(), signatureAlgorithmId, signature); } private static V4Signature generateSignature( SignerConfig signerConfig, V4Signature.HashingInfo hashingInfo, Map apkDigests, byte[] additionalData, long fileSize) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, CertificateEncodingException { byte[] apkDigest = apkDigests.containsKey(VERSION_APK_SIGNATURE_SCHEME_V3) ? apkDigests.get(VERSION_APK_SIGNATURE_SCHEME_V3) : apkDigests.get(VERSION_APK_SIGNATURE_SCHEME_V2); final V4Signature.SigningInfo signingInfo = generateSigningInfo(signerConfig.v4Config, hashingInfo, apkDigest, additionalData, fileSize); final V4Signature.SigningInfos signingInfos; if (signerConfig.v41Config != null) { if (!apkDigests.containsKey(VERSION_APK_SIGNATURE_SCHEME_V31)) { throw new IllegalStateException( "V4.1 cannot be signed without a V3.1 content digest"); } apkDigest = apkDigests.get(VERSION_APK_SIGNATURE_SCHEME_V31); final V4Signature.SigningInfoBlock extSigningBlock = new V4Signature.SigningInfoBlock( APK_SIGNATURE_SCHEME_V31_BLOCK_ID, generateSigningInfo(signerConfig.v41Config, hashingInfo, apkDigest, additionalData, fileSize).toByteArray()); signingInfos = new V4Signature.SigningInfos(signingInfo, extSigningBlock); } else { signingInfos = new V4Signature.SigningInfos(signingInfo); } return new V4Signature(V4Signature.CURRENT_VERSION, hashingInfo.toByteArray(), signingInfos.toByteArray()); } /** * Returns a {@code Map} from the APK signature scheme version to a {@code byte[]} of the * strongest supported content digest found in that version's signature block for the V2, * V3, and V3.1 signatures in the provided {@code apk}. * *

If a supported content digest algorithm is not found in any of the signature blocks, * or if the APK is not signed by any of these signature schemes, then an {@code IOException} * is thrown. */ private static Map getApkDigests(DataSource apk) throws IOException { ApkUtils.ZipSections zipSections; try { zipSections = ApkUtils.findZipSections(apk); } catch (ZipFormatException e) { throw new IOException("Malformed APK: not a ZIP archive", e); } Map sigSchemeToDigest = new HashMap<>(1); try { byte[] digest = getBestV3Digest(apk, zipSections, VERSION_APK_SIGNATURE_SCHEME_V31); sigSchemeToDigest.put(VERSION_APK_SIGNATURE_SCHEME_V31, digest); } catch (SignatureException expected) { // It is expected to catch a SignatureException if the APK does not have a v3.1 // signature. } SignatureException v3Exception = null; try { byte[] digest = getBestV3Digest(apk, zipSections, VERSION_APK_SIGNATURE_SCHEME_V3); sigSchemeToDigest.put(VERSION_APK_SIGNATURE_SCHEME_V3, digest); } catch (SignatureException e) { v3Exception = e; } SignatureException v2Exception = null; try { byte[] digest = getBestV2Digest(apk, zipSections); sigSchemeToDigest.put(VERSION_APK_SIGNATURE_SCHEME_V2, digest); } catch (SignatureException e) { v2Exception = e; } if (sigSchemeToDigest.size() > 0) { return sigSchemeToDigest; } throw new IOException( "Failed to obtain v2/v3 digest, v3 exception: " + v3Exception + ", v2 exception: " + v2Exception); } private static byte[] getBestV3Digest(DataSource apk, ApkUtils.ZipSections zipSections, int v3SchemeVersion) throws SignatureException { final Set contentDigestsToVerify = new HashSet<>(1); final ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( v3SchemeVersion); final int blockId; switch (v3SchemeVersion) { case VERSION_APK_SIGNATURE_SCHEME_V31: blockId = APK_SIGNATURE_SCHEME_V31_BLOCK_ID; break; case VERSION_APK_SIGNATURE_SCHEME_V3: blockId = APK_SIGNATURE_SCHEME_V3_BLOCK_ID; break; default: throw new IllegalArgumentException( "Invalid V3 scheme provided: " + v3SchemeVersion); } try { final SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections, blockId, result); final ByteBuffer apkSignatureSchemeV3Block = signatureInfo.signatureBlock; V3SchemeVerifier.parseSigners(apkSignatureSchemeV3Block, contentDigestsToVerify, result); } catch (Exception e) { throw new SignatureException("Failed to extract and parse v3 block", e); } if (result.signers.size() != 1) { throw new SignatureException("Should only have one signer, errors: " + result.getErrors()); } ApkSigningBlockUtils.Result.SignerInfo signer = result.signers.get(0); if (signer.containsErrors()) { throw new SignatureException("Parsing failed: " + signer.getErrors()); } final List contentDigests = result.signers.get(0).contentDigests; return pickBestDigest(contentDigests); } private static byte[] getBestV2Digest(DataSource apk, ApkUtils.ZipSections zipSections) throws SignatureException { final Set contentDigestsToVerify = new HashSet<>(1); final Set foundApkSigSchemeIds = new HashSet<>(1); final ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); try { final SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections, APK_SIGNATURE_SCHEME_V2_BLOCK_ID, result); final ByteBuffer apkSignatureSchemeV2Block = signatureInfo.signatureBlock; V2SchemeVerifier.parseSigners( apkSignatureSchemeV2Block, contentDigestsToVerify, Collections.emptyMap(), foundApkSigSchemeIds, Integer.MAX_VALUE, Integer.MAX_VALUE, result); } catch (Exception e) { throw new SignatureException("Failed to extract and parse v2 block", e); } if (result.signers.size() != 1) { throw new SignatureException("Should only have one signer, errors: " + result.getErrors()); } ApkSigningBlockUtils.Result.SignerInfo signer = result.signers.get(0); if (signer.containsErrors()) { throw new SignatureException("Parsing failed: " + signer.getErrors()); } final List contentDigests = signer.contentDigests; return pickBestDigest(contentDigests); } private static byte[] pickBestDigest(List contentDigests) throws SignatureException { if (contentDigests == null || contentDigests.isEmpty()) { throw new SignatureException("Should have at least one digest"); } int bestAlgorithmOrder = -1; byte[] bestDigest = null; for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest : contentDigests) { final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(contentDigest.getSignatureAlgorithmId()); final ContentDigestAlgorithm contentDigestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm(); if (!isSupported(contentDigestAlgorithm, true)) { continue; } final int algorithmOrder = digestAlgorithmSortingOrder(contentDigestAlgorithm); if (bestAlgorithmOrder < algorithmOrder) { bestAlgorithmOrder = algorithmOrder; bestDigest = contentDigest.getValue(); } } if (bestDigest == null) { throw new SignatureException("Failed to find a supported digest in the source APK"); } return bestDigest; } public static int digestAlgorithmSortingOrder(ContentDigestAlgorithm contentDigestAlgorithm) { switch (contentDigestAlgorithm) { case CHUNKED_SHA256: return 0; case VERITY_CHUNKED_SHA256: return 1; case CHUNKED_SHA512: return 2; default: return -1; } } private static boolean isSupported(final ContentDigestAlgorithm contentDigestAlgorithm, boolean forV3Digest) { if (contentDigestAlgorithm == null) { return false; } if (contentDigestAlgorithm == ContentDigestAlgorithm.CHUNKED_SHA256 || contentDigestAlgorithm == ContentDigestAlgorithm.CHUNKED_SHA512 || (forV3Digest && contentDigestAlgorithm == ContentDigestAlgorithm.VERITY_CHUNKED_SHA256)) { return true; } return false; } private static Pair convertToV4HashingInfo(ContentDigestAlgorithm algorithm) throws NoSuchAlgorithmException { switch (algorithm) { case VERITY_CHUNKED_SHA256: return Pair.of(V4Signature.HASHING_ALGORITHM_SHA256, V4Signature.LOG2_BLOCK_SIZE_4096_BYTES); default: throw new NoSuchAlgorithmException( "Invalid hash algorithm, only SHA2-256 over 4 KB chunks supported."); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v4_V4SchemeVerifier.java0100644 0000000 0000000 00000000034 14763776540 030211 xustar000000000 0000000 28 mtime=1741684064.5910000 src/main/java/com/android/apksig/internal/apk/v4/V4SchemeVerifier.java0100644 0000000 0000000 00000026023 14763776540 024640 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v4; import static com.android.apksig.internal.apk.ApkSigningBlockUtils.toHex; import com.android.apksig.ApkVerifier; import com.android.apksig.ApkVerifier.Issue; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.SignatureAlgorithm; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import com.android.apksig.internal.util.X509CertificateUtils; import com.android.apksig.util.DataSource; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; /** * APK Signature Scheme V4 verifier. *

* Verifies the serialized V4Signature file against an APK. */ public abstract class V4SchemeVerifier { /** * Hidden constructor to prevent instantiation. */ private V4SchemeVerifier() { } /** *

* The main goals of the verifier are: 1) parse V4Signature file fields 2) verifies the PKCS7 * signature block against the raw root hash bytes in the proto field 3) verifies that the raw * root hash matches with the actual hash tree root of the give APK 4) if the file contains a * verity tree, verifies that it matches with the actual verity tree computed from the given * APK. *

*/ public static ApkSigningBlockUtils.Result verify(DataSource apk, File v4SignatureFile) throws IOException, NoSuchAlgorithmException { final V4Signature signature; final byte[] tree; try (InputStream input = new FileInputStream(v4SignatureFile)) { signature = V4Signature.readFrom(input); tree = V4Signature.readBytes(input); } final ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4); if (signature == null) { result.addError(Issue.V4_SIG_NO_SIGNATURES, "Signature file does not contain a v4 signature."); return result; } if (signature.version != V4Signature.CURRENT_VERSION) { result.addWarning(Issue.V4_SIG_VERSION_NOT_CURRENT, signature.version, V4Signature.CURRENT_VERSION); } V4Signature.HashingInfo hashingInfo = V4Signature.HashingInfo.fromByteArray( signature.hashingInfo); V4Signature.SigningInfos signingInfos = V4Signature.SigningInfos.fromByteArray( signature.signingInfos); final ApkSigningBlockUtils.Result.SignerInfo signerInfo; // Verify the primary signature over signedData. { V4Signature.SigningInfo signingInfo = signingInfos.signingInfo; final byte[] signedData = V4Signature.getSignedData(apk.size(), hashingInfo, signingInfo); signerInfo = parseAndVerifySignatureBlock(signingInfo, signedData); result.signers.add(signerInfo); if (result.containsErrors()) { return result; } } // Verify all subsequent signatures. for (V4Signature.SigningInfoBlock signingInfoBlock : signingInfos.signingInfoBlocks) { V4Signature.SigningInfo signingInfo = V4Signature.SigningInfo.fromByteArray( signingInfoBlock.signingInfo); final byte[] signedData = V4Signature.getSignedData(apk.size(), hashingInfo, signingInfo); result.signers.add(parseAndVerifySignatureBlock(signingInfo, signedData)); if (result.containsErrors()) { return result; } } // Check if the root hash and the tree are correct. verifyRootHashAndTree(apk, signerInfo, hashingInfo.rawRootHash, tree); if (!result.containsErrors()) { result.verified = true; } return result; } /** * Parses the provided signature block and populates the {@code result}. *

* This verifies {@signingInfo} over {@code signedData}, as well as parsing the certificate * contained in the signature block. This method adds one or more errors to the {@code result}. */ private static ApkSigningBlockUtils.Result.SignerInfo parseAndVerifySignatureBlock( V4Signature.SigningInfo signingInfo, final byte[] signedData) throws NoSuchAlgorithmException { final ApkSigningBlockUtils.Result.SignerInfo result = new ApkSigningBlockUtils.Result.SignerInfo(); result.index = 0; final int sigAlgorithmId = signingInfo.signatureAlgorithmId; final byte[] sigBytes = signingInfo.signature; result.signatures.add( new ApkSigningBlockUtils.Result.SignerInfo.Signature(sigAlgorithmId, sigBytes)); SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId); if (signatureAlgorithm == null) { result.addError(Issue.V4_SIG_UNKNOWN_SIG_ALGORITHM, sigAlgorithmId); return result; } String jcaSignatureAlgorithm = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst(); AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond(); String keyAlgorithm = signatureAlgorithm.getJcaKeyAlgorithm(); final byte[] publicKeyBytes = signingInfo.publicKey; PublicKey publicKey; try { publicKey = KeyFactory.getInstance(keyAlgorithm).generatePublic( new X509EncodedKeySpec(publicKeyBytes)); } catch (Exception e) { result.addError(Issue.V4_SIG_MALFORMED_PUBLIC_KEY, e); return result; } try { Signature sig = Signature.getInstance(jcaSignatureAlgorithm); sig.initVerify(publicKey); if (jcaSignatureAlgorithmParams != null) { sig.setParameter(jcaSignatureAlgorithmParams); } sig.update(signedData); if (!sig.verify(sigBytes)) { result.addError(Issue.V4_SIG_DID_NOT_VERIFY, signatureAlgorithm); return result; } result.verifiedSignatures.put(signatureAlgorithm, sigBytes); } catch (InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) { result.addError(Issue.V4_SIG_VERIFY_EXCEPTION, signatureAlgorithm, e); return result; } if (signingInfo.certificate == null) { result.addError(Issue.V4_SIG_NO_CERTIFICATE); return result; } final X509Certificate certificate; try { // Wrap the cert so that the result's getEncoded returns exactly the original encoded // form. Without this, getEncoded may return a different form from what was stored in // the signature. This is because some X509Certificate(Factory) implementations // re-encode certificates. certificate = new GuaranteedEncodedFormX509Certificate( X509CertificateUtils.generateCertificate(signingInfo.certificate), signingInfo.certificate); } catch (CertificateException e) { result.addError(Issue.V4_SIG_MALFORMED_CERTIFICATE, e); return result; } result.certs.add(certificate); byte[] certificatePublicKeyBytes; try { certificatePublicKeyBytes = ApkSigningBlockUtils.encodePublicKey( certificate.getPublicKey()); } catch (InvalidKeyException e) { System.out.println("Caught an exception encoding the public key: " + e); e.printStackTrace(); certificatePublicKeyBytes = certificate.getPublicKey().getEncoded(); } if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) { result.addError( Issue.V4_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD, ApkSigningBlockUtils.toHex(certificatePublicKeyBytes), ApkSigningBlockUtils.toHex(publicKeyBytes)); return result; } // Add apk digest from the file to the result. ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest = new ApkSigningBlockUtils.Result.SignerInfo.ContentDigest( 0 /* signature algorithm id doesn't matter here */, signingInfo.apkDigest); result.contentDigests.add(contentDigest); return result; } private static void verifyRootHashAndTree(DataSource apkContent, ApkSigningBlockUtils.Result.SignerInfo signerInfo, byte[] expectedDigest, byte[] expectedTree) throws IOException, NoSuchAlgorithmException { ApkSigningBlockUtils.VerityTreeAndDigest actualContentDigestInfo = ApkSigningBlockUtils.computeChunkVerityTreeAndDigest(apkContent); ContentDigestAlgorithm algorithm = actualContentDigestInfo.contentDigestAlgorithm; final byte[] actualDigest = actualContentDigestInfo.rootHash; final byte[] actualTree = actualContentDigestInfo.tree; if (!Arrays.equals(expectedDigest, actualDigest)) { signerInfo.addError( ApkVerifier.Issue.V4_SIG_APK_ROOT_DID_NOT_VERIFY, algorithm, toHex(expectedDigest), toHex(actualDigest)); return; } // Only check verity tree if it is not empty if (expectedTree != null && !Arrays.equals(expectedTree, actualTree)) { signerInfo.addError( ApkVerifier.Issue.V4_SIG_APK_TREE_DID_NOT_VERIFY, algorithm, toHex(expectedDigest), toHex(actualDigest)); return; } signerInfo.verifiedContentDigests.put(algorithm, actualDigest); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_apk_v4_V4Signature.java0100644 0000000 0000000 00000000034 14763776540 027252 xustar000000000 0000000 28 mtime=1741684064.5910000 src/main/java/com/android/apksig/internal/apk/v4/V4Signature.java0100644 0000000 0000000 00000030325 14763776540 023701 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.apk.v4; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; public class V4Signature { public static final int CURRENT_VERSION = 2; public static final int HASHING_ALGORITHM_SHA256 = 1; public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12; public static final int MAX_SIGNING_INFOS_SIZE = 7168; public static class HashingInfo { public final int hashAlgorithm; // only 1 == SHA256 supported public final byte log2BlockSize; // only 12 (block size 4096) supported now public final byte[] salt; // used exactly as in fs-verity, 32 bytes max public final byte[] rawRootHash; // salted digest of the first Merkle tree page HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) { this.hashAlgorithm = hashAlgorithm; this.log2BlockSize = log2BlockSize; this.salt = salt; this.rawRootHash = rawRootHash; } static HashingInfo fromByteArray(byte[] bytes) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); final int hashAlgorithm = buffer.getInt(); final byte log2BlockSize = buffer.get(); byte[] salt = readBytes(buffer); byte[] rawRootHash = readBytes(buffer); return new HashingInfo(hashAlgorithm, log2BlockSize, salt, rawRootHash); } byte[] toByteArray() { final int size = 4/*hashAlgorithm*/ + 1/*log2BlockSize*/ + bytesSize(this.salt) + bytesSize(this.rawRootHash); ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(this.hashAlgorithm); buffer.put(this.log2BlockSize); writeBytes(buffer, this.salt); writeBytes(buffer, this.rawRootHash); return buffer.array(); } } public static class SigningInfo { public final byte[] apkDigest; // used to match with the corresponding APK public final byte[] certificate; // ASN.1 DER form public final byte[] additionalData; // a free-form binary data blob public final byte[] publicKey; // ASN.1 DER, must match the certificate public final int signatureAlgorithmId; // see the APK v2 doc for the list public final byte[] signature; SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData, byte[] publicKey, int signatureAlgorithmId, byte[] signature) { this.apkDigest = apkDigest; this.certificate = certificate; this.additionalData = additionalData; this.publicKey = publicKey; this.signatureAlgorithmId = signatureAlgorithmId; this.signature = signature; } static SigningInfo fromByteArray(byte[] bytes) throws IOException { return fromByteBuffer(ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN)); } static SigningInfo fromByteBuffer(ByteBuffer buffer) throws IOException { byte[] apkDigest = readBytes(buffer); byte[] certificate = readBytes(buffer); byte[] additionalData = readBytes(buffer); byte[] publicKey = readBytes(buffer); int signatureAlgorithmId = buffer.getInt(); byte[] signature = readBytes(buffer); return new SigningInfo(apkDigest, certificate, additionalData, publicKey, signatureAlgorithmId, signature); } byte[] toByteArray() { final int size = bytesSize(this.apkDigest) + bytesSize(this.certificate) + bytesSize( this.additionalData) + bytesSize(this.publicKey) + 4/*signatureAlgorithmId*/ + bytesSize(this.signature); ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); writeBytes(buffer, this.apkDigest); writeBytes(buffer, this.certificate); writeBytes(buffer, this.additionalData); writeBytes(buffer, this.publicKey); buffer.putInt(this.signatureAlgorithmId); writeBytes(buffer, this.signature); return buffer.array(); } } public static class SigningInfoBlock { public final int blockId; public final byte[] signingInfo; public SigningInfoBlock(int blockId, byte[] signingInfo) { this.blockId = blockId; this.signingInfo = signingInfo; } static SigningInfoBlock fromByteBuffer(ByteBuffer buffer) throws IOException { int blockId = buffer.getInt(); byte[] signingInfo = readBytes(buffer); return new SigningInfoBlock(blockId, signingInfo); } byte[] toByteArray() { final int size = 4/*blockId*/ + bytesSize(this.signingInfo); ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(this.blockId); writeBytes(buffer, this.signingInfo); return buffer.array(); } } public static class SigningInfos { public final SigningInfo signingInfo; public final SigningInfoBlock[] signingInfoBlocks; public SigningInfos(SigningInfo signingInfo) { this.signingInfo = signingInfo; this.signingInfoBlocks = new SigningInfoBlock[0]; } public SigningInfos(SigningInfo signingInfo, SigningInfoBlock... signingInfoBlocks) { this.signingInfo = signingInfo; this.signingInfoBlocks = signingInfoBlocks; } public static SigningInfos fromByteArray(byte[] bytes) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); SigningInfo signingInfo = SigningInfo.fromByteBuffer(buffer); if (!buffer.hasRemaining()) { return new SigningInfos(signingInfo); } ArrayList signingInfoBlocks = new ArrayList<>(1); while (buffer.hasRemaining()) { signingInfoBlocks.add(SigningInfoBlock.fromByteBuffer(buffer)); } return new SigningInfos(signingInfo, signingInfoBlocks.toArray(new SigningInfoBlock[signingInfoBlocks.size()])); } byte[] toByteArray() { byte[][] arrays = new byte[1 + this.signingInfoBlocks.length][]; arrays[0] = this.signingInfo.toByteArray(); int size = arrays[0].length; for (int i = 0, isize = this.signingInfoBlocks.length; i < isize; ++i) { arrays[i + 1] = this.signingInfoBlocks[i].toByteArray(); size += arrays[i + 1].length; } if (size > MAX_SIGNING_INFOS_SIZE) { throw new IllegalArgumentException( "Combined SigningInfos length exceeded limit of 7K: " + size); } // Combine all arrays into one. byte[] result = Arrays.copyOf(arrays[0], size); int offset = arrays[0].length; for (int i = 0, isize = this.signingInfoBlocks.length; i < isize; ++i) { System.arraycopy(arrays[i + 1], 0, result, offset, arrays[i + 1].length); offset += arrays[i + 1].length; } return result; } } // Always 2 for now. public final int version; public final byte[] hashingInfo; // Can contain either SigningInfo or SigningInfo + one or multiple SigningInfoBlock. // Passed as-is to the kernel. Can be retrieved later. public final byte[] signingInfos; V4Signature(int version, byte[] hashingInfo, byte[] signingInfos) { this.version = version; this.hashingInfo = hashingInfo; this.signingInfos = signingInfos; } static V4Signature readFrom(InputStream stream) throws IOException { final int version = readIntLE(stream); if (version != CURRENT_VERSION) { throw new IOException("Invalid signature version."); } final byte[] hashingInfo = readBytes(stream); final byte[] signingInfo = readBytes(stream); return new V4Signature(version, hashingInfo, signingInfo); } public void writeTo(OutputStream stream) throws IOException { writeIntLE(stream, this.version); writeBytes(stream, this.hashingInfo); writeBytes(stream, this.signingInfos); } static byte[] getSignedData(long fileSize, HashingInfo hashingInfo, SigningInfo signingInfo) { final int size = 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize( hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize( signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize( signingInfo.additionalData); ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(size); buffer.putLong(fileSize); buffer.putInt(hashingInfo.hashAlgorithm); buffer.put(hashingInfo.log2BlockSize); writeBytes(buffer, hashingInfo.salt); writeBytes(buffer, hashingInfo.rawRootHash); writeBytes(buffer, signingInfo.apkDigest); writeBytes(buffer, signingInfo.certificate); writeBytes(buffer, signingInfo.additionalData); return buffer.array(); } // Utility methods. static int bytesSize(byte[] bytes) { return 4/*length*/ + (bytes == null ? 0 : bytes.length); } static void readFully(InputStream stream, byte[] buffer) throws IOException { int len = buffer.length; int n = 0; while (n < len) { int count = stream.read(buffer, n, len - n); if (count < 0) { throw new EOFException(); } n += count; } } static int readIntLE(InputStream stream) throws IOException { final byte[] buffer = new byte[4]; readFully(stream, buffer); return ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt(); } static void writeIntLE(OutputStream stream, int v) throws IOException { final byte[] buffer = ByteBuffer.wrap(new byte[4]).order(ByteOrder.LITTLE_ENDIAN).putInt(v).array(); stream.write(buffer); } static byte[] readBytes(InputStream stream) throws IOException { try { final int size = readIntLE(stream); final byte[] bytes = new byte[size]; readFully(stream, bytes); return bytes; } catch (EOFException ignored) { return null; } } static byte[] readBytes(ByteBuffer buffer) throws IOException { if (buffer.remaining() < 4) { throw new EOFException(); } final int size = buffer.getInt(); if (buffer.remaining() < size) { throw new EOFException(); } final byte[] bytes = new byte[size]; buffer.get(bytes); return bytes; } static void writeBytes(OutputStream stream, byte[] bytes) throws IOException { if (bytes == null) { writeIntLE(stream, 0); return; } writeIntLE(stream, bytes.length); stream.write(bytes); } static void writeBytes(ByteBuffer buffer, byte[] bytes) { if (bytes == null) { buffer.putInt(0); return; } buffer.putInt(bytes.length); buffer.put(bytes); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_0100644 0000000 0000000 00000000034 14763776540 023714 xustar000000000 0000000 28 mtime=1741684064.5920000 src/main/java/com/android/apksig/internal/asn1/0040755 0000000 0000000 00000000000 14763776540 020421 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1BerParser.java0100644 0000000 0000000 00000000034 14763776540 027165 xustar000000000 0000000 28 mtime=1741684064.5920000 src/main/java/com/android/apksig/internal/asn1/Asn1BerParser.java0100644 0000000 0000000 00000071170 14763776540 023677 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; import com.android.apksig.internal.asn1.ber.BerDataValue; import com.android.apksig.internal.asn1.ber.BerDataValueFormatException; import com.android.apksig.internal.asn1.ber.BerDataValueReader; import com.android.apksig.internal.asn1.ber.BerEncoding; import com.android.apksig.internal.asn1.ber.ByteBufferBerDataValueReader; import com.android.apksig.internal.util.ByteBufferUtils; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Parser of ASN.1 BER-encoded structures. * *

Structure is described to the parser by providing a class annotated with {@link Asn1Class}, * containing fields annotated with {@link Asn1Field}. */ public final class Asn1BerParser { private Asn1BerParser() {} /** * Returns the ASN.1 structure contained in the BER encoded input. * * @param encoded encoded input. If the decoding operation succeeds, the position of this buffer * is advanced to the first position following the end of the consumed structure. * @param containerClass class describing the structure of the input. The class must meet the * following requirements: *

    *
  • The class must be annotated with {@link Asn1Class}.
  • *
  • The class must expose a public no-arg constructor.
  • *
  • Member fields of the class which are populated with parsed input must be * annotated with {@link Asn1Field} and be public and non-final.
  • *
* * @throws Asn1DecodingException if the input could not be decoded into the specified Java * object */ public static T parse(ByteBuffer encoded, Class containerClass) throws Asn1DecodingException { BerDataValue containerDataValue; try { containerDataValue = new ByteBufferBerDataValueReader(encoded).readDataValue(); } catch (BerDataValueFormatException e) { throw new Asn1DecodingException("Failed to decode top-level data value", e); } if (containerDataValue == null) { throw new Asn1DecodingException("Empty input"); } return parse(containerDataValue, containerClass); } /** * Returns the implicit {@code SET OF} contained in the provided ASN.1 BER input. Implicit means * that this method does not care whether the tag number of this data structure is * {@code SET OF} and whether the tag class is {@code UNIVERSAL}. * *

Note: The returned type is {@link List} rather than {@link java.util.Set} because ASN.1 * SET may contain duplicate elements. * * @param encoded encoded input. If the decoding operation succeeds, the position of this buffer * is advanced to the first position following the end of the consumed structure. * @param elementClass class describing the structure of the values/elements contained in this * container. The class must meet the following requirements: *

    *
  • The class must be annotated with {@link Asn1Class}.
  • *
  • The class must expose a public no-arg constructor.
  • *
  • Member fields of the class which are populated with parsed input must be * annotated with {@link Asn1Field} and be public and non-final.
  • *
* * @throws Asn1DecodingException if the input could not be decoded into the specified Java * object */ public static List parseImplicitSetOf(ByteBuffer encoded, Class elementClass) throws Asn1DecodingException { BerDataValue containerDataValue; try { containerDataValue = new ByteBufferBerDataValueReader(encoded).readDataValue(); } catch (BerDataValueFormatException e) { throw new Asn1DecodingException("Failed to decode top-level data value", e); } if (containerDataValue == null) { throw new Asn1DecodingException("Empty input"); } return parseSetOf(containerDataValue, elementClass); } private static T parse(BerDataValue container, Class containerClass) throws Asn1DecodingException { if (container == null) { throw new NullPointerException("container == null"); } if (containerClass == null) { throw new NullPointerException("containerClass == null"); } Asn1Type dataType = getContainerAsn1Type(containerClass); switch (dataType) { case CHOICE: return parseChoice(container, containerClass); case SEQUENCE: { int expectedTagClass = BerEncoding.TAG_CLASS_UNIVERSAL; int expectedTagNumber = BerEncoding.getTagNumber(dataType); if ((container.getTagClass() != expectedTagClass) || (container.getTagNumber() != expectedTagNumber)) { throw new Asn1UnexpectedTagException( "Unexpected data value read as " + containerClass.getName() + ". Expected " + BerEncoding.tagClassAndNumberToString( expectedTagClass, expectedTagNumber) + ", but read: " + BerEncoding.tagClassAndNumberToString( container.getTagClass(), container.getTagNumber())); } return parseSequence(container, containerClass); } case UNENCODED_CONTAINER: return parseSequence(container, containerClass, true); default: throw new Asn1DecodingException("Parsing container " + dataType + " not supported"); } } private static T parseChoice(BerDataValue dataValue, Class containerClass) throws Asn1DecodingException { List fields = getAnnotatedFields(containerClass); if (fields.isEmpty()) { throw new Asn1DecodingException( "No fields annotated with " + Asn1Field.class.getName() + " in CHOICE class " + containerClass.getName()); } // Check that class + tagNumber don't clash between the choices for (int i = 0; i < fields.size() - 1; i++) { AnnotatedField f1 = fields.get(i); int tagNumber1 = f1.getBerTagNumber(); int tagClass1 = f1.getBerTagClass(); for (int j = i + 1; j < fields.size(); j++) { AnnotatedField f2 = fields.get(j); int tagNumber2 = f2.getBerTagNumber(); int tagClass2 = f2.getBerTagClass(); if ((tagNumber1 == tagNumber2) && (tagClass1 == tagClass2)) { throw new Asn1DecodingException( "CHOICE fields are indistinguishable because they have the same tag" + " class and number: " + containerClass.getName() + "." + f1.getField().getName() + " and ." + f2.getField().getName()); } } } // Instantiate the container object / result T obj; try { obj = containerClass.getConstructor().newInstance(); } catch (IllegalArgumentException | ReflectiveOperationException e) { throw new Asn1DecodingException("Failed to instantiate " + containerClass.getName(), e); } // Set the matching field's value from the data value for (AnnotatedField field : fields) { try { field.setValueFrom(dataValue, obj); return obj; } catch (Asn1UnexpectedTagException expected) { // not a match } } throw new Asn1DecodingException( "No options of CHOICE " + containerClass.getName() + " matched"); } private static T parseSequence(BerDataValue container, Class containerClass) throws Asn1DecodingException { return parseSequence(container, containerClass, false); } private static T parseSequence(BerDataValue container, Class containerClass, boolean isUnencodedContainer) throws Asn1DecodingException { List fields = getAnnotatedFields(containerClass); Collections.sort( fields, (f1, f2) -> f1.getAnnotation().index() - f2.getAnnotation().index()); // Check that there are no fields with the same index if (fields.size() > 1) { AnnotatedField lastField = null; for (AnnotatedField field : fields) { if ((lastField != null) && (lastField.getAnnotation().index() == field.getAnnotation().index())) { throw new Asn1DecodingException( "Fields have the same index: " + containerClass.getName() + "." + lastField.getField().getName() + " and ." + field.getField().getName()); } lastField = field; } } // Instantiate the container object / result T t; try { t = containerClass.getConstructor().newInstance(); } catch (IllegalArgumentException | ReflectiveOperationException e) { throw new Asn1DecodingException("Failed to instantiate " + containerClass.getName(), e); } // Parse fields one by one. A complication is that there may be optional fields. int nextUnreadFieldIndex = 0; BerDataValueReader elementsReader = container.contentsReader(); while (nextUnreadFieldIndex < fields.size()) { BerDataValue dataValue; try { // if this is the first field of an unencoded container then the entire contents of // the container should be used when assigning to this field. if (isUnencodedContainer && nextUnreadFieldIndex == 0) { dataValue = container; } else { dataValue = elementsReader.readDataValue(); } } catch (BerDataValueFormatException e) { throw new Asn1DecodingException("Malformed data value", e); } if (dataValue == null) { break; } for (int i = nextUnreadFieldIndex; i < fields.size(); i++) { AnnotatedField field = fields.get(i); try { if (field.isOptional()) { // Optional field -- might not be present and we may thus be trying to set // it from the wrong tag. try { field.setValueFrom(dataValue, t); nextUnreadFieldIndex = i + 1; break; } catch (Asn1UnexpectedTagException e) { // This field is not present, attempt to use this data value for the // next / iteration of the loop continue; } } else { // Mandatory field -- if we can't set its value from this data value, then // it's an error field.setValueFrom(dataValue, t); nextUnreadFieldIndex = i + 1; break; } } catch (Asn1DecodingException e) { throw new Asn1DecodingException( "Failed to parse " + containerClass.getName() + "." + field.getField().getName(), e); } } } return t; } // NOTE: This method returns List rather than Set because ASN.1 SET_OF does require uniqueness // of elements -- it's an unordered collection. @SuppressWarnings("unchecked") private static List parseSetOf(BerDataValue container, Class elementClass) throws Asn1DecodingException { List result = new ArrayList<>(); BerDataValueReader elementsReader = container.contentsReader(); while (true) { BerDataValue dataValue; try { dataValue = elementsReader.readDataValue(); } catch (BerDataValueFormatException e) { throw new Asn1DecodingException("Malformed data value", e); } if (dataValue == null) { break; } T element; if (ByteBuffer.class.equals(elementClass)) { element = (T) dataValue.getEncodedContents(); } else if (Asn1OpaqueObject.class.equals(elementClass)) { element = (T) new Asn1OpaqueObject(dataValue.getEncoded()); } else { element = parse(dataValue, elementClass); } result.add(element); } return result; } private static Asn1Type getContainerAsn1Type(Class containerClass) throws Asn1DecodingException { Asn1Class containerAnnotation = containerClass.getDeclaredAnnotation(Asn1Class.class); if (containerAnnotation == null) { throw new Asn1DecodingException( containerClass.getName() + " is not annotated with " + Asn1Class.class.getName()); } switch (containerAnnotation.type()) { case CHOICE: case SEQUENCE: case UNENCODED_CONTAINER: return containerAnnotation.type(); default: throw new Asn1DecodingException( "Unsupported ASN.1 container annotation type: " + containerAnnotation.type()); } } private static Class getElementType(Field field) throws Asn1DecodingException, ClassNotFoundException { String type = field.getGenericType().getTypeName(); int delimiterIndex = type.indexOf('<'); if (delimiterIndex == -1) { throw new Asn1DecodingException("Not a container type: " + field.getGenericType()); } int startIndex = delimiterIndex + 1; int endIndex = type.indexOf('>', startIndex); // TODO: handle comma? if (endIndex == -1) { throw new Asn1DecodingException("Not a container type: " + field.getGenericType()); } String elementClassName = type.substring(startIndex, endIndex); return Class.forName(elementClassName); } private static final class AnnotatedField { private final Field mField; private final Asn1Field mAnnotation; private final Asn1Type mDataType; private final Asn1TagClass mTagClass; private final int mBerTagClass; private final int mBerTagNumber; private final Asn1Tagging mTagging; private final boolean mOptional; public AnnotatedField(Field field, Asn1Field annotation) throws Asn1DecodingException { mField = field; mAnnotation = annotation; mDataType = annotation.type(); Asn1TagClass tagClass = annotation.cls(); if (tagClass == Asn1TagClass.AUTOMATIC) { if (annotation.tagNumber() != -1) { tagClass = Asn1TagClass.CONTEXT_SPECIFIC; } else { tagClass = Asn1TagClass.UNIVERSAL; } } mTagClass = tagClass; mBerTagClass = BerEncoding.getTagClass(mTagClass); int tagNumber; if (annotation.tagNumber() != -1) { tagNumber = annotation.tagNumber(); } else if ((mDataType == Asn1Type.CHOICE) || (mDataType == Asn1Type.ANY)) { tagNumber = -1; } else { tagNumber = BerEncoding.getTagNumber(mDataType); } mBerTagNumber = tagNumber; mTagging = annotation.tagging(); if (((mTagging == Asn1Tagging.EXPLICIT) || (mTagging == Asn1Tagging.IMPLICIT)) && (annotation.tagNumber() == -1)) { throw new Asn1DecodingException( "Tag number must be specified when tagging mode is " + mTagging); } mOptional = annotation.optional(); } public Field getField() { return mField; } public Asn1Field getAnnotation() { return mAnnotation; } public boolean isOptional() { return mOptional; } public int getBerTagClass() { return mBerTagClass; } public int getBerTagNumber() { return mBerTagNumber; } public void setValueFrom(BerDataValue dataValue, Object obj) throws Asn1DecodingException { int readTagClass = dataValue.getTagClass(); if (mBerTagNumber != -1) { int readTagNumber = dataValue.getTagNumber(); if ((readTagClass != mBerTagClass) || (readTagNumber != mBerTagNumber)) { throw new Asn1UnexpectedTagException( "Tag mismatch. Expected: " + BerEncoding.tagClassAndNumberToString(mBerTagClass, mBerTagNumber) + ", but found " + BerEncoding.tagClassAndNumberToString(readTagClass, readTagNumber)); } } else { if (readTagClass != mBerTagClass) { throw new Asn1UnexpectedTagException( "Tag mismatch. Expected class: " + BerEncoding.tagClassToString(mBerTagClass) + ", but found " + BerEncoding.tagClassToString(readTagClass)); } } if (mTagging == Asn1Tagging.EXPLICIT) { try { dataValue = dataValue.contentsReader().readDataValue(); } catch (BerDataValueFormatException e) { throw new Asn1DecodingException( "Failed to read contents of EXPLICIT data value", e); } } BerToJavaConverter.setFieldValue(obj, mField, mDataType, dataValue); } } private static class Asn1UnexpectedTagException extends Asn1DecodingException { private static final long serialVersionUID = 1L; public Asn1UnexpectedTagException(String message) { super(message); } } private static String oidToString(ByteBuffer encodedOid) throws Asn1DecodingException { if (!encodedOid.hasRemaining()) { throw new Asn1DecodingException("Empty OBJECT IDENTIFIER"); } // First component encodes the first two nodes, X.Y, as X * 40 + Y, with 0 <= X <= 2 long firstComponent = decodeBase128UnsignedLong(encodedOid); int firstNode = (int) Math.min(firstComponent / 40, 2); long secondNode = firstComponent - firstNode * 40; StringBuilder result = new StringBuilder(); result.append(Long.toString(firstNode)).append('.') .append(Long.toString(secondNode)); // Each consecutive node is encoded as a separate component while (encodedOid.hasRemaining()) { long node = decodeBase128UnsignedLong(encodedOid); result.append('.').append(Long.toString(node)); } return result.toString(); } private static long decodeBase128UnsignedLong(ByteBuffer encoded) throws Asn1DecodingException { if (!encoded.hasRemaining()) { return 0; } long result = 0; while (encoded.hasRemaining()) { if (result > Long.MAX_VALUE >>> 7) { throw new Asn1DecodingException("Base-128 number too large"); } int b = encoded.get() & 0xff; result <<= 7; result |= b & 0x7f; if ((b & 0x80) == 0) { return result; } } throw new Asn1DecodingException( "Truncated base-128 encoded input: missing terminating byte, with highest bit not" + " set"); } private static BigInteger integerToBigInteger(ByteBuffer encoded) { if (!encoded.hasRemaining()) { return BigInteger.ZERO; } return new BigInteger(ByteBufferUtils.toByteArray(encoded)); } private static int integerToInt(ByteBuffer encoded) throws Asn1DecodingException { BigInteger value = integerToBigInteger(encoded); if (value.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || value.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { throw new Asn1DecodingException( String.format("INTEGER cannot be represented as int: %1$d (0x%1$x)", value)); } return value.intValue(); } private static long integerToLong(ByteBuffer encoded) throws Asn1DecodingException { BigInteger value = integerToBigInteger(encoded); if (value.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0 || value.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { throw new Asn1DecodingException( String.format("INTEGER cannot be represented as long: %1$d (0x%1$x)", value)); } return value.longValue(); } private static List getAnnotatedFields(Class containerClass) throws Asn1DecodingException { Field[] declaredFields = containerClass.getDeclaredFields(); List result = new ArrayList<>(declaredFields.length); for (Field field : declaredFields) { Asn1Field annotation = field.getDeclaredAnnotation(Asn1Field.class); if (annotation == null) { continue; } if (Modifier.isStatic(field.getModifiers())) { throw new Asn1DecodingException( Asn1Field.class.getName() + " used on a static field: " + containerClass.getName() + "." + field.getName()); } AnnotatedField annotatedField; try { annotatedField = new AnnotatedField(field, annotation); } catch (Asn1DecodingException e) { throw new Asn1DecodingException( "Invalid ASN.1 annotation on " + containerClass.getName() + "." + field.getName(), e); } result.add(annotatedField); } return result; } private static final class BerToJavaConverter { private BerToJavaConverter() {} public static void setFieldValue( Object obj, Field field, Asn1Type type, BerDataValue dataValue) throws Asn1DecodingException { try { switch (type) { case SET_OF: case SEQUENCE_OF: if (Asn1OpaqueObject.class.equals(field.getType())) { field.set(obj, convert(type, dataValue, field.getType())); } else { field.set(obj, parseSetOf(dataValue, getElementType(field))); } return; default: field.set(obj, convert(type, dataValue, field.getType())); break; } } catch (ReflectiveOperationException e) { throw new Asn1DecodingException( "Failed to set value of " + obj.getClass().getName() + "." + field.getName(), e); } } private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; @SuppressWarnings("unchecked") public static T convert( Asn1Type sourceType, BerDataValue dataValue, Class targetType) throws Asn1DecodingException { if (ByteBuffer.class.equals(targetType)) { return (T) dataValue.getEncodedContents(); } else if (byte[].class.equals(targetType)) { ByteBuffer resultBuf = dataValue.getEncodedContents(); if (!resultBuf.hasRemaining()) { return (T) EMPTY_BYTE_ARRAY; } byte[] result = new byte[resultBuf.remaining()]; resultBuf.get(result); return (T) result; } else if (Asn1OpaqueObject.class.equals(targetType)) { return (T) new Asn1OpaqueObject(dataValue.getEncoded()); } ByteBuffer encodedContents = dataValue.getEncodedContents(); switch (sourceType) { case INTEGER: if ((int.class.equals(targetType)) || (Integer.class.equals(targetType))) { return (T) Integer.valueOf(integerToInt(encodedContents)); } else if ((long.class.equals(targetType)) || (Long.class.equals(targetType))) { return (T) Long.valueOf(integerToLong(encodedContents)); } else if (BigInteger.class.equals(targetType)) { return (T) integerToBigInteger(encodedContents); } break; case OBJECT_IDENTIFIER: if (String.class.equals(targetType)) { return (T) oidToString(encodedContents); } break; case UTC_TIME: case GENERALIZED_TIME: if (String.class.equals(targetType)) { return (T) new String(ByteBufferUtils.toByteArray(encodedContents)); } break; case BOOLEAN: // A boolean should be encoded in a single byte with a value of 0 for false and // any non-zero value for true. if (boolean.class.equals(targetType)) { if (encodedContents.remaining() != 1) { throw new Asn1DecodingException( "Incorrect encoded size of boolean value: " + encodedContents.remaining()); } boolean result; if (encodedContents.get() == 0) { result = false; } else { result = true; } return (T) new Boolean(result); } break; case SEQUENCE: { Asn1Class containerAnnotation = targetType.getDeclaredAnnotation(Asn1Class.class); if ((containerAnnotation != null) && (containerAnnotation.type() == Asn1Type.SEQUENCE)) { return parseSequence(dataValue, targetType); } break; } case CHOICE: { Asn1Class containerAnnotation = targetType.getDeclaredAnnotation(Asn1Class.class); if ((containerAnnotation != null) && (containerAnnotation.type() == Asn1Type.CHOICE)) { return parseChoice(dataValue, targetType); } break; } default: break; } throw new Asn1DecodingException( "Unsupported conversion: ASN.1 " + sourceType + " to " + targetType.getName()); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1Class.java0100644 0000000 0000000 00000000034 14763776540 026345 xustar000000000 0000000 28 mtime=1741684064.5930000 src/main/java/com/android/apksig/internal/asn1/Asn1Class.java0100644 0000000 0000000 00000001666 14763776540 023062 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Asn1Class { public Asn1Type type(); } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1DecodingException.java0100644 0000000 0000000 00000000034 14763776540 030673 xustar000000000 0000000 28 mtime=1741684064.5930000 src/main/java/com/android/apksig/internal/asn1/Asn1DecodingException.java0100644 0000000 0000000 00000002026 14763776540 025377 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; /** * Indicates that input could not be decoded into intended ASN.1 structure. */ public class Asn1DecodingException extends Exception { private static final long serialVersionUID = 1L; public Asn1DecodingException(String message) { super(message); } public Asn1DecodingException(String message, Throwable cause) { super(message, cause); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1DerEncoder.java0100644 0000000 0000000 00000000034 14763776540 027312 xustar000000000 0000000 28 mtime=1741684064.5940000 src/main/java/com/android/apksig/internal/asn1/Asn1DerEncoder.java0100644 0000000 0000000 00000060103 14763776540 024016 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; import com.android.apksig.internal.asn1.ber.BerEncoding; import java.io.ByteArrayOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Encoder of ASN.1 structures into DER-encoded form. * *

Structure is described to the encoder by providing a class annotated with {@link Asn1Class}, * containing fields annotated with {@link Asn1Field}. */ public final class Asn1DerEncoder { private Asn1DerEncoder() {} /** * Returns the DER-encoded form of the provided ASN.1 structure. * * @param container container to be encoded. The container's class must meet the following * requirements: *

    *
  • The class must be annotated with {@link Asn1Class}.
  • *
  • Member fields of the class which are to be encoded must be annotated with * {@link Asn1Field} and be public.
  • *
* * @throws Asn1EncodingException if the input could not be encoded */ public static byte[] encode(Object container) throws Asn1EncodingException { Class containerClass = container.getClass(); Asn1Class containerAnnotation = containerClass.getDeclaredAnnotation(Asn1Class.class); if (containerAnnotation == null) { throw new Asn1EncodingException( containerClass.getName() + " not annotated with " + Asn1Class.class.getName()); } Asn1Type containerType = containerAnnotation.type(); switch (containerType) { case CHOICE: return toChoice(container); case SEQUENCE: return toSequence(container); case UNENCODED_CONTAINER: return toSequence(container, true); default: throw new Asn1EncodingException("Unsupported container type: " + containerType); } } private static byte[] toChoice(Object container) throws Asn1EncodingException { Class containerClass = container.getClass(); List fields = getAnnotatedFields(container); if (fields.isEmpty()) { throw new Asn1EncodingException( "No fields annotated with " + Asn1Field.class.getName() + " in CHOICE class " + containerClass.getName()); } AnnotatedField resultField = null; for (AnnotatedField field : fields) { Object fieldValue = getMemberFieldValue(container, field.getField()); if (fieldValue != null) { if (resultField != null) { throw new Asn1EncodingException( "Multiple non-null fields in CHOICE class " + containerClass.getName() + ": " + resultField.getField().getName() + ", " + field.getField().getName()); } resultField = field; } } if (resultField == null) { throw new Asn1EncodingException( "No non-null fields in CHOICE class " + containerClass.getName()); } return resultField.toDer(); } private static byte[] toSequence(Object container) throws Asn1EncodingException { return toSequence(container, false); } private static byte[] toSequence(Object container, boolean omitTag) throws Asn1EncodingException { Class containerClass = container.getClass(); List fields = getAnnotatedFields(container); Collections.sort( fields, (f1, f2) -> f1.getAnnotation().index() - f2.getAnnotation().index()); if (fields.size() > 1) { AnnotatedField lastField = null; for (AnnotatedField field : fields) { if ((lastField != null) && (lastField.getAnnotation().index() == field.getAnnotation().index())) { throw new Asn1EncodingException( "Fields have the same index: " + containerClass.getName() + "." + lastField.getField().getName() + " and ." + field.getField().getName()); } lastField = field; } } List serializedFields = new ArrayList<>(fields.size()); int contentLen = 0; for (AnnotatedField field : fields) { byte[] serializedField; try { serializedField = field.toDer(); } catch (Asn1EncodingException e) { throw new Asn1EncodingException( "Failed to encode " + containerClass.getName() + "." + field.getField().getName(), e); } if (serializedField != null) { serializedFields.add(serializedField); contentLen += serializedField.length; } } if (omitTag) { byte[] unencodedResult = new byte[contentLen]; int index = 0; for (byte[] serializedField : serializedFields) { System.arraycopy(serializedField, 0, unencodedResult, index, serializedField.length); index += serializedField.length; } return unencodedResult; } else { return createTag( BerEncoding.TAG_CLASS_UNIVERSAL, true, BerEncoding.TAG_NUMBER_SEQUENCE, serializedFields.toArray(new byte[0][])); } } private static byte[] toSetOf(Collection values, Asn1Type elementType) throws Asn1EncodingException { return toSequenceOrSetOf(values, elementType, true); } private static byte[] toSequenceOf(Collection values, Asn1Type elementType) throws Asn1EncodingException { return toSequenceOrSetOf(values, elementType, false); } private static byte[] toSequenceOrSetOf(Collection values, Asn1Type elementType, boolean toSet) throws Asn1EncodingException { List serializedValues = new ArrayList<>(values.size()); for (Object value : values) { serializedValues.add(JavaToDerConverter.toDer(value, elementType, null)); } int tagNumber; if (toSet) { if (serializedValues.size() > 1) { Collections.sort(serializedValues, ByteArrayLexicographicComparator.INSTANCE); } tagNumber = BerEncoding.TAG_NUMBER_SET; } else { tagNumber = BerEncoding.TAG_NUMBER_SEQUENCE; } return createTag( BerEncoding.TAG_CLASS_UNIVERSAL, true, tagNumber, serializedValues.toArray(new byte[0][])); } /** * Compares two bytes arrays based on their lexicographic order. Corresponding elements of the * two arrays are compared in ascending order. Elements at out of range indices are assumed to * be smaller than the smallest possible value for an element. */ private static class ByteArrayLexicographicComparator implements Comparator { private static final ByteArrayLexicographicComparator INSTANCE = new ByteArrayLexicographicComparator(); @Override public int compare(byte[] arr1, byte[] arr2) { int commonLength = Math.min(arr1.length, arr2.length); for (int i = 0; i < commonLength; i++) { int diff = (arr1[i] & 0xff) - (arr2[i] & 0xff); if (diff != 0) { return diff; } } return arr1.length - arr2.length; } } private static List getAnnotatedFields(Object container) throws Asn1EncodingException { Class containerClass = container.getClass(); Field[] declaredFields = containerClass.getDeclaredFields(); List result = new ArrayList<>(declaredFields.length); for (Field field : declaredFields) { Asn1Field annotation = field.getDeclaredAnnotation(Asn1Field.class); if (annotation == null) { continue; } if (Modifier.isStatic(field.getModifiers())) { throw new Asn1EncodingException( Asn1Field.class.getName() + " used on a static field: " + containerClass.getName() + "." + field.getName()); } AnnotatedField annotatedField; try { annotatedField = new AnnotatedField(container, field, annotation); } catch (Asn1EncodingException e) { throw new Asn1EncodingException( "Invalid ASN.1 annotation on " + containerClass.getName() + "." + field.getName(), e); } result.add(annotatedField); } return result; } private static byte[] toInteger(int value) { return toInteger((long) value); } private static byte[] toInteger(long value) { return toInteger(BigInteger.valueOf(value)); } private static byte[] toInteger(BigInteger value) { return createTag( BerEncoding.TAG_CLASS_UNIVERSAL, false, BerEncoding.TAG_NUMBER_INTEGER, value.toByteArray()); } private static byte[] toBoolean(boolean value) { // A boolean should be encoded in a single byte with a value of 0 for false and any non-zero // value for true. byte[] result = new byte[1]; if (value == false) { result[0] = 0; } else { result[0] = 1; } return createTag(BerEncoding.TAG_CLASS_UNIVERSAL, false, BerEncoding.TAG_NUMBER_BOOLEAN, result); } private static byte[] toOid(String oid) throws Asn1EncodingException { ByteArrayOutputStream encodedValue = new ByteArrayOutputStream(); String[] nodes = oid.split("\\."); if (nodes.length < 2) { throw new Asn1EncodingException( "OBJECT IDENTIFIER must contain at least two nodes: " + oid); } int firstNode; try { firstNode = Integer.parseInt(nodes[0]); } catch (NumberFormatException e) { throw new Asn1EncodingException("Node #1 not numeric: " + nodes[0]); } if ((firstNode > 6) || (firstNode < 0)) { throw new Asn1EncodingException("Invalid value for node #1: " + firstNode); } int secondNode; try { secondNode = Integer.parseInt(nodes[1]); } catch (NumberFormatException e) { throw new Asn1EncodingException("Node #2 not numeric: " + nodes[1]); } if ((secondNode >= 40) || (secondNode < 0)) { throw new Asn1EncodingException("Invalid value for node #2: " + secondNode); } int firstByte = firstNode * 40 + secondNode; if (firstByte > 0xff) { throw new Asn1EncodingException( "First two nodes out of range: " + firstNode + "." + secondNode); } encodedValue.write(firstByte); for (int i = 2; i < nodes.length; i++) { String nodeString = nodes[i]; int node; try { node = Integer.parseInt(nodeString); } catch (NumberFormatException e) { throw new Asn1EncodingException("Node #" + (i + 1) + " not numeric: " + nodeString); } if (node < 0) { throw new Asn1EncodingException("Invalid value for node #" + (i + 1) + ": " + node); } if (node <= 0x7f) { encodedValue.write(node); continue; } if (node < 1 << 14) { encodedValue.write(0x80 | (node >> 7)); encodedValue.write(node & 0x7f); continue; } if (node < 1 << 21) { encodedValue.write(0x80 | (node >> 14)); encodedValue.write(0x80 | ((node >> 7) & 0x7f)); encodedValue.write(node & 0x7f); continue; } throw new Asn1EncodingException("Node #" + (i + 1) + " too large: " + node); } return createTag( BerEncoding.TAG_CLASS_UNIVERSAL, false, BerEncoding.TAG_NUMBER_OBJECT_IDENTIFIER, encodedValue.toByteArray()); } private static Object getMemberFieldValue(Object obj, Field field) throws Asn1EncodingException { try { return field.get(obj); } catch (ReflectiveOperationException e) { throw new Asn1EncodingException( "Failed to read " + obj.getClass().getName() + "." + field.getName(), e); } } private static final class AnnotatedField { private final Field mField; private final Object mObject; private final Asn1Field mAnnotation; private final Asn1Type mDataType; private final Asn1Type mElementDataType; private final Asn1TagClass mTagClass; private final int mDerTagClass; private final int mDerTagNumber; private final Asn1Tagging mTagging; private final boolean mOptional; public AnnotatedField(Object obj, Field field, Asn1Field annotation) throws Asn1EncodingException { mObject = obj; mField = field; mAnnotation = annotation; mDataType = annotation.type(); mElementDataType = annotation.elementType(); Asn1TagClass tagClass = annotation.cls(); if (tagClass == Asn1TagClass.AUTOMATIC) { if (annotation.tagNumber() != -1) { tagClass = Asn1TagClass.CONTEXT_SPECIFIC; } else { tagClass = Asn1TagClass.UNIVERSAL; } } mTagClass = tagClass; mDerTagClass = BerEncoding.getTagClass(mTagClass); int tagNumber; if (annotation.tagNumber() != -1) { tagNumber = annotation.tagNumber(); } else if ((mDataType == Asn1Type.CHOICE) || (mDataType == Asn1Type.ANY)) { tagNumber = -1; } else { tagNumber = BerEncoding.getTagNumber(mDataType); } mDerTagNumber = tagNumber; mTagging = annotation.tagging(); if (((mTagging == Asn1Tagging.EXPLICIT) || (mTagging == Asn1Tagging.IMPLICIT)) && (annotation.tagNumber() == -1)) { throw new Asn1EncodingException( "Tag number must be specified when tagging mode is " + mTagging); } mOptional = annotation.optional(); } public Field getField() { return mField; } public Asn1Field getAnnotation() { return mAnnotation; } public byte[] toDer() throws Asn1EncodingException { Object fieldValue = getMemberFieldValue(mObject, mField); if (fieldValue == null) { if (mOptional) { return null; } throw new Asn1EncodingException("Required field not set"); } byte[] encoded = JavaToDerConverter.toDer(fieldValue, mDataType, mElementDataType); switch (mTagging) { case NORMAL: return encoded; case EXPLICIT: return createTag(mDerTagClass, true, mDerTagNumber, encoded); case IMPLICIT: int originalTagNumber = BerEncoding.getTagNumber(encoded[0]); if (originalTagNumber == 0x1f) { throw new Asn1EncodingException("High-tag-number form not supported"); } if (mDerTagNumber >= 0x1f) { throw new Asn1EncodingException( "Unsupported high tag number: " + mDerTagNumber); } encoded[0] = BerEncoding.setTagNumber(encoded[0], mDerTagNumber); encoded[0] = BerEncoding.setTagClass(encoded[0], mDerTagClass); return encoded; default: throw new RuntimeException("Unknown tagging mode: " + mTagging); } } } private static byte[] createTag( int tagClass, boolean constructed, int tagNumber, byte[]... contents) { if (tagNumber >= 0x1f) { throw new IllegalArgumentException("High tag numbers not supported: " + tagNumber); } // tag class & number fit into the first byte byte firstIdentifierByte = (byte) ((tagClass << 6) | (constructed ? 1 << 5 : 0) | tagNumber); int contentsLength = 0; for (byte[] c : contents) { contentsLength += c.length; } int contentsPosInResult; byte[] result; if (contentsLength < 0x80) { // Length fits into one byte contentsPosInResult = 2; result = new byte[contentsPosInResult + contentsLength]; result[0] = firstIdentifierByte; result[1] = (byte) contentsLength; } else { // Length is represented as multiple bytes // The low 7 bits of the first byte represent the number of length bytes (following the // first byte) in which the length is in big-endian base-256 form if (contentsLength <= 0xff) { contentsPosInResult = 3; result = new byte[contentsPosInResult + contentsLength]; result[1] = (byte) 0x81; // 1 length byte result[2] = (byte) contentsLength; } else if (contentsLength <= 0xffff) { contentsPosInResult = 4; result = new byte[contentsPosInResult + contentsLength]; result[1] = (byte) 0x82; // 2 length bytes result[2] = (byte) (contentsLength >> 8); result[3] = (byte) (contentsLength & 0xff); } else if (contentsLength <= 0xffffff) { contentsPosInResult = 5; result = new byte[contentsPosInResult + contentsLength]; result[1] = (byte) 0x83; // 3 length bytes result[2] = (byte) (contentsLength >> 16); result[3] = (byte) ((contentsLength >> 8) & 0xff); result[4] = (byte) (contentsLength & 0xff); } else { contentsPosInResult = 6; result = new byte[contentsPosInResult + contentsLength]; result[1] = (byte) 0x84; // 4 length bytes result[2] = (byte) (contentsLength >> 24); result[3] = (byte) ((contentsLength >> 16) & 0xff); result[4] = (byte) ((contentsLength >> 8) & 0xff); result[5] = (byte) (contentsLength & 0xff); } result[0] = firstIdentifierByte; } for (byte[] c : contents) { System.arraycopy(c, 0, result, contentsPosInResult, c.length); contentsPosInResult += c.length; } return result; } private static final class JavaToDerConverter { private JavaToDerConverter() {} public static byte[] toDer(Object source, Asn1Type targetType, Asn1Type targetElementType) throws Asn1EncodingException { Class sourceType = source.getClass(); if (Asn1OpaqueObject.class.equals(sourceType)) { ByteBuffer buf = ((Asn1OpaqueObject) source).getEncoded(); byte[] result = new byte[buf.remaining()]; buf.get(result); return result; } if ((targetType == null) || (targetType == Asn1Type.ANY)) { return encode(source); } switch (targetType) { case OCTET_STRING: case BIT_STRING: byte[] value = null; if (source instanceof ByteBuffer) { ByteBuffer buf = (ByteBuffer) source; value = new byte[buf.remaining()]; buf.slice().get(value); } else if (source instanceof byte[]) { value = (byte[]) source; } if (value != null) { return createTag( BerEncoding.TAG_CLASS_UNIVERSAL, false, BerEncoding.getTagNumber(targetType), value); } break; case INTEGER: if (source instanceof Integer) { return toInteger((Integer) source); } else if (source instanceof Long) { return toInteger((Long) source); } else if (source instanceof BigInteger) { return toInteger((BigInteger) source); } break; case BOOLEAN: if (source instanceof Boolean) { return toBoolean((Boolean) (source)); } break; case UTC_TIME: case GENERALIZED_TIME: if (source instanceof String) { return createTag(BerEncoding.TAG_CLASS_UNIVERSAL, false, BerEncoding.getTagNumber(targetType), ((String) source).getBytes()); } break; case OBJECT_IDENTIFIER: if (source instanceof String) { return toOid((String) source); } break; case SEQUENCE: { Asn1Class containerAnnotation = sourceType.getDeclaredAnnotation(Asn1Class.class); if ((containerAnnotation != null) && (containerAnnotation.type() == Asn1Type.SEQUENCE)) { return toSequence(source); } break; } case CHOICE: { Asn1Class containerAnnotation = sourceType.getDeclaredAnnotation(Asn1Class.class); if ((containerAnnotation != null) && (containerAnnotation.type() == Asn1Type.CHOICE)) { return toChoice(source); } break; } case SET_OF: return toSetOf((Collection) source, targetElementType); case SEQUENCE_OF: return toSequenceOf((Collection) source, targetElementType); default: break; } throw new Asn1EncodingException( "Unsupported conversion: " + sourceType.getName() + " to ASN.1 " + targetType); } } /** ASN.1 DER-encoded {@code NULL}. */ public static final Asn1OpaqueObject ASN1_DER_NULL = new Asn1OpaqueObject(new byte[] {BerEncoding.TAG_NUMBER_NULL, 0}); } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1EncodingException.java0100644 0000000 0000000 00000000034 14763776540 030705 xustar000000000 0000000 28 mtime=1741684064.5950000 src/main/java/com/android/apksig/internal/asn1/Asn1EncodingException.java0100644 0000000 0000000 00000002005 14763776540 025406 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; /** * Indicates that an ASN.1 structure could not be encoded. */ public class Asn1EncodingException extends Exception { private static final long serialVersionUID = 1L; public Asn1EncodingException(String message) { super(message); } public Asn1EncodingException(String message, Throwable cause) { super(message, cause); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1Field.java0100644 0000000 0000000 00000000034 14763776540 026323 xustar000000000 0000000 28 mtime=1741684064.5950000 src/main/java/com/android/apksig/internal/asn1/Asn1Field.java0100644 0000000 0000000 00000003140 14763776540 023025 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Asn1Field { /** Index used to order fields in a container. Required for fields of SEQUENCE containers. */ public int index() default 0; public Asn1TagClass cls() default Asn1TagClass.AUTOMATIC; public Asn1Type type(); /** Tagging mode. Default: NORMAL. */ public Asn1Tagging tagging() default Asn1Tagging.NORMAL; /** Tag number. Required when IMPLICIT and EXPLICIT tagging mode is used.*/ public int tagNumber() default -1; /** {@code true} if this field is optional. Ignored for fields of CHOICE containers. */ public boolean optional() default false; /** Type of elements. Used only for SET_OF or SEQUENCE_OF. */ public Asn1Type elementType() default Asn1Type.ANY; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1OpaqueObject.java0100644 0000000 0000000 00000000034 14763776540 027661 xustar000000000 0000000 28 mtime=1741684064.5950000 src/main/java/com/android/apksig/internal/asn1/Asn1OpaqueObject.java0100644 0000000 0000000 00000002073 14763776540 024367 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; import java.nio.ByteBuffer; /** * Opaque holder of encoded ASN.1 stuff. */ public class Asn1OpaqueObject { private final ByteBuffer mEncoded; public Asn1OpaqueObject(ByteBuffer encoded) { mEncoded = encoded.slice(); } public Asn1OpaqueObject(byte[] encoded) { mEncoded = ByteBuffer.wrap(encoded); } public ByteBuffer getEncoded() { return mEncoded.slice(); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1TagClass.java0100644 0000000 0000000 00000000034 14763776540 027001 xustar000000000 0000000 28 mtime=1741684064.5950000 src/main/java/com/android/apksig/internal/asn1/Asn1TagClass.java0100644 0000000 0000000 00000001622 14763776540 023506 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; public enum Asn1TagClass { UNIVERSAL, APPLICATION, CONTEXT_SPECIFIC, PRIVATE, /** * Not really an actual tag class: decoder/encoder will attempt to deduce the correct tag class * automatically. */ AUTOMATIC, } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1Tagging.java0100644 0000000 0000000 00000000034 14763776540 026660 xustar000000000 0000000 28 mtime=1741684064.5950000 src/main/java/com/android/apksig/internal/asn1/Asn1Tagging.java0100644 0000000 0000000 00000001334 14763776540 023365 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; public enum Asn1Tagging { NORMAL, EXPLICIT, IMPLICIT, } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_Asn1Type.java0100644 0000000 0000000 00000000034 14763776540 026221 xustar000000000 0000000 28 mtime=1741684064.5950000 src/main/java/com/android/apksig/internal/asn1/Asn1Type.java0100644 0000000 0000000 00000002010 14763776540 022716 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; public enum Asn1Type { ANY, CHOICE, INTEGER, OBJECT_IDENTIFIER, OCTET_STRING, SEQUENCE, SEQUENCE_OF, SET_OF, BIT_STRING, UTC_TIME, GENERALIZED_TIME, BOOLEAN, // This type can be used to annotate classes that encapsulate ASN.1 structures that are not // classified as a SEQUENCE or SET. UNENCODED_CONTAINER } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_ber_0100644 0000000 0000000 00000000034 14763776540 024544 xustar000000000 0000000 28 mtime=1741684064.5950000 src/main/java/com/android/apksig/internal/asn1/ber/0040755 0000000 0000000 00000000000 14763776540 021171 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_ber_BerDataValue.java0100644 0000000 0000000 00000000034 14763776540 027704 xustar000000000 0000000 28 mtime=1741684064.5950000 src/main/java/com/android/apksig/internal/asn1/ber/BerDataValue.java0100644 0000000 0000000 00000006402 14763776540 024332 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; import java.nio.ByteBuffer; /** * ASN.1 Basic Encoding Rules (BER) data value -- see {@code X.690}. */ public class BerDataValue { private final ByteBuffer mEncoded; private final ByteBuffer mEncodedContents; private final int mTagClass; private final boolean mConstructed; private final int mTagNumber; BerDataValue( ByteBuffer encoded, ByteBuffer encodedContents, int tagClass, boolean constructed, int tagNumber) { mEncoded = encoded; mEncodedContents = encodedContents; mTagClass = tagClass; mConstructed = constructed; mTagNumber = tagNumber; } /** * Returns the tag class of this data value. See {@link BerEncoding} {@code TAG_CLASS} * constants. */ public int getTagClass() { return mTagClass; } /** * Returns {@code true} if the content octets of this data value are the complete BER encoding * of one or more data values, {@code false} if the content octets of this data value directly * represent the value. */ public boolean isConstructed() { return mConstructed; } /** * Returns the tag number of this data value. See {@link BerEncoding} {@code TAG_NUMBER} * constants. */ public int getTagNumber() { return mTagNumber; } /** * Returns the encoded form of this data value. */ public ByteBuffer getEncoded() { return mEncoded.slice(); } /** * Returns the encoded contents of this data value. */ public ByteBuffer getEncodedContents() { return mEncodedContents.slice(); } /** * Returns a new reader of the contents of this data value. */ public BerDataValueReader contentsReader() { return new ByteBufferBerDataValueReader(getEncodedContents()); } /** * Returns a new reader which returns just this data value. This may be useful for re-reading * this value in different contexts. */ public BerDataValueReader dataValueReader() { return new ParsedValueReader(this); } private static final class ParsedValueReader implements BerDataValueReader { private final BerDataValue mValue; private boolean mValueOutput; public ParsedValueReader(BerDataValue value) { mValue = value; } @Override public BerDataValue readDataValue() throws BerDataValueFormatException { if (mValueOutput) { return null; } mValueOutput = true; return mValue; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_ber_BerDataValueFormatException.java0100644 0000000 0000000 00000000034 14763776540 032734 xustar000000000 0000000 28 mtime=1741684064.5960000 src/main/java/com/android/apksig/internal/asn1/ber/BerDataValueFormatException.java0100644 0000000 0000000 00000002114 14763776540 027356 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; /** * Indicates that an ASN.1 data value being read could not be decoded using * Basic Encoding Rules (BER). */ public class BerDataValueFormatException extends Exception { private static final long serialVersionUID = 1L; public BerDataValueFormatException(String message) { super(message); } public BerDataValueFormatException(String message, Throwable cause) { super(message, cause); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_ber_BerDataValueReader.java0100644 0000000 0000000 00000000034 14763776540 031027 xustar000000000 0000000 28 mtime=1741684064.5960000 src/main/java/com/android/apksig/internal/asn1/ber/BerDataValueReader.java0100644 0000000 0000000 00000002430 14763776540 025452 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; /** * Reader of ASN.1 Basic Encoding Rules (BER) data values. * *

BER data value reader returns data values, one by one, from a source. The interpretation of * data values (e.g., how to obtain a numeric value from an INTEGER data value, or how to extract * the elements of a SEQUENCE value) is left to clients of the reader. */ public interface BerDataValueReader { /** * Returns the next data value or {@code null} if end of input has been reached. * * @throws BerDataValueFormatException if the value being read is malformed. */ BerDataValue readDataValue() throws BerDataValueFormatException; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_ber_BerEncoding.java0100644 0000000 0000000 00000000034 14763776540 027564 xustar000000000 0000000 28 mtime=1741684064.5960000 src/main/java/com/android/apksig/internal/asn1/ber/BerEncoding.java0100644 0000000 0000000 00000015371 14763776540 024217 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.asn1.Asn1TagClass; /** * ASN.1 Basic Encoding Rules (BER) constants and helper methods. See {@code X.690}. */ public abstract class BerEncoding { private BerEncoding() {} /** * Constructed vs primitive flag in the first identifier byte. */ public static final int ID_FLAG_CONSTRUCTED_ENCODING = 1 << 5; /** * Tag class: UNIVERSAL */ public static final int TAG_CLASS_UNIVERSAL = 0; /** * Tag class: APPLICATION */ public static final int TAG_CLASS_APPLICATION = 1; /** * Tag class: CONTEXT SPECIFIC */ public static final int TAG_CLASS_CONTEXT_SPECIFIC = 2; /** * Tag class: PRIVATE */ public static final int TAG_CLASS_PRIVATE = 3; /** * Tag number: BOOLEAN */ public static final int TAG_NUMBER_BOOLEAN = 0x1; /** * Tag number: INTEGER */ public static final int TAG_NUMBER_INTEGER = 0x2; /** * Tag number: BIT STRING */ public static final int TAG_NUMBER_BIT_STRING = 0x3; /** * Tag number: OCTET STRING */ public static final int TAG_NUMBER_OCTET_STRING = 0x4; /** * Tag number: NULL */ public static final int TAG_NUMBER_NULL = 0x05; /** * Tag number: OBJECT IDENTIFIER */ public static final int TAG_NUMBER_OBJECT_IDENTIFIER = 0x6; /** * Tag number: SEQUENCE */ public static final int TAG_NUMBER_SEQUENCE = 0x10; /** * Tag number: SET */ public static final int TAG_NUMBER_SET = 0x11; /** * Tag number: UTC_TIME */ public final static int TAG_NUMBER_UTC_TIME = 0x17; /** * Tag number: GENERALIZED_TIME */ public final static int TAG_NUMBER_GENERALIZED_TIME = 0x18; public static int getTagNumber(Asn1Type dataType) { switch (dataType) { case INTEGER: return TAG_NUMBER_INTEGER; case OBJECT_IDENTIFIER: return TAG_NUMBER_OBJECT_IDENTIFIER; case OCTET_STRING: return TAG_NUMBER_OCTET_STRING; case BIT_STRING: return TAG_NUMBER_BIT_STRING; case SET_OF: return TAG_NUMBER_SET; case SEQUENCE: case SEQUENCE_OF: return TAG_NUMBER_SEQUENCE; case UTC_TIME: return TAG_NUMBER_UTC_TIME; case GENERALIZED_TIME: return TAG_NUMBER_GENERALIZED_TIME; case BOOLEAN: return TAG_NUMBER_BOOLEAN; default: throw new IllegalArgumentException("Unsupported data type: " + dataType); } } public static int getTagClass(Asn1TagClass tagClass) { switch (tagClass) { case APPLICATION: return TAG_CLASS_APPLICATION; case CONTEXT_SPECIFIC: return TAG_CLASS_CONTEXT_SPECIFIC; case PRIVATE: return TAG_CLASS_PRIVATE; case UNIVERSAL: return TAG_CLASS_UNIVERSAL; default: throw new IllegalArgumentException("Unsupported tag class: " + tagClass); } } public static String tagClassToString(int typeClass) { switch (typeClass) { case TAG_CLASS_APPLICATION: return "APPLICATION"; case TAG_CLASS_CONTEXT_SPECIFIC: return ""; case TAG_CLASS_PRIVATE: return "PRIVATE"; case TAG_CLASS_UNIVERSAL: return "UNIVERSAL"; default: throw new IllegalArgumentException("Unsupported type class: " + typeClass); } } public static String tagClassAndNumberToString(int tagClass, int tagNumber) { String classString = tagClassToString(tagClass); String numberString = tagNumberToString(tagNumber); return classString.isEmpty() ? numberString : classString + " " + numberString; } public static String tagNumberToString(int tagNumber) { switch (tagNumber) { case TAG_NUMBER_INTEGER: return "INTEGER"; case TAG_NUMBER_OCTET_STRING: return "OCTET STRING"; case TAG_NUMBER_BIT_STRING: return "BIT STRING"; case TAG_NUMBER_NULL: return "NULL"; case TAG_NUMBER_OBJECT_IDENTIFIER: return "OBJECT IDENTIFIER"; case TAG_NUMBER_SEQUENCE: return "SEQUENCE"; case TAG_NUMBER_SET: return "SET"; case TAG_NUMBER_BOOLEAN: return "BOOLEAN"; case TAG_NUMBER_GENERALIZED_TIME: return "GENERALIZED TIME"; case TAG_NUMBER_UTC_TIME: return "UTC TIME"; default: return "0x" + Integer.toHexString(tagNumber); } } /** * Returns {@code true} if the provided first identifier byte indicates that the data value uses * constructed encoding for its contents, or {@code false} if the data value uses primitive * encoding for its contents. */ public static boolean isConstructed(byte firstIdentifierByte) { return (firstIdentifierByte & ID_FLAG_CONSTRUCTED_ENCODING) != 0; } /** * Returns the tag class encoded in the provided first identifier byte. See {@code TAG_CLASS} * constants. */ public static int getTagClass(byte firstIdentifierByte) { return (firstIdentifierByte & 0xff) >> 6; } public static byte setTagClass(byte firstIdentifierByte, int tagClass) { return (byte) ((firstIdentifierByte & 0x3f) | (tagClass << 6)); } /** * Returns the tag number encoded in the provided first identifier byte. See {@code TAG_NUMBER} * constants. */ public static int getTagNumber(byte firstIdentifierByte) { return firstIdentifierByte & 0x1f; } public static byte setTagNumber(byte firstIdentifierByte, int tagNumber) { return (byte) ((firstIdentifierByte & ~0x1f) | tagNumber); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_ber_ByteBufferBerDataValueReader.java0100644 0000000 0000000 00000000034 14763776540 033005 xustar000000000 0000000 28 mtime=1741684064.5970000 src/main/java/com/android/apksig/internal/asn1/ber/ByteBufferBerDataValueReader.java0100644 0000000 0000000 00000020010 14763776540 027422 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; import java.nio.ByteBuffer; /** * {@link BerDataValueReader} which reads from a {@link ByteBuffer} containing BER-encoded data * values. See {@code X.690} for the encoding. */ public class ByteBufferBerDataValueReader implements BerDataValueReader { private final ByteBuffer mBuf; public ByteBufferBerDataValueReader(ByteBuffer buf) { if (buf == null) { throw new NullPointerException("buf == null"); } mBuf = buf; } @Override public BerDataValue readDataValue() throws BerDataValueFormatException { int startPosition = mBuf.position(); if (!mBuf.hasRemaining()) { return null; } byte firstIdentifierByte = mBuf.get(); int tagNumber = readTagNumber(firstIdentifierByte); boolean constructed = BerEncoding.isConstructed(firstIdentifierByte); if (!mBuf.hasRemaining()) { throw new BerDataValueFormatException("Missing length"); } int firstLengthByte = mBuf.get() & 0xff; int contentsLength; int contentsOffsetInTag; if ((firstLengthByte & 0x80) == 0) { // short form length contentsLength = readShortFormLength(firstLengthByte); contentsOffsetInTag = mBuf.position() - startPosition; skipDefiniteLengthContents(contentsLength); } else if (firstLengthByte != 0x80) { // long form length contentsLength = readLongFormLength(firstLengthByte); contentsOffsetInTag = mBuf.position() - startPosition; skipDefiniteLengthContents(contentsLength); } else { // indefinite length -- value ends with 0x00 0x00 contentsOffsetInTag = mBuf.position() - startPosition; contentsLength = constructed ? skipConstructedIndefiniteLengthContents() : skipPrimitiveIndefiniteLengthContents(); } // Create the encoded data value ByteBuffer int endPosition = mBuf.position(); mBuf.position(startPosition); int bufOriginalLimit = mBuf.limit(); mBuf.limit(endPosition); ByteBuffer encoded = mBuf.slice(); mBuf.position(mBuf.limit()); mBuf.limit(bufOriginalLimit); // Create the encoded contents ByteBuffer encoded.position(contentsOffsetInTag); encoded.limit(contentsOffsetInTag + contentsLength); ByteBuffer encodedContents = encoded.slice(); encoded.clear(); return new BerDataValue( encoded, encodedContents, BerEncoding.getTagClass(firstIdentifierByte), constructed, tagNumber); } private int readTagNumber(byte firstIdentifierByte) throws BerDataValueFormatException { int tagNumber = BerEncoding.getTagNumber(firstIdentifierByte); if (tagNumber == 0x1f) { // high-tag-number form, where the tag number follows this byte in base-128 // big-endian form, where each byte has the highest bit set, except for the last // byte return readHighTagNumber(); } else { // low-tag-number form return tagNumber; } } private int readHighTagNumber() throws BerDataValueFormatException { // Base-128 big-endian form, where each byte has the highest bit set, except for the last // byte int b; int result = 0; do { if (!mBuf.hasRemaining()) { throw new BerDataValueFormatException("Truncated tag number"); } b = mBuf.get(); if (result > Integer.MAX_VALUE >>> 7) { throw new BerDataValueFormatException("Tag number too large"); } result <<= 7; result |= b & 0x7f; } while ((b & 0x80) != 0); return result; } private int readShortFormLength(int firstLengthByte) { return firstLengthByte & 0x7f; } private int readLongFormLength(int firstLengthByte) throws BerDataValueFormatException { // The low 7 bits of the first byte represent the number of bytes (following the first // byte) in which the length is in big-endian base-256 form int byteCount = firstLengthByte & 0x7f; if (byteCount > 4) { throw new BerDataValueFormatException("Length too large: " + byteCount + " bytes"); } int result = 0; for (int i = 0; i < byteCount; i++) { if (!mBuf.hasRemaining()) { throw new BerDataValueFormatException("Truncated length"); } int b = mBuf.get(); if (result > Integer.MAX_VALUE >>> 8) { throw new BerDataValueFormatException("Length too large"); } result <<= 8; result |= b & 0xff; } return result; } private void skipDefiniteLengthContents(int contentsLength) throws BerDataValueFormatException { if (mBuf.remaining() < contentsLength) { throw new BerDataValueFormatException( "Truncated contents. Need: " + contentsLength + " bytes, available: " + mBuf.remaining()); } mBuf.position(mBuf.position() + contentsLength); } private int skipPrimitiveIndefiniteLengthContents() throws BerDataValueFormatException { // Contents are terminated by 0x00 0x00 boolean prevZeroByte = false; int bytesRead = 0; while (true) { if (!mBuf.hasRemaining()) { throw new BerDataValueFormatException( "Truncated indefinite-length contents: " + bytesRead + " bytes read"); } int b = mBuf.get(); bytesRead++; if (bytesRead < 0) { throw new BerDataValueFormatException("Indefinite-length contents too long"); } if (b == 0) { if (prevZeroByte) { // End of contents reached -- we've read the value and its terminator 0x00 0x00 return bytesRead - 2; } prevZeroByte = true; } else { prevZeroByte = false; } } } private int skipConstructedIndefiniteLengthContents() throws BerDataValueFormatException { // Contents are terminated by 0x00 0x00. However, this data value is constructed, meaning it // can contain data values which are themselves indefinite length encoded. As a result, we // must parse the direct children of this data value to correctly skip over the contents of // this data value. int startPos = mBuf.position(); while (mBuf.hasRemaining()) { // Check whether the 0x00 0x00 terminator is at current position if ((mBuf.remaining() > 1) && (mBuf.getShort(mBuf.position()) == 0)) { int contentsLength = mBuf.position() - startPos; mBuf.position(mBuf.position() + 2); return contentsLength; } // No luck. This must be a BER-encoded data value -- skip over it by parsing it readDataValue(); } throw new BerDataValueFormatException( "Truncated indefinite-length contents: " + (mBuf.position() - startPos) + " bytes read"); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_asn1_ber_InputStreamBerDataValueReader.jav0100644 0000000 0000000 00000000034 14763776540 033062 xustar000000000 0000000 28 mtime=1741684064.5970000 src/main/java/com/android/apksig/internal/asn1/ber/InputStreamBerDataValueReader.java0100644 0000000 0000000 00000026152 14763776540 027655 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; /** * {@link BerDataValueReader} which reads from an {@link InputStream} returning BER-encoded data * values. See {@code X.690} for the encoding. */ public class InputStreamBerDataValueReader implements BerDataValueReader { private final InputStream mIn; public InputStreamBerDataValueReader(InputStream in) { if (in == null) { throw new NullPointerException("in == null"); } mIn = in; } @Override public BerDataValue readDataValue() throws BerDataValueFormatException { return readDataValue(mIn); } /** * Returns the next data value or {@code null} if end of input has been reached. * * @throws BerDataValueFormatException if the value being read is malformed. */ @SuppressWarnings("resource") private static BerDataValue readDataValue(InputStream input) throws BerDataValueFormatException { RecordingInputStream in = new RecordingInputStream(input); try { int firstIdentifierByte = in.read(); if (firstIdentifierByte == -1) { // End of input return null; } int tagNumber = readTagNumber(in, firstIdentifierByte); int firstLengthByte = in.read(); if (firstLengthByte == -1) { throw new BerDataValueFormatException("Missing length"); } boolean constructed = BerEncoding.isConstructed((byte) firstIdentifierByte); int contentsLength; int contentsOffsetInDataValue; if ((firstLengthByte & 0x80) == 0) { // short form length contentsLength = readShortFormLength(firstLengthByte); contentsOffsetInDataValue = in.getReadByteCount(); skipDefiniteLengthContents(in, contentsLength); } else if ((firstLengthByte & 0xff) != 0x80) { // long form length contentsLength = readLongFormLength(in, firstLengthByte); contentsOffsetInDataValue = in.getReadByteCount(); skipDefiniteLengthContents(in, contentsLength); } else { // indefinite length contentsOffsetInDataValue = in.getReadByteCount(); contentsLength = constructed ? skipConstructedIndefiniteLengthContents(in) : skipPrimitiveIndefiniteLengthContents(in); } byte[] encoded = in.getReadBytes(); ByteBuffer encodedContents = ByteBuffer.wrap(encoded, contentsOffsetInDataValue, contentsLength); return new BerDataValue( ByteBuffer.wrap(encoded), encodedContents, BerEncoding.getTagClass((byte) firstIdentifierByte), constructed, tagNumber); } catch (IOException e) { throw new BerDataValueFormatException("Failed to read data value", e); } } private static int readTagNumber(InputStream in, int firstIdentifierByte) throws IOException, BerDataValueFormatException { int tagNumber = BerEncoding.getTagNumber((byte) firstIdentifierByte); if (tagNumber == 0x1f) { // high-tag-number form return readHighTagNumber(in); } else { // low-tag-number form return tagNumber; } } private static int readHighTagNumber(InputStream in) throws IOException, BerDataValueFormatException { // Base-128 big-endian form, where each byte has the highest bit set, except for the last // byte where the highest bit is not set int b; int result = 0; do { b = in.read(); if (b == -1) { throw new BerDataValueFormatException("Truncated tag number"); } if (result > Integer.MAX_VALUE >>> 7) { throw new BerDataValueFormatException("Tag number too large"); } result <<= 7; result |= b & 0x7f; } while ((b & 0x80) != 0); return result; } private static int readShortFormLength(int firstLengthByte) { return firstLengthByte & 0x7f; } private static int readLongFormLength(InputStream in, int firstLengthByte) throws IOException, BerDataValueFormatException { // The low 7 bits of the first byte represent the number of bytes (following the first // byte) in which the length is in big-endian base-256 form int byteCount = firstLengthByte & 0x7f; if (byteCount > 4) { throw new BerDataValueFormatException("Length too large: " + byteCount + " bytes"); } int result = 0; for (int i = 0; i < byteCount; i++) { int b = in.read(); if (b == -1) { throw new BerDataValueFormatException("Truncated length"); } if (result > Integer.MAX_VALUE >>> 8) { throw new BerDataValueFormatException("Length too large"); } result <<= 8; result |= b & 0xff; } return result; } private static void skipDefiniteLengthContents(InputStream in, int len) throws IOException, BerDataValueFormatException { long bytesRead = 0; while (len > 0) { int skipped = (int) in.skip(len); if (skipped <= 0) { throw new BerDataValueFormatException( "Truncated definite-length contents: " + bytesRead + " bytes read" + ", " + len + " missing"); } len -= skipped; bytesRead += skipped; } } private static int skipPrimitiveIndefiniteLengthContents(InputStream in) throws IOException, BerDataValueFormatException { // Contents are terminated by 0x00 0x00 boolean prevZeroByte = false; int bytesRead = 0; while (true) { int b = in.read(); if (b == -1) { throw new BerDataValueFormatException( "Truncated indefinite-length contents: " + bytesRead + " bytes read"); } bytesRead++; if (bytesRead < 0) { throw new BerDataValueFormatException("Indefinite-length contents too long"); } if (b == 0) { if (prevZeroByte) { // End of contents reached -- we've read the value and its terminator 0x00 0x00 return bytesRead - 2; } prevZeroByte = true; continue; } else { prevZeroByte = false; } } } private static int skipConstructedIndefiniteLengthContents(RecordingInputStream in) throws BerDataValueFormatException { // Contents are terminated by 0x00 0x00. However, this data value is constructed, meaning it // can contain data values which are indefinite length encoded as well. As a result, we // must parse the direct children of this data value to correctly skip over the contents of // this data value. int readByteCountBefore = in.getReadByteCount(); while (true) { // We can't easily peek for the 0x00 0x00 terminator using the provided InputStream. // Thus, we use the fact that 0x00 0x00 parses as a data value whose encoded form we // then check below to see whether it's 0x00 0x00. BerDataValue dataValue = readDataValue(in); if (dataValue == null) { throw new BerDataValueFormatException( "Truncated indefinite-length contents: " + (in.getReadByteCount() - readByteCountBefore) + " bytes read"); } if (in.getReadByteCount() <= 0) { throw new BerDataValueFormatException("Indefinite-length contents too long"); } ByteBuffer encoded = dataValue.getEncoded(); if ((encoded.remaining() == 2) && (encoded.get(0) == 0) && (encoded.get(1) == 0)) { // 0x00 0x00 encountered return in.getReadByteCount() - readByteCountBefore - 2; } } } private static class RecordingInputStream extends InputStream { private final InputStream mIn; private final ByteArrayOutputStream mBuf; private RecordingInputStream(InputStream in) { mIn = in; mBuf = new ByteArrayOutputStream(); } public byte[] getReadBytes() { return mBuf.toByteArray(); } public int getReadByteCount() { return mBuf.size(); } @Override public int read() throws IOException { int b = mIn.read(); if (b != -1) { mBuf.write(b); } return b; } @Override public int read(byte[] b) throws IOException { int len = mIn.read(b); if (len > 0) { mBuf.write(b, 0, len); } return len; } @Override public int read(byte[] b, int off, int len) throws IOException { len = mIn.read(b, off, len); if (len > 0) { mBuf.write(b, off, len); } return len; } @Override public long skip(long n) throws IOException { if (n <= 0) { return mIn.skip(n); } byte[] buf = new byte[4096]; int len = mIn.read(buf, 0, (int) Math.min(buf.length, n)); if (len > 0) { mBuf.write(buf, 0, len); } return (len < 0) ? 0 : len; } @Override public int available() throws IOException { return super.available(); } @Override public void close() throws IOException { super.close(); } @Override public synchronized void mark(int readlimit) {} @Override public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } @Override public boolean markSupported() { return false; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_jar_0100644 0000000 0000000 00000000034 14763776540 023626 xustar000000000 0000000 28 mtime=1741684064.5980000 src/main/java/com/android/apksig/internal/jar/0040755 0000000 0000000 00000000000 14763776540 020333 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_jar_ManifestParser.java0100644 0000000 0000000 00000000034 14763776540 027412 xustar000000000 0000000 28 mtime=1741684064.5980000 src/main/java/com/android/apksig/internal/jar/ManifestParser.java0100644 0000000 0000000 00000026500 14763776540 024121 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.jar; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.jar.Attributes; /** * JAR manifest and signature file parser. * *

These files consist of a main section followed by individual sections. Individual sections * are named, their names referring to JAR entries. * * @see JAR Manifest format */ public class ManifestParser { private final byte[] mManifest; private int mOffset; private int mEndOffset; private byte[] mBufferedLine; /** * Constructs a new {@code ManifestParser} with the provided input. */ public ManifestParser(byte[] data) { this(data, 0, data.length); } /** * Constructs a new {@code ManifestParser} with the provided input. */ public ManifestParser(byte[] data, int offset, int length) { mManifest = data; mOffset = offset; mEndOffset = offset + length; } /** * Returns the remaining sections of this file. */ public List

readAllSections() { List
sections = new ArrayList<>(); Section section; while ((section = readSection()) != null) { sections.add(section); } return sections; } /** * Returns the next section from this file or {@code null} if end of file has been reached. */ public Section readSection() { // Locate the first non-empty line int sectionStartOffset; String attr; do { sectionStartOffset = mOffset; attr = readAttribute(); if (attr == null) { return null; } } while (attr.length() == 0); List attrs = new ArrayList<>(); attrs.add(parseAttr(attr)); // Read attributes until end of section reached while (true) { attr = readAttribute(); if ((attr == null) || (attr.length() == 0)) { // End of section break; } attrs.add(parseAttr(attr)); } int sectionEndOffset = mOffset; int sectionSizeBytes = sectionEndOffset - sectionStartOffset; return new Section(sectionStartOffset, sectionSizeBytes, attrs); } private static Attribute parseAttr(String attr) { // Name is separated from value by a semicolon followed by a single SPACE character. // This permits trailing spaces in names and leading and trailing spaces in values. // Some APK obfuscators take advantage of this fact. We thus need to preserve these unusual // spaces to be able to parse such obfuscated APKs. int delimiterIndex = attr.indexOf(": "); if (delimiterIndex == -1) { return new Attribute(attr, ""); } else { return new Attribute( attr.substring(0, delimiterIndex), attr.substring(delimiterIndex + ": ".length())); } } /** * Returns the next attribute or empty {@code String} if end of section has been reached or * {@code null} if end of input has been reached. */ private String readAttribute() { byte[] bytes = readAttributeBytes(); if (bytes == null) { return null; } else if (bytes.length == 0) { return ""; } else { return new String(bytes, StandardCharsets.UTF_8); } } /** * Returns the next attribute or empty array if end of section has been reached or {@code null} * if end of input has been reached. */ private byte[] readAttributeBytes() { // Check whether end of section was reached during previous invocation if ((mBufferedLine != null) && (mBufferedLine.length == 0)) { mBufferedLine = null; return EMPTY_BYTE_ARRAY; } // Read the next line byte[] line = readLine(); if (line == null) { // End of input if (mBufferedLine != null) { byte[] result = mBufferedLine; mBufferedLine = null; return result; } return null; } // Consume the read line if (line.length == 0) { // End of section if (mBufferedLine != null) { byte[] result = mBufferedLine; mBufferedLine = EMPTY_BYTE_ARRAY; return result; } return EMPTY_BYTE_ARRAY; } byte[] attrLine; if (mBufferedLine == null) { attrLine = line; } else { if ((line.length == 0) || (line[0] != ' ')) { // The most common case: buffered line is a full attribute byte[] result = mBufferedLine; mBufferedLine = line; return result; } attrLine = mBufferedLine; mBufferedLine = null; attrLine = concat(attrLine, line, 1, line.length - 1); } // Everything's buffered in attrLine now. mBufferedLine is null // Read more lines while (true) { line = readLine(); if (line == null) { // End of input return attrLine; } else if (line.length == 0) { // End of section mBufferedLine = EMPTY_BYTE_ARRAY; // return "end of section" next time return attrLine; } if (line[0] == ' ') { // Continuation line attrLine = concat(attrLine, line, 1, line.length - 1); } else { // Next attribute mBufferedLine = line; return attrLine; } } } private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; private static byte[] concat(byte[] arr1, byte[] arr2, int offset2, int length2) { byte[] result = new byte[arr1.length + length2]; System.arraycopy(arr1, 0, result, 0, arr1.length); System.arraycopy(arr2, offset2, result, arr1.length, length2); return result; } /** * Returns the next line (without line delimiter characters) or {@code null} if end of input has * been reached. */ private byte[] readLine() { if (mOffset >= mEndOffset) { return null; } int startOffset = mOffset; int newlineStartOffset = -1; int newlineEndOffset = -1; for (int i = startOffset; i < mEndOffset; i++) { byte b = mManifest[i]; if (b == '\r') { newlineStartOffset = i; int nextIndex = i + 1; if ((nextIndex < mEndOffset) && (mManifest[nextIndex] == '\n')) { newlineEndOffset = nextIndex + 1; break; } newlineEndOffset = nextIndex; break; } else if (b == '\n') { newlineStartOffset = i; newlineEndOffset = i + 1; break; } } if (newlineStartOffset == -1) { newlineStartOffset = mEndOffset; newlineEndOffset = mEndOffset; } mOffset = newlineEndOffset; if (newlineStartOffset == startOffset) { return EMPTY_BYTE_ARRAY; } return Arrays.copyOfRange(mManifest, startOffset, newlineStartOffset); } /** * Attribute. */ public static class Attribute { private final String mName; private final String mValue; /** * Constructs a new {@code Attribute} with the provided name and value. */ public Attribute(String name, String value) { mName = name; mValue = value; } /** * Returns this attribute's name. */ public String getName() { return mName; } /** * Returns this attribute's value. */ public String getValue() { return mValue; } } /** * Section. */ public static class Section { private final int mStartOffset; private final int mSizeBytes; private final String mName; private final List mAttributes; /** * Constructs a new {@code Section}. * * @param startOffset start offset (in bytes) of the section in the input file * @param sizeBytes size (in bytes) of the section in the input file * @param attrs attributes contained in the section */ public Section(int startOffset, int sizeBytes, List attrs) { mStartOffset = startOffset; mSizeBytes = sizeBytes; String sectionName = null; if (!attrs.isEmpty()) { Attribute firstAttr = attrs.get(0); if ("Name".equalsIgnoreCase(firstAttr.getName())) { sectionName = firstAttr.getValue(); } } mName = sectionName; mAttributes = Collections.unmodifiableList(new ArrayList<>(attrs)); } public String getName() { return mName; } /** * Returns the offset (in bytes) at which this section starts in the input. */ public int getStartOffset() { return mStartOffset; } /** * Returns the size (in bytes) of this section in the input. */ public int getSizeBytes() { return mSizeBytes; } /** * Returns this section's attributes, in the order in which they appear in the input. */ public List getAttributes() { return mAttributes; } /** * Returns the value of the specified attribute in this section or {@code null} if this * section does not contain a matching attribute. */ public String getAttributeValue(Attributes.Name name) { return getAttributeValue(name.toString()); } /** * Returns the value of the specified attribute in this section or {@code null} if this * section does not contain a matching attribute. * * @param name name of the attribute. Attribute names are case-insensitive. */ public String getAttributeValue(String name) { for (Attribute attr : mAttributes) { if (attr.getName().equalsIgnoreCase(name)) { return attr.getValue(); } } return null; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_jar_ManifestWriter.java0100644 0000000 0000000 00000000034 14763776540 027432 xustar000000000 0000000 28 mtime=1741684064.5980000 src/main/java/com/android/apksig/internal/jar/ManifestWriter.java0100644 0000000 0000000 00000011335 14763776540 024141 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.jar; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.jar.Attributes; /** * Producer of {@code META-INF/MANIFEST.MF} file. * * @see JAR Manifest format */ public abstract class ManifestWriter { private static final byte[] CRLF = new byte[] {'\r', '\n'}; private static final int MAX_LINE_LENGTH = 70; private ManifestWriter() {} public static void writeMainSection(OutputStream out, Attributes attributes) throws IOException { // Main section must start with the Manifest-Version attribute. // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File. String manifestVersion = attributes.getValue(Attributes.Name.MANIFEST_VERSION); if (manifestVersion == null) { throw new IllegalArgumentException( "Mandatory " + Attributes.Name.MANIFEST_VERSION + " attribute missing"); } writeAttribute(out, Attributes.Name.MANIFEST_VERSION, manifestVersion); if (attributes.size() > 1) { SortedMap namedAttributes = getAttributesSortedByName(attributes); namedAttributes.remove(Attributes.Name.MANIFEST_VERSION.toString()); writeAttributes(out, namedAttributes); } writeSectionDelimiter(out); } public static void writeIndividualSection(OutputStream out, String name, Attributes attributes) throws IOException { writeAttribute(out, "Name", name); if (!attributes.isEmpty()) { writeAttributes(out, getAttributesSortedByName(attributes)); } writeSectionDelimiter(out); } static void writeSectionDelimiter(OutputStream out) throws IOException { out.write(CRLF); } static void writeAttribute(OutputStream out, Attributes.Name name, String value) throws IOException { writeAttribute(out, name.toString(), value); } private static void writeAttribute(OutputStream out, String name, String value) throws IOException { writeLine(out, name + ": " + value); } private static void writeLine(OutputStream out, String line) throws IOException { byte[] lineBytes = line.getBytes(StandardCharsets.UTF_8); int offset = 0; int remaining = lineBytes.length; boolean firstLine = true; while (remaining > 0) { int chunkLength; if (firstLine) { // First line chunkLength = Math.min(remaining, MAX_LINE_LENGTH); } else { // Continuation line out.write(CRLF); out.write(' '); chunkLength = Math.min(remaining, MAX_LINE_LENGTH - 1); } out.write(lineBytes, offset, chunkLength); offset += chunkLength; remaining -= chunkLength; firstLine = false; } out.write(CRLF); } static SortedMap getAttributesSortedByName(Attributes attributes) { Set> attributesEntries = attributes.entrySet(); SortedMap namedAttributes = new TreeMap(); for (Map.Entry attribute : attributesEntries) { String attrName = attribute.getKey().toString(); String attrValue = attribute.getValue().toString(); namedAttributes.put(attrName, attrValue); } return namedAttributes; } static void writeAttributes( OutputStream out, SortedMap attributesSortedByName) throws IOException { for (Map.Entry attribute : attributesSortedByName.entrySet()) { String attrName = attribute.getKey(); String attrValue = attribute.getValue(); writeAttribute(out, attrName, attrValue); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_jar_SignatureFileWriter.java0100644 0000000 0000000 00000000034 14763776540 030425 xustar000000000 0000000 28 mtime=1741684064.5980000 src/main/java/com/android/apksig/internal/jar/SignatureFileWriter.java0100644 0000000 0000000 00000004605 14763776540 025136 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.jar; import java.io.IOException; import java.io.OutputStream; import java.util.SortedMap; import java.util.jar.Attributes; /** * Producer of JAR signature file ({@code *.SF}). * * @see JAR Manifest format */ public abstract class SignatureFileWriter { private SignatureFileWriter() {} public static void writeMainSection(OutputStream out, Attributes attributes) throws IOException { // Main section must start with the Signature-Version attribute. // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File. String signatureVersion = attributes.getValue(Attributes.Name.SIGNATURE_VERSION); if (signatureVersion == null) { throw new IllegalArgumentException( "Mandatory " + Attributes.Name.SIGNATURE_VERSION + " attribute missing"); } ManifestWriter.writeAttribute(out, Attributes.Name.SIGNATURE_VERSION, signatureVersion); if (attributes.size() > 1) { SortedMap namedAttributes = ManifestWriter.getAttributesSortedByName(attributes); namedAttributes.remove(Attributes.Name.SIGNATURE_VERSION.toString()); ManifestWriter.writeAttributes(out, namedAttributes); } writeSectionDelimiter(out); } public static void writeIndividualSection(OutputStream out, String name, Attributes attributes) throws IOException { ManifestWriter.writeIndividualSection(out, name, attributes); } public static void writeSectionDelimiter(OutputStream out) throws IOException { ManifestWriter.writeSectionDelimiter(out); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_oid_0100644 0000000 0000000 00000000034 14763776540 023625 xustar000000000 0000000 28 mtime=1741684064.5990000 src/main/java/com/android/apksig/internal/oid/0040755 0000000 0000000 00000000000 14763776540 020332 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_oid_OidConstants.java0100644 0000000 0000000 00000000034 14763776540 027076 xustar000000000 0000000 28 mtime=1741684064.5990000 src/main/java/com/android/apksig/internal/oid/OidConstants.java0100644 0000000 0000000 00000051431 14763776540 023606 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.oid; import com.android.apksig.internal.util.InclusiveIntRange; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; public class OidConstants { public static final String OID_DIGEST_MD5 = "1.2.840.113549.2.5"; public static final String OID_DIGEST_SHA1 = "1.3.14.3.2.26"; public static final String OID_DIGEST_SHA224 = "2.16.840.1.101.3.4.2.4"; public static final String OID_DIGEST_SHA256 = "2.16.840.1.101.3.4.2.1"; public static final String OID_DIGEST_SHA384 = "2.16.840.1.101.3.4.2.2"; public static final String OID_DIGEST_SHA512 = "2.16.840.1.101.3.4.2.3"; public static final String OID_SIG_RSA = "1.2.840.113549.1.1.1"; public static final String OID_SIG_MD5_WITH_RSA = "1.2.840.113549.1.1.4"; public static final String OID_SIG_SHA1_WITH_RSA = "1.2.840.113549.1.1.5"; public static final String OID_SIG_SHA224_WITH_RSA = "1.2.840.113549.1.1.14"; public static final String OID_SIG_SHA256_WITH_RSA = "1.2.840.113549.1.1.11"; public static final String OID_SIG_SHA384_WITH_RSA = "1.2.840.113549.1.1.12"; public static final String OID_SIG_SHA512_WITH_RSA = "1.2.840.113549.1.1.13"; public static final String OID_SIG_DSA = "1.2.840.10040.4.1"; public static final String OID_SIG_SHA1_WITH_DSA = "1.2.840.10040.4.3"; public static final String OID_SIG_SHA224_WITH_DSA = "2.16.840.1.101.3.4.3.1"; public static final String OID_SIG_SHA256_WITH_DSA = "2.16.840.1.101.3.4.3.2"; public static final String OID_SIG_SHA384_WITH_DSA = "2.16.840.1.101.3.4.3.3"; public static final String OID_SIG_SHA512_WITH_DSA = "2.16.840.1.101.3.4.3.4"; public static final String OID_SIG_EC_PUBLIC_KEY = "1.2.840.10045.2.1"; public static final String OID_SIG_SHA1_WITH_ECDSA = "1.2.840.10045.4.1"; public static final String OID_SIG_SHA224_WITH_ECDSA = "1.2.840.10045.4.3.1"; public static final String OID_SIG_SHA256_WITH_ECDSA = "1.2.840.10045.4.3.2"; public static final String OID_SIG_SHA384_WITH_ECDSA = "1.2.840.10045.4.3.3"; public static final String OID_SIG_SHA512_WITH_ECDSA = "1.2.840.10045.4.3.4"; public static final Map> SUPPORTED_SIG_ALG_OIDS = new HashMap<>(); static { addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_RSA, InclusiveIntRange.from(0)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_RSA, InclusiveIntRange.from(0)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.from(0)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 21)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 21)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_RSA, InclusiveIntRange.from(18)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_RSA, InclusiveIntRange.from(18)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_MD5_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_RSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_RSA, InclusiveIntRange.fromTo(21, 21)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_RSA, InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_DSA, InclusiveIntRange.from(0)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.from(9)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_DSA, InclusiveIntRange.from(22)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_DSA, InclusiveIntRange.from(22)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_DSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(18)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(18)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(18)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_EC_PUBLIC_KEY, InclusiveIntRange.from(18)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_MD5, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.from(18)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.from(21)); addSupportedSigAlg( OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_ECDSA, InclusiveIntRange.fromTo(21, 23)); addSupportedSigAlg( OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_ECDSA, InclusiveIntRange.from(21)); } public static void addSupportedSigAlg( String digestAlgorithmOid, String signatureAlgorithmOid, InclusiveIntRange... supportedApiLevels) { SUPPORTED_SIG_ALG_OIDS.put( digestAlgorithmOid + "with" + signatureAlgorithmOid, Arrays.asList(supportedApiLevels)); } public static List getSigAlgSupportedApiLevels( String digestAlgorithmOid, String signatureAlgorithmOid) { List result = SUPPORTED_SIG_ALG_OIDS.get(digestAlgorithmOid + "with" + signatureAlgorithmOid); return (result != null) ? result : Collections.emptyList(); } public static class OidToUserFriendlyNameMapper { private OidToUserFriendlyNameMapper() {} private static final Map OID_TO_USER_FRIENDLY_NAME = new HashMap<>(); static { OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_MD5, "MD5"); OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA1, "SHA-1"); OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA224, "SHA-224"); OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA256, "SHA-256"); OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA384, "SHA-384"); OID_TO_USER_FRIENDLY_NAME.put(OID_DIGEST_SHA512, "SHA-512"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_RSA, "RSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_MD5_WITH_RSA, "MD5 with RSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA1_WITH_RSA, "SHA-1 with RSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA224_WITH_RSA, "SHA-224 with RSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA256_WITH_RSA, "SHA-256 with RSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA384_WITH_RSA, "SHA-384 with RSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA512_WITH_RSA, "SHA-512 with RSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_DSA, "DSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA1_WITH_DSA, "SHA-1 with DSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA224_WITH_DSA, "SHA-224 with DSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA256_WITH_DSA, "SHA-256 with DSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA384_WITH_DSA, "SHA-384 with DSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA512_WITH_DSA, "SHA-512 with DSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_EC_PUBLIC_KEY, "ECDSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA1_WITH_ECDSA, "SHA-1 with ECDSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA224_WITH_ECDSA, "SHA-224 with ECDSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA256_WITH_ECDSA, "SHA-256 with ECDSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA384_WITH_ECDSA, "SHA-384 with ECDSA"); OID_TO_USER_FRIENDLY_NAME.put(OID_SIG_SHA512_WITH_ECDSA, "SHA-512 with ECDSA"); } public static String getUserFriendlyNameForOid(String oid) { return OID_TO_USER_FRIENDLY_NAME.get(oid); } } public static final Map OID_TO_JCA_DIGEST_ALG = new HashMap<>(); static { OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_MD5, "MD5"); OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA1, "SHA-1"); OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA224, "SHA-224"); OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA256, "SHA-256"); OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA384, "SHA-384"); OID_TO_JCA_DIGEST_ALG.put(OID_DIGEST_SHA512, "SHA-512"); } public static final Map OID_TO_JCA_SIGNATURE_ALG = new HashMap<>(); static { OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_MD5_WITH_RSA, "MD5withRSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA1_WITH_RSA, "SHA1withRSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA224_WITH_RSA, "SHA224withRSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA256_WITH_RSA, "SHA256withRSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA384_WITH_RSA, "SHA384withRSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA512_WITH_RSA, "SHA512withRSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA1_WITH_DSA, "SHA1withDSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA224_WITH_DSA, "SHA224withDSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA256_WITH_DSA, "SHA256withDSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA1_WITH_ECDSA, "SHA1withECDSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA224_WITH_ECDSA, "SHA224withECDSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA256_WITH_ECDSA, "SHA256withECDSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA384_WITH_ECDSA, "SHA384withECDSA"); OID_TO_JCA_SIGNATURE_ALG.put(OID_SIG_SHA512_WITH_ECDSA, "SHA512withECDSA"); } private OidConstants() {} } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_0100644 0000000 0000000 00000000034 14763776540 024101 xustar000000000 0000000 28 mtime=1741684064.5990000 src/main/java/com/android/apksig/internal/pkcs7/0040755 0000000 0000000 00000000000 14763776540 020606 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_AlgorithmIdentifier.java0100644 0000000 0000000 00000000034 14763776540 030673 xustar000000000 0000000 28 mtime=1741684064.5990000 src/main/java/com/android/apksig/internal/pkcs7/AlgorithmIdentifier.java0100644 0000000 0000000 00000016720 14763776540 025405 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; import static com.android.apksig.Constants.OID_RSA_ENCRYPTION; import static com.android.apksig.internal.asn1.Asn1DerEncoder.ASN1_DER_NULL; import static com.android.apksig.internal.oid.OidConstants.OID_DIGEST_SHA1; import static com.android.apksig.internal.oid.OidConstants.OID_DIGEST_SHA256; import static com.android.apksig.internal.oid.OidConstants.OID_SIG_DSA; import static com.android.apksig.internal.oid.OidConstants.OID_SIG_EC_PUBLIC_KEY; import static com.android.apksig.internal.oid.OidConstants.OID_SIG_RSA; import static com.android.apksig.internal.oid.OidConstants.OID_SIG_SHA256_WITH_DSA; import static com.android.apksig.internal.oid.OidConstants.OID_TO_JCA_DIGEST_ALG; import static com.android.apksig.internal.oid.OidConstants.OID_TO_JCA_SIGNATURE_ALG; import com.android.apksig.internal.apk.v1.DigestAlgorithm; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.util.Pair; import java.security.InvalidKeyException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; /** * PKCS #7 {@code AlgorithmIdentifier} as specified in RFC 5652. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class AlgorithmIdentifier { @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) public String algorithm; @Asn1Field(index = 1, type = Asn1Type.ANY, optional = true) public Asn1OpaqueObject parameters; public AlgorithmIdentifier() {} public AlgorithmIdentifier(String algorithmOid, Asn1OpaqueObject parameters) { this.algorithm = algorithmOid; this.parameters = parameters; } /** * Returns the PKCS #7 {@code DigestAlgorithm} to use when signing using the specified digest * algorithm. */ public static AlgorithmIdentifier getSignerInfoDigestAlgorithmOid( DigestAlgorithm digestAlgorithm) { switch (digestAlgorithm) { case SHA1: return new AlgorithmIdentifier(OID_DIGEST_SHA1, ASN1_DER_NULL); case SHA256: return new AlgorithmIdentifier(OID_DIGEST_SHA256, ASN1_DER_NULL); } throw new IllegalArgumentException("Unsupported digest algorithm: " + digestAlgorithm); } /** * Returns the JCA {@link Signature} algorithm and PKCS #7 {@code SignatureAlgorithm} to use * when signing with the specified key and digest algorithm. */ public static Pair getSignerInfoSignatureAlgorithm( PublicKey publicKey, DigestAlgorithm digestAlgorithm, boolean deterministicDsaSigning) throws InvalidKeyException { String keyAlgorithm = publicKey.getAlgorithm(); String jcaDigestPrefixForSigAlg; switch (digestAlgorithm) { case SHA1: jcaDigestPrefixForSigAlg = "SHA1"; break; case SHA256: jcaDigestPrefixForSigAlg = "SHA256"; break; default: throw new IllegalArgumentException( "Unexpected digest algorithm: " + digestAlgorithm); } if ("RSA".equalsIgnoreCase(keyAlgorithm) || OID_RSA_ENCRYPTION.equals(keyAlgorithm)) { return Pair.of( jcaDigestPrefixForSigAlg + "withRSA", new AlgorithmIdentifier(OID_SIG_RSA, ASN1_DER_NULL)); } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) { AlgorithmIdentifier sigAlgId; switch (digestAlgorithm) { case SHA1: sigAlgId = new AlgorithmIdentifier(OID_SIG_DSA, ASN1_DER_NULL); break; case SHA256: // DSA signatures with SHA-256 in SignedData are accepted by Android API Level // 21 and higher. However, there are two ways to specify their SignedData // SignatureAlgorithm: dsaWithSha256 (2.16.840.1.101.3.4.3.2) and // dsa (1.2.840.10040.4.1). The latter works only on API Level 22+. Thus, we use // the former. sigAlgId = new AlgorithmIdentifier(OID_SIG_SHA256_WITH_DSA, ASN1_DER_NULL); break; default: throw new IllegalArgumentException( "Unexpected digest algorithm: " + digestAlgorithm); } String signingAlgorithmName = jcaDigestPrefixForSigAlg + (deterministicDsaSigning ? "withDetDSA" : "withDSA"); return Pair.of(signingAlgorithmName, sigAlgId); } else if ("EC".equalsIgnoreCase(keyAlgorithm)) { return Pair.of( jcaDigestPrefixForSigAlg + "withECDSA", new AlgorithmIdentifier(OID_SIG_EC_PUBLIC_KEY, ASN1_DER_NULL)); } else { throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm); } } public static String getJcaSignatureAlgorithm( String digestAlgorithmOid, String signatureAlgorithmOid) throws SignatureException { // First check whether the signature algorithm OID alone is sufficient String result = OID_TO_JCA_SIGNATURE_ALG.get(signatureAlgorithmOid); if (result != null) { return result; } // Signature algorithm OID alone is insufficient. Need to combine digest algorithm OID // with signature algorithm OID. String suffix; if (OID_SIG_RSA.equals(signatureAlgorithmOid)) { suffix = "RSA"; } else if (OID_SIG_DSA.equals(signatureAlgorithmOid)) { suffix = "DSA"; } else if (OID_SIG_EC_PUBLIC_KEY.equals(signatureAlgorithmOid)) { suffix = "ECDSA"; } else { throw new SignatureException( "Unsupported JCA Signature algorithm" + " . Digest algorithm: " + digestAlgorithmOid + ", signature algorithm: " + signatureAlgorithmOid); } String jcaDigestAlg = getJcaDigestAlgorithm(digestAlgorithmOid); // Canonical name for SHA-1 with ... is SHA1with, rather than SHA1. Same for all other // SHA algorithms. if (jcaDigestAlg.startsWith("SHA-")) { jcaDigestAlg = "SHA" + jcaDigestAlg.substring("SHA-".length()); } return jcaDigestAlg + "with" + suffix; } public static String getJcaDigestAlgorithm(String oid) throws SignatureException { String result = OID_TO_JCA_DIGEST_ALG.get(oid); if (result == null) { throw new SignatureException("Unsupported digest algorithm: " + oid); } return result; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_Attribute.java0100644 0000000 0000000 00000000034 14763776540 026705 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/pkcs7/Attribute.java0100644 0000000 0000000 00000002275 14763776540 023417 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.asn1.Asn1Type; import java.util.List; /** * PKCS #7 {@code Attribute} as specified in RFC 5652. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class Attribute { @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) public String attrType; @Asn1Field(index = 1, type = Asn1Type.SET_OF) public List attrValues; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_ContentInfo.java0100644 0000000 0000000 00000000034 14763776540 027170 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/pkcs7/ContentInfo.java0100644 0000000 0000000 00000002405 14763776540 023675 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.asn1.Asn1Tagging; /** * PKCS #7 {@code ContentInfo} as specified in RFC 5652. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class ContentInfo { @Asn1Field(index = 1, type = Asn1Type.OBJECT_IDENTIFIER) public String contentType; @Asn1Field(index = 2, type = Asn1Type.ANY, tagging = Asn1Tagging.EXPLICIT, tagNumber = 0) public Asn1OpaqueObject content; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_EncapsulatedContentInfo.java0100644 0000000 0000000 00000000034 14763776540 031521 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/pkcs7/EncapsulatedContentInfo.java0100644 0000000 0000000 00000002726 14763776540 026234 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.asn1.Asn1Tagging; import java.nio.ByteBuffer; /** * PKCS #7 {@code EncapsulatedContentInfo} as specified in RFC 5652. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class EncapsulatedContentInfo { @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) public String contentType; @Asn1Field( index = 1, type = Asn1Type.OCTET_STRING, tagging = Asn1Tagging.EXPLICIT, tagNumber = 0, optional = true) public ByteBuffer content; public EncapsulatedContentInfo() {} public EncapsulatedContentInfo(String contentTypeOid) { contentType = contentTypeOid; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_IssuerAndSerialNumber.java0100644 0000000 0000000 00000000034 14763776540 031150 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/pkcs7/IssuerAndSerialNumber.java0100644 0000000 0000000 00000002703 14763776540 025656 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.asn1.Asn1Type; import java.math.BigInteger; /** * PKCS #7 {@code IssuerAndSerialNumber} as specified in RFC 5652. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class IssuerAndSerialNumber { @Asn1Field(index = 0, type = Asn1Type.ANY) public Asn1OpaqueObject issuer; @Asn1Field(index = 1, type = Asn1Type.INTEGER) public BigInteger certificateSerialNumber; public IssuerAndSerialNumber() {} public IssuerAndSerialNumber(Asn1OpaqueObject issuer, BigInteger certificateSerialNumber) { this.issuer = issuer; this.certificateSerialNumber = certificateSerialNumber; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_Pkcs7Constants.java0100644 0000000 0000000 00000000034 14763776540 027626 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/pkcs7/Pkcs7Constants.java0100644 0000000 0000000 00000002071 14763776540 024332 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; /** * Assorted PKCS #7 constants from RFC 5652. */ public abstract class Pkcs7Constants { private Pkcs7Constants() {} public static final String OID_DATA = "1.2.840.113549.1.7.1"; public static final String OID_SIGNED_DATA = "1.2.840.113549.1.7.2"; public static final String OID_CONTENT_TYPE = "1.2.840.113549.1.9.3"; public static final String OID_MESSAGE_DIGEST = "1.2.840.113549.1.9.4"; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_Pkcs7DecodingException.java0100644 0000000 0000000 00000000034 14763776540 031245 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/pkcs7/Pkcs7DecodingException.java0100644 0000000 0000000 00000002035 14763776540 025751 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; /** * Indicates that an error was encountered while decoding a PKCS #7 structure. */ public class Pkcs7DecodingException extends Exception { private static final long serialVersionUID = 1L; public Pkcs7DecodingException(String message) { super(message); } public Pkcs7DecodingException(String message, Throwable cause) { super(message, cause); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_SignedData.java0100644 0000000 0000000 00000000034 14763776540 026745 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/pkcs7/SignedData.java0100644 0000000 0000000 00000003553 14763776540 023457 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.asn1.Asn1Tagging; import java.nio.ByteBuffer; import java.util.List; /** * PKCS #7 {@code SignedData} as specified in RFC 5652. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class SignedData { @Asn1Field(index = 0, type = Asn1Type.INTEGER) public int version; @Asn1Field(index = 1, type = Asn1Type.SET_OF) public List digestAlgorithms; @Asn1Field(index = 2, type = Asn1Type.SEQUENCE) public EncapsulatedContentInfo encapContentInfo; @Asn1Field( index = 3, type = Asn1Type.SET_OF, tagging = Asn1Tagging.IMPLICIT, tagNumber = 0, optional = true) public List certificates; @Asn1Field( index = 4, type = Asn1Type.SET_OF, tagging = Asn1Tagging.IMPLICIT, tagNumber = 1, optional = true) public List crls; @Asn1Field(index = 5, type = Asn1Type.SET_OF) public List signerInfos; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_SignerIdentifier.java0100644 0000000 0000000 00000000034 14763776540 030174 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/pkcs7/SignerIdentifier.java0100644 0000000 0000000 00000002651 14763776540 024704 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.asn1.Asn1Tagging; import java.nio.ByteBuffer; /** * PKCS #7 {@code SignerIdentifier} as specified in RFC 5652. */ @Asn1Class(type = Asn1Type.CHOICE) public class SignerIdentifier { @Asn1Field(type = Asn1Type.SEQUENCE) public IssuerAndSerialNumber issuerAndSerialNumber; @Asn1Field(type = Asn1Type.OCTET_STRING, tagging = Asn1Tagging.IMPLICIT, tagNumber = 0) public ByteBuffer subjectKeyIdentifier; public SignerIdentifier() {} public SignerIdentifier(IssuerAndSerialNumber issuerAndSerialNumber) { this.issuerAndSerialNumber = issuerAndSerialNumber; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_pkcs7_SignerInfo.java0100644 0000000 0000000 00000000034 14763776540 027005 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/pkcs7/SignerInfo.java0100644 0000000 0000000 00000003667 14763776540 023525 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.pkcs7; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.asn1.Asn1Tagging; import java.nio.ByteBuffer; import java.util.List; /** * PKCS #7 {@code SignerInfo} as specified in RFC 5652. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class SignerInfo { @Asn1Field(index = 0, type = Asn1Type.INTEGER) public int version; @Asn1Field(index = 1, type = Asn1Type.CHOICE) public SignerIdentifier sid; @Asn1Field(index = 2, type = Asn1Type.SEQUENCE) public AlgorithmIdentifier digestAlgorithm; @Asn1Field( index = 3, type = Asn1Type.SET_OF, tagging = Asn1Tagging.IMPLICIT, tagNumber = 0, optional = true) public Asn1OpaqueObject signedAttrs; @Asn1Field(index = 4, type = Asn1Type.SEQUENCE) public AlgorithmIdentifier signatureAlgorithm; @Asn1Field(index = 5, type = Asn1Type.OCTET_STRING) public ByteBuffer signature; @Asn1Field( index = 6, type = Asn1Type.SET_OF, tagging = Asn1Tagging.IMPLICIT, tagNumber = 1, optional = true) public List unsignedAttrs; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_0100644 0000000 0000000 00000000034 14763776540 024027 xustar000000000 0000000 28 mtime=1741684064.6000000 src/main/java/com/android/apksig/internal/util/0040755 0000000 0000000 00000000000 14763776540 020534 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_AndroidSdkVersion.java0100644 0000000 0000000 00000000034 14763776540 030260 xustar000000000 0000000 28 mtime=1741684064.6010000 src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java0100644 0000000 0000000 00000003767 14763776540 025001 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; /** * Android SDK version / API Level constants. */ public abstract class AndroidSdkVersion { /** Hidden constructor to prevent instantiation. */ private AndroidSdkVersion() {} /** Android 1.0 */ public static final int INITIAL_RELEASE = 1; /** Android 2.3. */ public static final int GINGERBREAD = 9; /** Android 3.0 */ public static final int HONEYCOMB = 11; /** Android 4.3. The revenge of the beans. */ public static final int JELLY_BEAN_MR2 = 18; /** Android 4.4. KitKat, another tasty treat. */ public static final int KITKAT = 19; /** Android 5.0. A flat one with beautiful shadows. But still tasty. */ public static final int LOLLIPOP = 21; /** Android 6.0. M is for Marshmallow! */ public static final int M = 23; /** Android 7.0. N is for Nougat. */ public static final int N = 24; /** Android O. */ public static final int O = 26; /** Android P. */ public static final int P = 28; /** Android Q. */ public static final int Q = 29; /** Android R. */ public static final int R = 30; /** Android S. */ public static final int S = 31; /** Android Sv2. */ public static final int Sv2 = 32; /** Android Tiramisu. */ public static final int T = 33; /** Android Upside Down Cake. */ public static final int U = 34; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_ByteArrayDataSink.java0100644 0000000 0000000 00000000034 14763776540 030211 xustar000000000 0000000 28 mtime=1741684064.6010000 src/main/java/com/android/apksig/internal/util/ByteArrayDataSink.java0100644 0000000 0000000 00000021030 14763776540 024711 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import com.android.apksig.util.ReadableDataSink; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; /** * Growable byte array which can be appended to via {@link DataSink} interface and read from via * {@link DataSource} interface. */ public class ByteArrayDataSink implements ReadableDataSink { private static final int MAX_READ_CHUNK_SIZE = 65536; private byte[] mArray; private int mSize; public ByteArrayDataSink() { this(65536); } public ByteArrayDataSink(int initialCapacity) { if (initialCapacity < 0) { throw new IllegalArgumentException("initial capacity: " + initialCapacity); } mArray = new byte[initialCapacity]; } @Override public void consume(byte[] buf, int offset, int length) throws IOException { if (offset < 0) { // Must perform this check because System.arraycopy below doesn't perform it when // length == 0 throw new IndexOutOfBoundsException("offset: " + offset); } if (offset > buf.length) { // Must perform this check because System.arraycopy below doesn't perform it when // length == 0 throw new IndexOutOfBoundsException( "offset: " + offset + ", buf.length: " + buf.length); } if (length == 0) { return; } ensureAvailable(length); System.arraycopy(buf, offset, mArray, mSize, length); mSize += length; } @Override public void consume(ByteBuffer buf) throws IOException { if (!buf.hasRemaining()) { return; } if (buf.hasArray()) { consume(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining()); buf.position(buf.limit()); return; } ensureAvailable(buf.remaining()); byte[] tmp = new byte[Math.min(buf.remaining(), MAX_READ_CHUNK_SIZE)]; while (buf.hasRemaining()) { int chunkSize = Math.min(buf.remaining(), tmp.length); buf.get(tmp, 0, chunkSize); System.arraycopy(tmp, 0, mArray, mSize, chunkSize); mSize += chunkSize; } } private void ensureAvailable(int minAvailable) throws IOException { if (minAvailable <= 0) { return; } long minCapacity = ((long) mSize) + minAvailable; if (minCapacity <= mArray.length) { return; } if (minCapacity > Integer.MAX_VALUE) { throw new IOException( "Required capacity too large: " + minCapacity + ", max: " + Integer.MAX_VALUE); } int doubleCurrentSize = (int) Math.min(mArray.length * 2L, Integer.MAX_VALUE); int newSize = (int) Math.max(minCapacity, doubleCurrentSize); mArray = Arrays.copyOf(mArray, newSize); } @Override public long size() { return mSize; } @Override public ByteBuffer getByteBuffer(long offset, int size) { checkChunkValid(offset, size); // checkChunkValid ensures that it's OK to cast offset to int. return ByteBuffer.wrap(mArray, (int) offset, size).slice(); } @Override public void feed(long offset, long size, DataSink sink) throws IOException { checkChunkValid(offset, size); // checkChunkValid ensures that it's OK to cast offset and size to int. sink.consume(mArray, (int) offset, (int) size); } @Override public void copyTo(long offset, int size, ByteBuffer dest) throws IOException { checkChunkValid(offset, size); // checkChunkValid ensures that it's OK to cast offset to int. dest.put(mArray, (int) offset, size); } private void checkChunkValid(long offset, long size) { if (offset < 0) { throw new IndexOutOfBoundsException("offset: " + offset); } if (size < 0) { throw new IndexOutOfBoundsException("size: " + size); } if (offset > mSize) { throw new IndexOutOfBoundsException( "offset (" + offset + ") > source size (" + mSize + ")"); } long endOffset = offset + size; if (endOffset < offset) { throw new IndexOutOfBoundsException( "offset (" + offset + ") + size (" + size + ") overflow"); } if (endOffset > mSize) { throw new IndexOutOfBoundsException( "offset (" + offset + ") + size (" + size + ") > source size (" + mSize + ")"); } } @Override public DataSource slice(long offset, long size) { checkChunkValid(offset, size); // checkChunkValid ensures that it's OK to cast offset and size to int. return new SliceDataSource((int) offset, (int) size); } /** * Slice of the growable byte array. The slice's offset and size in the array are fixed. */ private class SliceDataSource implements DataSource { private final int mSliceOffset; private final int mSliceSize; private SliceDataSource(int offset, int size) { mSliceOffset = offset; mSliceSize = size; } @Override public long size() { return mSliceSize; } @Override public void feed(long offset, long size, DataSink sink) throws IOException { checkChunkValid(offset, size); // checkChunkValid combined with the way instances of this class are constructed ensures // that mSliceOffset + offset does not overflow and that it's fine to cast size to int. sink.consume(mArray, (int) (mSliceOffset + offset), (int) size); } @Override public ByteBuffer getByteBuffer(long offset, int size) throws IOException { checkChunkValid(offset, size); // checkChunkValid combined with the way instances of this class are constructed ensures // that mSliceOffset + offset does not overflow. return ByteBuffer.wrap(mArray, (int) (mSliceOffset + offset), size).slice(); } @Override public void copyTo(long offset, int size, ByteBuffer dest) throws IOException { checkChunkValid(offset, size); // checkChunkValid combined with the way instances of this class are constructed ensures // that mSliceOffset + offset does not overflow. dest.put(mArray, (int) (mSliceOffset + offset), size); } @Override public DataSource slice(long offset, long size) { checkChunkValid(offset, size); // checkChunkValid combined with the way instances of this class are constructed ensures // that mSliceOffset + offset does not overflow and that it's fine to cast size to int. return new SliceDataSource((int) (mSliceOffset + offset), (int) size); } private void checkChunkValid(long offset, long size) { if (offset < 0) { throw new IndexOutOfBoundsException("offset: " + offset); } if (size < 0) { throw new IndexOutOfBoundsException("size: " + size); } if (offset > mSliceSize) { throw new IndexOutOfBoundsException( "offset (" + offset + ") > source size (" + mSliceSize + ")"); } long endOffset = offset + size; if (endOffset < offset) { throw new IndexOutOfBoundsException( "offset (" + offset + ") + size (" + size + ") overflow"); } if (endOffset > mSliceSize) { throw new IndexOutOfBoundsException( "offset (" + offset + ") + size (" + size + ") > source size (" + mSliceSize + ")"); } } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_ByteBufferDataSource.java0100644 0000000 0000000 00000000034 14763776540 030700 xustar000000000 0000000 28 mtime=1741684064.6010000 src/main/java/com/android/apksig/internal/util/ByteBufferDataSource.java0100644 0000000 0000000 00000010646 14763776540 025413 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import java.io.IOException; import java.nio.ByteBuffer; /** * {@link DataSource} backed by a {@link ByteBuffer}. */ public class ByteBufferDataSource implements DataSource { private final ByteBuffer mBuffer; private final int mSize; /** * Constructs a new {@code ByteBufferDigestSource} based on the data contained in the provided * buffer between the buffer's position and limit. */ public ByteBufferDataSource(ByteBuffer buffer) { this(buffer, true); } /** * Constructs a new {@code ByteBufferDigestSource} based on the data contained in the provided * buffer between the buffer's position and limit. */ private ByteBufferDataSource(ByteBuffer buffer, boolean sliceRequired) { mBuffer = (sliceRequired) ? buffer.slice() : buffer; mSize = buffer.remaining(); } @Override public long size() { return mSize; } @Override public ByteBuffer getByteBuffer(long offset, int size) { checkChunkValid(offset, size); // checkChunkValid ensures that it's OK to cast offset to int. int chunkPosition = (int) offset; int chunkLimit = chunkPosition + size; // Creating a slice of ByteBuffer modifies the state of the source ByteBuffer (position // and limit fields, to be more specific). We thus use synchronization around these // state-changing operations to make instances of this class thread-safe. synchronized (mBuffer) { // ByteBuffer.limit(int) and .position(int) check that that the position >= limit // invariant is not broken. Thus, the only way to safely change position and limit // without caring about their current values is to first set position to 0 or set the // limit to capacity. mBuffer.position(0); mBuffer.limit(chunkLimit); mBuffer.position(chunkPosition); return mBuffer.slice(); } } @Override public void copyTo(long offset, int size, ByteBuffer dest) { dest.put(getByteBuffer(offset, size)); } @Override public void feed(long offset, long size, DataSink sink) throws IOException { if ((size < 0) || (size > mSize)) { throw new IndexOutOfBoundsException("size: " + size + ", source size: " + mSize); } sink.consume(getByteBuffer(offset, (int) size)); } @Override public ByteBufferDataSource slice(long offset, long size) { if ((offset == 0) && (size == mSize)) { return this; } if ((size < 0) || (size > mSize)) { throw new IndexOutOfBoundsException("size: " + size + ", source size: " + mSize); } return new ByteBufferDataSource( getByteBuffer(offset, (int) size), false // no need to slice -- it's already a slice ); } private void checkChunkValid(long offset, long size) { if (offset < 0) { throw new IndexOutOfBoundsException("offset: " + offset); } if (size < 0) { throw new IndexOutOfBoundsException("size: " + size); } if (offset > mSize) { throw new IndexOutOfBoundsException( "offset (" + offset + ") > source size (" + mSize + ")"); } long endOffset = offset + size; if (endOffset < offset) { throw new IndexOutOfBoundsException( "offset (" + offset + ") + size (" + size + ") overflow"); } if (endOffset > mSize) { throw new IndexOutOfBoundsException( "offset (" + offset + ") + size (" + size + ") > source size (" + mSize +")"); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_ByteBufferSink.java0100644 0000000 0000000 00000000034 14763776540 027552 xustar000000000 0000000 28 mtime=1741684064.6010000 src/main/java/com/android/apksig/internal/util/ByteBufferSink.java0100644 0000000 0000000 00000003431 14763776540 024257 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; /** * Data sink which stores all received data into the associated {@link ByteBuffer}. */ public class ByteBufferSink implements DataSink { private final ByteBuffer mBuffer; public ByteBufferSink(ByteBuffer buffer) { mBuffer = buffer; } public ByteBuffer getBuffer() { return mBuffer; } @Override public void consume(byte[] buf, int offset, int length) throws IOException { try { mBuffer.put(buf, offset, length); } catch (BufferOverflowException e) { throw new IOException( "Insufficient space in output buffer for " + length + " bytes", e); } } @Override public void consume(ByteBuffer buf) throws IOException { int length = buf.remaining(); try { mBuffer.put(buf); } catch (BufferOverflowException e) { throw new IOException( "Insufficient space in output buffer for " + length + " bytes", e); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_ByteBufferUtils.java0100644 0000000 0000000 00000000034 14763776540 027746 xustar000000000 0000000 28 mtime=1741684064.6010000 src/main/java/com/android/apksig/internal/util/ByteBufferUtils.java0100644 0000000 0000000 00000002101 14763776540 024444 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import java.nio.ByteBuffer; public final class ByteBufferUtils { private ByteBufferUtils() {} /** * Returns the remaining data of the provided buffer as a new byte array and advances the * position of the buffer to the buffer's limit. */ public static byte[] toByteArray(ByteBuffer buf) { byte[] result = new byte[buf.remaining()]; buf.get(result); return result; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_ByteStreams.java0100644 0000000 0000000 00000000034 14763776540 027132 xustar000000000 0000000 28 mtime=1741684064.6010000 src/main/java/com/android/apksig/internal/util/ByteStreams.java0100644 0000000 0000000 00000002455 14763776540 023644 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; /** * Utilities for byte arrays and I/O streams. */ public final class ByteStreams { private ByteStreams() {} /** * Returns the data remaining in the provided input stream as a byte array */ public static byte[] toByteArray(InputStream in) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream(); byte[] buf = new byte[16384]; int chunkSize; while ((chunkSize = in.read(buf)) != -1) { result.write(buf, 0, chunkSize); } return result.toByteArray(); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_ChainedDataSource.java0100644 0000000 0000000 00000000034 14763776540 030176 xustar000000000 0000000 28 mtime=1741684064.6020000 src/main/java/com/android/apksig/internal/util/ChainedDataSource.java0100644 0000000 0000000 00000012156 14763776540 024707 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; /** Pseudo {@link DataSource} that chains the given {@link DataSource} as a continuous one. */ public class ChainedDataSource implements DataSource { private final DataSource[] mSources; private final long mTotalSize; public ChainedDataSource(DataSource... sources) { mSources = sources; mTotalSize = Arrays.stream(sources).mapToLong(src -> src.size()).sum(); } @Override public long size() { return mTotalSize; } @Override public void feed(long offset, long size, DataSink sink) throws IOException { if (offset + size > mTotalSize) { throw new IndexOutOfBoundsException("Requested more than available"); } for (DataSource src : mSources) { // Offset is beyond the current source. Skip. if (offset >= src.size()) { offset -= src.size(); continue; } // If the remaining is enough, finish it. long remaining = src.size() - offset; if (remaining >= size) { src.feed(offset, size, sink); break; } // If the remaining is not enough, consume all. src.feed(offset, remaining, sink); size -= remaining; offset = 0; } } @Override public ByteBuffer getByteBuffer(long offset, int size) throws IOException { if (offset + size > mTotalSize) { throw new IndexOutOfBoundsException("Requested more than available"); } // Skip to the first DataSource we need. Pair firstSource = locateDataSource(offset); int i = firstSource.getFirst(); offset = firstSource.getSecond(); // Return the current source's ByteBuffer if it fits. if (offset + size <= mSources[i].size()) { return mSources[i].getByteBuffer(offset, size); } // Otherwise, read into a new buffer. ByteBuffer buffer = ByteBuffer.allocate(size); for (; i < mSources.length && buffer.hasRemaining(); i++) { long sizeToCopy = Math.min(mSources[i].size() - offset, buffer.remaining()); mSources[i].copyTo(offset, Math.toIntExact(sizeToCopy), buffer); offset = 0; // may not be zero for the first source, but reset after that. } buffer.rewind(); return buffer; } @Override public void copyTo(long offset, int size, ByteBuffer dest) throws IOException { feed(offset, size, new ByteBufferSink(dest)); } @Override public DataSource slice(long offset, long size) { // Find the first slice. Pair firstSource = locateDataSource(offset); int beginIndex = firstSource.getFirst(); long beginLocalOffset = firstSource.getSecond(); DataSource beginSource = mSources[beginIndex]; if (beginLocalOffset + size <= beginSource.size()) { return beginSource.slice(beginLocalOffset, size); } // Add the first slice to chaining, followed by the middle full slices, then the last. ArrayList sources = new ArrayList<>(); sources.add(beginSource.slice( beginLocalOffset, beginSource.size() - beginLocalOffset)); Pair lastSource = locateDataSource(offset + size - 1); int endIndex = lastSource.getFirst(); long endLocalOffset = lastSource.getSecond(); for (int i = beginIndex + 1; i < endIndex; i++) { sources.add(mSources[i]); } sources.add(mSources[endIndex].slice(0, endLocalOffset + 1)); return new ChainedDataSource(sources.toArray(new DataSource[0])); } /** * Find the index of DataSource that offset is at. * @return Pair of DataSource index and the local offset in the DataSource. */ private Pair locateDataSource(long offset) { long localOffset = offset; for (int i = 0; i < mSources.length; i++) { if (localOffset < mSources[i].size()) { return Pair.of(i, localOffset); } localOffset -= mSources[i].size(); } throw new IndexOutOfBoundsException("Access is out of bound, offset: " + offset + ", totalSize: " + mTotalSize); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_DelegatingX509Certificate.java0100644 0000000 0000000 00000000034 14763776540 031464 xustar000000000 0000000 28 mtime=1741684064.6020000 src/main/java/com/android/apksig/internal/util/DelegatingX509Certificate.java0100644 0000000 0000000 00000013701 14763776540 026172 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Principal; import java.security.Provider; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Set; import javax.security.auth.x500.X500Principal; /** * {@link X509Certificate} which delegates all method invocations to the provided delegate * {@code X509Certificate}. */ public class DelegatingX509Certificate extends X509Certificate { private static final long serialVersionUID = 1L; private final X509Certificate mDelegate; public DelegatingX509Certificate(X509Certificate delegate) { this.mDelegate = delegate; } @Override public Set getCriticalExtensionOIDs() { return mDelegate.getCriticalExtensionOIDs(); } @Override public byte[] getExtensionValue(String oid) { return mDelegate.getExtensionValue(oid); } @Override public Set getNonCriticalExtensionOIDs() { return mDelegate.getNonCriticalExtensionOIDs(); } @Override public boolean hasUnsupportedCriticalExtension() { return mDelegate.hasUnsupportedCriticalExtension(); } @Override public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { mDelegate.checkValidity(); } @Override public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException { mDelegate.checkValidity(date); } @Override public int getVersion() { return mDelegate.getVersion(); } @Override public BigInteger getSerialNumber() { return mDelegate.getSerialNumber(); } @Override public Principal getIssuerDN() { return mDelegate.getIssuerDN(); } @Override public Principal getSubjectDN() { return mDelegate.getSubjectDN(); } @Override public Date getNotBefore() { return mDelegate.getNotBefore(); } @Override public Date getNotAfter() { return mDelegate.getNotAfter(); } @Override public byte[] getTBSCertificate() throws CertificateEncodingException { return mDelegate.getTBSCertificate(); } @Override public byte[] getSignature() { return mDelegate.getSignature(); } @Override public String getSigAlgName() { return mDelegate.getSigAlgName(); } @Override public String getSigAlgOID() { return mDelegate.getSigAlgOID(); } @Override public byte[] getSigAlgParams() { return mDelegate.getSigAlgParams(); } @Override public boolean[] getIssuerUniqueID() { return mDelegate.getIssuerUniqueID(); } @Override public boolean[] getSubjectUniqueID() { return mDelegate.getSubjectUniqueID(); } @Override public boolean[] getKeyUsage() { return mDelegate.getKeyUsage(); } @Override public int getBasicConstraints() { return mDelegate.getBasicConstraints(); } @Override public byte[] getEncoded() throws CertificateEncodingException { return mDelegate.getEncoded(); } @Override public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { mDelegate.verify(key); } @Override public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { mDelegate.verify(key, sigProvider); } @Override public String toString() { return mDelegate.toString(); } @Override public PublicKey getPublicKey() { return mDelegate.getPublicKey(); } @Override public X500Principal getIssuerX500Principal() { return mDelegate.getIssuerX500Principal(); } @Override public X500Principal getSubjectX500Principal() { return mDelegate.getSubjectX500Principal(); } @Override public List getExtendedKeyUsage() throws CertificateParsingException { return mDelegate.getExtendedKeyUsage(); } @Override public Collection> getSubjectAlternativeNames() throws CertificateParsingException { return mDelegate.getSubjectAlternativeNames(); } @Override public Collection> getIssuerAlternativeNames() throws CertificateParsingException { return mDelegate.getIssuerAlternativeNames(); } @Override @SuppressWarnings("AndroidJdkLibsChecker") public void verify(PublicKey key, Provider sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { mDelegate.verify(key, sigProvider); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_FileChannelDataSource.java0100644 0000000 0000000 00000000034 14763776540 031013 xustar000000000 0000000 28 mtime=1741684064.6020000 src/main/java/com/android/apksig/internal/util/FileChannelDataSource.java0100644 0000000 0000000 00000014426 14763776540 025526 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * {@link DataSource} backed by a {@link FileChannel} for {@link RandomAccessFile} access. */ public class FileChannelDataSource implements DataSource { private static final int MAX_READ_CHUNK_SIZE = 1024 * 1024; private final FileChannel mChannel; private final long mOffset; private final long mSize; /** * Constructs a new {@code FileChannelDataSource} based on the data contained in the * whole file. Changes to the contents of the file, including the size of the file, * will be visible in this data source. */ public FileChannelDataSource(FileChannel channel) { mChannel = channel; mOffset = 0; mSize = -1; } /** * Constructs a new {@code FileChannelDataSource} based on the data contained in the * specified region of the provided file. Changes to the contents of the file will be visible in * this data source. * * @throws IndexOutOfBoundsException if {@code offset} or {@code size} is negative. */ public FileChannelDataSource(FileChannel channel, long offset, long size) { if (offset < 0) { throw new IndexOutOfBoundsException("offset: " + size); } if (size < 0) { throw new IndexOutOfBoundsException("size: " + size); } mChannel = channel; mOffset = offset; mSize = size; } @Override public long size() { if (mSize == -1) { try { return mChannel.size(); } catch (IOException e) { return 0; } } else { return mSize; } } @Override public FileChannelDataSource slice(long offset, long size) { long sourceSize = size(); checkChunkValid(offset, size, sourceSize); if ((offset == 0) && (size == sourceSize)) { return this; } return new FileChannelDataSource(mChannel, mOffset + offset, size); } @Override public void feed(long offset, long size, DataSink sink) throws IOException { long sourceSize = size(); checkChunkValid(offset, size, sourceSize); if (size == 0) { return; } long chunkOffsetInFile = mOffset + offset; long remaining = size; ByteBuffer buf = ByteBuffer.allocateDirect((int) Math.min(remaining, MAX_READ_CHUNK_SIZE)); while (remaining > 0) { int chunkSize = (int) Math.min(remaining, buf.capacity()); int chunkRemaining = chunkSize; buf.limit(chunkSize); synchronized (mChannel) { mChannel.position(chunkOffsetInFile); while (chunkRemaining > 0) { int read = mChannel.read(buf); if (read < 0) { throw new IOException("Unexpected EOF encountered"); } chunkRemaining -= read; } } buf.flip(); sink.consume(buf); buf.clear(); chunkOffsetInFile += chunkSize; remaining -= chunkSize; } } @Override public void copyTo(long offset, int size, ByteBuffer dest) throws IOException { long sourceSize = size(); checkChunkValid(offset, size, sourceSize); if (size == 0) { return; } if (size > dest.remaining()) { throw new BufferOverflowException(); } long offsetInFile = mOffset + offset; int remaining = size; int prevLimit = dest.limit(); try { // FileChannel.read(ByteBuffer) reads up to dest.remaining(). Thus, we need to adjust // the buffer's limit to avoid reading more than size bytes. dest.limit(dest.position() + size); while (remaining > 0) { int chunkSize; synchronized (mChannel) { mChannel.position(offsetInFile); chunkSize = mChannel.read(dest); } offsetInFile += chunkSize; remaining -= chunkSize; } } finally { dest.limit(prevLimit); } } @Override public ByteBuffer getByteBuffer(long offset, int size) throws IOException { if (size < 0) { throw new IndexOutOfBoundsException("size: " + size); } ByteBuffer result = ByteBuffer.allocate(size); copyTo(offset, size, result); result.flip(); return result; } private static void checkChunkValid(long offset, long size, long sourceSize) { if (offset < 0) { throw new IndexOutOfBoundsException("offset: " + offset); } if (size < 0) { throw new IndexOutOfBoundsException("size: " + size); } if (offset > sourceSize) { throw new IndexOutOfBoundsException( "offset (" + offset + ") > source size (" + sourceSize + ")"); } long endOffset = offset + size; if (endOffset < offset) { throw new IndexOutOfBoundsException( "offset (" + offset + ") + size (" + size + ") overflow"); } if (endOffset > sourceSize) { throw new IndexOutOfBoundsException( "offset (" + offset + ") + size (" + size + ") > source size (" + sourceSize +")"); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_GuaranteedEncodedFormX509Certificate.0100644 0000000 0000000 00000000034 14763776540 032744 xustar000000000 0000000 28 mtime=1741684064.6020000 src/main/java/com/android/apksig/internal/util/GuaranteedEncodedFormX509Certificate.java0100644 0000000 0000000 00000004154 14763776540 030316 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Arrays; /** * {@link X509Certificate} whose {@link #getEncoded()} returns the data provided at construction * time. */ public class GuaranteedEncodedFormX509Certificate extends DelegatingX509Certificate { private static final long serialVersionUID = 1L; private final byte[] mEncodedForm; private int mHash = -1; public GuaranteedEncodedFormX509Certificate(X509Certificate wrapped, byte[] encodedForm) { super(wrapped); this.mEncodedForm = (encodedForm != null) ? encodedForm.clone() : null; } @Override public byte[] getEncoded() throws CertificateEncodingException { return (mEncodedForm != null) ? mEncodedForm.clone() : null; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof X509Certificate)) return false; try { byte[] a = this.getEncoded(); byte[] b = ((X509Certificate) o).getEncoded(); return Arrays.equals(a, b); } catch (CertificateEncodingException e) { return false; } } @Override public int hashCode() { if (mHash == -1) { try { mHash = Arrays.hashCode(this.getEncoded()); } catch (CertificateEncodingException e) { mHash = 0; } } return mHash; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_InclusiveIntRange.java0100644 0000000 0000000 00000000034 14763776540 030261 xustar000000000 0000000 28 mtime=1741684064.6020000 src/main/java/com/android/apksig/internal/util/InclusiveIntRange.java0100644 0000000 0000000 00000005135 14763776540 024771 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Inclusive interval of integers. */ public class InclusiveIntRange { private final int min; private final int max; private InclusiveIntRange(int min, int max) { this.min = min; this.max = max; } public int getMin() { return min; } public int getMax() { return max; } public static InclusiveIntRange fromTo(int min, int max) { return new InclusiveIntRange(min, max); } public static InclusiveIntRange from(int min) { return new InclusiveIntRange(min, Integer.MAX_VALUE); } public List getValuesNotIn( List sortedNonOverlappingRanges) { if (sortedNonOverlappingRanges.isEmpty()) { return Collections.singletonList(this); } int testValue = min; List result = null; for (InclusiveIntRange range : sortedNonOverlappingRanges) { int rangeMax = range.max; if (testValue > rangeMax) { continue; } int rangeMin = range.min; if (testValue < range.min) { if (result == null) { result = new ArrayList<>(); } result.add(fromTo(testValue, rangeMin - 1)); } if (rangeMax >= max) { return (result != null) ? result : Collections.emptyList(); } testValue = rangeMax + 1; } if (testValue <= max) { if (result == null) { result = new ArrayList<>(1); } result.add(fromTo(testValue, max)); } return (result != null) ? result : Collections.emptyList(); } @Override public String toString() { return "[" + min + ", " + ((max < Integer.MAX_VALUE) ? (max + "]") : "\u221e)"); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_MessageDigestSink.java0100644 0000000 0000000 00000000034 14763776540 030241 xustar000000000 0000000 28 mtime=1741684064.6020000 src/main/java/com/android/apksig/internal/util/MessageDigestSink.java0100644 0000000 0000000 00000003321 14763776540 024744 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import java.nio.ByteBuffer; import java.security.MessageDigest; /** * Data sink which feeds all received data into the associated {@link MessageDigest} instances. Each * {@code MessageDigest} instance receives the same data. */ public class MessageDigestSink implements DataSink { private final MessageDigest[] mMessageDigests; public MessageDigestSink(MessageDigest[] digests) { mMessageDigests = digests; } @Override public void consume(byte[] buf, int offset, int length) { for (MessageDigest md : mMessageDigests) { md.update(buf, offset, length); } } @Override public void consume(ByteBuffer buf) { int originalPosition = buf.position(); for (MessageDigest md : mMessageDigests) { // Reset the position back to the original because the previous iteration's // MessageDigest.update set the buffer's position to the buffer's limit. buf.position(originalPosition); md.update(buf); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_OutputStreamDataSink.java0100644 0000000 0000000 00000000034 14763776540 030763 xustar000000000 0000000 28 mtime=1741684064.6030000 src/main/java/com/android/apksig/internal/util/OutputStreamDataSink.java0100644 0000000 0000000 00000004506 14763776540 025474 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; /** * {@link DataSink} which outputs received data into the associated {@link OutputStream}. */ public class OutputStreamDataSink implements DataSink { private static final int MAX_READ_CHUNK_SIZE = 65536; private final OutputStream mOut; /** * Constructs a new {@code OutputStreamDataSink} which outputs received data into the provided * {@link OutputStream}. */ public OutputStreamDataSink(OutputStream out) { if (out == null) { throw new NullPointerException("out == null"); } mOut = out; } /** * Returns {@link OutputStream} into which this data sink outputs received data. */ public OutputStream getOutputStream() { return mOut; } @Override public void consume(byte[] buf, int offset, int length) throws IOException { mOut.write(buf, offset, length); } @Override public void consume(ByteBuffer buf) throws IOException { if (!buf.hasRemaining()) { return; } if (buf.hasArray()) { mOut.write( buf.array(), buf.arrayOffset() + buf.position(), buf.remaining()); buf.position(buf.limit()); } else { byte[] tmp = new byte[Math.min(buf.remaining(), MAX_READ_CHUNK_SIZE)]; while (buf.hasRemaining()) { int chunkSize = Math.min(buf.remaining(), tmp.length); buf.get(tmp, 0, chunkSize); mOut.write(tmp, 0, chunkSize); } } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_Pair.java0100644 0000000 0000000 00000000034 14763776540 025563 xustar000000000 0000000 28 mtime=1741684064.6030000 src/main/java/com/android/apksig/internal/util/Pair.java0100644 0000000 0000000 00000004156 14763776540 022275 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; /** * Pair of two elements. */ public final class Pair { private final A mFirst; private final B mSecond; private Pair(A first, B second) { mFirst = first; mSecond = second; } public static Pair of(A first, B second) { return new Pair(first, second); } public A getFirst() { return mFirst; } public B getSecond() { return mSecond; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode()); result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } @SuppressWarnings("rawtypes") Pair other = (Pair) obj; if (mFirst == null) { if (other.mFirst != null) { return false; } } else if (!mFirst.equals(other.mFirst)) { return false; } if (mSecond == null) { if (other.mSecond != null) { return false; } } else if (!mSecond.equals(other.mSecond)) { return false; } return true; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_RandomAccessFileDataSink.java0100644 0000000 0000000 00000000034 14763776540 031451 xustar000000000 0000000 28 mtime=1741684064.6030000 src/main/java/com/android/apksig/internal/util/RandomAccessFileDataSink.java0100644 0000000 0000000 00000006367 14763776540 026171 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * {@link DataSink} which outputs received data into the associated file, sequentially. */ public class RandomAccessFileDataSink implements DataSink { private final RandomAccessFile mFile; private final FileChannel mFileChannel; private long mPosition; /** * Constructs a new {@code RandomAccessFileDataSink} which stores output starting from the * beginning of the provided file. */ public RandomAccessFileDataSink(RandomAccessFile file) { this(file, 0); } /** * Constructs a new {@code RandomAccessFileDataSink} which stores output starting from the * specified position of the provided file. */ public RandomAccessFileDataSink(RandomAccessFile file, long startPosition) { if (file == null) { throw new NullPointerException("file == null"); } if (startPosition < 0) { throw new IllegalArgumentException("startPosition: " + startPosition); } mFile = file; mFileChannel = file.getChannel(); mPosition = startPosition; } /** * Returns the underlying {@link RandomAccessFile}. */ public RandomAccessFile getFile() { return mFile; } @Override public void consume(byte[] buf, int offset, int length) throws IOException { if (offset < 0) { // Must perform this check here because RandomAccessFile.write doesn't throw when offset // is negative but length is 0 throw new IndexOutOfBoundsException("offset: " + offset); } if (offset > buf.length) { // Must perform this check here because RandomAccessFile.write doesn't throw when offset // is too large but length is 0 throw new IndexOutOfBoundsException( "offset: " + offset + ", buf.length: " + buf.length); } if (length == 0) { return; } synchronized (mFile) { mFile.seek(mPosition); mFile.write(buf, offset, length); mPosition += length; } } @Override public void consume(ByteBuffer buf) throws IOException { int length = buf.remaining(); if (length == 0) { return; } synchronized (mFile) { mFile.seek(mPosition); while (buf.hasRemaining()) { mFileChannel.write(buf); } mPosition += length; } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_TeeDataSink.java0100644 0000000 0000000 00000000034 14763776540 027024 xustar000000000 0000000 28 mtime=1741684064.6030000 src/main/java/com/android/apksig/internal/util/TeeDataSink.java0100644 0000000 0000000 00000002770 14763776540 023536 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import java.io.IOException; import java.nio.ByteBuffer; /** * {@link DataSink} which copies provided input into each of the sinks provided to it. */ public class TeeDataSink implements DataSink { private final DataSink[] mSinks; public TeeDataSink(DataSink[] sinks) { mSinks = sinks; } @Override public void consume(byte[] buf, int offset, int length) throws IOException { for (DataSink sink : mSinks) { sink.consume(buf, offset, length); } } @Override public void consume(ByteBuffer buf) throws IOException { int originalPosition = buf.position(); for (int i = 0; i < mSinks.length; i++) { if (i > 0) { buf.position(originalPosition); } mSinks[i].consume(buf); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_VerityTreeBuilder.java0100644 0000000 0000000 00000000034 14763776540 030301 xustar000000000 0000000 28 mtime=1741684064.6040000 src/main/java/com/android/apksig/internal/util/VerityTreeBuilder.java0100644 0000000 0000000 00000031042 14763776540 025005 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import static java.util.concurrent.TimeUnit.MILLISECONDS; import com.android.apksig.internal.zip.ZipUtils; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Phaser; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * VerityTreeBuilder is used to generate the root hash of verity tree built from the input file. * The root hash can be used on device for on-access verification. The tree itself is reproducible * on device, and is not shipped with the APK. */ public class VerityTreeBuilder implements AutoCloseable { /** * Maximum size (in bytes) of each node of the tree. */ private final static int CHUNK_SIZE = 4096; /** * Maximum parallelism while calculating digests. */ private final static int DIGEST_PARALLELISM = Math.min(32, Runtime.getRuntime().availableProcessors()); /** * Queue size. */ private final static int MAX_OUTSTANDING_CHUNKS = 4; /** * Typical prefetch size. */ private final static int MAX_PREFETCH_CHUNKS = 1024; /** * Minimum chunks to be processed by a single worker task. */ private final static int MIN_CHUNKS_PER_WORKER = 8; /** * Digest algorithm (JCA Digest algorithm name) used in the tree. */ private final static String JCA_ALGORITHM = "SHA-256"; /** * Optional salt to apply before each digestion. */ private final byte[] mSalt; private final MessageDigest mMd; private final ExecutorService mExecutor = new ThreadPoolExecutor(DIGEST_PARALLELISM, DIGEST_PARALLELISM, 0L, MILLISECONDS, new ArrayBlockingQueue<>(MAX_OUTSTANDING_CHUNKS), new ThreadPoolExecutor.CallerRunsPolicy()); public VerityTreeBuilder(byte[] salt) throws NoSuchAlgorithmException { mSalt = salt; mMd = getNewMessageDigest(); } @Override public void close() { mExecutor.shutdownNow(); } /** * Returns the root hash of the APK verity tree built from ZIP blocks. * * Specifically, APK verity tree is built from the APK, but as if the APK Signing Block (which * must be page aligned) and the "Central Directory offset" field in End of Central Directory * are skipped. */ public byte[] generateVerityTreeRootHash(DataSource beforeApkSigningBlock, DataSource centralDir, DataSource eocd) throws IOException { if (beforeApkSigningBlock.size() % CHUNK_SIZE != 0) { throw new IllegalStateException("APK Signing Block size not a multiple of " + CHUNK_SIZE + ": " + beforeApkSigningBlock.size()); } // Ensure that, when digesting, ZIP End of Central Directory record's Central Directory // offset field is treated as pointing to the offset at which the APK Signing Block will // start. long centralDirOffsetForDigesting = beforeApkSigningBlock.size(); ByteBuffer eocdBuf = ByteBuffer.allocate((int) eocd.size()); eocdBuf.order(ByteOrder.LITTLE_ENDIAN); eocd.copyTo(0, (int) eocd.size(), eocdBuf); eocdBuf.flip(); ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, centralDirOffsetForDigesting); return generateVerityTreeRootHash(new ChainedDataSource(beforeApkSigningBlock, centralDir, DataSources.asDataSource(eocdBuf))); } /** * Returns the root hash of the verity tree built from the data source. */ public byte[] generateVerityTreeRootHash(DataSource fileSource) throws IOException { ByteBuffer verityBuffer = generateVerityTree(fileSource); return getRootHashFromTree(verityBuffer); } /** * Returns the byte buffer that contains the whole verity tree. * * The tree is built bottom up. The bottom level has 256-bit digest for each 4 KB block in the * input file. If the total size is larger than 4 KB, take this level as input and repeat the * same procedure, until the level is within 4 KB. If salt is given, it will apply to each * digestion before the actual data. * * The returned root hash is calculated from the last level of 4 KB chunk, similarly with salt. * * The tree is currently stored only in memory and is never written out. Nevertheless, it is * the actual verity tree format on disk, and is supposed to be re-generated on device. */ public ByteBuffer generateVerityTree(DataSource fileSource) throws IOException { int digestSize = mMd.getDigestLength(); // Calculate the summed area table of level size. In other word, this is the offset // table of each level, plus the next non-existing level. int[] levelOffset = calculateLevelOffset(fileSource.size(), digestSize); ByteBuffer verityBuffer = ByteBuffer.allocate(levelOffset[levelOffset.length - 1]); // Generate the hash tree bottom-up. for (int i = levelOffset.length - 2; i >= 0; i--) { DataSink middleBufferSink = new ByteBufferSink( slice(verityBuffer, levelOffset[i], levelOffset[i + 1])); DataSource src; if (i == levelOffset.length - 2) { src = fileSource; digestDataByChunks(src, middleBufferSink); } else { src = DataSources.asDataSource(slice(verityBuffer.asReadOnlyBuffer(), levelOffset[i + 1], levelOffset[i + 2])); digestDataByChunks(src, middleBufferSink); } // If the output is not full chunk, pad with 0s. long totalOutput = divideRoundup(src.size(), CHUNK_SIZE) * digestSize; int incomplete = (int) (totalOutput % CHUNK_SIZE); if (incomplete > 0) { byte[] padding = new byte[CHUNK_SIZE - incomplete]; middleBufferSink.consume(padding, 0, padding.length); } } return verityBuffer; } /** * Returns the digested root hash from the top level (only page) of a verity tree. */ public byte[] getRootHashFromTree(ByteBuffer verityBuffer) throws IOException { ByteBuffer firstPage = slice(verityBuffer.asReadOnlyBuffer(), 0, CHUNK_SIZE); return saltedDigest(firstPage); } /** * Returns an array of summed area table of level size in the verity tree. In other words, the * returned array is offset of each level in the verity tree file format, plus an additional * offset of the next non-existing level (i.e. end of the last level + 1). Thus the array size * is level + 1. */ private static int[] calculateLevelOffset(long dataSize, int digestSize) { // Compute total size of each level, bottom to top. ArrayList levelSize = new ArrayList<>(); while (true) { long chunkCount = divideRoundup(dataSize, CHUNK_SIZE); long size = CHUNK_SIZE * divideRoundup(chunkCount * digestSize, CHUNK_SIZE); levelSize.add(size); if (chunkCount * digestSize <= CHUNK_SIZE) { break; } dataSize = chunkCount * digestSize; } // Reverse and convert to summed area table. int[] levelOffset = new int[levelSize.size() + 1]; levelOffset[0] = 0; for (int i = 0; i < levelSize.size(); i++) { // We don't support verity tree if it is larger then Integer.MAX_VALUE. levelOffset[i + 1] = levelOffset[i] + Math.toIntExact( levelSize.get(levelSize.size() - i - 1)); } return levelOffset; } /** * Digest data source by chunks then feeds them to the sink one by one. If the last unit is * less than the chunk size and padding is desired, feed with extra padding 0 to fill up the * chunk before digesting. */ private void digestDataByChunks(DataSource dataSource, DataSink dataSink) throws IOException { final long size = dataSource.size(); final int chunks = (int) divideRoundup(size, CHUNK_SIZE); /** Single IO operation size, in chunks. */ final int ioSizeChunks = MAX_PREFETCH_CHUNKS; final byte[][] hashes = new byte[chunks][]; Phaser tasks = new Phaser(1); // Reading the input file as fast as we can. final long maxReadSize = ioSizeChunks * CHUNK_SIZE; long readOffset = 0; int startChunkIndex = 0; while (readOffset < size) { final long readLimit = Math.min(readOffset + maxReadSize, size); final int readSize = (int) (readLimit - readOffset); final int bufferSizeChunks = (int) divideRoundup(readSize, CHUNK_SIZE); // Overllocating to zero-pad last chunk. // With 4MiB block size, 32 threads and 4 queue size we might allocate up to 144MiB. final ByteBuffer buffer = ByteBuffer.allocate(bufferSizeChunks * CHUNK_SIZE); dataSource.copyTo(readOffset, readSize, buffer); buffer.rewind(); final int readChunkIndex = startChunkIndex; Runnable task = () -> { final MessageDigest md = cloneMessageDigest(); for (int offset = 0, finish = buffer.capacity(), chunkIndex = readChunkIndex; offset < finish; offset += CHUNK_SIZE, ++chunkIndex) { ByteBuffer chunk = slice(buffer, offset, offset + CHUNK_SIZE); hashes[chunkIndex] = saltedDigest(md, chunk); } tasks.arriveAndDeregister(); }; tasks.register(); mExecutor.execute(task); startChunkIndex += bufferSizeChunks; readOffset += readSize; } // Waiting for the tasks to complete. tasks.arriveAndAwaitAdvance(); // Streaming hashes back. for (byte[] hash : hashes) { dataSink.consume(hash, 0, hash.length); } } /** Returns the digest of data with salt prepended. */ private byte[] saltedDigest(ByteBuffer data) { return saltedDigest(mMd, data); } private byte[] saltedDigest(MessageDigest md, ByteBuffer data) { md.reset(); if (mSalt != null) { md.update(mSalt); } md.update(data); return md.digest(); } /** Divides a number and round up to the closest integer. */ private static long divideRoundup(long dividend, long divisor) { return (dividend + divisor - 1) / divisor; } /** Returns a slice of the buffer with shared the content. */ private static ByteBuffer slice(ByteBuffer buffer, int begin, int end) { ByteBuffer b = buffer.duplicate(); b.position(0); // to ensure position <= limit invariant. b.limit(end); b.position(begin); return b.slice(); } /** * Obtains a new instance of the message digest algorithm. */ private static MessageDigest getNewMessageDigest() throws NoSuchAlgorithmException { return MessageDigest.getInstance(JCA_ALGORITHM); } /** * Clones the existing message digest, or creates a new instance if clone is unavailable. */ private MessageDigest cloneMessageDigest() { try { return (MessageDigest) mMd.clone(); } catch (CloneNotSupportedException ignored) { try { return getNewMessageDigest(); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException( "Failed to obtain an instance of a previously available message digest", e); } } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_util_X509CertificateUtils.java0100644 0000000 0000000 00000000034 14763776540 030521 xustar000000000 0000000 28 mtime=1741684064.6050000 src/main/java/com/android/apksig/internal/util/X509CertificateUtils.java0100644 0000000 0000000 00000032650 14763776540 025233 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.internal.asn1.Asn1BerParser; import com.android.apksig.internal.asn1.Asn1DecodingException; import com.android.apksig.internal.asn1.Asn1DerEncoder; import com.android.apksig.internal.asn1.Asn1EncodingException; import com.android.apksig.internal.x509.Certificate; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Base64; import java.util.Collection; /** * Provides methods to generate {@code X509Certificate}s from their encoded form. These methods * can be used to generate certificates that would be rejected by the Java {@code * CertificateFactory}. */ public class X509CertificateUtils { private static volatile CertificateFactory sCertFactory = null; // The PEM certificate header and footer as specified in RFC 7468: // There is exactly one space character (SP) separating the "BEGIN" or // "END" from the label. There are exactly five hyphen-minus (also // known as dash) characters ("-") on both ends of the encapsulation // boundaries, no more, no less. public static final byte[] BEGIN_CERT_HEADER = "-----BEGIN CERTIFICATE-----".getBytes(); public static final byte[] END_CERT_FOOTER = "-----END CERTIFICATE-----".getBytes(); private static void buildCertFactory() { if (sCertFactory != null) { return; } buildCertFactoryHelper(); } private static synchronized void buildCertFactoryHelper() { if (sCertFactory != null) { return; } try { sCertFactory = CertificateFactory.getInstance("X.509"); } catch (CertificateException e) { throw new RuntimeException("Failed to create X.509 CertificateFactory", e); } } /** * Generates an {@code X509Certificate} from the {@code InputStream}. * * @throws CertificateException if the {@code InputStream} cannot be decoded to a valid * certificate. */ public static X509Certificate generateCertificate(InputStream in) throws CertificateException { byte[] encodedForm; try { encodedForm = ByteStreams.toByteArray(in); } catch (IOException e) { throw new CertificateException("Failed to parse certificate", e); } return generateCertificate(encodedForm); } /** * Generates an {@code X509Certificate} from the encoded form. * * @throws CertificateException if the encodedForm cannot be decoded to a valid certificate. */ public static X509Certificate generateCertificate(byte[] encodedForm) throws CertificateException { buildCertFactory(); return generateCertificate(encodedForm, sCertFactory); } /** * Generates an {@code X509Certificate} from the encoded form using the provided * {@code CertificateFactory}. * * @throws CertificateException if the encodedForm cannot be decoded to a valid certificate. */ public static X509Certificate generateCertificate(byte[] encodedForm, CertificateFactory certFactory) throws CertificateException { X509Certificate certificate; try { certificate = (X509Certificate) certFactory.generateCertificate( new ByteArrayInputStream(encodedForm)); return certificate; } catch (CertificateException e) { // This could be expected if the certificate is encoded using a BER encoding that does // not use the minimum number of bytes to represent the length of the contents; attempt // to decode the certificate using the BER parser and re-encode using the DER encoder // below. } try { // Some apps were previously signed with a BER encoded certificate that now results // in exceptions from the CertificateFactory generateCertificate(s) methods. Since // the original BER encoding of the certificate is used as the signature for these // apps that original encoding must be maintained when signing updated versions of // these apps and any new apps that may require capabilities guarded by the // signature. To maintain the same signature the BER parser can be used to parse // the certificate, then it can be re-encoded to its DER equivalent which is // accepted by the generateCertificate method. The positions in the ByteBuffer can // then be used with the GuaranteedEncodedFormX509Certificate object to ensure the // getEncoded method returns the original signature of the app. ByteBuffer encodedCertBuffer = getNextDEREncodedCertificateBlock( ByteBuffer.wrap(encodedForm)); int startingPos = encodedCertBuffer.position(); Certificate reencodedCert = Asn1BerParser.parse(encodedCertBuffer, Certificate.class); byte[] reencodedForm = Asn1DerEncoder.encode(reencodedCert); certificate = (X509Certificate) certFactory.generateCertificate( new ByteArrayInputStream(reencodedForm)); // If the reencodedForm is successfully accepted by the CertificateFactory then copy the // original encoding from the ByteBuffer and use that encoding in the Guaranteed object. byte[] originalEncoding = new byte[encodedCertBuffer.position() - startingPos]; encodedCertBuffer.position(startingPos); encodedCertBuffer.get(originalEncoding); GuaranteedEncodedFormX509Certificate guaranteedEncodedCert = new GuaranteedEncodedFormX509Certificate(certificate, originalEncoding); return guaranteedEncodedCert; } catch (Asn1DecodingException | Asn1EncodingException | CertificateException e) { throw new CertificateException("Failed to parse certificate", e); } } /** * Generates a {@code Collection} of {@code Certificate} objects from the encoded {@code * InputStream}. * * @throws CertificateException if the InputStream cannot be decoded to zero or more valid * {@code Certificate} objects. */ public static Collection generateCertificates( InputStream in) throws CertificateException { buildCertFactory(); return generateCertificates(in, sCertFactory); } /** * Generates a {@code Collection} of {@code Certificate} objects from the encoded {@code * InputStream} using the provided {@code CertificateFactory}. * * @throws CertificateException if the InputStream cannot be decoded to zero or more valid * {@code Certificates} objects. */ public static Collection generateCertificates( InputStream in, CertificateFactory certFactory) throws CertificateException { // Since the InputStream is not guaranteed to support mark / reset operations first read it // into a byte array to allow using the BER parser / DER encoder if it cannot be read by // the CertificateFactory. byte[] encodedCerts; try { encodedCerts = ByteStreams.toByteArray(in); } catch (IOException e) { throw new CertificateException("Failed to read the input stream", e); } try { return certFactory.generateCertificates(new ByteArrayInputStream(encodedCerts)); } catch (CertificateException e) { // This could be expected if the certificates are encoded using a BER encoding that does // not use the minimum number of bytes to represent the length of the contents; attempt // to decode the certificates using the BER parser and re-encode using the DER encoder // below. } try { Collection certificates = new ArrayList<>(1); ByteBuffer encodedCertsBuffer = ByteBuffer.wrap(encodedCerts); while (encodedCertsBuffer.hasRemaining()) { ByteBuffer certBuffer = getNextDEREncodedCertificateBlock(encodedCertsBuffer); int startingPos = certBuffer.position(); Certificate reencodedCert = Asn1BerParser.parse(certBuffer, Certificate.class); byte[] reencodedForm = Asn1DerEncoder.encode(reencodedCert); X509Certificate certificate = (X509Certificate) certFactory.generateCertificate( new ByteArrayInputStream(reencodedForm)); byte[] originalEncoding = new byte[certBuffer.position() - startingPos]; certBuffer.position(startingPos); certBuffer.get(originalEncoding); GuaranteedEncodedFormX509Certificate guaranteedEncodedCert = new GuaranteedEncodedFormX509Certificate(certificate, originalEncoding); certificates.add(guaranteedEncodedCert); } return certificates; } catch (Asn1DecodingException | Asn1EncodingException e) { throw new CertificateException("Failed to parse certificates", e); } } /** * Parses the provided ByteBuffer to obtain the next certificate in DER encoding. If the buffer * does not begin with the PEM certificate header then it is returned with the assumption that * it is already DER encoded. If the buffer does begin with the PEM certificate header then the * certificate data is read from the buffer until the PEM certificate footer is reached; this * data is then base64 decoded and returned in a new ByteBuffer. * * If the buffer is in PEM format then the position of the buffer is moved to the end of the * current certificate; if the buffer is already DER encoded then the position of the buffer is * not modified. * * @throws CertificateException if the buffer contains the PEM certificate header but does not * contain the expected footer. */ private static ByteBuffer getNextDEREncodedCertificateBlock(ByteBuffer certificateBuffer) throws CertificateException { if (certificateBuffer == null) { throw new NullPointerException("The certificateBuffer cannot be null"); } // if the buffer does not contain enough data for the PEM cert header then just return the // provided buffer. if (certificateBuffer.remaining() < BEGIN_CERT_HEADER.length) { return certificateBuffer; } certificateBuffer.mark(); for (int i = 0; i < BEGIN_CERT_HEADER.length; i++) { if (certificateBuffer.get() != BEGIN_CERT_HEADER[i]) { certificateBuffer.reset(); return certificateBuffer; } } StringBuilder pemEncoding = new StringBuilder(); while (certificateBuffer.hasRemaining()) { char encodedChar = (char) certificateBuffer.get(); // if the current character is a '-' then the beginning of the footer has been reached if (encodedChar == '-') { break; } else if (Character.isWhitespace(encodedChar)) { continue; } else { pemEncoding.append(encodedChar); } } // start from the second index in the certificate footer since the first '-' should have // been consumed above. for (int i = 1; i < END_CERT_FOOTER.length; i++) { if (!certificateBuffer.hasRemaining()) { throw new CertificateException( "The provided input contains the PEM certificate header but does not " + "contain sufficient data for the footer"); } if (certificateBuffer.get() != END_CERT_FOOTER[i]) { throw new CertificateException( "The provided input contains the PEM certificate header without a " + "valid certificate footer"); } } byte[] derEncoding = Base64.getDecoder().decode(pemEncoding.toString()); // consume any trailing whitespace in the byte buffer int nextEncodedChar = certificateBuffer.position(); while (certificateBuffer.hasRemaining()) { char trailingChar = (char) certificateBuffer.get(); if (Character.isWhitespace(trailingChar)) { nextEncodedChar++; } else { break; } } certificateBuffer.position(nextEncodedChar); return ByteBuffer.wrap(derEncoding); } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_0100644 0000000 0000000 00000000034 14763776540 023557 xustar000000000 0000000 28 mtime=1741684064.6050000 src/main/java/com/android/apksig/internal/x509/0040755 0000000 0000000 00000000000 14763776540 020264 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_AttributeTypeAndValue.java0100644 0000000 0000000 00000000034 14763776540 030645 xustar000000000 0000000 28 mtime=1741684064.6050000 src/main/java/com/android/apksig/internal/x509/AttributeTypeAndValue.java0100644 0000000 0000000 00000002252 14763776540 025352 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.asn1.Asn1Type; /** * {@code AttributeTypeAndValue} as specified in RFC 5280. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class AttributeTypeAndValue { @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) public String attrType; @Asn1Field(index = 1, type = Asn1Type.ANY) public Asn1OpaqueObject attrValue; }./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_Certificate.java0100644 0000000 0000000 00000000034 14763776540 026642 xustar000000000 0000000 28 mtime=1741684064.6050000 src/main/java/com/android/apksig/internal/x509/Certificate.java0100644 0000000 0000000 00000010611 14763776540 023345 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1OpaqueObject; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.pkcs7.AlgorithmIdentifier; import com.android.apksig.internal.pkcs7.IssuerAndSerialNumber; import com.android.apksig.internal.pkcs7.SignerIdentifier; import com.android.apksig.internal.util.ByteBufferUtils; import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate; import com.android.apksig.internal.util.X509CertificateUtils; import java.math.BigInteger; import java.nio.ByteBuffer; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.security.auth.x500.X500Principal; /** * X509 {@code Certificate} as specified in RFC 5280. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class Certificate { @Asn1Field(index = 0, type = Asn1Type.SEQUENCE) public TBSCertificate certificate; @Asn1Field(index = 1, type = Asn1Type.SEQUENCE) public AlgorithmIdentifier signatureAlgorithm; @Asn1Field(index = 2, type = Asn1Type.BIT_STRING) public ByteBuffer signature; public static X509Certificate findCertificate( Collection certs, SignerIdentifier id) { for (X509Certificate cert : certs) { if (isMatchingCerticicate(cert, id)) { return cert; } } return null; } private static boolean isMatchingCerticicate(X509Certificate cert, SignerIdentifier id) { if (id.issuerAndSerialNumber == null) { // Android doesn't support any other means of identifying the signing certificate return false; } IssuerAndSerialNumber issuerAndSerialNumber = id.issuerAndSerialNumber; byte[] encodedIssuer = ByteBufferUtils.toByteArray(issuerAndSerialNumber.issuer.getEncoded()); X500Principal idIssuer = new X500Principal(encodedIssuer); BigInteger idSerialNumber = issuerAndSerialNumber.certificateSerialNumber; return idSerialNumber.equals(cert.getSerialNumber()) && idIssuer.equals(cert.getIssuerX500Principal()); } public static List parseCertificates( List encodedCertificates) throws CertificateException { if (encodedCertificates.isEmpty()) { return Collections.emptyList(); } List result = new ArrayList<>(encodedCertificates.size()); for (int i = 0; i < encodedCertificates.size(); i++) { Asn1OpaqueObject encodedCertificate = encodedCertificates.get(i); X509Certificate certificate; byte[] encodedForm = ByteBufferUtils.toByteArray(encodedCertificate.getEncoded()); try { certificate = X509CertificateUtils.generateCertificate(encodedForm); } catch (CertificateException e) { throw new CertificateException("Failed to parse certificate #" + (i + 1), e); } // Wrap the cert so that the result's getEncoded returns exactly the original // encoded form. Without this, getEncoded may return a different form from what was // stored in the signature. This is because some X509Certificate(Factory) // implementations re-encode certificates and/or some implementations of // X509Certificate.getEncoded() re-encode certificates. certificate = new GuaranteedEncodedFormX509Certificate(certificate, encodedForm); result.add(certificate); } return result; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_Extension.java0100644 0000000 0000000 00000000034 14763776540 026374 xustar000000000 0000000 28 mtime=1741684064.6060000 src/main/java/com/android/apksig/internal/x509/Extension.java0100644 0000000 0000000 00000002360 14763776540 023101 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; import java.nio.ByteBuffer; /** * X509 {@code Extension} as specified in RFC 5280. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class Extension { @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) public String extensionID; @Asn1Field(index = 1, type = Asn1Type.BOOLEAN, optional = true) public boolean isCritial = false; @Asn1Field(index = 2, type = Asn1Type.OCTET_STRING) public ByteBuffer extensionValue; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_Name.java0100644 0000000 0000000 00000000034 14763776540 025300 xustar000000000 0000000 28 mtime=1741684064.6060000 src/main/java/com/android/apksig/internal/x509/Name.java0100644 0000000 0000000 00000002164 14763776540 022007 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; import java.util.List; /** * X501 {@code Name} as specified in RFC 5280. */ @Asn1Class(type = Asn1Type.CHOICE) public class Name { // This field is the RDNSequence specified in RFC 5280. @Asn1Field(index = 0, type = Asn1Type.SEQUENCE_OF) public List relativeDistinguishedNames; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_RSAPublicKey.java0100644 0000000 0000000 00000000034 14763776540 026655 xustar000000000 0000000 28 mtime=1741684064.6060000 src/main/java/com/android/apksig/internal/x509/RSAPublicKey.java0100644 0000000 0000000 00000002170 14763776540 023361 0ustar000000000 0000000 /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; import java.math.BigInteger; /** * {@code RSAPublicKey} as specified in RFC 3279. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class RSAPublicKey { @Asn1Field(index = 0, type = Asn1Type.INTEGER) public BigInteger modulus; @Asn1Field(index = 1, type = Asn1Type.INTEGER) public BigInteger publicExponent; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_RelativeDistinguishedName.java0100644 0000000 0000000 00000000034 14763776540 031520 xustar000000000 0000000 28 mtime=1741684064.6060000 src/main/java/com/android/apksig/internal/x509/RelativeDistinguishedName.java0100644 0000000 0000000 00000002121 14763776540 026220 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; import java.util.List; /** * {@code RelativeDistinguishedName} as specified in RFC 5280. */ @Asn1Class(type = Asn1Type.UNENCODED_CONTAINER) public class RelativeDistinguishedName { @Asn1Field(index = 0, type = Asn1Type.SET_OF) public List attributes; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_SubjectPublicKeyInfo.java0100644 0000000 0000000 00000000034 14763776540 030443 xustar000000000 0000000 28 mtime=1741684064.6060000 src/main/java/com/android/apksig/internal/x509/SubjectPublicKeyInfo.java0100644 0000000 0000000 00000002340 14763776540 025146 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.pkcs7.AlgorithmIdentifier; import java.nio.ByteBuffer; /** * {@code SubjectPublicKeyInfo} as specified in RFC 5280. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class SubjectPublicKeyInfo { @Asn1Field(index = 0, type = Asn1Type.SEQUENCE) public AlgorithmIdentifier algorithmIdentifier; @Asn1Field(index = 1, type = Asn1Type.BIT_STRING) public ByteBuffer subjectPublicKey; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_TBSCertificate.java0100644 0000000 0000000 00000000034 14763776540 027213 xustar000000000 0000000 28 mtime=1741684064.6060000 src/main/java/com/android/apksig/internal/x509/TBSCertificate.java0100644 0000000 0000000 00000004627 14763776540 023730 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; import com.android.apksig.internal.asn1.Asn1Tagging; import com.android.apksig.internal.pkcs7.AlgorithmIdentifier; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.List; /** * To Be Signed Certificate as specified in RFC 5280. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class TBSCertificate { @Asn1Field( index = 0, type = Asn1Type.INTEGER, tagging = Asn1Tagging.EXPLICIT, tagNumber = 0) public int version; @Asn1Field(index = 1, type = Asn1Type.INTEGER) public BigInteger serialNumber; @Asn1Field(index = 2, type = Asn1Type.SEQUENCE) public AlgorithmIdentifier signatureAlgorithm; @Asn1Field(index = 3, type = Asn1Type.CHOICE) public Name issuer; @Asn1Field(index = 4, type = Asn1Type.SEQUENCE) public Validity validity; @Asn1Field(index = 5, type = Asn1Type.CHOICE) public Name subject; @Asn1Field(index = 6, type = Asn1Type.SEQUENCE) public SubjectPublicKeyInfo subjectPublicKeyInfo; @Asn1Field(index = 7, type = Asn1Type.BIT_STRING, tagging = Asn1Tagging.IMPLICIT, optional = true, tagNumber = 1) public ByteBuffer issuerUniqueID; @Asn1Field(index = 8, type = Asn1Type.BIT_STRING, tagging = Asn1Tagging.IMPLICIT, optional = true, tagNumber = 2) public ByteBuffer subjectUniqueID; @Asn1Field(index = 9, type = Asn1Type.SEQUENCE_OF, tagging = Asn1Tagging.EXPLICIT, optional = true, tagNumber = 3) public List extensions; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_Time.java0100644 0000000 0000000 00000000034 14763776540 025316 xustar000000000 0000000 28 mtime=1741684064.6070000 src/main/java/com/android/apksig/internal/x509/Time.java0100644 0000000 0000000 00000002066 14763776540 022026 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; /** * {@code Time} as specified in RFC 5280. */ @Asn1Class(type = Asn1Type.CHOICE) public class Time { @Asn1Field(type = Asn1Type.UTC_TIME) public String utcTime; @Asn1Field(type = Asn1Type.GENERALIZED_TIME) public String generalizedTime; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_x509_Validity.java0100644 0000000 0000000 00000000034 14763776540 026205 xustar000000000 0000000 28 mtime=1741684064.6070000 src/main/java/com/android/apksig/internal/x509/Validity.java0100644 0000000 0000000 00000002101 14763776540 022703 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.x509; import com.android.apksig.internal.asn1.Asn1Class; import com.android.apksig.internal.asn1.Asn1Field; import com.android.apksig.internal.asn1.Asn1Type; /** * {@code Validity} as specified in RFC 5280. */ @Asn1Class(type = Asn1Type.SEQUENCE) public class Validity { @Asn1Field(index = 0, type = Asn1Type.CHOICE) public Time notBefore; @Asn1Field(index = 1, type = Asn1Type.CHOICE) public Time notAfter; } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_zip_0100644 0000000 0000000 00000000034 14763776540 023654 xustar000000000 0000000 28 mtime=1741684064.6070000 src/main/java/com/android/apksig/internal/zip/0040755 0000000 0000000 00000000000 14763776540 020361 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_internal_zip_CentralDirectoryRecord.java0100644 0000000 0000000 00000000034 14763776540 031131 xustar000000000 0000000 28 mtime=1741684064.6070000 src/main/java/com/android/apksig/internal/zip/CentralDirectoryRecord.java0100644 0000000 0000000 00000025665 14763776540 025653 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.zip; import com.android.apksig.zip.ZipFormatException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.Comparator; /** * ZIP Central Directory (CD) Record. */ public class CentralDirectoryRecord { /** * Comparator which compares records by the offset of the corresponding Local File Header in the * archive. */ public static final Comparator BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR = new ByLocalFileHeaderOffsetComparator(); private static final int RECORD_SIGNATURE = 0x02014b50; private static final int HEADER_SIZE_BYTES = 46; private static final int GP_FLAGS_OFFSET = 8; private static final int LOCAL_FILE_HEADER_OFFSET_OFFSET = 42; private static final int NAME_OFFSET = HEADER_SIZE_BYTES; private final ByteBuffer mData; private final short mGpFlags; private final short mCompressionMethod; private final int mLastModificationTime; private final int mLastModificationDate; private final long mCrc32; private final long mCompressedSize; private final long mUncompressedSize; private final long mLocalFileHeaderOffset; private final String mName; private final int mNameSizeBytes; private CentralDirectoryRecord( ByteBuffer data, short gpFlags, short compressionMethod, int lastModificationTime, int lastModificationDate, long crc32, long compressedSize, long uncompressedSize, long localFileHeaderOffset, String name, int nameSizeBytes) { mData = data; mGpFlags = gpFlags; mCompressionMethod = compressionMethod; mLastModificationDate = lastModificationDate; mLastModificationTime = lastModificationTime; mCrc32 = crc32; mCompressedSize = compressedSize; mUncompressedSize = uncompressedSize; mLocalFileHeaderOffset = localFileHeaderOffset; mName = name; mNameSizeBytes = nameSizeBytes; } public int getSize() { return mData.remaining(); } public String getName() { return mName; } public int getNameSizeBytes() { return mNameSizeBytes; } public short getGpFlags() { return mGpFlags; } public short getCompressionMethod() { return mCompressionMethod; } public int getLastModificationTime() { return mLastModificationTime; } public int getLastModificationDate() { return mLastModificationDate; } public long getCrc32() { return mCrc32; } public long getCompressedSize() { return mCompressedSize; } public long getUncompressedSize() { return mUncompressedSize; } public long getLocalFileHeaderOffset() { return mLocalFileHeaderOffset; } /** * Returns the Central Directory Record starting at the current position of the provided buffer * and advances the buffer's position immediately past the end of the record. */ public static CentralDirectoryRecord getRecord(ByteBuffer buf) throws ZipFormatException { ZipUtils.assertByteOrderLittleEndian(buf); if (buf.remaining() < HEADER_SIZE_BYTES) { throw new ZipFormatException( "Input too short. Need at least: " + HEADER_SIZE_BYTES + " bytes, available: " + buf.remaining() + " bytes", new BufferUnderflowException()); } int originalPosition = buf.position(); int recordSignature = buf.getInt(); if (recordSignature != RECORD_SIGNATURE) { throw new ZipFormatException( "Not a Central Directory record. Signature: 0x" + Long.toHexString(recordSignature & 0xffffffffL)); } buf.position(originalPosition + GP_FLAGS_OFFSET); short gpFlags = buf.getShort(); short compressionMethod = buf.getShort(); int lastModificationTime = ZipUtils.getUnsignedInt16(buf); int lastModificationDate = ZipUtils.getUnsignedInt16(buf); long crc32 = ZipUtils.getUnsignedInt32(buf); long compressedSize = ZipUtils.getUnsignedInt32(buf); long uncompressedSize = ZipUtils.getUnsignedInt32(buf); int nameSize = ZipUtils.getUnsignedInt16(buf); int extraSize = ZipUtils.getUnsignedInt16(buf); int commentSize = ZipUtils.getUnsignedInt16(buf); buf.position(originalPosition + LOCAL_FILE_HEADER_OFFSET_OFFSET); long localFileHeaderOffset = ZipUtils.getUnsignedInt32(buf); buf.position(originalPosition); int recordSize = HEADER_SIZE_BYTES + nameSize + extraSize + commentSize; if (recordSize > buf.remaining()) { throw new ZipFormatException( "Input too short. Need: " + recordSize + " bytes, available: " + buf.remaining() + " bytes", new BufferUnderflowException()); } String name = getName(buf, originalPosition + NAME_OFFSET, nameSize); buf.position(originalPosition); int originalLimit = buf.limit(); int recordEndInBuf = originalPosition + recordSize; ByteBuffer recordBuf; try { buf.limit(recordEndInBuf); recordBuf = buf.slice(); } finally { buf.limit(originalLimit); } // Consume this record buf.position(recordEndInBuf); return new CentralDirectoryRecord( recordBuf, gpFlags, compressionMethod, lastModificationTime, lastModificationDate, crc32, compressedSize, uncompressedSize, localFileHeaderOffset, name, nameSize); } public void copyTo(ByteBuffer output) { output.put(mData.slice()); } public CentralDirectoryRecord createWithModifiedLocalFileHeaderOffset( long localFileHeaderOffset) { ByteBuffer result = ByteBuffer.allocate(mData.remaining()); result.put(mData.slice()); result.flip(); result.order(ByteOrder.LITTLE_ENDIAN); ZipUtils.setUnsignedInt32(result, LOCAL_FILE_HEADER_OFFSET_OFFSET, localFileHeaderOffset); return new CentralDirectoryRecord( result, mGpFlags, mCompressionMethod, mLastModificationTime, mLastModificationDate, mCrc32, mCompressedSize, mUncompressedSize, localFileHeaderOffset, mName, mNameSizeBytes); } public static CentralDirectoryRecord createWithDeflateCompressedData( String name, int lastModifiedTime, int lastModifiedDate, long crc32, long compressedSize, long uncompressedSize, long localFileHeaderOffset) { byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8); short gpFlags = ZipUtils.GP_FLAG_EFS; // UTF-8 character encoding used for entry name short compressionMethod = ZipUtils.COMPRESSION_METHOD_DEFLATED; int recordSize = HEADER_SIZE_BYTES + nameBytes.length; ByteBuffer result = ByteBuffer.allocate(recordSize); result.order(ByteOrder.LITTLE_ENDIAN); result.putInt(RECORD_SIGNATURE); ZipUtils.putUnsignedInt16(result, 0x14); // Version made by ZipUtils.putUnsignedInt16(result, 0x14); // Minimum version needed to extract result.putShort(gpFlags); result.putShort(compressionMethod); ZipUtils.putUnsignedInt16(result, lastModifiedTime); ZipUtils.putUnsignedInt16(result, lastModifiedDate); ZipUtils.putUnsignedInt32(result, crc32); ZipUtils.putUnsignedInt32(result, compressedSize); ZipUtils.putUnsignedInt32(result, uncompressedSize); ZipUtils.putUnsignedInt16(result, nameBytes.length); ZipUtils.putUnsignedInt16(result, 0); // Extra field length ZipUtils.putUnsignedInt16(result, 0); // File comment length ZipUtils.putUnsignedInt16(result, 0); // Disk number ZipUtils.putUnsignedInt16(result, 0); // Internal file attributes ZipUtils.putUnsignedInt32(result, 0); // External file attributes ZipUtils.putUnsignedInt32(result, localFileHeaderOffset); result.put(nameBytes); if (result.hasRemaining()) { throw new RuntimeException("pos: " + result.position() + ", limit: " + result.limit()); } result.flip(); return new CentralDirectoryRecord( result, gpFlags, compressionMethod, lastModifiedTime, lastModifiedDate, crc32, compressedSize, uncompressedSize, localFileHeaderOffset, name, nameBytes.length); } static String getName(ByteBuffer record, int position, int nameLengthBytes) { byte[] nameBytes; int nameBytesOffset; if (record.hasArray()) { nameBytes = record.array(); nameBytesOffset = record.arrayOffset() + position; } else { nameBytes = new byte[nameLengthBytes]; nameBytesOffset = 0; int originalPosition = record.position(); try { record.position(position); record.get(nameBytes); } finally { record.position(originalPosition); } } return new String(nameBytes, nameBytesOffset, nameLengthBytes, StandardCharsets.UTF_8); } private static class ByLocalFileHeaderOffsetComparator implements Comparator { @Override public int compare(CentralDirectoryRecord r1, CentralDirectoryRecord r2) { long offset1 = r1.getLocalFileHeaderOffset(); long offset2 = r2.getLocalFileHeaderOffset(); if (offset1 > offset2) { return 1; } else if (offset1 < offset2) { return -1; } else { return 0; } } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_zip_EocdRecord.java0100644 0000000 0000000 00000000034 14763776540 026526 xustar000000000 0000000 28 mtime=1741684064.6080000 src/main/java/com/android/apksig/internal/zip/EocdRecord.java0100644 0000000 0000000 00000004306 14763776540 023235 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.zip; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * ZIP End of Central Directory record. */ public class EocdRecord { private static final int CD_RECORD_COUNT_ON_DISK_OFFSET = 8; private static final int CD_RECORD_COUNT_TOTAL_OFFSET = 10; private static final int CD_SIZE_OFFSET = 12; private static final int CD_OFFSET_OFFSET = 16; public static ByteBuffer createWithModifiedCentralDirectoryInfo( ByteBuffer original, int centralDirectoryRecordCount, long centralDirectorySizeBytes, long centralDirectoryOffset) { ByteBuffer result = ByteBuffer.allocate(original.remaining()); result.order(ByteOrder.LITTLE_ENDIAN); result.put(original.slice()); result.flip(); ZipUtils.setUnsignedInt16( result, CD_RECORD_COUNT_ON_DISK_OFFSET, centralDirectoryRecordCount); ZipUtils.setUnsignedInt16( result, CD_RECORD_COUNT_TOTAL_OFFSET, centralDirectoryRecordCount); ZipUtils.setUnsignedInt32(result, CD_SIZE_OFFSET, centralDirectorySizeBytes); ZipUtils.setUnsignedInt32(result, CD_OFFSET_OFFSET, centralDirectoryOffset); return result; } public static ByteBuffer createWithPaddedComment(ByteBuffer original, int padding) { ByteBuffer result = ByteBuffer.allocate((int) original.remaining() + padding); result.order(ByteOrder.LITTLE_ENDIAN); result.put(original.slice()); result.rewind(); ZipUtils.updateZipEocdCommentLen(result); return result; } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_zip_LocalFileRecord.java0100644 0000000 0000000 00000000034 14763776540 027506 xustar000000000 0000000 28 mtime=1741684064.6080000 src/main/java/com/android/apksig/internal/zip/LocalFileRecord.java0100644 0000000 0000000 00000056221 14763776540 024220 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.zip; import com.android.apksig.internal.util.ByteBufferSink; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import com.android.apksig.zip.ZipFormatException; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.zip.DataFormatException; import java.util.zip.Inflater; /** * ZIP Local File record. * *

The record consists of the Local File Header, file data, and (if present) Data Descriptor. */ public class LocalFileRecord { private static final int RECORD_SIGNATURE = 0x04034b50; private static final int HEADER_SIZE_BYTES = 30; private static final int GP_FLAGS_OFFSET = 6; private static final int CRC32_OFFSET = 14; private static final int COMPRESSED_SIZE_OFFSET = 18; private static final int UNCOMPRESSED_SIZE_OFFSET = 22; private static final int NAME_LENGTH_OFFSET = 26; private static final int EXTRA_LENGTH_OFFSET = 28; private static final int NAME_OFFSET = HEADER_SIZE_BYTES; private static final int DATA_DESCRIPTOR_SIZE_BYTES_WITHOUT_SIGNATURE = 12; private static final int DATA_DESCRIPTOR_SIGNATURE = 0x08074b50; private final String mName; private final int mNameSizeBytes; private final ByteBuffer mExtra; private final long mStartOffsetInArchive; private final long mSize; private final int mDataStartOffset; private final long mDataSize; private final boolean mDataCompressed; private final long mUncompressedDataSize; private LocalFileRecord( String name, int nameSizeBytes, ByteBuffer extra, long startOffsetInArchive, long size, int dataStartOffset, long dataSize, boolean dataCompressed, long uncompressedDataSize) { mName = name; mNameSizeBytes = nameSizeBytes; mExtra = extra; mStartOffsetInArchive = startOffsetInArchive; mSize = size; mDataStartOffset = dataStartOffset; mDataSize = dataSize; mDataCompressed = dataCompressed; mUncompressedDataSize = uncompressedDataSize; } public String getName() { return mName; } public ByteBuffer getExtra() { return (mExtra.capacity() > 0) ? mExtra.slice() : mExtra; } public int getExtraFieldStartOffsetInsideRecord() { return HEADER_SIZE_BYTES + mNameSizeBytes; } public long getStartOffsetInArchive() { return mStartOffsetInArchive; } public int getDataStartOffsetInRecord() { return mDataStartOffset; } /** * Returns the size (in bytes) of this record. */ public long getSize() { return mSize; } /** * Returns {@code true} if this record's file data is stored in compressed form. */ public boolean isDataCompressed() { return mDataCompressed; } /** * Returns the Local File record starting at the current position of the provided buffer * and advances the buffer's position immediately past the end of the record. The record * consists of the Local File Header, data, and (if present) Data Descriptor. */ public static LocalFileRecord getRecord( DataSource apk, CentralDirectoryRecord cdRecord, long cdStartOffset) throws ZipFormatException, IOException { return getRecord( apk, cdRecord, cdStartOffset, true, // obtain extra field contents true // include Data Descriptor (if present) ); } /** * Returns the Local File record starting at the current position of the provided buffer * and advances the buffer's position immediately past the end of the record. The record * consists of the Local File Header, data, and (if present) Data Descriptor. */ private static LocalFileRecord getRecord( DataSource apk, CentralDirectoryRecord cdRecord, long cdStartOffset, boolean extraFieldContentsNeeded, boolean dataDescriptorIncluded) throws ZipFormatException, IOException { // IMPLEMENTATION NOTE: This method attempts to mimic the behavior of Android platform // exhibited when reading an APK for the purposes of verifying its signatures. String entryName = cdRecord.getName(); int cdRecordEntryNameSizeBytes = cdRecord.getNameSizeBytes(); int headerSizeWithName = HEADER_SIZE_BYTES + cdRecordEntryNameSizeBytes; long headerStartOffset = cdRecord.getLocalFileHeaderOffset(); long headerEndOffset = headerStartOffset + headerSizeWithName; if (headerEndOffset > cdStartOffset) { throw new ZipFormatException( "Local File Header of " + entryName + " extends beyond start of Central" + " Directory. LFH end: " + headerEndOffset + ", CD start: " + cdStartOffset); } ByteBuffer header; try { header = apk.getByteBuffer(headerStartOffset, headerSizeWithName); } catch (IOException e) { throw new IOException("Failed to read Local File Header of " + entryName, e); } header.order(ByteOrder.LITTLE_ENDIAN); int recordSignature = header.getInt(); if (recordSignature != RECORD_SIGNATURE) { throw new ZipFormatException( "Not a Local File Header record for entry " + entryName + ". Signature: 0x" + Long.toHexString(recordSignature & 0xffffffffL)); } short gpFlags = header.getShort(GP_FLAGS_OFFSET); boolean dataDescriptorUsed = (gpFlags & ZipUtils.GP_FLAG_DATA_DESCRIPTOR_USED) != 0; boolean cdDataDescriptorUsed = (cdRecord.getGpFlags() & ZipUtils.GP_FLAG_DATA_DESCRIPTOR_USED) != 0; if (dataDescriptorUsed != cdDataDescriptorUsed) { throw new ZipFormatException( "Data Descriptor presence mismatch between Local File Header and Central" + " Directory for entry " + entryName + ". LFH: " + dataDescriptorUsed + ", CD: " + cdDataDescriptorUsed); } long uncompressedDataCrc32FromCdRecord = cdRecord.getCrc32(); long compressedDataSizeFromCdRecord = cdRecord.getCompressedSize(); long uncompressedDataSizeFromCdRecord = cdRecord.getUncompressedSize(); if (!dataDescriptorUsed) { long crc32 = ZipUtils.getUnsignedInt32(header, CRC32_OFFSET); if (crc32 != uncompressedDataCrc32FromCdRecord) { throw new ZipFormatException( "CRC-32 mismatch between Local File Header and Central Directory for entry " + entryName + ". LFH: " + crc32 + ", CD: " + uncompressedDataCrc32FromCdRecord); } long compressedSize = ZipUtils.getUnsignedInt32(header, COMPRESSED_SIZE_OFFSET); if (compressedSize != compressedDataSizeFromCdRecord) { throw new ZipFormatException( "Compressed size mismatch between Local File Header and Central Directory" + " for entry " + entryName + ". LFH: " + compressedSize + ", CD: " + compressedDataSizeFromCdRecord); } long uncompressedSize = ZipUtils.getUnsignedInt32(header, UNCOMPRESSED_SIZE_OFFSET); if (uncompressedSize != uncompressedDataSizeFromCdRecord) { throw new ZipFormatException( "Uncompressed size mismatch between Local File Header and Central Directory" + " for entry " + entryName + ". LFH: " + uncompressedSize + ", CD: " + uncompressedDataSizeFromCdRecord); } } int nameLength = ZipUtils.getUnsignedInt16(header, NAME_LENGTH_OFFSET); if (nameLength > cdRecordEntryNameSizeBytes) { throw new ZipFormatException( "Name mismatch between Local File Header and Central Directory for entry" + entryName + ". LFH: " + nameLength + " bytes, CD: " + cdRecordEntryNameSizeBytes + " bytes"); } String name = CentralDirectoryRecord.getName(header, NAME_OFFSET, nameLength); if (!entryName.equals(name)) { throw new ZipFormatException( "Name mismatch between Local File Header and Central Directory. LFH: \"" + name + "\", CD: \"" + entryName + "\""); } int extraLength = ZipUtils.getUnsignedInt16(header, EXTRA_LENGTH_OFFSET); long dataStartOffset = headerStartOffset + HEADER_SIZE_BYTES + nameLength + extraLength; long dataSize; boolean compressed = (cdRecord.getCompressionMethod() != ZipUtils.COMPRESSION_METHOD_STORED); if (compressed) { dataSize = compressedDataSizeFromCdRecord; } else { dataSize = uncompressedDataSizeFromCdRecord; } long dataEndOffset = dataStartOffset + dataSize; if (dataEndOffset > cdStartOffset) { throw new ZipFormatException( "Local File Header data of " + entryName + " overlaps with Central Directory" + ". LFH data start: " + dataStartOffset + ", LFH data end: " + dataEndOffset + ", CD start: " + cdStartOffset); } ByteBuffer extra = EMPTY_BYTE_BUFFER; if ((extraFieldContentsNeeded) && (extraLength > 0)) { extra = apk.getByteBuffer( headerStartOffset + HEADER_SIZE_BYTES + nameLength, extraLength); } long recordEndOffset = dataEndOffset; // Include the Data Descriptor (if requested and present) into the record. if ((dataDescriptorIncluded) && ((gpFlags & ZipUtils.GP_FLAG_DATA_DESCRIPTOR_USED) != 0)) { // The record's data is supposed to be followed by the Data Descriptor. Unfortunately, // the descriptor's size is not known in advance because the spec lets the signature // field (the first four bytes) be omitted. Thus, there's no 100% reliable way to tell // how long the Data Descriptor record is. Most parsers (including Android) check // whether the first four bytes look like Data Descriptor record signature and, if so, // assume that it is indeed the record's signature. However, this is the wrong // conclusion if the record's CRC-32 (next field after the signature) has the same value // as the signature. In any case, we're doing what Android is doing. long dataDescriptorEndOffset = dataEndOffset + DATA_DESCRIPTOR_SIZE_BYTES_WITHOUT_SIGNATURE; if (dataDescriptorEndOffset > cdStartOffset) { throw new ZipFormatException( "Data Descriptor of " + entryName + " overlaps with Central Directory" + ". Data Descriptor end: " + dataEndOffset + ", CD start: " + cdStartOffset); } ByteBuffer dataDescriptorPotentialSig = apk.getByteBuffer(dataEndOffset, 4); dataDescriptorPotentialSig.order(ByteOrder.LITTLE_ENDIAN); if (dataDescriptorPotentialSig.getInt() == DATA_DESCRIPTOR_SIGNATURE) { dataDescriptorEndOffset += 4; if (dataDescriptorEndOffset > cdStartOffset) { throw new ZipFormatException( "Data Descriptor of " + entryName + " overlaps with Central Directory" + ". Data Descriptor end: " + dataEndOffset + ", CD start: " + cdStartOffset); } } recordEndOffset = dataDescriptorEndOffset; } long recordSize = recordEndOffset - headerStartOffset; int dataStartOffsetInRecord = HEADER_SIZE_BYTES + nameLength + extraLength; return new LocalFileRecord( entryName, cdRecordEntryNameSizeBytes, extra, headerStartOffset, recordSize, dataStartOffsetInRecord, dataSize, compressed, uncompressedDataSizeFromCdRecord); } /** * Outputs this record and returns returns the number of bytes output. */ public long outputRecord(DataSource sourceApk, DataSink output) throws IOException { long size = getSize(); sourceApk.feed(getStartOffsetInArchive(), size, output); return size; } /** * Outputs this record, replacing its extra field with the provided one, and returns returns the * number of bytes output. */ public long outputRecordWithModifiedExtra( DataSource sourceApk, ByteBuffer extra, DataSink output) throws IOException { long recordStartOffsetInSource = getStartOffsetInArchive(); int extraStartOffsetInRecord = getExtraFieldStartOffsetInsideRecord(); int extraSizeBytes = extra.remaining(); int headerSize = extraStartOffsetInRecord + extraSizeBytes; ByteBuffer header = ByteBuffer.allocate(headerSize); header.order(ByteOrder.LITTLE_ENDIAN); sourceApk.copyTo(recordStartOffsetInSource, extraStartOffsetInRecord, header); header.put(extra.slice()); header.flip(); ZipUtils.setUnsignedInt16(header, EXTRA_LENGTH_OFFSET, extraSizeBytes); long outputByteCount = header.remaining(); output.consume(header); long remainingRecordSize = getSize() - mDataStartOffset; sourceApk.feed(recordStartOffsetInSource + mDataStartOffset, remainingRecordSize, output); outputByteCount += remainingRecordSize; return outputByteCount; } /** * Outputs the specified Local File Header record with its data and returns the number of bytes * output. */ public static long outputRecordWithDeflateCompressedData( String name, int lastModifiedTime, int lastModifiedDate, byte[] compressedData, long crc32, long uncompressedSize, DataSink output) throws IOException { byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8); int recordSize = HEADER_SIZE_BYTES + nameBytes.length; ByteBuffer result = ByteBuffer.allocate(recordSize); result.order(ByteOrder.LITTLE_ENDIAN); result.putInt(RECORD_SIGNATURE); ZipUtils.putUnsignedInt16(result, 0x14); // Minimum version needed to extract result.putShort(ZipUtils.GP_FLAG_EFS); // General purpose flag: UTF-8 encoded name result.putShort(ZipUtils.COMPRESSION_METHOD_DEFLATED); ZipUtils.putUnsignedInt16(result, lastModifiedTime); ZipUtils.putUnsignedInt16(result, lastModifiedDate); ZipUtils.putUnsignedInt32(result, crc32); ZipUtils.putUnsignedInt32(result, compressedData.length); ZipUtils.putUnsignedInt32(result, uncompressedSize); ZipUtils.putUnsignedInt16(result, nameBytes.length); ZipUtils.putUnsignedInt16(result, 0); // Extra field length result.put(nameBytes); if (result.hasRemaining()) { throw new RuntimeException("pos: " + result.position() + ", limit: " + result.limit()); } result.flip(); long outputByteCount = result.remaining(); output.consume(result); outputByteCount += compressedData.length; output.consume(compressedData, 0, compressedData.length); return outputByteCount; } private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0); /** * Sends uncompressed data of this record into the the provided data sink. */ public void outputUncompressedData( DataSource lfhSection, DataSink sink) throws IOException, ZipFormatException { long dataStartOffsetInArchive = mStartOffsetInArchive + mDataStartOffset; try { if (mDataCompressed) { try (InflateSinkAdapter inflateAdapter = new InflateSinkAdapter(sink)) { lfhSection.feed(dataStartOffsetInArchive, mDataSize, inflateAdapter); long actualUncompressedSize = inflateAdapter.getOutputByteCount(); if (actualUncompressedSize != mUncompressedDataSize) { throw new ZipFormatException( "Unexpected size of uncompressed data of " + mName + ". Expected: " + mUncompressedDataSize + " bytes" + ", actual: " + actualUncompressedSize + " bytes"); } } catch (IOException e) { if (e.getCause() instanceof DataFormatException) { throw new ZipFormatException("Data of entry " + mName + " malformed", e); } throw e; } } else { lfhSection.feed(dataStartOffsetInArchive, mDataSize, sink); // No need to check whether output size is as expected because DataSource.feed is // guaranteed to output exactly the number of bytes requested. } } catch (IOException e) { throw new IOException( "Failed to read data of " + ((mDataCompressed) ? "compressed" : "uncompressed") + " entry " + mName, e); } // Interestingly, Android doesn't check that uncompressed data's CRC-32 is as expected. We // thus don't check either. } /** * Sends uncompressed data pointed to by the provided ZIP Central Directory (CD) record into the * provided data sink. */ public static void outputUncompressedData( DataSource source, CentralDirectoryRecord cdRecord, long cdStartOffsetInArchive, DataSink sink) throws ZipFormatException, IOException { // IMPLEMENTATION NOTE: This method attempts to mimic the behavior of Android platform // exhibited when reading an APK for the purposes of verifying its signatures. // When verifying an APK, Android doesn't care reading the extra field or the Data // Descriptor. LocalFileRecord lfhRecord = getRecord( source, cdRecord, cdStartOffsetInArchive, false, // don't care about the extra field false // don't read the Data Descriptor ); lfhRecord.outputUncompressedData(source, sink); } /** * Returns the uncompressed data pointed to by the provided ZIP Central Directory (CD) record. */ public static byte[] getUncompressedData( DataSource source, CentralDirectoryRecord cdRecord, long cdStartOffsetInArchive) throws ZipFormatException, IOException { if (cdRecord.getUncompressedSize() > Integer.MAX_VALUE) { throw new IOException( cdRecord.getName() + " too large: " + cdRecord.getUncompressedSize()); } byte[] result = null; try { result = new byte[(int) cdRecord.getUncompressedSize()]; } catch (OutOfMemoryError e) { throw new IOException( cdRecord.getName() + " too large: " + cdRecord.getUncompressedSize(), e); } ByteBuffer resultBuf = ByteBuffer.wrap(result); ByteBufferSink resultSink = new ByteBufferSink(resultBuf); outputUncompressedData( source, cdRecord, cdStartOffsetInArchive, resultSink); return result; } /** * {@link DataSink} which inflates received data and outputs the deflated data into the provided * delegate sink. */ private static class InflateSinkAdapter implements DataSink, Closeable { private final DataSink mDelegate; private Inflater mInflater = new Inflater(true); private byte[] mOutputBuffer; private byte[] mInputBuffer; private long mOutputByteCount; private boolean mClosed; private InflateSinkAdapter(DataSink delegate) { mDelegate = delegate; } @Override public void consume(byte[] buf, int offset, int length) throws IOException { checkNotClosed(); mInflater.setInput(buf, offset, length); if (mOutputBuffer == null) { mOutputBuffer = new byte[65536]; } while (!mInflater.finished()) { int outputChunkSize; try { outputChunkSize = mInflater.inflate(mOutputBuffer); } catch (DataFormatException e) { throw new IOException("Failed to inflate data", e); } if (outputChunkSize == 0) { return; } mDelegate.consume(mOutputBuffer, 0, outputChunkSize); mOutputByteCount += outputChunkSize; } } @Override public void consume(ByteBuffer buf) throws IOException { checkNotClosed(); if (buf.hasArray()) { consume(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining()); buf.position(buf.limit()); } else { if (mInputBuffer == null) { mInputBuffer = new byte[65536]; } while (buf.hasRemaining()) { int chunkSize = Math.min(buf.remaining(), mInputBuffer.length); buf.get(mInputBuffer, 0, chunkSize); consume(mInputBuffer, 0, chunkSize); } } } public long getOutputByteCount() { return mOutputByteCount; } @Override public void close() throws IOException { mClosed = true; mInputBuffer = null; mOutputBuffer = null; if (mInflater != null) { mInflater.end(); mInflater = null; } } private void checkNotClosed() { if (mClosed) { throw new IllegalStateException("Closed"); } } } } ./PaxHeaders.X/src_main_java_com_android_apksig_internal_zip_ZipUtils.java0100644 0000000 0000000 00000000034 14763776540 026300 xustar000000000 0000000 28 mtime=1741684064.6090000 src/main/java/com/android/apksig/internal/zip/ZipUtils.java0100644 0000000 0000000 00000041106 14763776540 023006 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.zip; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.internal.util.Pair; import com.android.apksig.util.DataSource; import com.android.apksig.zip.ZipFormatException; import com.android.apksig.zip.ZipSections; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; import java.util.zip.CRC32; import java.util.zip.Deflater; /** * Assorted ZIP format helpers. * *

NOTE: Most helper methods operating on {@code ByteBuffer} instances expect that the byte * order of these buffers is little-endian. */ public abstract class ZipUtils { private ZipUtils() {} public static final short COMPRESSION_METHOD_STORED = 0; public static final short COMPRESSION_METHOD_DEFLATED = 8; public static final short GP_FLAG_DATA_DESCRIPTOR_USED = 0x08; public static final short GP_FLAG_EFS = 0x0800; private static final int ZIP_EOCD_REC_MIN_SIZE = 22; private static final int ZIP_EOCD_REC_SIG = 0x06054b50; private static final int ZIP_EOCD_CENTRAL_DIR_TOTAL_RECORD_COUNT_OFFSET = 10; private static final int ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET = 12; private static final int ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET = 16; private static final int ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET = 20; private static final int UINT16_MAX_VALUE = 0xffff; /** * Sets the offset of the start of the ZIP Central Directory in the archive. * *

NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian. */ public static void setZipEocdCentralDirectoryOffset( ByteBuffer zipEndOfCentralDirectory, long offset) { assertByteOrderLittleEndian(zipEndOfCentralDirectory); setUnsignedInt32( zipEndOfCentralDirectory, zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET, offset); } /** * Sets the length of EOCD comment. * *

NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian. */ public static void updateZipEocdCommentLen(ByteBuffer zipEndOfCentralDirectory) { assertByteOrderLittleEndian(zipEndOfCentralDirectory); int commentLen = zipEndOfCentralDirectory.remaining() - ZIP_EOCD_REC_MIN_SIZE; setUnsignedInt16( zipEndOfCentralDirectory, zipEndOfCentralDirectory.position() + ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET, commentLen); } /** * Returns the offset of the start of the ZIP Central Directory in the archive. * *

NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian. */ public static long getZipEocdCentralDirectoryOffset(ByteBuffer zipEndOfCentralDirectory) { assertByteOrderLittleEndian(zipEndOfCentralDirectory); return getUnsignedInt32( zipEndOfCentralDirectory, zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET); } /** * Returns the size (in bytes) of the ZIP Central Directory. * *

NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian. */ public static long getZipEocdCentralDirectorySizeBytes(ByteBuffer zipEndOfCentralDirectory) { assertByteOrderLittleEndian(zipEndOfCentralDirectory); return getUnsignedInt32( zipEndOfCentralDirectory, zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET); } /** * Returns the total number of records in ZIP Central Directory. * *

NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian. */ public static int getZipEocdCentralDirectoryTotalRecordCount( ByteBuffer zipEndOfCentralDirectory) { assertByteOrderLittleEndian(zipEndOfCentralDirectory); return getUnsignedInt16( zipEndOfCentralDirectory, zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_TOTAL_RECORD_COUNT_OFFSET); } /** * Returns the ZIP End of Central Directory record of the provided ZIP file. * * @return contents of the ZIP End of Central Directory record and the record's offset in the * file or {@code null} if the file does not contain the record. * * @throws IOException if an I/O error occurs while reading the file. */ public static Pair findZipEndOfCentralDirectoryRecord(DataSource zip) throws IOException { // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. // The record can be identified by its 4-byte signature/magic which is located at the very // beginning of the record. A complication is that the record is variable-length because of // the comment field. // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from // end of the buffer for the EOCD record signature. Whenever we find a signature, we check // the candidate record's comment length is such that the remainder of the record takes up // exactly the remaining bytes in the buffer. The search is bounded because the maximum // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. long fileSize = zip.size(); if (fileSize < ZIP_EOCD_REC_MIN_SIZE) { return null; } // Optimization: 99.99% of APKs have a zero-length comment field in the EoCD record and thus // the EoCD record offset is known in advance. Try that offset first to avoid unnecessarily // reading more data. Pair result = findZipEndOfCentralDirectoryRecord(zip, 0); if (result != null) { return result; } // EoCD does not start where we expected it to. Perhaps it contains a non-empty comment // field. Expand the search. The maximum size of the comment field in EoCD is 65535 because // the comment length field is an unsigned 16-bit number. return findZipEndOfCentralDirectoryRecord(zip, UINT16_MAX_VALUE); } /** * Returns the ZIP End of Central Directory record of the provided ZIP file. * * @param maxCommentSize maximum accepted size (in bytes) of EoCD comment field. The permitted * value is from 0 to 65535 inclusive. The smaller the value, the faster this method * locates the record, provided its comment field is no longer than this value. * * @return contents of the ZIP End of Central Directory record and the record's offset in the * file or {@code null} if the file does not contain the record. * * @throws IOException if an I/O error occurs while reading the file. */ private static Pair findZipEndOfCentralDirectoryRecord( DataSource zip, int maxCommentSize) throws IOException { // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. // The record can be identified by its 4-byte signature/magic which is located at the very // beginning of the record. A complication is that the record is variable-length because of // the comment field. // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from // end of the buffer for the EOCD record signature. Whenever we find a signature, we check // the candidate record's comment length is such that the remainder of the record takes up // exactly the remaining bytes in the buffer. The search is bounded because the maximum // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. if ((maxCommentSize < 0) || (maxCommentSize > UINT16_MAX_VALUE)) { throw new IllegalArgumentException("maxCommentSize: " + maxCommentSize); } long fileSize = zip.size(); if (fileSize < ZIP_EOCD_REC_MIN_SIZE) { // No space for EoCD record in the file. return null; } // Lower maxCommentSize if the file is too small. maxCommentSize = (int) Math.min(maxCommentSize, fileSize - ZIP_EOCD_REC_MIN_SIZE); int maxEocdSize = ZIP_EOCD_REC_MIN_SIZE + maxCommentSize; long bufOffsetInFile = fileSize - maxEocdSize; ByteBuffer buf = zip.getByteBuffer(bufOffsetInFile, maxEocdSize); buf.order(ByteOrder.LITTLE_ENDIAN); int eocdOffsetInBuf = findZipEndOfCentralDirectoryRecord(buf); if (eocdOffsetInBuf == -1) { // No EoCD record found in the buffer return null; } // EoCD found buf.position(eocdOffsetInBuf); ByteBuffer eocd = buf.slice(); eocd.order(ByteOrder.LITTLE_ENDIAN); return Pair.of(eocd, bufOffsetInFile + eocdOffsetInBuf); } /** * Returns the position at which ZIP End of Central Directory record starts in the provided * buffer or {@code -1} if the record is not present. * *

NOTE: Byte order of {@code zipContents} must be little-endian. */ private static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) { assertByteOrderLittleEndian(zipContents); // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. // The record can be identified by its 4-byte signature/magic which is located at the very // beginning of the record. A complication is that the record is variable-length because of // the comment field. // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from // end of the buffer for the EOCD record signature. Whenever we find a signature, we check // the candidate record's comment length is such that the remainder of the record takes up // exactly the remaining bytes in the buffer. The search is bounded because the maximum // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. int archiveSize = zipContents.capacity(); if (archiveSize < ZIP_EOCD_REC_MIN_SIZE) { return -1; } int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT16_MAX_VALUE); int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE; for (int expectedCommentLength = 0; expectedCommentLength <= maxCommentLength; expectedCommentLength++) { int eocdStartPos = eocdWithEmptyCommentStartPosition - expectedCommentLength; if (zipContents.getInt(eocdStartPos) == ZIP_EOCD_REC_SIG) { int actualCommentLength = getUnsignedInt16( zipContents, eocdStartPos + ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET); if (actualCommentLength == expectedCommentLength) { return eocdStartPos; } } } return -1; } static void assertByteOrderLittleEndian(ByteBuffer buffer) { if (buffer.order() != ByteOrder.LITTLE_ENDIAN) { throw new IllegalArgumentException("ByteBuffer byte order must be little endian"); } } public static int getUnsignedInt16(ByteBuffer buffer, int offset) { return buffer.getShort(offset) & 0xffff; } public static int getUnsignedInt16(ByteBuffer buffer) { return buffer.getShort() & 0xffff; } public static List parseZipCentralDirectory( DataSource apk, ZipSections apkSections) throws IOException, ApkFormatException { // Read the ZIP Central Directory long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes(); if (cdSizeBytes > Integer.MAX_VALUE) { throw new ApkFormatException("ZIP Central Directory too large: " + cdSizeBytes); } long cdOffset = apkSections.getZipCentralDirectoryOffset(); ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes); cd.order(ByteOrder.LITTLE_ENDIAN); // Parse the ZIP Central Directory int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount(); List cdRecords = new ArrayList<>(expectedCdRecordCount); for (int i = 0; i < expectedCdRecordCount; i++) { CentralDirectoryRecord cdRecord; int offsetInsideCd = cd.position(); try { cdRecord = CentralDirectoryRecord.getRecord(cd); } catch (ZipFormatException e) { throw new ApkFormatException( "Malformed ZIP Central Directory record #" + (i + 1) + " at file offset " + (cdOffset + offsetInsideCd), e); } String entryName = cdRecord.getName(); if (entryName.endsWith("/")) { // Ignore directory entries continue; } cdRecords.add(cdRecord); } // There may be more data in Central Directory, but we don't warn or throw because Android // ignores unused CD data. return cdRecords; } static void setUnsignedInt16(ByteBuffer buffer, int offset, int value) { if ((value < 0) || (value > 0xffff)) { throw new IllegalArgumentException("uint16 value of out range: " + value); } buffer.putShort(offset, (short) value); } static void setUnsignedInt32(ByteBuffer buffer, int offset, long value) { if ((value < 0) || (value > 0xffffffffL)) { throw new IllegalArgumentException("uint32 value of out range: " + value); } buffer.putInt(offset, (int) value); } public static void putUnsignedInt16(ByteBuffer buffer, int value) { if ((value < 0) || (value > 0xffff)) { throw new IllegalArgumentException("uint16 value of out range: " + value); } buffer.putShort((short) value); } static long getUnsignedInt32(ByteBuffer buffer, int offset) { return buffer.getInt(offset) & 0xffffffffL; } static long getUnsignedInt32(ByteBuffer buffer) { return buffer.getInt() & 0xffffffffL; } static void putUnsignedInt32(ByteBuffer buffer, long value) { if ((value < 0) || (value > 0xffffffffL)) { throw new IllegalArgumentException("uint32 value of out range: " + value); } buffer.putInt((int) value); } public static DeflateResult deflate(ByteBuffer input) { byte[] inputBuf; int inputOffset; int inputLength = input.remaining(); if (input.hasArray()) { inputBuf = input.array(); inputOffset = input.arrayOffset() + input.position(); input.position(input.limit()); } else { inputBuf = new byte[inputLength]; inputOffset = 0; input.get(inputBuf); } CRC32 crc32 = new CRC32(); crc32.update(inputBuf, inputOffset, inputLength); long crc32Value = crc32.getValue(); ByteArrayOutputStream out = new ByteArrayOutputStream(); Deflater deflater = new Deflater(9, true); deflater.setInput(inputBuf, inputOffset, inputLength); deflater.finish(); byte[] buf = new byte[65536]; while (!deflater.finished()) { int chunkSize = deflater.deflate(buf); out.write(buf, 0, chunkSize); } return new DeflateResult(inputLength, crc32Value, out.toByteArray()); } public static class DeflateResult { public final int inputSizeBytes; public final long inputCrc32; public final byte[] output; public DeflateResult(int inputSizeBytes, long inputCrc32, byte[] output) { this.inputSizeBytes = inputSizeBytes; this.inputCrc32 = inputCrc32; this.output = output; } } }./PaxHeaders.X/src_main_java_com_android_apksig_kms_0100644 0000000 0000000 00000000034 14763776540 021750 xustar000000000 0000000 28 mtime=1741684064.6100000 src/main/java/com/android/apksig/kms/0040755 0000000 0000000 00000000000 14763776540 016535 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_kms_KmsException.java0100644 0000000 0000000 00000000034 14763776540 025222 xustar000000000 0000000 28 mtime=1741684064.6100000 src/main/java/com/android/apksig/kms/KmsException.java0100644 0000000 0000000 00000002473 14763776540 022014 0ustar000000000 0000000 /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.kms; /** Represents an exception thrown by the external KMS. */ public class KmsException extends RuntimeException { private final KmsType mKmsType; public KmsException(KmsType kmsType, String message) { super(message); this.mKmsType = kmsType; } public KmsException(KmsType kmsType, String message, Throwable cause) { super(message, cause); this.mKmsType = kmsType; } public KmsException(KmsType kmsType, Throwable cause) { super(cause); this.mKmsType = kmsType; } @Override public String getMessage() { return "KMS " + mKmsType.toString() + " threw exception: " + super.getMessage(); } } ./PaxHeaders.X/src_main_java_com_android_apksig_kms_KmsSignerEngine.java0100644 0000000 0000000 00000000034 14763776540 025641 xustar000000000 0000000 28 mtime=1741684064.6100000 src/main/java/com/android/apksig/kms/KmsSignerEngine.java0100644 0000000 0000000 00000002753 14763776540 022434 0ustar000000000 0000000 /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.kms; import com.android.apksig.KeyConfig; import com.android.apksig.SignerEngine; /** Performs cryptographic signing with a Key Management Service (KMS). */ public abstract class KmsSignerEngine implements SignerEngine { public final KmsType kmsType; public final String keyAlias; /** Subclasses must specify the type of KMS and a signing key alias. */ public KmsSignerEngine(KmsType kmsType, String keyAlias) { this.kmsType = kmsType; this.keyAlias = keyAlias; } @Override public abstract byte[] sign(byte[] data); /** Fetch a concrete implementation based on the provided config. */ public static KmsSignerEngine fromKmsConfig(KeyConfig.Kms kmsConfig) { switch (kmsConfig.kmsType) { default: throw new KmsException(kmsConfig.kmsType, "Unsupported KMS"); } } } ./PaxHeaders.X/src_main_java_com_android_apksig_kms_KmsType.java0100644 0000000 0000000 00000000034 14763776540 024205 xustar000000000 0000000 28 mtime=1741684064.6100000 src/main/java/com/android/apksig/kms/KmsType.java0100644 0000000 0000000 00000001336 14763776540 020774 0ustar000000000 0000000 /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.kms; /** Represents the supported Key Management Services. */ public enum KmsType {} ./PaxHeaders.X/src_main_java_com_android_apksig_util_0100644 0000000 0000000 00000000034 14763776540 022133 xustar000000000 0000000 28 mtime=1741684064.6100000 src/main/java/com/android/apksig/util/0040755 0000000 0000000 00000000000 14763776540 016720 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_util_DataSink.java0100644 0000000 0000000 00000000034 14763776540 024472 xustar000000000 0000000 28 mtime=1741684064.6100000 src/main/java/com/android/apksig/util/DataSink.java0100644 0000000 0000000 00000003054 14763776540 021260 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import java.io.IOException; import java.nio.ByteBuffer; /** * Consumer of input data which may be provided in one go or in chunks. */ public interface DataSink { /** * Consumes the provided chunk of data. * *

This data sink guarantees to not hold references to the provided buffer after this method * terminates. * * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if * {@code offset + length} is greater than {@code buf.length}. */ void consume(byte[] buf, int offset, int length) throws IOException; /** * Consumes all remaining data in the provided buffer and advances the buffer's position * to the buffer's limit. * *

This data sink guarantees to not hold references to the provided buffer after this method * terminates. */ void consume(ByteBuffer buf) throws IOException; } ./PaxHeaders.X/src_main_java_com_android_apksig_util_DataSinks.java0100644 0000000 0000000 00000000034 14763776540 024655 xustar000000000 0000000 28 mtime=1741684064.6100000 src/main/java/com/android/apksig/util/DataSinks.java0100644 0000000 0000000 00000005060 14763776540 021442 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import com.android.apksig.internal.util.ByteArrayDataSink; import com.android.apksig.internal.util.MessageDigestSink; import com.android.apksig.internal.util.OutputStreamDataSink; import com.android.apksig.internal.util.RandomAccessFileDataSink; import java.io.OutputStream; import java.io.RandomAccessFile; import java.security.MessageDigest; /** * Utility methods for working with {@link DataSink} abstraction. */ public abstract class DataSinks { private DataSinks() {} /** * Returns a {@link DataSink} which outputs received data into the provided * {@link OutputStream}. */ public static DataSink asDataSink(OutputStream out) { return new OutputStreamDataSink(out); } /** * Returns a {@link DataSink} which outputs received data into the provided file, sequentially, * starting at the beginning of the file. */ public static DataSink asDataSink(RandomAccessFile file) { return new RandomAccessFileDataSink(file); } /** * Returns a {@link DataSink} which forwards data into the provided {@link MessageDigest} * instances via their {@code update} method. Each {@code MessageDigest} instance receives the * same data. */ public static DataSink asDataSink(MessageDigest... digests) { return new MessageDigestSink(digests); } /** * Returns a new in-memory {@link DataSink} which exposes all data consumed so far via the * {@link DataSource} interface. */ public static ReadableDataSink newInMemoryDataSink() { return new ByteArrayDataSink(); } /** * Returns a new in-memory {@link DataSink} which exposes all data consumed so far via the * {@link DataSource} interface. * * @param initialCapacity initial capacity in bytes */ public static ReadableDataSink newInMemoryDataSink(int initialCapacity) { return new ByteArrayDataSink(initialCapacity); } } ./PaxHeaders.X/src_main_java_com_android_apksig_util_DataSource.java0100644 0000000 0000000 00000000034 14763776540 025026 xustar000000000 0000000 28 mtime=1741684064.6100000 src/main/java/com/android/apksig/util/DataSource.java0100644 0000000 0000000 00000011773 14763776540 021623 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import java.io.IOException; import java.nio.ByteBuffer; /** * Abstract representation of a source of data. * *

This abstraction serves three purposes: *

    *
  • Transparent handling of different types of sources, such as {@code byte[]}, * {@link java.nio.ByteBuffer}, {@link java.io.RandomAccessFile}, memory-mapped file.
  • *
  • Support sources larger than 2 GB. If all sources were smaller than 2 GB, {@code ByteBuffer} * may have worked as the unifying abstraction.
  • *
  • Support sources which do not fit into logical memory as a contiguous region.
  • *
* *

There are following ways to obtain a chunk of data from the data source: *

    *
  • Stream the chunk's data into a {@link DataSink} using * {@link #feed(long, long, DataSink) feed}. This is best suited for scenarios where there is no * need to have the chunk's data accessible at the same time, for example, when computing the * digest of the chunk. If you need to keep the chunk's data around after {@code feed} * completes, you must create a copy during {@code feed}. However, in that case the following * methods of obtaining the chunk's data may be more appropriate.
  • *
  • Obtain a {@link ByteBuffer} containing the chunk's data using * {@link #getByteBuffer(long, int) getByteBuffer}. Depending on the data source, the chunk's * data may or may not be copied by this operation. This is best suited for scenarios where * you need to access the chunk's data in arbitrary order, but don't need to modify the data and * thus don't require a copy of the data.
  • *
  • Copy the chunk's data to a {@link ByteBuffer} using * {@link #copyTo(long, int, ByteBuffer) copyTo}. This is best suited for scenarios where * you require a copy of the chunk's data, such as to when you need to modify the data. *
  • *
*/ public interface DataSource { /** * Returns the amount of data (in bytes) contained in this data source. */ long size(); /** * Feeds the specified chunk from this data source into the provided sink. * * @param offset index (in bytes) at which the chunk starts inside data source * @param size size (in bytes) of the chunk * * @throws IndexOutOfBoundsException if {@code offset} or {@code size} is negative, or if * {@code offset + size} is greater than {@link #size()}. */ void feed(long offset, long size, DataSink sink) throws IOException; /** * Returns a buffer holding the contents of the specified chunk of data from this data source. * Changes to the data source are not guaranteed to be reflected in the returned buffer. * Similarly, changes in the buffer are not guaranteed to be reflected in the data source. * *

The returned buffer's position is {@code 0}, and the buffer's limit and capacity is * {@code size}. * * @param offset index (in bytes) at which the chunk starts inside data source * @param size size (in bytes) of the chunk * * @throws IndexOutOfBoundsException if {@code offset} or {@code size} is negative, or if * {@code offset + size} is greater than {@link #size()}. */ ByteBuffer getByteBuffer(long offset, int size) throws IOException; /** * Copies the specified chunk from this data source into the provided destination buffer, * advancing the destination buffer's position by {@code size}. * * @param offset index (in bytes) at which the chunk starts inside data source * @param size size (in bytes) of the chunk * * @throws IndexOutOfBoundsException if {@code offset} or {@code size} is negative, or if * {@code offset + size} is greater than {@link #size()}. */ void copyTo(long offset, int size, ByteBuffer dest) throws IOException; /** * Returns a data source representing the specified region of data of this data source. Changes * to data represented by this data source will also be visible in the returned data source. * * @param offset index (in bytes) at which the region starts inside data source * @param size size (in bytes) of the region * * @throws IndexOutOfBoundsException if {@code offset} or {@code size} is negative, or if * {@code offset + size} is greater than {@link #size()}. */ DataSource slice(long offset, long size); } ./PaxHeaders.X/src_main_java_com_android_apksig_util_DataSources.java0100644 0000000 0000000 00000000034 14763776540 025211 xustar000000000 0000000 28 mtime=1741684064.6110000 src/main/java/com/android/apksig/util/DataSources.java0100644 0000000 0000000 00000005650 14763776540 022003 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import com.android.apksig.internal.util.ByteBufferDataSource; import com.android.apksig.internal.util.FileChannelDataSource; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * Utility methods for working with {@link DataSource} abstraction. */ public abstract class DataSources { private DataSources() {} /** * Returns a {@link DataSource} backed by the provided {@link ByteBuffer}. The data source * represents the data contained between the position and limit of the buffer. Changes to the * buffer's contents will be visible in the data source. */ public static DataSource asDataSource(ByteBuffer buffer) { if (buffer == null) { throw new NullPointerException(); } return new ByteBufferDataSource(buffer); } /** * Returns a {@link DataSource} backed by the provided {@link RandomAccessFile}. Changes to the * file, including changes to size of file, will be visible in the data source. */ public static DataSource asDataSource(RandomAccessFile file) { return asDataSource(file.getChannel()); } /** * Returns a {@link DataSource} backed by the provided region of the {@link RandomAccessFile}. * Changes to the file will be visible in the data source. */ public static DataSource asDataSource(RandomAccessFile file, long offset, long size) { return asDataSource(file.getChannel(), offset, size); } /** * Returns a {@link DataSource} backed by the provided {@link FileChannel}. Changes to the * file, including changes to size of file, will be visible in the data source. */ public static DataSource asDataSource(FileChannel channel) { if (channel == null) { throw new NullPointerException(); } return new FileChannelDataSource(channel); } /** * Returns a {@link DataSource} backed by the provided region of the {@link FileChannel}. * Changes to the file will be visible in the data source. */ public static DataSource asDataSource(FileChannel channel, long offset, long size) { if (channel == null) { throw new NullPointerException(); } return new FileChannelDataSource(channel, offset, size); } } ./PaxHeaders.X/src_main_java_com_android_apksig_util_ReadableDataSink.java0100644 0000000 0000000 00000000034 14763776540 026112 xustar000000000 0000000 28 mtime=1741684064.6110000 src/main/java/com/android/apksig/util/ReadableDataSink.java0100644 0000000 0000000 00000001572 14763776540 022703 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; /** * {@link DataSink} which exposes all data consumed so far as a {@link DataSource}. This abstraction * offers append-only write access and random read access. */ public interface ReadableDataSink extends DataSink, DataSource { } ./PaxHeaders.X/src_main_java_com_android_apksig_util_RunnablesExecutor.java0100644 0000000 0000000 00000000034 14763776540 026444 xustar000000000 0000000 28 mtime=1741684064.6110000 src/main/java/com/android/apksig/util/RunnablesExecutor.java0100644 0000000 0000000 00000004214 14763776540 023231 0ustar000000000 0000000 /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Phaser; import java.util.concurrent.ThreadPoolExecutor; public interface RunnablesExecutor { static final RunnablesExecutor SINGLE_THREADED = p -> p.createRunnable().run(); static final RunnablesExecutor MULTI_THREADED = new RunnablesExecutor() { private final int PARALLELISM = Math.min(32, Runtime.getRuntime().availableProcessors()); private final int QUEUE_SIZE = 4; @Override public void execute(RunnablesProvider provider) { final ExecutorService mExecutor = new ThreadPoolExecutor(PARALLELISM, PARALLELISM, 0L, MILLISECONDS, new ArrayBlockingQueue<>(QUEUE_SIZE), new ThreadPoolExecutor.CallerRunsPolicy()); Phaser tasks = new Phaser(1); for (int i = 0; i < PARALLELISM; ++i) { Runnable task = () -> { Runnable r = provider.createRunnable(); r.run(); tasks.arriveAndDeregister(); }; tasks.register(); mExecutor.execute(task); } // Waiting for the tasks to complete. tasks.arriveAndAwaitAdvance(); mExecutor.shutdownNow(); } }; void execute(RunnablesProvider provider); } ./PaxHeaders.X/src_main_java_com_android_apksig_util_RunnablesProvider.java0100644 0000000 0000000 00000000034 14763776540 026440 xustar000000000 0000000 28 mtime=1741684064.6110000 src/main/java/com/android/apksig/util/RunnablesProvider.java0100644 0000000 0000000 00000001325 14763776540 023225 0ustar000000000 0000000 /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; public interface RunnablesProvider { Runnable createRunnable(); } ./PaxHeaders.X/src_main_java_com_android_apksig_zip_0100644 0000000 0000000 00000000034 14763776540 021760 xustar000000000 0000000 28 mtime=1741684064.6110000 src/main/java/com/android/apksig/zip/0040755 0000000 0000000 00000000000 14763776540 016545 5ustar000000000 0000000 ./PaxHeaders.X/src_main_java_com_android_apksig_zip_ZipFormatException.java0100644 0000000 0000000 00000000034 14763776540 026413 xustar000000000 0000000 28 mtime=1741684064.6110000 src/main/java/com/android/apksig/zip/ZipFormatException.java0100644 0000000 0000000 00000001753 14763776540 023205 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.zip; /** * Indicates that a ZIP archive is not well-formed. */ public class ZipFormatException extends Exception { private static final long serialVersionUID = 1L; public ZipFormatException(String message) { super(message); } public ZipFormatException(String message, Throwable cause) { super(message, cause); } } ./PaxHeaders.X/src_main_java_com_android_apksig_zip_ZipSections.java0100644 0000000 0000000 00000000034 14763776540 025073 xustar000000000 0000000 28 mtime=1741684064.6110000 src/main/java/com/android/apksig/zip/ZipSections.java0100644 0000000 0000000 00000005476 14763776540 021673 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.zip; import java.nio.ByteBuffer; /** * Base representation of an APK's zip sections containing the central directory's offset, the size * of the central directory in bytes, the number of records in the central directory, the offset * of the end of central directory, and a ByteBuffer containing the end of central directory * contents. */ public class ZipSections { private final long mCentralDirectoryOffset; private final long mCentralDirectorySizeBytes; private final int mCentralDirectoryRecordCount; private final long mEocdOffset; private final ByteBuffer mEocd; public ZipSections( long centralDirectoryOffset, long centralDirectorySizeBytes, int centralDirectoryRecordCount, long eocdOffset, ByteBuffer eocd) { mCentralDirectoryOffset = centralDirectoryOffset; mCentralDirectorySizeBytes = centralDirectorySizeBytes; mCentralDirectoryRecordCount = centralDirectoryRecordCount; mEocdOffset = eocdOffset; mEocd = eocd; } /** * Returns the start offset of the ZIP Central Directory. This value is taken from the * ZIP End of Central Directory record. */ public long getZipCentralDirectoryOffset() { return mCentralDirectoryOffset; } /** * Returns the size (in bytes) of the ZIP Central Directory. This value is taken from the * ZIP End of Central Directory record. */ public long getZipCentralDirectorySizeBytes() { return mCentralDirectorySizeBytes; } /** * Returns the number of records in the ZIP Central Directory. This value is taken from the * ZIP End of Central Directory record. */ public int getZipCentralDirectoryRecordCount() { return mCentralDirectoryRecordCount; } /** * Returns the start offset of the ZIP End of Central Directory record. The record extends * until the very end of the APK. */ public long getZipEndOfCentralDirectoryOffset() { return mEocdOffset; } /** * Returns the contents of the ZIP End of Central Directory. */ public ByteBuffer getZipEndOfCentralDirectory() { return mEocd; } }./PaxHeaders.X/src_test_0100644 0000000 0000000 00000000034 14763776540 014234 xustar000000000 0000000 28 mtime=1741684064.6120000 src/test/0040755 0000000 0000000 00000000000 14763776540 011401 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_0100644 0000000 0000000 00000000034 14763776540 015235 xustar000000000 0000000 28 mtime=1741684064.6120000 src/test/java/0040755 0000000 0000000 00000000000 14763776540 012322 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_0100644 0000000 0000000 00000000034 14763776540 016073 xustar000000000 0000000 28 mtime=1741684064.6120000 src/test/java/com/0040755 0000000 0000000 00000000000 14763776540 013100 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_0100644 0000000 0000000 00000000034 14763776540 017573 xustar000000000 0000000 28 mtime=1741684064.6120000 src/test/java/com/android/0040755 0000000 0000000 00000000000 14763776540 014520 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_0100644 0000000 0000000 00000000034 14763776540 021131 xustar000000000 0000000 28 mtime=1741684064.6120000 src/test/java/com/android/apksig/0040755 0000000 0000000 00000000000 14763776540 015776 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_AllTests.java0100644 0000000 0000000 00000000034 14763776540 023525 xustar000000000 0000000 28 mtime=1741684064.6120000 src/test/java/com/android/apksig/AllTests.java0100644 0000000 0000000 00000002033 14763776540 020367 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ ApkSignerTest.class, ApkVerifierTest.class, SigningCertificateLineageTest.class, SourceStampVerifierTest.class, com.android.apksig.apk.AllTests.class, com.android.apksig.internal.AllTests.class, com.android.apksig.util.AllTests.class, }) public class AllTests {} ./PaxHeaders.X/src_test_java_com_android_apksig_ApkSignerTest.java0100644 0000000 0000000 00000000034 14763776540 024515 xustar000000000 0000000 28 mtime=1741684064.6130000 src/test/java/com/android/apksig/ApkSignerTest.java0100644 0000000 0000000 00000574746 14763776540 021410 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.ApkVerifier.Result.V3SchemeSignerInfo; import static com.android.apksig.ApkVerifierTest.assertVerificationWarning; import static com.android.apksig.SigningCertificateLineage.SignerCapabilities; import static com.android.apksig.SigningCertificateLineageTest.assertLineageContainsExpectedSigners; import static com.android.apksig.SigningCertificateLineageTest.assertLineageContainsExpectedSignersWithCapabilities; import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME; import static com.android.apksig.apk.ApkUtils.findZipSections; import static com.android.apksig.internal.util.Resources.EC_P256_2_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.EC_P256_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS; import static com.android.apksig.internal.util.Resources.FIRST_RSA_2048_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.FIRST_RSA_4096_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_2_SIGNERS_2_3_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_3_SIGNERS_1_NO_CAPS_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_TO_RSA_4096_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.SECOND_RSA_2048_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.THIRD_RSA_2048_SIGNER_RESOURCE_NAME; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeNoException; import static org.junit.Assume.assumeTrue; import com.android.apksig.ApkVerifier.Issue; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.SignatureInfo; import com.android.apksig.internal.apk.stamp.SourceStampConstants; import com.android.apksig.internal.apk.v1.V1SchemeVerifier; import com.android.apksig.internal.apk.v2.V2SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.asn1.Asn1BerParser; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.Pair; import com.android.apksig.internal.util.Resources; import com.android.apksig.internal.x509.RSAPublicKey; import com.android.apksig.internal.x509.SubjectPublicKeyInfo; import com.android.apksig.internal.zip.CentralDirectoryRecord; import com.android.apksig.internal.zip.LocalFileRecord; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import com.android.apksig.zip.ZipFormatException; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Security; import java.security.Signature; import java.security.SignatureException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @RunWith(JUnit4.class) public class ApkSignerTest { /** * Whether to preserve, as files, outputs of failed tests. This is useful for investigating test * failures. */ private static final boolean KEEP_FAILING_OUTPUT_AS_FILES = false; private static final SignerCapabilities DEFAULT_CAPABILITIES = new SignerCapabilities.Builder().build(); private static final SignerCapabilities NO_CAPABILITIES = new SignerCapabilities.Builder( 0).build(); // These are the ID and value of an extra signature block within the APK signing block that // can be preserved through the setOtherSignersSignaturesPreserved API. private final int EXTRA_BLOCK_ID = 0x7e57c0de; private final byte[] EXTRA_BLOCK_VALUE = {0, 1, 2, 3, 4, 5, 6, 7}; @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); public static void main(String[] params) throws Exception { File outDir = (params.length > 0) ? new File(params[0]) : new File("."); generateGoldenFiles(outDir); } private static void generateGoldenFiles(File outDir) throws Exception { System.out.println( "Generating golden files " + ApkSignerTest.class.getSimpleName() + " into " + outDir); if (!outDir.exists()) { if (!outDir.mkdirs()) { throw new IOException("Failed to create directory: " + outDir); } } List rsa2048SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); List rsa2048SignerConfigWithLineage = Arrays.asList( rsa2048SignerConfig.get(0), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v1-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v1-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v1-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v3-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v3-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v3-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v3-lineage-out.apk"), new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v3-lineage-out.apk"), new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v3-lineage-out.apk"), new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v1v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(false)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v1v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(false)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v1v2-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(false)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v2v3-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v2v3-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v2v3-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v2v3-lineage-out.apk"), new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v2v3-lineage-out.apk"), new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v2v3-lineage-out.apk"), new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v1v2v3-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v1v2v3-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v1v2v3-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true)); signGolden( "golden-unaligned-in.apk", new File(outDir, "golden-unaligned-v1v2v3-lineage-out.apk"), new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-legacy-aligned-in.apk", new File(outDir, "golden-legacy-aligned-v1v2v3-lineage-out.apk"), new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "golden-aligned-in.apk", new File(outDir, "golden-aligned-v1v2v3-lineage-out.apk"), new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); signGolden( "original.apk", new File(outDir, "golden-rsa-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig)); signGolden( "original.apk", new File(outDir, "golden-rsa-minSdkVersion-1-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(1)); signGolden( "original.apk", new File(outDir, "golden-rsa-minSdkVersion-18-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(18)); signGolden( "original.apk", new File(outDir, "golden-rsa-minSdkVersion-24-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(24)); signGolden( "original.apk", new File(outDir, "golden-rsa-verity-out.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setVerityEnabled(true)); signGolden( "original.apk", new File(outDir, "golden-file-size-aligned.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setAlignFileSize(true)); signGolden( "pinsapp-unsigned.apk", new File(outDir, "golden-pinsapp-signed.apk"), new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setVerityEnabled(true)); } private static void signGolden( String inResourceName, File outFile, ApkSigner.Builder apkSignerBuilder) throws Exception { DataSource in = DataSources.asDataSource( ByteBuffer.wrap(Resources.toByteArray(ApkSigner.class, inResourceName))); apkSignerBuilder.setInputApk(in).setOutputApk(outFile); File outFileIdSig = new File(outFile.getCanonicalPath() + ".idsig"); apkSignerBuilder.setV4SignatureOutputFile(outFileIdSig); apkSignerBuilder.setV4ErrorReportingEnabled(true); apkSignerBuilder.build().sign(); } @Test public void testAlignmentPreserved_Golden() throws Exception { // Regression tests for preserving (mis)alignment of ZIP Local File Header data // NOTE: Expected output files can be re-generated by running the "main" method. List rsa2048SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); List rsa2048SignerConfigWithLineage = Arrays.asList( rsa2048SignerConfig.get(0), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); // Uncompressed entries in this input file are not aligned -- the file was created using // the jar utility. temp4.txt entry was then manually added into the archive. This entry's // ZIP Local File Header "extra" field declares that the entry's data must be aligned to // 4 kB boundary, but the data isn't actually aligned in the file. assertGolden( "golden-unaligned-in.apk", "golden-unaligned-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setAlignmentPreserved(true)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v1-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false) .setAlignmentPreserved(true)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setAlignmentPreserved(true)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v3-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setAlignmentPreserved(true)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v3-lineage-out.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage) .setAlignmentPreserved(true)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v1v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setAlignmentPreserved(true)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v2v3-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setAlignmentPreserved(true)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v2v3-lineage-out.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage) .setAlignmentPreserved(true)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v1v2v3-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setAlignmentPreserved(true)); assertGolden( "golden-unaligned-in.apk", "golden-unaligned-v1v2v3-lineage-out.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage) .setAlignmentPreserved(true)); // Uncompressed entries in this input file are aligned by zero-padding the "extra" field, as // performed by zipalign at the time of writing. This padding technique produces ZIP // archives whose "extra" field are not compliant with APPNOTE.TXT. Hence, this technique // was deprecated. assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v3-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v3-lineage-out.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2v3-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v2v3-lineage-out.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2v3-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); assertGolden( "golden-legacy-aligned-in.apk", "golden-legacy-aligned-v1v2v3-lineage-out.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage) .setLibraryPageAlignmentBytes(4096) .setAlignmentPreserved(true)); // Uncompressed entries in this input file are aligned by padding the "extra" field, as // generated by signapk and apksigner. This padding technique produces "extra" fields which // are compliant with APPNOTE.TXT. assertGolden( "golden-aligned-in.apk", "golden-aligned-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setAlignmentPreserved(true)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v1-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false) .setAlignmentPreserved(true)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setAlignmentPreserved(true)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v3-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setAlignmentPreserved(true)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v3-lineage-out.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage) .setAlignmentPreserved(true)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v1v2-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setAlignmentPreserved(true)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v2v3-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setAlignmentPreserved(true)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v2v3-lineage-out.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage) .setAlignmentPreserved(true)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v1v2v3-out.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setAlignmentPreserved(true)); assertGolden( "golden-aligned-in.apk", "golden-aligned-v1v2v3-lineage-out.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage) .setAlignmentPreserved(true)); } @Test public void testMinSdkVersion_Golden() throws Exception { // Regression tests for minSdkVersion-based signature/digest algorithm selection // NOTE: Expected output files can be re-generated by running the "main" method. List rsaSignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); assertGolden("original.apk", "golden-rsa-out.apk", new ApkSigner.Builder(rsaSignerConfig).setAlignmentPreserved(true)); assertGolden( "original.apk", "golden-rsa-minSdkVersion-1-out.apk", new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(1) .setAlignmentPreserved(true)); assertGolden( "original.apk", "golden-rsa-minSdkVersion-18-out.apk", new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(18) .setAlignmentPreserved(true)); assertGolden( "original.apk", "golden-rsa-minSdkVersion-24-out.apk", new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(24) .setAlignmentPreserved(true)); // TODO: Add tests for DSA and ECDSA. This is non-trivial because the default // implementations of these signature algorithms are non-deterministic which means output // files always differ from golden files. } @Test public void testVerityEnabled_Golden() throws Exception { List rsaSignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); assertGolden( "original.apk", "golden-rsa-verity-out.apk", new ApkSigner.Builder(rsaSignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setVerityEnabled(true) .setAlignmentPreserved(true)); } @Test public void testAlignFileSize_Golden() throws Exception { List rsaSignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); String goldenOutput = "golden-file-size-aligned.apk"; assertGolden( "original.apk", goldenOutput, new ApkSigner.Builder(rsaSignerConfig) .setAlignFileSize(true) .setAlignmentPreserved(true)); assertTrue(Resources.toByteArray(getClass(), goldenOutput).length % 4096 == 0); } @Test public void testRsaSignedVerifies() throws Exception { List signers = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); String in = "original.apk"; // Sign so that the APK is guaranteed to verify on API Level 1+ File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1)); assertVerified(verifyForMinSdkVersion(out, 1)); // Sign so that the APK is guaranteed to verify on API Level 18+ out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18)); assertVerified(verifyForMinSdkVersion(out, 18)); // Does not verify on API Level 17 because RSA with SHA-256 not supported assertVerificationFailure( verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); } @Test public void testDsaSignedVerifies() throws Exception { List signers = Collections.singletonList(getDefaultSignerConfigFromResources("dsa-1024")); String in = "original.apk"; // Sign so that the APK is guaranteed to verify on API Level 1+ File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1)); assertVerified(verifyForMinSdkVersion(out, 1)); // Sign so that the APK is guaranteed to verify on API Level 21+ out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(21)); assertVerified(verifyForMinSdkVersion(out, 21)); // Does not verify on API Level 20 because DSA with SHA-256 not supported assertVerificationFailure( verifyForMinSdkVersion(out, 20), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); } @Test public void testDeterministicDsaSignedVerifies() throws Exception { Security.addProvider(new BouncyCastleProvider()); try { // TODO(b/319494004) see if external/bouncycastle can support this algorithm assumeSHA1withDetDSAIsSupported(); List signers = Collections.singletonList( getDeterministicDsaSignerConfigFromResources("dsa-2048")); String in = "original.apk"; // Sign so that the APK is guaranteed to verify on API Level 1+ File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1)); assertVerified(verifyForMinSdkVersion(out, 1)); // Sign so that the APK is guaranteed to verify on API Level 21+ out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(21)); assertVerified(verifyForMinSdkVersion(out, 21)); // Does not verify on API Level 20 because DSA with SHA-256 not supported assertVerificationFailure( verifyForMinSdkVersion(out, 20), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); } finally { Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); } } @Test public void testDeterministicDsaSigningIsDeterministic() throws Exception { Security.addProvider(new BouncyCastleProvider()); try { // TODO(b/319494004) see if external/bouncycastle can support this algorithm assumeSHA1withDetDSAIsSupported(); List signers = Collections.singletonList( getDeterministicDsaSignerConfigFromResources("dsa-2048")); String in = "original.apk"; ApkSigner.Builder apkSignerBuilder = new ApkSigner.Builder(signers).setMinSdkVersion(1); File first = sign(in, apkSignerBuilder); File second = sign(in, apkSignerBuilder); assertFileContentsEqual(first, second); } finally { Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); } } private void assumeSHA1withDetDSAIsSupported() { try { Signature.getInstance("SHA1withDetDSA"); } catch (NoSuchAlgorithmException e) { assumeNoException( "We should be running with a provider that supports SHA1withDetDSA", e); } } @Test public void testEcSignedVerifies() throws Exception { List signers = Collections.singletonList( getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); String in = "original.apk"; // NOTE: EC APK signatures are not supported prior to API Level 18 // Sign so that the APK is guaranteed to verify on API Level 18+ File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18)); assertVerified(verifyForMinSdkVersion(out, 18)); // Does not verify on API Level 17 because EC not supported assertVerificationFailure( verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG); } @Test public void testV1SigningRejectsInvalidZipEntryNames() throws Exception { // ZIP/JAR entry name cannot contain CR, LF, or NUL characters when the APK is being // JAR-signed. List signers = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); assertThrows( ApkFormatException.class, () -> sign( "v1-only-with-cr-in-entry-name.apk", new ApkSigner.Builder(signers).setV1SigningEnabled(true))); assertThrows( ApkFormatException.class, () -> sign( "v1-only-with-lf-in-entry-name.apk", new ApkSigner.Builder(signers).setV1SigningEnabled(true))); assertThrows( ApkFormatException.class, () -> sign( "v1-only-with-nul-in-entry-name.apk", new ApkSigner.Builder(signers).setV1SigningEnabled(true))); } @Test public void testV1SigningAllowedWithMaximumNumberOfSigners() throws Exception { // The APK Signature Scheme v1 supports a maximum of 10 signers; this test verifies a // signing config with the maximum number of signers is allowed to sign the APK. List signers = List.of( getDefaultSignerConfigFromResources("dsa-1024"), getDefaultSignerConfigFromResources("dsa-2048"), getDefaultSignerConfigFromResources("dsa-3072"), getDefaultSignerConfigFromResources("rsa-1024"), getDefaultSignerConfigFromResources("rsa-2048"), getDefaultSignerConfigFromResources("rsa-3072"), getDefaultSignerConfigFromResources("rsa-4096"), getDefaultSignerConfigFromResources("rsa-8192"), getDefaultSignerConfigFromResources("ec-p256"), getDefaultSignerConfigFromResources("ec-p384") ); sign("original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false)); } @Test public void testV1SigningRejectedWithMoreThanMaximumNumberOfSigners() throws Exception { // This test ensures a v1 signing config with more than the maximum supported number // of signers will fail to sign. List signers = List.of( getDefaultSignerConfigFromResources("dsa-1024"), getDefaultSignerConfigFromResources("dsa-2048"), getDefaultSignerConfigFromResources("dsa-3072"), getDefaultSignerConfigFromResources("rsa-1024"), getDefaultSignerConfigFromResources("rsa-2048"), getDefaultSignerConfigFromResources("rsa-3072"), getDefaultSignerConfigFromResources("rsa-4096"), getDefaultSignerConfigFromResources("rsa-8192"), getDefaultSignerConfigFromResources("ec-p256"), getDefaultSignerConfigFromResources("ec-p384"), getDefaultSignerConfigFromResources("ec-p521") ); assertThrows(IllegalArgumentException.class, () -> sign("original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false))); } @Test public void testV2SigningAllowedWithMaximumNumberOfSigners() throws Exception { // The APK Signature Scheme v2 supports a maximum of 10 signers; this test verifies a // signing config with the maximum number of signers is allowed to sign the APK. List signers = List.of( getDefaultSignerConfigFromResources("dsa-1024"), getDefaultSignerConfigFromResources("dsa-2048"), getDefaultSignerConfigFromResources("dsa-3072"), getDefaultSignerConfigFromResources("rsa-1024"), getDefaultSignerConfigFromResources("rsa-2048"), getDefaultSignerConfigFromResources("rsa-3072"), getDefaultSignerConfigFromResources("rsa-4096"), getDefaultSignerConfigFromResources("rsa-8192"), getDefaultSignerConfigFromResources("ec-p256"), getDefaultSignerConfigFromResources("ec-p384") ); sign("original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setV4SigningEnabled(false)); } @Test public void testV2SigningRejectedWithMoreThanMaximumNumberOfSigners() throws Exception { // This test ensures a v2 signing config with more than the maximum supported number // of signers will fail to sign. List signers = List.of( getDefaultSignerConfigFromResources("dsa-1024"), getDefaultSignerConfigFromResources("dsa-2048"), getDefaultSignerConfigFromResources("dsa-3072"), getDefaultSignerConfigFromResources("rsa-1024"), getDefaultSignerConfigFromResources("rsa-2048"), getDefaultSignerConfigFromResources("rsa-3072"), getDefaultSignerConfigFromResources("rsa-4096"), getDefaultSignerConfigFromResources("rsa-8192"), getDefaultSignerConfigFromResources("ec-p256"), getDefaultSignerConfigFromResources("ec-p384"), getDefaultSignerConfigFromResources("ec-p521") ); assertThrows(IllegalArgumentException.class, () -> sign("original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setV4SigningEnabled(false))); } @Test public void testWeirdZipCompressionMethod() throws Exception { // Any ZIP compression method other than STORED is treated as DEFLATED by Android. // This APK declares compression method 21 (neither STORED nor DEFLATED) for CERT.RSA entry, // but the entry is actually Deflate-compressed. List signers = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); sign("weird-compression-method.apk", new ApkSigner.Builder(signers)); } @Test public void testZipCompressionMethodMismatchBetweenLfhAndCd() throws Exception { // Android Package Manager ignores compressionMethod field in Local File Header and always // uses the compressionMethod from Central Directory instead. // In this APK, compression method of CERT.RSA is declared as STORED in Local File Header // and as DEFLATED in Central Directory. The entry is actually Deflate-compressed. List signers = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); sign("mismatched-compression-method.apk", new ApkSigner.Builder(signers)); } @Test public void testDebuggableApk() throws Exception { // APK which uses a boolean value "true" in its android:debuggable final String debuggableBooleanApk = "debuggable-boolean.apk"; List signers = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); // Signing debuggable APKs is permitted by default sign(debuggableBooleanApk, new ApkSigner.Builder(signers)); // Signing debuggable APK succeeds when explicitly requested sign(debuggableBooleanApk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true)); // Signing debuggable APK fails when requested assertThrows( SignatureException.class, () -> sign( debuggableBooleanApk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(false))); // APK which uses a reference value, pointing to boolean "false", in its android:debuggable final String debuggableResourceApk = "debuggable-resource.apk"; // When we permit signing regardless of whether the APK is debuggable, the value of // android:debuggable should be ignored. sign(debuggableResourceApk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true)); // When we disallow signing debuggable APKs, APKs with android:debuggable being a resource // reference must be rejected, because there's no easy way to establish whether the resolved // boolean value is the same for all resource configurations. assertThrows( SignatureException.class, () -> sign( debuggableResourceApk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(false))); } @Test public void testV3SigningWithSignersNotInLineageFails() throws Exception { // APKs signed with the v3 scheme after a key rotation must specify the lineage containing // the proof of rotation. This test verifies that the signing will fail if the provided // signers are not in the specified lineage. List signers = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), "rsa-1024-lineage-2-signers"); assertThrows( IllegalStateException.class, () -> sign( "original.apk", new ApkSigner.Builder(signers) .setSigningCertificateLineage(lineage))); } @Test public void testSigningWithLineageRequiresOldestSignerForV1AndV2() throws Exception { // After a key rotation the oldest signer must still be specified for v1 and v2 signing. // The lineage contains the proof of rotation and will be used to determine the oldest // signer. ApkSigner.SignerConfig firstSigner = getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig secondSigner = getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig thirdSigner = getDefaultSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-3-signers"); // Verifies that the v1 signing scheme requires the oldest signer after a key rotation. List signers = Collections.singletonList(thirdSigner); try { sign( "original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setSigningCertificateLineage(lineage)); fail( "The signing should have failed due to the oldest signer in the lineage not" + " being provided for v1 signing"); } catch (IllegalArgumentException expected) { } // Verifies that the v2 signing scheme requires the oldest signer after a key rotation. try { sign( "original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setSigningCertificateLineage(lineage)); fail( "The signing should have failed due to the oldest signer in the lineage not" + " being provided for v2 signing"); } catch (IllegalArgumentException expected) { } // Verifies that when only the v3 signing scheme is requested the oldest signer does not // need to be provided. sign( "original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); // Verifies that an intermediate signer in the lineage is not sufficient to satisfy the // requirement that the oldest signer be provided for v1 and v2 signing. signers = Arrays.asList(secondSigner, thirdSigner); try { sign( "original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setSigningCertificateLineage(lineage)); fail( "The signing should have failed due to the oldest signer in the lineage not" + " being provided for v1/v2 signing"); } catch (IllegalArgumentException expected) { } // Verifies that the signing is successful when the oldest and newest signers are provided // and that intermediate signers are not required. signers = Arrays.asList(firstSigner, thirdSigner); sign( "original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setSigningCertificateLineage(lineage)); } @Test public void testV3SigningWithMultipleSignersAndNoLineageFails() throws Exception { // The v3 signing scheme does not support multiple signers; if multiple signers are provided // it is assumed these signers are part of the lineage. This test verifies v3 signing // fails if multiple signers are provided without a lineage. ApkSigner.SignerConfig firstSigner = getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig secondSigner = getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); List signers = Arrays.asList(firstSigner, secondSigner); assertThrows( IllegalStateException.class, () -> sign( "original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true))); } @Test public void testLineageCanBeReadAfterV3Signing() throws Exception { SigningCertificateLineage.SignerConfig firstSigner = Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage.SignerConfig secondSigner = Resources.toLineageSignerConfig(getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage lineage = new SigningCertificateLineage.Builder(firstSigner, secondSigner).build(); List signerConfigs = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); File out = sign( "original.apk", new ApkSigner.Builder(signerConfigs) .setV3SigningEnabled(true) .setSigningCertificateLineage(lineage)); SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkFile(out); assertTrue( "The first signer was not in the lineage from the signed APK", lineageFromApk.isSignerInLineage((firstSigner))); assertTrue( "The second signer was not in the lineage from the signed APK", lineageFromApk.isSignerInLineage((secondSigner))); } @Test public void testPublicKeyHasPositiveModulusAfterSigning() throws Exception { // The V2 and V3 signature schemes include the public key from the certificate in the // signing block. If a certificate with an RSAPublicKey is improperly encoded with a // negative modulus this was previously written to the signing block as is and failed on // device verification since on device the public key in the certificate was reencoded with // the correct encoding for the modulus. This test uses an improperly encoded certificate to // sign an APK and verifies that the public key in the signing block is corrected with a // positive modulus to allow on device installs / updates. List signersList = Collections.singletonList( getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS)); File signedApk = sign( "original.apk", new ApkSigner.Builder(signersList) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true)); RSAPublicKey v2PublicKey = getRSAPublicKeyFromSigningBlock( signedApk, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2); assertTrue( "The modulus in the public key in the V2 signing block must not be negative", v2PublicKey.modulus.compareTo(BigInteger.ZERO) > 0); RSAPublicKey v3PublicKey = getRSAPublicKeyFromSigningBlock( signedApk, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); assertTrue( "The modulus in the public key in the V3 signing block must not be negative", v3PublicKey.modulus.compareTo(BigInteger.ZERO) > 0); } @Test public void testV4State_disableV2V3EnableV4_fails() throws Exception { ApkSigner.SignerConfig signer = getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); assertThrows( IllegalStateException.class, () -> sign( "original.apk", new ApkSigner.Builder(Collections.singletonList(signer)) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(true))); } @Test public void testSignApk_stampFile() throws Exception { List signers = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); ApkSigner.SignerConfig sourceStampSigner = getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(sourceStampSigner.getCertificates().get(0).getEncoded()); byte[] expectedStampCertificateDigest = messageDigest.digest(); File signedApkFile = sign( "original.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setSourceStampSignerConfig(sourceStampSigner)); try (RandomAccessFile f = new RandomAccessFile(signedApkFile, "r")) { DataSource signedApk = DataSources.asDataSource(f, 0, f.length()); ApkUtils.ZipSections zipSections = findZipSections(signedApk); List cdRecords = V1SchemeVerifier.parseZipCentralDirectory(signedApk, zipSections); CentralDirectoryRecord stampCdRecord = null; for (CentralDirectoryRecord cdRecord : cdRecords) { if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) { stampCdRecord = cdRecord; break; } } assertNotNull(stampCdRecord); byte[] actualStampCertificateDigest = LocalFileRecord.getUncompressedData( signedApk, stampCdRecord, zipSections.getZipCentralDirectoryOffset()); assertArrayEquals(expectedStampCertificateDigest, actualStampCertificateDigest); } } @Test public void testSignApk_existingStampFile_sameSourceStamp() throws Exception { List signers = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); ApkSigner.SignerConfig sourceStampSigner = getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME); File signedApk = sign( "original-with-stamp-file.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setSourceStampSignerConfig(sourceStampSigner)); ApkVerifier.Result sourceStampVerificationResult = verify(signedApk, /* minSdkVersionOverride= */ null); assertSourceStampVerified(signedApk, sourceStampVerificationResult); } @Test public void testSignApk_existingStampFile_differentSourceStamp() throws Exception { List signers = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); ApkSigner.SignerConfig sourceStampSigner = getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); Exception exception = assertThrows( ApkFormatException.class, () -> sign( "original-with-stamp-file.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setSourceStampSignerConfig(sourceStampSigner))); assertEquals( String.format( "Cannot generate SourceStamp. APK contains an existing entry with the" + " name: %s, and it is different than the provided source stamp" + " certificate", SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME), exception.getMessage()); } @Test public void testSignApk_existingStampFile_differentSourceStamp_forceOverwrite() throws Exception { List signers = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); ApkSigner.SignerConfig sourceStampSigner = getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); File signedApk = sign( "original-with-stamp-file.apk", new ApkSigner.Builder(signers) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setForceSourceStampOverwrite(true) .setSourceStampSignerConfig(sourceStampSigner)); ApkVerifier.Result sourceStampVerificationResult = verify(signedApk, /* minSdkVersionOverride= */ null); assertSourceStampVerified(signedApk, sourceStampVerificationResult); } @Test public void testSignApk_stampBlock_noStampGenerated() throws Exception { List signersList = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); File signedApkFile = sign( "original.apk", new ApkSigner.Builder(signersList) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true)); try (RandomAccessFile f = new RandomAccessFile(signedApkFile, "r")) { DataSource signedApk = DataSources.asDataSource(f, 0, f.length()); ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(signedApk); ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(ApkSigningBlockUtils.VERSION_SOURCE_STAMP); assertThrows( ApkSigningBlockUtils.SignatureNotFoundException.class, () -> ApkSigningBlockUtils.findSignature( signedApk, zipSections, ApkSigningBlockUtils.VERSION_SOURCE_STAMP, result)); } } @Test public void testSignApk_stampBlock_whenV1SignaturePresent() throws Exception { List signersList = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); ApkSigner.SignerConfig sourceStampSigner = getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); File signedApk = sign( "original.apk", new ApkSigner.Builder(signersList) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false) .setSourceStampSignerConfig(sourceStampSigner)); ApkVerifier.Result sourceStampVerificationResult = verify(signedApk, /* minSdkVersionOverride= */ null); assertSourceStampVerified(signedApk, sourceStampVerificationResult); } @Test public void testSignApk_stampBlock_whenV2SignaturePresent() throws Exception { List signersList = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); ApkSigner.SignerConfig sourceStampSigner = getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); File signedApk = sign( "original.apk", new ApkSigner.Builder(signersList) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setSourceStampSignerConfig(sourceStampSigner)); ApkVerifier.Result sourceStampVerificationResult = verifyForMinSdkVersion(signedApk, /* minSdkVersion= */ AndroidSdkVersion.N); assertSourceStampVerified(signedApk, sourceStampVerificationResult); } @Test public void testSignApk_stampBlock_whenV3SignaturePresent() throws Exception { List signersList = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); ApkSigner.SignerConfig sourceStampSigner = getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); File signedApk = sign( "original.apk", new ApkSigner.Builder(signersList) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setSourceStampSignerConfig(sourceStampSigner)); ApkVerifier.Result sourceStampVerificationResult = verifyForMinSdkVersion(signedApk, /* minSdkVersion= */ AndroidSdkVersion.N); assertSourceStampVerified(signedApk, sourceStampVerificationResult); } @Test public void testSignApk_stampBlock_withStampLineage() throws Exception { List signersList = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); ApkSigner.SignerConfig sourceStampSigner = getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage sourceStampLineage = Resources.toSigningCertificateLineage( getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApk = sign( "original.apk", new ApkSigner.Builder(signersList) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setSourceStampSignerConfig(sourceStampSigner) .setSourceStampSigningCertificateLineage(sourceStampLineage)); ApkVerifier.Result sourceStampVerificationResult = verify(signedApk, /* minSdkVersion= */ null); assertSourceStampVerified(signedApk, sourceStampVerificationResult); } @Test public void testSignApk_Pinlist() throws Exception { List rsa2048SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); assertGolden( "pinsapp-unsigned.apk", "golden-pinsapp-signed.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setVerityEnabled(true) .setAlignmentPreserved(true)); assertTrue("pinlist.meta file must be in the signed APK.", resourceZipFileContains("golden-pinsapp-signed.apk", "pinlist.meta")); } @Test public void testOtherSignersSignaturesPreserved_extraSigBlock_signatureAppended() throws Exception { // The DefaultApkSignerEngine contains support to append a signature to an existing // signing block; any existing signature blocks within the APK signing block should be // left intact except for the original verity padding block (since this is regenerated) and // the source stamp. This test verifies that an extra signature block is still in // the APK signing block after appending a V2 signature. List ecP256SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); File signedApk = sign("v2-rsa-2048-with-extra-sig-block.apk", new ApkSigner.Builder(ecP256SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setV4SigningEnabled(false) .setOtherSignersSignaturesPreserved(true)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, EC_P256_SIGNER_RESOURCE_NAME); assertSigningBlockContains(signedApk, Pair.of(EXTRA_BLOCK_VALUE, EXTRA_BLOCK_ID)); } @Test public void testOtherSignersSignaturesPreserved_v1Only_signatureAppended() throws Exception { // This test verifies appending an additional V1 signature to an existing V1 signer behaves // similar to jarsigner where the APK is then verified as signed by both signers. List ecP256SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); File signedApk = sign("v1-only-with-rsa-2048.apk", new ApkSigner.Builder(ecP256SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(false) .setV3SigningEnabled(false) .setV4SigningEnabled(false) .setOtherSignersSignaturesPreserved(true)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, EC_P256_SIGNER_RESOURCE_NAME); } @Test public void testOtherSignersSignaturesPreserved_v3OnlyDifferentSigner_throwsException() throws Exception { // The V3 Signature Scheme only supports a single signer; if an attempt is made to append // a different signer to a V3 signature then an exception should be thrown. // The APK used for this test is signed with the ec-p256 signer so use the rsa-2048 to // attempt to append a different signature. List rsa2048SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); assertThrows(IllegalStateException.class, () -> sign("v3-only-with-stamp.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setOtherSignersSignaturesPreserved(true)) ); } @Test public void testOtherSignersSignaturesPreserved_v2OnlyAppendV2V3SameSigner_signatureAppended() throws Exception { // A V2 and V3 signature can be appended to an existing V2 signature if the same signer is // used to resign the APK; this could be used in a case where an APK was previously signed // with just the V2 signature scheme along with additional non-APK signing scheme signature // blocks and the signer wanted to preserve those existing blocks. List rsa2048SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); File signedApk = sign("v2-rsa-2048-with-extra-sig-block.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setOtherSignersSignaturesPreserved(true)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME); assertSigningBlockContains(signedApk, Pair.of(EXTRA_BLOCK_VALUE, EXTRA_BLOCK_ID)); } @Test public void testOtherSignersSignaturesPreserved_v2OnlyAppendV3SameSigner_throwsException() throws Exception { // A V3 only signature cannot be appended to an existing V2 signature, even when using the // same signer, since the V2 signature would then not contain the stripping protection for // the V3 signature. If the same signer is being used then the signer should be configured // to resign using the V2 signature scheme as well as the V3 signature scheme. List rsa2048SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); assertThrows(IllegalStateException.class, () -> sign("v2-rsa-2048-with-extra-sig-block.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setOtherSignersSignaturesPreserved(true))); } @Test public void testOtherSignersSignaturesPreserved_v1v2IndividuallySign_signaturesAppended() throws Exception { // One of the primary requirements for appending signatures is when an APK has already // released with two signers; with the minimum signature scheme v2 requirement for target // SDK version 30+ each signer must be able to append their signature to the existing // signature block. This test verifies an APK with appended signatures verifies as expected // after a series of appending V1 and V2 signatures. List rsa2048SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); List ecP256SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); // When two parties are signing an APK the first must sign with both V1 and V2; this will // write the stripping-protection attribute to the V1 signature. File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setV4SigningEnabled(false)); // The second party can then append their signature with both the V1 and V2 signature; this // will invalidate the V2 signature of the initial signer since the APK itself will be // modified with this signers V1 / jar signature. signedApk = sign(signedApk, new ApkSigner.Builder(ecP256SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setV4SigningEnabled(false) .setOtherSignersSignaturesPreserved(true)); // The first party will then need to resign with just the V2 signature after its previous // signature was invalidated by the V1 signature of the second signer; however since this // signature is appended its previous V2 signature should be removed from the signature // block and replaced with this new signature while preserving the V2 signature of the // other signer. signedApk = sign(signedApk, new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(false) .setV2SigningEnabled(true) .setV3SigningEnabled(false) .setV4SigningEnabled(false) .setOtherSignersSignaturesPreserved(true)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, EC_P256_SIGNER_RESOURCE_NAME); } @Test public void testSetMinSdkVersionForRotation_lessThanT_noV31Block() throws Exception { // The V3.1 signing block is intended to allow APK signing key rotation to target T+, but // a minimum SDK version can be explicitly set for rotation; if it is less than T than // the rotated key will be included in the V3.0 block. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApkMinRotationP = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); ApkVerifier.Result resultMinRotationP = verify(signedApkMinRotationP, null); // The V3.1 signature scheme was introduced in T; specifying an older SDK version as the // minimum for rotation should cause the APK to still be signed with rotation in the V3.0 // signing block. File signedApkMinRotationS = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(AndroidSdkVersion.S) .setSigningCertificateLineage(lineage)); ApkVerifier.Result resultMinRotationS = verify(signedApkMinRotationS, null); assertVerified(resultMinRotationP); assertFalse(resultMinRotationP.isVerifiedUsingV31Scheme()); assertEquals(1, resultMinRotationP.getV3SchemeSigners().size()); assertResultContainsSigners(resultMinRotationP, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertVerified(resultMinRotationS); assertFalse(resultMinRotationS.isVerifiedUsingV31Scheme()); // While rotation is targeting S, signer blocks targeting specific SDK versions have not // been tested in previous platform releases; ensure only a single signer block with the // rotated key is in the V3 block. assertEquals(1, resultMinRotationS.getV3SchemeSigners().size()); assertResultContainsSigners(resultMinRotationS, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testSetMinSdkVersionForRotation_TAndLater_v31Block() throws Exception { // When T or later is specified as the minimum SDK version for rotation, then a new V3.1 // signing block should be created with the new rotated key, and the V3.0 signing block // should still be signed with the original key. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApkMinRotationT = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(AndroidSdkVersion.T) .setSigningCertificateLineage(lineage)); ApkVerifier.Result resultMinRotationT = verify(signedApkMinRotationT, null); // The API level for a release after T is not yet defined, so for now treat it as T + 1. File signedApkMinRotationU = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(AndroidSdkVersion.T + 1) .setSigningCertificateLineage(lineage)); ApkVerifier.Result resultMinRotationU = verify(signedApkMinRotationU, null); assertVerified(resultMinRotationT); assertTrue(resultMinRotationT.isVerifiedUsingV31Scheme()); assertResultContainsSigners(resultMinRotationT, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertV31SignerTargetsMinApiLevel(resultMinRotationT, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertVerified(resultMinRotationU); assertTrue(resultMinRotationU.isVerifiedUsingV31Scheme()); assertResultContainsSigners(resultMinRotationU, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertV31SignerTargetsMinApiLevel(resultMinRotationU, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T + 1); } @Test public void testSetMinSdkVersionForRotation_targetTNoOriginalSigner_fails() throws Exception { // Similar to the V1 and V2 signatures schemes, if an app is targeting P or later with // rotation targeting T, the original signer must be provided so that it can be used in the // V3.0 signing block; if it is not provided the signer should throw an Exception. List rsa2048SignerConfigWithLineage = List .of(getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); assertThrows(IllegalArgumentException.class, () -> sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersion(28) .setMinSdkVersionForRotation(AndroidSdkVersion.T) .setSigningCertificateLineage(lineage))); } @Test public void testSetMinSdkVersionForRotation_targetTAndApkMinSdkT_onlySignsV3Block() throws Exception { // A V3.1 signing block should only exist alongside a V3.0 signing block; if an APK's // min SDK version is greater than or equal to the SDK version for rotation then the // original signer should not be required, and the rotated signing key should be in // a V3.0 signing block. List rsa2048SignerConfigWithLineage = List .of(getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersion(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT) .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT) .setSigningCertificateLineage(lineage)); ApkVerifier.Result result = verifyForMinSdkVersion(signedApk, V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT); assertVerified(result); assertFalse(result.isVerifiedUsingV31Scheme()); assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testSetMinSdkVersionForRotation_targetTWithSourceStamp_noWarnings() throws Exception { // Source stamp verification will report a warning if a stamp signature is not found for any // of the APK Signature Schemes used to sign the APK. This test verifies an APK signed with // a rotated key in the v3.1 block and a source stamp successfully verifies, including the // source stamp, without any warnings. ApkSigner.SignerConfig rsa2048OriginalSignerConfig = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); List rsa2048SignerConfigWithLineage = Arrays.asList( rsa2048OriginalSignerConfig, getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(AndroidSdkVersion.T) .setSigningCertificateLineage(lineage) .setSourceStampSignerConfig(rsa2048OriginalSignerConfig)); ApkVerifier.Result result = verify(signedApk, null); assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertSourceStampVerified(signedApk, result); } @Test public void testSetRotationTargetsDevRelease_target34_v30SignerTargetsAtLeast34() throws Exception { // During development of a new platform release the new platform will use the SDK version // of the previously released platform, so in order to test rotation on a new platform // release it must target the SDK version of the previous platform. However an APK signed // with the v3.1 signature scheme and targeting rotation on the previous platform release X // would still use rotation if that APK were installed on a device running release version // X. To support targeting rotation on the main branch, the v3.1 signature scheme supports // a rotation-targets-dev-release attribute; this allows the APK to use the v3.1 signer // block on a development platform with SDK version X while a release platform X will // skip this signer block when it sees this additional attribute. To ensure that the APK // will still target the released platform X, the v3.0 signer must have a maxSdkVersion // of at least X for the signer. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); int rotationMinSdkVersion = 10000; File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(rotationMinSdkVersion) .setSigningCertificateLineage(lineage) .setRotationTargetsDevRelease(true)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertTrue(result.isVerifiedUsingV31Scheme()); assertTrue(result.getV31SchemeSigners().get(0).getRotationTargetsDevRelease()); assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertTrue(result.getV3SchemeSigners().get(0).getMaxSdkVersion() >= rotationMinSdkVersion); } @Test public void testV3_rotationMinSdkVersionLessThanTV3Only_origSignerNotRequired() throws Exception { // The v3.1 signature scheme allows a rotation-min-sdk-version be specified to target T+ // for rotation; however if this value is less than the expected SDK version of T, then // apksig should just use the rotated signing key in the v3.0 block. An APK that targets // P+ that wants to use rotation in the v3.0 signing block should only need to provide // the rotated signing key and lineage; this test ensures this behavior when the // rotation-min-sdk-version is set to a value > P and < T. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApkRotationOnQ = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersion(AndroidSdkVersion.P) .setMinSdkVersionForRotation(AndroidSdkVersion.Q) .setSigningCertificateLineage(lineage)); ApkVerifier.Result resultRotationOnQ = verify(signedApkRotationOnQ, AndroidSdkVersion.P); assertVerified(resultRotationOnQ); assertEquals(1, resultRotationOnQ.getV3SchemeSigners().size()); assertFalse(resultRotationOnQ.isVerifiedUsingV31Scheme()); assertResultContainsSigners(resultRotationOnQ, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_rotationMinSdkVersionEqualsMinSdkVersion_v3SignerPresent() throws Exception { // The SDK version for Sv2 (32) is used as the minSdkVersion for the V3.1 signature // scheme to allow rotation to target the T development platform; this will be updated // to the real SDK version of T once its SDK is finalized. This test verifies if a // package has Sv2 as its minSdkVersion, the signing can complete as expected with the // v3 block signed by the original signer and targeting just Sv2, and the v3.1 block // signed by the rotated signer and targeting the dev release of Sv2 and all later releases. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApk = sign("original-minSdk32.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT) .setSigningCertificateLineage(lineage)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); } @Test public void testV31_rotationMinSdkVersionTWithoutLineage_v30VerificationSucceeds() throws Exception { // apksig allows setting a rotation-min-sdk-version without providing a rotated signing // key / lineage; however in the absence of rotation, the rotation-min-sdk-version should // be a no-op, and the stripping protection attribute should not be written to the v3.0 // signer. List rsa2048SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertFalse(result.isVerifiedUsingV31Scheme()); assertTrue(result.isVerifiedUsingV3Scheme()); } @Test public void testV31_rotationMinSdkVersionDefault_rotationTargetsT() throws Exception { // The v3.1 signature scheme was introduced in T to allow developers to target T+ for // rotation due to known issues with rotation on previous platform releases. This test // verifies an APK signed with a rotated signing key defaults to the original signing // key used in the v3 signing block for pre-T devices, and the rotated signing key used // in the v3.1 signing block for T+ devices. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setSigningCertificateLineage(lineage)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); } @Test public void testV31_rotationMinSdkVersionP_rotationTargetsP() throws Exception { // While the V3.1 signature scheme will target T by default, a package that has // previously rotated can provide a rotation-min-sdk-version less than T to continue // using the rotated signing key in the v3.0 block. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertTrue(result.isVerifiedUsingV3Scheme()); assertFalse(result.isVerifiedUsingV31Scheme()); } @Test public void testV31_rotationMinSdkVersionDevRelease_rotationTargetsDevRelease() throws Exception { // The V3.1 signature scheme can be used to target rotation for a development release; // a development release uses the SDK version of the previously finalized release until // its own SDK is finalized. This test verifies if the rotation-min-sdk-version is set to // the current development release, then the resulting APK should target the previously // finalized release and the rotation-targets-dev-release attribute should be set for // the signer. // If the development release is less than the first release that supports V3.1, then // a development release is not currently supported. assumeTrue(V3SchemeConstants.DEV_RELEASE >= V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT); List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setMinSdkVersionForRotation(V3SchemeConstants.DEV_RELEASE) .setSigningCertificateLineage(lineage)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(V3SchemeConstants.PROD_RELEASE, result.getV31SchemeSigners().get(0).getMinSdkVersion()); assertTrue(result.getV31SchemeSigners().get(0).getRotationTargetsDevRelease()); // The maxSdkVersion for the V3 signer should overlap with the minSdkVersion for the V3.1 // signer. assertEquals(V3SchemeConstants.PROD_RELEASE, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); } @Test public void testV31_oneTargetedSigningConfigT_targetsT() throws Exception { // The V3.1 signature scheme supports targeting a signing config for devices running // T+. This test verifies a single signing config targeting T+ is written to the v3.1 // block, and the original signer is used for pre-T devices in the v3.0 block. This // is functionally equivalent to calling setMinSdkVersionForRotation(AndroidSdkVersion.T). SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineage); List signerConfigs = Arrays.asList(originalSigner, targetedSigner); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertEquals(1, result.getV31SchemeSigners().size()); assertLineageContainsExpectedSigners( result.getV31SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_oneTargetedSigningConfig10000_targets10000() throws Exception { // When a signing config targets a later release, the V3.0 signature should be used for all // platform releases prior to the targeted release. This test verifies a signing config // targeting SDK 10000 has a V3.0 block that targets through SDK 9999. SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, 10000, lineage); List signerConfigs = Arrays.asList(originalSigner, targetedSigner); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(9999, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 10000); assertEquals(1, result.getV31SchemeSigners().size()); assertLineageContainsExpectedSigners( result.getV31SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void test31_twoTargetedSigningConfigs_twoV31Signers() throws Exception { // This test verifies multiple signing configs targeting T+ can be added to the V3.1 // signing block. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetU = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU); List signerConfigs = Arrays.asList(originalSigner, signerTargetT, signerTargetU); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); assertEquals(2, result.getV31SchemeSigners().size()); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.U); assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.T).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.U).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void test31_threeTargetedSigningConfigs_threeV31Signers() throws Exception { // This test verifies multiple signing configs targeting T+ with modified capabilities // can be added to the V3.1 signing block. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetU = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTarget10000 = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_1_NO_CAPS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU); ApkSigner.SignerConfig signerTarget10000 = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, 10000, lineageTarget10000); List signerConfigs = Arrays.asList(originalSigner, signerTargetT, signerTargetU, signerTarget10000); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); assertEquals(3, result.getV31SchemeSigners().size()); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.U); assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 10000); assertLineageContainsExpectedSignersWithCapabilities(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.T).getSigningCertificateLineage(), new String[]{FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME}, new SignerCapabilities[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); assertLineageContainsExpectedSignersWithCapabilities(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.U).getSigningCertificateLineage(), new String[]{FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME}, new SignerCapabilities[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); assertLineageContainsExpectedSignersWithCapabilities(getV31SignerTargetingSdkVersion(result, 10000).getSigningCertificateLineage(), new String[]{FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME}, new SignerCapabilities[]{NO_CAPABILITIES, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); } @Test public void testV31_oneTargetedSigningConfigP_targetsP() throws Exception { // A single signing config can be specified targeting < T; this test verifies a single // config targeting P is written to the V3.0 signing block SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, lineage); List signerConfigs = Arrays.asList(originalSigner, targetedSigner); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertFalse(result.isVerifiedUsingV31Scheme()); assertEquals(1, result.getV3SchemeSigners().size()); assertEquals(AndroidSdkVersion.P, result.getV3SchemeSigners().get(0).getMinSdkVersion()); assertLineageContainsExpectedSigners( result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_oneTargetedSigningConfigS_targetsP() throws Exception { // A single signing config can be specified targeting < T, but the V3.0 signature scheme // does not have verified SDK targeting. If a signing config is specified to target < T and // > P, the targeted SDK version should be set to P to ensure it applies on all platform // releases that support the V3.0 signature scheme. SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.S, lineage); List signerConfigs = Arrays.asList(originalSigner, targetedSigner); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertFalse(result.isVerifiedUsingV31Scheme()); assertEquals(1, result.getV3SchemeSigners().size()); assertEquals(AndroidSdkVersion.P, result.getV3SchemeSigners().get(0).getMinSdkVersion()); assertLineageContainsExpectedSigners( result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_twoTargetedSigningConfigsTargetT_throwsException() throws Exception { // The V3.1 signature scheme does not support multiple targeted signers targeting the same // SDK version; this test ensures an Exception is thrown if the caller specifies multiple // signers targeting the same release. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage secondLineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); ApkSigner.SignerConfig secondSignerTargetT = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, secondLineageTargetT); List signerConfigs = Arrays.asList(originalSigner, signerTargetT, secondSignerTargetT); assertThrows(IllegalStateException.class, () -> sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false))); } @Test public void testV31_oneTargetedSignerUAndDefaultRotationMinSdkVersion_multipleV31Signers() throws Exception { // SDK targeted signing configs can be specified alongside the rotation-min-sdk-version // for the initial rotation. This test verifies when the initial rotation is specified with // the default value for rotation-min-sdk-version and a separate signing config targeting U, // the two signing configs are written as separate V3.1 signatures. SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetU = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU); List signerConfigs = Arrays.asList(originalSigner, rotatedSigner, signerTargetU); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setSigningCertificateLineage(lineage)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); assertEquals(2, result.getV31SchemeSigners().size()); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.U); assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.T).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.U).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_oneTargetedSignerSAndRotationMinSdkVersionP_throwsException() throws Exception { // Since the v3.0 does not have verified targeted signing configs, any targeted SDK < T // will target P. If a signing config targets < T and the rotation-min-sdk-version targets // < T, then an exception should be thrown to prevent both signers from targeting P. SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetS = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetS = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.S, lineageTargetS); List signerConfigs = Arrays.asList(originalSigner, rotatedSigner, signerTargetS); assertThrows(IllegalStateException.class, () -> sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setSigningCertificateLineage(lineage) .setMinSdkVersionForRotation(AndroidSdkVersion.P))); } @Test public void testV31_twoTargetedSignerPAndS_throwsException() throws Exception { // Since the v3.0 does not have verified targeted signing configs, any targeted SDK < T // will target P. If two signing configs target < T, then an exception should be thrown to // prevent both signers from targeting P. SigningCertificateLineage lineageTargetP = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetS = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetP = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, lineageTargetP); ApkSigner.SignerConfig signerTargetS = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.S, lineageTargetS); List signerConfigs = Arrays.asList(originalSigner, signerTargetP, signerTargetS); assertThrows(IllegalStateException.class, () -> sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false))); } @Test public void testV31_oneTargetedSignerTAndRotationMinSdkVersionP_rotationInV3andV31() throws Exception { // An initial rotation could target P with a separate signing config targeting T+; this // test verifies a rotation-min-sdk-version < T and a signing config targeting T results // in the initial rotation being written to the V3 signing block and the targeted signing // config written to the V3.1 block. SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); List signerConfigs = Arrays.asList(originalSigner, rotatedSigner, signerTargetT); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setSigningCertificateLineage(lineage) .setMinSdkVersionForRotation(AndroidSdkVersion.P)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion()); assertEquals(1, result.getV31SchemeSigners().size()); assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertLineageContainsExpectedSigners( result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.T).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_oneTargetedSignerTApkMinSdkT_oneV3Signer() throws Exception { // The V3.1 signature scheme was introduced in SDK version 33; an APK with 33 as its // minSdkVersion can only be installed on devices with v3.1 support. However the V3.1 // signature scheme should only be used if there's a separate signing config in the V3.0 // block. This test verifies a single signing config targeting an APK's minSdkVersion of // 33 is written to the V3.0 block. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); List signerConfigs = Arrays.asList(signerTargetT); File signedApk = sign("original-minSdk33.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertFalse(result.isVerifiedUsingV31Scheme()); assertLineageContainsExpectedSigners( result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_oneTargetedSignerTApkMinSdkSv2_throwsException() throws Exception { // When a signing config targeting T+ is specified for an APK with a minSdkVersion < T, // the original signer (or another config targeting the minSdkVersion), must be specified // to ensure the APK can be installed on all supported platform releases. If a signer is // not provided for the minimum SDK version, then an Exception should be thrown. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); List signerConfigs = Arrays.asList(signerTargetT); assertThrows(IllegalArgumentException.class, () -> sign("original-minSdk32.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false))); } @Test public void testV31_twoTargetedSignersSv2AndTApkMinSdkSv2_v3AndV31Signed() throws Exception { // V3.0 does not support verified SDK targeting, so a signing config targeting SDK > P and // < T will be applied to P in the V3.0 signing block. If an app's minSdkVersion > P, then // the app should still successfully sign and verify with one of the signers targeting the // APK's minSdkVersion. SigningCertificateLineage lineageTargetSv2 = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetSv2 = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.Sv2, lineageTargetSv2); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); List signerConfigs = Arrays.asList(signerTargetSv2, signerTargetT); File signedApk = sign("original-minSdk32.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(1, result.getV31SchemeSigners().size()); assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertLineageContainsExpectedSigners( result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.T).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_twoTargetedSignersTAndUApkMinSdkT_v3AndV31Signed() throws Exception { // A V3.0 block is always required before a V3.1 block can be written to the APK's signing // block. If an APK targets T (the first release with support for V3.1), and has two // targeted signers, the signer targeting T should be written to the V3.0 block and the // signer targeting a later release should be written to the V3.1 block. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetU = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU); List signerConfigs = Arrays.asList(signerTargetT, signerTargetU); File signedApk = sign("original-minSdk33.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(1, result.getV3SchemeSigners().size()); assertEquals(1, result.getV31SchemeSigners().size()); assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.U); assertLineageContainsExpectedSigners( result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.U).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_twoTargetedSignersTAndUWithTruncatedLineage_v3AndV31Signed() throws Exception { // The V3.1 signature scheme allows different lineages to be specified for each targeted // signing config as long as all the lineages can be merged to form a common lineage. A // signing lineage with signers A -> B -> C could be truncated to only signer C in a // targeted signing config. SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineage); // Manually instantiate this signer instance to make use of the Builder's setMinSdkVersion. ApkSigner.SignerConfig signerTargetU = new ApkSigner.SignerConfig.Builder( signerTargetT.getName(), signerTargetT.getKeyConfig(), signerTargetT.getCertificates()) .setMinSdkVersion(AndroidSdkVersion.U) .build(); List signerConfigs = Arrays.asList(signerTargetT, signerTargetU); File signedApk = sign("original-minSdk33.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(1, result.getV3SchemeSigners().size()); assertEquals(1, result.getV31SchemeSigners().size()); assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.U); assertLineageContainsExpectedSigners( result.getV3SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertNull(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.U).getSigningCertificateLineage()); } @Test public void testV31_twoTargetedSignersTAndUWithSignerNotInLineage_throwsException() throws Exception { // While the V3.1 signature scheme allows a targeted signing config to omit a lineage, // this can only be used if a previous targeted signer has specified a lineage that // includes the new signer without a lineage. If an independent signer is specified // that is not in the common lineage, an Exception should be thrown. SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineage); ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, null); List signerConfigs = Arrays.asList(signerTargetT, signerTargetU); assertThrows(IllegalStateException.class, () -> sign("original-minSdk33.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false))); } @Test public void testV31_twoTargetedSignersSeparateLineages_throwsException() throws Exception { // When multiple SDK targeted signers are specified, the lineage for each signer must // be part of a common lineage; if any of the targeted signers has a lineage that diverges // from the common lineage, then an Exception should be thrown. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetU = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( EC_P256_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( EC_P256_2_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU); List signerConfigs = Arrays.asList(originalSigner, signerTargetT, signerTargetU); assertThrows(IllegalStateException.class, () -> sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false))); } @Test public void testV31_targetedSignerTAndRotationMinSdkVersionPSeparateLineages_throwsException() throws Exception { // When one or more SDK targeted signers are specified with the initial rotation using // rotation-min-sdk-version, the lineage for each signer must be part of a common lineage; // if any of the targeted signers has a lineage that diverges from the common lineage, // then an Exception should be thrown. SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( EC_P256_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources( EC_P256_2_SIGNER_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetT); List signerConfigs = Arrays.asList(originalSigner, rotatedSigner, signerTargetT); assertThrows(IllegalStateException.class, () -> sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(false) .setV2SigningEnabled(false) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setSigningCertificateLineage(lineage) .setMinSdkVersionForRotation(AndroidSdkVersion.P))); } @Test public void testV31_targetedSignerWithSignerNotInLineage_throwsException() throws Exception { // When a targeted signer is created with a lineage, the signer must be in the provided // lineage otherwise an Exception should be thrown. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME); assertThrows(IllegalArgumentException.class, () -> getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT)); } @Test public void testV31_targetedSignerTCertNotLastInLineage_truncatesLineage() throws Exception { // Previously when a rotation signing config was provided with a lineage that did not // contain the signer as the last node, the lineage was truncated to the signer's position. // This test verifies a targeted signing config specified with a lineage containing signers // later than the current signer will be truncated to the provided signer. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); List signerConfigs = Arrays.asList(originalSigner, signerTargetT); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(1, result.getV3SchemeSigners().size()); assertEquals(1, result.getV31SchemeSigners().size()); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertLineageContainsExpectedSigners( result.getV31SchemeSigners().get(0).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_targetedSignerTAndUSubLineages_signsWithExpectedLineages() throws Exception { // Since the V3.1 signature scheme supports targeted signing configs with separate lineages // as long as the lineages can be merged into a common lineage, this test verifies two // targeted signing configs with lineages A -> B and B -> C can be used to sign an APK // and that each signer from a verification has the expected lineage. SigningCertificateLineage lineageTargetT = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); SigningCertificateLineage lineageTargetU = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_2_3_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT); ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU); ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); List signerConfigs = Arrays.asList(originalSigner, signerTargetT, signerTargetU); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertTrue(result.isVerifiedUsingV31Scheme()); assertEquals(1, result.getV3SchemeSigners().size()); assertEquals(2, result.getV31SchemeSigners().size()); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T); assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.U); assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.T).getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.U).getSigningCertificateLineage(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(result.getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV31_targetedSignerPNoOriginalSigner_throwsException() throws Exception { // Targeted signing configs can only target Android P and later since this was the initial // release that added support for V3. This test verifies if a signing config with a lineage // targeting P is provided without an original signer, an Exception is thrown to indicate // the original signer is required for the V1 and V2 signature schemes. SigningCertificateLineage lineageTargetP = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); ApkSigner.SignerConfig signerTargetP = getDefaultSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, lineageTargetP); List signerConfigs = Arrays.asList(signerTargetP); assertThrows(IllegalArgumentException.class, () -> sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false))); } @Test public void testV31_targetedSignerPOriginalSigner_signed() throws Exception { // While SDK targeted signing configs are intended to target later platform releases for // rotation, it is possible for a signer to target P with the original signing key. Without // a lineage, the signer will treat this as the original signing key and can use it to sign // the V1 and V2 blocks as well. ApkSigner.SignerConfig signerTargetP = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, null); List signerConfigs = Arrays.asList(signerTargetP); File signedApk = sign("original.apk", new ApkSigner.Builder(signerConfigs) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertVerified(result); assertVerificationWarning(result, null); assertTrue(result.isVerifiedUsingV3Scheme()); assertFalse(result.isVerifiedUsingV31Scheme()); } @Test public void testV4_rotationMinSdkVersionLessThanT_signatureOnlyHasRotatedSigner() throws Exception { // To support SDK version targeting in the v3.1 signature scheme, apksig added a // rotation-min-sdk-version option to allow the caller to specify the level from which // the rotated signer should be used. A value less than T should result in a single // rotated signer in the V3 block (along with the corresponding lineage), and the V4 // signature should use this signer. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.P) .setSigningCertificateLineage(lineage)); ApkVerifier.Result result = verify(signedApk, null); assertResultContainsV4Signers(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV4_rotationMinSdkVersionT_signatureHasOrigAndRotatedKey() throws Exception { // When an APK is signed with a rotated key and the rotation-min-sdk-version X is set to T+, // a V3.1 block will be signed with the rotated signing key targeting X and later, and // a V3.0 block will be signed with the original signing key targeting P - X-1. The // V4 signature should contain both the original signing key and the rotated signing // key; this ensures if an APK is installed on a device running an SDK version less than X, // the V4 signature will be verified using the original signing key which will be the only // signing key visible to the platform. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME); File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.T) .setSigningCertificateLineage(lineage)); ApkVerifier.Result result = verify(signedApk, null); assertResultContainsV4Signers(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testV41_rotationWithDifferentDigestAlgos_v41UsesCorrectDigest() throws Exception { // When signing an APK, the digest algorithm is determined by the number of bits in the // signing key to ensure the digest is not weaker than the key. If an original signing key // meets the requirements for the CHUNKED_SHA256 digest and the rotated signing key // meets the requirements for CHUNKED_SHA512, then the v3.0 and v3.1 signing blocks will // use different digests. The v4.1 signature must use the content digest from the v3.1 // block since that's the digest that will be used to verify the v4.1 signature on all // platform versions that support the v3.1 signer. List rsa2048SignerConfigWithLineage = Arrays.asList( getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME), getDefaultSignerConfigFromResources(FIRST_RSA_4096_SIGNER_RESOURCE_NAME)); SigningCertificateLineage lineage = Resources.toSigningCertificateLineage( ApkSignerTest.class, LINEAGE_RSA_2048_TO_RSA_4096_RESOURCE_NAME); File signedApk = sign("original.apk", new ApkSigner.Builder(rsa2048SignerConfigWithLineage) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(true) .setMinSdkVersionForRotation(AndroidSdkVersion.T) .setSigningCertificateLineage(lineage)); ApkVerifier.Result result = verify(signedApk, null); assertResultContainsV4Signers(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, FIRST_RSA_4096_SIGNER_RESOURCE_NAME); } @Test public void testSourceStampTimestamp_signWithSourceStampAndTimestampDefault_validTimestampValue() throws Exception { // Source stamps should include a timestamp attribute with the epoch time the stamp block // was signed. This test verifies a standard signing with a source stamp includes a valid // value for the source stamp timestamp attribute by default. ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); List ecP256SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); File signedApk = sign("original.apk", new ApkSigner.Builder(ecP256SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setSourceStampSignerConfig(rsa2048SignerConfig)); ApkVerifier.Result result = verify(signedApk, null); assertSourceStampVerified(signedApk, result); long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds(); assertTrue("Invalid source stamp timestamp value: " + timestamp, timestamp > 0); } @Test public void testSourceStampTimestamp_signWithSourceStampAndTimestampEnabled_validTimestampValue() throws Exception { // Similar to above, this test verifies a valid timestamp value is written to the // attribute when the caller explicitly requests to enable the source stamp timestamp. ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); List ecP256SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); File signedApk = sign("original.apk", new ApkSigner.Builder(ecP256SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setSourceStampSignerConfig(rsa2048SignerConfig) .setSourceStampTimestampEnabled(true)); ApkVerifier.Result result = verify(signedApk, null); assertSourceStampVerified(signedApk, result); long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds(); assertTrue("Invalid source stamp timestamp value: " + timestamp, timestamp > 0); } @Test public void testSourceStampTimestamp_signWithSourceStampAndTimestampDisabled_defaultTimestampValue() throws Exception { // While source stamps should include a timestamp attribute indicating the time at which // the stamp was signed, this can cause problems for reproducible builds. The // ApkSigner.Builder#setSourceStampTimestampEnabled API allows the caller to specify // whether the timestamp attribute should be written; this test verifies no timestamp is // written to the source stamp if this API is used to disable the timestamp. ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); List ecP256SignerConfig = Collections.singletonList( getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME)); File signedApk = sign("original.apk", new ApkSigner.Builder(ecP256SignerConfig) .setV1SigningEnabled(true) .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setV4SigningEnabled(false) .setSourceStampSignerConfig(rsa2048SignerConfig) .setSourceStampTimestampEnabled(false)); ApkVerifier.Result result = verify(signedApk, null); assertSourceStampVerified(signedApk, result); long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds(); assertEquals(0, timestamp); } /** * Asserts the provided {@code signedApk} contains a signature block with the expected * {@code byte[]} value and block ID as specified in the {@code expectedBlock}. */ private static void assertSigningBlockContains(File signedApk, Pair expectedBlock) throws Exception { try (RandomAccessFile apkFile = new RandomAccessFile(signedApk, "r")) { ApkUtils.ApkSigningBlock apkSigningBlock = ApkUtils.findApkSigningBlock( DataSources.asDataSource(apkFile)); List> signatureBlocks = ApkSigningBlockUtils.getApkSignatureBlocks(apkSigningBlock.getContents()); for (Pair signatureBlock : signatureBlocks) { if (signatureBlock.getSecond().equals(expectedBlock.getSecond())) { if (Arrays.equals(signatureBlock.getFirst(), expectedBlock.getFirst())) { return; } } } fail(String.format( "The APK signing block did not contain the expected block with ID %08x", expectedBlock.getSecond())); } } /** * Asserts the provided verification {@code result} contains the expected {@code signers} for * each scheme that was used to verify the APK's signature. */ static void assertResultContainsSigners(ApkVerifier.Result result, String... signers) throws Exception { assertResultContainsSigners(result, false, signers); } /** * Asserts the provided verification {@code result} contains the expected {@code signers} for * each scheme that was used to verify the APK's signature; if {@code rotationExpected} is set * to {@code true}, then the first element in {@code signers} is treated as the expected * original signer for any V1, V2, and V3 (where applicable) signatures, and the last element * is the rotated expected signer for V3+. */ static void assertResultContainsSigners(ApkVerifier.Result result, boolean rotationExpected, String... signers) throws Exception { // A result must be successfully verified before verifying any of the result's signers. assertTrue(result.isVerified()); List expectedSigners = new ArrayList<>(); for (String signer : signers) { ApkSigner.SignerConfig signerConfig = getDefaultSignerConfigFromResources(signer); expectedSigners.addAll(signerConfig.getCertificates()); } // If rotation is expected then the V1 and V2 signature should only be signed by the // original signer. List expectedV1Signers = rotationExpected ? List.of(expectedSigners.get(0)) : expectedSigners; List expectedV2Signers = rotationExpected ? List.of(expectedSigners.get(0)) : expectedSigners; // V3 only supports a single signer; if rotation is not expected or the V3.1 block contains // the rotated signing key then the expected V3.0 signer should be the original signer. List expectedV3Signers = !rotationExpected || result.isVerifiedUsingV31Scheme() ? List.of(expectedSigners.get(0)) : List.of(expectedSigners.get(expectedSigners.size() - 1)); if (result.isVerifiedUsingV1Scheme()) { Set v1Signers = new HashSet<>(); for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) { v1Signers.add(signer.getCertificate()); } assertTrue("Expected V1 signers: " + getAllSubjectNamesFrom(expectedV1Signers) + ", actual V1 signers: " + getAllSubjectNamesFrom(v1Signers), v1Signers.containsAll(expectedV1Signers)); } if (result.isVerifiedUsingV2Scheme()) { Set v2Signers = new HashSet<>(); for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) { v2Signers.add(signer.getCertificate()); } assertTrue("Expected V2 signers: " + getAllSubjectNamesFrom(expectedV2Signers) + ", actual V2 signers: " + getAllSubjectNamesFrom(v2Signers), v2Signers.containsAll(expectedV2Signers)); } if (result.isVerifiedUsingV3Scheme()) { Set v3Signers = new HashSet<>(); for (V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { v3Signers.add(signer.getCertificate()); } assertTrue("Expected V3 signers: " + getAllSubjectNamesFrom(expectedV3Signers) + ", actual V3 signers: " + getAllSubjectNamesFrom(v3Signers), v3Signers.containsAll(expectedV3Signers)); } if (result.isVerifiedUsingV31Scheme()) { Set v31Signers = new HashSet<>(); for (V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { v31Signers.add(signer.getCertificate()); } // V3.1 only supports specifying signatures with a rotated signing key; if a V3.1 // signing block was verified then ensure it contains the expected rotated signer. List expectedV31Signers = List .of(expectedSigners.get(expectedSigners.size() - 1)); assertTrue("Expected V3.1 signers: " + getAllSubjectNamesFrom(expectedV31Signers) + ", actual V3.1 signers: " + getAllSubjectNamesFrom(v31Signers), v31Signers.containsAll(expectedV31Signers)); } } /** * Asserts the provided verification {@code result} contains the expected V4 {@code signers}. */ private static void assertResultContainsV4Signers(ApkVerifier.Result result, String... signers) throws Exception { assertTrue(result.isVerified()); assertTrue(result.isVerifiedUsingV4Scheme()); List expectedSigners = new ArrayList<>(); for (String signer : signers) { ApkSigner.SignerConfig signerConfig = getDefaultSignerConfigFromResources(signer); expectedSigners.addAll(signerConfig.getCertificates()); } List v4Signers = new ArrayList<>(); for (ApkVerifier.Result.V4SchemeSignerInfo signer : result.getV4SchemeSigners()) { v4Signers.addAll(signer.getCertificates()); } assertTrue("Expected V4 signers: " + getAllSubjectNamesFrom(expectedSigners) + ", actual V4 signers: " + getAllSubjectNamesFrom(v4Signers), v4Signers.containsAll(expectedSigners)); } /** * Asserts the provided {@code result} contains the expected {@code signer} targeting * {@code minSdkVersion} as the minimum version for rotation. */ static void assertV31SignerTargetsMinApiLevel(ApkVerifier.Result result, String signer, int minSdkVersion) throws Exception { assertTrue(result.isVerifiedUsingV31Scheme()); ApkSigner.SignerConfig expectedSignerConfig = getDefaultSignerConfigFromResources(signer); StringBuilder errorMessage = new StringBuilder(); boolean signerTargetsDevRelease = false; if (minSdkVersion == V3SchemeConstants.DEV_RELEASE) { minSdkVersion = V3SchemeConstants.PROD_RELEASE; signerTargetsDevRelease = true; } for (V3SchemeSignerInfo signerConfig : result.getV31SchemeSigners()) { if (signerConfig.getCertificates() .containsAll(expectedSignerConfig.getCertificates())) { // The V3.1 signature scheme allows the same signer to target multiple SDK versions // with different capabilities in the lineage, so save the current error message // in case no subsequent instances of this signer target the specified SDK version. if (minSdkVersion != signerConfig.getMinSdkVersion()) { if (errorMessage.length() > 0) { errorMessage.append(System.getProperty("line.separator")); } errorMessage.append( "The signer, " + getAllSubjectNamesFrom(signerConfig.getCertificates()) + ", is expected to target SDK version " + minSdkVersion + ", instead it is targeting " + signerConfig.getMinSdkVersion()); } else if (signerTargetsDevRelease && !signerConfig.getRotationTargetsDevRelease()) { if (errorMessage.length() > 0) { errorMessage.append(System.getProperty("line.separator")); } errorMessage.append( "The signer, " + getAllSubjectNamesFrom(signerConfig.getCertificates()) + ", is targeting a development release, " + minSdkVersion + ", but the attribute to target a development release is not" + " set"); } else { return; } } } fail("Did not find the expected signer, " + getAllSubjectNamesFrom( expectedSignerConfig.getCertificates()) + ": " + errorMessage); } /** * Returns the V3.1 signer from the provided {@code result} targeting the specified {@code * targetSdkVersion}. */ private V3SchemeSignerInfo getV31SignerTargetingSdkVersion(ApkVerifier.Result result, int targetSdkVersion) throws Exception { boolean signerTargetsDevRelease = false; if (targetSdkVersion == V3SchemeConstants.DEV_RELEASE) { targetSdkVersion = V3SchemeConstants.PROD_RELEASE; signerTargetsDevRelease = true; } for (V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { if (signer.getMinSdkVersion() == targetSdkVersion) { // If a signer is targeting a development release and another signer is targeting // the most recent production release, then both could be targeting the same SDK // version. if (signerTargetsDevRelease != signer.getRotationTargetsDevRelease()) { continue; } return signer; } } fail("No V3.1 signer found targeting min SDK version " + targetSdkVersion + ", dev release: " + signerTargetsDevRelease); return null; } /** * Returns a comma delimited {@code String} containing all of the Subject Names from the * provided {@code certificates}. */ private static String getAllSubjectNamesFrom(Collection certificates) { StringBuilder result = new StringBuilder(); for (X509Certificate certificate : certificates) { if (result.length() > 0) { result.append(", "); } result.append(certificate.getSubjectDN().getName()); } return result.toString(); } private static boolean resourceZipFileContains(String resourceName, String zipEntryName) throws IOException { ZipInputStream zip = new ZipInputStream( Resources.toInputStream(ApkSignerTest.class, resourceName)); while (true) { ZipEntry entry = zip.getNextEntry(); if (entry == null) { break; } if (entry.getName().equals(zipEntryName)) { return true; } } return false; } private RSAPublicKey getRSAPublicKeyFromSigningBlock(File apk, int signatureVersionId) throws Exception { int signatureVersionBlockId; switch (signatureVersionId) { case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2: signatureVersionBlockId = V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID; break; case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3: signatureVersionBlockId = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; break; default: throw new Exception( "Invalid signature version ID specified: " + signatureVersionId); } SignatureInfo signatureInfo = getSignatureInfoFromApk(apk, signatureVersionId, signatureVersionBlockId); // FORMAT: // * length prefixed sequence of length prefixed signers // * length-prefixed signed data // * V3+ only - minSDK (uint32) // * V3+ only - maxSDK (uint32) // * length-prefixed sequence of length-prefixed signatures: // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded) ByteBuffer signers = ApkSigningBlockUtils.getLengthPrefixedSlice(signatureInfo.signatureBlock); ByteBuffer signer = ApkSigningBlockUtils.getLengthPrefixedSlice(signers); // Since all the data is read from the signer block the signedData and signatures are // discarded. ApkSigningBlockUtils.getLengthPrefixedSlice(signer); // For V3+ signature version IDs discard the min / max SDKs as well if (signatureVersionId >= ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3) { signer.getInt(); signer.getInt(); } ApkSigningBlockUtils.getLengthPrefixedSlice(signer); ByteBuffer publicKey = ApkSigningBlockUtils.getLengthPrefixedSlice(signer); SubjectPublicKeyInfo subjectPublicKeyInfo = Asn1BerParser.parse(publicKey, SubjectPublicKeyInfo.class); ByteBuffer subjectPublicKeyBuffer = subjectPublicKeyInfo.subjectPublicKey; // The SubjectPublicKey is stored as a bit string in the SubjectPublicKeyInfo with the first // byte indicating the number of padding bits in the public key. Read this first byte to // allow parsing the rest of the RSAPublicKey as a sequence. subjectPublicKeyBuffer.get(); return Asn1BerParser.parse(subjectPublicKeyBuffer, RSAPublicKey.class); } private static SignatureInfo getSignatureInfoFromApk( File apkFile, int signatureVersionId, int signatureVersionBlockId) throws IOException, ZipFormatException, ApkSigningBlockUtils.SignatureNotFoundException { try (RandomAccessFile f = new RandomAccessFile(apkFile, "r")) { DataSource apk = DataSources.asDataSource(f, 0, f.length()); ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( signatureVersionId); return ApkSigningBlockUtils.findSignature(apk, zipSections, signatureVersionBlockId, result); } } /** * Asserts that signing the specified golden input file using the provided signing configuration * produces output identical to the specified golden output file. */ private void assertGolden( String inResourceName, String expectedOutResourceName, ApkSigner.Builder apkSignerBuilder) throws Exception { // Sign the provided golden input File out = sign(inResourceName, apkSignerBuilder); assertVerified(verify(out, AndroidSdkVersion.P)); // Assert that the output is identical to the provided golden output if (out.length() > Integer.MAX_VALUE) { throw new RuntimeException("Output too large: " + out.length() + " bytes"); } byte[] outData = new byte[(int) out.length()]; try (FileInputStream fis = new FileInputStream(out)) { fis.read(outData); } ByteBuffer actualOutBuf = ByteBuffer.wrap(outData); ByteBuffer expectedOutBuf = ByteBuffer.wrap(Resources.toByteArray(getClass(), expectedOutResourceName)); boolean identical = false; if (actualOutBuf.remaining() == expectedOutBuf.remaining()) { while (actualOutBuf.hasRemaining()) { if (actualOutBuf.get() != expectedOutBuf.get()) { break; } } identical = !actualOutBuf.hasRemaining(); } if (identical) { return; } if (KEEP_FAILING_OUTPUT_AS_FILES) { File tmp = File.createTempFile(getClass().getSimpleName(), ".apk"); Files.copy(out.toPath(), tmp.toPath()); fail(tmp + " differs from " + expectedOutResourceName); } else { fail("Output differs from " + expectedOutResourceName); } } private File sign(File inApkFile, ApkSigner.Builder apkSignerBuilder) throws Exception { try (RandomAccessFile apkFile = new RandomAccessFile(inApkFile, "r")) { DataSource in = DataSources.asDataSource(apkFile); return sign(in, apkSignerBuilder); } } private File sign(String inResourceName, ApkSigner.Builder apkSignerBuilder) throws Exception { DataSource in = DataSources.asDataSource( ByteBuffer.wrap(Resources.toByteArray(getClass(), inResourceName))); return sign(in, apkSignerBuilder); } private File sign(DataSource in, ApkSigner.Builder apkSignerBuilder) throws Exception { File outFile = mTemporaryFolder.newFile(); apkSignerBuilder.setInputApk(in).setOutputApk(outFile); File outFileIdSig = new File(outFile.getCanonicalPath() + ".idsig"); apkSignerBuilder.setV4SignatureOutputFile(outFileIdSig); apkSignerBuilder.setV4ErrorReportingEnabled(true); apkSignerBuilder.build().sign(); return outFile; } private static ApkVerifier.Result verifyForMinSdkVersion(File apk, int minSdkVersion) throws IOException, ApkFormatException, NoSuchAlgorithmException { return verify(apk, minSdkVersion); } private static ApkVerifier.Result verify(File apk, Integer minSdkVersionOverride) throws IOException, ApkFormatException, NoSuchAlgorithmException { ApkVerifier.Builder builder = new ApkVerifier.Builder(apk); if (minSdkVersionOverride != null) { builder.setMinCheckedPlatformVersion(minSdkVersionOverride); } File idSig = new File(apk.getCanonicalPath() + ".idsig"); if (idSig.exists()) { builder.setV4SignatureFile(idSig); } return builder.build().verify(); } private static void assertVerified(ApkVerifier.Result result) { ApkVerifierTest.assertVerified(result); } private static void assertSourceStampVerified(File signedApk, ApkVerifier.Result result) throws ApkSigningBlockUtils.SignatureNotFoundException, IOException, ZipFormatException { SignatureInfo signatureInfo = getSignatureInfoFromApk( signedApk, ApkSigningBlockUtils.VERSION_SOURCE_STAMP, SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID); assertNotNull(signatureInfo.signatureBlock); assertTrue(result.isSourceStampVerified()); } private static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) { ApkVerifierTest.assertVerificationFailure(result, expectedIssue); } private void assertFileContentsEqual(File first, File second) throws IOException { assertArrayEquals(Files.readAllBytes(Paths.get(first.getPath())), Files.readAllBytes(Paths.get(second.getPath()))); } private static List getSignerConfigsFromResources( String... signerNames) throws Exception { List signerConfigs = new ArrayList<>(); for (String signerName : signerNames) { signerConfigs.add(getDefaultSignerConfigFromResources(signerName)); } return signerConfigs; } private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( String keyNameInResources) throws Exception { return getDefaultSignerConfigFromResources(keyNameInResources, false); } private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( String keyNameInResources, boolean deterministicDsaSigning) throws Exception { return getDefaultSignerConfigFromResources(keyNameInResources, deterministicDsaSigning, 0, null); } /** * Returns a new {@link ApkSigner.SignerConfig} with the certificate and private key in * resources with the file prefix {@code keyNameInResources} targeting {@code targetSdkVersion} * with lineage {@code lineage} and using deterministic DSA signing when {@code * deterministicDsaSigning} is set to true. */ private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( String keyNameInResources, boolean deterministicDsaSigning, int targetSdkVersion, SigningCertificateLineage lineage) throws Exception { PrivateKey privateKey = Resources.toPrivateKey(ApkSignerTest.class, keyNameInResources + ".pk8"); List certs = Resources.toCertificateChain(ApkSignerTest.class, keyNameInResources + ".x509.pem"); ApkSigner.SignerConfig.Builder signerConfigBuilder = new ApkSigner.SignerConfig.Builder( keyNameInResources, new KeyConfig.Jca(privateKey), certs, deterministicDsaSigning); if (targetSdkVersion > 0) { signerConfigBuilder.setLineageForMinSdkVersion(lineage, targetSdkVersion); } return signerConfigBuilder.build(); } private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources( String keyNameInResources, String certNameInResources) throws Exception { PrivateKey privateKey = Resources.toPrivateKey(ApkSignerTest.class, keyNameInResources + ".pk8"); List certs = Resources.toCertificateChain(ApkSignerTest.class, certNameInResources); return new ApkSigner.SignerConfig.Builder( keyNameInResources, new KeyConfig.Jca(privateKey), certs) .build(); } private static ApkSigner.SignerConfig getDeterministicDsaSignerConfigFromResources( String keyNameInResources) throws Exception { return getDefaultSignerConfigFromResources(keyNameInResources, true); } } ./PaxHeaders.X/src_test_java_com_android_apksig_ApkVerifierTest.java0100644 0000000 0000000 00000000034 14763776540 025041 xustar000000000 0000000 28 mtime=1741684064.6200000 src/test/java/com/android/apksig/ApkVerifierTest.java0100644 0000000 0000000 00000342543 14763776540 021720 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.ApkSignerTest.assertResultContainsSigners; import static com.android.apksig.ApkSignerTest.assertV31SignerTargetsMinApiLevel; import static com.android.apksig.Constants.VERSION_APK_SIGNATURE_SCHEME_V2; import static com.android.apksig.Constants.VERSION_APK_SIGNATURE_SCHEME_V3; import static com.android.apksig.Constants.VERSION_APK_SIGNATURE_SCHEME_V31; import static com.android.apksig.internal.util.Resources.FIRST_RSA_2048_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.SECOND_RSA_2048_SIGNER_RESOURCE_NAME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeNoException; import static org.junit.Assume.assumeTrue; import com.android.apksig.ApkVerifier.Issue; import com.android.apksig.ApkVerifier.IssueWithParams; import com.android.apksig.ApkVerifier.Result; import com.android.apksig.ApkVerifier.Result.SourceStampInfo.SourceStampVerificationStatus; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.apk.ApkUtils; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.ContentDigestAlgorithm; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.HexEncoding; import com.android.apksig.internal.util.Resources; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.PublicKey; import java.security.Security; import java.security.Signature; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; @RunWith(JUnit4.class) public class ApkVerifierTest { @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private static final String[] DSA_KEY_NAMES = {"1024", "2048", "3072"}; private static final String[] DSA_KEY_NAMES_1024_AND_SMALLER = {"1024"}; private static final String[] DSA_KEY_NAMES_2048_AND_LARGER = {"2048", "3072"}; private static final String[] EC_KEY_NAMES = {"p256", "p384", "p521"}; private static final String[] RSA_KEY_NAMES = {"1024", "2048", "3072", "4096", "8192", "16384"}; private static final String[] RSA_KEY_NAMES_2048_AND_LARGER = { "2048", "3072", "4096", "8192", "16384" }; private static final String RSA_2048_CERT_SHA256_DIGEST = "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8"; private static final String EC_P256_CERT_SHA256_DIGEST = "6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599"; private static final String RSA_2048_CHUNKED_SHA256_DIGEST = "0a457e6dd7cc8d4dde28a4dae843032de5fbe58123eedd0a31e7f958f23e1626"; private static final String RSA_2048_CHUNKED_SHA256_DIGEST_FROM_INCORRECTLY_SIGNED_APK = "0a457e6dd7cc8d4dde28a4dae843032de5fbe58101eedd0a31e7f958f23e1626"; @Test public void testOriginalAccepted() throws Exception { // APK signed with v1 and v2 schemes. Obtained by building // cts/hostsidetests/appsecurity/test-apps/tinyapp. // This APK is used as a basis for many of the other tests here. Hence, we check that this // APK verifies. assertVerified(verify("original.apk")); } @Test public void testV1OneSignerMD5withRSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach( "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); assertVerifiedForEach( "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-%s.apk", RSA_KEY_NAMES); } @Test public void testV1OneSignerSHA1withRSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-%s.apk", RSA_KEY_NAMES); } @Test public void testV1OneSignerSHA224withRSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.14-%s.apk", RSA_KEY_NAMES); } @Test public void testV1OneSignerSHA256withRSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.11-%s.apk", RSA_KEY_NAMES); } @Test public void testV1OneSignerSHA384withRSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-%s.apk", RSA_KEY_NAMES); } @Test public void testV1OneSignerSHA512withRSAVerifies() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); assertVerifiedForEach( "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.13-%s.apk", RSA_KEY_NAMES); } @Test public void testV1OneSignerSHA1withECDSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach("v1-only-with-ecdsa-sha1-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); assertVerifiedForEach("v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-%s.apk", EC_KEY_NAMES); } @Test public void testV1OneSignerSHA224withECDSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach("v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); assertVerifiedForEach("v1-only-with-ecdsa-sha224-1.2.840.10045.4.3.1-%s.apk", EC_KEY_NAMES); } @Test public void testV1OneSignerSHA256withECDSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach("v1-only-with-ecdsa-sha256-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); assertVerifiedForEach("v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-%s.apk", EC_KEY_NAMES); } @Test public void testV1OneSignerSHA384withECDSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach("v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); assertVerifiedForEach("v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-%s.apk", EC_KEY_NAMES); } @Test public void testV1OneSignerSHA512withECDSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach("v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); assertVerifiedForEach("v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-%s.apk", EC_KEY_NAMES); } @Test public void testV1OneSignerSHA1withDSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer // NOTE: This test is split into two because JCA Providers shipping with OpenJDK refuse to // verify DSA signatures with keys too long for the SHA-1 digest. assertVerifiedForEach( "v1-only-with-dsa-sha1-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER); assertVerifiedForEach( "v1-only-with-dsa-sha1-1.2.840.10040.4.3-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER); } @Test public void testV1OneSignerSHA1withDSAAcceptedWithKeysTooLongForDigest() throws Exception { // APK signed with v1 scheme only, one signer // OpenJDK's default implementation of Signature.SHA1withDSA refuses to verify signatures // created with keys too long for the digest used. Android Package Manager does not reject // such signatures. We thus skip this test if Signature.SHA1withDSA exhibits this issue. PublicKey publicKey = Resources.toCertificate(getClass(), "dsa-2048.x509.pem").getPublicKey(); Signature s = Signature.getInstance("SHA1withDSA"); try { s.initVerify(publicKey); } catch (InvalidKeyException e) { assumeNoException(e); } assertVerifiedForEach( "v1-only-with-dsa-sha1-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER); assertVerifiedForEach( "v1-only-with-dsa-sha1-1.2.840.10040.4.3-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER); } @Test public void testV1OneSignerSHA224withDSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer // NOTE: This test is split into two because JCA Providers shipping with OpenJDK refuse to // verify DSA signatures with keys too long for the SHA-224 digest. assertVerifiedForEach( "v1-only-with-dsa-sha224-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER); assertVerifiedForEach( "v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER); } @Test public void testV1OneSignerSHA224withDSAAcceptedWithKeysTooLongForDigest() throws Exception { // APK signed with v1 scheme only, one signer // OpenJDK's default implementation of Signature.SHA224withDSA refuses to verify signatures // created with keys too long for the digest used. Android Package Manager does not reject // such signatures. We thus skip this test if Signature.SHA224withDSA exhibits this issue. PublicKey publicKey = Resources.toCertificate(getClass(), "dsa-2048.x509.pem").getPublicKey(); Signature s = Signature.getInstance("SHA224withDSA"); try { s.initVerify(publicKey); } catch (InvalidKeyException e) { assumeNoException(e); } assertVerifiedForEach( "v1-only-with-dsa-sha224-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER); assertVerifiedForEach( "v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER); } @Test public void testV1OneSignerSHA256withDSAAccepted() throws Exception { // APK signed with v1 scheme only, one signer assertVerifiedForEach("v1-only-with-dsa-sha256-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES); assertVerifiedForEach( "v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.2-%s.apk", DSA_KEY_NAMES); } @Test public void testV1MaxSupportedSignersAccepted() throws Exception { // The APK Signature Scheme V1 supports a maximum of 10 signers; this test ensures an // APK signed with that many signers successfully verifies. assertVerified(verify("v1-only-10-signers.apk")); } @Test public void testV1MoreThanMaxSupportedSignersRejected() throws Exception { // This test ensure an APK signed with more than the supported number of signers fails // to verify. assertVerificationFailure("v1-only-11-signers.apk", Issue.JAR_SIG_MAX_SIGNATURES_EXCEEDED); } @Test public void testV2StrippedRejected() throws Exception { // APK signed with v1 and v2 schemes, but v2 signature was stripped from the file (by using // zipalign). // This should fail because the v1 signature indicates that the APK was supposed to be // signed with v2 scheme as well, making the platform's anti-stripping protections reject // the APK. assertVerificationFailure("v2-stripped.apk", Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED); // Similar to above, but the X-Android-APK-Signed anti-stripping header in v1 signature // lists unknown signature schemes in addition to APK Signature Scheme v2. Unknown schemes // should be ignored. assertVerificationFailure( "v2-stripped-with-ignorable-signing-schemes.apk", Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED); } @Test public void testV3StrippedRejected() throws Exception { // APK signed with v2 and v3 schemes, but v3 signature was stripped from the file by // modifying the v3 block ID to be the verity padding block ID. Without the stripping // protection this modification ignores the v3 signing scheme block. assertVerificationFailure("v3-stripped.apk", Issue.V2_SIG_MISSING_APK_SIG_REFERENCED); } @Test public void testSignaturesIgnoredForMaxSDK() throws Exception { // The V2 signature scheme was introduced in N, and V3 was introduced in P. This test // verifies a max SDK of pre-P ignores the V3 signature and a max SDK of pre-N ignores both // the V2 and V3 signatures. assertVerified( verifyForMaxSdkVersion( "v1v2v3-with-rsa-2048-lineage-3-signers.apk", AndroidSdkVersion.O)); assertVerified( verifyForMaxSdkVersion( "v1v2v3-with-rsa-2048-lineage-3-signers.apk", AndroidSdkVersion.M)); } @Test public void testGetResultLineage() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v31-tgt-33-no-v3-attr.apk"))); int sdkVersion = AndroidSdkVersion.O; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result = ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); assertTrue(ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31).size() == 2); assertEquals(ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31) .getCertificatesInLineage().get(1), result.getV31SchemeSigners().get(0).getCertificate()); SigningCertificateLineageTest.assertLineageContainsExpectedSigners( ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testGetResultV3Lineage() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v3-rsa-2048_2-tgt-dev-release.apk"))); int sdkVersion = AndroidSdkVersion.N; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result = ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3); assertTrue(ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3).size() == 2); assertEquals(ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3) .getCertificatesInLineage().get(1), result.getV3SchemeSigners().get(0).getCertificate()); SigningCertificateLineageTest.assertLineageContainsExpectedSigners( ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testGetResultNoLineageApk() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v31-empty-lineage-no-v3.apk"))); int sdkVersion = AndroidSdkVersion.N; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result = ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); assertTrue(result != null); assertTrue(!ApkVerifier.containsLineageErrors(result)); assertTrue(ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31) != null); assertEquals(ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31) .getCertificatesInLineage().get(0), result.getV31SchemeSigners().get(0).getCertificate()); } @Test public void testGetResultNoV31Apk() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v3-rsa-2048_2-tgt-dev-release.apk"))); int sdkVersion = AndroidSdkVersion.N; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result = ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); assertTrue(result.getV31SchemeSigners().isEmpty()); } @Test public void testGetResultFromV3BlockFromV31SignedApk() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v31-rsa-2048_2-tgt-33-1-tgt-28.apk"))); int sdkVersion = AndroidSdkVersion.N; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result = ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3); assertTrue(!result.getV3SchemeSigners().isEmpty()); assertTrue(ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3) .getCertificatesInLineage() .equals(Arrays.asList(result.getV3SchemeSigners().get(0).getCertificate()))); } @Test public void testGetResultContainsLineageErrors() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v31-2elem-incorrect-lineage.apk"))); int sdkVersion = AndroidSdkVersion.P; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result = ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); assertTrue(result != null); assertTrue(ApkVerifier.containsLineageErrors(result)); assertTrue(ApkVerifier.getLineageFromResult( result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31) == null); } @Test public void testGetResultDigests() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v31-empty-lineage-no-v3.apk"))); int sdkVersion = AndroidSdkVersion.N; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result = ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); Map digests = ApkVerifier.getContentDigestsFromResult( result, VERSION_APK_SIGNATURE_SCHEME_V31); assertTrue(digests.size() == 1); assertTrue(digests.containsKey(ContentDigestAlgorithm.CHUNKED_SHA256)); assertTrue(RSA_2048_CHUNKED_SHA256_DIGEST.equalsIgnoreCase( ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); } @Test public void testGetV3ResultDigests() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v31-rsa-2048_2-tgt-33-1-tgt-28.apk"))); int sdkVersion = AndroidSdkVersion.N; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result = ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3); Map digests = ApkVerifier.getContentDigestsFromResult( result, VERSION_APK_SIGNATURE_SCHEME_V3); assertTrue(digests.size() == 1); assertTrue(digests.containsKey(ContentDigestAlgorithm.CHUNKED_SHA256)); assertTrue(RSA_2048_CHUNKED_SHA256_DIGEST.equalsIgnoreCase( ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); } @Test public void testGetV2ResultDigests() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v31-rsa-2048_2-tgt-33-1-tgt-28.apk"))); int sdkVersion = AndroidSdkVersion.N; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result =ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V2); Map digests = ApkVerifier.getContentDigestsFromResult( result, VERSION_APK_SIGNATURE_SCHEME_V2); assertTrue(digests.size() == 1); assertTrue(digests.containsKey(ContentDigestAlgorithm.CHUNKED_SHA256)); assertTrue(RSA_2048_CHUNKED_SHA256_DIGEST.equalsIgnoreCase( ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); } @Test public void testGetResultIncorrectDigests() throws Exception { DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), "v31-2elem-lineage-incorrect-digest.apk"))); int sdkVersion = AndroidSdkVersion.S; ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); Result result = ApkVerifier.getSigningBlockResult( apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); Map digests = ApkVerifier.getContentDigestsFromResult( result, VERSION_APK_SIGNATURE_SCHEME_V31); assertTrue(digests.size() == 1); assertTrue(digests.containsKey(ContentDigestAlgorithm.CHUNKED_SHA256)); assertTrue(!RSA_2048_CHUNKED_SHA256_DIGEST.equalsIgnoreCase( ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); assertTrue(RSA_2048_CHUNKED_SHA256_DIGEST_FROM_INCORRECTLY_SIGNED_APK.equalsIgnoreCase( ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); } @Test public void testV2OneSignerOneSignatureAccepted() throws Exception { // APK signed with v2 scheme only, one signer, one signature assertVerifiedForEachForMinSdkVersion( "v2-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES, AndroidSdkVersion.N); assertVerifiedForEachForMinSdkVersion( "v2-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.N); assertVerifiedForEachForMinSdkVersion( "v2-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.N); // RSA-PSS signatures tested in a separate test below // DSA with SHA-512 is not supported by Android platform and thus APK Signature Scheme v2 // does not support that either // assertInstallSucceedsForEach("v2-only-with-dsa-sha512-%s.apk", DSA_KEY_NAMES); assertVerifiedForEachForMinSdkVersion( "v2-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.N); assertVerifiedForEachForMinSdkVersion( "v2-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.N); } @Test public void testV3OneSignerOneSignatureAccepted() throws Exception { // APK signed with v3 scheme only, one signer, one signature assertVerifiedForEachForMinSdkVersion( "v3-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES, AndroidSdkVersion.P); assertVerifiedForEachForMinSdkVersion( "v3-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.P); assertVerifiedForEachForMinSdkVersion( "v3-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.P); assertVerifiedForEachForMinSdkVersion( "v3-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.P); assertVerifiedForEachForMinSdkVersion( "v3-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.P); } @Test public void testV2OneSignerOneRsaPssSignatureAccepted() throws Exception { assumeThatRsaPssAvailable(); // APK signed with v2 scheme only, one signer, one signature assertVerifiedForEachForMinSdkVersion( "v2-only-with-rsa-pss-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.N); assertVerifiedForEachForMinSdkVersion( "v2-only-with-rsa-pss-sha512-%s.apk", RSA_KEY_NAMES_2048_AND_LARGER, // 1024-bit key is too short for PSS with SHA-512 AndroidSdkVersion.N); } @Test public void testV2SignatureDoesNotMatchSignedDataRejected() throws Exception { // APK signed with v2 scheme only, but the signature over signed-data does not verify // Bitflip in certificate field inside signed-data. Based on // v2-only-with-dsa-sha256-1024.apk. assertVerificationFailure( "v2-only-with-dsa-sha256-1024-sig-does-not-verify.apk", Issue.V2_SIG_DID_NOT_VERIFY); // Signature claims to be RSA PKCS#1 v1.5 with SHA-256, but is actually using SHA-512. // Based on v2-only-with-rsa-pkcs1-sha256-2048.apk. assertVerificationIssue( verify("v2-only-with-rsa-pkcs1-sha256-2048-sig-does-not-verify.apk"), true, Issue.V2_SIG_VERIFY_EXCEPTION, Issue.V2_SIG_DID_NOT_VERIFY); // Bitflip in the ECDSA signature. Based on v2-only-with-ecdsa-sha256-p256.apk. assertVerificationFailure( "v2-only-with-ecdsa-sha256-p256-sig-does-not-verify.apk", Issue.V2_SIG_DID_NOT_VERIFY); } @Test public void testV3SignatureDoesNotMatchSignedDataRejected() throws Exception { // APK signed with v3 scheme only, but the signature over signed-data does not verify // Bitflip in DSA signature. Based on v3-only-with-dsa-sha256-2048.apk. assertVerificationFailure( "v3-only-with-dsa-sha256-2048-sig-does-not-verify.apk", Issue.V3_SIG_DID_NOT_VERIFY); // Bitflip in signed data. Based on v3-only-with-rsa-pkcs1-sha256-3072.apk assertVerificationFailure( "v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk", Issue.V3_SIG_DID_NOT_VERIFY); // Based on v3-only-with-ecdsa-sha512-p521 with the signature ID changed to be ECDSA with // SHA-256. assertVerificationFailure( "v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk", Issue.V3_SIG_DID_NOT_VERIFY); } @Test public void testV2RsaPssSignatureDoesNotMatchSignedDataRejected() throws Exception { assumeThatRsaPssAvailable(); // APK signed with v2 scheme only, but the signature over signed-data does not verify. // Signature claims to be RSA PSS with SHA-256 and 32 bytes of salt, but is actually using 0 // bytes of salt. Based on v2-only-with-rsa-pkcs1-sha256-2048.apk. Obtained by modifying APK // signer to use the wrong amount of salt. assertVerificationFailure( "v2-only-with-rsa-pss-sha256-2048-sig-does-not-verify.apk", Issue.V2_SIG_DID_NOT_VERIFY); } @Test public void testV2ContentDigestMismatchRejected() throws Exception { // APK signed with v2 scheme only, but the digest of contents does not match the digest // stored in signed-data ApkVerifier.Issue error = Issue.V2_SIG_APK_DIGEST_DID_NOT_VERIFY; // Based on v2-only-with-rsa-pkcs1-sha512-4096.apk. Obtained by modifying APK signer to // flip the leftmost bit in content digest before signing signed-data. assertVerificationFailure("v2-only-with-rsa-pkcs1-sha512-4096-digest-mismatch.apk", error); // Based on v2-only-with-ecdsa-sha256-p256.apk. Obtained by modifying APK signer to flip the // leftmost bit in content digest before signing signed-data. assertVerificationFailure("v2-only-with-ecdsa-sha256-p256-digest-mismatch.apk", error); } @Test public void testV3ContentDigestMismatchRejected() throws Exception { // APK signed with v3 scheme only, but the digest of contents does not match the digest // stored in signed-data. // Based on v3-only-with-rsa-pkcs1-sha512-8192. Obtained by flipping a bit in the local // file header of the APK. assertVerificationFailure( "v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY); // Based on v3-only-with-dsa-sha256-3072.apk. Obtained by modifying APK signer to flip the // leftmost bit in content digest before signing signed-data. assertVerificationFailure( "v3-only-with-dsa-sha256-3072-digest-mismatch.apk", Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY); } @Test public void testNoApkSignatureSchemeBlockRejected() throws Exception { // APK signed with v2 scheme only, but the rules for verifying APK Signature Scheme v2 // signatures say that this APK must not be verified using APK Signature Scheme v2. // Obtained from v2-only-with-rsa-pkcs1-sha512-4096.apk by flipping a bit in the magic // field in the footer of APK Signing Block. This makes the APK Signing Block disappear. assertVerificationFailure( "v2-only-wrong-apk-sig-block-magic.apk", Issue.JAR_SIG_NO_MANIFEST); // Obtained by modifying APK signer to insert "GARBAGE" between ZIP Central Directory and // End of Central Directory. The APK is otherwise fine and is signed with APK Signature // Scheme v2. Based on v2-only-with-rsa-pkcs1-sha256.apk. assertVerificationFailure( "v2-only-garbage-between-cd-and-eocd.apk", Issue.JAR_SIG_NO_MANIFEST); // Obtained by modifying the size in APK Signature Block header. Based on // v2-only-with-ecdsa-sha512-p521.apk. assertVerificationFailure( "v2-only-apk-sig-block-size-mismatch.apk", Issue.JAR_SIG_NO_MANIFEST); // Obtained by modifying the ID under which APK Signature Scheme v2 Block is stored in // APK Signing Block and by modifying the APK signer to not insert anti-stripping // protections into JAR Signature. The APK should appear as having no APK Signature Scheme // v2 Block and should thus successfully verify using JAR Signature Scheme. assertVerified(verify("v1-with-apk-sig-block-but-without-apk-sig-scheme-v2-block.apk")); } @Test public void testNoV3ApkSignatureSchemeBlockRejected() throws Exception { // Obtained from v3-only-with-ecdsa-sha512-p384.apk by flipping a bit in the magic field // in the footer of the APK Signing Block. assertVerificationFailure( "v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", Issue.JAR_SIG_NO_MANIFEST); // Obtained from v3-only-with-rsa-pkcs1-sha512-4096.apk by modifying the size in the APK // Signature Block header and footer. assertVerificationFailure( "v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk", Issue.JAR_SIG_NO_MANIFEST); } @Test(expected = ApkFormatException.class) public void testTruncatedZipCentralDirectoryRejected() throws Exception { // Obtained by modifying APK signer to truncate the ZIP Central Directory by one byte. The // APK is otherwise fine and is signed with APK Signature Scheme v2. Based on // v2-only-with-rsa-pkcs1-sha256.apk verify("v2-only-truncated-cd.apk"); } @Test public void testV2UnknownPairIgnoredInApkSigningBlock() throws Exception { // Obtained by modifying APK signer to emit an unknown ID-value pair into APK Signing Block // before the ID-value pair containing the APK Signature Scheme v2 Block. The unknown // ID-value should be ignored. assertVerified( verifyForMinSdkVersion( "v2-only-unknown-pair-in-apk-sig-block.apk", AndroidSdkVersion.N)); } @Test public void testV3UnknownPairIgnoredInApkSigningBlock() throws Exception { // Obtained by modifying APK signer to emit an unknown ID value pair into APK Signing Block // before the ID value pair containing the APK Signature Scheme v3 Block. The unknown // ID value should be ignored. assertVerified( verifyForMinSdkVersion( "v3-only-unknown-pair-in-apk-sig-block.apk", AndroidSdkVersion.P)); } @Test public void testV2UnknownSignatureAlgorithmsIgnored() throws Exception { // APK is signed with a known signature algorithm and with a couple of unknown ones. // Obtained by modifying APK signer to use "unknown" signature algorithms in addition to // known ones. assertVerified( verifyForMinSdkVersion( "v2-only-with-ignorable-unsupported-sig-algs.apk", AndroidSdkVersion.N)); } @Test public void testV3UnknownSignatureAlgorithmsIgnored() throws Exception { // APK is signed with a known signature algorithm and a couple of unknown ones. // Obtained by modifying APK signer to use "unknown" signature algorithms in addition to // known ones. assertVerified( verifyForMinSdkVersion( "v3-only-with-ignorable-unsupported-sig-algs.apk", AndroidSdkVersion.P)); } @Test public void testV3WithOnlyUnknownSignatureAlgorithmsRejected() throws Exception { // APK is only signed with an unknown signature algorithm. Obtained by modifying APK // signer's ID for a known signature algorithm. assertVerificationFailure( "v3-only-no-supported-sig-algs.apk", Issue.V3_SIG_NO_SUPPORTED_SIGNATURES); } @Test public void testV2UnknownAdditionalAttributeIgnored() throws Exception { // APK's v2 signature contains an unknown additional attribute, but is otherwise fine. // Obtained by modifying APK signer to output an additional attribute with ID 0x01020304 // and value 0x05060708. assertVerified( verifyForMinSdkVersion("v2-only-unknown-additional-attr.apk", AndroidSdkVersion.N)); } @Test public void testV3UnknownAdditionalAttributeIgnored() throws Exception { // APK's v3 signature contains unknown additional attributes before and after the lineage. // Obtained by modifying APK signer to output additional attributes with IDs 0x11223344 // and 0x99aabbcc with values 0x55667788 and 0xddeeff00 assertVerified( verifyForMinSdkVersion("v3-only-unknown-additional-attr.apk", AndroidSdkVersion.P)); // APK's v2 and v3 signatures contain unknown additional attributes before and after the // anti-stripping and lineage attributes. assertVerified( verifyForMinSdkVersion("v2v3-unknown-additional-attr.apk", AndroidSdkVersion.P)); } @Test public void testV2MismatchBetweenSignaturesAndDigestsBlockRejected() throws Exception { // APK is signed with a single signature algorithm, but the digests block claims that it is // signed with two different signature algorithms. Obtained by modifying APK Signer to // emit an additional digest record with signature algorithm 0x12345678. assertVerificationFailure( "v2-only-signatures-and-digests-block-mismatch.apk", Issue.V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS); } @Test public void testV3MismatchBetweenSignaturesAndDigestsBlockRejected() throws Exception { // APK is signed with a single signature algorithm, but the digests block claims that it is // signed with two different signature algorithms. Obtained by modifying APK Signer to // emit an additional digest record with signature algorithm 0x11223344. assertVerificationFailure( "v3-only-signatures-and-digests-block-mismatch.apk", Issue.V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS); } @Test public void testV2MismatchBetweenPublicKeyAndCertificateRejected() throws Exception { // APK is signed with v2 only. The public key field does not match the public key in the // leaf certificate. Obtained by modifying APK signer to write out a modified leaf // certificate where the RSA modulus has a bitflip. assertVerificationFailure( "v2-only-cert-and-public-key-mismatch.apk", Issue.V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD); } @Test public void testV3MismatchBetweenPublicKeyAndCertificateRejected() throws Exception { // APK is signed with v3 only. The public key field does not match the public key in the // leaf certificate. Obtained by modifying APK signer to write out a modified leaf // certificate where the RSA modulus has a bitflip. assertVerificationFailure( "v3-only-cert-and-public-key-mismatch.apk", Issue.V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD); } @Test public void testV2SignerBlockWithNoCertificatesRejected() throws Exception { // APK is signed with v2 only. There are no certificates listed in the signer block. // Obtained by modifying APK signer to output no certificates. assertVerificationFailure("v2-only-no-certs-in-sig.apk", Issue.V2_SIG_NO_CERTIFICATES); } @Test public void testV3SignerBlockWithNoCertificatesRejected() throws Exception { // APK is signed with v3 only. There are no certificates listed in the signer block. // Obtained by modifying APK signer to output no certificates. assertVerificationFailure("v3-only-no-certs-in-sig.apk", Issue.V3_SIG_NO_CERTIFICATES); } @Test public void testTwoSignersAccepted() throws Exception { // APK signed by two different signers assertVerified(verify("two-signers.apk")); assertVerified(verify("v1-only-two-signers.apk")); assertVerified(verifyForMinSdkVersion("v2-only-two-signers.apk", AndroidSdkVersion.N)); } @Test public void testV2TwoSignersRejectedWhenOneBroken() throws Exception { // Bitflip in the ECDSA signature of second signer. Based on two-signers.apk. // This asserts that breakage in any signer leads to rejection of the APK. assertVerificationFailure( "two-signers-second-signer-v2-broken.apk", Issue.V2_SIG_DID_NOT_VERIFY); } @Test public void testV2TwoSignersRejectedWhenOneWithoutSignatures() throws Exception { // APK v2-signed by two different signers. However, there are no signatures for the second // signer. assertVerificationFailure( "v2-only-two-signers-second-signer-no-sig.apk", Issue.V2_SIG_NO_SIGNATURES); } @Test public void testV2TwoSignersRejectedWhenOneWithoutSupportedSignatures() throws Exception { // APK v2-signed by two different signers. However, there are no supported signatures for // the second signer. assertVerificationFailure( "v2-only-two-signers-second-signer-no-supported-sig.apk", Issue.V2_SIG_NO_SUPPORTED_SIGNATURES); } @Test public void testV2MaxSupportedSignersAccepted() throws Exception { // The APK Signature Scheme v2 supports a maximum of 10 signers; this test ensures an // APK signed with that many signers successfully verifies. assertVerified(verifyForMinSdkVersion("v2-only-10-signers.apk", AndroidSdkVersion.N)); } @Test public void testV2MoreThanMaxSupportedSignersRejected() throws Exception { // This test ensure an APK signed with more than the supported number of signers fails // to verify. assertVerificationFailure( verifyForMinSdkVersion("v2-only-11-signers.apk", AndroidSdkVersion.N), Issue.V2_SIG_MAX_SIGNATURES_EXCEEDED); } @Test public void testCorrectCertUsedFromPkcs7SignedDataCertsSet() throws Exception { // Obtained by prepending the rsa-1024 certificate to the PKCS#7 SignedData certificates set // of v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-2048.apk META-INF/CERT.RSA. The certs // (in the order of appearance in the file) are thus: rsa-1024, rsa-2048. The package's // signing cert is rsa-2048. ApkVerifier.Result result = verify("v1-only-pkcs7-cert-bag-first-cert-not-used.apk"); assertVerified(result); List signingCerts = result.getSignerCertificates(); assertEquals(1, signingCerts.size()); assertEquals( "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8", HexEncoding.encode(sha256(signingCerts.get(0).getEncoded()))); } @Test public void testV1SchemeSignatureCertNotReencoded() throws Exception { // Regression test for b/30148997 and b/18228011. When PackageManager does not preserve the // original encoded form of signing certificates, bad things happen, such as rejection of // completely valid updates to apps. The issue in b/30148997 and b/18228011 was that // PackageManager started re-encoding signing certs into DER. This normally produces exactly // the original form because X.509 certificates are supposed to be DER-encoded. However, a // small fraction of Android apps uses X.509 certificates which are not DER-encoded. For // such apps, re-encoding into DER changes the serialized form of the certificate, creating // a mismatch with the serialized form stored in the PackageManager database, leading to the // rejection of updates for the app. // // v1-only-with-rsa-1024-cert-not-der.apk cert's signature is not DER-encoded. It is // BER-encoded, with length encoded as two bytes instead of just one. // v1-only-with-rsa-1024-cert-not-der.apk META-INF/CERT.RSA was obtained from // v1-only-with-rsa-1024.apk META-INF/CERT.RSA by manually modifying the ASN.1 structure. ApkVerifier.Result result = verify("v1-only-with-rsa-1024-cert-not-der.apk"); // On JDK 8u131 and newer, when the default (SUN) X.509 CertificateFactory implementation is // used, PKCS #7 signature verification fails because the certificate is not DER-encoded. // This contrived block of code disables this test in this scenario. if (!result.isVerified()) { List signers = result.getV1SchemeSigners(); if (signers.size() > 0) { ApkVerifier.Result.V1SchemeSignerInfo signer = signers.get(0); for (IssueWithParams issue : signer.getErrors()) { if (issue.getIssue() == Issue.JAR_SIG_PARSE_EXCEPTION) { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); if ("SUN".equals(certFactory.getProvider().getName())) { Throwable exception = (Throwable) issue.getParams()[1]; Throwable e = exception; while (e != null) { String msg = e.getMessage(); e = e.getCause(); if ((msg != null) && (msg.contains("Redundant length bytes found"))) { Assume.assumeNoException(exception); } } } break; } } } } assertVerified(result); List signingCerts = result.getSignerCertificates(); assertEquals(1, signingCerts.size()); assertEquals( "c5d4535a7e1c8111687a8374b2198da6f5ff8d811a7a25aa99ef060669342fa9", HexEncoding.encode(sha256(signingCerts.get(0).getEncoded()))); } @Test public void testV1SchemeSignatureCertNotReencoded2() throws Exception { // Regression test for b/30148997 and b/18228011. When PackageManager does not preserve the // original encoded form of signing certificates, bad things happen, such as rejection of // completely valid updates to apps. The issue in b/30148997 and b/18228011 was that // PackageManager started re-encoding signing certs into DER. This normally produces exactly // the original form because X.509 certificates are supposed to be DER-encoded. However, a // small fraction of Android apps uses X.509 certificates which are not DER-encoded. For // such apps, re-encoding into DER changes the serialized form of the certificate, creating // a mismatch with the serialized form stored in the PackageManager database, leading to the // rejection of updates for the app. // // v1-only-with-rsa-1024-cert-not-der2.apk cert's signature is not DER-encoded. It is // BER-encoded, with the BIT STRING value containing an extraneous leading 0x00 byte. // v1-only-with-rsa-1024-cert-not-der2.apk META-INF/CERT.RSA was obtained from // v1-only-with-rsa-1024.apk META-INF/CERT.RSA by manually modifying the ASN.1 structure. ApkVerifier.Result result = verify("v1-only-with-rsa-1024-cert-not-der2.apk"); assertVerified(result); List signingCerts = result.getSignerCertificates(); assertEquals(1, signingCerts.size()); assertEquals( "da3da398de674541313deed77218ce94798531ea5131bb9b1bb4063ba4548cfb", HexEncoding.encode(sha256(signingCerts.get(0).getEncoded()))); } @Test public void testMaxSizedZipEocdCommentAccepted() throws Exception { // Obtained by modifying apksigner to produce a max-sized (0xffff bytes long) ZIP End of // Central Directory comment, and signing the original.apk using the modified apksigner. assertVerified(verify("v1-only-max-sized-eocd-comment.apk")); assertVerified( verifyForMinSdkVersion("v2-only-max-sized-eocd-comment.apk", AndroidSdkVersion.N)); } @Test public void testEmptyApk() throws Exception { // Unsigned empty ZIP archive try { verifyForMinSdkVersion("empty-unsigned.apk", 1); fail("ApkFormatException should've been thrown"); } catch (ApkFormatException expected) { } // JAR-signed empty ZIP archive try { verifyForMinSdkVersion("v1-only-empty.apk", 18); fail("ApkFormatException should've been thrown"); } catch (ApkFormatException expected) { } // APK Signature Scheme v2 signed empty ZIP archive try { verifyForMinSdkVersion("v2-only-empty.apk", AndroidSdkVersion.N); fail("ApkFormatException should've been thrown"); } catch (ApkFormatException expected) { } // APK Signature Scheme v3 signed empty ZIP archive try { verifyForMinSdkVersion("v3-only-empty.apk", AndroidSdkVersion.P); fail("ApkFormatException should've been thrown"); } catch (ApkFormatException expected) { } } @Test public void testTargetSandboxVersion2AndHigher() throws Exception { // This APK (and its variants below) use minSdkVersion 18, meaning it needs to be signed // with v1 and v2 schemes // This APK is signed with v1 and v2 schemes and thus should verify assertVerified(verify("targetSandboxVersion-2.apk")); // v1 signature is needed only if minSdkVersion is lower than 24 assertVerificationFailure( verify("v2-only-targetSandboxVersion-2.apk"), Issue.JAR_SIG_NO_MANIFEST); assertVerified(verifyForMinSdkVersion("v2-only-targetSandboxVersion-2.apk", 24)); // v2 signature is required assertVerificationFailure( verify("v1-only-targetSandboxVersion-2.apk"), Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION); assertVerificationFailure( verify("unsigned-targetSandboxVersion-2.apk"), Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION); // minSdkVersion 28, meaning v1 signature not needed assertVerified(verify("v2-only-targetSandboxVersion-3.apk")); } @Test public void testTargetSdkMinSchemeVersionNotMet() throws Exception { // Android 11 / SDK version 30 requires apps targeting this SDK version or higher must be // signed with at least the V2 signature scheme. This test verifies if an app is targeting // this SDK version and is only signed with a V1 signature then the verifier reports the // platform will not accept it. assertVerificationFailure(verify("v1-ec-p256-targetSdk-30.apk"), Issue.MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET); } @Test public void testTargetSdkMinSchemeVersionMet() throws Exception { // This test verifies if an app is signed with the minimum required signature scheme version // for the target SDK version then the verifier reports the platform will accept it. assertVerified(verify("v2-ec-p256-targetSdk-30.apk")); // If an app is only signed with a signature scheme higher than the required version for the // target SDK the verifier should also report that the platform will accept it. assertVerified(verify("v3-ec-p256-targetSdk-30.apk")); } @Test public void testTargetSdkMinSchemeVersionNotMetMaxLessThanTarget() throws Exception { // If the minimum signature scheme for the target SDK version is not met but the maximum // SDK version is less than the target then the verifier should report that the platform // will accept it since the specified max SDK version does not know about the minimum // signature scheme requirement. verifyForMaxSdkVersion("v1-ec-p256-targetSdk-30.apk", 29); } @Test public void testTargetSdkNoUsesSdkElement() throws Exception { // The target SDK minimum signature scheme version check will attempt to obtain the // targetSdkVersion attribute value from the uses-sdk element in the AndroidManifest. If // the targetSdkVersion is not specified then the verifier should behave the same as the // platform; the minSdkVersion should be used when available and when neither the minimum or // target SDK are specified a default value of 1 should be used. This test verifies that the // verifier does not fail when the uses-sdk element is not specified. verify("v1-only-no-uses-sdk.apk"); } @Test public void testV1MultipleDigestAlgsInManifestAndSignatureFile() throws Exception { // MANIFEST.MF contains SHA-1 and SHA-256 digests for each entry, .SF contains only SHA-1 // digests. This file was obtained by: // jarsigner -sigalg SHA256withRSA -digestalg SHA-256 ... ... // jarsigner -sigalg SHA1withRSA -digestalg SHA1 ... ... assertVerified(verify("v1-sha1-sha256-manifest-and-sha1-sf.apk")); // MANIFEST.MF and .SF contain SHA-1 and SHA-256 digests for each entry. This file was // obtained by modifying apksigner to output multiple digests. assertVerified(verify("v1-sha1-sha256-manifest-and-sf.apk")); // One of the digests is wrong in either MANIFEST.MF or .SF. These files were obtained by // modifying apksigner to output multiple digests and to flip a bit to create a wrong // digest. // SHA-1 digests in MANIFEST.MF are wrong, but SHA-256 digests are OK. // The APK will fail to verify on API Level 17 and lower, but will verify on API Level 18 // and higher. assertVerificationFailure( verify("v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk"), Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY); assertVerificationFailure( verifyForMaxSdkVersion( "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", 17), Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY); assertVerified( verifyForMinSdkVersion( "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", 18)); // SHA-1 digests in .SF are wrong, but SHA-256 digests are OK. // The APK will fail to verify on API Level 17 and lower, but will verify on API Level 18 // and higher. assertVerificationFailure( verify("v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk"), Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY); assertVerificationFailure( verifyForMaxSdkVersion( "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", 17), Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY); assertVerified( verifyForMinSdkVersion( "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", 18)); // SHA-256 digests in MANIFEST.MF are wrong, but SHA-1 digests are OK. // The APK will fail to verify on API Level 18 and higher, but will verify on API Level 17 // and lower. assertVerificationFailure( verify("v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk"), Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY); assertVerificationFailure( verifyForMinSdkVersion( "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", 18), Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY); assertVerified( verifyForMaxSdkVersion( "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", 17)); // SHA-256 digests in .SF are wrong, but SHA-1 digests are OK. // The APK will fail to verify on API Level 18 and higher, but will verify on API Level 17 // and lower. assertVerificationFailure( verify("v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk"), Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY); assertVerificationFailure( verifyForMinSdkVersion( "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", 18), Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY); assertVerified( verifyForMaxSdkVersion( "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", 17)); } @Test public void testV1WithUnsupportedCharacterInZipEntryName() throws Exception { // Android Package Manager does not support ZIP entry names containing CR or LF assertVerificationFailure( verify("v1-only-with-cr-in-entry-name.apk"), Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION); assertVerificationFailure( verify("v1-only-with-lf-in-entry-name.apk"), Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION); } @Test public void testWeirdZipCompressionMethod() throws Exception { // Any ZIP compression method other than STORED is treated as DEFLATED by Android. // This APK declares compression method 21 (neither STORED nor DEFLATED) for CERT.RSA entry, // but the entry is actually Deflate-compressed. assertVerified(verify("weird-compression-method.apk")); } @Test public void testZipCompressionMethodMismatchBetweenLfhAndCd() throws Exception { // Android Package Manager ignores compressionMethod field in Local File Header and always // uses the compressionMethod from Central Directory instead. // In this APK, compression method of CERT.RSA is declared as STORED in Local File Header // and as DEFLATED in Central Directory. The entry is actually Deflate-compressed. assertVerified(verify("mismatched-compression-method.apk")); } @Test public void testV1SignedAttrs() throws Exception { String apk = "v1-only-with-signed-attrs.apk"; assertVerificationFailure( verifyForMinSdkVersion(apk, AndroidSdkVersion.JELLY_BEAN_MR2), Issue.JAR_SIG_VERIFY_EXCEPTION); assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.KITKAT)); apk = "v1-only-with-signed-attrs-signerInfo1-good-signerInfo2-good.apk"; assertVerificationFailure( verifyForMinSdkVersion(apk, AndroidSdkVersion.JELLY_BEAN_MR2), Issue.JAR_SIG_VERIFY_EXCEPTION); assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.KITKAT)); } @Test public void testV1SignedAttrsNotInDerOrder() throws Exception { // Android does not re-order SignedAttributes despite it being a SET OF. Pre-N, Android // treated them as SEQUENCE OF, meaning no re-ordering is necessary. From N onwards, it // treats them as SET OF, but does not re-encode into SET OF during verification if all // attributes parsed fine. assertVerified(verify("v1-only-with-signed-attrs-wrong-order.apk")); assertVerified( verify("v1-only-with-signed-attrs-signerInfo1-wrong-order-signerInfo2-good.apk")); } @Test public void testV1SignedAttrsMissingContentType() throws Exception { // SignedAttributes must contain ContentType. Pre-N, Android ignores this requirement. // Android N onwards rejects such APKs. String apk = "v1-only-with-signed-attrs-missing-content-type.apk"; assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1)); assertVerificationFailure(verify(apk), Issue.JAR_SIG_VERIFY_EXCEPTION); // Assert that this issue fails verification of the entire signature block, rather than // skipping the broken SignerInfo. The second signer info SignerInfo verifies fine, but // verification does not get there. apk = "v1-only-with-signed-attrs-signerInfo1-missing-content-type-signerInfo2-good.apk"; assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1)); assertVerificationFailure(verify(apk), Issue.JAR_SIG_VERIFY_EXCEPTION); } @Test public void testV1SignedAttrsWrongContentType() throws Exception { // ContentType of SignedAttributes must equal SignedData.encapContentInfo.eContentType. // Pre-N, Android ignores this requirement. // From N onwards, Android rejects such SignerInfos. String apk = "v1-only-with-signed-attrs-wrong-content-type.apk"; assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1)); assertVerificationFailure(verify(apk), Issue.JAR_SIG_DID_NOT_VERIFY); // First SignerInfo does not verify on Android N and newer, but verification moves on to the // second SignerInfo, which verifies. apk = "v1-only-with-signed-attrs-signerInfo1-wrong-content-type-signerInfo2-good.apk"; assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1)); assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.N)); // Although the APK's signature verifies on pre-N and N+, we reject such APKs because the // APK's verification results in different verified SignerInfos (and thus potentially // different signing certs) between pre-N and N+. assertVerificationFailure(verify(apk), Issue.JAR_SIG_DID_NOT_VERIFY); } @Test public void testV1SignedAttrsMissingDigest() throws Exception { // Content digest must be present in SignedAttributes String apk = "v1-only-with-signed-attrs-missing-digest.apk"; assertVerificationFailure( verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_VERIFY_EXCEPTION); assertVerificationFailure( verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_VERIFY_EXCEPTION); // Assert that this issue fails verification of the entire signature block, rather than // skipping the broken SignerInfo. The second signer info SignerInfo verifies fine, but // verification does not get there. apk = "v1-only-with-signed-attrs-signerInfo1-missing-digest-signerInfo2-good.apk"; assertVerificationFailure( verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_VERIFY_EXCEPTION); assertVerificationFailure( verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_VERIFY_EXCEPTION); } @Test public void testV1SignedAttrsMultipleGoodDigests() throws Exception { // Only one content digest must be present in SignedAttributes String apk = "v1-only-with-signed-attrs-multiple-good-digests.apk"; assertVerificationFailure( verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_PARSE_EXCEPTION); assertVerificationFailure( verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_PARSE_EXCEPTION); // Assert that this issue fails verification of the entire signature block, rather than // skipping the broken SignerInfo. The second signer info SignerInfo verifies fine, but // verification does not get there. apk = "v1-only-with-signed-attrs-signerInfo1-multiple-good-digests-signerInfo2-good.apk"; assertVerificationFailure( verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_PARSE_EXCEPTION); assertVerificationFailure( verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_PARSE_EXCEPTION); } @Test public void testV1SignedAttrsWrongDigest() throws Exception { // Content digest in SignedAttributes does not match the contents String apk = "v1-only-with-signed-attrs-wrong-digest.apk"; assertVerificationFailure( verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY); assertVerificationFailure( verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_DID_NOT_VERIFY); // First SignerInfo does not verify, but Android N and newer moves on to the second // SignerInfo, which verifies. apk = "v1-only-with-signed-attrs-signerInfo1-wrong-digest-signerInfo2-good.apk"; assertVerificationFailure( verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY); assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.N)); } @Test public void testV1SignedAttrsWrongSignature() throws Exception { // Signature over SignedAttributes does not verify String apk = "v1-only-with-signed-attrs-wrong-signature.apk"; assertVerificationFailure( verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY); assertVerificationFailure( verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_DID_NOT_VERIFY); // First SignerInfo does not verify, but Android N and newer moves on to the second // SignerInfo, which verifies. apk = "v1-only-with-signed-attrs-signerInfo1-wrong-signature-signerInfo2-good.apk"; assertVerificationFailure( verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY); assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.N)); } @Test public void testSourceStampBlock_correctSignature() throws Exception { ApkVerifier.Result verificationResult = verify("valid-stamp.apk"); // Verifies the signature of the APK. assertVerified(verificationResult); // Verifies the signature of source stamp. assertTrue(verificationResult.isSourceStampVerified()); } @Test public void verifySourceStamp_correctSignature() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("valid-stamp.apk"); // Since the API is only verifying the source stamp the result itself should be marked as // verified. assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); // The source stamp can also be verified by platform version; confirm the verification works // using just the max signature scheme version supported by that platform version. verificationResult = verifySourceStamp("valid-stamp.apk", 18, 18); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); verificationResult = verifySourceStamp("valid-stamp.apk", 24, 24); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); verificationResult = verifySourceStamp("valid-stamp.apk", 28, 28); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); } @Test public void testSourceStampBlock_signatureMissing() throws Exception { ApkVerifier.Result verificationResult = verify("stamp-without-block.apk"); // A broken stamp should not block a signing scheme verified APK. assertVerified(verificationResult); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_SIG_MISSING); } @Test public void verifySourceStamp_signatureMissing() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("stamp-without-block.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_NOT_VERIFIED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_SIG_MISSING); } @Test public void testSourceStampBlock_certificateMismatch() throws Exception { ApkVerifier.Result verificationResult = verify("stamp-certificate-mismatch.apk"); // A broken stamp should not block a signing scheme verified APK. assertVerified(verificationResult); assertSourceStampVerificationFailure( verificationResult, Issue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK); } @Test public void verifySourceStamp_certificateMismatch() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("stamp-certificate-mismatch.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure( verificationResult, Issue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK); } @Test public void testSourceStampBlock_v1OnlySignatureValidStamp() throws Exception { ApkVerifier.Result verificationResult = verify("v1-only-with-stamp.apk"); assertVerified(verificationResult); assertTrue(verificationResult.isSourceStampVerified()); } @Test public void verifySourceStamp_v1OnlySignatureValidStamp() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("v1-only-with-stamp.apk"); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); // Confirm that the source stamp verification succeeds when specifying platform versions // that supported later signature scheme versions. verificationResult = verifySourceStamp("v1-only-with-stamp.apk", 28, 28); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); verificationResult = verifySourceStamp("v1-only-with-stamp.apk", 24, 24); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); } @Test public void testSourceStampBlock_v2OnlySignatureValidStamp() throws Exception { ApkVerifier.Result verificationResult = verify("v2-only-with-stamp.apk"); assertVerified(verificationResult); assertTrue(verificationResult.isSourceStampVerified()); } @Test public void verifySourceStamp_v2OnlySignatureValidStamp() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("v2-only-with-stamp.apk"); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); // Confirm that the source stamp verification succeeds when specifying a platform version // that supports a later signature scheme version. verificationResult = verifySourceStamp("v2-only-with-stamp.apk", 28, 28); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); } @Test public void testSourceStampBlock_v3OnlySignatureValidStamp() throws Exception { ApkVerifier.Result verificationResult = verify("v3-only-with-stamp.apk"); assertVerified(verificationResult); assertTrue(verificationResult.isSourceStampVerified()); } @Test public void verifySourceStamp_v3OnlySignatureValidStamp() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk"); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); } @Test public void testSourceStampBlock_apkHashMismatch_v1SignatureScheme() throws Exception { ApkVerifier.Result verificationResult = verify("stamp-apk-hash-mismatch-v1.apk"); // A broken stamp should not block a signing scheme verified APK. assertVerified(verificationResult); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void verifySourceStamp_apkHashMismatch_v1SignatureScheme() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("stamp-apk-hash-mismatch-v1.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void testSourceStampBlock_apkHashMismatch_v2SignatureScheme() throws Exception { ApkVerifier.Result verificationResult = verify("stamp-apk-hash-mismatch-v2.apk"); // A broken stamp should not block a signing scheme verified APK. assertVerified(verificationResult); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void verifySourceStamp_apkHashMismatch_v2SignatureScheme() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("stamp-apk-hash-mismatch-v2.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void testSourceStampBlock_apkHashMismatch_v3SignatureScheme() throws Exception { ApkVerifier.Result verificationResult = verify("stamp-apk-hash-mismatch-v3.apk"); // A broken stamp should not block a signing scheme verified APK. assertVerified(verificationResult); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void verifySourceStamp_apkHashMismatch_v3SignatureScheme() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("stamp-apk-hash-mismatch-v3.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void testSourceStampBlock_malformedSignature() throws Exception { ApkVerifier.Result verificationResult = verify("stamp-malformed-signature.apk"); // A broken stamp should not block a signing scheme verified APK. assertVerified(verificationResult); assertSourceStampVerificationFailure( verificationResult, Issue.SOURCE_STAMP_MALFORMED_SIGNATURE); } @Test public void verifySourceStamp_malformedSignature() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("stamp-malformed-signature.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure( verificationResult, Issue.SOURCE_STAMP_MALFORMED_SIGNATURE); } @Test public void verifySourceStamp_expectedDigestMatchesActual() throws Exception { // The ApkVerifier provides an API to specify the expected certificate digest; this test // verifies that the test runs through to completion when the actual digest matches the // provided value. ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", RSA_2048_CERT_SHA256_DIGEST); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); } @Test public void verifySourceStamp_expectedDigestMismatch() throws Exception { // If the caller requests source stamp verification with an expected cert digest that does // not match the actual digest in the APK the verifier should report the mismatch. ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", EC_P256_CERT_SHA256_DIGEST); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.CERT_DIGEST_MISMATCH); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH); } @Test public void verifySourceStamp_validStampLineage() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("stamp-lineage-valid.apk"); assertVerified(verificationResult); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFIED); } @Test public void verifySourceStamp_invalidStampLineage() throws Exception { ApkVerifier.Result verificationResult = verifySourceStamp("stamp-lineage-invalid.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_POR_CERT_MISMATCH); } @Test public void verifySourceStamp_noTimestamp_returnsDefaultValue() throws Exception { // A timestamp attribute was added to the source stamp, but verification of APKs that were // generated prior to the addition of the timestamp should still complete successfully, // returning a default value of 0 for the timestamp. ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk"); assertTrue(verificationResult.isSourceStampVerified()); assertEquals( "A value of 0 should be returned for the timestamp when the attribute is not " + "present", 0, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); } @Test public void verifySourceStamp_validTimestamp_returnsExpectedValue() throws Exception { // Once an APK is signed with a source stamp that contains a valid value for the timestamp // attribute, verification of the source stamp should result in the same value for the // timestamp returned to the verifier. ApkVerifier.Result verificationResult = verifySourceStamp( "stamp-valid-timestamp-value.apk"); assertTrue(verificationResult.isSourceStampVerified()); assertEquals(1644886584, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); } @Test public void verifySourceStamp_validTimestampLargerBuffer_returnsExpectedValue() throws Exception { // The source stamp timestamp attribute value is expected to be written to an 8 byte buffer // as a little-endian long; while a larger buffer will not result in an error, any // additional space after the buffer's initial 8 bytes will be ignored. This test verifies a // valid timestamp value written to the first 8 bytes of a 16 byte buffer can still be read // successfully. ApkVerifier.Result verificationResult = verifySourceStamp( "stamp-valid-timestamp-16-byte-buffer.apk"); assertTrue(verificationResult.isSourceStampVerified()); assertEquals(1645126786, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); } @Test public void verifySourceStamp_invalidTimestampValueEqualsZero_verificationFails() throws Exception { // If the source stamp timestamp attribute exists and is <= 0, then a warning should be // reported to notify the caller to the invalid attribute value. This test verifies a // a warning is reported when the timestamp attribute value is 0. ApkVerifier.Result verificationResult = verifySourceStamp( "stamp-invalid-timestamp-value-zero.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_INVALID_TIMESTAMP); } @Test public void verifySourceStamp_invalidTimestampValueLessThanZero_verificationFails() throws Exception { // If the source stamp timestamp attribute exists and is <= 0, then a warning should be // reported to notify the caller to the invalid attribute value. This test verifies a // a warning is reported when the timestamp attribute value is < 0. ApkVerifier.Result verificationResult = verifySourceStamp( "stamp-invalid-timestamp-value-less-than-zero.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_INVALID_TIMESTAMP); } @Test public void verifySourceStamp_invalidTimestampZeroInFirst8BytesOfBuffer_verificationFails() throws Exception { // The source stamp's timestamp attribute value is expected to be written to the first 8 // bytes of the attribute's value buffer; if a larger buffer is used and the timestamp // value is not written as a little-endian long to the first 8 bytes of the buffer, then // an error should be reported for the timestamp attribute since the rest of the buffer will // be ignored. ApkVerifier.Result verificationResult = verifySourceStamp( "stamp-timestamp-in-last-8-of-16-byte-buffer.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_INVALID_TIMESTAMP); } @Test public void verifySourceStamp_intTimestampValue_verificationFails() throws Exception { // Since the source stamp timestamp attribute value is a long, an attribute value with // insufficient space to hold a long value should result in a warning reported to the user. ApkVerifier.Result verificationResult = verifySourceStamp( "stamp-int-timestamp-value.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_MALFORMED_ATTRIBUTE); } @Test public void verifySourceStamp_modifiedTimestampValue_verificationFails() throws Exception { // The source stamp timestamp attribute is part of the block's signed data; this test // verifies if the value of the timestamp in the stamp block is modified then verification // of the source stamp should fail. ApkVerifier.Result verificationResult = verifySourceStamp( "stamp-valid-timestamp-value-modified.apk"); assertSourceStampVerificationStatus(verificationResult, SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void apkVerificationIssueAdapter_verifyAllBaseIssuesMapped() throws Exception { Field[] fields = ApkVerificationIssue.class.getFields(); StringBuilder msg = new StringBuilder(); for (Field field : fields) { // All public static int fields in the ApkVerificationIssue class should be issue IDs; // if any are added that are not intended as IDs a filter set should be applied to this // test. if (Modifier.isStatic(field.getModifiers()) && field.getType() == int.class) { if (!ApkVerifier.ApkVerificationIssueAdapter .sVerificationIssueIdToIssue.containsKey(field.get(null))) { if (msg.length() > 0) { msg.append('\n'); } msg.append( "A mapping is required from ApkVerificationIssue." + field.getName() + " to an ApkVerifier.Issue in ApkVerificationIssueAdapter"); } } } if (msg.length() > 0) { fail(msg.toString()); } } @Test public void verifySignature_negativeModulusConscryptProvider() throws Exception { Provider conscryptProvider = null; try { conscryptProvider = new org.conscrypt.OpenSSLProvider(); Security.insertProviderAt(conscryptProvider, 1); assertVerified(verify("v1v2v3-rsa-2048-negmod-in-cert.apk")); } catch (UnsatisfiedLinkError e) { // If the library for conscrypt is not available then skip this test. return; } finally { if (conscryptProvider != null) { Security.removeProvider(conscryptProvider.getName()); } } } @Test public void verifyV31_rotationTarget34_containsExpectedSigners() throws Exception { // This test verifies an APK targeting a specific SDK version for rotation properly reports // that version for the rotated signer in the v3.1 block, and all other signing blocks // use the original signing key. The target is set to 10000 to prevent test failures when // SDK version 34 is set as the development release. ApkVerifier.Result result = verify("v31-rsa-2048_2-tgt-10000-1-tgt-28.apk"); assertVerified(result); assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 10000); } @Test public void verifyV31_missingStrippingAttr_warningReported() throws Exception { // The v3.1 signing block supports targeting SDK versions; to protect against these target // versions being modified the v3 signer contains a stripping protection attribute with the // SDK version on which rotation should be applied. This test verifies a warning is reported // when this attribute is not present in the v3 signer. ApkVerifier.Result result = verify("v31-tgt-33-no-v3-attr.apk"); assertVerificationWarning(result, Issue.V31_ROTATION_MIN_SDK_ATTR_MISSING); } @Test public void verifyV31_strippingAttrMismatch_errorReportedOnSupportedVersions() throws Exception { // This test verifies if the stripping protection attribute does not properly match the // minimum SDK version on which rotation is supported then the APK should fail verification. ApkVerifier.Result result = verify("v31-tgt-34-v3-attr-value-33.apk"); assertVerificationFailure(result, Issue.V31_ROTATION_MIN_SDK_MISMATCH); // SDK versions that do not support v3.1 should ignore the stripping protection attribute // and the v3.1 signing block. result = verifyForMaxSdkVersion("v31-tgt-34-v3-attr-value-33.apk", V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT - 1); assertVerified(result); } @Test public void verifyV31_missingV31Block_errorReportedOnSupportedVersions() throws Exception { // This test verifies if the stripping protection attribute contains a value for rotation // but a v3.1 signing block was not found then the APK should fail verification. ApkVerifier.Result result = verify("v31-block-stripped-v3-attr-value-33.apk"); assertVerificationFailure(result, Issue.V31_BLOCK_MISSING); // SDK versions that do not support v3.1 should ignore the stripping protection attribute // and the v3.1 signing block. result = verifyForMaxSdkVersion("v31-block-stripped-v3-attr-value-33.apk", V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT - 1); assertVerified(result); } @Test public void verifyV31_v31BlockWithoutV3Block_reportsError() throws Exception { // A v3.1 block must always exist alongside a v3.0 block; if an APK's minSdkVersion is the // same as the version supporting rotation then it should be written to a v3.0 block. ApkVerifier.Result result = verify("v31-tgt-33-no-v3-block.apk"); assertVerificationFailure(result, Issue.V31_BLOCK_FOUND_WITHOUT_V3_BLOCK); } @Test public void verifyV31_rotationTargetsDevRelease_resultReportsDevReleaseFlag() throws Exception { // Development releases use the SDK version of the previous release until the SDK is // finalized. In order to only target the development release and later, the v3.1 signature // scheme supports targeting development releases such that the SDK version X will install // on a device running X with the system property ro.build.version.codename set to a new // development codename (eg T); a release platform will have this set to "REL", and the // platform will ignore the v3.1 signer if the minSdkVersion is X and the codename is "REL". // The target is set to 10000 to prevent test failures when SDK version 34 is set as the // development release. ApkVerifier.Result result = verify("v31-rsa-2048_2-tgt-10000-dev-release.apk"); assertVerified(result); assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 10000); assertEquals(1, result.getV31SchemeSigners().size()); assertTrue(result.getV31SchemeSigners().get(0).getRotationTargetsDevRelease()); assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void verifyV3_v3RotatedSignerTargetsDevRelease_warningReported() throws Exception { // While a v3.1 signer can target a development release, v3.0 does not support the same // attribute since it is only intended for v3.1 with v3.0 using the original signer. This // test verifies a warning is reported if an APK has this flag set on a v3.0 signer since it // will be ignored by the platform. ApkVerifier.Result result = verify("v3-rsa-2048_2-tgt-dev-release.apk"); assertVerificationWarning(result, Issue.V31_ROTATION_TARGETS_DEV_RELEASE_ATTR_ON_V3_SIGNER); } @Test public void verifyV31_rotationTargets34_resultContainsExpectedLineage() throws Exception { // During verification of the v3.1 and v3.0 signing blocks, ApkVerifier will set the // signing certificate lineage in the Result object; this test verifies a null lineage from // a v3.0 signer does not overwrite a valid lineage from a v3.1 signer. ApkVerifier.Result result = verify("v31-rsa-2048_2-tgt-34-1-tgt-28.apk"); assertNotNull(result.getSigningCertificateLineage()); SigningCertificateLineageTest.assertLineageContainsExpectedSigners( result.getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void verify31_minSdkVersionT_resultSuccessfullyVerified() throws Exception { // When a min-sdk-version of 33 is explicitly specified, apksig will behave the same as a // device running this API level and only verify a v3.1 signature if it exists. This test // verifies this v3.1 signature is sufficient to report the APK as verified. ApkVerifier.Result result = verifyForMinSdkVersion("v31-rsa-2048_2-tgt-33-1-tgt-28.apk", 33); assertVerified(result); assertTrue(result.isVerifiedUsingV31Scheme()); } @Test public void verify31_minSdkVersionTTargetSdk30_resultSuccessfullyVerified() throws Exception { // This test verifies when a min-sdk-version of 33 is specified and the APK targets API // level 30 or later, the v3.1 signature is sufficient to report the APK meets the // requirement of a minimum v2 signature. ApkVerifier.Result result = verifyForMinSdkVersion( "v31-ec-p256-2-tgt-33-1-tgt-28-targetSdk-30.apk", 33); assertVerified(result); assertTrue(result.isVerifiedUsingV31Scheme()); } @Test public void verify41_v41DigestMismatchedWithV31_reportsError() throws Exception { // This test verifies a digest mismatch between the v4.1 signature and the v3.1 signature // is properly reported during v4 signature verification. ApkVerifier.Result result = verifyWithV4Signature("v41-digest-mismatched-with-v31.apk", "v41-digest-mismatched-with-v31.apk.idsig"); assertVerificationFailure(result, Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH); } @Test(expected = IOException.class) public void verify_largeFileSize_doesNotFailWithOOMError() throws Exception { // TODO(b/319479290) make the test run with a specific max heap size assumeTrue(Runtime.getRuntime().maxMemory() < 2016310387L); // 2gb // During V1 signature verification, each file needs to be uncompressed to calculate // its digest; the verifier uses the file size from the central directory record to // determine the size of the byte[] to allocate. If there is not sufficient memory // in the heap for the allocation, the verification should fail with an exception // instead of an OutOfMemoryError. This test uses an APK where the size of the // MANIFEST.MF is reported as 2016310387. verify("incorrect-manifest-size.apk"); } @Test(expected = ApkFormatException.class) public void verify_invalidApk_throwsApkFormatException() throws Exception { // This is just some random bytes and thus an invalid manifest verify("invalid_manifest.apk"); } @Test public void compareMatchingDigests() throws Exception { Map firstDigest = new HashMap<>(); firstDigest.put(ContentDigestAlgorithm.SHA256, RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); Map secondDigest = new HashMap<>(); secondDigest.put(ContentDigestAlgorithm.SHA256, RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); secondDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); assertTrue(ApkVerifier.compareDigests(firstDigest, secondDigest)); } @Test public void compareMatchingIntersectionDigests() throws Exception { Map firstDigest = new HashMap<>(); firstDigest.put(ContentDigestAlgorithm.SHA256, RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); Map secondDigest = new HashMap<>(); secondDigest.put(ContentDigestAlgorithm.SHA256, RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); secondDigest.put(ContentDigestAlgorithm.VERITY_CHUNKED_SHA256, RSA_2048_CHUNKED_SHA256_DIGEST_FROM_INCORRECTLY_SIGNED_APK .getBytes(StandardCharsets.UTF_8)); assertTrue(ApkVerifier.compareDigests(firstDigest, secondDigest)); } @Test public void compareNoIntersectionDigests() throws Exception { Map firstDigest = new HashMap<>(); firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); Map secondDigest = new HashMap<>(); secondDigest.put(ContentDigestAlgorithm.SHA256, RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); assertTrue(!ApkVerifier.compareDigests(firstDigest, secondDigest)); } @Test public void compareNotMatchingDigests() throws Exception { Map firstDigest = new HashMap<>(); firstDigest.put(ContentDigestAlgorithm.SHA256, RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); Map secondDigest = new HashMap<>(); secondDigest.put(ContentDigestAlgorithm.SHA256, RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); secondDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); assertTrue(!ApkVerifier.compareDigests(firstDigest, secondDigest)); } @Test public void comparePartiallyNotMatchingDigests() throws Exception { Map firstDigest = new HashMap<>(); firstDigest.put(ContentDigestAlgorithm.SHA256, RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); Map secondDigest = new HashMap<>(); secondDigest.put(ContentDigestAlgorithm.SHA256, RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); secondDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, RSA_2048_CHUNKED_SHA256_DIGEST_FROM_INCORRECTLY_SIGNED_APK .getBytes(StandardCharsets.UTF_8)); assertTrue(!ApkVerifier.compareDigests(firstDigest, secondDigest)); } private ApkVerifier.Result verify(String apkFilenameInResources) throws IOException, ApkFormatException, NoSuchAlgorithmException { return verify(apkFilenameInResources, null, null); } private ApkVerifier.Result verifyForMinSdkVersion( String apkFilenameInResources, int minSdkVersion) throws IOException, ApkFormatException, NoSuchAlgorithmException { return verify(apkFilenameInResources, minSdkVersion, null); } private ApkVerifier.Result verifyForMaxSdkVersion( String apkFilenameInResources, int maxSdkVersion) throws IOException, ApkFormatException, NoSuchAlgorithmException { return verify(apkFilenameInResources, null, maxSdkVersion); } private ApkVerifier.Result verify( String apkFilenameInResources, Integer minSdkVersionOverride, Integer maxSdkVersionOverride) throws IOException, ApkFormatException, NoSuchAlgorithmException { byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources); ApkVerifier.Builder builder = new ApkVerifier.Builder(DataSources.asDataSource(ByteBuffer.wrap(apkBytes))); if (minSdkVersionOverride != null) { builder.setMinCheckedPlatformVersion(minSdkVersionOverride); } if (maxSdkVersionOverride != null) { builder.setMaxCheckedPlatformVersion(maxSdkVersionOverride); } return builder.build().verify(); } private ApkVerifier.Result verifyWithV4Signature( String apkFilenameInResources, String v4SignatureFile) throws IOException, ApkFormatException, NoSuchAlgorithmException, URISyntaxException { byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources); ApkVerifier.Builder builder = new ApkVerifier.Builder(DataSources.asDataSource(ByteBuffer.wrap(apkBytes))); if (v4SignatureFile != null) { builder.setV4SignatureFile( Resources.toFile(getClass(), v4SignatureFile, mTemporaryFolder)); } return builder.build().verify(); } private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources) throws Exception { return verifySourceStamp(apkFilenameInResources, null, null, null); } private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources, String expectedCertDigest) throws Exception { return verifySourceStamp(apkFilenameInResources, expectedCertDigest, null, null); } private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources, Integer minSdkVersionOverride, Integer maxSdkVersionOverride) throws Exception { return verifySourceStamp(apkFilenameInResources, null, minSdkVersionOverride, maxSdkVersionOverride); } private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources, String expectedCertDigest, Integer minSdkVersionOverride, Integer maxSdkVersionOverride) throws Exception { byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources); ApkVerifier.Builder builder = new ApkVerifier.Builder( DataSources.asDataSource(ByteBuffer.wrap(apkBytes))); if (minSdkVersionOverride != null) { builder.setMinCheckedPlatformVersion(minSdkVersionOverride); } if (maxSdkVersionOverride != null) { builder.setMaxCheckedPlatformVersion(maxSdkVersionOverride); } return builder.build().verifySourceStamp(expectedCertDigest); } static void assertVerified(ApkVerifier.Result result) { assertVerified(result, "APK"); } static void assertVerified(ApkVerifier.Result result, String apkId) { if (result.isVerified()) { return; } StringBuilder msg = new StringBuilder(); for (IssueWithParams issue : result.getErrors()) { if (msg.length() > 0) { msg.append('\n'); } msg.append(issue); } for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) { String signerName = signer.getName(); for (IssueWithParams issue : signer.getErrors()) { if (msg.length() > 0) { msg.append('\n'); } msg.append("JAR signer ") .append(signerName) .append(": ") .append(issue.getIssue()) .append(": ") .append(issue); } } for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); for (IssueWithParams issue : signer.getErrors()) { if (msg.length() > 0) { msg.append('\n'); } msg.append("APK Signature Scheme v2 signer ") .append(signerName) .append(": ") .append(issue.getIssue()) .append(": ") .append(issue); } } for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); for (IssueWithParams issue : signer.getErrors()) { if (msg.length() > 0) { msg.append('\n'); } msg.append("APK Signature Scheme v3 signer ") .append(signerName) .append(": ") .append(issue.getIssue()) .append(": ") .append(issue); } } for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); for (IssueWithParams issue : signer.getErrors()) { if (msg.length() > 0) { msg.append('\n'); } msg.append("APK Signature Scheme v3.1 signer ") .append(signerName) .append(": ") .append(issue.getIssue()) .append(": ") .append(issue); } } fail(apkId + " did not verify: " + msg); } private void assertVerified( String apkFilenameInResources, Integer minSdkVersionOverride, Integer maxSdkVersionOverride) throws Exception { assertVerified( verify(apkFilenameInResources, minSdkVersionOverride, maxSdkVersionOverride), apkFilenameInResources); } static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) { assertVerificationIssue(result, true, expectedIssue); } static void assertVerificationWarning(ApkVerifier.Result result, Issue expectedIssue) { assertVerificationIssue(result, false, expectedIssue); } /** * Asserts the provided {@code result} contains one of the {@code expectedIssues}; if {@code * verifyError} is set to {@code true} then the specified {@link Issue} will be expected as an * error, otherwise it will be expected as a warning. */ private static void assertVerificationIssue(ApkVerifier.Result result, boolean verifyError, Issue... expectedIssues) { List expectedIssuesList = expectedIssues != null ? Arrays.asList(expectedIssues) : Collections.emptyList(); if (result.isVerified() && verifyError) { fail("APK verification succeeded instead of failing with " + expectedIssuesList); return; } StringBuilder msg = new StringBuilder(); for (IssueWithParams issue : (verifyError ? result.getErrors() : result.getWarnings())) { if (expectedIssuesList.contains(issue.getIssue())) { return; } if (msg.length() > 0) { msg.append('\n'); } msg.append(issue); } for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) { String signerName = signer.getName(); for (ApkVerifier.IssueWithParams issue : (verifyError ? signer.getErrors() : signer.getWarnings())) { if (expectedIssuesList.contains(issue.getIssue())) { return; } if (msg.length() > 0) { msg.append('\n'); } msg.append("JAR signer ") .append(signerName) .append(": ") .append(issue.getIssue()) .append(" ") .append(issue); } } for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); for (IssueWithParams issue : (verifyError ? signer.getErrors() : signer.getWarnings())) { if (expectedIssuesList.contains(issue.getIssue())) { return; } if (msg.length() > 0) { msg.append('\n'); } msg.append("APK Signature Scheme v2 signer ") .append(signerName) .append(": ") .append(issue); } } for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); for (IssueWithParams issue : (verifyError ? signer.getErrors() : signer.getWarnings())) { if (expectedIssuesList.contains(issue.getIssue())) { return; } if (msg.length() > 0) { msg.append('\n'); } msg.append("APK Signature Scheme v3 signer ") .append(signerName) .append(": ") .append(issue); } } for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { String signerName = "signer #" + (signer.getIndex() + 1); for (IssueWithParams issue : (verifyError ? signer.getErrors() : signer.getWarnings())) { if (expectedIssuesList.contains(issue.getIssue())) { return; } if (msg.length() > 0) { msg.append('\n'); } msg.append("APK Signature Scheme v3.1 signer ") .append(signerName) .append(": ") .append(issue); } } if ((expectedIssuesList.isEmpty() || expectedIssues[0] == null) && msg.length() == 0) { return; } fail( "APK failed verification for the wrong reason" + ". Expected: " + expectedIssuesList + ", actual: " + msg); } private static void assertSourceStampVerificationFailure( ApkVerifier.Result result, Issue expectedIssue) { if (result.isSourceStampVerified()) { fail( "APK source stamp verification succeeded instead of failing with " + expectedIssue); return; } StringBuilder msg = new StringBuilder(); List resultIssueWithParams = Stream.of(result.getErrors(), result.getWarnings()) .filter(Objects::nonNull) .flatMap(Collection::stream) .collect(Collectors.toList()); for (IssueWithParams issue : resultIssueWithParams) { if (expectedIssue.equals(issue.getIssue())) { return; } if (msg.length() > 0) { msg.append('\n'); } msg.append(issue); } ApkVerifier.Result.SourceStampInfo signer = result.getSourceStampInfo(); if (signer != null) { List sourceStampIssueWithParams = Stream.of(signer.getErrors(), signer.getWarnings()) .filter(Objects::nonNull) .flatMap(Collection::stream) .collect(Collectors.toList()); for (IssueWithParams issue : sourceStampIssueWithParams) { if (expectedIssue.equals(issue.getIssue())) { return; } if (msg.length() > 0) { msg.append('\n'); } msg.append("APK SourceStamp signer").append(": ").append(issue); } } fail( "APK source stamp failed verification for the wrong reason" + ". Expected: " + expectedIssue + ", actual: " + msg); } private static void assertSourceStampVerificationStatus(ApkVerifier.Result result, SourceStampVerificationStatus verificationStatus) throws Exception { assertEquals(verificationStatus, result.getSourceStampInfo().getSourceStampVerificationStatus()); } private void assertVerificationFailure( String apkFilenameInResources, ApkVerifier.Issue expectedIssue) throws Exception { assertVerificationFailure(verify(apkFilenameInResources), expectedIssue); } private void assertVerifiedForEach(String apkFilenamePatternInResources, String[] args) throws Exception { assertVerifiedForEach(apkFilenamePatternInResources, args, null, null); } private void assertVerifiedForEach( String apkFilenamePatternInResources, String[] args, Integer minSdkVersionOverride, Integer maxSdkVersionOverride) throws Exception { for (String arg : args) { String apkFilenameInResources = String.format(Locale.US, apkFilenamePatternInResources, arg); assertVerified(apkFilenameInResources, minSdkVersionOverride, maxSdkVersionOverride); } } private void assertVerifiedForEachForMinSdkVersion( String apkFilenameInResources, String[] args, int minSdkVersion) throws Exception { assertVerifiedForEach(apkFilenameInResources, args, minSdkVersion, null); } private static byte[] sha256(byte[] msg) { try { return MessageDigest.getInstance("SHA-256").digest(msg); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Failed to create SHA-256 MessageDigest", e); } } private static void assumeThatRsaPssAvailable() { Assume.assumeTrue(Security.getProviders("Signature.SHA256withRSA/PSS") != null); } } ./PaxHeaders.X/src_test_java_com_android_apksig_SigningCertificateLineageTest.java0100644 0000000 0000000 00000000034 14763776540 027660 xustar000000000 0000000 28 mtime=1741684064.6240000 src/test/java/com/android/apksig/SigningCertificateLineageTest.java0100644 0000000 0000000 00000170241 14763776540 024531 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.internal.util.Resources.FIRST_RSA_1024_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.FIRST_RSA_2048_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.SECOND_RSA_1024_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.SECOND_RSA_2048_SIGNER_RESOURCE_NAME; import static com.android.apksig.internal.util.Resources.THIRD_RSA_2048_SIGNER_RESOURCE_NAME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.android.apksig.SigningCertificateLineage.SignerCapabilities; import com.android.apksig.SigningCertificateLineage.SignerConfig; import com.android.apksig.apk.ApkFormatException; import com.android.apksig.internal.apk.ApkSigningBlockUtils; import com.android.apksig.internal.apk.v3.V3SchemeConstants; import com.android.apksig.internal.apk.v3.V3SchemeSigner; import com.android.apksig.internal.util.ByteBufferUtils; import com.android.apksig.internal.util.Resources; import com.android.apksig.util.DataSource; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.File; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @RunWith(JUnit4.class) public class SigningCertificateLineageTest { // createLineageWithSignersFromResources and updateLineageWithSignerFromResources will add the // SignerConfig for the signers added to the Lineage to this list. private List mSigners; @Before public void setUp() { mSigners = new ArrayList<>(); } @Test public void testLineageWithSingleSignerContainsExpectedSigner() throws Exception { SignerConfig signerConfig = Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage lineage = new SigningCertificateLineage.Builder( signerConfig).build(); assertLineageContainsExpectedSigners(lineage, FIRST_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testFirstRotationContainsExpectedSigners() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(lineage, mSigners); SignerConfig unknownSigner = Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertFalse("The signer " + unknownSigner.getCertificate().getSubjectDN() + " should not be in the lineage", lineage.isSignerInLineage(unknownSigner)); } @Test public void testRotationWithExistingLineageContainsExpectedSigners() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); lineage = updateLineageWithSignerFromResources(lineage, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(lineage, mSigners); } @Test public void testLineageFromBytesContainsExpectedSigners() throws Exception { // This file contains the lineage with the three rsa-2048 signers DataSource lineageDataSource = Resources.toDataSource(getClass(), "rsa-2048-lineage-3-signers"); SigningCertificateLineage lineage = SigningCertificateLineage.readFromBytes( lineageDataSource.getByteBuffer(0, (int) lineageDataSource.size()).array()); List signers = new ArrayList<>(3); signers.add( Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); signers.add( Resources.toLineageSignerConfig(getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); signers.add( Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertLineageContainsExpectedSigners(lineage, signers); } @Test public void testLineageFromFileContainsExpectedSigners() throws Exception { // This file contains the lineage with the three rsa-2048 signers DataSource lineageDataSource = Resources.toDataSource(getClass(), "rsa-2048-lineage-3-signers"); SigningCertificateLineage lineage = SigningCertificateLineage.readFromDataSource( lineageDataSource); List signers = new ArrayList<>(3); signers.add( Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); signers.add( Resources.toLineageSignerConfig(getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); signers.add( Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertLineageContainsExpectedSigners(lineage, signers); } @Test public void testLineageFromFileDoesNotContainUnknownSigner() throws Exception { // This file contains the lineage with the first two rsa-2048 signers SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-2-signers"); SignerConfig unknownSigner = Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertFalse("The signer " + unknownSigner.getCertificate().getSubjectDN() + " should not be in the lineage", lineage.isSignerInLineage(unknownSigner)); } @Test(expected = IllegalArgumentException.class) public void testLineageFromFileWithInvalidMagicFails() throws Exception { // This file contains the lineage with two rsa-2048 signers and a modified MAGIC value Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-invalid-magic"); } @Test(expected = IllegalArgumentException.class) public void testLineageFromFileWithInvalidVersionFails() throws Exception { // This file contains the lineage with two rsa-2048 signers and an invalid value of FF for // the version Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-invalid-version"); } @Test public void testLineageWrittenToBytesContainsExpectedSigners() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); lineage = updateLineageWithSignerFromResources(lineage, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); byte[] lineageBytes = lineage.getBytes(); lineage = SigningCertificateLineage.readFromBytes(lineageBytes); assertLineageContainsExpectedSigners(lineage, mSigners); } @Test public void testLineageWrittenToFileContainsExpectedSigners() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); lineage = updateLineageWithSignerFromResources(lineage, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); File lineageFile = File.createTempFile(getClass().getSimpleName(), ".bin"); lineageFile.deleteOnExit(); lineage.writeToFile(lineageFile); lineage = SigningCertificateLineage.readFromFile(lineageFile); assertLineageContainsExpectedSigners(lineage, mSigners); } @Test public void testUpdatedCapabilitiesInLineage() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig oldSignerConfig = mSigners.get(0); List expectedCapabilityValues = Arrays.asList(false, false, false, false, false); SignerCapabilities newCapabilities = buildSignerCapabilities(expectedCapabilityValues); lineage.updateSignerCapabilities(oldSignerConfig, newCapabilities); SignerCapabilities updatedCapabilities = lineage.getSignerCapabilities(oldSignerConfig); assertExpectedCapabilityValues(updatedCapabilities, expectedCapabilityValues); } @Test public void testUpdatedCapabilitiesInLineageWrittenToFile() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig oldSignerConfig = mSigners.get(0); List expectedCapabilityValues = Arrays.asList(false, false, false, false, false); SignerCapabilities newCapabilities = buildSignerCapabilities(expectedCapabilityValues); lineage.updateSignerCapabilities(oldSignerConfig, newCapabilities); File lineageFile = File.createTempFile(getClass().getSimpleName(), ".bin"); lineageFile.deleteOnExit(); lineage.writeToFile(lineageFile); lineage = SigningCertificateLineage.readFromFile(lineageFile); SignerCapabilities updatedCapabilities = lineage.getSignerCapabilities(oldSignerConfig); assertExpectedCapabilityValues(updatedCapabilities, expectedCapabilityValues); } @Test public void testCapabilitiesAreNotUpdatedWithDefaultValues() throws Exception { // This file contains the lineage with the first two rsa-2048 signers with the first signer // having all of the capabilities set to false. SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-no-capabilities-first-signer"); List expectedCapabilityValues = Arrays.asList(false, false, false, false, false); SignerConfig oldSignerConfig = Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME); SignerCapabilities oldSignerCapabilities = lineage.getSignerCapabilities(oldSignerConfig); assertExpectedCapabilityValues(oldSignerCapabilities, expectedCapabilityValues); // The builder is called directly to ensure all of the capabilities are set to the default // values and the caller configured flags are not modified in this SignerCapabilities. SignerCapabilities newCapabilities = new SignerCapabilities.Builder().build(); lineage.updateSignerCapabilities(oldSignerConfig, newCapabilities); SignerCapabilities updatedCapabilities = lineage.getSignerCapabilities(oldSignerConfig); assertExpectedCapabilityValues(updatedCapabilities, expectedCapabilityValues); } @Test public void testUpdatedCapabilitiesInLineageByCertificate() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); X509Certificate oldSignerCertificate = mSigners.get(0).getCertificate(); List expectedCapabilityValues = Arrays.asList(false, false, false, false, false); SignerCapabilities newCapabilities = buildSignerCapabilities(expectedCapabilityValues); lineage.updateSignerCapabilities(oldSignerCertificate, newCapabilities); assertExpectedCapabilityValues(lineage.getSignerCapabilities(oldSignerCertificate), expectedCapabilityValues); } @Test public void testUpdateSignerCapabilitiesCertificateNotInLineageThrowsException() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); X509Certificate certificate = getSignerConfigFromResources( FIRST_RSA_1024_SIGNER_RESOURCE_NAME).getCertificate(); List expectedCapabilityValues = Arrays.asList(false, false, false, false, false); SignerCapabilities newCapabilities = buildSignerCapabilities(expectedCapabilityValues); assertThrows(IllegalArgumentException.class, () -> lineage.updateSignerCapabilities(certificate, newCapabilities)); } @Test public void testFirstRotationWitNonDefaultCapabilitiesForSigners() throws Exception { SignerConfig oldSigner = Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig newSigner = Resources.toLineageSignerConfig(getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME); List oldSignerCapabilityValues = Arrays.asList(false, false, false, false, false); List newSignerCapabilityValues = Arrays.asList(false, true, false, false, false); SigningCertificateLineage lineage = new SigningCertificateLineage.Builder(oldSigner, newSigner) .setOriginalCapabilities(buildSignerCapabilities(oldSignerCapabilityValues)) .setNewCapabilities(buildSignerCapabilities(newSignerCapabilityValues)) .build(); SignerCapabilities oldSignerCapabilities = lineage.getSignerCapabilities(oldSigner); assertExpectedCapabilityValues(oldSignerCapabilities, oldSignerCapabilityValues); SignerCapabilities newSignerCapabilities = lineage.getSignerCapabilities(newSigner); assertExpectedCapabilityValues(newSignerCapabilities, newSignerCapabilityValues); } @Test public void testRotationWithExitingLineageAndNonDefaultCapabilitiesForNewSigner() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig oldSigner = mSigners.get(mSigners.size() - 1); SignerConfig newSigner = Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME); List newSignerCapabilityValues = Arrays.asList(false, false, false, false, false); lineage = lineage.spawnDescendant(oldSigner, newSigner, buildSignerCapabilities(newSignerCapabilityValues)); SignerCapabilities newSignerCapabilities = lineage.getSignerCapabilities(newSigner); assertExpectedCapabilityValues(newSignerCapabilities, newSignerCapabilityValues); } @Test(expected = IllegalArgumentException.class) public void testRotationWithExistingLineageUsingNonParentSignerFails() throws Exception { // When rotating the signing certificate the most recent signer must be provided to the // spawnDescendant method. This test ensures that using an ancestor of the most recent // signer will fail as expected. SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig oldestSigner = mSigners.get(0); SignerConfig newSigner = Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME); lineage.spawnDescendant(oldestSigner, newSigner); } @Test public void testLineageFromV3SignerAttribute() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); // The format of the V3 Signer Attribute is as follows (little endian): // * length-prefixed bytes: attribute pair // * uint32: ID // * bytes: value - encoded V3 SigningCertificateLineage ByteBuffer v3SignerAttribute = ByteBuffer.wrap( V3SchemeSigner.generateV3SignerAttribute(lineage)); v3SignerAttribute.order(ByteOrder.LITTLE_ENDIAN); ByteBuffer attribute = ApkSigningBlockUtils.getLengthPrefixedSlice(v3SignerAttribute); // The generateV3SignerAttribute method should only use the PROOF_OF_ROTATION_ATTR_ID // value for the ID. int id = attribute.getInt(); assertEquals( "The ID of the v3SignerAttribute ByteBuffer is not the expected " + "PROOF_OF_ROTATION_ATTR_ID", V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID, id); lineage = SigningCertificateLineage.readFromV3AttributeValue( ByteBufferUtils.toByteArray(attribute)); assertLineageContainsExpectedSigners(lineage, mSigners); } @Test public void testSortedSignerConfigsAreInSortedOrder() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); DefaultApkSignerEngine.SignerConfig oldSigner = getApkSignerEngineSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); DefaultApkSignerEngine.SignerConfig newSigner = getApkSignerEngineSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME); List signers = Arrays.asList(newSigner, oldSigner); List sortedSigners = lineage.sortSignerConfigs( signers); assertEquals("The sorted signer list does not contain the expected number of elements", signers.size(), sortedSigners.size()); assertEquals("The first element in the sorted list should be the first signer", oldSigner, sortedSigners.get(0)); assertEquals("The second element in the sorted list should be the second signer", newSigner, sortedSigners.get(1)); } @Test(expected = IllegalArgumentException.class) public void testSortedSignerConfigsWithUnknownSignerFails() throws Exception { // Since this test includes a signer that is not in the lineage the sort should fail with // an IllegalArgumentException. SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); DefaultApkSignerEngine.SignerConfig oldSigner = getApkSignerEngineSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); DefaultApkSignerEngine.SignerConfig newSigner = getApkSignerEngineSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME); DefaultApkSignerEngine.SignerConfig unknownSigner = getApkSignerEngineSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME); List signers = Arrays.asList(newSigner, oldSigner, unknownSigner); lineage.sortSignerConfigs(signers); } @Test public void testIsCertificateLatestInLineageWithLatestCertReturnsTrue() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); DefaultApkSignerEngine.SignerConfig latestSigner = getApkSignerEngineSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertTrue(lineage.isCertificateLatestInLineage(latestSigner.getCertificates().get(0))); } @Test public void testIsCertificateLatestInLineageWithOlderCertReturnsFalse() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); DefaultApkSignerEngine.SignerConfig olderSigner = getApkSignerEngineSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME); assertFalse(lineage.isCertificateLatestInLineage(olderSigner.getCertificates().get(0))); } @Test public void testIsCertificateLatestInLineageWithUnknownCertReturnsFalse() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); DefaultApkSignerEngine.SignerConfig unknownSigner = getApkSignerEngineSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertFalse(lineage.isCertificateLatestInLineage(unknownSigner.getCertificates().get(0))); } @Test public void testAllExpectedCertificatesAreInLineage() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); lineage = updateLineageWithSignerFromResources(lineage, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); Set expectedCertSet = new HashSet<>(); for (int i = 0; i < mSigners.size(); i++) { expectedCertSet.add(mSigners.get(i).getCertificate()); } List certs = lineage.getCertificatesInLineage(); assertEquals( "The number of elements in the certificate list from the lineage does not equal " + "the expected number", expectedCertSet.size(), certs.size()); for (X509Certificate cert : certs) { // remove the certificate from the Set to ensure duplicate certs were not returned. assertTrue("An unexpected certificate, " + cert.getSubjectDN() + ", is in the lineage", expectedCertSet.remove(cert)); } } @Test public void testSublineageContainsExpectedSigners() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); lineage = updateLineageWithSignerFromResources(lineage, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); List subList = mSigners.subList(0, 2); X509Certificate cert = subList.get(1).getCertificate(); SigningCertificateLineage subLineage = lineage.getSubLineage(cert); assertLineageContainsExpectedSigners(subLineage, subList); } @Test public void testConsolidatedLineageContainsExpectedSigners() throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage updatedLineage = updateLineageWithSignerFromResources(lineage, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); List lineages = Arrays.asList(lineage, updatedLineage); SigningCertificateLineage consolidatedLineage = SigningCertificateLineage.consolidateLineages(lineages); assertLineageContainsExpectedSigners(consolidatedLineage, mSigners); } @Test(expected = IllegalArgumentException.class) public void testConsolidatedLineageWithDisjointLineagesFail() throws Exception { List lineages = new ArrayList<>(); lineages.add(createLineageWithSignersFromResources(FIRST_RSA_1024_SIGNER_RESOURCE_NAME, SECOND_RSA_1024_SIGNER_RESOURCE_NAME)); lineages.add(createLineageWithSignersFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage.consolidateLineages(lineages); } @Test public void testLineageFromAPKContainsExpectedSigners() throws Exception { SignerConfig firstSigner = getSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig secondSigner = getSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig thirdSigner = getSignerConfigFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME); List expectedSigners = Arrays.asList(firstSigner, secondSigner, thirdSigner); DataSource apkDataSource = Resources.toDataSource(getClass(), "v1v2v3-with-rsa-2048-lineage-3-signers.apk"); SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkDataSource( apkDataSource); assertLineageContainsExpectedSigners(lineageFromApk, expectedSigners); } @Test public void testLineageFromAPKWithV31BlockContainsExpectedSigners() throws Exception { SignerConfig firstSigner = getSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig secondSigner = getSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME); List expectedSigners = Arrays.asList(firstSigner, secondSigner); DataSource apkDataSource = Resources.toDataSource(getClass(), "v31-rsa-2048_2-tgt-34-1-tgt-28.apk"); SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkDataSource( apkDataSource); assertLineageContainsExpectedSigners(lineageFromApk, expectedSigners); } @Test public void testOnlyV31LineageFromAPKWithV31BlockContainsExpectedSigners() throws Exception { SignerConfig firstSigner = getSignerConfigFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME); SignerConfig secondSigner = getSignerConfigFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME); List expectedSigners = Arrays.asList(firstSigner, secondSigner); DataSource apkDataSource = Resources.toDataSource(getClass(), "v31-rsa-2048_2-tgt-34-1-tgt-28.apk"); SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readV31FromApkDataSource( apkDataSource); assertLineageContainsExpectedSigners(lineageFromApk, expectedSigners); } @Test(expected = ApkFormatException.class) public void testLineageFromAPKWithInvalidZipCDSizeFails() throws Exception { // This test verifies that attempting to read the lineage from an APK where the zip // sections cannot be parsed fails. This APK is based off the // v1v2v3-with-rsa-2048-lineage-3-signers.apk with a modified CD size in the EoCD. DataSource apkDataSource = Resources.toDataSource(getClass(), "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk"); SigningCertificateLineage.readFromApkDataSource(apkDataSource); } @Test public void testLineageFromAPKWithNoLineageFails() throws Exception { // This test verifies that attempting to read the lineage from an APK without a lineage // fails. // This is a valid APK that has only been signed with the V1 and V2 signature schemes; // since the lineage is an attribute in the V3 signature block this test should fail. DataSource apkDataSource = Resources.toDataSource(getClass(), "golden-aligned-v1v2-out.apk"); try { SigningCertificateLineage.readFromApkDataSource(apkDataSource); fail("A failure should have been reported due to the APK not containing a V3 signing " + "block"); } catch (IllegalArgumentException expected) {} // This is a valid APK signed with the V1, V2, and V3 signature schemes, but there is no // lineage in the V3 signature block. apkDataSource = Resources.toDataSource(getClass(), "golden-aligned-v1v2v3-out.apk"); try { SigningCertificateLineage.readFromApkDataSource(apkDataSource); fail("A failure should have been reported due to the APK containing a V3 signing " + "block without the lineage attribute"); } catch (IllegalArgumentException expected) {} // This APK is based off the v1v2v3-with-rsa-2048-lineage-3-signers.apk with a bit flip // in the lineage attribute ID in the V3 signature block. apkDataSource = Resources.toDataSource(getClass(), "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-lineage-attr.apk"); try { SigningCertificateLineage.readFromApkDataSource(apkDataSource); fail("A failure should have been reported due to the APK containing a V3 signing " + "block with a modified lineage attribute ID"); } catch (IllegalArgumentException expected) {} } @Test public void testV31LineageFromAPKWithNoV31LineageFails() throws Exception { DataSource apkDataSource = Resources.toDataSource(getClass(), "golden-aligned-v1v2-out.apk"); try { SigningCertificateLineage.readV31FromApkDataSource(apkDataSource); fail("A failure should have been reported due to the APK not containing a V3 signing " + "block"); } catch (IllegalArgumentException expected) {} // This is a valid APK signed with the V1, V2, and V3 signature schemes, but there is no // lineage in the V3 signature block. apkDataSource = Resources.toDataSource(getClass(), "golden-aligned-v1v2v3-out.apk"); try { SigningCertificateLineage.readV31FromApkDataSource(apkDataSource); fail("A failure should have been reported due to the APK containing a V3 signing " + "block without the lineage attribute"); } catch (IllegalArgumentException expected) {} // This is a valid APK signed with the V1, V2, and V3 signature schemes, with a valid // lineage in the V3 signature block, but no V3.1 lineage. apkDataSource = Resources.toDataSource(getClass(), "v1v2v3-with-rsa-2048-lineage-3-signers.apk"); try { SigningCertificateLineage.readV31FromApkDataSource(apkDataSource); fail("A failure should have been reported due to the APK containing a V3 signing " + "block without the lineage attribute"); } catch (IllegalArgumentException expected) {} } @Test /** * old lineage: A -> B * new lineage: A -> B */ public void testCheckLineagesCompatibilitySameLineages() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); assertTrue(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A -> B * new lineage: A -> B -> C */ public void testCheckLineagesCompatibilityUpdateLonger() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertTrue(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A * new lineage: A -> B -> C */ public void testCheckLineagesCompatibilityUpdateExtended() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertTrue(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A -> B * new lineage: C -> B */ public void testCheckLineagesCompatibilityUpdateFirstMismatch() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(THIRD_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); assertFalse(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A -> B * new lineage: A -> C */ public void testCheckLineagesCompatibilityUpdateSecondMismatch() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertFalse(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A -> B -> C * new lineage: A -> B */ public void testCheckLineagesCompatibilityUpdateShorter() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); assertFalse(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A_withRollbackCapability -> B -> C * new lineage: A -> B */ public void testCheckLineagesCompatibilityUpdateShorterWithDifferentKeyRollback() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME), Arrays.asList(0)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); assertFalse(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A -> B_withRollbackCapability -> C * new lineage: A -> B */ public void testCheckLineagesCompatibilityUpdateShorterWithRollback() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME), Arrays.asList(1)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); assertTrue(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A_withRollbackCapability -> B_withRollbackCapability -> C * new lineage: A -> B */ public void testCheckLineagesCompatibilityUpdateShorterWithMultipleRollbacks() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME), Arrays.asList(0, 1)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); assertTrue(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A_withRollbackCapability -> B * new lineage: A -> C */ public void testCheckLineagesCompatibilityUpdateShorterWithRollbackAdditionalCertificate() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME), Arrays.asList(0)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertFalse(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: empty * new lineage: A -> B */ public void testCheckLineagesCompatibilityOldNotV31Signed() throws Exception { SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_1024_SIGNER_RESOURCE_NAME, SECOND_RSA_1024_SIGNER_RESOURCE_NAME)); assertTrue(SigningCertificateLineage.checkLineagesCompatibility( /* oldLineage= */ null, newLineage)); } @Test /** * old lineage: A -> B * new lineage: empty */ public void testCheckLineagesCompatibilityNewNotV31Signed() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_1024_SIGNER_RESOURCE_NAME, SECOND_RSA_1024_SIGNER_RESOURCE_NAME)); assertFalse(SigningCertificateLineage.checkLineagesCompatibility( oldLineage, /* newLineage= */ null)); } @Test /** * old lineage: empty * new lineage: empty */ public void testCheckLineagesCompatibilityBothNotV31Signed() throws Exception { assertTrue(SigningCertificateLineage.checkLineagesCompatibility( /* oldLineage= */ null, /* newLineage= */ null)); } @Test /** * old lineage: A -> B -> C * new lineage: B -> C */ public void testCheckLineagesCompatibilityUpdateTrimmed() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertTrue(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A -> B * new lineage: B -> C */ public void testCheckLineagesCompatibilityUpdateTrimmedAndExtended() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertTrue(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A -> B -> C * new lineage: C */ public void testCheckLineagesCompatibilityUpdateTrimmedToOne() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertTrue(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test /** * old lineage: A -> B -> C * new lineage: A -> C */ public void testCheckLineagesCompatibilityUpdateWronglyTrimmed() throws Exception { SigningCertificateLineage oldLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); SigningCertificateLineage newLineage = createLineageWithSignersFromResources( Arrays.asList(FIRST_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME)); assertFalse(SigningCertificateLineage.checkLineagesCompatibility(oldLineage, newLineage)); } @Test public void testMergeLineageWithTwoEqualLineagesReturnsMergedLineage() throws Exception { // The mergeLineageWith method is intended to merge two separate lineages into a superset // that spans both lineages. This method verifies if both lineages have the same signers, // the merged lineage will have the same signers as well. SigningCertificateLineage lineage1 = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage lineage2 = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage mergedLineage = lineage1.mergeLineageWith(lineage2); assertLineageContainsExpectedSigners(mergedLineage, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testMergeLineageWithOverlappingLineageReturnsMergedLineage() throws Exception { // When A -> B and B -> C are passed to mergeLineageWith, the merged lineage should be // A -> B -> C. SigningCertificateLineage lineage1 = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage lineage2 = createLineageWithSignersFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage mergedLineage1 = lineage1.mergeLineageWith(lineage2); SigningCertificateLineage mergedLineage2 = lineage2.mergeLineageWith(lineage1); assertLineageContainsExpectedSigners(mergedLineage1, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(mergedLineage2, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testMergeLineageWithNoOverlappingLineageThrowsException() throws Exception { // When two lineages do not have any overlap, an exception should be thrown since the two // lineages cannot be merged. SigningCertificateLineage lineage1 = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage lineage2 = createLineageWithSignersFromResources( THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertThrows(IllegalArgumentException.class, () -> lineage1.mergeLineageWith(lineage2)); assertThrows(IllegalArgumentException.class, () -> lineage2.mergeLineageWith(lineage1)); } @Test public void testMergeLineageWithDivergedLineageThrowsException() throws Exception { // When two lineages share a common ancestor but diverge at later signers, an exception // should be thrown since the two lineages cannot be merged. SigningCertificateLineage lineage1 = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage lineage2 = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertThrows(IllegalArgumentException.class, () -> lineage1.mergeLineageWith(lineage2)); assertThrows(IllegalArgumentException.class, () -> lineage2.mergeLineageWith(lineage1)); } @Test public void testMergeLineageWithSingleSublineageInLineageReturnsMergedLineage() throws Exception { // If A -> B -> C and B are passed to mergeLineageWith, then the merged lineage should be // A -> B -> C. SigningCertificateLineage lineage1 = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage lineage2 = createLineageWithSignersFromResources( SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage mergedLineage1 = lineage1.mergeLineageWith(lineage2); SigningCertificateLineage mergedLineage2 = lineage2.mergeLineageWith(lineage1); assertLineageContainsExpectedSigners(mergedLineage1, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(mergedLineage2, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); } @Test public void testMergeLineageWithAncestorSublineageInLineageReturnsMergedLineage() throws Exception { // If A -> B -> C and A -> B are passed to mergeLineageWith, then the merged lineage should // be A -> B -> C. SigningCertificateLineage lineage1 = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage lineage2 = createLineageWithSignersFromResources( FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); SigningCertificateLineage mergedLineage1 = lineage1.mergeLineageWith(lineage2); SigningCertificateLineage mergedLineage2 = lineage2.mergeLineageWith(lineage1); assertLineageContainsExpectedSigners(mergedLineage1, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); assertLineageContainsExpectedSigners(mergedLineage2, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME); } /** * Builds a new {@code SigningCertificateLinage.SignerCapabilities} object using the values in * the provided {@code List}. The {@code List} should contain {@code boolean} values to be * passed to the following methods in the * {@code SigningCertificateLineage.SignerCapabilities.Builder} (if a value is not provided the * noted default is used): * * {@code SigningCertificateLineage.SignerCapabilities.Builder.setInstalledData} [{@code true}] * {@code SigningCertificateLineage.SignerCapabilities.Builder.setSharedUid} [{@code true}] * {@code SigningCertificateLineage.SignerCapabilities.Builder.setPermission} [{@code true}] * {@code SigningCertificateLineage.SignerCapabilities.Builder.setRollback} [{@code false}] * {@code SigningCertificateLineage.SignerCapabilities.Builder.setAuth} [{@code true}] * * This method should not be used when testing caller configured capabilities since the setXX * method for each capability is called. */ private SignerCapabilities buildSignerCapabilities(List capabilityValues) { return new SignerCapabilities.Builder() .setInstalledData(capabilityValues.size() > 0 ? capabilityValues.get(0) : true) .setSharedUid(capabilityValues.size() > 1 ? capabilityValues.get(1) : true) .setPermission(capabilityValues.size() > 2 ? capabilityValues.get(2) : true) .setRollback(capabilityValues.size() > 3 ? capabilityValues.get(3) : false) .setAuth(capabilityValues.size() > 4 ? capabilityValues.get(4) : true) .build(); } /** * Verifies the specified {@code SigningCertificateLinage.SignerCapabilities} contains the * expected values from the provided {@code List}. The {@code List} should contain * {@code boolean} values to be verified against the * {@code SigningCertificateLinage.SignerCapabilities} methods in the following order: * * {@mcode SigningCertificateLineage.SignerCapabilities.hasInstalledData} * {@mcode SigningCertificateLineage.SignerCapabilities.hasSharedUid} * {@mcode SigningCertificateLineage.SignerCapabilities.hasPermission} * {@mcode SigningCertificateLineage.SignerCapabilities.hasRollback} * {@mcode SigningCertificateLineage.SignerCapabilities.hasAuth} */ private void assertExpectedCapabilityValues(SignerCapabilities capabilities, List expectedCapabilityValues) { assertTrue("The expectedCapabilityValues do not contain the expected number of elements", expectedCapabilityValues.size() >= 5); assertEquals( "The installed data capability is not set to the expected value", expectedCapabilityValues.get(0), capabilities.hasInstalledData()); assertEquals( "The shared UID capability is not set to the expected value", expectedCapabilityValues.get(1), capabilities.hasSharedUid()); assertEquals( "The permission capability is not set to the expected value", expectedCapabilityValues.get(2), capabilities.hasPermission()); assertEquals( "The rollback capability is not set to the expected value", expectedCapabilityValues.get(3), capabilities.hasRollback()); assertEquals( "The auth capability is not set to the expected value", expectedCapabilityValues.get(4), capabilities.hasAuth()); } /** * Creates a new {@code SigningCertificateLineage} with the specified signers from the * resources. {@code mSigners} will be updated with the * {@code SigningCertificateLineage.SignerConfig} for each signer added to the lineage. */ private SigningCertificateLineage createLineageWithSignersFromResources( String oldSignerResourceName, String newSignerResourceName) throws Exception { SignerConfig oldSignerConfig = Resources.toLineageSignerConfig(getClass(), oldSignerResourceName); mSigners.add(oldSignerConfig); SignerConfig newSignerConfig = Resources.toLineageSignerConfig(getClass(), newSignerResourceName); mSigners.add(newSignerConfig); return new SigningCertificateLineage.Builder(oldSignerConfig, newSignerConfig).build(); } private SigningCertificateLineage createLineageWithSignersFromResources( String signerResourceName) throws Exception { SignerConfig signerConfig = Resources.toLineageSignerConfig(getClass(), signerResourceName); mSigners.add(signerConfig); return new SigningCertificateLineage.Builder(signerConfig).build(); } private SigningCertificateLineage createLineageWithSignersFromResources( List signerResourcesNames) throws Exception { if (signerResourcesNames.isEmpty()) { throw new Exception(); } SigningCertificateLineage lineage = createLineageWithSignersFromResources(signerResourcesNames.get(0)); for (String resourceName : signerResourcesNames.subList(1, signerResourcesNames.size())) { lineage = updateLineageWithSignerFromResources(lineage, resourceName); } return lineage; } private SigningCertificateLineage createLineageWithSignersFromResources( List signerResourcesNames, List rollbackCapabilityNodes) throws Exception { SigningCertificateLineage lineage = createLineageWithSignersFromResources(signerResourcesNames); for (Integer i : rollbackCapabilityNodes) { if (i < mSigners.size()) { SignerCapabilities newCapabilities = new SignerCapabilities.Builder() .setRollback(true).build(); lineage.updateSignerCapabilities(mSigners.get(i), newCapabilities); } } return lineage; } /** * Creates a new {@code SigningCertificateLineage} with the specified signers from the * resources. */ private SigningCertificateLineage createLineageWithSignersFromResources(String... signers) throws Exception { SignerConfig ancestorSignerConfig = Resources.toLineageSignerConfig(getClass(), signers[0]); SigningCertificateLineage lineage = new SigningCertificateLineage.Builder( ancestorSignerConfig).build(); for (int i = 1; i < signers.length; i++) { SignerConfig descendantSignerConfig = Resources.toLineageSignerConfig(getClass(), signers[i]); lineage = lineage.spawnDescendant(ancestorSignerConfig, descendantSignerConfig); ancestorSignerConfig = descendantSignerConfig; } return lineage; } /** * Updates the specified {@code SigningCertificateLineage} with the signer from the resources. * Requires that the {@code mSigners} list contains the previous signers in the lineage since * the most recent signer must be specified when adding a new signer to the lineage. */ private SigningCertificateLineage updateLineageWithSignerFromResources( SigningCertificateLineage lineage, String newSignerResourceName) throws Exception { // To add a new Signer to an existing lineage the config of the last signer must be // specified. If this class was used to create the lineage then the last signer should // be in the mSigners list. assertTrue("The mSigners list did not contain the expected signers to update the lineage", mSigners.size() >= 1); SignerConfig oldSignerConfig = mSigners.get(mSigners.size() - 1); SignerConfig newSignerConfig = Resources.toLineageSignerConfig(getClass(), newSignerResourceName); mSigners.add(newSignerConfig); return lineage.spawnDescendant(oldSignerConfig, newSignerConfig); } /** * Asserts the provided {@code lineage} contains the {@code expectedSigners} from the test's * resources. */ protected static void assertLineageContainsExpectedSigners(SigningCertificateLineage lineage, String... expectedSigners) throws Exception { assertLineageContainsExpectedSigners(lineage, getSignerConfigsFromResources(expectedSigners)); } private static List getSignerConfigsFromResources(String... signers) throws Exception { List signerConfigs = new ArrayList<>(); for (String signer : signers) { signerConfigs.add(getSignerConfigFromResources(signer)); } return signerConfigs; } private static void assertLineageContainsExpectedSigners(SigningCertificateLineage lineage, List signers) { assertEquals("The lineage does not contain the expected number of signers", signers.size(), lineage.size()); for (SignerConfig signer : signers) { assertTrue("The signer " + signer.getCertificate().getSubjectDN() + " is expected to be in the lineage", lineage.isSignerInLineage(signer)); } } protected static void assertLineageContainsExpectedSignersWithCapabilities( SigningCertificateLineage lineage, String[] signers, SignerCapabilities[] capabilities) throws Exception { List signerConfigs = getSignerConfigsFromResources(signers); assertEquals("The lineage does not contain the expected number of signers", signerConfigs.size(), lineage.size()); assertEquals( "The capabilities does not contain the expected number for the provided signers", signerConfigs.size(), capabilities.length); for (int i = 0; i < signerConfigs.size(); i++) { SignerConfig signerConfig = signerConfigs.get(i); assertTrue("The signer " + signerConfig.getCertificate().getSubjectDN() + " is expected to be in the lineage", lineage.isSignerInLineage(signerConfig)); assertEquals(lineage.getSignerCapabilities(signerConfig), capabilities[i]); } } private static SignerConfig getSignerConfigFromResources( String resourcePrefix) throws Exception { PrivateKey privateKey = Resources.toPrivateKey(SigningCertificateLineageTest.class, resourcePrefix + ".pk8"); X509Certificate cert = Resources.toCertificate(SigningCertificateLineageTest.class, resourcePrefix + ".x509.pem"); return new SignerConfig.Builder(new KeyConfig.Jca(privateKey), cert).build(); } private static DefaultApkSignerEngine.SignerConfig getApkSignerEngineSignerConfigFromResources( String resourcePrefix) throws Exception { return getApkSignerEngineSignerConfigFromResources(resourcePrefix, 0, null); } private static DefaultApkSignerEngine.SignerConfig getApkSignerEngineSignerConfigFromResources( String resourcePrefix, int minSdkVersion, SigningCertificateLineage lineage) throws Exception { PrivateKey privateKey = Resources.toPrivateKey(SigningCertificateLineageTest.class, resourcePrefix + ".pk8"); X509Certificate cert = Resources.toCertificate(SigningCertificateLineageTest.class, resourcePrefix + ".x509.pem"); DefaultApkSignerEngine.SignerConfig.Builder configBuilder = new DefaultApkSignerEngine.SignerConfig.Builder( resourcePrefix, new KeyConfig.Jca(privateKey), Collections.singletonList(cert)); if (minSdkVersion > 0) { configBuilder.setLineageForMinSdkVersion(lineage, minSdkVersion); } return configBuilder.build(); } } ./PaxHeaders.X/src_test_java_com_android_apksig_SourceStampVerifierTest.java0100644 0000000 0000000 00000000034 14763776540 026573 xustar000000000 0000000 28 mtime=1741684064.6270000 src/test/java/com/android/apksig/SourceStampVerifierTest.java0100644 0000000 0000000 00000066525 14763776540 023455 0ustar000000000 0000000 /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig; import static com.android.apksig.apk.ApkUtilsLite.computeSha256DigestBytes; import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.toHex; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.android.apksig.SourceStampVerifier.Result; import com.android.apksig.SourceStampVerifier.Result.SignerInfo; import com.android.apksig.internal.util.AndroidSdkVersion; import com.android.apksig.internal.util.Resources; import com.android.apksig.util.DataSources; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.nio.ByteBuffer; import java.security.cert.X509Certificate; import java.util.List; @RunWith(JUnit4.class) public class SourceStampVerifierTest { private static final String RSA_2048_CERT_SHA256_DIGEST = "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8"; private static final String RSA_2048_2_CERT_SHA256_DIGEST = "681b0e56a796350c08647352a4db800cc44b2adc8f4c72fa350bd05d4d50264d"; private static final String RSA_2048_3_CERT_SHA256_DIGEST = "bb77a72efc60e66501ab75953af735874f82cfe52a70d035186a01b3482180f3"; private static final String EC_P256_CERT_SHA256_DIGEST = "6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599"; private static final String EC_P256_2_CERT_SHA256_DIGEST = "d78405f761ff6236cc9b570347a570aba0c62a129a3ac30c831c64d09ad95469"; @Test public void verifySourceStamp_correctSignature() throws Exception { Result verificationResult = verifySourceStamp("valid-stamp.apk"); // Since the API is only verifying the source stamp the result itself should be marked as // verified. assertVerified(verificationResult); // The source stamp can also be verified by platform version; confirm the verification works // using just the max signature scheme version supported by that platform version. verificationResult = verifySourceStamp("valid-stamp.apk", 18, 18); assertVerified(verificationResult); verificationResult = verifySourceStamp("valid-stamp.apk", 24, 24); assertVerified(verificationResult); verificationResult = verifySourceStamp("valid-stamp.apk", 28, 28); assertVerified(verificationResult); } @Test public void verifySourceStamp_rotatedV3Key_signingCertDigestsMatch() throws Exception { // The SourceStampVerifier should return a result that includes all of the latest signing // certificates for each of the signature schemes that are applicable to the specified // min / max SDK versions. // Verify when platform versions that support the V1 - V3 signature schemes are specified // that an APK signed with all signature schemes has its expected signers returned in the // result. Result verificationResult = verifySourceStamp("v1v2v3-rotated-v3-key-valid-stamp.apk", 23, 28); assertVerified(verificationResult); assertSigningCertificates(verificationResult, EC_P256_CERT_SHA256_DIGEST, EC_P256_CERT_SHA256_DIGEST, EC_P256_2_CERT_SHA256_DIGEST); // Verify when the specified platform versions only support a single signature scheme that // scheme's signer is the only one in the result. verificationResult = verifySourceStamp("v1v2v3-rotated-v3-key-valid-stamp.apk", 18, 18); assertVerified(verificationResult); assertSigningCertificates(verificationResult, EC_P256_CERT_SHA256_DIGEST, null, null); verificationResult = verifySourceStamp("v1v2v3-rotated-v3-key-valid-stamp.apk", 24, 24); assertVerified(verificationResult); assertSigningCertificates(verificationResult, null, EC_P256_CERT_SHA256_DIGEST, null); verificationResult = verifySourceStamp("v1v2v3-rotated-v3-key-valid-stamp.apk", 28, 28); assertVerified(verificationResult); assertSigningCertificates(verificationResult, null, null, EC_P256_2_CERT_SHA256_DIGEST); } @Test public void verifySourceStamp_signatureMissing() throws Exception { Result verificationResult = verifySourceStamp( "stamp-without-block.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_SIG_MISSING); } @Test public void verifySourceStamp_certificateMismatch() throws Exception { Result verificationResult = verifySourceStamp( "stamp-certificate-mismatch.apk"); assertSourceStampVerificationFailure( verificationResult, ApkVerificationIssue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK); } @Test public void verifySourceStamp_v1OnlySignatureValidStamp() throws Exception { Result verificationResult = verifySourceStamp("v1-only-with-stamp.apk"); assertVerified(verificationResult); assertSigningCertificates(verificationResult, EC_P256_CERT_SHA256_DIGEST, null, null); // Confirm that the source stamp verification succeeds when specifying platform versions // that supported later signature scheme versions. verificationResult = verifySourceStamp("v1-only-with-stamp.apk", 28, 28); assertVerified(verificationResult); assertSigningCertificates(verificationResult, EC_P256_CERT_SHA256_DIGEST, null, null); verificationResult = verifySourceStamp("v1-only-with-stamp.apk", 24, 24); assertVerified(verificationResult); assertSigningCertificates(verificationResult, EC_P256_CERT_SHA256_DIGEST, null, null); } @Test public void verifySourceStamp_v2OnlySignatureValidStamp() throws Exception { // The SourceStampVerifier will not query the APK's manifest for the minSdkVersion, so // set the min / max versions to prevent failure due to a missing V1 signature. Result verificationResult = verifySourceStamp("v2-only-with-stamp.apk", 24, 24); assertVerified(verificationResult); assertSigningCertificates(verificationResult, null, EC_P256_CERT_SHA256_DIGEST, null); // Confirm that the source stamp verification succeeds when specifying a platform version // that supports a later signature scheme version. verificationResult = verifySourceStamp("v2-only-with-stamp.apk", 28, 28); assertVerified(verificationResult); assertSigningCertificates(verificationResult, null, EC_P256_CERT_SHA256_DIGEST, null); } @Test public void verifySourceStamp_v3OnlySignatureValidStamp() throws Exception { // The SourceStampVerifier will not query the APK's manifest for the minSdkVersion, so // set the min / max versions to prevent failure due to a missing V1 signature. Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", 28, 28); assertVerified(verificationResult); assertSigningCertificates(verificationResult, null, null, EC_P256_CERT_SHA256_DIGEST); } @Test public void verifySourceStamp_apkHashMismatch_v1SignatureScheme() throws Exception { Result verificationResult = verifySourceStamp( "stamp-apk-hash-mismatch-v1.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void verifySourceStamp_apkHashMismatch_v2SignatureScheme() throws Exception { Result verificationResult = verifySourceStamp( "stamp-apk-hash-mismatch-v2.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void verifySourceStamp_apkHashMismatch_v3SignatureScheme() throws Exception { Result verificationResult = verifySourceStamp( "stamp-apk-hash-mismatch-v3.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void verifySourceStamp_malformedSignature() throws Exception { Result verificationResult = verifySourceStamp( "stamp-malformed-signature.apk"); assertSourceStampVerificationFailure( verificationResult, ApkVerificationIssue.SOURCE_STAMP_MALFORMED_SIGNATURE); } @Test public void verifySourceStamp_expectedDigestMatchesActual() throws Exception { // The ApkVerifier provides an API to specify the expected certificate digest; this test // verifies that the test runs through to completion when the actual digest matches the // provided value. Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", RSA_2048_CERT_SHA256_DIGEST, 28, 28); assertVerified(verificationResult); } @Test public void verifySourceStamp_expectedDigestMismatch() throws Exception { // If the caller requests source stamp verification with an expected cert digest that does // not match the actual digest in the APK the verifier should report the mismatch. Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", EC_P256_CERT_SHA256_DIGEST); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH); } @Test public void verifySourceStamp_noStampCertDigestNorSignatureBlock() throws Exception { // The caller of this API expects that the provided APK should be signed with a source // stamp; if no artifacts of the stamp are present ensure that the API fails indicating the // missing stamp. Result verificationResult = verifySourceStamp("original.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING); } @Test public void verifySourceStamp_validStampLineage() throws Exception { Result verificationResult = verifySourceStamp( "stamp-lineage-valid.apk"); assertVerified(verificationResult); assertSigningCertificatesInLineage(verificationResult, RSA_2048_CERT_SHA256_DIGEST, RSA_2048_2_CERT_SHA256_DIGEST); } @Test public void verifySourceStamp_invalidStampLineage() throws Exception { Result verificationResult = verifySourceStamp( "stamp-lineage-invalid.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_POR_CERT_MISMATCH); } @Test public void verifySourceStamp_multipleSignersInLineage() throws Exception { Result verificationResult = verifySourceStamp("stamp-lineage-with-3-signers.apk", 18, 28); assertVerified(verificationResult); assertSigningCertificatesInLineage(verificationResult, RSA_2048_CERT_SHA256_DIGEST, RSA_2048_2_CERT_SHA256_DIGEST, RSA_2048_3_CERT_SHA256_DIGEST); } @Test public void verifySourceStamp_noSignersInLineage_returnsEmptyLineage() throws Exception { // If the source stamp's signer has not yet been rotated then an empty lineage should be // returned. Result verificationResult = verifySourceStamp("valid-stamp.apk"); assertSigningCertificatesInLineage(verificationResult); } @Test public void verifySourceStamp_noApkSignature_succeeds() throws Exception { // The SourceStampVerifier is designed to verify an APK's source stamp with minimal // verification of the APK signature schemes. This test verifies if just the MANIFEST.MF // is present without any other APK signatures the stamp signature can still be successfully // verified. Result verificationResult = verifySourceStamp("stamp-without-apk-signature.apk", 18, 28); assertVerified(verificationResult); assertSigningCertificates(verificationResult, null, null, null); // While the source stamp verification should succeed a warning should still be logged to // notify the caller that there were no signers. assertSourceStampVerificationWarning(verificationResult, ApkVerificationIssue.JAR_SIG_NO_SIGNATURES); } @Test public void verifySourceStamp_noTimestamp_returnsDefaultValue() throws Exception { // A timestamp attribute was added to the source stamp, but verification of APKs that were // generated prior to the addition of the timestamp should still complete successfully, // returning a default value of 0 for the timestamp. Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", AndroidSdkVersion.P, AndroidSdkVersion.P); assertVerified(verificationResult); assertEquals( "A value of 0 should be returned for the timestamp when the attribute is not " + "present", 0, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); } @Test public void verifySourceStamp_validTimestamp_returnsExpectedValue() throws Exception { // Once an APK is signed with a source stamp that contains a valid value for the timestamp // attribute, verification of the source stamp should result in the same value for the // timestamp returned to the verifier. Result verificationResult = verifySourceStamp("stamp-valid-timestamp-value.apk"); assertVerified(verificationResult); assertEquals(1644886584, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); } @Test public void verifySourceStamp_validTimestampLargerBuffer_returnsExpectedValue() throws Exception { // The source stamp timestamp attribute value is expected to be written to an 8 byte buffer // as a little-endian long; while a larger buffer will not result in an error, any // additional space after the buffer's initial 8 bytes will be ignored. This test verifies a // valid timestamp value written to the first 8 bytes of a 16 byte buffer can still be read // successfully. Result verificationResult = verifySourceStamp("stamp-valid-timestamp-16-byte-buffer.apk"); assertEquals(1645126786, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); } @Test public void verifySourceStamp_invalidTimestampValueEqualsZero_verificationFails() throws Exception { // If the source stamp timestamp attribute exists and is <= 0, then a warning should be // reported to notify the caller to the invalid attribute value. This test verifies a // a warning is reported when the timestamp attribute value is 0. Result verificationResult = verifySourceStamp("stamp-invalid-timestamp-value-zero.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP); } @Test public void verifySourceStamp_invalidTimestampValueLessThanZero_verificationFails() throws Exception { // If the source stamp timestamp attribute exists and is <= 0, then a warning should be // reported to notify the caller to the invalid attribute value. This test verifies a // a warning is reported when the timestamp attribute value is < 0. Result verificationResult = verifySourceStamp( "stamp-invalid-timestamp-value-less-than-zero.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP); } @Test public void verifySourceStamp_invalidTimestampZeroInFirst8BytesOfBuffer_verificationFails() throws Exception { // The source stamp's timestamp attribute value is expected to be written to the first 8 // bytes of the attribute's value buffer; if a larger buffer is used and the timestamp // value is not written as a little-endian long to the first 8 bytes of the buffer, then // an error should be reported for the timestamp attribute since the rest of the buffer will // be ignored. Result verificationResult = verifySourceStamp( "stamp-timestamp-in-last-8-of-16-byte-buffer.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP); } @Test public void verifySourceStamp_intTimestampValue_verificationFails() throws Exception { // Since the source stamp timestamp attribute value is a long, an attribute value with // insufficient space to hold a long value should result in a warning reported to the user. Result verificationResult = verifySourceStamp( "stamp-int-timestamp-value.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_MALFORMED_ATTRIBUTE); } @Test public void verifySourceStamp_modifiedTimestampValue_verificationFails() throws Exception { // The source stamp timestamp attribute is part of the block's signed data; this test // verifies if the value of the timestamp in the stamp block is modified then verification // of the source stamp should fail. Result verificationResult = verifySourceStamp( "stamp-valid-timestamp-value-modified.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY); } @Test public void verifySourceStamp_unknownAttribute_verificationSucceeds() throws Exception { // When a new attribute is added to the source stamp, verifiers previously released to // prod will not recognize this new attribute. This test verifies an unknown attribute // will not cause the verification to fail by using an attribute with ID 0xe43c5945. Result verificationResult = verifySourceStamp("stamp-unknown-attr.apk"); assertVerified(verificationResult); assertTrue(verificationResult.getSourceStampInfo().containsInfoMessages()); assertTrue(verificationResult.getSourceStampInfo().getInfoMessages().stream().anyMatch( info -> info.getIssueId() == ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE)); } @Test public void verifySourceStamp_unknownSigAlgorithm_verificationSucceeds() throws Exception { // When a new signature algorithm is added to the source stamp, verifiers previously // released to prod will not recognize the new algorithm. This test verifies an unknown // signature algorithm will not cause the verification to fail as long as there is a // known signature that can be verified; this test uses a signature algorithm with ID // 0x1ee. Result verificationResult = verifySourceStamp("stamp-unknown-sig.apk"); assertVerified(verificationResult); assertTrue(verificationResult.getSourceStampInfo().containsInfoMessages()); assertTrue(verificationResult.getSourceStampInfo().getInfoMessages().stream().anyMatch( info -> info.getIssueId() == ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM)); } @Test public void verifySourceStamp_onlyUnknownSigAlgorithms_verificationFails() throws Exception { // When a new signature algorithm is added to the source stamp, previously supported // signature algorithms should still be written to the stamp to ensure existing verifiers // can continue verifying the stamp. This test verifies if a stamp only contains signature // algorithms unknown to the verifier then the verification fails as it is not able to // verify any signatures; this test uses signature algorithms with IDs 0x1ee and 0x1ef. Result verificationResult = verifySourceStamp("stamp-only-unknown-sigs.apk"); assertSourceStampVerificationFailure(verificationResult, ApkVerificationIssue.SOURCE_STAMP_NO_SIGNATURE); } private Result verifySourceStamp(String apkFilenameInResources) throws Exception { return verifySourceStamp(apkFilenameInResources, null, null, null); } private Result verifySourceStamp(String apkFilenameInResources, String expectedCertDigest) throws Exception { return verifySourceStamp(apkFilenameInResources, expectedCertDigest, null, null); } private Result verifySourceStamp(String apkFilenameInResources, Integer minSdkVersionOverride, Integer maxSdkVersionOverride) throws Exception { return verifySourceStamp(apkFilenameInResources, null, minSdkVersionOverride, maxSdkVersionOverride); } private Result verifySourceStamp(String apkFilenameInResources, String expectedCertDigest, Integer minSdkVersionOverride, Integer maxSdkVersionOverride) throws Exception { byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources); SourceStampVerifier.Builder builder = new SourceStampVerifier.Builder( DataSources.asDataSource(ByteBuffer.wrap(apkBytes))); if (minSdkVersionOverride != null) { builder.setMinCheckedPlatformVersion(minSdkVersionOverride); } if (maxSdkVersionOverride != null) { builder.setMaxCheckedPlatformVersion(maxSdkVersionOverride); } return builder.build().verifySourceStamp(expectedCertDigest); } private static void assertVerified(Result result) { if (result.isVerified()) { return; } StringBuilder msg = new StringBuilder(); for (ApkVerificationIssue error : result.getAllErrors()) { if (msg.length() > 0) { msg.append('\n'); } msg.append(error.toString()); } fail("APK failed source stamp verification: " + msg.toString()); } private static void assertSourceStampVerificationFailure(Result result, int expectedIssueId) { if (result.isVerified()) { fail( "APK source stamp verification succeeded instead of failing with " + expectedIssueId); return; } assertSourceStampVerificationIssue(result.getAllErrors(), expectedIssueId); } private static void assertSourceStampVerificationWarning(Result result, int expectedIssueId) { assertSourceStampVerificationIssue(result.getAllWarnings(), expectedIssueId); } private static void assertSourceStampVerificationIssue(List issues, int expectedIssueId) { StringBuilder msg = new StringBuilder(); for (ApkVerificationIssue issue : issues) { if (issue.getIssueId() == expectedIssueId) { return; } if (msg.length() > 0) { msg.append('\n'); } msg.append(issue.toString()); } fail( "APK source stamp verification did not report the expected issue. " + "Expected error ID: " + expectedIssueId + ", actual: " + (msg.length() > 0 ? msg.toString() : "No reported issues")); } /** * Asserts that the provided {@code expectedCertDigests} match their respective signing * certificate digest in the specified {@code result}. * *

{@code expectedCertDigests} should be provided in order of the signature schemes with V1 * being the first element, V2 the second, etc. If a signer is not expected to be present for * a signature scheme version a {@code null} value should be provided; for instance if only a V3 * signing certificate is expected the following should be provided: {@code null, null, * v3ExpectedCertDigest}. * *

Note, this method only supports a single signer per signature scheme; if an expected * certificate digest is provided for a signature scheme and multiple signers are found an * assertion exception will be thrown. */ private static void assertSigningCertificates(Result result, String... expectedCertDigests) throws Exception { for (int i = 0; i < expectedCertDigests.length; i++) { List signers = null; switch (i) { case 0: signers = result.getV1SchemeSigners(); break; case 1: signers = result.getV2SchemeSigners(); break; case 2: signers = result.getV3SchemeSigners(); break; default: fail("This method only supports verification of the signing certificates up " + "through the V3 Signature Scheme"); } if (expectedCertDigests[i] == null) { assertEquals( "Did not expect any V" + (i + 1) + " signers, found " + signers.size(), 0, signers.size()); continue; } if (signers.size() != 1) { fail("Expected one V" + (i + 1) + " signer with certificate digest " + expectedCertDigests[i] + ", found " + signers.size() + " V" + (i + 1) + " signers"); } X509Certificate signingCertificate = signers.get(0).getSigningCertificate(); assertNotNull(signingCertificate); assertEquals(expectedCertDigests[i], toHex(computeSha256DigestBytes(signingCertificate.getEncoded()))); } } /** * Asserts that the provided {@code expectedCertDigests} match their respective certificate in * the source stamp's lineage with the oldest signer at element 0. * *

If no values are provided for the expectedCertDigests, the source stamp's lineage will * be checked for an empty {@code List} indicating the source stamp has not been rotated. */ private static void assertSigningCertificatesInLineage(Result result, String... expectedCertDigests) throws Exception { List lineageCertificates = result.getSourceStampInfo().getCertificatesInLineage(); assertEquals("Unexpected number of lineage certificates", expectedCertDigests.length, lineageCertificates.size()); for (int i = 0; i < expectedCertDigests.length; i++) { assertEquals("Stamp lineage mismatch at signer " + i, expectedCertDigests[i], toHex(computeSha256DigestBytes(lineageCertificates.get(i).getEncoded()))); } } } ./PaxHeaders.X/src_test_java_com_android_apksig_apk_0100644 0000000 0000000 00000000034 14763776540 021764 xustar000000000 0000000 28 mtime=1741684064.6280000 src/test/java/com/android/apksig/apk/0040755 0000000 0000000 00000000000 14763776540 016551 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_apk_AllTests.java0100644 0000000 0000000 00000000034 14763776540 024360 xustar000000000 0000000 28 mtime=1741684064.6280000 src/test/java/com/android/apksig/apk/AllTests.java0100644 0000000 0000000 00000001460 14763776540 021145 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.apk; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ ApkUtilsTest.class, }) public class AllTests {} ./PaxHeaders.X/src_test_java_com_android_apksig_apk_ApkUtilsTest.java0100644 0000000 0000000 00000000034 14763776540 025221 xustar000000000 0000000 28 mtime=1741684064.6290000 src/test/java/com/android/apksig/apk/ApkUtilsTest.java0100644 0000000 0000000 00000014611 14763776540 022010 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.apk; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.nio.ByteBuffer; import java.security.MessageDigest; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import com.android.apksig.ApkSigner; import com.android.apksig.internal.util.HexEncoding; import com.android.apksig.internal.util.Resources; import com.android.apksig.util.DataSources; @RunWith(JUnit4.class) public class ApkUtilsTest { @Test public void testGetMinSdkVersionForValidCodename() throws Exception { assertEquals(1, ApkUtils.getMinSdkVersionForCodename("AAAA")); assertEquals(2, ApkUtils.getMinSdkVersionForCodename("CUPCAKE")); assertEquals(7, ApkUtils.getMinSdkVersionForCodename("FROYO")); assertEquals(23, ApkUtils.getMinSdkVersionForCodename("N")); assertEquals(23, ApkUtils.getMinSdkVersionForCodename("NMR1")); assertEquals(25, ApkUtils.getMinSdkVersionForCodename("OMG")); // Speculative: Q should be 27 or higher (not yet known at the time of writing) assertEquals(27, ApkUtils.getMinSdkVersionForCodename("QQQ")); } @Test(expected = CodenameMinSdkVersionException.class) public void testGetMinSdkVersionForEmptyCodename() throws Exception { ApkUtils.getMinSdkVersionForCodename(""); } @Test(expected = CodenameMinSdkVersionException.class) public void testGetMinSdkVersionForUnexpectedCodename() throws Exception { ApkUtils.getMinSdkVersionForCodename("1ABC"); } @Test public void testGetMinSdkVersionFromBinaryAndroidManifest() throws Exception { ByteBuffer manifest = getAndroidManifest("original.apk"); assertEquals(23, ApkUtils.getMinSdkVersionFromBinaryAndroidManifest(manifest)); } @Test public void testGetDebuggableFromBinaryAndroidManifest() throws Exception { ByteBuffer manifest = getAndroidManifest("original.apk"); assertFalse(ApkUtils.getDebuggableFromBinaryAndroidManifest(manifest)); manifest = getAndroidManifest("debuggable-boolean.apk"); assertTrue(ApkUtils.getDebuggableFromBinaryAndroidManifest(manifest)); // android:debuggable value is a resource reference -- this must be rejected manifest = getAndroidManifest("debuggable-resource.apk"); try { ApkUtils.getDebuggableFromBinaryAndroidManifest(manifest); fail(); } catch (ApkFormatException expected) {} } @Test public void testGetPackageNameFromBinaryAndroidManifest() throws Exception { ByteBuffer manifest = getAndroidManifest("original.apk"); assertEquals( "android.appsecurity.cts.tinyapp", ApkUtils.getPackageNameFromBinaryAndroidManifest(manifest)); } @Test public void testGetTargetSdkVersionFromBinaryAndroidManifest() throws Exception { ByteBuffer manifest = getAndroidManifest("v3-ec-p256-targetSdk-30.apk"); assertEquals(30, ApkUtils.getTargetSdkVersionFromBinaryAndroidManifest(manifest)); } @Test public void testGetTargetSdkVersion_noUsesSdkElement_returnsDefault() throws Exception { ByteBuffer manifest = getAndroidManifest("v1-only-no-uses-sdk.apk"); assertEquals(1, ApkUtils.getTargetSdkVersionFromBinaryAndroidManifest(manifest)); } @Test public void testGetTargetSandboxVersionFromBinaryAndroidManifest() throws Exception { ByteBuffer manifest = getAndroidManifest("targetSandboxVersion-2.apk"); assertEquals(2, ApkUtils.getTargetSandboxVersionFromBinaryAndroidManifest(manifest)); } @Test public void testGetTargetSandboxVersion_noTargetSandboxAttribute_returnsDefault() throws Exception { ByteBuffer manifest = getAndroidManifest("original.apk"); assertEquals(1, ApkUtils.getTargetSandboxVersionFromBinaryAndroidManifest(manifest)); } @Test public void testGetVersionCodeFromBinaryAndroidManifest() throws Exception { ByteBuffer manifest = getAndroidManifest("original.apk"); assertEquals(10, ApkUtils.getVersionCodeFromBinaryAndroidManifest(manifest)); } @Test public void testGetVersionCode_withVersionCodeMajor_returnsOnlyVersionCode() throws Exception { ByteBuffer manifest = getAndroidManifest("original-with-versionCodeMajor.apk"); assertEquals(25, ApkUtils.getVersionCodeFromBinaryAndroidManifest(manifest)); } @Test public void testGetLongVersionCodeFromBinaryAndroidManifest() throws Exception { ByteBuffer manifest = getAndroidManifest("original-with-versionCodeMajor.apk"); assertEquals(4294967321L, ApkUtils.getLongVersionCodeFromBinaryAndroidManifest(manifest)); } @Test public void testGetAndroidManifest() throws Exception { ByteBuffer manifest = getAndroidManifest("original.apk"); MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(manifest); byte[] actualDigest = md.digest(); assertEquals( "8b3de63a282652221162cdc327f424924ac3c7c24e642035975a1ee7a395c4dc", HexEncoding.encode(actualDigest)); } private static ByteBuffer getAndroidManifest(String apkResourceName) throws IOException, ApkFormatException { return getAndroidManifest(getResource(apkResourceName)); } private static ByteBuffer getAndroidManifest(byte[] apk) throws IOException, ApkFormatException { return ApkUtils.getAndroidManifest(DataSources.asDataSource(ByteBuffer.wrap(apk))); } private static byte[] getResource(String resourceName) throws IOException { return Resources.toByteArray(ApkSigner.class, resourceName); } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_0100644 0000000 0000000 00000000034 14763776540 023025 xustar000000000 0000000 28 mtime=1741684064.6290000 src/test/java/com/android/apksig/internal/0040755 0000000 0000000 00000000000 14763776540 017612 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_internal_AllTests.java0100644 0000000 0000000 00000000034 14763776540 025421 xustar000000000 0000000 28 mtime=1741684064.6290000 src/test/java/com/android/apksig/internal/AllTests.java0100644 0000000 0000000 00000001607 14763776540 022211 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ com.android.apksig.internal.asn1.AllTests.class, com.android.apksig.internal.util.AllTests.class, }) public class AllTests {} ./PaxHeaders.X/src_test_java_com_android_apksig_internal_apk_0100644 0000000 0000000 00000000034 14763776540 023660 xustar000000000 0000000 28 mtime=1741684064.6290000 src/test/java/com/android/apksig/internal/apk/0040755 0000000 0000000 00000000000 14763776540 020365 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_internal_apk_ApkSigningBlockUtilsTest.java0100644 0000000 0000000 00000000034 14763776540 031407 xustar000000000 0000000 28 mtime=1741684064.6290000 src/test/java/com/android/apksig/internal/apk/ApkSigningBlockUtilsTest.java0100644 0000000 0000000 00000011575 14763776540 026124 0ustar000000000 0000000 package com.android.apksig.internal.apk; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import com.android.apksig.util.RunnablesExecutor; import com.android.apksig.util.RunnablesProvider; import java.io.File; import java.io.FileOutputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.EnumMap; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class ApkSigningBlockUtilsTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); private static final int BASE = 255; // Intentionally not power of 2 to test properly DataSource[] dataSource; final Set algos = EnumSet.of(ContentDigestAlgorithm.CHUNKED_SHA512); @Before public void setUp() throws Exception { byte[] part1 = new byte[80 * 1024 * 1024 + 12345]; for (int i = 0; i < part1.length; ++i) { part1[i] = (byte)(i % BASE); } File dataFile = temporaryFolder.newFile("fake.apk"); try (FileOutputStream fos = new FileOutputStream(dataFile)) { fos.write(part1); } RandomAccessFile raf = new RandomAccessFile(dataFile, "r"); byte[] part2 = new byte[1_500_000]; for (int i = 0; i < part2.length; ++i) { part2[i] = (byte)(i % BASE); } byte[] part3 = new byte[30_000]; for (int i = 0; i < part3.length; ++i) { part3[i] = (byte)(i % BASE); } dataSource = new DataSource[] { DataSources.asDataSource(raf), DataSources.asDataSource(ByteBuffer.wrap(part2)), DataSources.asDataSource(ByteBuffer.wrap(part3)), }; } @Test public void testNewVersionMatchesOld() throws Exception { Map outputContentDigestsOld = new EnumMap<>(ContentDigestAlgorithm.class); Map outputContentDigestsNew = new EnumMap<>(ContentDigestAlgorithm.class); ApkSigningBlockUtils.computeOneMbChunkContentDigests( algos, dataSource, outputContentDigestsOld); ApkSigningBlockUtils.computeOneMbChunkContentDigests( RunnablesExecutor.SINGLE_THREADED, algos, dataSource, outputContentDigestsNew); assertEqualDigests(outputContentDigestsOld, outputContentDigestsNew); } @Test public void testMultithreadedVersionMatchesSinglethreaded() throws Exception { Map outputContentDigests = new EnumMap<>(ContentDigestAlgorithm.class); Map outputContentDigestsMultithreaded = new EnumMap<>(ContentDigestAlgorithm.class); ApkSigningBlockUtils.computeOneMbChunkContentDigests( RunnablesExecutor.SINGLE_THREADED, algos, dataSource, outputContentDigests); ApkSigningBlockUtils.computeOneMbChunkContentDigests( (RunnablesProvider provider) -> { ForkJoinPool forkJoinPool = ForkJoinPool.commonPool(); int jobCount = forkJoinPool.getParallelism(); List> jobs = new ArrayList<>(jobCount); for (int i = 0; i < jobCount; i++) { jobs.add(forkJoinPool.submit(provider.createRunnable())); } try { for (Future future : jobs) { future.get(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e); } }, algos, dataSource, outputContentDigestsMultithreaded); assertEqualDigests(outputContentDigestsMultithreaded, outputContentDigests); } private void assertEqualDigests( Map d1, Map d2) { assertEquals(d1.keySet(), d2.keySet()); for (ContentDigestAlgorithm algo : d1.keySet()) { byte[] digest1 = d1.get(algo); byte[] digest2 = d2.get(algo); assertArrayEquals(digest1, digest2); } } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_0100644 0000000 0000000 00000000034 14763776540 023747 xustar000000000 0000000 28 mtime=1741684064.6290000 src/test/java/com/android/apksig/internal/asn1/0040755 0000000 0000000 00000000000 14763776540 020454 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_AllTests.java0100644 0000000 0000000 00000000034 14763776540 026343 xustar000000000 0000000 28 mtime=1741684064.6290000 src/test/java/com/android/apksig/internal/asn1/AllTests.java0100644 0000000 0000000 00000001730 14763776540 023050 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ com.android.apksig.internal.asn1.Asn1BerParserTest.class, com.android.apksig.internal.asn1.Asn1DerEncoderTest.class, com.android.apksig.internal.asn1.ber.AllTests.class, }) public class AllTests {} ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_Asn1BerParserTest.java0100644 0000000 0000000 00000000034 14763776540 030060 xustar000000000 0000000 28 mtime=1741684064.6290000 src/test/java/com/android/apksig/internal/asn1/Asn1BerParserTest.java0100644 0000000 0000000 00000041400 14763776540 024563 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import com.android.apksig.internal.util.HexEncoding; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class Asn1BerParserTest { @Test(expected = NullPointerException.class) public void testNullInput() throws Exception { parse((ByteBuffer) null, EmptySequence.class); } @Test(expected = Asn1DecodingException.class) public void testEmptyInput() throws Exception { parse("", EmptySequence.class); } @Test public void testEmptySequence() throws Exception { // Empty SEQUENCE (0x3000) followed by garbage (0x12345678) ByteBuffer input = ByteBuffer.wrap(HexEncoding.decode("300012345678")); EmptySequence container = parse(input, EmptySequence.class); assertNotNull(container); // Check that input position has been advanced appropriately assertEquals(2, input.position()); } @Test public void testOctetString() throws Exception { assertEquals( "123456", HexEncoding.encode(parse("30050403123456", SequenceWithOctetString.class).buf)); assertEquals( "", HexEncoding.encode(parse("30020400", SequenceWithOctetString.class).buf)); } @Test public void testBitString() throws Exception { assertEquals( "123456", HexEncoding.encode(parse("30050303123456", SequenceWithBitString.class).buf)); assertEquals( "", HexEncoding.encode(parse("30020300", SequenceWithBitString.class).buf)); } @Test public void testBoolean() throws Exception { assertEquals(false, parse("3003010100", SequenceWithBoolean.class).value); assertEquals(true, parse("3003010101", SequenceWithBoolean.class).value); assertEquals(true, parse("30030101FF", SequenceWithBoolean.class).value); } @Test public void testUTCTime() throws Exception { assertEquals("1212211221Z", parse("300d170b313231323231313232315a", SequenceWithUTCTime.class).value); assertEquals("9912312359Z", parse("300d170b393931323331323335395a", SequenceWithUTCTime.class).value); } @Test public void testGeneralizedTime() throws Exception { assertEquals("201212211220.999-07", parse("301518133230313231323231313232302e3939392d3037", SequenceWithGeneralizedTime.class).value); assertEquals("20380119031407.000+00", parse("3017181532303338303131393033313430372e3030302b3030", SequenceWithGeneralizedTime.class).value); } @Test public void testInteger() throws Exception { // Various Java types decoded from INTEGER // Empty SEQUENCE (0x3000) followed by garbage (0x12345678) SequenceWithIntegers container = parse("301e" + "0201ff" // -1 + "0207ff123456789abc" // -7f123456789abc + "0200" // 0 + "020280ff" // -255 + "020a00000000000000001234", // 0x1234 SequenceWithIntegers.class); assertEquals(-1, container.n1); } @Test public void testOid() throws Exception { // Empty OID try { parse("30020600", SequenceWithOid.class); fail(); } catch (Asn1DecodingException expected) {} assertEquals("2.100.3", parse("30050603813403", SequenceWithOid.class).oid); assertEquals( "2.16.840.1.101.3.4.2.1", parse("300b0609608648016503040201", SequenceWithOid.class).oid); } @Test public void testSequenceOf() throws Exception { assertEquals(2, parse("3006300430003000", SequenceWithSequenceOf.class).values.size()); } @Test public void testSetOf() throws Exception { assertEquals(2, parse("3006310430003000", SequenceWithSetOf.class).values.size()); } @Test public void testUnencodedContainer() throws Exception { SequenceWithSequenceOfUnencodedContainers seq = parse("300C300A31023000310430003000", SequenceWithSequenceOfUnencodedContainers.class); assertEquals(2, seq.containers.size()); assertEquals(1, seq.containers.get(0).values.size()); assertEquals(2, seq.containers.get(1).values.size()); } @Test public void testImplicitOptionalField() throws Exception { // Optional field f2 missing in the input SequenceWithImplicitOptionalField seq = parse("300602010d02012a", SequenceWithImplicitOptionalField.class); assertEquals(13, seq.f1.intValue()); assertNull(seq.f2); assertEquals(42, seq.f3.intValue()); // Optional field f2 present in the input seq = parse("300a02010da102ffff02012a", SequenceWithImplicitOptionalField.class); assertEquals(13, seq.f1.intValue()); assertEquals(-1, seq.f2.intValue()); assertEquals(42, seq.f3.intValue()); } @Test public void testExplicitOptionalField() throws Exception { // Optional field f2 missing in the input SequenceWithExplicitOptionalField seq = parse("300602010d02012a", SequenceWithExplicitOptionalField.class); assertEquals(13, seq.f1.intValue()); assertNull(seq.f2); assertEquals(42, seq.f3.intValue()); // Optional field f2 present in the input seq = parse("300c02010da1040202ffff02012a", SequenceWithExplicitOptionalField.class); assertEquals(13, seq.f1.intValue()); assertEquals(-1, seq.f2.intValue()); assertEquals(42, seq.f3.intValue()); } @Test public void testChoiceWithDifferentTypedOptions() throws Exception { // The CHOICE can be either an INTEGER or an OBJECT IDENTIFIER // INTEGER ChoiceWithTwoOptions c = parse("0208ffffffffffffffff", ChoiceWithTwoOptions.class); assertNull(c.oid); assertEquals(-1, c.num.intValue()); // OBJECT IDENTIFIER c = parse("060100", ChoiceWithTwoOptions.class); assertEquals("0.0", c.oid); assertNull(c.num); // Empty input try { parse("", ChoiceWithTwoOptions.class); fail(); } catch (Asn1DecodingException expected) {} // Neither of the options match try { // Empty SEQUENCE parse("3000", ChoiceWithTwoOptions.class); fail(); } catch (Asn1DecodingException expected) {} } @Test public void testChoiceWithSameTypedOptions() throws Exception { // The CHOICE can be either a SEQUENCE, an IMPLICIT SEQUENCE, or an EXPLICIT SEQUENCE // SEQUENCE ChoiceWithThreeSequenceOptions c = parse("3000", ChoiceWithThreeSequenceOptions.class); assertNotNull(c.s1); assertNull(c.s2); assertNull(c.s3); // IMPLICIT [0] SEQUENCE c = parse("a000", ChoiceWithThreeSequenceOptions.class); assertNull(c.s1); assertNotNull(c.s2); assertNull(c.s3); // EXPLICIT [0] SEQUENCE c = parse("a1023000", ChoiceWithThreeSequenceOptions.class); assertNull(c.s1); assertNull(c.s2); assertNotNull(c.s3); // INTEGER -- None of the options match try { parse("02010a", ChoiceWithThreeSequenceOptions.class); fail(); } catch (Asn1DecodingException expected) {} } @Test(expected = Asn1DecodingException.class) public void testChoiceWithClashingOptions() throws Exception { // The CHOICE is between INTEGER and INTEGER which clash parse("0200", ChoiceWithClashingOptions.class); } @Test public void testPrimitiveIndefiniteLengthEncodingWithGarbage() throws Exception { // Indefinite length INTEGER containing what may look like a malformed definite length // INTEGER, followed by an INTEGER. This tests that contents of indefinite length encoded // primitive (i.e., not constructed) data values must not be parsed to locate the 0x00 0x00 // terminator. ByteBuffer input = ByteBuffer.wrap(HexEncoding.decode("0280020401000002010c")); ChoiceWithTwoOptions c = parse(input, ChoiceWithTwoOptions.class); // Check what's remaining in the input buffer assertEquals("02010c", HexEncoding.encode(input)); // Check what was consumed assertEquals(0x020401, c.num.intValue()); // Indefinite length INTEGER containing what may look like a malformed indefinite length // INTEGER, followed by an INTEGER input = ByteBuffer.wrap(HexEncoding.decode("0280028001000002010c")); c = parse(input, ChoiceWithTwoOptions.class); // Check what's remaining in the input buffer assertEquals("02010c", HexEncoding.encode(input)); // Check what was consumed assertEquals(0x028001, c.num.intValue()); } @Test public void testConstructedIndefiniteLengthEncodingWithoutNestedIndefiniteLengthDataValues() throws Exception { // Indefinite length SEQUENCE containing an INTEGER whose encoding contains 0x00 0x00 which // can be misinterpreted as indefinite length encoding terminator of the SEQUENCE, followed // by an INTEGER ByteBuffer input = ByteBuffer.wrap(HexEncoding.decode("308002020000000002010c")); SequenceWithAsn1Opaque c = parse(input, SequenceWithAsn1Opaque.class); // Check what's remaining in the input buffer assertEquals("02010c", HexEncoding.encode(input)); // Check what was read assertEquals("02020000", HexEncoding.encode(c.obj.getEncoded())); } @Test public void testConstructedIndefiniteLengthEncodingWithNestedIndefiniteLengthDataValues() throws Exception { // Indefinite length SEQUENCE containing two INTEGER fields using indefinite // length encoding, followed by an INTEGER. This tests that the 0x00 0x00 terminators used // by the encoding of the two INTEGERs are not confused for the 0x00 0x00 terminator of the // SEQUENCE. ByteBuffer input = ByteBuffer.wrap(HexEncoding.decode("308002800300000280030000020103000002010c")); SequenceWithAsn1Opaque c = parse(input, SequenceWithAsn1Opaque.class); // Check what's remaining in the input buffer assertEquals("02010c", HexEncoding.encode(input)); // Check what was consumed assertEquals("0280030000", HexEncoding.encode(c.obj.getEncoded())); } @Test(expected = Asn1DecodingException.class) public void testConstructedIndefiniteLengthEncodingWithGarbage() throws Exception { // Indefinite length SEQUENCE containing an indefinite length encoded SEQUENCE containing // garbage which doesn't parse as BER, followed by an INTEGER. This tests that contents of // the SEQUENCEs must be parsed to establish where their 0x00 0x00 terminators are located. ByteBuffer input = ByteBuffer.wrap(HexEncoding.decode("3080308002040000000002010c")); parse(input, SequenceWithAsn1Opaque.class); } private static T parse(String hexEncodedInput, Class containerClass) throws Asn1DecodingException { ByteBuffer input = (hexEncodedInput == null) ? null : ByteBuffer.wrap(HexEncoding.decode(hexEncodedInput)); return parse(input, containerClass); } private static T parse(ByteBuffer input, Class containerClass) throws Asn1DecodingException { return Asn1BerParser.parse(input, containerClass); } @Asn1Class(type = Asn1Type.SEQUENCE) public static class EmptySequence {} @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithIntegers { @Asn1Field(index = 1, type = Asn1Type.INTEGER) public int n1; @Asn1Field(index = 2, type = Asn1Type.INTEGER) public long n2; @Asn1Field(index = 3, type = Asn1Type.INTEGER) public Integer n3; @Asn1Field(index = 4, type = Asn1Type.INTEGER) public Long n4; @Asn1Field(index = 5, type = Asn1Type.INTEGER) public BigInteger n5; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithOid { @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) public String oid; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithImplicitOptionalField { @Asn1Field(index = 1, type = Asn1Type.INTEGER) public Integer f1; @Asn1Field(index = 2, type = Asn1Type.INTEGER, optional = true, tagging = Asn1Tagging.IMPLICIT, tagNumber = 1) public Integer f2; @Asn1Field(index = 3, type = Asn1Type.INTEGER) public Integer f3; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithExplicitOptionalField { @Asn1Field(index = 1, type = Asn1Type.INTEGER) public Integer f1; @Asn1Field(index = 2, type = Asn1Type.INTEGER, optional = true, tagging = Asn1Tagging.EXPLICIT, tagNumber = 1) public Integer f2; @Asn1Field(index = 3, type = Asn1Type.INTEGER) public Integer f3; } @Asn1Class(type = Asn1Type.CHOICE) public static class ChoiceWithTwoOptions { @Asn1Field(type = Asn1Type.OBJECT_IDENTIFIER) public String oid; @Asn1Field(type = Asn1Type.INTEGER) public Integer num; } @Asn1Class(type = Asn1Type.CHOICE) public static class ChoiceWithThreeSequenceOptions { @Asn1Field(type = Asn1Type.SEQUENCE) public EmptySequence s1; @Asn1Field(type = Asn1Type.SEQUENCE, tagging = Asn1Tagging.IMPLICIT, tagNumber = 0) public EmptySequence s2; @Asn1Field(type = Asn1Type.SEQUENCE, tagging = Asn1Tagging.EXPLICIT, tagNumber = 1) public EmptySequence s3; } @Asn1Class(type = Asn1Type.CHOICE) public static class ChoiceWithClashingOptions { @Asn1Field(type = Asn1Type.INTEGER) public int n1; @Asn1Field(type = Asn1Type.INTEGER) public Integer n2; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithOctetString { @Asn1Field(index = 0, type = Asn1Type.OCTET_STRING) public ByteBuffer buf; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithBitString { @Asn1Field(index = 0, type = Asn1Type.BIT_STRING) public ByteBuffer buf; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithSequenceOf { @Asn1Field(index = 0, type = Asn1Type.SEQUENCE_OF) public List values; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithSetOf { @Asn1Field(index = 0, type = Asn1Type.SET_OF) public List values; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithAsn1Opaque { @Asn1Field(type = Asn1Type.ANY) public Asn1OpaqueObject obj; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithSequenceOfUnencodedContainers { @Asn1Field(type = Asn1Type.SEQUENCE_OF) public List containers; } @Asn1Class(type = Asn1Type.UNENCODED_CONTAINER) public static class UnencodedContainerWithSetOf { @Asn1Field(type = Asn1Type.SET_OF) public List values; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithBoolean { @Asn1Field(type = Asn1Type.BOOLEAN) public boolean value; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithUTCTime { @Asn1Field(type = Asn1Type.UTC_TIME) public String value; } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithGeneralizedTime { @Asn1Field(type = Asn1Type.GENERALIZED_TIME) public String value; } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_Asn1DerEncoderTest.java0100644 0000000 0000000 00000000034 14763776540 030205 xustar000000000 0000000 28 mtime=1741684064.6300000 src/test/java/com/android/apksig/internal/asn1/Asn1DerEncoderTest.java0100644 0000000 0000000 00000033515 14763776540 024720 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; import com.android.apksig.internal.util.HexEncoding; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class Asn1DerEncoderTest { @Test public void testInteger() throws Exception { assertEquals("3003020100", encodeToHex(new SequenceWithInteger(0))); assertEquals("300302010c", encodeToHex(new SequenceWithInteger(12))); assertEquals("300302017f", encodeToHex(new SequenceWithInteger(0x7f))); assertEquals("3004020200ff", encodeToHex(new SequenceWithInteger(0xff))); assertEquals("30030201ff", encodeToHex(new SequenceWithInteger(-1))); assertEquals("3003020180", encodeToHex(new SequenceWithInteger(-128))); assertEquals("3005020300ffee", encodeToHex(new SequenceWithInteger(0xffee))); assertEquals("300602047fffffff", encodeToHex(new SequenceWithInteger(Integer.MAX_VALUE))); assertEquals("3006020480000000", encodeToHex(new SequenceWithInteger(Integer.MIN_VALUE))); } @Test public void testOctetString() throws Exception { assertEquals( "30050403010203", encodeToHex( new SequenceWithByteBufferOctetString( ByteBuffer.wrap(new byte[] {1, 2, 3})))); assertEquals( "30030401ff", encodeToHex( new SequenceWithByteBufferOctetString( ByteBuffer.wrap(new byte[] {(byte) 0xff})))); assertEquals( "30020400", encodeToHex( new SequenceWithByteBufferOctetString(ByteBuffer.wrap(new byte[0])))); } @Test public void testBitString() throws Exception { assertEquals( "30050303010203", encodeToHex( new SequenceWithByteBufferBitString( ByteBuffer.wrap(new byte[] {1, 2, 3})))); assertEquals( "30030301ff", encodeToHex( new SequenceWithByteBufferBitString( ByteBuffer.wrap(new byte[] {(byte) 0xff})))); assertEquals( "30020300", encodeToHex( new SequenceWithByteBufferBitString(ByteBuffer.wrap(new byte[0])))); } @Test public void testOid() throws Exception { assertEquals("3003060100", encodeToHex(new SequenceWithOid("0.0"))); assertEquals( "300b06092b0601040182371514", encodeToHex(new SequenceWithOid("1.3.6.1.4.1.311.21.20"))); assertEquals( "300b06092a864886f70d010701", encodeToHex(new SequenceWithOid("1.2.840.113549.1.7.1"))); assertEquals( "300b0609608648016503040201", encodeToHex(new SequenceWithOid("2.16.840.1.101.3.4.2.1"))); } @Test public void testChoice() throws Exception { assertEquals("0201ff", encodeToHex(Choice.of(-1))); assertEquals("80092b0601040182371514", encodeToHex(Choice.of("1.3.6.1.4.1.311.21.20"))); } @Test(expected = Asn1EncodingException.class) public void testChoiceWithNoFieldsSet() throws Exception { // CHOICE is required to have exactly one field set encode(new Choice(null, null)); } @Test(expected = Asn1EncodingException.class) public void testChoiceWithMultipleFieldsSet() throws Exception { // CHOICE is required to have exactly one field set encode(new Choice(123, "1.3.6.1.4.1.311.21.20")); } @Test public void testSetOf() throws Exception { assertEquals("3009310702010a020200ff", encodeToHex(SetOfIntegers.of(0x0a, 0xff))); // Reordering the elements of the set should not make a difference to the resulting encoding assertEquals("3009310702010a020200ff", encodeToHex(SetOfIntegers.of(0xff, 0x0a))); assertEquals( "300e310c02010a020200ff0203112233", encodeToHex(SetOfIntegers.of(0xff, 0x0a, 0x112233))); } @Test public void testSequence() throws Exception { assertEquals( "30080201000601000400", encodeToHex(new Sequence(BigInteger.ZERO, "0.0", new byte[0]))); // Optional OBJECT IDENTIFIER not set assertEquals( "30050201000400", encodeToHex(new Sequence(BigInteger.ZERO, null, new byte[0]))); // Required INTEGER not set try { assertEquals( "30050201000400", encodeToHex(new Sequence(null, "0.0", new byte[0]))); fail(); } catch (Asn1EncodingException expected) {} } @Test public void testAsn1Class() throws Exception { assertEquals( "30053003060100", encodeToHex(new SequenceWithAsn1Class(new SequenceWithOid("0.0")))); } @Test public void testOpaque() throws Exception { assertEquals( "3003060100", encodeToHex(new SequenceWithOpaque( new Asn1OpaqueObject(new byte[] {0x06, 0x01, 0x00})))); } @Test public void testBoolean() throws Exception { assertEquals("3003010100", encodeToHex(new SequenceWithBoolean(false))); String value = encodeToHex(new SequenceWithBoolean(true)); // The encoding of a true value can be any non-zero value so verify the static portion of // the encoding of a sequeuence with a boolean, then verify the last byte is non-zero assertEquals("The encoding of a sequence with a boolean is not the expected length.", 10, value.length()); assertEquals( "The prefix of the encoding of a sequence with a boolean is not the expected " + "value.", "30030101", value.substring(0, 8)); assertNotEquals("The encoding of true should be non-zero.", "00", value.substring(8)); } @Test public void testUTCTime() throws Exception { assertEquals("300d170b313231323231313232315a", encodeToHex(new SequenceWithUTCTime("1212211221Z"))); assertEquals("300d170b393931323331323335395a", encodeToHex(new SequenceWithUTCTime("9912312359Z"))); } @Test public void testGeneralizedTime() throws Exception { assertEquals("301518133230313231323231313232302e3939392d3037", encodeToHex(new SequenceWithGeneralizedTime("201212211220.999-07"))); assertEquals("3017181532303338303131393033313430372e3030302b3030", encodeToHex(new SequenceWithGeneralizedTime("20380119031407.000+00"))); } @Test public void testUnencodedContainer() throws Exception { assertEquals("30233021310b30030201003004020200ff310830060204800000003108300602047fffffff", encodeToHex( new SequenceWithSequenceOfUnencodedContainers( Arrays.asList( new UnencodedContainerWithSetOfIntegers( Arrays.asList( new SequenceWithInteger(0), new SequenceWithInteger(255))), new UnencodedContainerWithSetOfIntegers( Arrays.asList( new SequenceWithInteger( Integer.MIN_VALUE))), new UnencodedContainerWithSetOfIntegers( Arrays.asList( new SequenceWithInteger( Integer.MAX_VALUE))))))); } private static byte[] encode(Object obj) throws Asn1EncodingException { return Asn1DerEncoder.encode(obj); } private static String encodeToHex(Object obj) throws Asn1EncodingException { return HexEncoding.encode(encode(obj)); } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithInteger { @Asn1Field(index = 1, type = Asn1Type.INTEGER) public int num; public SequenceWithInteger(int num) { this.num = num; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithOid { @Asn1Field(index = 1, type = Asn1Type.OBJECT_IDENTIFIER) public String oid; public SequenceWithOid(String oid) { this.oid = oid; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithByteBufferOctetString { @Asn1Field(index = 1, type = Asn1Type.OCTET_STRING) public ByteBuffer data; public SequenceWithByteBufferOctetString(ByteBuffer data) { this.data = data; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithByteBufferBitString { @Asn1Field(index = 1, type = Asn1Type.BIT_STRING) public ByteBuffer data; public SequenceWithByteBufferBitString(ByteBuffer data) { this.data = data; } } @Asn1Class(type = Asn1Type.CHOICE) public static class Choice { @Asn1Field(type = Asn1Type.INTEGER) public Integer num; @Asn1Field(type = Asn1Type.OBJECT_IDENTIFIER, tagging = Asn1Tagging.IMPLICIT, tagNumber = 0) public String oid; public Choice(Integer num, String oid) { this.num = num; this.oid = oid; } public static Choice of(int num) { return new Choice(num, null); } public static Choice of(String oid) { return new Choice(null, oid); } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SetOfIntegers { @Asn1Field(type = Asn1Type.SET_OF, elementType = Asn1Type.INTEGER) public List values; public static SetOfIntegers of(Integer... values) { SetOfIntegers result = new SetOfIntegers(); result.values = Arrays.asList(values); return result; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class Sequence { @Asn1Field(type = Asn1Type.INTEGER, index = 0) public BigInteger num; @Asn1Field(type = Asn1Type.OBJECT_IDENTIFIER, index = 1, optional = true) public String oid; @Asn1Field(type = Asn1Type.OCTET_STRING, index = 2) public byte[] octets; public Sequence(BigInteger num, String oid, byte[] octets) { this.num = num; this.oid = oid; this.octets = octets; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithAsn1Class { @Asn1Field(type = Asn1Type.SEQUENCE) public SequenceWithOid seqWithOid; public SequenceWithAsn1Class(SequenceWithOid seqWithOid) { this.seqWithOid = seqWithOid; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithOpaque { @Asn1Field(type = Asn1Type.ANY) public Asn1OpaqueObject obj; public SequenceWithOpaque(Asn1OpaqueObject obj) { this.obj = obj; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithBoolean { @Asn1Field(index = 1, type = Asn1Type.BOOLEAN) public boolean value; public SequenceWithBoolean(boolean value) { this.value = value; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithUTCTime { @Asn1Field(index = 1, type = Asn1Type.UTC_TIME) public String utcTime; public SequenceWithUTCTime(String utcTime) { this.utcTime = utcTime; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithGeneralizedTime { @Asn1Field(index = 1, type = Asn1Type.GENERALIZED_TIME) public String generalizedTime; public SequenceWithGeneralizedTime(String generalizedTime) { this.generalizedTime = generalizedTime; } } @Asn1Class(type = Asn1Type.SEQUENCE) public static class SequenceWithSequenceOfUnencodedContainers { @Asn1Field(index = 1, type = Asn1Type.SEQUENCE_OF) public List containers; public SequenceWithSequenceOfUnencodedContainers( List containers) { this.containers = containers; } } @Asn1Class(type = Asn1Type.UNENCODED_CONTAINER) public static class UnencodedContainerWithSetOfIntegers { @Asn1Field(index = 1, type = Asn1Type.SET_OF) public List values; public UnencodedContainerWithSetOfIntegers(List values) { this.values = values; } } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_ber_0100644 0000000 0000000 00000000034 14763776540 024577 xustar000000000 0000000 28 mtime=1741684064.6310000 src/test/java/com/android/apksig/internal/asn1/ber/0040755 0000000 0000000 00000000000 14763776540 021224 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_ber_AllTests.java0100644 0000000 0000000 00000000034 14763776540 027173 xustar000000000 0000000 28 mtime=1741684064.6310000 src/test/java/com/android/apksig/internal/asn1/ber/AllTests.java0100644 0000000 0000000 00000001633 14763776540 023622 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ BerDataValueTest.class, ByteBufferBerDataValueReaderTest.class, InputStreamBerDataValueReaderTest.class, }) public class AllTests {} ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_ber_BerDataValueReaderTestBase.java0100644 0000000 0000000 00000000034 14763776540 032515 xustar000000000 0000000 28 mtime=1741684064.6310000 src/test/java/com/android/apksig/internal/asn1/ber/BerDataValueReaderTestBase.java0100644 0000000 0000000 00000030645 14763776540 027151 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; import static com.android.apksig.internal.test.MoreAsserts.assertByteBufferEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import com.android.apksig.internal.util.HexEncoding; import org.junit.Test; /** * Base class for unit tests of ASN.1 BER (see {@code X.690}) data value reader implementations. * *

Subclasses need to provide only an implementation of {@link #createReader(byte[])} and * subclass-specific tests. */ public abstract class BerDataValueReaderTestBase { /** * Returns a new reader initialized with the provided input. */ protected abstract BerDataValueReader createReader(byte[] input); @Test public void testEmptyInput() throws Exception { assertNull(readDataValue("")); } @Test public void testEndOfInput() throws Exception { BerDataValueReader reader = createReader("3000"); // SEQUENCE with empty contents assertNotNull(reader.readDataValue()); // End of input has been reached assertNull(reader.readDataValue()); // Null should also be returned on consecutive invocations assertNull(reader.readDataValue()); } @Test public void testSingleByteTagId() throws Exception { BerDataValue dataValue = readDataValue("1000"); assertEquals(BerEncoding.TAG_CLASS_UNIVERSAL, dataValue.getTagClass()); assertFalse(dataValue.isConstructed()); assertEquals(0x10, dataValue.getTagNumber()); dataValue = readDataValue("3900"); assertEquals(BerEncoding.TAG_CLASS_UNIVERSAL, dataValue.getTagClass()); assertTrue(dataValue.isConstructed()); assertEquals(0x19, dataValue.getTagNumber()); dataValue = readDataValue("6700"); assertEquals(BerEncoding.TAG_CLASS_APPLICATION, dataValue.getTagClass()); assertTrue(dataValue.isConstructed()); assertEquals(7, dataValue.getTagNumber()); dataValue = readDataValue("8600"); assertEquals(BerEncoding.TAG_CLASS_CONTEXT_SPECIFIC, dataValue.getTagClass()); assertFalse(dataValue.isConstructed()); assertEquals(6, dataValue.getTagNumber()); dataValue = readDataValue("fe00"); assertEquals(BerEncoding.TAG_CLASS_PRIVATE, dataValue.getTagClass()); assertTrue(dataValue.isConstructed()); assertEquals(0x1e, dataValue.getTagNumber()); } @Test public void testHighTagNumber() throws Exception { assertEquals(7, readDataValue("3f0700").getTagNumber()); assertEquals(7, readDataValue("3f800700").getTagNumber()); assertEquals(7, readDataValue("3f80800700").getTagNumber()); assertEquals(7, readDataValue("3f8080800700").getTagNumber()); assertEquals(7, readDataValue("3f808080808080808080808080808080800700").getTagNumber()); assertEquals(375, readDataValue("3f827700").getTagNumber()); assertEquals(268435455, readDataValue("3fffffff7f00").getTagNumber()); assertEquals(Integer.MAX_VALUE, readDataValue("3f87ffffff7f00").getTagNumber()); } @Test(expected = BerDataValueFormatException.class) public void testHighTagNumberTooLarge() throws Exception { readDataValue("3f888080800000"); // Integer.MAX_VALUE + 1 } // @Test(expected = BerDataValueFormatException.class) public void testTruncatedHighTagNumberLastOctetMissing() throws Exception { readDataValue("9f80"); // terminating octet must not have the highest bit set } @Test(expected = BerDataValueFormatException.class) public void testTruncatedBeforeFirstLengthOctet() throws Exception { readDataValue("30"); } @Test public void testShortFormLength() throws Exception { assertByteBufferEquals(new byte[0], readDataValue("3000").getEncodedContents()); assertByteBufferEquals( HexEncoding.decode("010203"), readDataValue("3003010203").getEncodedContents()); } @Test public void testLongFormLength() throws Exception { assertByteBufferEquals(new byte[0], readDataValue("308100").getEncodedContents()); assertByteBufferEquals( HexEncoding.decode("010203"), readDataValue("30820003010203").getEncodedContents()); assertEquals( 255, readDataValue(concat(HexEncoding.decode("3081ff"), new byte[255])) .getEncodedContents().remaining()); assertEquals( 0x110, readDataValue(concat(HexEncoding.decode("30820110"), new byte[0x110])) .getEncodedContents().remaining()); } @Test(expected = BerDataValueFormatException.class) public void testTruncatedLongFormLengthBeforeFirstLengthByte() throws Exception { readDataValue("3081"); } @Test(expected = BerDataValueFormatException.class) public void testTruncatedLongFormLengthLastLengthByteMissing() throws Exception { readDataValue("308200"); } @Test(expected = BerDataValueFormatException.class) public void testLongFormLengthTooLarge() throws Exception { readDataValue("3084ffffffff"); } @Test public void testIndefiniteFormLength() throws Exception { assertByteBufferEquals(new byte[0], readDataValue("30800000").getEncodedContents()); assertByteBufferEquals( HexEncoding.decode("020103"), readDataValue("30800201030000").getEncodedContents()); assertByteBufferEquals( HexEncoding.decode( "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f"), readDataValue( "0280" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f" + "0000" ).getEncodedContents()); } @Test(expected = BerDataValueFormatException.class) public void testDefiniteLengthContentsTruncatedBeforeFirstContentOctet() throws Exception { readDataValue("3001"); } @Test(expected = BerDataValueFormatException.class) public void testIndefiniteLengthContentsTruncatedBeforeFirstContentOctet() throws Exception { readDataValue("3080"); } @Test(expected = BerDataValueFormatException.class) public void testTruncatedDefiniteLengthContents() throws Exception { readDataValue("30030102"); } @Test(expected = BerDataValueFormatException.class) public void testTruncatedIndefiniteLengthContents() throws Exception { readDataValue("308001020300"); } @Test public void testEmptyDefiniteLengthContents() throws Exception { assertByteBufferEquals(new byte[0], readDataValue("3000").getEncodedContents()); } @Test public void testEmptyIndefiniteLengthContents() throws Exception { assertByteBufferEquals(new byte[0], readDataValue("30800000").getEncodedContents()); } @Test public void testPrimitiveIndefiniteLengthContentsMustNotBeParsed() throws Exception { // INTEGER (0x0203) followed by 0x010000. This could be misinterpreted as INTEGER // (0x0203000001) if the contents of the original INTEGER are parsed to find the 0x00 0x00 // indefinite length terminator. Such parsing must not take place for primitive (i.e., not // constructed) values. assertEquals( "0203", HexEncoding.encode(readDataValue("028002030000010000").getEncodedContents())); } @Test public void testConstructedIndefiniteLengthContentsContainingIndefiniteLengthEncodedValues() throws Exception { // Indefinite length SEQUENCE containing elements which themselves use indefinite length // encoding, followed by INTEGER (0x0e). assertEquals( "3080028001000000000280020000", HexEncoding.encode(readDataValue( "30803080028001000000000280020000000002010c").getEncodedContents())); } @Test(expected = BerDataValueFormatException.class) public void testConstructedIndefiniteLengthContentsContainingGarbage() throws Exception { // Indefinite length SEQUENCE containing truncated data value. Parsing is expected to fail // because the value of the sequence must be parsed (and this will fail because of garbage) // to establish where to look for the 0x00 0x00 indefinite length terminator of the // SEQUENCE. readDataValue("3080020a030000"); } @Test public void testReadAdvancesPosition() throws Exception { BerDataValueReader reader = createReader("37018f050001020304"); assertByteBufferEquals(HexEncoding.decode("37018f"), reader.readDataValue().getEncoded()); assertByteBufferEquals(HexEncoding.decode("0500"), reader.readDataValue().getEncoded()); assertByteBufferEquals(HexEncoding.decode("01020304"), reader.readDataValue().getEncoded()); assertNull(reader.readDataValue()); } private BerDataValueReader createReader(String hexEncodedInput) { return createReader(HexEncoding.decode(hexEncodedInput)); } private BerDataValue readDataValue(byte[] input) throws BerDataValueFormatException { return createReader(input).readDataValue(); } private BerDataValue readDataValue(String hexEncodedInput) throws BerDataValueFormatException { return createReader(hexEncodedInput).readDataValue(); } private static byte[] concat(byte[] arr1, byte[] arr2) { byte[] result = new byte[arr1.length + arr2.length]; System.arraycopy(arr1, 0, result, 0, arr1.length); System.arraycopy(arr2, 0, result, arr1.length, arr2.length); return result; } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_ber_BerDataValueTest.java0100644 0000000 0000000 00000000034 14763776540 030577 xustar000000000 0000000 28 mtime=1741684064.6310000 src/test/java/com/android/apksig/internal/asn1/ber/BerDataValueTest.java0100644 0000000 0000000 00000011571 14763776540 025230 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; import static com.android.apksig.internal.test.MoreAsserts.assertByteBufferEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import com.android.apksig.internal.util.HexEncoding; import java.nio.ByteBuffer; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class BerDataValueTest { private static final BerDataValue TEST_VALUE1 = new BerDataValue( ByteBuffer.wrap(HexEncoding.decode("aa")), ByteBuffer.wrap(HexEncoding.decode("bb")), BerEncoding.TAG_CLASS_UNIVERSAL, true, BerEncoding.TAG_NUMBER_SEQUENCE); private static final BerDataValue TEST_VALUE2 = new BerDataValue( ByteBuffer.wrap(HexEncoding.decode("cc")), ByteBuffer.wrap(HexEncoding.decode("dd")), BerEncoding.TAG_CLASS_CONTEXT_SPECIFIC, false, BerEncoding.TAG_NUMBER_OCTET_STRING); @Test public void testGetTagClass() { assertEquals(BerEncoding.TAG_CLASS_UNIVERSAL, TEST_VALUE1.getTagClass()); assertEquals(BerEncoding.TAG_CLASS_CONTEXT_SPECIFIC, TEST_VALUE2.getTagClass()); } @Test public void testIsConstructed() { assertTrue(TEST_VALUE1.isConstructed()); assertFalse(TEST_VALUE2.isConstructed()); } @Test public void testGetTagNumber() { assertEquals(BerEncoding.TAG_NUMBER_SEQUENCE, TEST_VALUE1.getTagNumber()); assertEquals(BerEncoding.TAG_NUMBER_OCTET_STRING, TEST_VALUE2.getTagNumber()); } @Test public void testGetEncoded() { assertByteBufferEquals(HexEncoding.decode("aa"), TEST_VALUE1.getEncoded()); assertByteBufferEquals(HexEncoding.decode("cc"), TEST_VALUE2.getEncoded()); } @Test public void testGetEncodedReturnsSlice() { // Assert that changing the position of returned ByteBuffer does not affect ByteBuffers // returned in the future ByteBuffer encoded = TEST_VALUE1.getEncoded(); assertByteBufferEquals(HexEncoding.decode("aa"), encoded); encoded.position(encoded.limit()); assertByteBufferEquals(HexEncoding.decode("aa"), TEST_VALUE1.getEncoded()); } @Test public void testGetEncodedContents() { assertByteBufferEquals(HexEncoding.decode("bb"), TEST_VALUE1.getEncodedContents()); assertByteBufferEquals(HexEncoding.decode("dd"), TEST_VALUE2.getEncodedContents()); } @Test public void testGetEncodedContentsReturnsSlice() { // Assert that changing the position of returned ByteBuffer does not affect ByteBuffers // returned in the future ByteBuffer encoded = TEST_VALUE1.getEncodedContents(); assertByteBufferEquals(HexEncoding.decode("bb"), encoded); encoded.position(encoded.limit()); assertByteBufferEquals(HexEncoding.decode("bb"), TEST_VALUE1.getEncodedContents()); } @Test public void testDataValueReader() throws BerDataValueFormatException { BerDataValueReader reader = TEST_VALUE1.dataValueReader(); assertSame(TEST_VALUE1, reader.readDataValue()); assertNull(reader.readDataValue()); assertNull(reader.readDataValue()); } @Test public void testContentsReader() throws BerDataValueFormatException { BerDataValue dataValue = new BerDataValue( ByteBuffer.allocate(0), ByteBuffer.wrap(HexEncoding.decode("300203040500")), BerEncoding.TAG_CLASS_UNIVERSAL, true, BerEncoding.TAG_NUMBER_SEQUENCE); BerDataValueReader reader = dataValue.contentsReader(); assertEquals(ByteBufferBerDataValueReader.class, reader.getClass()); assertByteBufferEquals(HexEncoding.decode("30020304"), reader.readDataValue().getEncoded()); assertByteBufferEquals(HexEncoding.decode("0500"), reader.readDataValue().getEncoded()); assertNull(reader.readDataValue()); } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_ber_ByteBufferBerDataValueReaderTest.0100644 0000000 0000000 00000000034 14763776540 033036 xustar000000000 0000000 28 mtime=1741684064.6310000 src/test/java/com/android/apksig/internal/asn1/ber/ByteBufferBerDataValueReaderTest.java0100644 0000000 0000000 00000002322 14763776540 030323 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; import java.nio.ByteBuffer; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class ByteBufferBerDataValueReaderTest extends BerDataValueReaderTestBase { @Override protected ByteBufferBerDataValueReader createReader(byte[] input) { return new ByteBufferBerDataValueReader(ByteBuffer.wrap(input)); } @Test(expected = NullPointerException.class) public void testConstructWithNullByteBuffer() throws Exception { new ByteBufferBerDataValueReader(null); } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_asn1_ber_InputStreamBerDataValueReaderTest0100644 0000000 0000000 00000000034 14763776540 033176 xustar000000000 0000000 28 mtime=1741684064.6320000 src/test/java/com/android/apksig/internal/asn1/ber/InputStreamBerDataValueReaderTest.java0100644 0000000 0000000 00000002350 14763776540 030542 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.asn1.ber; import java.io.ByteArrayInputStream; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class InputStreamBerDataValueReaderTest extends BerDataValueReaderTestBase { @Override protected InputStreamBerDataValueReader createReader(byte[] input) { return new InputStreamBerDataValueReader(new ByteArrayInputStream(input)); } @Test(expected = NullPointerException.class) public void testConstructWithNullByteBuffer() throws Exception { new InputStreamBerDataValueReader(null); } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_test_0100644 0000000 0000000 00000000034 14763776540 024064 xustar000000000 0000000 28 mtime=1741684064.6320000 src/test/java/com/android/apksig/internal/test/0040755 0000000 0000000 00000000000 14763776540 020571 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_internal_test_MoreAsserts.java0100644 0000000 0000000 00000000034 14763776540 027174 xustar000000000 0000000 28 mtime=1741684064.6320000 src/test/java/com/android/apksig/internal/test/MoreAsserts.java0100644 0000000 0000000 00000003566 14763776540 023712 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.test; import static org.junit.Assert.assertArrayEquals; import java.nio.ByteBuffer; public abstract class MoreAsserts { private MoreAsserts() {} /** * Asserts that the contents of the provided {@code ByteBuffer} are as expected. This method * does not change the position or the limit of the provided buffer. */ public static void assertByteBufferEquals(byte[] expected, ByteBuffer actual) { assertByteBufferEquals(null, expected, actual); } /** * Asserts that the contents of the provided {@code ByteBuffer} are as expected. This method * does not change the position or the limit of the provided buffer. */ public static void assertByteBufferEquals(String message, byte[] expected, ByteBuffer actual) { byte[] actualArr; if ((actual.hasArray()) && (actual.arrayOffset() == 0) && (actual.array().length == actual.remaining())) { actualArr = actual.array(); } else { actualArr = new byte[actual.remaining()]; int actualOriginalPos = actual.position(); actual.get(actualArr); actual.position(actualOriginalPos); } assertArrayEquals(message, expected, actualArr); } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_0100644 0000000 0000000 00000000034 14763776540 024062 xustar000000000 0000000 28 mtime=1741684064.6320000 src/test/java/com/android/apksig/internal/util/0040755 0000000 0000000 00000000000 14763776540 020567 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_AllTests.java0100644 0000000 0000000 00000000034 14763776540 026456 xustar000000000 0000000 28 mtime=1741684064.6320000 src/test/java/com/android/apksig/internal/util/AllTests.java0100644 0000000 0000000 00000001725 14763776540 023167 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ ArrayBackedByteBufferSinkTest.class, ChainedDataSourceTest.class, DirectByteBufferSinkTest.class, VerityTreeBuilderTest.class, X509CertificateUtilsTest.class, }) public class AllTests {} ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_ArrayBackedByteBufferSinkTest.java0100644 0000000 0000000 00000000034 14763776540 032536 xustar000000000 0000000 28 mtime=1741684064.6320000 src/test/java/com/android/apksig/internal/util/ArrayBackedByteBufferSinkTest.java0100644 0000000 0000000 00000001714 14763776540 027245 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import java.nio.ByteBuffer; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class ArrayBackedByteBufferSinkTest extends ByteBufferSinkTestBase { @Override protected ByteBuffer createBuffer(int size) { return ByteBuffer.allocate(size); } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_ByteBufferSinkTestBase.java0100644 0000000 0000000 00000000034 14763776540 031240 xustar000000000 0000000 28 mtime=1741684064.6320000 src/test/java/com/android/apksig/internal/util/ByteBufferSinkTestBase.java0100644 0000000 0000000 00000003446 14763776540 025753 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.util.DataSinkTestBase; import java.io.IOException; import java.nio.ByteBuffer; public abstract class ByteBufferSinkTestBase extends DataSinkTestBase { private static final int START_POS = 100; protected abstract ByteBuffer createBuffer(int size); @Override protected CloseableWithDataSink createDataSink() { ByteBuffer buf = createBuffer(1024); // Use non-zero position and limit which isn't set to capacity to catch the implementation // under test ignoring the initial position. buf.position(START_POS); buf.limit(buf.capacity() - 300); return CloseableWithDataSink.of(new ByteBufferSink(buf)); } @Override protected ByteBuffer getContents(ByteBufferSink dataSink) throws IOException { ByteBuffer buf = dataSink.getBuffer(); int oldPos = buf.position(); int oldLimit = buf.limit(); try { buf.position(START_POS); buf.limit(oldPos); return buf.slice(); } finally { buf.limit(oldLimit); buf.position(oldPos); } } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_ChainedDataSourceTest.java0100644 0000000 0000000 00000000034 14763776540 031071 xustar000000000 0000000 28 mtime=1741684064.6320000 src/test/java/com/android/apksig/internal/util/ChainedDataSourceTest.java0100644 0000000 0000000 00000011177 14763776540 025604 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import static java.nio.charset.StandardCharsets.US_ASCII; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import com.android.apksig.util.DataSources; import com.android.apksig.util.DataSinks; import com.android.apksig.util.ReadableDataSink; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.nio.ByteBuffer; /** Unit tests for {@link ChainedDataSource}. */ @RunWith(JUnit4.class) public final class ChainedDataSourceTest { private ChainedDataSource mChain; @Before public void setUp() { mChain = new ChainedDataSource( DataSources.asDataSource(ByteBuffer.wrap("12".getBytes(US_ASCII))), DataSources.asDataSource(ByteBuffer.wrap("34567".getBytes(US_ASCII))), DataSources.asDataSource(ByteBuffer.wrap("".getBytes(US_ASCII))), DataSources.asDataSource(ByteBuffer.wrap("890".getBytes(US_ASCII))), DataSources.asDataSource(ByteBuffer.wrap("".getBytes(US_ASCII)))); assertEquals(10, mChain.size()); } @Test public void feedAllPossibleRanges() throws Exception { for (int begin = 0; begin < mChain.size(); begin++) { for (int end = begin + 1; end < mChain.size(); end++) { int size = end - begin; ReadableDataSink sink = DataSinks.newInMemoryDataSink(size); mChain.feed(begin, size, sink); assertByteBufferEquals( ByteBuffer.wrap("1234567890".substring(begin, end).getBytes(US_ASCII)), sink.getByteBuffer(0, size)); } } } @Test(expected=IndexOutOfBoundsException.class) public void feedMoreThanAvailable() throws Exception { mChain.feed(0, mChain.size() + 1, DataSinks.newInMemoryDataSink(3)); } @Test public void getByteBufferFromAllPossibleRanges() throws Exception { for (int begin = 0; begin < mChain.size(); begin++) { for (int end = begin + 1; end < mChain.size(); end++) { int size = end - begin; ByteBuffer buffer = mChain.getByteBuffer(begin, size); assertByteBufferEquals( ByteBuffer.wrap("1234567890".substring(begin, end).getBytes(US_ASCII)), buffer); } } } @Test(expected=IndexOutOfBoundsException.class) public void getByteBufferForMoreThanAvailable() throws Exception { mChain.getByteBuffer(0, (int) mChain.size() + 1); } @Test public void copyTo() throws Exception { for (int begin = 0; begin < mChain.size(); begin++) { for (int end = begin + 1; end < mChain.size(); end++) { int size = end - begin; ByteBuffer buffer = ByteBuffer.allocate(size); mChain.copyTo(begin, size, buffer); assertEquals(size, buffer.position()); buffer.rewind(); assertByteBufferEquals( ByteBuffer.wrap("1234567890".substring(begin, end).getBytes(US_ASCII)), buffer); } } } @Test public void slice() throws Exception { for (int begin = 0; begin < mChain.size(); begin++) { for (int end = begin + 1; end < mChain.size(); end++) { int size = end - begin; ByteBuffer buffer = mChain.slice(begin, size).getByteBuffer(0, size); assertByteBufferEquals( ByteBuffer.wrap("1234567890".substring(begin, end).getBytes(US_ASCII)), buffer); } } } private void assertByteBufferEquals(ByteBuffer buffer, ByteBuffer buffer2) { assertTrue(buffer.toString() + " vs " + buffer2.toString() + ", byte array: " + HexEncoding.encode(buffer.array()) + " vs " + HexEncoding.encode(buffer2.array()), buffer.compareTo(buffer2) == 0); } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_DirectByteBufferSinkTest.java0100644 0000000 0000000 00000000034 14763776540 031600 xustar000000000 0000000 28 mtime=1741684064.6330000 src/test/java/com/android/apksig/internal/util/DirectByteBufferSinkTest.java0100644 0000000 0000000 00000001715 14763776540 026310 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import java.nio.ByteBuffer; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class DirectByteBufferSinkTest extends ByteBufferSinkTestBase { @Override protected ByteBuffer createBuffer(int size) { return ByteBuffer.allocateDirect(size); } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_FileChannelDataSourceTest.java0100644 0000000 0000000 00000000034 14763776540 031706 xustar000000000 0000000 28 mtime=1741684064.6330000 src/test/java/com/android/apksig/internal/util/FileChannelDataSourceTest.java0100644 0000000 0000000 00000010072 14763776540 026412 0ustar000000000 0000000 /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import static org.junit.Assert.assertArrayEquals; import com.android.apksig.util.DataSource; import java.io.File; import java.io.FileOutputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.Arrays; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class FileChannelDataSourceTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void testFeedsCorrectData_whenFilePartiallyReadFromBeginning() throws Exception { byte[] fullFileContent = createFileContent(1024 * 1024 + 987654); RandomAccessFile raf = createRaf(fullFileContent); DataSource rafDataSource = new FileChannelDataSource(raf.getChannel()); ByteArrayDataSink dataSink = new ByteArrayDataSink(); int bytesToFeed = 1024 * 1024 + 12345; rafDataSource.feed(0, bytesToFeed, dataSink); byte[] expectedBytes = Arrays.copyOf(fullFileContent, bytesToFeed); byte[] resultBytes = getDataSinkBytes(dataSink); assertArrayEquals(expectedBytes, resultBytes); } @Test public void testFeedsCorrectData_whenFilePartiallyReadWithOffset() throws Exception { byte[] fullFileContent = createFileContent(1024 * 1024 + 987654); RandomAccessFile raf = createRaf(fullFileContent); DataSource rafDataSource = new FileChannelDataSource(raf.getChannel()); ByteArrayDataSink dataSink = new ByteArrayDataSink(); int offset = 23456; int bytesToFeed = 1024 * 1024 + 12345; rafDataSource.feed(offset, bytesToFeed, dataSink); byte[] expectedBytes = Arrays.copyOfRange(fullFileContent, offset, offset + bytesToFeed); byte[] resultBytes = getDataSinkBytes(dataSink); assertArrayEquals(expectedBytes, resultBytes); } @Test public void testFeedsCorrectData_whenSeveralMbRead() throws Exception { byte[] fullFileContent = createFileContent(3 * 1024 * 1024 + 987654); RandomAccessFile raf = createRaf(fullFileContent); DataSource rafDataSource = new FileChannelDataSource(raf.getChannel()); ByteArrayDataSink dataSink = new ByteArrayDataSink(); int offset = 23456; int bytesToFeed = 2 * 1024 * 1024 + 12345; rafDataSource.feed(offset, bytesToFeed, dataSink); byte[] expectedBytes = Arrays.copyOfRange(fullFileContent, offset, offset + bytesToFeed); byte[] resultBytes = getDataSinkBytes(dataSink); assertArrayEquals(expectedBytes, resultBytes); } private static byte[] getDataSinkBytes(ByteArrayDataSink dataSink) { ByteBuffer result = dataSink.getByteBuffer(0, (int)dataSink.size()); byte[] resultBytes = new byte[result.limit()]; result.get(resultBytes); return resultBytes; } private static byte[] createFileContent(int fileSize) { byte[] fullFileContent = new byte[fileSize]; for (int i = 0; i < fileSize; ++i) { fullFileContent[i] = (byte) (i % 255); } return fullFileContent; } private RandomAccessFile createRaf(byte[] content) throws Exception { File dataFile = temporaryFolder.newFile(); try (FileOutputStream fos = new FileOutputStream(dataFile)) { fos.write(content); } return new RandomAccessFile(dataFile, "r"); } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_HexEncoding.java0100644 0000000 0000000 00000000034 14763776540 027116 xustar000000000 0000000 28 mtime=1741684064.6330000 src/test/java/com/android/apksig/internal/util/HexEncoding.java0100644 0000000 0000000 00000006474 14763776540 023635 0ustar000000000 0000000 /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import java.nio.ByteBuffer; /** * Hexadecimal encoding where each byte is represented by two hexadecimal digits. */ public class HexEncoding { /** Hidden constructor to prevent instantiation. */ private HexEncoding() {} private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); /** * Encodes the provided data as a hexadecimal string. */ public static String encode(byte[] data) { return encode(data, 0, data.length); } /** * Encodes the provided data as a hexadecimal string. */ public static String encode(byte[] data, int offset, int len) { StringBuilder result = new StringBuilder(len * 2); for (int i = 0; i < len; i++) { byte b = data[offset + i]; result.append(HEX_DIGITS[(b >>> 4) & 0x0f]); result.append(HEX_DIGITS[b & 0x0f]); } return result.toString(); } /** * Encodes the provided data as a hexadecimal string. */ public static String encode(ByteBuffer buf) { return encode(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining()); } /** * Decodes the provided hexadecimal string into an array of bytes. */ public static byte[] decode(String encoded) { // IMPLEMENTATION NOTE: Special care is taken to permit odd number of hexadecimal digits. int resultLengthBytes = (encoded.length() + 1) / 2; byte[] result = new byte[resultLengthBytes]; int resultOffset = 0; int encodedCharOffset = 0; if ((encoded.length() % 2) != 0) { // Odd number of digits -- the first digit is the lower 4 bits of the first result byte. result[resultOffset++] = (byte) getHexadecimalDigitValue(encoded.charAt(encodedCharOffset)); encodedCharOffset++; } for (int len = encoded.length(); encodedCharOffset < len; encodedCharOffset += 2) { result[resultOffset++] = (byte) ((getHexadecimalDigitValue(encoded.charAt(encodedCharOffset)) << 4) | getHexadecimalDigitValue(encoded.charAt(encodedCharOffset + 1))); } return result; } private static int getHexadecimalDigitValue(char c) { if ((c >= 'a') && (c <= 'f')) { return (c - 'a') + 0x0a; } else if ((c >= 'A') && (c <= 'F')) { return (c - 'A') + 0x0a; } else if ((c >= '0') && (c <= '9')) { return c - '0'; } else { throw new IllegalArgumentException( "Invalid hexadecimal digit at position : '" + c + "' (0x" + Integer.toHexString(c) + ")"); } } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_Resources.java0100644 0000000 0000000 00000000034 14763776540 026675 xustar000000000 0000000 28 mtime=1741684064.6330000 src/test/java/com/android/apksig/internal/util/Resources.java0100644 0000000 0000000 00000020212 14763776540 023376 0ustar000000000 0000000 /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import com.android.apksig.ApkSignerTest; import com.android.apksig.KeyConfig; import com.android.apksig.SigningCertificateLineage; import com.android.apksig.util.DataSource; import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Locale; /** * Assorted methods to obtaining test input from resources. */ public final class Resources { private Resources() {} // All signers with the same prefix and an _X suffix were signed with the private key of the // (X-1) signer. public static final String FIRST_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048"; public static final String SECOND_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_2"; public static final String THIRD_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_3"; public static final String FIRST_RSA_1024_SIGNER_RESOURCE_NAME = "rsa-1024"; public static final String SECOND_RSA_1024_SIGNER_RESOURCE_NAME = "rsa-1024_2"; public static final String FIRST_RSA_4096_SIGNER_RESOURCE_NAME = "rsa-4096"; public static final String EC_P256_SIGNER_RESOURCE_NAME = "ec-p256"; public static final String EC_P256_2_SIGNER_RESOURCE_NAME = "ec-p256_2"; // This is the same cert as above with the modulus reencoded to remove the leading 0 sign bit. public static final String FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS = "rsa-2048_negmod.x509.der"; public static final String LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME = "rsa-2048-lineage-2-signers"; public static final String LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME = "rsa-2048-lineage-3-signers"; public static final String LINEAGE_RSA_2048_3_SIGNERS_1_NO_CAPS_RESOURCE_NAME = "rsa-2048-lineage-3-signers-1-no-caps"; public static final String LINEAGE_RSA_2048_2_SIGNERS_2_3_RESOURCE_NAME = "rsa-2048-lineage-2-signers-2-3"; public static final String LINEAGE_RSA_2048_TO_RSA_4096_RESOURCE_NAME = "rsa-2048-to-4096-lineage-2-signers"; public static final String LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME = "ec-p256-lineage-2-signers"; public static byte[] toByteArray(Class cls, String resourceName) throws IOException { try (InputStream in = cls.getResourceAsStream(resourceName)) { if (in == null) { throw new IllegalArgumentException("Resource not found: " + resourceName); } return ByteStreams.toByteArray(in); } } public static InputStream toInputStream(Class cls, String resourceName) throws IOException { InputStream in = cls.getResourceAsStream(resourceName); if (in == null) { throw new IllegalArgumentException("Resource not found: " + resourceName); } return in; } public static X509Certificate toCertificate( Class cls, String resourceName) throws IOException, CertificateException { try (InputStream in = cls.getResourceAsStream(resourceName)) { if (in == null) { throw new IllegalArgumentException("Resource not found: " + resourceName); } return X509CertificateUtils.generateCertificate(in); } } public static List toCertificateChain( Class cls, String resourceName) throws IOException, CertificateException { Collection certs; try (InputStream in = cls.getResourceAsStream(resourceName)) { if (in == null) { throw new IllegalArgumentException("Resource not found: " + resourceName); } certs = X509CertificateUtils.generateCertificates(in); } List result = new ArrayList<>(certs.size()); for (Certificate cert : certs) { result.add((X509Certificate) cert); } return result; } public static PrivateKey toPrivateKey(Class cls, String resourceName) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { int delimiterIndex = resourceName.indexOf('-'); if (delimiterIndex == -1) { throw new IllegalArgumentException( "Failed to autodetect key algorithm from resource name: " + resourceName); } String keyAlgorithm = resourceName.substring(0, delimiterIndex).toUpperCase(Locale.US); return toPrivateKey(cls, resourceName, keyAlgorithm); } public static PrivateKey toPrivateKey( Class cls, String resourceName, String keyAlgorithm) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { byte[] encoded = toByteArray(cls, resourceName); // Keep overly strictly linter happy by limiting what JCA KeyFactory algorithms are used // here KeyFactory keyFactory; switch (keyAlgorithm.toUpperCase(Locale.US)) { case "RSA": keyFactory = KeyFactory.getInstance("rsa"); break; case "DSA": keyFactory = KeyFactory.getInstance("dsa"); break; case "EC": keyFactory = KeyFactory.getInstance("ec"); break; default: throw new InvalidKeySpecException("Unsupported key algorithm: " + keyAlgorithm); } return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); } public static SigningCertificateLineage.SignerConfig toLineageSignerConfig(Class cls, String resourcePrefix) throws Exception { PrivateKey privateKey = toPrivateKey(cls, resourcePrefix + ".pk8"); X509Certificate cert = Resources.toCertificate(cls, resourcePrefix + ".x509.pem"); return new SigningCertificateLineage.SignerConfig.Builder( new KeyConfig.Jca(privateKey), cert) .build(); } public static DataSource toDataSource(Class cls, String dataSourceResourceName) throws IOException { return new ByteBufferDataSource(ByteBuffer.wrap(Resources .toByteArray(ApkSignerTest.class, dataSourceResourceName))); } public static SigningCertificateLineage toSigningCertificateLineage(Class cls, String fileResourceName) throws IOException { DataSource lineageDataSource = toDataSource(cls, fileResourceName); return SigningCertificateLineage.readFromDataSource(lineageDataSource); } public static File toFile(Class cls, String fileResourceName, TemporaryFolder temporaryFolder) throws IOException { File outFile = temporaryFolder.newFile(); try (InputStream in = cls.getResourceAsStream(fileResourceName); OutputStream out = new FileOutputStream(outFile)) { if (in == null) { throw new IllegalArgumentException("Resource not found: " + fileResourceName); } in.transferTo(out); return outFile; } } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_VerityTreeBuilderTest.java0100644 0000000 0000000 00000000034 14763776540 031174 xustar000000000 0000000 28 mtime=1741684064.6330000 src/test/java/com/android/apksig/internal/util/VerityTreeBuilderTest.java0100644 0000000 0000000 00000012621 14763776540 025702 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import static java.nio.charset.StandardCharsets.UTF_8; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.fail; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.IOException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; /** Unit tests for {@link VerityTreeBuilder}. */ @RunWith(JUnit4.class) public final class VerityTreeBuilderTest { @Test public void SHA256RootHashMatch() throws Exception { expectRootHash("random-data-4096-bytes", null, "a3b013ea0f5d5ffbda26d5e84882faa4c051d592c04b8779bd1f0f4e95cc2657"); expectRootHash("random-data-4096-bytes", new byte[] { }, "a3b013ea0f5d5ffbda26d5e84882faa4c051d592c04b8779bd1f0f4e95cc2657"); expectRootHash("random-data-4096-bytes", new byte[] { 0x00 }, "cab1bcac3cf9b91151730c0de1880112d2c9865543d3fa56b534273c06667973"); expectRootHash("random-data-8192-bytes", new byte[] { 0x10 }, "477afbaa5e884454bb95a8d63366362c21c0a5d7b8e5476b004692bf9a692a00"); // 524289 requires additional tree level. expectRootHash("random-data-524287-bytes", new byte[] { 0x20 }, "39534c3a0bd27efcafb408e630248082156c80ab41789814749b33e325a93c62"); expectRootHash("random-data-524288-bytes", new byte[] { 0x21 }, "34f8b9c33cd49b753b9341b2d8a4b83e59c5ae458ec6a85fbfebd49314c24d4e"); expectRootHash("random-data-524289-bytes", new byte[] { 0x22 }, "1934793602f5e0b8c7aa7ed7e7acb42dca579ed11d8ac5ff9bb6d346f4222bd5"); expectRootHash("random-data-525000-bytes", new byte[] { 0x23 }, "f63b718c01f569386d7de2e813d7b1e452322c638fb240af3ef01c2e6d317ee8"); } private static void expectRootHash(String inputResource, byte[] salt, String expectedRootHash) throws IOException { assertEquals(expectedRootHash, generateRootHash(inputResource, salt)); } private static String generateRootHash(String inputResource, byte[] salt) throws IOException { byte[] input = Resources.toByteArray(VerityTreeBuilderTest.class, inputResource); assertNotNull(input); try (VerityTreeBuilder builder = new VerityTreeBuilder(salt)) { return HexEncoding.encode(builder.generateVerityTreeRootHash( DataSources.asDataSource(ByteBuffer.wrap(input)))); } catch (NoSuchAlgorithmException e) { fail(e.getMessage()); return null; } } private DataSource makeStringDataSource(String data) { return DataSources.asDataSource(ByteBuffer.wrap(data.getBytes(UTF_8))); } @Test public void generateVerityTreeRootHashFromPlaceholderDataSource() throws Exception { // This sample was taken from src/test/resources/com/android/apksig/original.apk. byte[] sampleEoCDFromDisk = new byte[] { 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x79, 0x01, 0x00, 0x00, 0x30, 0x16, 0x00, 0x00, 0x00, 0x00 }; String expectedRootHash = "7694e72c242107a5b4ce6091faf867e2f13c033b6b64faddcb13b3d698a8495a"; try (VerityTreeBuilder builder = new VerityTreeBuilder(null)) { byte[] rootHash = builder.generateVerityTreeRootHash( DataSources.asDataSource(ByteBuffer.allocate(4096)), // before Signing Block makeStringDataSource("this is central directory (fake data)"), DataSources.asDataSource(ByteBuffer.wrap(sampleEoCDFromDisk))); assertEquals(expectedRootHash, HexEncoding.encode(rootHash)); } // File ending with different length of zeros get the same hash. This is to match the // behavior of fs-verity, where it expects the client to add salt for the same level of // protection. try (VerityTreeBuilder builder = new VerityTreeBuilder(null)) { ByteBuffer fileTailWithDifferentLengthOfZeros = ByteBuffer.allocate( sampleEoCDFromDisk.length + 1); fileTailWithDifferentLengthOfZeros.put(sampleEoCDFromDisk); fileTailWithDifferentLengthOfZeros.rewind(); byte[] rootHash = builder.generateVerityTreeRootHash( DataSources.asDataSource(ByteBuffer.allocate(4096)), // before Signing Block makeStringDataSource("this is central directory (fake data)"), DataSources.asDataSource(fileTailWithDifferentLengthOfZeros)); assertEquals(expectedRootHash, HexEncoding.encode(rootHash)); } } } ./PaxHeaders.X/src_test_java_com_android_apksig_internal_util_X509CertificateUtilsTest.java0100644 0000000 0000000 00000000034 14763776540 031414 xustar000000000 0000000 28 mtime=1741684064.6340000 src/test/java/com/android/apksig/internal/util/X509CertificateUtilsTest.java0100644 0000000 0000000 00000024316 14763776540 026126 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.internal.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @RunWith(JUnit4.class) public class X509CertificateUtilsTest { // The PEM and DER encodings of a certificate without redundant length bytes; since the // certificates are the same they have the same hex encoding of their digest. public static final String RSA_2048_VALID_PEM_ENCODING = "rsa-2048.x509.pem"; public static final String RSA_2048_VALID_DER_ENCODING = "rsa-2048.x509.der"; public static final String RSA_2048_VALID_DIGEST_HEX_ENCODING = "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8"; // The PEM and DER encodings of a certificate with redundant length bytes; valid DER encodings // require that the length of contents within the encoding be specified with the minimum number // of bytes, but BER encodings allow redundant '00' bytes when specifying length. public static final String RSA_2048_REDUNDANT_LEN_BYTES_PEM_ENCODING = "rsa-2048-redun-len.x509.pem"; public static final String RSA_2048_REDUNDANT_LEN_BYTES_DER_ENCODING = "rsa-2048-redun-len.x509.der"; public static final String RSA_2048_REDUNDANT_LEN_DIGEST_HEX_ENCODING = "38481f124f8af6c36017abdfbefe375157ac304fb90adaa641ecba71b08dcd0f"; // The PEM and DER encodings of both the valid and redundant length byte certificates above. public static final String RSA_2048_TWO_CERTS_PEM_ENCODING = "rsa-2048-2-certs.x509.pem"; public static final String RSA_2048_TWO_CERTS_DER_ENCODING = "rsa-2048-2-certs.x509.der"; @Test public void testGenerateCertificateWithValidPEMEncoding() throws Exception { // The generateCertificate method should support both PEM and DER encodings; since the PEM // format is just the DER encoding base64'd with a header and a footer this test verifies // that a valid DER encoding in PEM format is successfully parsed and returns the expected // encoding. assertEquals(RSA_2048_VALID_DIGEST_HEX_ENCODING, getHexEncodedDigestOfCertFromResources(RSA_2048_VALID_PEM_ENCODING)); } @Test public void testGenerateCertificateWithRedundantLengthBytesInPEMEncoding() throws Exception { // This test verifies that a BER encoding of a certificate with redundant length bytes // can still be successfully parsed and returns the expected unmodified encoding. assertEquals(RSA_2048_REDUNDANT_LEN_DIGEST_HEX_ENCODING, getHexEncodedDigestOfCertFromResources(RSA_2048_REDUNDANT_LEN_BYTES_PEM_ENCODING)); } @Test public void testGenerateCertificateWithValidDEREncoding() throws Exception { // This test verifies the generateCertificate method successfully parses and returns the // expected encoding of a certificate with a valid DER encoding. assertEquals(RSA_2048_VALID_DIGEST_HEX_ENCODING, getHexEncodedDigestOfCertFromResources(RSA_2048_VALID_DER_ENCODING)); } @Test public void testGenerateCertificateWithRedundantLengthBytesInDERENcoding() throws Exception { // This test verifies the generateCertificate method successfully parses and returns the // original encoding of a certificate with redundant length bytes in the encoding. assertEquals(RSA_2048_REDUNDANT_LEN_DIGEST_HEX_ENCODING, getHexEncodedDigestOfCertFromResources(RSA_2048_REDUNDANT_LEN_BYTES_DER_ENCODING)); } @Test public void testGenerateCertificatesWithTwoPEMEncodedCerts() throws Exception { // The generateCertificates method accepts an InputStream which could contain zero or more // certificates in PEM or DER encoding; this test verifies both certificates in PEM format // are returned with the expected encodings. List encodedCerts = getHexEncodedDigestsOfCertsFromResources( RSA_2048_TWO_CERTS_PEM_ENCODING); Set expectedEncodings = createSetOfValues(RSA_2048_VALID_DIGEST_HEX_ENCODING, RSA_2048_REDUNDANT_LEN_DIGEST_HEX_ENCODING); assertEncodingsMatchExpectedValues(encodedCerts, expectedEncodings); } @Test public void testGenerateCertificatesWithTwoDEREncodedCerts() throws Exception { // This test verifies the generateCertificates method returns the expected encodings for // an InputStream with both DER encoded certificates. List encodedCerts = getHexEncodedDigestsOfCertsFromResources( RSA_2048_TWO_CERTS_DER_ENCODING); Set expectedEncodings = createSetOfValues(RSA_2048_VALID_DIGEST_HEX_ENCODING, RSA_2048_REDUNDANT_LEN_DIGEST_HEX_ENCODING); assertEncodingsMatchExpectedValues(encodedCerts, expectedEncodings); } @Test public void testGenerateCertificateAndGenerateCertificatesReturnSameValues() throws Exception { // The generateCertificates method is intended to read multiple certificates in the provided // InputStream, but it can also read a single certificate. Verify that both // generateCertificate and generateCertificates return the same encodings for the same // certificates. List certResources = Arrays.asList(RSA_2048_VALID_PEM_ENCODING, RSA_2048_VALID_DER_ENCODING, RSA_2048_REDUNDANT_LEN_BYTES_PEM_ENCODING, RSA_2048_REDUNDANT_LEN_BYTES_DER_ENCODING); for (String certResource : certResources) { String genCertValue = getHexEncodedDigestOfCertFromResources(certResource); List genCertsValues = getHexEncodedDigestsOfCertsFromResources(certResource); assertEquals( "The generateCertificates method should have returned a single certificate", 1, genCertsValues.size()); assertEquals( "The hex encoded digest of the certificate from generateCertificate does not " + "match the value from generateCertificates", genCertValue, genCertsValues.get(0)); } } @Test public void testGenerateCertificatesWithEmptyInput() throws Exception { // This test verifies the generateCertificates method returns an empty Collection of // Certificates when provided an empty InputStream. assertEquals( "Zero certificates should be returned when passing an empty InputStream to " + "generateCertificates", 0, X509CertificateUtils.generateCertificates( new ByteArrayInputStream(new byte[0])).size()); } private static Set createSetOfValues(String... values) { Set result = new HashSet<>(); for (String value : values) { result.add(value); } return result; } /** * Returns a hex encoding of the digest of the specified certificate from the resources. */ private static String getHexEncodedDigestOfCertFromResources(String resourceName) throws Exception { byte[] encodedForm = Resources.toByteArray(X509CertificateUtilsTest.class, resourceName); X509Certificate cert = X509CertificateUtils.generateCertificate(encodedForm); return getHexEncodedDigestOfBytes(cert.getEncoded()); } /** * Returns a list of hex encodings of the digests of the certificates in the specified resource. */ private static List getHexEncodedDigestsOfCertsFromResources(String resourceName) throws Exception { InputStream in = Resources.toInputStream(X509CertificateUtilsTest.class, resourceName); Collection certs = X509CertificateUtils.generateCertificates(in); List encodedCerts = new ArrayList<>(certs.size()); for (Certificate cert : certs) { encodedCerts.add(getHexEncodedDigestOfBytes(cert.getEncoded())); } return encodedCerts; } /** * Returns the hex encoding of the digest of the specified bytes. */ private static String getHexEncodedDigestOfBytes(byte[] bytes) throws NoSuchAlgorithmException { return HexEncoding.encode(MessageDigest.getInstance("SHA-256").digest(bytes)); } /** * Asserts that the encoding of the provided certificates match the expected values. */ private static void assertEncodingsMatchExpectedValues(List encodedCerts, Set expectedValues) { assertEquals( "The number of encoded certificates does not match the expected number of values", expectedValues.size(), encodedCerts.size()); for (String encodedCert : encodedCerts) { // if the current encoding is found in the expected Set then remove it to ensure that // duplicate values do not cause the test to pass if they are not expected. if (expectedValues.contains(encodedCert)) { expectedValues.remove(encodedCert); } else { fail("An unexpected certificate with the following encoding was returned: " + encodedCert); } } } } ./PaxHeaders.X/src_test_java_com_android_apksig_util_0100644 0000000 0000000 00000000034 14763776540 022166 xustar000000000 0000000 28 mtime=1741684064.6340000 src/test/java/com/android/apksig/util/0040755 0000000 0000000 00000000000 14763776540 016753 5ustar000000000 0000000 ./PaxHeaders.X/src_test_java_com_android_apksig_util_AllTests.java0100644 0000000 0000000 00000000034 14763776540 024562 xustar000000000 0000000 28 mtime=1741684064.6340000 src/test/java/com/android/apksig/util/AllTests.java0100644 0000000 0000000 00000002031 14763776540 021342 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ DataSinkFromOutputStreamTest.class, DataSinkFromRAFTest.class, DataSourceFromByteBufferTest.class, DataSourceFromRAFChunkTest.class, DataSourceFromRAFTest.class, InMemoryDataSinkDataSourceTest.class, InMemoryDataSinkTest.class, }) public class AllTests {} ./PaxHeaders.X/src_test_java_com_android_apksig_util_DataSinkFromOutputStreamTest.java0100644 0000000 0000000 00000000034 14763776540 030606 xustar000000000 0000000 28 mtime=1741684064.6340000 src/test/java/com/android/apksig/util/DataSinkFromOutputStreamTest.java0100644 0000000 0000000 00000003004 14763776540 025367 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import com.android.apksig.internal.util.OutputStreamDataSink; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for the {@link DataSink} returned by {@link DataSinks#asDataSink(java.io.OutputStream)}. */ @RunWith(JUnit4.class) public class DataSinkFromOutputStreamTest extends DataSinkTestBase { @Override protected CloseableWithDataSink createDataSink() { return CloseableWithDataSink.of( (OutputStreamDataSink) DataSinks.asDataSink(new ByteArrayOutputStream())); } @Override protected ByteBuffer getContents(OutputStreamDataSink dataSink) throws IOException { return ByteBuffer.wrap(((ByteArrayOutputStream) dataSink.getOutputStream()).toByteArray()); } } ./PaxHeaders.X/src_test_java_com_android_apksig_util_DataSinkFromRAFTest.java0100644 0000000 0000000 00000000034 14763776540 026542 xustar000000000 0000000 28 mtime=1741684064.6350000 src/test/java/com/android/apksig/util/DataSinkFromRAFTest.java0100644 0000000 0000000 00000004144 14763776540 023331 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import com.android.apksig.internal.util.RandomAccessFileDataSink; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for the {@link DataSink} returned by * {@link DataSinks#asDataSink(java.io.RandomAccessFile)}. */ @RunWith(JUnit4.class) public class DataSinkFromRAFTest extends DataSinkTestBase { @Override protected CloseableWithDataSink createDataSink() throws IOException { File tmp = File.createTempFile(DataSourceFromRAFTest.class.getSimpleName(), ".bin"); RandomAccessFile f = null; try { f = new RandomAccessFile(tmp, "rw"); } finally { if (f == null) { tmp.delete(); } } return CloseableWithDataSink.of( (RandomAccessFileDataSink) DataSinks.asDataSink(f), new DataSourceFromRAFTest.TmpFileCloseable(tmp, f)); } @Override protected ByteBuffer getContents(RandomAccessFileDataSink dataSink) throws IOException { RandomAccessFile f = dataSink.getFile(); if (f.length() > Integer.MAX_VALUE) { throw new IOException("File too large: " + f.length()); } byte[] contents = new byte[(int) f.length()]; f.seek(0); f.readFully(contents); return ByteBuffer.wrap(contents); } } ./PaxHeaders.X/src_test_java_com_android_apksig_util_DataSinkTestBase.java0100644 0000000 0000000 00000000034 14763776540 026160 xustar000000000 0000000 28 mtime=1741684064.6350000 src/test/java/com/android/apksig/util/DataSinkTestBase.java0100644 0000000 0000000 00000013024 14763776540 022744 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.junit.Test; /** * Base class for testing implementations of {@link DataSink}. This class tests the contract of * {@code DataSink}. * *

To subclass, provide an implementation of {@link #createDataSink()} which returns the * implementation of {@code DataSink} you want to test. */ public abstract class DataSinkTestBase { /** * Returns a new {@link DataSink}. */ protected abstract CloseableWithDataSink createDataSink() throws IOException; /** * Returns the contents of the data sink. */ protected abstract ByteBuffer getContents(T dataSink) throws IOException; @Test public void testConsumeFromArray() throws Exception { try (CloseableWithDataSink c = createDataSink()) { T sink = c.getDataSink(); byte[] input = "abcdefg".getBytes(StandardCharsets.UTF_8); sink.consume(input, 2, 3); // "cde" sink.consume(input, 0, 1); // "a" assertContentsEquals("cdea", sink); // Zero-length chunks sink.consume(input, 0, 0); sink.consume(input, 1, 0); sink.consume(input, input.length - 2, 0); sink.consume(input, input.length - 1, 0); sink.consume(input, input.length, 0); // Invalid chunks assertConsumeArrayThrowsIOOB(sink, input, -1, 0); assertConsumeArrayThrowsIOOB(sink, input, -1, 3); assertConsumeArrayThrowsIOOB(sink, input, 0, input.length + 1); assertConsumeArrayThrowsIOOB(sink, input, input.length - 2, 4); assertConsumeArrayThrowsIOOB(sink, input, input.length + 1, 0); assertConsumeArrayThrowsIOOB(sink, input, input.length + 1, 1); assertContentsEquals("cdea", sink); } } @Test public void testConsumeFromByteBuffer() throws Exception { try (CloseableWithDataSink c = createDataSink()) { T sink = c.getDataSink(); ByteBuffer input = ByteBuffer.wrap("abcdefg".getBytes(StandardCharsets.UTF_8)); input.position(2); input.limit(5); sink.consume(input); // "cde" assertEquals(5, input.position()); assertEquals(5, input.limit()); input.position(0); input.limit(1); sink.consume(input); // "a" assertContentsEquals("cdea", sink); // Empty input sink.consume(input); assertContentsEquals("cdea", sink); // ByteBuffer which isn't backed by a byte[] input = ByteBuffer.allocateDirect(2); input.put((byte) 'X'); input.put((byte) 'Z'); input.flip(); sink.consume(input); assertContentsEquals("cdeaXZ", sink); assertEquals(2, input.position()); assertEquals(2, input.limit()); // Empty input sink.consume(input); assertContentsEquals("cdeaXZ", sink); } } /** * Returns the contents of the provided buffer as a string. The buffer's position and limit * remain unchanged. */ private static String toString(ByteBuffer buf) { return DataSourceTestBase.toString(buf); } private void assertContentsEquals(String expectedContents, T sink) throws IOException { ByteBuffer actual = getContents(sink); assertEquals(expectedContents, toString(actual)); } private static void assertConsumeArrayThrowsIOOB( DataSink sink, byte[] arr, int offset, int length) throws IOException { try { sink.consume(arr, offset, length); fail(); } catch (IndexOutOfBoundsException expected) {} } public static class CloseableWithDataSink implements Closeable { private final T mDataSink; private final Closeable mCloseable; private CloseableWithDataSink(T dataSink, Closeable closeable) { mDataSink = dataSink; mCloseable = closeable; } public static CloseableWithDataSink of(T dataSink) { return new CloseableWithDataSink<>(dataSink, null); } public static CloseableWithDataSink of( T dataSink, Closeable closeable) { return new CloseableWithDataSink<>(dataSink, closeable); } public T getDataSink() { return mDataSink; } public Closeable getCloseable() { return mCloseable; } @Override public void close() throws IOException { if (mCloseable != null) { mCloseable.close(); } } } } ./PaxHeaders.X/src_test_java_com_android_apksig_util_DataSourceFromByteBufferTest.java0100644 0000000 0000000 00000000034 14763776540 030523 xustar000000000 0000000 28 mtime=1741684064.6350000 src/test/java/com/android/apksig/util/DataSourceFromByteBufferTest.java0100644 0000000 0000000 00000003453 14763776540 025314 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for the {@link DataSource} returned by {@link DataSources#asDataSource(ByteBuffer)}. */ @RunWith(JUnit4.class) public class DataSourceFromByteBufferTest extends DataSourceTestBase { @Test public void testChangesToBufferPosAndLimitNotVisible() throws Exception { ByteBuffer buf = ByteBuffer.wrap("abcdefgh".getBytes(StandardCharsets.UTF_8)); buf.position(1); buf.limit(4); DataSource ds = DataSources.asDataSource(buf); buf.position(2); buf.limit(buf.capacity()); assertGetByteBufferEquals("bcd", ds, 0, (int) ds.size()); assertFeedEquals("bcd", ds, 0, (int) ds.size()); assertSliceEquals("bcd", ds, 0, (int) ds.size()); assertCopyToEquals("bcd", ds, 0, (int) ds.size()); } @Override protected CloseableWithDataSource createDataSource(byte[] contents) throws IOException { return CloseableWithDataSource.of(DataSources.asDataSource(ByteBuffer.wrap(contents))); } } ./PaxHeaders.X/src_test_java_com_android_apksig_util_DataSourceFromRAFChunkTest.java0100644 0000000 0000000 00000000034 14763776540 030067 xustar000000000 0000000 28 mtime=1741684064.6350000 src/test/java/com/android/apksig/util/DataSourceFromRAFChunkTest.java0100644 0000000 0000000 00000007223 14763776540 024657 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import com.android.apksig.util.DataSourceFromRAFTest.TmpFileCloseable; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; /** * Tests for the {@link DataSource} returned by * {@link DataSources#asDataSource(RandomAccessFile, long, long)}. */ @RunWith(Parameterized.class) public class DataSourceFromRAFChunkTest extends DataSourceTestBase { @Parameterized.Parameters(name = "{0}") public static DataSourceFromRAFFactory[] data() { return DataSourceFromRAFFactory.values(); } @Parameterized.Parameter public DataSourceFromRAFFactory factory; @Test public void testFileSizeChangesNotVisible() throws Exception { try (CloseableWithDataSource c = createDataSource("abcdefg")) { DataSource ds = c.getDataSource(); DataSource slice = ds.slice(3, 2); File f = ((TmpFileCloseable) c.getCloseable()).getFile(); assertGetByteBufferEquals("abcdefg", ds, 0, (int) ds.size()); assertGetByteBufferEquals("de", slice, 0, (int) slice.size()); assertFeedEquals("cdefg", ds, 2, 5); assertFeedEquals("e", slice, 1, 1); assertCopyToEquals("cdefg", ds, 2, 5); assertCopyToEquals("e", slice, 1, 1); assertSliceEquals("cdefg", ds, 2, 5); assertSliceEquals("e", slice, 1, 1); try (RandomAccessFile raf = new RandomAccessFile(f, "rw")) { raf.seek(raf.length()); raf.write("hijkl".getBytes(StandardCharsets.UTF_8)); } assertGetByteBufferEquals("abcdefg", ds, 0, (int) ds.size()); assertGetByteBufferEquals("de", slice, 0, (int) slice.size()); assertGetByteBufferThrowsIOOB(ds, 0, (int) ds.size() + 3); assertFeedThrowsIOOB(ds, 0, (int) ds.size() + 3); assertSliceThrowsIOOB(ds, 0, (int) ds.size() + 3); assertCopyToThrowsIOOB(ds, 0, (int) ds.size() + 3); } } @Override protected CloseableWithDataSource createDataSource(byte[] contents) throws IOException { // "01" | contents | "9" byte[] fullContents = new byte[2 + contents.length + 1]; fullContents[0] = '0'; fullContents[1] = '1'; System.arraycopy(contents, 0, fullContents, 2, contents.length); fullContents[fullContents.length - 1] = '9'; File tmp = File.createTempFile(DataSourceFromRAFChunkTest.class.getSimpleName(), ".bin"); RandomAccessFile f = null; try { Files.write(tmp.toPath(), fullContents); f = new RandomAccessFile(tmp, "r"); } finally { if (f == null) { tmp.delete(); } } return CloseableWithDataSource.of( factory.create(f, 2, contents.length), new DataSourceFromRAFTest.TmpFileCloseable(tmp, f)); } } ./PaxHeaders.X/src_test_java_com_android_apksig_util_DataSourceFromRAFFactory.java0100644 0000000 0000000 00000000034 14763776540 027566 xustar000000000 0000000 28 mtime=1741684064.6350000 src/test/java/com/android/apksig/util/DataSourceFromRAFFactory.java0100644 0000000 0000000 00000002744 14763776540 024361 0ustar000000000 0000000 /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import java.io.RandomAccessFile; enum DataSourceFromRAFFactory { RANDOM_ACCESS_FILE { @Override DataSource create(RandomAccessFile file) { return DataSources.asDataSource(file); } @Override DataSource create(RandomAccessFile file, long offset, long size) { return DataSources.asDataSource(file, offset, size); } }, FILE_CHANNEL { @Override DataSource create(RandomAccessFile file) { return DataSources.asDataSource(file.getChannel()); } @Override DataSource create(RandomAccessFile file, long offset, long size) { return DataSources.asDataSource(file.getChannel(), offset, size); } }; abstract DataSource create(RandomAccessFile file); abstract DataSource create(RandomAccessFile file, long offset, long size); } ./PaxHeaders.X/src_test_java_com_android_apksig_util_DataSourceFromRAFTest.java0100644 0000000 0000000 00000000034 14763776540 027076 xustar000000000 0000000 28 mtime=1741684064.6350000 src/test/java/com/android/apksig/util/DataSourceFromRAFTest.java0100644 0000000 0000000 00000010033 14763776540 023657 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import static org.junit.Assert.assertEquals; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; /** * Tests for the {@link DataSource} returned by * {@link DataSources#asDataSource(java.io.RandomAccessFile)}. */ @RunWith(Parameterized.class) public class DataSourceFromRAFTest extends DataSourceTestBase { @Parameterized.Parameters(name = "{0}") public static DataSourceFromRAFFactory[] data() { return DataSourceFromRAFFactory.values(); } @Parameterized.Parameter public DataSourceFromRAFFactory factory; @Test public void testFileSizeChangesVisible() throws Exception { try (CloseableWithDataSource c = createDataSource("abcdefg")) { DataSource ds = c.getDataSource(); DataSource slice = ds.slice(3, 2); File f = ((TmpFileCloseable) c.getCloseable()).getFile(); assertGetByteBufferEquals("abcdefg", ds, 0, (int) ds.size()); assertGetByteBufferEquals("de", slice, 0, (int) slice.size()); assertFeedEquals("cdefg", ds, 2, 5); assertFeedEquals("e", slice, 1, 1); assertCopyToEquals("cdefg", ds, 2, 5); assertCopyToEquals("e", slice, 1, 1); assertSliceEquals("cdefg", ds, 2, 5); assertSliceEquals("e", slice, 1, 1); try (RandomAccessFile raf = new RandomAccessFile(f, "rw")) { raf.seek(7); raf.write("hijkl".getBytes(StandardCharsets.UTF_8)); } assertEquals(12, ds.size()); assertGetByteBufferEquals("abcdefghijkl", ds, 0, (int) ds.size()); assertGetByteBufferEquals("de", slice, 0, (int) slice.size()); assertFeedEquals("cdefg", ds, 2, 5); assertFeedEquals("fgh", ds, 5, 3); assertCopyToEquals("fgh", ds, 5, 3); assertSliceEquals("fgh", ds, 5, 3); } } @Override protected CloseableWithDataSource createDataSource(byte[] contents) throws IOException { File tmp = File.createTempFile(DataSourceFromRAFTest.class.getSimpleName(), ".bin"); RandomAccessFile f = null; try { Files.write(tmp.toPath(), contents); f = new RandomAccessFile(tmp, "r"); } finally { if (f == null) { tmp.delete(); } } return CloseableWithDataSource.of( factory.create(f), new TmpFileCloseable(tmp, f)); } /** * {@link Closeable} which closes the delegate {@code Closeable} and deletes the provided file. */ static class TmpFileCloseable implements Closeable { private final File mFile; private final Closeable mDelegate; TmpFileCloseable(File file, Closeable closeable) { mFile = file; mDelegate = closeable; } File getFile() { return mFile; } @Override public void close() throws IOException { try { if (mDelegate != null) { mDelegate.close(); } } finally { if (mFile != null) { mFile.delete(); } } } } } ./PaxHeaders.X/src_test_java_com_android_apksig_util_DataSourceTestBase.java0100644 0000000 0000000 00000000034 14763776540 026514 xustar000000000 0000000 28 mtime=1741684064.6350000 src/test/java/com/android/apksig/util/DataSourceTestBase.java0100644 0000000 0000000 00000035625 14763776540 023313 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.io.Closeable; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.junit.Test; /** * Base class for testing implementations of {@link DataSource}. This class tests the contract of * {@code DataSource}. * *

To subclass, provide an implementation of {@link #createDataSource(byte[])} which returns * the implementation of {@code DataSource} you want to test. */ public abstract class DataSourceTestBase { /** * Returns a new {@link DataSource} containing the provided contents. */ protected abstract CloseableWithDataSource createDataSource(byte[] contents) throws IOException; protected CloseableWithDataSource createDataSource(String contents) throws IOException { return createDataSource(contents.getBytes(StandardCharsets.UTF_8)); } @Test public void testSize() throws Exception { try (CloseableWithDataSource c = createDataSource("Hello12345")) { DataSource ds = c.getDataSource(); assertEquals(10, ds.size()); } } @Test public void testSlice() throws Exception { try (CloseableWithDataSource c = createDataSource("Hello12345")) { DataSource ds = c.getDataSource(); assertSliceEquals("123", ds, 5, 3); DataSource slice = ds.slice(3, 5); assertGetByteBufferEquals("lo123", slice, 0, 5); // Zero-length slices assertSliceEquals("", ds, 0, 0); assertSliceEquals("", ds, 1, 0); assertSliceEquals("", ds, ds.size() - 2, 0); assertSliceEquals("", ds, ds.size() - 1, 0); assertSliceEquals("", ds, ds.size(), 0); assertSliceEquals("", slice, 0, 0); assertSliceEquals("", slice, 1, 0); assertSliceEquals("", slice, slice.size() - 2, 0); assertSliceEquals("", slice, slice.size() - 1, 0); assertSliceEquals("", slice, slice.size(), 0); // Invalid slices assertSliceThrowsIOOB(ds, -1, 0); assertSliceThrowsIOOB(slice, -1, 0); assertSliceThrowsIOOB(ds, -1, 2); assertSliceThrowsIOOB(slice, -1, 2); assertSliceThrowsIOOB(ds, -1, 20); assertSliceThrowsIOOB(slice, -1, 20); assertSliceThrowsIOOB(ds, 1, 20); assertSliceThrowsIOOB(slice, 1, 20); assertSliceThrowsIOOB(ds, ds.size() + 1, 0); assertSliceThrowsIOOB(slice, slice.size() + 1, 0); assertSliceThrowsIOOB(ds, ds.size(), 1); assertSliceThrowsIOOB(slice, slice.size(), 1); assertSliceThrowsIOOB(ds, ds.size() - 1, -1); assertSliceThrowsIOOB(ds, slice.size() - 1, -1); } } @Test public void testGetByteBuffer() throws Exception { try (CloseableWithDataSource c = createDataSource("test1234")) { DataSource ds = c.getDataSource(); assertGetByteBufferEquals("s", ds, 2, 1); DataSource slice = ds.slice(3, 4); // "t123" assertGetByteBufferEquals("2", slice, 2, 1); // Zero-length chunks assertEquals(0, ds.getByteBuffer(0, 0).capacity()); assertEquals(0, ds.getByteBuffer(ds.size(), 0).capacity()); assertEquals(0, ds.getByteBuffer(ds.size() - 1, 0).capacity()); assertEquals(0, ds.getByteBuffer(ds.size() - 2, 0).capacity()); assertEquals(0, slice.getByteBuffer(0, 0).capacity()); assertEquals(0, slice.getByteBuffer(slice.size(), 0).capacity()); assertEquals(0, slice.getByteBuffer(slice.size() - 1, 0).capacity()); assertEquals(0, slice.getByteBuffer(slice.size() - 2, 0).capacity()); // Invalid chunks assertGetByteBufferThrowsIOOB(ds, -1, 0); assertGetByteBufferThrowsIOOB(slice, -1, 0); assertGetByteBufferThrowsIOOB(ds, -1, 2); assertGetByteBufferThrowsIOOB(slice, -1, 2); assertGetByteBufferThrowsIOOB(ds, -1, 20); assertGetByteBufferThrowsIOOB(slice, -1, 20); assertGetByteBufferThrowsIOOB(ds, 1, 20); assertGetByteBufferThrowsIOOB(slice, 1, 20); assertGetByteBufferThrowsIOOB(ds, ds.size() + 1, 0); assertGetByteBufferThrowsIOOB(slice, slice.size() + 1, 0); assertGetByteBufferThrowsIOOB(ds, ds.size(), 1); assertGetByteBufferThrowsIOOB(slice, slice.size(), 1); assertGetByteBufferThrowsIOOB(ds, ds.size() - 1, -1); assertGetByteBufferThrowsIOOB(ds, slice.size() - 1, -1); } } @Test public void testFeed() throws Exception { try (CloseableWithDataSource c = createDataSource("test1234")) { DataSource ds = c.getDataSource(); assertFeedEquals("23", ds, 5, 2); DataSource slice = ds.slice(1, 5); // "est12" assertFeedEquals("t", slice, 2, 1); // Zero-length chunks assertFeedEquals("", ds, 0, 0); assertFeedEquals("", ds, 1, 0); assertFeedEquals("", ds, ds.size() - 2, 0); assertFeedEquals("", ds, ds.size() - 1, 0); assertFeedEquals("", ds, ds.size(), 0); assertFeedEquals("", slice, 0, 0); assertFeedEquals("", slice, 2, 0); assertFeedEquals("", slice, slice.size() - 2, 0); assertFeedEquals("", slice, slice.size() - 1, 0); assertFeedEquals("", slice, slice.size(), 0); // Invalid chunks assertFeedThrowsIOOB(ds, -1, 0); assertFeedThrowsIOOB(slice, -1, 0); assertFeedThrowsIOOB(ds, -1, 2); assertFeedThrowsIOOB(slice, -1, 2); assertFeedThrowsIOOB(ds, -1, 10); assertFeedThrowsIOOB(slice, -1, 10); assertFeedThrowsIOOB(ds, 1, 10); assertFeedThrowsIOOB(slice, 1, 10); assertFeedThrowsIOOB(ds, ds.size() + 1, 0); assertFeedThrowsIOOB(slice, slice.size() + 1, 0); assertFeedThrowsIOOB(ds, ds.size(), 1); assertFeedThrowsIOOB(slice, slice.size(), 1); assertFeedThrowsIOOB(ds, ds.size() - 1, -1); assertFeedThrowsIOOB(ds, slice.size() - 1, -1); } } @Test public void testCopyTo() throws Exception { try (CloseableWithDataSource c = createDataSource("abcdefghijklmnop")) { DataSource ds = c.getDataSource(); assertCopyToEquals("fgh", ds, 5, 3); DataSource slice = ds.slice(2, 7); // "cdefghi" assertCopyToEquals("efgh", slice, 2, 4); // Zero-length chunks assertCopyToEquals("", ds, 0, 0); assertCopyToEquals("", ds, 1, 0); assertCopyToEquals("", ds, ds.size() - 2, 0); assertCopyToEquals("", ds, ds.size() - 1, 0); assertCopyToEquals("", ds, ds.size(), 0); assertCopyToEquals("", slice, 0, 0); assertCopyToEquals("", slice, 2, 0); assertCopyToEquals("", slice, slice.size() - 2, 0); assertCopyToEquals("", slice, slice.size() - 1, 0); assertCopyToEquals("", slice, slice.size(), 0); // Invalid chunks assertCopyToThrowsIOOB(ds, -1, 0); assertCopyToThrowsIOOB(slice, -1, 0); assertCopyToThrowsIOOB(ds, -1, 2); assertCopyToThrowsIOOB(slice, -1, 2); assertCopyToThrowsIOOB(ds, -1, 20); assertCopyToThrowsIOOB(slice, -1, 20); assertCopyToThrowsIOOB(ds, 1, 20); assertCopyToThrowsIOOB(slice, 1, 20); assertCopyToThrowsIOOB(ds, ds.size() + 1, 0); assertCopyToThrowsIOOB(slice, slice.size() + 1, 0); assertCopyToThrowsIOOB(ds, ds.size(), 1); assertCopyToThrowsIOOB(slice, slice.size(), 1); assertCopyToThrowsIOOB(ds, ds.size() - 1, -1); assertCopyToThrowsIOOB(ds, slice.size() - 1, -1); // Destination buffer too small ByteBuffer buf = ByteBuffer.allocate(5); buf.position(2); buf.limit(3); assertCopyToThrowsBufferOverflow(ds, 0, 2, buf); buf.position(2); buf.limit(3); assertCopyToThrowsBufferOverflow(slice, 1, 2, buf); // Destination buffer larger than chunk copied using copyTo buf = ByteBuffer.allocate(10); buf.position(2); assertCopyToEquals("bcd", ds, 1, 3, buf); buf = ByteBuffer.allocate(10); buf.position(2); assertCopyToEquals("fg", slice, 3, 2, buf); } } protected static void assertSliceEquals( String expectedContents, DataSource ds, long offset, int size) throws IOException { DataSource slice = ds.slice(offset, size); assertEquals(size, slice.size()); assertGetByteBufferEquals(expectedContents, slice, 0, size); } protected static void assertSliceThrowsIOOB(DataSource ds, long offset, int size) { try { ds.slice(offset, size); fail(); } catch (IndexOutOfBoundsException expected) {} } protected static void assertGetByteBufferEquals( String expectedContents, DataSource ds, long offset, int size) throws IOException { ByteBuffer buf = ds.getByteBuffer(offset, size); assertEquals(0, buf.position()); assertEquals(size, buf.limit()); assertEquals(size, buf.capacity()); assertEquals(expectedContents, toString(buf)); } protected static void assertGetByteBufferThrowsIOOB(DataSource ds, long offset, int size) throws IOException { try { ds.getByteBuffer(offset, size); fail(); } catch (IndexOutOfBoundsException expected) {} } protected static void assertFeedEquals( String expectedFedContents, DataSource ds, long offset, int size) throws IOException { ReadableDataSink out = DataSinks.newInMemoryDataSink(size); ds.feed(offset, size, out); assertEquals(size, out.size()); assertEquals(expectedFedContents, toString(out.getByteBuffer(0, size))); } protected static void assertFeedThrowsIOOB(DataSource ds, long offset, long size) throws IOException { try { ds.feed(offset, size, NullDataSink.INSTANCE); fail(); } catch (IndexOutOfBoundsException expected) {} } protected static void assertCopyToEquals( String expectedContents, DataSource ds, long offset, int size) throws IOException { // Create a ByteBuffer backed by a section of a byte array. The ByteBuffer is on purpose not // starting at offset 0 to catch issues to do with not checking ByteBuffer.arrayOffset(). byte[] arr = new byte[size + 10]; ByteBuffer buf = ByteBuffer.wrap(arr, 1, size + 5); // Use non-zero position to catch issues with not checking buf.position() buf.position(2); // Buffer contains sufficient space for the requested copyTo operation assertEquals(size + 4, buf.remaining()); assertCopyToEquals(expectedContents, ds, offset, size, buf); } private static void assertCopyToEquals( String expectedContents, DataSource ds, long offset, int size, ByteBuffer buf) throws IOException { int oldPosition = buf.position(); int oldLimit = buf.limit(); ds.copyTo(offset, size, buf); // Position should've advanced by size whereas limit should've remained unchanged assertEquals(oldPosition + size, buf.position()); assertEquals(oldLimit, buf.limit()); buf.limit(buf.position()); buf.position(oldPosition); assertEquals(expectedContents, toString(buf)); } protected static void assertCopyToThrowsIOOB(DataSource ds, long offset, int size) throws IOException { ByteBuffer buf = ByteBuffer.allocate((size < 0) ? 0 : size); try { ds.copyTo(offset, size, buf); fail(); } catch (IndexOutOfBoundsException expected) {} } private static void assertCopyToThrowsBufferOverflow( DataSource ds, long offset, int size, ByteBuffer buf) throws IOException { try { ds.copyTo(offset, size, buf); fail(); } catch (BufferOverflowException expected) {} } /** * Returns the contents of the provided buffer as a string. The buffer's position and limit * remain unchanged. */ static String toString(ByteBuffer buf) { byte[] arr; int offset; int size = buf.remaining(); if (buf.hasArray()) { arr = buf.array(); offset = buf.arrayOffset() + buf.position(); } else { arr = new byte[buf.remaining()]; offset = 0; int oldPos = buf.position(); buf.get(arr); buf.position(oldPos); } return new String(arr, offset, size, StandardCharsets.UTF_8); } public static class CloseableWithDataSource implements Closeable { private final DataSource mDataSource; private final Closeable mCloseable; private CloseableWithDataSource(DataSource dataSource, Closeable closeable) { mDataSource = dataSource; mCloseable = closeable; } public static CloseableWithDataSource of(DataSource dataSource) { return new CloseableWithDataSource(dataSource, null); } public static CloseableWithDataSource of(DataSource dataSource, Closeable closeable) { return new CloseableWithDataSource(dataSource, closeable); } public DataSource getDataSource() { return mDataSource; } public Closeable getCloseable() { return mCloseable; } @Override public void close() throws IOException { if (mCloseable != null) { mCloseable.close(); } } } private static final class NullDataSink implements DataSink { private static final NullDataSink INSTANCE = new NullDataSink(); @Override public void consume(byte[] buf, int offset, int length) {} @Override public void consume(ByteBuffer buf) {} } } ./PaxHeaders.X/src_test_java_com_android_apksig_util_InMemoryDataSinkDataSourceTest.java0100644 0000000 0000000 00000000034 14763776540 031020 xustar000000000 0000000 28 mtime=1741684064.6360000 src/test/java/com/android/apksig/util/InMemoryDataSinkDataSourceTest.java0100644 0000000 0000000 00000002310 14763776540 025600 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import java.io.IOException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for the {@link DataSource} returned by {@link DataSinks#newInMemoryDataSink()}. */ @RunWith(JUnit4.class) public class InMemoryDataSinkDataSourceTest extends DataSourceTestBase { @Override protected CloseableWithDataSource createDataSource(byte[] contents) throws IOException { ReadableDataSink sink = DataSinks.newInMemoryDataSink(); sink.consume(contents, 0, contents.length); return CloseableWithDataSource.of(sink); } } ./PaxHeaders.X/src_test_java_com_android_apksig_util_InMemoryDataSinkTest.java0100644 0000000 0000000 00000000034 14763776540 027045 xustar000000000 0000000 28 mtime=1741684064.6360000 src/test/java/com/android/apksig/util/InMemoryDataSinkTest.java0100644 0000000 0000000 00000002652 14763776540 023636 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.apksig.util; import java.io.IOException; import java.nio.ByteBuffer; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for the {@link DataSink} returned by {@link DataSinks#newInMemoryDataSink()}. */ @RunWith(JUnit4.class) public class InMemoryDataSinkTest extends DataSinkTestBase { @Override protected CloseableWithDataSink createDataSink() { return CloseableWithDataSink.of(DataSinks.newInMemoryDataSink()); } @Override protected ByteBuffer getContents(ReadableDataSink dataSink) throws IOException { if (dataSink.size() > Integer.MAX_VALUE) { throw new IOException("Too much data: " + dataSink.size()); } return dataSink.getByteBuffer(0, (int) dataSink.size()); } } ./PaxHeaders.X/src_test_resources_0100644 0000000 0000000 00000000034 14763776540 016326 xustar000000000 0000000 28 mtime=1741684064.6360000 src/test/resources/0040755 0000000 0000000 00000000000 14763776540 013413 5ustar000000000 0000000 ./PaxHeaders.X/src_test_resources_com_0100644 0000000 0000000 00000000034 14763776540 017164 xustar000000000 0000000 28 mtime=1741684064.6360000 src/test/resources/com/0040755 0000000 0000000 00000000000 14763776540 014171 5ustar000000000 0000000 ./PaxHeaders.X/src_test_resources_com_android_0100644 0000000 0000000 00000000034 14763776540 020664 xustar000000000 0000000 28 mtime=1741684064.6360000 src/test/resources/com/android/0040755 0000000 0000000 00000000000 14763776540 015611 5ustar000000000 0000000 ./PaxHeaders.X/src_test_resources_com_android_apksig_0100644 0000000 0000000 00000000034 14763776540 022222 xustar000000000 0000000 28 mtime=1741684064.6360000 src/test/resources/com/android/apksig/0040755 0000000 0000000 00000000000 14763776540 017067 5ustar000000000 0000000 ./PaxHeaders.X/src_test_resources_com_android_apksig_debuggable-boolean.apk0100644 0000000 0000000 00000000034 14763776540 026413 xustar000000000 0000000 28 mtime=1741684064.6370000 src/test/resources/com/android/apksig/debuggable-boolean.apk0100644 0000000 0000000 00000030627 14763776540 023267 0ustar000000000 0000000 PK !:;k薸 resources.arsc5 ($hTiny App for CTS [b U b` one two three]  . Tiny,   . App,   . for,   . CTS,   android.appsecurity.cts.tinyapp `@$ attrstring4 app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTMOQ=3eWKB?ZQƕHL4A~ؔR҅ .\ \pNϽ}>^!2͙νF=(k1yC<'G @Alg'CdC:xItcELнh`-Mf8 UTS* v'hFѤflOq =UyF98kQסC#='h>*uQ#SyӗgB͊*ԩ Bרj9:=d r^wZ֗[Ezh3Zg= D{O|B&g!nᶞ2|^|wRAOuDwjҷٓuT!GOK kgDOASv$R'~}P)d]կ󓽗W(c~跎x 50J$&A ^/1{O|QfqsF?&.iMnyMr1Sdžԑ5uNS!r<^}/c/cdw;pYW񚽠W ϙWxK4MK7^p_g?ǯ` %_W>oPK+qPK!: classes.dexTOA;ݖK) S$(%)1QЄa;bAAN&x(GCLBG4Com(PB3 |xxHw/?(ID1E\AL! KD!j԰,:AG$IC;RjmP+n(;Q2d)^kZ]=`jD'Xo3:__H~wEVIAؽ|]Ψ/1_uy7Ktك*"+*>GVOnfAA$5緶7.7;12z1g'pn$7>J_!֊C,|$v}Um`Я>}O%9@7AtwYƑϖMyS˂%i'ܰ6J7O:1B?3/+)rT-ߘ-M [8lצYN>e<{K%m(6?Q_#S9Ğ5'7\:bn,e\륚L0pVKH* /װ0ji"H.jngibd7[Fhez,'xMGVnZu:zyD>m;"s*#ӹaoDXÓo^)ݙ>/Q5C:4,\񑡁[n*ۺtpU͐9IeExP[!i${t jWvFol,{ }g']۾s՘m[/<ӼVRoŸIfO,PK[ PK!:META-INF/MANIFEST.MF]AO0}1f %bqccҴ/Z{ɧDq X,6MLB:A8PpNFh 7EK,Ej.|b1a?~LBLfkYr^R hV\9r+KggOj<{Et(@+ψ'uUoJޛ=?PKJ{h q`\X( yě!FR[OL7W( >j; nKfq:Fk"d(W00 n0  *H 01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 080229013346Z 350717013346Z01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 0  *H  0֓ $b%>>l輨kxn l5Y>wC=KdVg0xCLrF!8N¾8Ϗ0ЧJP" P2j3Y榔,򄠤fz;1 :g7./dnmBX Dlø$XH%dOޘ(wHgjT TȻGU3ko̎iȢЎx& ?r}<rߛ8[j00UHYV=',Ft ʌ0U#0HYV=',Ft ʌ01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com n0 U00  *H zPAQګ[vZ'´O" Z-rl inݮ)E,K~~, NSU$V"@-uZB`S]41$"F6,*^0j/b-'`;,T |6BqR~8EkEsʋgJGtօ3wG+3-jwիz]0j+O 8EcK0'&Llcbz%ƏE~PZqkOr'hJEA|c"9ܞ!ټ\Y:FPHz!|[鑘 1K-hIKk J`"=B˭sXeN)k'1ǣ h>~8EkEsʋgJGtօ3wG+3-jwիz]0j+O 8Ec$0 0  *H  0֓ $b%>>l輨kxn l5Y>wC=KdVg0xCLrF!8N¾8Ϗ0ЧJP" P2j3Y榔,򄠤fz;1 :g7./dnmBX Dlø$XH%dOޘ(wHgjT TȻGU3ko̎iȢЎx& ?r}<rߛ8[jhwerBAPK Sig Block 42PK !:;k薸 resources.arsc5PK!:+qAndroidManifest.xmlPK!:C classes.dexPK!:-(U META-INF/CERT.SFPK!:[ A META-INF/CERT.RSAPK!:J{META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_debuggable-resource.apk0100644 0000000 0000000 00000000034 14763776540 026623 xustar000000000 0000000 28 mtime=1741684064.6380000 src/test/resources/com/android/apksig/debuggable-resource.apk0100644 0000000 0000000 00000030627 14763776540 023477 0ustar000000000 0000000 PK !: @OXX resources.arsc5 X($hTiny App for CTS [b U b` one two three]  . Tiny,   . App,   . for,   . CTS,   xandroid.appsecurity.cts.tinyapp pP( attrstringboolP$app_name debuggableL`P8L`P8enXAL`P8arXBL`P8PK!:AndroidManifest.xmlTMOQ=3eWKB?ZQƕHL4A~ؔR… .\ \pϽ}>^!2͙νF#([1yK '+@AlG8&!pxH":I&&^G]4x"3y*kdcK*)ABe;s4hRIɧq͸(<#ڜcСOTFN}/3X|USWfkQW>2Rsm*;tzz䶼;x5m/!8ԋf{ka-B=e ):w鉺ԤoM;'>Bޏ2Vψ 0ܗاH C˧ԒuORW:O^2_u~8 (Di"L3<{^cZ5,͋(13tM\bӆ6܄8ct3u;#k:BCpyy6^6ǜ_:#-;\t`rR}%wr^ofh'_J:Wt9~Wpu/y=XO❅1x{PK}PK!: classes.dexMSW}yM+fv&3S*3- YT]ݕN.q#.,@i]( ]7f s߯_(MB֯KۋO[]+}u_C37Du":(W@i[ց]f#?x SDW` 24{~Wup O ^3òaL~C`a7kgYC ;g!cZVS&n;5u;l좙g:rY79 oo9߀}tζnjQjzaFylʬў`c^+ӎ?|K[Ozc`Mffgq ʖ|]>rg$y.F׽$hɹ2㱨4I%< <wIx?e%2~e4Y+G֖i(+E >K%< "}!acC!2m{fNJ0o/HFx{!5XeIT2tװז-8OZq|$䵘ҸIɗ٪d2р/*+SbdTHjySs2N/suipEЮϜ/,r/*fXfYi6s~kĞv>h;ڒ99;1}Ӷz9=[ i=}q">K 9}A3]G?Gam7333cVWOuz:ޡNwY^UO_{j^~d|KTV浯z 5kPK ePK!:META-INF/CERT.SF]MO@; a4RVhT*Ѐ=niUwzQ{}&ۆvm\dꆪxh ߟ tC&ZP:d_ުJHL-qp,}xJ#VXl觴8Kms*BkpC]"uɰ g8r 0+;YR WYy"QwuPx߁VNdۋbѭG8Zs/L|PKUPK!:META-INF/CERT.RSA3hb[ƩiA|&Ll|LR E X40hbarޚ}25N&l(l ps9'de&مy}KJ32S  @\y)E)0n f b‰q(k 'k`a`ddi`hllb%klj`nhVg71* 0#+s#/PdI{ǸU6= U;9egYbϊٗ+s-yƷiv+Nߢ>>J_!֊C,|$v}Um`Я>}O%9@7AtwYƑϖMyS˂%i'ܰ6J7O:1B?3/+)rT-ߘ-M [8lצYN>e<{K%m(6?Q_#S9Ğ5'7\:bn,e\륚L0pVKH* /װ0ji"H.jngibdXU ?3T:d\eҺy~o~֓Kfh2AB՗u/,m;9d;ug{y~}RȬu rjWXOўx?^y/8lyڹV_]~q1flL2u6_ohOYsrSvx"O窅oyb>Si\f{46~KƍCy?-P 3F,{ݡ쪗JtcNs8{}3PKyPK!:META-INF/MANIFEST.MF]N0} IKJ#HJJBRVؗ`ƒ2HnռU/.ZEeڌĴ_$⭴FIr4WXZ$H-5aw|&/KFMGه+]*Iz-ur>s戀T?3~ >o1vk- ܢ8 P{Ο&4`q֬OPKh q`\X( ؂4 t7 8w@nBf -.( f ic$=5Erd200 n0  *H 01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 080229013346Z 350717013346Z01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 0  *H  0֓ $b%>>l輨kxn l5Y>wC=KdVg0xCLrF!8N¾8Ϗ0ЧJP" P2j3Y榔,򄠤fz;1 :g7./dnmBX Dlø$XH%dOޘ(wHgjT TȻGU3ko̎iȢЎx& ?r}<rߛ8[j00UHYV=',Ft ʌ0U#0HYV=',Ft ʌ01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com n0 U00  *H zPAQګ[vZ'´O" Z-rl inݮ)E,K~~, NSU$V"@-uZB`S]41$"F6,*^0j/b-'`;,T |6BqR>l輨kxn l5Y>wC=KdVg0xCLrF!8N¾8Ϗ0ЧJP" P2j3Y榔,򄠤fz;1 :g7./dnmBX Dlø$XH%dOޘ(wHgjT TȻGU3ko̎iȢЎx& ?r}<rߛ8[jhwerBAPK Sig Block 42PK !: @OXX resources.arsc5PK!:}AndroidManifest.xmlPK!: e aclasses.dexPK!:U META-INF/CERT.SFPK!:y3 META-INF/CERT.RSAPK!:META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_dsa-1024.pk80100644 0000000 0000000 00000000034 14763776540 023777 xustar000000000 0000000 28 mtime=1741684064.6380000 src/test/resources/com/android/apksig/dsa-1024.pk80100644 0000000 0000000 00000000516 14763776540 020645 0ustar000000000 0000000 0J0+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,0A 7``./PaxHeaders.X/src_test_resources_com_android_apksig_dsa-1024.x509.pem0100644 0000000 0000000 00000000034 14763776540 024562 xustar000000000 0000000 28 mtime=1741684064.6380000 src/test/resources/com/android/apksig/dsa-1024.x509.pem0100644 0000000 0000000 00000001741 14763776540 021431 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIICsTCCAnGgAwIBAgIJAP6EmkoBF8UoMAkGByqGSM44BAMwEzERMA8GA1UEAwwI ZHNhLTEwMjQwHhcNMTYwMzMxMTUyNzEwWhcNNDMwODE3MTUyNzEwWjATMREwDwYD VQQDDAhkc2EtMTAyNDCCAbYwggErBgcqhkjOOAQBMIIBHgKBgQCTDpv8gS5Y+Ehl oln6WT/MYBnywrc3tWDfnlY+9MpVDdB2+kcB7WrhobW1L+6ayKmlkrTaAFjiMPDf bdyA6hy3fDu1teLCb89R0uodfZa3MDXXlmqvBk4Fdw8fYijWI/q175e4Y5sNYO9+ QZg8bBIZnxxCdbKASJ6NAHc50ts3vwIVAIebRw3HnYOZbo6rPoBmcBOxcZTLAoGA ch+0D7JrbqmR1w5S3VBtTnONLiBYnaz1Ri3Pfiw5FHKfJcfFcQopIOLJwfdBmY4b FLGV5u7DXeJNp16Nvl4MrsmVjkWs9MZVAp5RqzrN9JhVi4ShpdelyFjdWOXHPbc7 NNqQpTjdkK23r/tCE6XkvkCiWm7Rt22LMpZA4ePALIoDgYQAAoGAc8SkppDzSUPH SpKrhrldRyh5m4wSH14ZE96mlSze9tRoSDo8hsA9/vGLgoN7F+3jYSvj8m42tmNt jZJWk7vPkJHC/9qoEGbVBY+aTNYwVJyKDJ07vZB9bLxpjD/yyQlsn7/vZTOS657c W2S817RgGGyGcCNRoKNig6i0k9fzE8ajUDBOMB0GA1UdDgQWBBSPwzoIjftVH2ke EJXtLq+bB50lzDAfBgNVHSMEGDAWgBSPwzoIjftVH2keEJXtLq+bB50lzDAMBgNV HRMEBTADAQH/MAkGByqGSM44BAMDLwAwLAIUH1GQcpqx8/9p9QfhCRMvcxrECM4C FH8ZULK91BMaHodbRMUtdxB9kIbL -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_dsa-2048.pk80100644 0000000 0000000 00000000034 14763776540 024006 xustar000000000 0000000 28 mtime=1741684064.6380000 src/test/resources/com/android/apksig/dsa-2048.pk80100644 0000000 0000000 00000001150 14763776540 020647 0ustar000000000 0000000 0d09*H80,oRG"٦Y92+lS[2Z1MZn=Zq !_2h"2~At~zYc{6->rx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@" pDE1j-vf8D'钛./PaxHeaders.X/src_test_resources_com_android_apksig_dsa-2048.x509.pem0100644 0000000 0000000 00000000034 14763776540 024571 xustar000000000 0000000 28 mtime=1741684064.6380000 src/test/resources/com/android/apksig/dsa-2048.x509.pem0100644 0000000 0000000 00000003042 14763776540 021434 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIEWzCCBAKgAwIBAgIJAK4uYtTAat4+MAkGByqGSM44BAMwEzERMA8GA1UEAwwI ZHNhLTIwNDgwHhcNMTYwMzMxMTgzMDAxWhcNNDMwODE3MTgzMDAxWjATMREwDwYD VQQDDAhkc2EtMjA0ODCCA0cwggI5BgcqhkjOOAQBMIICLAKCAQEAv2/yUkci8fnZ ppepy1kcOTIrbPJTW46Z2jKOWjHCTd1aqRFunz3gllpx/YDgjboNIYZf2jKhk2gi MgF+8OT0hEHQGXSofnqRoVljexA2nC3tPhVyjN14pQ7ZJXp/raHh1uCT5K7Lfafc 8y5Cs180CdfisspANfCp10jFu1D2hKOLfggUA9efN05c0f2yOx/Pm9uEiz6ftLUJ UWgcJenx1WUk4Nb1qMOWgg9nKQyDiBxZKoVCH5/XJO0K225w6F4scol2hMdTRlA+ 9TGb5tvHKT+Y+lE7fBbcBSKzLT3pwD/T08ErpsIoHqRPamLGHeuamGzvXLHwI1UN cJNGg2nnCwIhAJz9K+Qpgg7sbqRMeRvIf2cuWRWbi4XnYR+IAGqgfYUZAoIBADks N3taHUnIvkcxTuEUiCSb+d1KMlvbZiH5zLVIyzi5kZNQW8L/gR23z8T6qngXXnFw tcTRcb0TDqR7iYPxsX3/x+jBjiApIgebqpnPdpiEAUfORZX7w6CpCNsp9Kqxb98z 4PtKE9z/20Q3zKhqlwDm8uSjjT7E5xw/YpV8bg7YuQ5eQ8mkonipNXllf3qmDV48 see/+QwIeBiL7jTjgeBv5ziL8BY2AYmfOwGKwmK9ERxJpIoxgS+5fFuA2oAQ8CwJ UDwwv5Wbz7NVkvljHK1Fh5u26ZHdeLT4qp4F0GamJcikq4MqFco6ToCvp8NoDNMZ MOno2k1IvsIwrIbrQJgDggEGAAKCAQEAuxbFbx2H4n4BqfbkC9tjn/Mg3zr4LZgG 7v3FWpzpkbAcEcFCVIqTmJiRlsuf4ml/t9hflOvarfD6TesSc7gyGCJ/2QiqJcI+ Vif5AKqZskQFlZ5BUMIMjPFMy1WtTVpEotmdbIOaQif4wQrz6SNFUOAXPBKRTY33 HOLMoo8FRiZ1+uMu9PlUWYqMhSJg+rm2AQPt06D+JToXREaNkYjN0K97T2MTcUNh OWiliH/zFuF8N2s6IlNaCv1Yc4FoYEIRoS07dUxcjrV9KRd0TyU0q++rRPluytJP yAoyTIrfwa2SM5JR9RtdBZsPdR9Ckpy4ZKSJqDzTbCIU70zsGgHA4aNQME4wHQYD VR0OBBYEFL3koBjVHAySf8Xr196yqDT6VhwSMB8GA1UdIwQYMBaAFL3koBjVHAyS f8Xr196yqDT6VhwSMAwGA1UdEwQFMAMBAf8wCQYHKoZIzjgEAwNIADBFAiEAjowh laeXA/CUrHt6iH4u6edWGeZzyFGlpWsxssKTMBECIG2tZs/xnZVAtXioiIcH1CXT LN5AAzZ8wlNUKSvTc12j -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_dsa-3072.pk80100644 0000000 0000000 00000000034 14763776540 024004 xustar000000000 0000000 28 mtime=1741684064.6380000 src/test/resources/com/android/apksig/dsa-3072.pk80100644 0000000 0000000 00000001550 14763776540 020651 0ustar000000000 0000000 0d09*H80,(2< u^OO6?qX1t"Q@N|%, p۳$}0.E1.=u,D੝  s-amTKmܣ jY>nDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P" _2:j t#_t?yKiu./PaxHeaders.X/src_test_resources_com_android_apksig_dsa-3072.x509.pem0100644 0000000 0000000 00000000034 14763776540 024567 xustar000000000 0000000 28 mtime=1741684064.6380000 src/test/resources/com/android/apksig/dsa-3072.x509.pem0100644 0000000 0000000 00000004056 14763776540 021440 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIF3DCCBYKgAwIBAgIJAKmpH5wXPDbGMAkGByqGSM44BAMwEzERMA8GA1UEAwwI ZHNhLTMwNzIwHhcNMTYwMzMxMTgzMjU2WhcNNDMwODE3MTgzMjU2WjATMREwDwYD VQQDDAhkc2EtMzA3MjCCBMcwggM5BgcqhkjOOAQBMIIDLAKCAYEAscwotTL9jjwZ CpGRqXVezU9P0zY/wHFYMQB09JjgoCJRm0D9FZzmTnySJa4suwlwoduzgv+pkIkk nn0w+hL6LsblRTEuFj11o/fLLAZE4KmdIAPkCcH74HOnws4BwazJLWFtVEv+bdyj CmrI2urYWT5uRJwWZmkG4iUnoL/5FUv2pvpSOzu6EO+qFcFcWf3/VrQDaaP3jRRh iVCThxvX5xOrO24krrOFlHQufY0A6fHYQqII6EYac6tKTI+ltz121DYH914SHMe8 0JJYpDVOatjTJADyg1AzHAKsyfTKHHpHbqkgU/CRtALEC1DUyLqQhHvBGpodmqPH r9Mbd7pXudw2h6O8uapAsmaFt/YdrSbd37UvhLJyyW0fodTjBTA0WiNs0CLTJL9o 422Nt1cUDQS1JpsghAzmMWaavq7+QIRhpnMHL47p1jCZvcwMboMlcJwOCjyyZS0F fmaD+pEi56Lx+9XDblESM78crb8/lbvuohyrwIJciQmL0hQoKIg1AiEApYT1sROj BMB4FIQaS86VKdJ4h9OR13AraES3ba9WV1cCggGAI9LskZ7wcUna6nZyGT4f3bH1 KSeD3602Yd86NKuB8QGr/QT5L+wFwCuvvI4V8P2lM4wb+q1LrR688f53WS4c0Kin /MeN4YtkQBzV4l04Bxrce//EcfQzUPDAr41Fwu2tA0BHE67trxGjNS7SzoFZUiDo d/aOZCVilFIay5oF2X8yeNcpTynsxTbGxjxhhgG2tS0fvxhbGF6rl4D0Ddskx6L/ xjstzu0ADE8FNpDehuKqxzOisNLIJgc1INPQ06AmaUF1Pge74T2AJYPYwqfzArG3 pj+l2iND0PM7lDeQCgivPAqhYrveoIqJe0NAEu/PiCnOW50hQ4F6dl9lzn9sGXTP 5KxdR5OyG+Ki+nWTN8h3yOy9ZK7Gh8gUeqE3QVow4+1LhcO19C+X+NM588KbKsq9 KRP5H4v238g6oF2wgbmPrYvqiwoa1qjHvUX7ZjQuIfrN9+lICflsHKtAtchuUIiO b1AWPeaJwAynTn2EeDsVd0e8bm+h+g7ZSDAwUOKuA4IBhgACggGBAJfW6T1BxtZo EacqR2TwTi9B3O93Zcj0OzDOn9UMahepcfaF1B3wq9UdJ4PPIC1U27CaEXEJNxgJ cIURfFPDXA6pLVEqrGgRX2u0FR1pmHVIxjBpvPJvO954+Hawp+ClU6PKLmxuVZpA VeJBx7C8/DU/58J//iuG9B6/mwzPuEoPmnGYMsfVCcEX8yQ2PZXyNGp+KnUygyql mMxpE8UXtH6mHYontjiw4Afxqwop3v/bG8eT5FHhpXoQDSN/oeGghDaq9DyIjCqr GXZDBvEufLYRgOvnpV6T0oMa9U2W2Trz3HYD6eX09FOhGnMWl9euYrSVRihAKBh1 72ystvPy2R4wdAA5EQf7EsDdgt/QOMf/AvkaKHMP7DNK5BZxwPQ0KwRkLB+0vaDI Mnpu29L6TiixMxI1ihnZbR/U0v8H+/SlSjZaPNRQeuoV9d8t81miUqVSMLcpqNui ZljSCtoGfktPAbPyFwGenBkGK5oo0I381KTECMynC0R2P9CQUHegeaNQME4wHQYD VR0OBBYEFPRqMyQLPdq0guTx3YKiVxtWN+bPMB8GA1UdIwQYMBaAFPRqMyQLPdq0 guTx3YKiVxtWN+bPMAwGA1UdEwQFMAMBAf8wCQYHKoZIzjgEAwNJADBGAiEAjcit 8dFR02elWKoeRAounP9TE2aqDqd5cJXqXn0ssMYCIQCIINjXEYRovfVjDotKelRg 5k0lmzMmx6Xfz8EgZDLovw== -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_ec-p256-lineage-2-signers0100644 0000000 0000000 00000000034 14763776540 026435 xustar000000000 0000000 28 mtime=1741684064.6380000 src/test/resources/com/android/apksig/ec-p256-lineage-2-signers0100644 0000000 0000000 00000001557 14763776540 023311 0ustar000000000 0000000 9>cxp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,Uyq0m0 UҲ%0 *H=010U ec-p2560 180713174151Z 280710174151Z010U ec-p256_20Y0*H=*H=BLr< =bPx3nuSJkPa 'b-b vzD"P0N0Uy+D(^_0U#05h[02 qC[0 U00 *H=H0E 'i4yĮcR, +C0!`l` $1I*F0D `:z,y+RI^"*X X=͎$и\3PduDk./PaxHeaders.X/src_test_resources_com_android_apksig_ec-p256.pk80100644 0000000 0000000 00000000034 14763776540 023725 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/ec-p256.pk80100644 0000000 0000000 00000000212 14763776540 020564 0ustar000000000 0000000 00*H=*H=m0k |g*.M ]1=XM BX!ƾDB_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD./PaxHeaders.X/src_test_resources_com_android_apksig_ec-p256.x509.pem0100644 0000000 0000000 00000000034 14763776540 024510 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/ec-p256.x509.pem0100644 0000000 0000000 00000001052 14763776540 021352 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIBbDCCARGgAwIBAgIJAMoPtk37ZudyMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM B2VjLXAyNTYwHhcNMTYwMzMxMTQ1ODA2WhcNNDMwODE3MTQ1ODA2WjASMRAwDgYD VQQDDAdlYy1wMjU2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpl8RPSLLSROQ gwesMe4roOkTi3hfrGU20U6izpDStL/hlLUM3I4Wn1SnOpke8Pp2MpglvgeMx4J0 BwPaRLTX66NQME4wHQYDVR0OBBYEFNQTNWi5WzAVizIgceqMQ/9bBczIMB8GA1Ud IwQYMBaAFNQTNWi5WzAVizIgceqMQ/9bBczIMAwGA1UdEwQFMAMBAf8wCgYIKoZI zj0EAwIDSQAwRgIhAPUEoIZsrvAp9BcULFy3E1THn/zR1kBhjfyk8Z4W23jWAiEA +O6kgpeZwGytCMbT0tLsBeBXQVTnR+oP27gELLZVqt0= -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_ec-p256_2.pk80100644 0000000 0000000 00000000034 14763776540 024146 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/ec-p256_2.pk80100644 0000000 0000000 00000000212 14763776540 021005 0ustar000000000 0000000 00*H=*H=m0k J9]X¥eA72;FkDBLr< =bPx3nuSJkPa 'b-b vzD"./PaxHeaders.X/src_test_resources_com_android_apksig_ec-p256_2.x509.pem0100644 0000000 0000000 00000000034 14763776540 024731 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/ec-p256_2.x509.pem0100644 0000000 0000000 00000001052 14763776540 021573 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIBbTCCAROgAwIBAgIJAIhVvR3SsrIlMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM B2VjLXAyNTYwHhcNMTgwNzEzMTc0MTUxWhcNMjgwNzEwMTc0MTUxWjAUMRIwEAYD VQQDDAllYy1wMjU2XzIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQdTMoEcq2X 7jzs7w2pPWK0UMZ4gzOzbnVTzen3SrXfALu6a6lQ5oRh1wu8JxtiFR2tLeK/YgPN IHaAHHqdRCLho1AwTjAdBgNVHQ4EFgQUeZHZKwII/ESL9QbU78n/9CjLXl8wHwYD VR0jBBgwFoAU1BM1aLlbMBWLMiBx6oxD/1sFzMgwDAYDVR0TBAUwAwEB/zAKBggq hkjOPQQDAgNIADBFAiAnaauxtJ/C9TR5xK6SpmMdq/1SLJrLC7orQ+vrmcYwEQIh ANJg+x0fF2z5t/pgCYv9JDGfSQWj5f2hAKb+Giqxn/Ce -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_ec-p384.pk80100644 0000000 0000000 00000000034 14763776540 023727 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/ec-p384.pk80100644 0000000 0000000 00000000271 14763776540 020573 0ustar000000000 0000000 00*H=+"00eOk_TRZK!Osyw(`cNf74ppNظtdbcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ ./PaxHeaders.X/src_test_resources_com_android_apksig_ec-p384.x509.pem0100644 0000000 0000000 00000000034 14763776540 024512 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/ec-p384.x509.pem0100644 0000000 0000000 00000001173 14763776540 021360 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIBqTCCAS6gAwIBAgIJAMRWS+CLIsxqMAoGCCqGSM49BAMDMBIxEDAOBgNVBAMM B2VjLXAzODQwHhcNMTYwMzMxMTUzMDU3WhcNNDMwODE3MTUzMDU3WjASMRAwDgYD VQQDDAdlYy1wMzg0MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE18hjdbk/HXGQNIuF xKPMAZO3PQnROO6izB/mHM1BKPpih2/51iMTFKn6KCU9NZt/Q4Z+PpZVLuawEWP/ uoWwWIj+60vk25z47/Sr0icelSDGt9T9ujiNP6aTA5hc9gypo1AwTjAdBgNVHQ4E FgQU981MoejFjh0rbaGXODywOYvB32kwHwYDVR0jBBgwFoAU981MoejFjh0rbaGX ODywOYvB32kwDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQDAwNpADBmAjEA/58rXa+F mB6JwB89/IAucpNlktjSPrH2tD63BSROvpUpXNy+p+OlJu4sCvY7HnwEAjEA0VWw QqUBFLQHFJx1JjMYYfT78V8ylY+Ns1lxrdvs29NNg45MA9uw/ZVMMHgTFNph -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_ec-p521.pk80100644 0000000 0000000 00000000034 14763776540 023720 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/ec-p521.pk80100644 0000000 0000000 00000000361 14763776540 020564 0ustar000000000 0000000 00*H=+#0BوWt$J*xX,UD>/y8mMmڡ/56ѱ@0Utay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPz./PaxHeaders.X/src_test_resources_com_android_apksig_ec-p521.x509.pem0100644 0000000 0000000 00000000034 14763776540 024503 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/ec-p521.x509.pem0100644 0000000 0000000 00000001341 14763776540 021346 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIB8zCCAVSgAwIBAgIJAOxXdFsvm3YiMAoGCCqGSM49BAMEMBIxEDAOBgNVBAMM B2VjLXA1MjEwHhcNMTYwMzMxMTUzMTIyWhcNNDMwODE3MTUzMTIyWjASMRAwDgYD VQQDDAdlYy1wNTIxMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAYX95sSjPEQqg yLD04tNUyq9y/w8seblOpfqa/Amx6H4GFdrjGXX0YTfXKr9GhAyIyQSnNrIg0zDl WQUbBPRW4CYBLFOg1pUn1NBhKFD4NtO1KWvYtNOYDegFjRCPB0p+fEXDbq8QFDYv lh+NZUJ16+ih8XNIf1C29xuLEqN6oKOnAvajUDBOMB0GA1UdDgQWBBT/Ra3kz60g Q7tYk3byZckcLabt8TAfBgNVHSMEGDAWgBT/Ra3kz60gQ7tYk3byZckcLabt8TAM BgNVHRMEBTADAQH/MAoGCCqGSM49BAMEA4GMADCBiAJCAP39hYLsWk2H84oEw+HJ qGGjexhqeD3vSO1mWhopripE/81oy3yV10puYoJe11xDSfcDj2VfNCHazuXO3kSx GA/1AkIBLUJxp/WYbYzhBGKr6lcxczKI/wuMfkZ6vL+0EMJVA/2uEoeqvnl7Bsdk icyaOBNEADijuVdaPPIWzKClt9OaVxE= -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_empty-unsigned.apk0100644 0000000 0000000 00000000034 14763776540 025665 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/empty-unsigned.apk0100644 0000000 0000000 00000000026 14763776540 022527 0ustar000000000 0000000 PK./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-in.apk0100644 0000000 0000000 00000000034 14763776540 026172 xustar000000000 0000000 28 mtime=1741684064.6390000 src/test/resources/com/android/apksig/golden-aligned-in.apk0100644 0000000 0000000 00000012754 14763776540 023047 0ustar000000000 0000000 PK)[J META-INF/PKPK)[JLMMETA-INF/MANIFEST.MFMLK-. K-*ϳR03r.JM,IMu Xěf敤%(h%&*8%irrPKLMPK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK)[J META-INF/PK)[JLM=META-INF/MANIFEST.MFPK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[J temp.txtPK [J51 lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPK./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-out.apk0100644 0000000 0000000 00000000034 14763776540 026373 xustar000000000 0000000 28 mtime=1741684064.6400000 src/test/resources/com/android/apksig/golden-aligned-out.apk0100644 0000000 0000000 00000031101 14763776540 023233 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[J`#ɴMETA-INF/RSA-2048.SFmM@້nXqE0 _Fhn_fw6kƹU*ު(M׌t$:jep!}ST}oXf\%{1X8(˦{? doH}iRT\ 90In[w+:?I5s`.3gͩUer(1:giqr1W7Z{#\(-7YM<9uA]`#=8+i˛՞FK 1̺=[r!ATsǴ5feS{^P30 3i~i"nK%+?gU YtA帨:\g/Bma%s dgjn>=EͲ{} : Tv鶺PtaYSwnq쇻%opPK[JPh$META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICDwaIԼdid fiP΅' qI,( BMXpWlWc/Nioi&T6)00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*átw_Jj%&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( BMXpWlWc/Nioi&T6)00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@* temp2.txtPK[J`#ɴMETA-INF/RSA-2048.SFPK[JPh$META-INF/RSA-2048.RSAPK[JcM2META-INF/MANIFEST.MFPK +0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-v1-out.apk0100644 0000000 0000000 00000000034 14763776540 026717 xustar000000000 0000000 28 mtime=1741684064.6410000 src/test/resources/com/android/apksig/golden-aligned-v1-out.apk0100644 0000000 0000000 00000017033 14763776540 023567 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[J|META-INF/RSA-2048.SFmK@l\pGyޚFhnOfw'1*ʯU!> j)x0~n`(eB~/&:Deߋx'y%x]w ܩn& ;2ch<2ʯ;ZDr|QY XY`g5"o'?*t@P#Lj|vWyխזv^O,mg2h+2, I~- 7ۋ[C/!Ѭ\ܭ?N96SӶ] yAK7?z@gVUܨWv.C 4׌gZ&GJDIfWJi+rpÏu Oܬ֊(]s!BHݹ6Gp;PK[JB%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICNfJLhTcxJWE2y,ZnL41NSCslv)>e ]U;=smƒ%I[:TJdt OZ1X:QmG…{gU_vzWc,'%vK-OXM}wS -1U ٣Ÿ {# 50^^'}o!5-~gh8(^us}$冎PK[JcMMETA-INF/MANIFEST.MFmM@߁cʚv,R 0#0 n^*ȸ@+AӉM!`H_."!Š-lB{BN'I:L\RJ,];DwaIԼdid fiP΅'PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPK[J|META-INF/RSA-2048.SFPK[JB%META-INF/RSA-2048.RSAPK[JcMMETA-INF/MANIFEST.MFPK +./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-v1v2-out.apk0100644 0000000 0000000 00000000034 14763776540 027167 xustar000000000 0000000 28 mtime=1741684064.6410000 src/test/resources/com/android/apksig/golden-aligned-v1v2-out.apk0100644 0000000 0000000 00000031101 14763776540 024027 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[JIMETA-INF/RSA-2048.SFm]s0{g\nABEXK.&QuvS.sΜ'!S+ )Puvˮ1)oA5. zjH=f\%c(p^cs QMo47 BP۝^_[n'r`4vݺwD_>Ku+J1u Μ x=zQn/1:Giqr!WҖ7=kcu{T CƂikʦf?i,θ튌QF,/UZ2 <{l3~62kU67H>*uٯ.>uMm}n4sONe)~{ybě Iqۮٟ֮+~WY77~3 9Lx0^y͵xJ^}Zx/dO(0{[Ҿr3Nkߍ,MUZGϰp]j3:}_jn^)uHkNz%F}[sٻPK[JcMMETA-INF/MANIFEST.MFmM@߁cʚv,R 0#0 n^*ȸ@+AӉM!`H_."!Š-lB{BN'I:L\RJ,];DwaIԼdid fiP΅' q{=,( ϞB.칂yCr}2A[174ZI鍲w00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*6IWr㫷' y1TjI/d5[^ $|+D|`  !Ø9>Yu0@TMp]dy& XJIīMdJ[P/xL }>@c$[aّjxh!0AA.ʁ7DJ hY T/(|PQLVmQwb1:RГv/w|qZe ݍNH7ppSM0{ &0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[I werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPK[JIMETA-INF/RSA-2048.SFPK[J%META-INF/RSA-2048.RSAPK[JcM0META-INF/MANIFEST.MFPK +0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-v1v2v3-lineage-out.apk0100644 0000000 0000000 00000000034 14763776540 031042 xustar000000000 0000000 28 mtime=1741684064.6410000 src/test/resources/com/android/apksig/golden-aligned-v1v2v3-lineage-out.apk0100644 0000000 0000000 00000041101 14763776540 025703 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[J`#ɴMETA-INF/RSA-2048.SFmM@້nXqE0 _Fhn_fw6kƹU*ު(M׌t$:jep!}ST}oXf\%{1X8(˦{? doH}iRT\ 90In[w+:?I5s`.3gͩUer(1:giqr1W7Z{#\(-7YM<9uA]`#=8+i˛՞FK 1̺=[r!ATsǴ5feS{^P30 3i~i"nK%+?gU YtA帨:\g/Bma%s dgjn>=EͲ{} : Tv鶺PtaYSwnq쇻%opPK[JPh$META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICDwaIԼdid fiP΅' qI,( BMXpWlWc/Nioi&T6)00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*átw_Jj%&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ hS ,( BMXpWlWc/Nioi&T6)00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq` werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPK[J`#ɴMETA-INF/RSA-2048.SFPK[JPh$META-INF/RSA-2048.RSAPK[JcM2META-INF/MANIFEST.MFPK +@./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-v1v2v3-out.apk0100644 0000000 0000000 00000000034 14763776540 027440 xustar000000000 0000000 28 mtime=1741684064.6410000 src/test/resources/com/android/apksig/golden-aligned-v1v2v3-out.apk0100644 0000000 0000000 00000031101 14763776540 024300 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[J`#ɴMETA-INF/RSA-2048.SFmM@້nXqE0 _Fhn_fw6kƹU*ު(M׌t$:jep!}ST}oXf\%{1X8(˦{? doH}iRT\ 90In[w+:?I5s`.3gͩUer(1:giqr1W7Z{#\(-7YM<9uA]`#=8+i˛՞FK 1̺=[r!ATsǴ5feS{^P30 3i~i"nK%+?gU YtA帨:\g/Bma%s dgjn>=EͲ{} : Tv鶺PtaYSwnq쇻%opPK[JPh$META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICDwaIԼdid fiP΅' qI,( BMXpWlWc/Nioi&T6)00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*átw_Jj%&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( BMXpWlWc/Nioi&T6)00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@* temp2.txtPK[J`#ɴMETA-INF/RSA-2048.SFPK[JPh$META-INF/RSA-2048.RSAPK[JcM2META-INF/MANIFEST.MFPK +0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-v2-out.apk0100644 0000000 0000000 00000000034 14763776540 026720 xustar000000000 0000000 28 mtime=1741684064.6420000 src/test/resources/com/android/apksig/golden-aligned-v2-out.apk0100644 0000000 0000000 00000030572 14763776540 023573 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK> q{=,( vpr jO zB"y5A]U00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*1 {Н=)9,~O3ÖĜгQnj9ϟZ\2>xJaO=lvyj[Q/pR},WV탥D2U@ub!WNU ocvkDp)8E+"J!>] Mmv28.,hitb&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[I werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-v2v3-lineage-out.apk0100644 0000000 0000000 00000000034 14763776540 030573 xustar000000000 0000000 28 mtime=1741684064.6420000 src/test/resources/com/android/apksig/golden-aligned-v2v3-lineage-out.apk0100644 0000000 0000000 00000040572 14763776540 025447 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK> qI,( vpr jO zB"y5A]U00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq` werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd@./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-v2v3-out.apk0100644 0000000 0000000 00000000034 14763776540 027171 xustar000000000 0000000 28 mtime=1741684064.6420000 src/test/resources/com/android/apksig/golden-aligned-v2v3-out.apk0100644 0000000 0000000 00000030572 14763776540 024044 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK> qI,( vpr jO zB"y5A]U00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*JG^yҫ*fìf,{y|E9}5`@_nE%N; ڈXa&•1 Smįhxiv:UNCD6x[>_q'.5GP>n5FEٴ>5L`m H%Tzp>sDriNp{ҍӠt E&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-v3-lineage-out.apk0100644 0000000 0000000 00000000034 14763776540 030323 xustar000000000 0000000 28 mtime=1741684064.6420000 src/test/resources/com/android/apksig/golden-aligned-v3-lineage-out.apk0100644 0000000 0000000 00000030572 14763776540 025176 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK> hS ,( vpr jO zB"y5A]U00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqwerBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-aligned-v3-out.apk0100644 0000000 0000000 00000000034 14763776540 026721 xustar000000000 0000000 28 mtime=1741684064.6420000 src/test/resources/com/android/apksig/golden-aligned-v3-out.apk0100644 0000000 0000000 00000030572 14763776540 023574 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>hSA,( vpr jO zB"y5A]U00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*JG^yҫ*fìf,{y|E9}5`@_nE%N; ڈXa&•1 Smįhxiv:UNCD6x[>_q'.5GP>n5FEٴ>5L`m H%Tzp>sDriNp{ҍӠt E&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[= werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-file-size-aligned.apk0100644 0000000 0000000 00000000034 14763776540 027453 xustar000000000 0000000 28 mtime=1741684064.6420000 src/test/resources/com/android/apksig/golden-file-size-aligned.apk0100644 0000000 0000000 00000040000 14763776540 024311 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy044:`Sq<e [UND wFF5@\AgIM;؎ ׂYrM>#%]{ =* &0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( IcR74~R0мh_00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*()UAzHw&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK0i./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-in.apk0100644 0000000 0000000 00000000034 14763776540 027434 xustar000000000 0000000 28 mtime=1741684064.6430000 src/test/resources/com/android/apksig/golden-legacy-aligned-in.apk0100644 0000000 0000000 00000012750 14763776540 024305 0ustar000000000 0000000 PK)[J META-INF/PKPK)[JLMMETA-INF/MANIFEST.MFMLK-. K-*ϳR03r.JM,IMu Xěf敤%(h%&*8%irrPKLMPK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK)[J META-INF/PK)[JLM=META-INF/MANIFEST.MFPK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[J temp.txtPK [J51 lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPK./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-out.apk0100644 0000000 0000000 00000000034 14763776540 027635 xustar000000000 0000000 28 mtime=1741684064.6430000 src/test/resources/com/android/apksig/golden-legacy-aligned-out.apk0100644 0000000 0000000 00000031101 14763776540 024475 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[J`#ɴMETA-INF/RSA-2048.SFmM@້nXqE0 _Fhn_fw6kƹU*ު(M׌t$:jep!}ST}oXf\%{1X8(˦{? doH}iRT\ 90In[w+:?I5s`.3gͩUer(1:giqr1W7Z{#\(-7YM<9uA]`#=8+i˛՞FK 1̺=[r!ATsǴ5feS{^P30 3i~i"nK%+?gU YtA帨:\g/Bma%s dgjn>=EͲ{} : Tv鶺PtaYSwnq쇻%opPK[JPh$META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICDwaIԼdid fiP΅' qI,( x[n5 QML5DLEZ$00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*p¾z`K@TfI<.FWmt󯑑8X7TcGƔ$~Rb+t  ~Z!E$y&ctPŶ(F%o2NEۤ?&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( x[n5 QML5DLEZ$00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*w>>]EևY "8Au5+30Yݨ+FsՅ}=c[#>4stv~,?f/&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPK[J`#ɴMETA-INF/RSA-2048.SFPK[JPh$META-INF/RSA-2048.RSAPK[JcM.META-INF/MANIFEST.MFPK +0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-v1-out.apk0100644 0000000 0000000 00000000034 14763776540 030161 xustar000000000 0000000 28 mtime=1741684064.6430000 src/test/resources/com/android/apksig/golden-legacy-aligned-v1-out.apk0100644 0000000 0000000 00000017027 14763776540 025034 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[J|META-INF/RSA-2048.SFmK@l\pGyޚFhnOfw'1*ʯU!> j)x0~n`(eB~/&:Deߋx'y%x]w ܩn& ;2ch<2ʯ;ZDr|QY XY`g5"o'?*t@P#Lj|vWyխזv^O,mg2h+2, I~- 7ۋ[C/!Ѭ\ܭ?N96SӶ] yAK7?z@gVUܨWv.C 4׌gZ&GJDIfWJi+rpÏu Oܬ֊(]s!BHݹ6Gp;PK[JB%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICNfJLhTcxJWE2y,ZnL41NSCslv)>e ]U;=smƒ%I[:TJdt OZ1X:QmG…{gU_vzWc,'%vK-OXM}wS -1U ٣Ÿ {# 50^^'}o!5-~gh8(^us}$冎PK[JcMMETA-INF/MANIFEST.MFmM@߁cʚv,R 0#0 n^*ȸ@+AӉM!`H_."!Š-lB{BN'I:L\RJ,];DwaIԼdid fiP΅'PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPK[J|META-INF/RSA-2048.SFPK[JB%META-INF/RSA-2048.RSAPK[JcMMETA-INF/MANIFEST.MFPK +./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-v1v2-out.apk0100644 0000000 0000000 00000000034 14763776540 030431 xustar000000000 0000000 28 mtime=1741684064.6440000 src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2-out.apk0100644 0000000 0000000 00000031101 14763776540 025271 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[JIMETA-INF/RSA-2048.SFm]s0{g\nABEXK.&QuvS.sΜ'!S+ )Puvˮ1)oA5. zjH=f\%c(p^cs QMo47 BP۝^_[n'r`4vݺwD_>Ku+J1u Μ x=zQn/1:Giqr!WҖ7=kcu{T CƂikʦf?i,θ튌QF,/UZ2 <{l3~62kU67H>*uٯ.>uMm}n4sONe)~{ybě Iqۮٟ֮+~WY77~3 9Lx0^y͵xJ^}Zx/dO(0{[Ҿr3Nkߍ,MUZGϰp]j3:}_jn^)uHkNz%F}[sٻPK[JcMMETA-INF/MANIFEST.MFmM@߁cʚv,R 0#0 n^*ȸ@+AӉM!`H_."!Š-lB{BN'I:L\RJ,];DwaIԼdid fiP΅' q{=,( +Bݿۥuƾ>"S gY j9rl00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:r^E~c0K2V2ɔ temp2.txtPK[JIMETA-INF/RSA-2048.SFPK[J%META-INF/RSA-2048.RSAPK[JcM,META-INF/MANIFEST.MFPK +0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-v1v2v3-lineage-out.apk0100644 0000000 0000000 00000000034 14763776540 032304 xustar000000000 0000000 28 mtime=1741684064.6440000 src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2v3-lineage-out.apk0100644 0000000 0000000 00000041101 14763776540 027145 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[J`#ɴMETA-INF/RSA-2048.SFmM@້nXqE0 _Fhn_fw6kƹU*ު(M׌t$:jep!}ST}oXf\%{1X8(˦{? doH}iRT\ 90In[w+:?I5s`.3gͩUer(1:giqr1W7Z{#\(-7YM<9uA]`#=8+i˛՞FK 1̺=[r!ATsǴ5feS{^P30 3i~i"nK%+?gU YtA帨:\g/Bma%s dgjn>=EͲ{} : Tv鶺PtaYSwnq쇻%opPK[JPh$META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICDwaIԼdid fiP΅' qI,( x[n5 QML5DLEZ$00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*p¾z`K@TfI<.FWmt󯑑8X7TcGƔ$~Rb+t  ~Z!E$y&ctPŶ(F%o2NEۤ?&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ hS ,( x[n5 QML5DLEZ$00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >J VRg毕Q&Բh=st'1u mjR7mg^uY;DT"f&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq` werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPK[J`#ɴMETA-INF/RSA-2048.SFPK[JPh$META-INF/RSA-2048.RSAPK[JcM.META-INF/MANIFEST.MFPK +@./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-v1v2v3-out.apk0100644 0000000 0000000 00000000034 14763776540 030702 xustar000000000 0000000 28 mtime=1741684064.6440000 src/test/resources/com/android/apksig/golden-legacy-aligned-v1v2v3-out.apk0100644 0000000 0000000 00000031101 14763776540 025542 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>PK[J`#ɴMETA-INF/RSA-2048.SFmM@້nXqE0 _Fhn_fw6kƹU*ު(M׌t$:jep!}ST}oXf\%{1X8(˦{? doH}iRT\ 90In[w+:?I5s`.3gͩUer(1:giqr1W7Z{#\(-7YM<9uA]`#=8+i˛՞FK 1̺=[r!ATsǴ5feS{^P30 3i~i"nK%+?gU YtA帨:\g/Bma%s dgjn>=EͲ{} : Tv鶺PtaYSwnq쇻%opPK[JPh$META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICDwaIԼdid fiP΅' qI,( x[n5 QML5DLEZ$00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*p¾z`K@TfI<.FWmt󯑑8X7TcGƔ$~Rb+t  ~Z!E$y&ctPŶ(F%o2NEۤ?&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( x[n5 QML5DLEZ$00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*w>>]EևY "8Au5+30Yݨ+FsՅ}=c[#>4stv~,?f/&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPK[J`#ɴMETA-INF/RSA-2048.SFPK[JPh$META-INF/RSA-2048.RSAPK[JcM.META-INF/MANIFEST.MFPK +0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-v2-out.apk0100644 0000000 0000000 00000000034 14763776540 030162 xustar000000000 0000000 28 mtime=1741684064.6440000 src/test/resources/com/android/apksig/golden-legacy-aligned-v2-out.apk0100644 0000000 0000000 00000030572 14763776540 025035 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK> q{=,( d;t}WJo(';?’c200 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*7*ix.*ާg+;K|\@`9ܓxeg}r6 :!5+op;'ؐuB3+9B-slҌ(I;R/=&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[I werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-v2v3-lineage-out.apk0100644 0000000 0000000 00000000034 14763776540 032035 xustar000000000 0000000 28 mtime=1741684064.6440000 src/test/resources/com/android/apksig/golden-legacy-aligned-v2v3-lineage-out.apk0100644 0000000 0000000 00000040572 14763776540 026711 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK> qI,( d;t}WJo(';?’c200 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ڭ|0}lDѻ^ʺ<`Z ٣\] ٲ*Ef ;E {x@fFbۄ}O'b:#w c9Kdϲi2/OE&[??Y|@ D/Oa\}]"bliZ sd~PrGZtSOU)p!?e6FHBPO'@]A׃;&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ hS ,( d;t}WJo(';?’c200 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq` werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd@./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-v2v3-out.apk0100644 0000000 0000000 00000000034 14763776540 030433 xustar000000000 0000000 28 mtime=1741684064.6450000 src/test/resources/com/android/apksig/golden-legacy-aligned-v2v3-out.apk0100644 0000000 0000000 00000030572 14763776540 025306 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK> qI,( d;t}WJo(';?’c200 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ڭ|0}lDѻ^ʺ<`Z ٣\] ٲ*Ef ;E {x@fFbۄ}O'b:#w c9Kdϲi2/OE&[??Y|@ D/Oa\}]"bliZ sd~PrGZtSOU)p!?e6FHBPO'@]A׃;&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( d;t}WJo(';?’c200 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*mF+*zbB7v'Ʒr+A)U3qF1_O</fdcB$1Mv@8 5۵]P4!!|l)N 3~YHL6oxoQ6`x @WNc r@X&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-v3-lineage-out.apk0100644 0000000 0000000 00000000034 14763776540 031565 xustar000000000 0000000 28 mtime=1741684064.6450000 src/test/resources/com/android/apksig/golden-legacy-aligned-v3-lineage-out.apk0100644 0000000 0000000 00000030572 14763776540 026440 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK> hS ,( d;t}WJo(';?’c200 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqwerBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-legacy-aligned-v3-out.apk0100644 0000000 0000000 00000000034 14763776540 030163 xustar000000000 0000000 28 mtime=1741684064.6450000 src/test/resources/com/android/apksig/golden-legacy-aligned-v3-out.apk0100644 0000000 0000000 00000030572 14763776540 025036 0ustar000000000 0000000 PK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dex5dex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.so5٧Hello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J> temp2.txtsr\PK>hSA,( d;t}WJo(';?’c200 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*mF+*zbB7v'Ʒr+A)U3qF1_O</fdcB$1Mv@8 5۵]P4!!|l)N 3~YHL6oxoQ6`x @WNc r@X&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[= werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51$ lib/armeabi/fake.soPK !:!Oresources.arscPK[J> temp2.txtPKd0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-pinsapp-signed.apk0100644 0000000 0000000 00000000034 14763776540 027104 xustar000000000 0000000 28 mtime=1741684064.6450000 src/test/resources/com/android/apksig/golden-pinsapp-signed.apk0100644 0000000 0000000 00000021034 14763776540 023750 0ustar000000000 0000000 PK!< classes.dexmRK@4UDDA9pk". n/ɵ=m~5"88*88NN"8&(>x߽_wl9\?N]O~=>hw ^ !Ve6AeBBroHп!>#x^Al#vNSoɩyWDtߋX.:vq[Wwyt@-Ql3> ڪ@ OxX܅.i\J+Cj\֩bZxqUfibbvY:U紮@ѰjTUR4+:kZì5tb̕`a. B bUuQӓSos#칯q.z `Fki&Bo9BLR'IB7y,y1,}PK.*pPK!߉5c5ثOX>"e*s@B7PM.^&ʽ-b XZ)x-ϫR)Xe w%&){H)J{`gwuB % J )*(N mhGWƣM` *ީa!M'4^Ϟ/٩|]v>ߖgU)[TH.f:QSPrR+'L!nV3 S.`5p PK!<#META-INF/RSA-2048.RSA3hbajhδ%נ%ѐۀUIqA_&M03121q2en W 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s9G "Ȱ?} 2M~Vr'ǵsz7߿(uJOWrxQz&b/2V7^G3]R#mu,Z{3Lnhcb\O>9LŪmqA+o˧m/)%'lٯ,hQTOWݩ)&[>:;wm;$up۬<-h:퀒I @_ܚۿ.IfmlaWn<PK!5][,LxŤ#]<ew.`/Ow%ݮK8M"KbBIr\ QWEGzSLcvOlxE@Y2 %;4R٠ts+6?,#"f[9奲~&90'eƎ}D}NEs\;Z&S3g@&R轮Z0KۯEDM1*Pס8XIvt}6 oyO$"ڱWY4M2.v=pk͙."8#Qe!nc1`8bR&)d261rɡ$eD:X'MB؃zAH5T>;4R٠ts+6?,#"f[9奲~&90'eƎ}D}NEs\;Z&S3g@&R轮Z0KۯEDM1*Pס8XIvt}6 oyO$"ڱWY4M2.v=pk͙."8#Qe&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSu`( ~~ᎏid6B`?ފQv0!(4q 6Xe1D%v00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ޞuM*3+Bp]{KR[%6j]I;_ifon.xī]Λpb I%ґ M٦Tdq6zM <2zк Dt;!Y~: e4xtsijBC3WThtq1ZTA| xEwj̐QG7US^6Y䘐̚S1"]p*=FhZ ;n>ޞuM*3+Bp]{KR[%6j]I;_ifon.xī]Λpb I%ґ M٦Tdq6zM <2zк Dt;&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,["werBAPK Sig Block 42PK!<.*p classes.dexPK!:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:ے,PMETA-INF/RSA-2048.SF]Mo@D$=%MH< cQEᶰ ]Pח&yo<+q0 x^PeE,FqM v<%aUNE_*|3keXg;pzme}EOWGQ8;Mwh/`, QmqwfȫwDGڵݑ {_OrœS.>AY 쬚~t_$7_xFyհd0`ƓEӇqJCkks# 8)PK!:ed#META-INF/RSA-2048.RSA3hbajhδ%נ%ѐۀUIqA_&M03121q2en W 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s9G "Ȱx] ;>9gn4環"A KRu>DO˛[t3hi.(tfrNsc9OڹeD>o^<{J績IJmrb}^gLt:ūg6ظݸk}1wMtojzc>U[3/y65O7i޾cs< BTPK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC| qI,( V &DSx\Ƌ%Abo00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*dqy\Ա~X٫8dP˩WIt$W5MH&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( V &DSx\Ƌ%Abo00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*7}h{&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:ے,P META-INF/RSA-2048.SFPK!:ed# META-INF/RSA-2048.RSAPK!:j)META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-rsa-minSdkVersion-18-out.apk0100644 0000000 0000000 00000000034 14763776540 030574 xustar000000000 0000000 28 mtime=1741684064.6460000 src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-18-out.apk0100644 0000000 0000000 00000030627 14763776540 025450 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*#_nxI0zB=/r'٬XW|zk<֌CPُjkw ގS:+@И=Y~yG42b| ,+0y@ = CbӁx%#TČֶ`}'MʶܝW0LY#K/R[ZtOg}Ͷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-rsa-minSdkVersion-24-out.apk0100644 0000000 0000000 00000000034 14763776540 030571 xustar000000000 0000000 28 mtime=1741684064.6460000 src/test/resources/com/android/apksig/golden-rsa-minSdkVersion-24-out.apk0100644 0000000 0000000 00000030627 14763776540 025445 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*#_nxI0zB=/r'٬XW|zk<֌CPُjkw ގS:+@И=Y~yG42b| ,+0y@ = CbӁx%#TČֶ`}'MʶܝW0LY#K/R[ZtOg}Ͷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-rsa-no-verity-out.apk0100644 0000000 0000000 00000000034 14763776540 027507 xustar000000000 0000000 28 mtime=1741684064.6460000 src/test/resources/com/android/apksig/golden-rsa-no-verity-out.apk0100644 0000000 0000000 00000030627 14763776540 024363 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*#_nxI0zB=/r'٬XW|zk<֌CPُjkw ގS:+@И=Y~yG42b| ,+0y@ = CbӁx%#TČֶ`}'MʶܝW0LY#K/R[ZtOg}Ͷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-rsa-out.apk0100644 0000000 0000000 00000000034 14763776540 025555 xustar000000000 0000000 28 mtime=1741684064.6460000 src/test/resources/com/android/apksig/golden-rsa-out.apk0100644 0000000 0000000 00000030627 14763776540 022431 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*#_nxI0zB=/r'٬XW|zk<֌CPُjkw ގS:+@И=Y~yG42b| ,+0y@ = CbӁx%#TČֶ`}'MʶܝW0LY#K/R[ZtOg}Ͷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-rsa-verity-out.apk0100644 0000000 0000000 00000000034 14763776540 027075 xustar000000000 0000000 28 mtime=1741684064.6470000 src/test/resources/com/android/apksig/golden-rsa-verity-out.apk0100644 0000000 0000000 00000030627 14763776540 023751 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&0!(g/DRgX'(![G_1+M:U z _% !00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ!nC?Z7@~f5jy$WN[[mF>|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSu`( E~m̍M(C-# 1X>&0!(g/DRgX'(![G_1+M:U z _% !00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789 PK)[J META-INF/PK)[JLM=META-INF/MANIFEST.MFPK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[J temp.txtPK [J51 lib/armeabi/fake.soPK !:!O resources.arscPK[J> temp2.txtPK hJv@ temp4.txt123456PK ?./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-out.apk0100644 0000000 0000000 00000000034 14763776540 026736 xustar000000000 0000000 28 mtime=1741684064.6480000 src/test/resources/com/android/apksig/golden-unaligned-out.apk0100644 0000000 0000000 00000031176 14763776540 023612 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789 PKhJYiMETA-INF/RSA-2048.SFmˎPཉr& r1rzW(c:=*)nJHyPfDJ F}/_R9~R)GV5:܆|!Z++SeB7&8r>IIrn=`4#?d8 3_ĨMuMlsR.0Y&/ATFlɈx^6g^>Tm.rvh?LP8!𿔥kdx#s]Xp&/o?V_S{џ[~d[0 ;ʹe9%wݪcϽcOXk(|.x֝sxg8>WN3k?؜*~y'yn3g]ҽ})${Z™o{fCW̯n!U3PKhJʶXѯMETA-INF/MANIFEST.MFmO@c#I Re¥`ageiu/y-(H"x1 Rp?&Ha)GARКP^ .:N)A,Uh2L߳ur塼:.di~Ql5ۋ8yA~0t3S೏6y7 ?l_qM \ijФ,Q,ZA#1yww,r هăc4ݩh ihr (Ky\1oto\rEu5?ʒnV<V[]S]'wT$dRWX3$~ɛ| nV'IBҴEE.f9:+H3 T.6?`oQɮ .d7IؙK;]7,8@u qI,( j1p"߭p[ͦ(0`00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*p%^  1ৼMjO(7Y  temp2.txtPK hJv@ < temp4.txt123456PKhJYit META-INF/RSA-2048.SFPKhJ:l(META-INF/RSA-2048.RSAPKhJʶXѯMETA-INF/MANIFEST.MFPK h0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-v1-out.apk0100644 0000000 0000000 00000000034 14763776540 027262 xustar000000000 0000000 28 mtime=1741684064.6480000 src/test/resources/com/android/apksig/golden-unaligned-v1-out.apk0100644 0000000 0000000 00000014041 14763776540 024126 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789 PKhJw7META-INF/RSA-2048.SFmK@ཉL PG@^"+RxJ;Ĩu9U]Jl>FlT(*O0%gQw!? |F çc>i9 NW$p 4z^ ]{ R43&M;%޸X\h1kυ^JA5 /Q QW2w~gTewDTNksW^wg@i+cPx|1V% k̀tD22Ag%EZJeY/ǵ%y]⦆6HX+JVX|h+);^u^I ҃ZUM!Olu06Ᲊon<=lveE7J< -.$&wO{rv$sNd=rD3aPKhJ@g$META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC*U8׹BLs<ÉC\/ݚ; &KvZjxSdBفWax;x]zaS;:C?v7ZX?v3܉Z?qY9[>/z6ժǖnQvovڈy1֦3PKhJʶXѯMETA-INF/MANIFEST.MFmO@c#I Re¥`ageiu/y-(H"x1 Rp?&Ha)GARКP^ .:N)A,Uh2L߳ur塼:.di~Ql5ۋ8yA~0t3S೏6y7 ?l_qM \ijФ,Q,ZA#1yww,r هăc4ݩh ihr (Ky\1oto\rEu5?ʒnV<V[]S]'wT$dRWX3$~ɛ| nV'IBҴEE.f9:+H3 T.6?`oQɮ .d7IؙK;]7,8@uPK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51 lib/armeabi/fake.soPK !:!OS resources.arscPK[J> temp2.txtPK hJv@ < temp4.txt123456PKhJw7t META-INF/RSA-2048.SFPKhJ@g$kMETA-INF/RSA-2048.RSAPKhJʶXѯMETA-INF/MANIFEST.MFPK h./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-v1v2-out.apk0100644 0000000 0000000 00000000034 14763776540 027532 xustar000000000 0000000 28 mtime=1741684064.6480000 src/test/resources/com/android/apksig/golden-unaligned-v1v2-out.apk0100644 0000000 0000000 00000031176 14763776540 024406 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789 PKhJMETA-INF/RSA-2048.SFmˎPཉr&lPG@n"rs~L'c]*?SM(T`FpT AL%qUp$r)=>̈G]廲[ZN & ݘ4fpS?$%kʹ`20#~Q3;.g=ziV+DdڨOt7U|?bо1z>oM'|m!rqh|l n@0UAFxe}k䁰x4frNDb5hsmpTѽoP,hh1C"(pT7:hdtA~ j rT /kdoMʶJ&COs[h8&zδMsk_1r5WעSW͑hrH],=SHa&9 5tPW*L{.$}7PKhJ%]&META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC,qΡwq ~<9U37qm88`f|ZюvZ^KsO$(u%&^MPKhJʶXѯMETA-INF/MANIFEST.MFmO@c#I Re¥`ageiu/y-(H"x1 Rp?&Ha)GARКP^ .:N)A,Uh2L߳ur塼:.di~Ql5ۋ8yA~0t3S೏6y7 ?l_qM \ijФ,Q,ZA#1yww,r هăc4ݩh ihr (Ky\1oto\rEu5?ʒnV<V[]S]'wT$dRWX3$~ɛ| nV'IBҴEE.f9:+H3 T.6?`oQɮ .d7IؙK;]7,8@u q{=,( J5o[g.D# Ju00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*KUj|5^jb^qZ?ӯIF9ӐCSvU:&ѬRQ temp2.txtPK hJv@ < temp4.txt123456PKhJt META-INF/RSA-2048.SFPKhJ%]&|META-INF/RSA-2048.RSAPKhJʶXѯMETA-INF/MANIFEST.MFPK h0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-v1v2v3-lineage-out.apk0100644 0000000 0000000 00000000034 14763776540 031405 xustar000000000 0000000 28 mtime=1741684064.6480000 src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-lineage-out.apk0100644 0000000 0000000 00000041176 14763776540 026262 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789 PKhJYiMETA-INF/RSA-2048.SFmˎPཉr& r1rzW(c:=*)nJHyPfDJ F}/_R9~R)GV5:܆|!Z++SeB7&8r>IIrn=`4#?d8 3_ĨMuMlsR.0Y&/ATFlɈx^6g^>Tm.rvh?LP8!𿔥kdx#s]Xp&/o?V_S{џ[~d[0 ;ʹe9%wݪcϽcOXk(|.x֝sxg8>WN3k?؜*~y'yn3g]ҽ})${Z™o{fCW̯n!U3PKhJʶXѯMETA-INF/MANIFEST.MFmO@c#I Re¥`ageiu/y-(H"x1 Rp?&Ha)GARКP^ .:N)A,Uh2L߳ur塼:.di~Ql5ۋ8yA~0t3S೏6y7 ?l_qM \ijФ,Q,ZA#1yww,r هăc4ݩh ihr (Ky\1oto\rEu5?ʒnV<V[]S]'wT$dRWX3$~ɛ| nV'IBҴEE.f9:+H3 T.6?`oQɮ .d7IؙK;]7,8@u qI,( j1p"߭p[ͦ(0`00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*p%^  1ৼMjO(7Y ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >OKnk }pfWaB/62Fsü$)SٺsVIIe$ps6b#|spF^\&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq` werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51 lib/armeabi/fake.soPK !:!OS resources.arscPK[J> temp2.txtPK hJv@ < temp4.txt123456PKhJYit META-INF/RSA-2048.SFPKhJ:l(META-INF/RSA-2048.RSAPKhJʶXѯMETA-INF/MANIFEST.MFPK h@./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-v1v2v3-out.apk0100644 0000000 0000000 00000000034 14763776540 030003 xustar000000000 0000000 28 mtime=1741684064.6490000 src/test/resources/com/android/apksig/golden-unaligned-v1v2v3-out.apk0100644 0000000 0000000 00000031176 14763776540 024657 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789 PKhJYiMETA-INF/RSA-2048.SFmˎPཉr& r1rzW(c:=*)nJHyPfDJ F}/_R9~R)GV5:܆|!Z++SeB7&8r>IIrn=`4#?d8 3_ĨMuMlsR.0Y&/ATFlɈx^6g^>Tm.rvh?LP8!𿔥kdx#s]Xp&/o?V_S{џ[~d[0 ;ʹe9%wݪcϽcOXk(|.x֝sxg8>WN3k?؜*~y'yn3g]ҽ})${Z™o{fCW̯n!U3PKhJʶXѯMETA-INF/MANIFEST.MFmO@c#I Re¥`ageiu/y-(H"x1 Rp?&Ha)GARКP^ .:N)A,Uh2L߳ur塼:.di~Ql5ۋ8yA~0t3S೏6y7 ?l_qM \ijФ,Q,ZA#1yww,r هăc4ݩh ihr (Ky\1oto\rEu5?ʒnV<V[]S]'wT$dRWX3$~ɛ| nV'IBҴEE.f9:+H3 T.6?`oQɮ .d7IؙK;]7,8@u qI,( j1p"߭p[ͦ(0`00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*p%^  1ৼMjO(7Y  temp2.txtPK hJv@ < temp4.txt123456PKhJYit META-INF/RSA-2048.SFPKhJ:l(META-INF/RSA-2048.RSAPKhJʶXѯMETA-INF/MANIFEST.MFPK h0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-v2-out.apk0100644 0000000 0000000 00000000034 14763776540 027263 xustar000000000 0000000 28 mtime=1741684064.6490000 src/test/resources/com/android/apksig/golden-unaligned-v2-out.apk0100644 0000000 0000000 00000020667 14763776540 024142 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789  q{=,( WW)8&fYUϲ vHtDٽ00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@* temp2.txtPK hJv@ < temp4.txt123456PK ./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-v2v3-lineage-out.apk0100644 0000000 0000000 00000000034 14763776540 031136 xustar000000000 0000000 28 mtime=1741684064.6490000 src/test/resources/com/android/apksig/golden-unaligned-v2v3-lineage-out.apk0100644 0000000 0000000 00000030667 14763776540 026016 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789  qI,( WW)8&fYUϲ vHtDٽ00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*<G'APqS;|"|j%f  &0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ hS ,( WW)8&fYUϲ vHtDٽ00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >, j`79TcA(v%#<3h%ov'p"@:)m;Š](d> m͏~xLER@u2e #If gcȄ[C{BqxO4KO{6yuH)0h *CJ?! T!cUf̊}NԏP-.|)Wo`1~*Ll}mk%&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq` werBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51 lib/armeabi/fake.soPK !:!OS resources.arscPK[J> temp2.txtPK hJv@ < temp4.txt123456PK0./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-v2v3-out.apk0100644 0000000 0000000 00000000034 14763776540 027534 xustar000000000 0000000 28 mtime=1741684064.6490000 src/test/resources/com/android/apksig/golden-unaligned-v2v3-out.apk0100644 0000000 0000000 00000020667 14763776540 024413 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789  qI,( WW)8&fYUϲ vHtDٽ00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*<G'APqS;|"|j%f  &0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( WW)8&fYUϲ vHtDٽ00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@* temp2.txtPK hJv@ < temp4.txt123456PK ./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-v3-lineage-out.apk0100644 0000000 0000000 00000000034 14763776540 030666 xustar000000000 0000000 28 mtime=1741684064.6490000 src/test/resources/com/android/apksig/golden-unaligned-v3-lineage-out.apk0100644 0000000 0000000 00000020667 14763776540 025545 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789  hS ,( WW)8&fYUϲ vHtDٽ00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >, j`79TcA(v%#<3h%ov'p"@:)m;Š](d> m͏~xLER@u2e #If gcȄ[C{BqxO4KO{6yuH)0h *CJ?! T!cUf̊}NԏP-.|)Wo`1~*Ll}mk%&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqwerBAPK Sig Block 42PK!:^avAndroidManifest.xmlPK !:Շ classes.dexPKZ[Jtemp.txtPK [J51 lib/armeabi/fake.soPK !:!OS resources.arscPK[J> temp2.txtPK hJv@ < temp4.txt123456PK ./PaxHeaders.X/src_test_resources_com_android_apksig_golden-unaligned-v3-out.apk0100644 0000000 0000000 00000000034 14763776540 027264 xustar000000000 0000000 28 mtime=1741684064.6490000 src/test/resources/com/android/apksig/golden-unaligned-v3-out.apk0100644 0000000 0000000 00000020667 14763776540 024143 0ustar000000000 0000000 PK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK !:Շ classes.dexdex 035jIr9ԄJo:u?pxV40p  $ ,t Gs(;CFJWag{        $po nppp ILandroid/app/Activity;.Landroid/appsecurity/cts/tinyapp/MainActivity;(Landroid/appsecurity/cts/tinyapp/R$attr;*Landroid/appsecurity/cts/tinyapp/R$string;#Landroid/appsecurity/cts/tinyapp/R;Landroid/os/Bundle;"Ldalvik/annotation/EnclosingClass;Ldalvik/annotation/InnerClass;!Ldalvik/annotation/MemberClasses;Ljava/lang/Object;MainActivity.javaR.javaVVL accessFlagsapp_nameattremitter: jack-3.26finishnameonCreatesavedInstanceStatestringthisvalue<9    dp  $ ,t       0PKZ[Jtemp.txtst PKPK [J51lib/armeabi/fake.soHello PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK[J temp2.txtsr\PK>PK hJv@ temp4.txt50123456789 hSA,( WW)8&fYUϲ vHtDٽ00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@* temp2.txtPK hJv@ < temp4.txt123456PK ./PaxHeaders.X/src_test_resources_com_android_apksig_incorrect-manifest-size.apk0100644 0000000 0000000 00000000034 14763776540 027461 xustar000000000 0000000 28 mtime=1741684064.6500000 src/test/resources/com/android/apksig/incorrect-manifest-size.apk0100644 0000000 0000000 00000013677 14763776540 024343 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox#b|:gm"#)rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L87#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK޾ů'META-INF/CERT.SFmKo@= e6tC ).'̨Cy__k4inMsBr,!o(1T.(XC0[ =%AϢ. LU`#fx$KHuU?#n61v|&Ql1޶^ƫ($N_#]RDA6t?a[uIhtO~(|Dvzk]e`)Yfḿ'Hzlu*& Vi2//OtHͽZiהT)%>mqwUߍ/"ZLc]7վzAfPK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy%td$NN>]byT+rg?$g@d~_,(l+LEw{4YY*=&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:ů' META-INF/CERT.SFPK!:A' META-INF/CERT.RSAPK!:Dmanifest.xVMETA-INF/MANIFEST.MFPKy0./PaxHeaders.X/src_test_resources_com_android_apksig_internal_0100644 0000000 0000000 00000000034 14763776540 024116 xustar000000000 0000000 28 mtime=1741684064.6500000 src/test/resources/com/android/apksig/internal/0040755 0000000 0000000 00000000000 14763776540 020703 5ustar000000000 0000000 ./PaxHeaders.X/src_test_resources_com_android_apksig_internal_util_0100644 0000000 0000000 00000000034 14763776540 025153 xustar000000000 0000000 28 mtime=1741684064.6500000 src/test/resources/com/android/apksig/internal/util/0040755 0000000 0000000 00000000000 14763776540 021660 5ustar000000000 0000000 ./PaxHeaders.X/src_test_resources_com_android_apksig_internal_util_random-data-4096-bytes0100644 0000000 0000000 00000000034 14763776540 031007 xustar000000000 0000000 28 mtime=1741684064.6500000 src/test/resources/com/android/apksig/internal/util/random-data-4096-bytes0100644 0000000 0000000 00000010000 14763776540 025502 0ustar000000000 0000000 \Ku3k5o;FR )nM[s2!c{$ EN$ >P=ߢSrBS#}x)Y&gZhľ8mcw=֦dCMP&Qi|5ůL1 x7_AZȆy maLC?d+ DkC9& 5(yg7W颒W49u?\G{idR(fVk %:}J-BX BXߩ>.HTGgz:J  #,'cY8LY$now~gzLd60棱<\prR|%7)y6p G=<4|KBfZ{K:ڂO ?pI}0q=2hP.N!L0ZQQ;Ju~ƶJy{ e.}DDŽ%$1ŀC,Lv?6j@恘ºY3@SD\Yz i6|>U7 ^hoAofQPxRH߱MzUqYa= Y={l(@5vCIJ'vu.9ͅ<{_V $;IPYƙzua˯z^+?y Ö4#Үtv|f!).1#QDcpt'5F~ozvȬ#bo9Ƿ$ 3+ &ޖC5J; Gq3F @o8aQAI Lt܏h@7LCU1ayA/q{iD bW;Ԏ}ځa `2CRtOom&Vq i'8{/$-~x6/#+L(|m/S.c}#b=oMkXk Ԥk_}PHNIL|P' c^3^Q4l|Hxƴ. -+!Z|N+~L ;`* I:5x/H43Ivz Dx=U O;$Ǝb;']mߙ!)P5M8Gu#n`"#wIDfI|58r .WǎAK+ɓ@- 'YDX՞"T݊ڮx7tAA;S›^~:"۳gq3!Ɔ+ 2-2J mMUNJWGö.[9d"ԭã";' D5$(iG2'sP:뢨X|B N}۶mJL}ICAOJ/N숗__X\7Y ȮO2]/(zB1B;s27UD&Ah"c"Ci~D/r'o:SE(ÿсr_K~5 @fV#co[R@ĮRd^tuW*w^_l{Wj&o.A[NR!lO"Ap=ڻV^ZڑNAf&?ϥ=& uRl)͊ɍ`0H[k}XT SCcL+P}6ɪW;ͫa'಻!\`(i=CX@*IM;a9,򿧑~'RFB_>\sڜ]ѐy"6c2[vrv ϸ $mv+g%NAjHߡ}xr1Yg4Fm$j:S2W)t3InzRoNejbcJ=cKaؓ͊fWv&I[xӲnÝq"Wz<̓ LaD'YP5#,FJǙ4c* ֻZ#Z 1 ۀr>i<2EG/VKxP DZMq֝$jxI෾6wƔ6!+Ib6a&i.ɒᮗ: #X`2>mA9`BȜ hKS<7[)јtl\^r6U #Q&Cj-j !.o i̴Aorx19['XK :oá%uP,Kq$~2 F,(o_YV6~,ף9"IS _IrD_qFYvÂʴb̗-^m6@.]EpDZO@5+Z8cB0캥CXA$ 5mjMncCֳFCHPjrYdÎ+LjŜ^rAus\, vZ/`Ȕ}Jjz "^ gгo=^[TӑԣTc(Z{buʀx$j@T?a N@ܶtVPq%V z09FS-2jX03uLnM\g,A-)E|0ISsI!zB:yW ]#Pw}ܹmbukSjmK1!bـk|ÝPMHjm($p !Q폂ok[LG0*([>$S< mMcB+ҩ!bc&;\]u;gU"Zh }e3b Hf]l>P`jD dP]'_.J le:(nva\ChIqCA, pYViY8]).os;":*-wW5l`ގ:Yz[_3{=J:]_ 9S$.V',)}yG\m_6$1|G4CCgφ6چyZu_gAY{/k.U'0޻iwuz4Cod o!,UxUwVpp( DPl7 "3 ;Y??t?!IU)^jQujBs8)I'FĴR1evBv p鑞[_23Xae(Z=_;W-u*tjA:wfj>ln5.3eZpKpmI ;AhJ=dɕm?4[zޮ9+iEݒtLϑ5J !|0L,SVw =#s&@ {#݊zw).ӏs- Vm=&n W>0 A(Nr5ږ\mBb K >׳Uk7:j~JL/@虩+n-Zipv@mEf|F|,@O MeerAzt 7"4>}fSdYVZaM+ ϘMk6ƕ 2Y?hQyoLl7 92oDKOe\i(#"/?z Ω `C!00qsj'C7՝ 742ZE( c_r90%.ʰ3vS(ta<$PΈBuje5ou Jw~3:=zJ@kHF!n0 e9G% p=8Z`27d]"g"٫fH&i,0PXZ($yҷ MCp;r<^2@i"O =GBgsâql[3}l$Cw:z$$_ H4LB(R*J |kӅQOQ.i ՔJȎL4D;Ϟxدz 'Y07kek7,!L_ Wүڡl3+OwqAK%n"`&yJ9\[R2#"gK\֡:ԃ<$*wUd6(VWV4s1'J)UX8j;CAb}~ThҩB;&W#u烐i"[|YaTHfe^h 3SMΦzH vYm47ᤏh5iNlUlھiiDDNw(7zCe[b*9YwжU`48F\Q)qO,z1W=Ąn.=)n'K(\X ETS ocq48l`I\ ۳6:$1t0X2ԔCPW5KIR@k(7=f}fvK$׵{n蘑{ʓU8Y1uU #M~`պȧx \z[5ŧ3逫2F6} m{@2кfgDcRnrָ Q~K>B q;P>!)IFji%ԆBRD,Q@S8 ɐ@m7~}uj(V0}<ތ/B߈7:ű9_ϦB >yrBj޸ـ:6eZ(w͎xCx^CgK"M`P5%y-͢^@]8U2n^ "AEB5|ĄOh< s Hv>0R>o(s21>'XWGwYگFCpclfN g\@k<,sVHOwtv oKxd_l_Dtja]|{H ̅⇕>: m,:wl. ͯGa͓5ekbqEgh_yJhoUs񰋧@/Jn_oC:܈tc EQćx]}u6oG~pèo;BNʭݶ@{\T$'X(1g W L\Mq?R(t#SҖ9T)[!?s_.f~-.'F"ߎd &KhcQ*J]b X= ?jiu#ðtss/Maq{YxTgO&>мMXFVpr{:y@-xvڪq˳\ԉ@kDTYC0 E'\qkؙ: ~czhdKJ{5XMI%<@}q#GD'mJ`{l!z> bW졜В IxOx% ћLKck}WR㑁'=XԅB r%tIU^S;j` kRG _@T# (ЏϠl x?Q5q]VI~J7ɚtxOZHhss3.u+ qO;o7ע*lY"\p`#Hf<+*?7l74ҼuIdh?qCyՈJOdFOO DI@+dg*z.9!sO`wqg,hcJ&I Oaj/ڜ z$xmpsQxड़5';WHfbyzO:{X5Ԭ؛Raq|lm1[AgF 8rDLVƗM")²[/WzSDQ/)^r}IvE>r[y?'VVh|km @yѨwlMKcR%h FJ<-)x&,gI~Xgτ"*&dC 8`+"2e;l[Q<|ɣݒ׍ q صATpaT?k,3}ܭ/-K|M/ :$!eϹvJT'2wk m);Knԉ4H.+L5!.ްwVk#k# m[`]rqc4wGc]Fo겡"v [T.E/8i}ٰ*BBa%s#^ ಃ,Z5h1ġ$qύP 3@$HʓEU ֭GGaw-TZ)yF!8873DrN$\~ԡ{s{مm耋g$G[?5吺mDx _rx .E w"%"g2]u<t5y@DT0Wh~)15ܟQ9SZJ;@)]ݍe }`x[*`/oP~Z)YvT#aeZ)zD``IP5:Nsb_Y Gʖ@ċ  tl* -qx7J,?&n'=EE{""#&@i.ț)/C,BU,/glBo&3!D7`]]gK<#,V Q8 9(6j=9"ZpK6.V<ܐQh~Sb~5d I##P3$_q$ =dOYI`}M<^ \?w9PtbT\_؇C( ` qڂe,$[e귲XE}(ẹd4~]rUOcy繡W ֠[pV͢hQO)_m!XFj 6_k)+>=.S1[MRЦ(Y9#)}fyPk$Gi 2 qH7fupc'\*X*e*pCq}_'~/bM/¼aM Yv4C?TpbBg?8p*+'7I5 0<.{1ބh$0AKv`|f.`|7JߣAGV>k)~V3LP'reJNhu{5=j4 t1, &9es2L_9/dlRo؜HDzup 04Pm guj0mE˨J̚J,]= e R*xr$ GKYgt̫o Nj^ա FhX7w]1q,FJQvTq#A F^גC]ĵnw 瞈T`I =?th_}P5>{_FsC.(K k  UEj2(v o`bR !tIݟMRغcD8.Bd`mϺmq@j9eݱq huT4-h8;4DNQxu,; V˒:ID&n ,1/{Ћ;p~ \g> yh )Xr@0&'9SVc:Pv 4O _-~ kcbA#:1PS$4BntoU kC 9S&!9(;j<)9`ps)TZ|q!mFRn^"q"__r|t*;f%sm8vLA.o6PZUs _ltܹBmxUF6V90 O߂mxFma??*.wFr "3%|5ѡi` cR=cxQq{ġ`Qb~yHY)\\x9wGOF;~V'E@*i>j;j?+EcN;VE՗,L2l 4LH,VpXJVڬM'XߢG[ AaiG(?zW9p\pa ʁaAק//b0˦86D0_S~DrhPgzxI_dl%]ШV}m7U5) @J)/ c0QTo[18ZXNivΘldO@_&62X!|8j <w`cDzAUwEDUl`;b6&HPy{y}bc(e^Hy'kqF{Tw}Kuic'EYϙ)%۩mF}YICS6vQOvH@mtq85mJpS~uxڭ:z[r.]9ơ-ę%, Ǝƚc (ެ }h< *Ϳ)հ x>!岫K/./P_ 0+5wLV Qz npĄ7as09 YV w`R&-4݀iy^ej%HoF-/e@p.IpՅH$oQ;^90`Po!\X'ڻA5ez-ތmW^T%1}#.yU9X۠9^ @sV1~\|,a+frQ FX]ZߘLw}k ]4)0w,KbYbn1w}yuӌQG'?Om*" 8Gprπ%O{Zw b+$4HA4@"mvo$?\=`zݦҙz]\iV(}qI23|^0/@]]7W]DžM^u.ï\k>}ɵ!~9ַ׼{j$+y]09-ET8|u( b]H _ 騠oo7=H_}?/Pb˄1 *ѪZfT=)16*(Md,-BȐߣ^Uޟb6Ҥ( $憰,~2 .*q#%%DAi#zbmB'5&# xQ~&%tP+l06O1E˾">b9%] v.dkϩl%rHsy@b1mQ OJ6MNIyogcܗo@)Y^k^JnCF:HXl^V !n蔦#6he4Э|Cg /-yD@"6"©QwAJ,zb,uY?~aøDĥSyF=A" գA[Ai-aFU8*V4a ;xnNɀdjW#* ik!Q}̐up}t W ooSop\3,k5Ij5#y'mc+`5R-Y`˷ȈScHjTj81qvpM2hoPbjs>H.D&dȑik=X-YPETcC"'Q ^L-ME ڲ`(=nbKZ0m:`3'sBBabWj&tOI=QXߵh$(fd9 :|Ե,Qd!PUC-icTzB@[c tZhl`{V tlIzf74)J/~=%AQã*/z9| ]).,҄j/O%26=C!aS:w״;orm/R܅ *!|lO^Ga$ȩ`$9'NX&<*4,d􁔤v_,4ii?--j&kղDZ*;[6<$֢؉;ʬ!9[*J5OBf3S S.{H<1 :hzkaA"uQBO&lڹ Q S_V 06 q YGn-4`2i4)Z0 m| Ъʕh{ }O5l2q^WG#uY6<E`nQ9<3ssc3T E5#;5IQy`t6XPdC1߽i?1y!2~dLl%qʄg;@v,<E ^<ͯk 0g]~CEMX֖QqSz^~ߪ]Ȗ`gT/&2JF#jE #vD]_dJ [lF8G|Kafz 4yX8k-xW2Sb.~x7<@:؇,I_AP ~l2JGxL|yYia0V1 r9$D1W}l|0;tOxX,rNt)0@ 2M~<2'B}vGI8;%u(`Eb{h3`0 kO~MMyϡ:Ō[|3/QP:. TDLL^3Gy؏"Sk: SZZы`Ak) 48]# ۫B)zoB%&@XL=: w:@, 0 `@g5l<ۈcIoRR#**E*!zw6@ GJ"!wsx@.2ꎣsF ~'N(N8`'\Mx?oЀGcYakQAe7'\.\xNN$q:^_L0gTidSf=fr(cWFdQNn֕9K67.:oz$wA[.ur˧WK+$ E 8|l$ ʫ# &xDgIƻ!*8+"@*n ko O$H%b=æ⺫~#&{v>*s]Mo8F3wgJU!U]!K(MX$$2!(| =>7|Ԏ\iO&jo|<~Z=>ԠES6u팚|bɝi;8ŘZϞjlBm7j%,`}T,.x@w$=<ګB e8+%? ෬H-XĶimJ)课|Gn0v4@di }0řl+YGGɃ'.hu7$/x~;A`֩^ͷlZQy翔%̏dm*GxRv/.:.E48_&FbVs95!p$e 7{Lj}Kϥ&ٙIo 5UOubKkeI f%1m; E:Ԟ4rlb^4ID#{*3yqo\q撺?73Tm&ܯ !E!9*+'HgNWMd߲\{`Lɍ@tyzV:_~yH7EgtD>yy mmb_8 ;g0w!t8[2+|Ž4PэV13NFDmmbeAnkf ~n_6tI˯2jpη-:DƇaZZLv+5B ~\BEqnBF Dv3RD,J /J,O ~D(ڻ"0+ {9Vƶ/vVikޮWlu{9>LbbIVffF&=$£V{_ρ_&\[]Xs1ÌE@F'wq{Y*뇞E3^΂ qS>N014K\UzyČ͑w jaM27dXqU$[K2[H\CKVWYz 缘#>8I`hKM8\䧼 p{kt ]) !/*@poO>{ʂI%LyCòy}pġl ?0yR+t|Q_80&,=>[PdbWI-ta4^ZG)óA:olYyA{.Aoǡ !3BՙbxV+["Fnq:*W#`Ƨ{o+#z(&D9( *P&߲j#;m5ZZ$'=t*`8p|wT]3A6C0BfC\&v!qF7oQދykj5XzuQf]\9aJs5Bca1! ⎴ws4%%~Svp%NvƉg,Ŧ=Q]19<,0 BK,;4_WÄf|{]:% 1[[Ҕ)w<&gu}F>0tIg4.#80.V9L^`'= MZS'zCeyW:  - :T62j#"+z6PWwFp2m/3zA;$,Z5w[fQ_#P:\]l~!5c]mCR)t'7n0fp,'{V(sq\xy(AMv""מkMV F4˜/c_r2/1~U'4I\{] *}#ǿ&]ޚl0l Aa,6`5ga]ϴ8AEbq*O #hV'VҮ2 !N謲SOC̘x`z|pZb7['Q+_6+4:*Kk飘܎X%9dJYGh|̇R xqHbf9?3~,=7Sס$nSI p dɥрM!Rw(KRY.g]~6 76;C6nkǏKeYBO÷N ~<|E v0897BR@) 1m%8GFoz.M&6e1 y$>+Ply"NsqNt"a(Dp)_g8`kl9+88;ܞ wHLd\Z7~vCG.F j#Af4⠩pPaDQ^\ E`Ȣ'n$6JKkc*jX*6iix64U31,lߜDE֦K#Nͺ\P6U͹ٗ" E|`𨒚(Y-N Rщkx[q=סm)H/뷬 ,W`MPAsO7 3T?e}TKMDR?yô8!ݭke9ZhWIBgMr7}bRJ˪w^TIYƍ->+:ui'U1b ゑigp=ʸ1ǺrkWuNr/i KKaI[ׁǧU_7x4Yx\p$.I]U=6v'!,brOòaƎA\-QB#=9[~DlUڎSuhPlfY2%1h3 PrR_%z8gM%BFfS,E3yx,)XG:$? "6`/ބE:2DUdUژv1_9}:c ? r$P?9}rk CNs<W[?3!& F^g%^| D2t%Ls_y{A:ÉzC:+җnhJNґ»n@j8Z]Y"*uuRv" `U[XK}q܉Ɩ4RdN嚽ߧ`j:B'CS4xDh 4$4;9Ә"O-ڿ} "94x3px{qPɍUT;6R~!D ua^Dz!nZK-QKڵ 7-Gp>Dt:1(h>URű?HM}T%kp U~/zS}5V~\;Ni?iGhQ^繄Hdw Fa*!7}0 7ers9F4VYz#uZ[Qxb[ѕv /_.h q1P`L?GU Xv׌0[#랃4؎UR yADm^ug;ԞYCMebSFNH]s=d.n<\o.#)qgZ\n:׌"I$^= $0#Il҄#P,N)j1[#U[aHe+ RY,Z?E&q 1v#ٔ!74t*tq[ MFy/lO|= c/V/XwkRj+6QKhm^&7wYH,ahi#È~?ʷm)UL9:հmq7E%M6\3C%9+LQ]>d,r{ rjBbYNIgT/._l/f))BC yayl prcI|>:}>+L!#&_dӧ9,&faq]%b\W)Fzs07a&( jjRspZ݀%Dv9Me4-Nؔ41 Mp%;ڠVZ0nB$qA)><2W(XCB!LmظH<;^?6:N޽W U hL)Z 86nІT'%aN.xC#|l>g֊oX2{8DƁb_G{&gVž!SU;1Gu_*kOJf֙:h<|.lLvrf>De| #jw4cW7b.N(--ZwԫI#%#md[$o3aB.Z]mٱNq/w[lwDypv Xz'4 bL?5Twg@'-;z:9$>3^+o+75kfoһROdV}j+d8``EmS%Ǡ f_]g͖oΓ퍳dٯS4ad!#l&.'u4O>kmIJS3$٧۰u, P]yw KZfa?@ð#߈Fiݍ"8הWSphچ@R<55Bl1ngMDdL0)i`Tg*LjvK3e^J۱ KF~m_K6ꥹ]*6/k$ũR%%Rwض!zG?ܯ GbDArD%*.>Tk WaE`02a[z"D=y1ؚ!h\@") TRE!RP=ڲWr@fܪ!Q/Nb/P,Zr1ԮԊ<$j%>0.K=v7] 77i"1&O"[S B]F~/B_+.V#rUƣAEqzWX;Iw t3vQqj{5Q>;*ߦw "#^t'Ge=~7meXt㓲)䇐l"Vf~T!<*6)Tw.;pgfB1bl|z/RbHH&v.UV&dI/pfϢ)jM'tT ԿqmJ5Ւt㚱GaCs|}ds˘,DtvEE>#zĐ`Y#38-.eb)DrDF]9g{G [Qq]f!"󴜧^opI PCm3$ٺ ' [r ˶c]]MſΊ1я4ѣ0<~Ƽ)E?}yTAv Pqv'DYz)b n}μpFQ#kފ!s| FqEŃ]MŇ*`w674?ĵfM.']I%3&Vsz3Z镛Mf>WUڳ擩tJKT>BeS N{ՏsrR r4]INI;C+t2<|TDwwfaOcQ62=ÉBb]eLmnk:%񻓒XR6$-FS&*mu=sQw?G6}y*c'}bjTaM!d(3]LǛ?yfbY_s|#㝀 z nFՎyU$OtYiP5H,(nRW9Ǟ홃3Qdm.E]\뾷^-WI([*; t=; NIu)wf`FnGf<7 왲^ n;'Aq3ft{.w-{_C7/@; xx-ɘ(<6'L4ܮi,Ygɑi۹L&] <`_bman$Y.MG>00b+ ֞y*aEqqvz+A&+m/d*i]A\.O3`v KI +Nz4 [&4;GFNm$e\3ПHsުe#t7i>o0~.g;I~A aIꙢG ĵCsZHPft,BɏN+!`>$1De,O("]?o:779r<Q.3q(Gse/#B S>rHEcAlSle\*c15k3^f LhIY72%D#*6KkOA)9Ms}FilԶAqN8BbJ.5PR`}RHvqW;_4w5v0OE]Յ~,aV2{9{&؁` _g;Q";f^J<9Z1. 9z֝xf 0^\ E7jy$Ԑ/|YWjzަ nTC.H"x!n4>L+nvwxQObSBleA j:e@2=[0phF:)y|&ceQHЙ˔tNKoPZx7C|5@R[K Qk@XzMbs]MKpeNcRz@J.13M.i fS}V%r-BXκFjRʤx(1h"Ʀq U{@ע$ȿJ$!̎8;Vms] on;%U7$kEL!?agBl!"=!`|fO_ [/$~Ec|],&EjCU -';IѢD}|1Ach6}8ďa!'?{g(C&Q) ,ɸȮX\ x o(J2VqTlTN^lzSKʗ_biClkruEjn@V([/ 1xop=KN6Cci+(Cfݣ30_l3=7RLN SMX K]xd|qH"ƴ> kLe@##U K!+w ;X9'"@δh xۍBDw>w1 cV.*\@O~chGBLk \.5rLe97wuw+=6B-F6qWf}dydJ1U*$%Y\8h˝xCj8|u$. /#ZU#D*p֟VYHgDeQѠ*2u"aM_cxK9a "9M ӰĢvOp,!zנ40t}& ֽ! }::Ƌ?!JL] O)8Z)ʔm4E;mpk@ Fu?)Ґ(L)  )Uk"ST~c/҂C`tR*3Քȱ) d?2mݠ#`4;A*>ŠY"| Q&mW9Z %R*_kѼ+)&BfRl7t xb#^B,qf2K<-5g1XPoS5LT%QcW, K6fu–ݜ]vVh^;fjk9什xl& v5FQx^|fh\5_oӖVz@F毋j! S%2)CvtYKy6-/ xPL8r3kaeoB(_^n2cm_,Dƣ9&r$?tA/y]+-;3C !v۱)DE, -7k֝9zg9::J pmǝC1`S}ezc:W` WKTd~GWO&s0~X3s(-WYOx}E%Nk<o(ûWX2´tg[  !.DuxTFf }CL !?"#&qϜ_֒)κ`sognw- ."w!}[.ޥJScgf/qT8Fn?s,uyZS/.Otk*4`)_>s+ QOSlE2WT l^v_*ls -(l{5*f,+L(Nɖx,h=WD ҷd pMB t\S΄ Nytr ̕ 3״?9ަ9= OC3bD{gE }B@f:W$h.HԄJE!rjY`r+T,yёޱJ+f-ω"I 5 [;@MDaUFĚe,;=6,g~:-nD8iдJCϓ% CԇʺQ)۳>X-TJ@m^6?ES8<Iv}QHܕ*]N5裗391RV0U  [*ǃ4׀ /"b{6afYϨc[9^h]{80g&WD ds{,2W3Vʀ`O9`FY? FVE6p8~ cи8e>YtrLrSe8$`CT9V(crؠ"JfLZ^n?s62QGaxC ekzoyҌ9#cҒ|/$PSu)SLh6Ĝʸ7ػ)7ʹ܎e]s6ie0mҡ\{x[fpt.]ʕ[F J2Nfy]s 91,PZ0,t!8_7 tsi0wĞm_GYS+oJzP+,bN)ʰ =>]sy[@R1Pj]l=xQG3^tY>Yx FgT+Ȅiw/aӫ#Y?PqE'?[sm!a.|:Z3(+ W6weXz 9{x ǎM[<׺xm쎞($re@1 0pW6jYĜ}~hm!#5 Kn,7ˀ֫soXx?cekފS MݾG؂HlB}I( -P={O^ý^6j@)nWP./hq—t\#FV-d""bIމfEÛjp WH"9 ؊5/~n]Pb5@\hUdKڜBp[Ch We":JRj,HzP>ZtqrhNFG]#.}q=hu(г;aD.%DŮsW)r ؈ ftB_&GKU1HlPR0S)q'V6ih%)f1Iv|te'LEcȽeUg[lX[B0Nm5iO,=r4݇I(I/< YWA=sq8_"2'DJg7K@a\Jmy#a^GQ) +"cS^.ܛu6,LMDHL"tŞ4)ۣpC16*Qq%%\.6o:- c[\䡵s61#mlF>ONI0EW !mtMC;4*>H@rF ,4n{(Ss [(>L]lN6WrGZ# a۠<=*{یS3*+">6]Cm2Vqp5>j)=9 𠛺AtS^798ٟf:WP 5U]E^zF|"SL#GPWV?e~K{9%\:#O;ƅj"'&<6^ U6s/J%W;98CxiUF1, G8#B\ ڐ]j2Š-7ۓoWZv;0w+ʦYAIKX=CPw=gR z8adw+ބۉ"~O5!Ș}oa~z -REN.%^o) D^MѷFe BJnUf8{t,$`Ovɨ^] ek=k wS1JiU0~9 |(Y)k֣DYJvDgEiV/cp?jr(Q$zb0kCƏ)4Y)<}C+@]3 =S"[A;w`Bl#hduڿ2W=2v_/&FnŧeM+ 0d a\?>SMT\ d. A9R?zW`f"T%mپܵK$u"kG.GY̩KccnpX,(XL\s.0ҡ-Nhݟ|y0qy(~AInWT 2 "I~uvmտQU-rMt ՂA`PqZIKt9"jmJZ<}ut%%V革\ /  ,3w?/*40wV3AolEsDn4 niw9$yK\"8:؟[Hc!G"qDH]69rfhb AZ=>-y#iB.wºm8CLev-Өrflj46Q߿W*>vhipUQ0vɽA̤ kˍXy,p{hfM1_9퍿 ` I"2e,M5 \ QnSu~PClwA,1GOA,Ei,-+Si[a2 !Yܹ,!(X +ꑫh9='# *_Q69bU@{,%V4wglnh{p2TJqRn nb8L O#°&A:= V3;H6Ywo=On7a]6)n? $aLQnƂa5r| m7hdxTwP&V}4 AEI 0#ϗ[GrqۡN{@pi W?s 1*J & + s- ,e) -V)́TueBF/g&dM.x.!-jQ붲%~n`[I@0gOBF$ʮ%fblTxDu^4 OD*oty| >0~Ҫ_nsOTHXyimmzq`yz 8 \6>xrޯ2t@15ӀDoIOpkn@qy&}DkV'ݤ "y;)Ԝ9@hKĠ{N4MtбsU[ȳEE_~p#Gg*EwΈ[h 1 Vxx zM:RgGeK# 3o ^LHMˁS|bUNmjWe~٣;_5rvecNr-KIhC]U[:4c-#NYO_kW55$)w&L;/S?LWpфTFWT淟f+{+mUOih"#n/Ƭr6a6KoԼ ""44MoU3 >s$00e0Xqe Ϩ{jY*Qw;[7 C ΛU@+N8Y [8,i,00Ƞzq-[̿[\;A"~mY>:ʦF5p BrV֙_ lgHEמ0~2>(Lx䪱,tWYW6#j эJ"s, #dZGdbuK;ӲֱӅsdm^]5J7}Ύ"=X7ܿ\{_ U9랶1ǪZJ k}e{XBz^2'Jf%`MŎZcs:S8]G>F̱\G(Y#:u82Ս8B5c|&Cyu]=1ND&<"-:'XIdk‹^mDG(`?Ba_$vF1PSc}Ye6ql dCe hL+y_{ӞwGrYj㦁@C\k$*Têg#H^pݶ[AiGuoK7כ@Z&RTR90t`lr([C (Dۮ)3'6/g>Do`-R fbCkvځFCVf+f<>f =e&LڠEMϠCVMЁ..q_|{Il0vl;渶jw%N\GUY k/?k-V:fvdePpdk8OAS̾8<3=I*7UՆk1|%%/HGKձrtf%GFeE̿}(O&,!1:߁~v1AS8jc;&kQ n7 6;!v%VUW9ٸǽ7Ŧ٬_R35M߇Yee0ei[?Vs|oU6dtTX*6zٔ-”mx hP8(r%~\~EuI@26ƩC!"Fl\+QAmo|VȠk5u뙁#>@X;Ȱ+`Kk@;p Rŭ|U_BxQwͤ~pd77Mh6C3N^'m܏(#egZ+V!;wz7}d(9Q g˷wSbW`dq_0s$ ^Z&O@ˉH$͋l@Be!89xxgу@[(P@4S!l1fLG3GCU#XqS9%MՖ6DI?bhkxyovT0ɛֵϬClb+?3eKP46wYƐRI(8sO:ǻ]7rHt(F|A!9{2)>-֙160jaaH8j&P!Fn"*~aXs|~.9<[jXR}#gAvTlu$Vg+L޶(8;zkW( 7Di\,WUMnV7o hF}P_rW&ISYv Ģ=KU=MHtdAVrs@t=!%0䧛y]_r~I+:^<*= z~[-A+# @AK&쇺!X| ESl!ꏳORhY8GuUgRm_7Y+:B9@yu8d)6E!߉5OKZt( Gv y mrͨja;lf8>biCT.7Mqa˿mgr7kH B=n:y>!t-? V<н3Z2$o(~Q4I31tՖ- 3U>R'{&Vc*#NNVGP*_* LrKL;,M_r R-fB}gh7';|LOy8|7Lt!SB 2?)*Os<+#غiC bFcP"F慊k𖖸bEiZF*AZREN__O*{ikvp*5s2׭H+.v̍_2ߨ4JRÈ-|Z<}D8R,g(\f+4}Lo^8xwG,G[iڌz`A䒞"Cyۧ;Vm=8iMjΤ1HŸS^x:Az,_P*źG0lS#8d HGVΊb9h |)x_&K03\PIL0Cv;Yt1o\2;Tpa2Iɍzhc٭}ˆY{3ѱs!KUa9 ?9 'jaS.У/7LZcJ,<#XK۸b5xQPF")q!C.4?D5=_oʐJ8!pOzgs^æJr1% BlItjMjf޺o 8 \[ 9*A;cMMפXNfe7%qx.מa]ִMW7`7%1fh F%BJK?)S1Ή(uLs,x{?o"|qߎH0qS ٹo&M .G(Џ.PimYAZZpDJW6KNm\:k߫\|c#17Q+6]ts+-pv]pC@@%0Htv9FmtgM= dmM}t &6:k3/p" tcʕy{(%G)ҍC4늑>s=ٖ[n=Z$ӆ6Ρ5ulz/oIAo͠P~?p|ᇐj<*ܢmiq"3j {AmFi0s7<>Zu0S9ծ]M3?OAx-Pg@P[n׉c^OIYV6J=}d,wwѺqP/g'5~qO,LsC }EaCzG T(t(nVZ*Pz+O,-Ϸ{ƟU2.I@y3 5L$Ѹ9t {=B[s``;n煠2A9.ӭ8qBy zX$2NP0i̢[7.WtđZ !zv%˳>]:bקZ!9K2$0)}(g+pO셖T=u^^s8!¼#^Xg 5eS>+(vfFWKjPPq9gi(*Ƽ!5_[;́[S󽭞BrJ`L^M=Lab F veMʋJ6 7\T}U=ᨠ=1vе/77Ρʘ0R-ycrFJW9@E'Jwqb޳τYDS`a8o_)xGθa6VbT՚'bv2RYEcۧ`}6q0vAS@4yƇZL0:qL?a,ݭuo*7#@k~,!X g9@!~jO=*Ǭ.Tdof:3{sqgΠoZKVMZ,ŭ@6E_$,l镄n ,.P"ӺVem41\8As9l?rիdxBȏzfg_R<_j=MwKM*c _8J3ZsBӗ؂QdE qo.HzLKC:3HZʐ;Ce=꺞K`i\ߐm8EqGGhћqX',a9Z;La ^ ՔzvȿOj!P3 ~ݩaita߃p-|1a̯Vľѓ\u (&:kPcz>7G̱As8Dpᣗ8^jm+c>CoJJ0@Xgh{N&a:7$%e7 cE9?H\{$QLMdp@`a0վ"C_ro7&{p>[dxz/YùDG'3ER˔E3'Tol e8j" Lhy58$en]zBG ʞG]=3c^5uJ?&%HK%tCL`SWaSTޤebKDVR<.JZp,ɫVס龚Gg9bJk t3PiA{X:Jϣ7*z$c{ɍ 9ٺEbԴ@?Ûb 2Dud>[erLTO ˞b3JlG=q}B}&9!`/(DIlnBbxcF*(:h*՗PO !ڦ,٬Oe) -_m'8hp,$)ǻ>dїIr8 iƥfyU|Enwo|u`^[;-sr:FTOB6P0ӿ!NWU"+ͮ $E@Q*yKPwćUo[AOc]׋} UΞ2E+7iSąͯNT!O u|( u&Z\R5sEI$+#s[=s}.pO`]&o.?'4eH;F':5if,%vCaHI7PI.:e /NU/&)|zэɈA̼=0= W> RV^>mHczk}k04@L' 6y;KX9!&\9²b%b0Ιxc,\5[Bƀimdm~Ǒ@ 5NDL^ _v:i4= |?I)ΌΗř ~ ۝3=>8k c9 '2 L[r1x?l@ū9F+\[w5\9@לN!x?X 'mJ5)R7VR;?3!`#bE)C"t(3p Ұ,Pȿ>,^1a dg]Ŏ)ʰ$Z<5 UAqp ; ?@8U9gA"N[V;:Γ}/O$[9'3taL8qM3Ň;2X5$Iҩ%Ϻm3aʉJIԥ'{%o &N@@\YD^Dňꟑ%$3ja(W8TLxcoSu@w@"&sZ U[4üYT#$ ij mJ`~Is(1&HYQXj+v~9 -\Y2^(/#KD_-,E9SĔaA4P'R98.'Ŏu_@O @)43ߪ-R>Bt@V/yMVѫ03X3#)UER3 ۩_Gth͘+G߆Mu#W96FGWOTSe~M_5G\=V[5T ym1 e` ٪b=~Q,oa W'P6m0\V=Iz?gmQ%t6oLPGm&^>s\RD@jf E ,˴5)\ȯ! ,#xytg1ŊHV#{ -`:v~ԫY+)8ݴꐟU%o_30|6K#lmVIhc~ EcwAn> aʉY;pG283.>4 ֨S^yYĄD\쥡(o)=}O5X\smV9YU*#BjJ)K be *>顝g{TƓi %` Ś9._P&sfW6]y[>q#VU[hqؘ==#?{A&yvTً)rA8 w!>TZ_?;aWO^!;H%yABnǻ: 82 +sVL_Zc"7ρzh솼oBSHMsk*$8?nrȬ{ Ht? _󯸋6H>շaȦM7)aHoѲE]t =˞:Bd,ٚ(zmAӠg}4ڏ4%QMPEȽjw@s,&Xַ49z;y.#'@%w0$V4B'  ,U$㲔:fO"&6tYbSSYIpmQ9R㦻pA~H['[Qh x"6(:ꔏ^(A6L D+c[vqCS0R,$Eިs+8Y4 : C89k "7}<.X_?j6.fbx:_ee _qŘ+LAu3t6m|E_ڃ=(Ok6%2x'k1[۰RP)<[բUbi=vY{ Á_Ȥ Wѣ].aI%j/wWL!)*Y{G@Kϋ ?$>9'_G:;0~Uo/W}EtzIi`0l3fe w*9uG};4Re* |5.iڨhr*a:9Nlt).fyO!Bs)WE^WoQv.zbYMW|~aO''צ^[SwWAKnt$fTujY^+$F2M9t>R.F"97![D[ zN3[  $Bm3%}=-4rg hFdxg3wg$[m%0y)P8؎`Y6%Kp4qiFs\<=~+tYQfQ嵋$'~ZYjFq۾dDD[Cɳ{-ojbp9>HʍLoX4Pr,J\@^ѫvxv%ycV׹?P N[A]dF +JGhrai4.y5M#ɨ|),7%m hRD%g$n5gwew6Ƚs |$KJOz̳2G~wLEu= 6lnOmQx/E/<']A# tMw 0 >R[6PPJ'qDŽ'qj9G KBNA ^DbAN q.q}EYC "xi0)/fE\Z#Ȇm.P*]G6j_k_Jm6}?Aפ3ɗr^Q#%>/y5ask,Xz4Xa 8w>z|D0=Ŋveԗt'x0wXum*k^$6<$W QYЄ8 iqato`~[ubT80VR *\fnbyMޕB?B;HonQ`1A^$"Jbr #!Z۵Hb6$RK.K앤xԞkZVäљ81sY^@5nf*O&}J4e|I^[ր:g ƒD)5Bic'HAe8 1e,0ʦ;2o^( L wF V({查YGAmĬbweT!b„@ܔ wYqMC]JV~-2a:չ_p>A13u@!ɎnRm.)eVW& aKȚ Sj'[Ys~ E]p.EFri!M&bߢ~,ȏPa"';B ?EH+h{42Z!$ʟ7blzOKõ0fY\)\T[Ahu颋 kz-Fn!X !^L}[vr¥czz ; EMtJcS3H0ij!%^AaAccXZpbl~?:%Ab@|VJ?_?oo.&f,?o OEA#LJH8aCXnV~;`CSl5x whC09T}dVV<]C|e֔ rEیu,Xw_Tj^J !òRIM┷F~ 8 =Rk湡=ځ;8"Zv9ZCcV2,P6 EQ/ WEPD"קJkg1r7U~y,U`z@Z͏eahz,6ʋ6F Ãc]V5>1(@sȠ7Sv7YюUQ`>XX04NwXq&KJg]c(%jGm궏"_v ^[ @csDڸ9Z/>iƍC G' U[M\=ԥgǴ=kb9h P]Չ%3=QAv~,*fXf$)de~yJYՑ1 vbM/irءsr #Y>Y=_L ޢ*|g|<֠P=)_s-F3(JfY)cT6b^ģW5-}[eTErBoabm5L8E \ܼq0sô΢ 0wCU :Z{qx!?(@{(]qW;&A_ p%! t`%<cD΍ TJqY+~LƝCB~jr kM8ƘAfi"}LB !*,Vq\kIȎ-ߠ:reBB4vZsoj!@ Ukx!RajC$Ψx2KF/aʕ^lD^َyӷ8aħlF$ }Qzy)41a1I*r8~Xb$G>0#.n09(H̬/ W9.?u JRp9ӦVS19e[;? w?-2<+Gޏ oCboK }gHOch'lC0eZ]62ޑ* ^ѓ;ݔUdʾ^ʠO#8NN)OeJ/ bhh ( Pͳw\? ޭ CϓF찓8s }>Yz0R8o_sHY) eho?:{Iib$4 'IXi*[ۋp{˳S(ùP`bnӉ;eA v61כPGfohgcɆZǕrPRXv(bܹYwT>%(<8qi>β2{ѽM9HR645飒}Pc=^Ŀ"|hi]+4mɿJ48 s\q3&aO(RM"C.ۇ~{/H~kA"ZGERyHomͮN=xy3ߋ^c2~vk9UӚ.okAxA֏Mո7vHm%'H}ho "gMqc^GPC1YG T`BF:hҳϧ{.5/)g1"Ov=Yj,j6λK=Mv*t),@&.fڀ^z@ U$ݒ @aPC")"kUQӻh|nѝ9pPьKw2F)h3MǥL82 ݧ]lEƌ(pC4¨ NOR`zs03~z:.G)əy}{͇Si7#\4 __@>7varS01L(3QdoTFA7K0Ly7˯B/ocz,uE*%}M Ŋz Ҽ'm WTo)_\G#Zo+}<\JqMi+U-TDrnFwuPgFD+89d=0{/ZQ[S I0z8bUBCʵ[,@DS ϭ?A (3\o GW S}IOe'QʬZlk'hn;Fcޭ<mhvv$;OSgMV*.KQK*ɔV-W%ë"&"Q:_E'ֻgc6@h5Rd!z)B8<_,k I6RiɟDrWsS"$:[ $dܿ#[ 5Z*'R6\܃psm1s%s \z6+U0 37k`-ŎHц, le#q&AUӭIX#`r^:Sf)O8P\TUіbo 6nWveJU >u3u2Tv]E_-hzPlb~~o (MEp㑷[h9!'5fs< ѩK׼${K/7ZatB2@5%O]H@N8IWv/{gFL]'4J~kc7-Yp+FiTx `gҵAf*@Id.7268S? Bc0Ƞ9 S+h0rC*C/*t*zCTKlf5KD|)]"ΖmFT* [aɲV::yU!E7T]gsqI9 {0^qu)~QyLljLJ\6Qfe]=, Øpa.+?V=1O(e?D^Hde^ 3 b@,RVWؖڗٿۅQmt"3~r,-cD* kh߁CMipԯ Qr?Biɗ7$CW JWwH'j.AH2>˾ za(2! oŸßYٌwGc#p˥?UE=tˆW3gPӂ)w dH%ZW\ X}5s2^xq)|Nimg)Yle[b22n%uF92,qro1{xѐn :y&@wQ Bg[ GeGEQO|tahWpAu)e 9IJt~SHu^ye ʅ$N184?zڲX\)bA9E+ K%a]dli,wYQO8L1 Q\'6b[yoE슬Ed͓u 10ւ^ϔөԲ"/OϚh*e)_6X@=Gm¹4dq>5n w/1 Ab,ߺ`Z;=t(>C)v#{  MjJ[e_A4xȅ:ߪ"@j *&KMR9n\L'Q,ML܍L}%Gi fDN[vW~5jϮm#_oo:(urx5ekAbCL_lD֘ǘ.tqm<"Dӟvd0]hM'8`p^6o97i~/u}Z5]-eF]2cpXb>$CvkpQO~8 ?*>($8*Su#aM(N.K UHlU2錺ժe*w]Mk;IA֣oZ<*xbحt[1a+"Ƕ} Epjʄ 2!ˣ>Ȃ1am3T{&%5+N!lBT ? Adi|up̈ rp?GJCػ v:.OW)(FZD#뵒hϹ`qn,E##uIϜ@tg'bkWpae 6 vc~Hɘ%Z`'QRLopU{ihR{$Mت/rL%t)>JWjvl h$!eU(b|R*QH3t˵޾ܖ&H.hzxVr b\O'ؚlOo+[9&lNFj}VIH X_3v!@Bh煜2}W B.IGlLYrU܃%P¨GXnr4|=]$/P )iY3ć|}j V>)S \D43W,`pVʢ5#yBY7Ϊ%D(zl{S+U)sm̚Ǘ`?`R,PpyZ AAEJ3S>є0|p n(//mğ~іJp6v[O ĵ ^ΧufyHIFitT*]M>+$1fC^4R~OӤ}1)n]zCekU{'x6P#nbڌc!6! yRx&=i&@h |,mKЫVVI^WZ!4J'~/6!UU񏨸ݷRU*.9X<0CDxNRSϑKd䄄ڌt7ԭf֫yt%ߙ{DA `̚A kgӾ$ |~[KַOhs9,pSwg.e1jƣ< ʝpAhć2JxxB?ע~PϚ'+@vP1$"#|^xY{ k wN&8ѯ@rE? zc#%~xkQ$ɼd&SH;m݁5h@*f6$|2fhH%<Vf871/70(ˎ8<5ɾBl>|D,tb{?\goSZǃ+MY@]{})!I7u?%krϺ]:fsXEaG$)jT1Ib:(.Yɡx`h OIkLFA+b QLcNK!adJr`ģtݘި~RG9/Cyˎh@K~/`heS9֢|uD ynJ%Y^Vx20@{6RW*@a#Ȭ' * #OUn r OP,;pQ*A Y<[)PU7iiDcH]t^tzHcS#n/gf̜lfk%/-vߍAT̮x*+:i r6!͟`tF6KbVWPxN?`y` 䚌{^/H2De+R}ל;L"A )upge/Wͻa, }Bn8ڜ E `WBhm^Sv%|MScCakÌ 4GurZdwSf3ŭ& G@!XQ*va='_N*K5;z9SۧfDؑm3I_d%y^Mwp9> P5Fh*NmJ$a6,T I]FT^`LY'uM.ֱIsRbYHu=||5lqcj<`܇զŪ`6a!1j4 JdaHFϱ5('|Ff#rEv=5 ;G2zUt찰cD&}9!01]܅.?AO_,D_dQr43a؞ _d-y &6*{h_n;. .JjLtB:YH [;,dx"(x=aI[a&~-}#W;[$F8 #N a"12ڵш}&pv{P-&zk,@vp+%sYqJlo'XL4Zu!B} ѣ!XKb\ᅩ"buPh Ly;h<>\+@xj4ܝua)z p y;5(J_8%N;ȥm'Us5\7u7LbC;LF>f:? O+t.(ybEd'} MaKn5JK0Ô&[qRble^/%ؿo]|O/bbMEJ0NfaR]U}cWC< K <lpc؏& !邫fJo&W-9&Dih0qgSVR]X!M؇  9׵e*|]FyEx';q8驪u*&Mrph24N*+{ +$zXF-x^Os8̔vH]Mp>-Vz>Y,uH5’=x5HAd7\ ZpA"̖$~Cl-Us< Wi"ϝ c!OBk=X0[~H翺?T׆ hY-ʺۀe}MJ^A@\JPjKS2f~>dSᡌKsR*0E3ƠI!ꪱu.m|M^NrEMO\ m-aծ=$~ȫo XqE߰.=*9jU]v}Y{戅NffKzqy=&hki)/.}TrR_z9插 /]VazW(m?lkg%+xz~{1c[ +7GX67,O1Ͳ]}]DMF e_f^ūv*ޝ,,وhKg@o0aJ.5_D {ѥD|W%~kPY eqI`Q}j\FM *?Nҁd}wS5!0g L7Z V[j$I!Q^k_;!' TYWwc)jTlLK"kmJ],Z2&ng(\ӯ Lڻ wZ Lc9JJTmSîYд}jgˁޛToơgp5ثÑuVڹM6'/:ʛ 5N7- DB$V\V&5M̩ojOtcJc_C!!*C]bs8 !s~߮LVr,Hm%.F(QL{~߀mk{81&F- X)݂ ы3(,4#2/ 8"VQ^+N=͉%7&^"ͱ+^]rott{}OI!b[b4yIno}ǖΜdC-N%1"L/߮T'Ka w|ڌBgLtx@Oe)5 ؇P$3Hӂv__c& ఢ@BՇ,ذ :5kvýr@58aZ)"Xbyhv&+4^w Ҋ" ڕ ;@9ǝzj>-Dkq-e6ު=7jy3SnFoh57"~9װLvyl?8L5#R'c~!ΒH] },-BBiKpVD6m7 KGvU =*wmhƘ ))Ku\Mm$*AUvM ubXoϮf#izIPYqrH8X~W+ CﱦZ,]y^hY 2Մ2ռa9Z,[ܓ@yNnL>HLbR ē׳OZ3?FXp8p,n6椨 xj-RAb InnjaLk]b%.Q ~قڗ?K| K9 2 ;V|D'Zlé6cr|-l_~˃cKlNH9\J~j{R[_7vP^)Uޏ6W\{w'D_7F2K& ^ੂ`zA|sC@ԑd&rӋtEyn'H=c)0 FcP6KߙYP ̠M[rZčM2ɍBS!+$lfQ'Y vf9dqǒ39, QPaR#ѝ;0pYDrY W݄(y]@]w[}z t"9#,9̈́F9YVB:VvU3=9U/Wz=7=6l`4h=+P"Qaur2N /#ⵗHsXֳE pRBjnmt7.ӄ#HY2jgfJ >TR}:J[<[`l\ ޒ0gl9US ;e6:a(dJP̂xb5 ~/=ZsA,2_J=M4q o$tB^m1h#X KV Y[sטet) \uKāt}fӱzEs6,̴ !"'UW#R@j繠v~ Z;aYBP'pbnY%;Cӡ0NX&GfS*kw~d{Q&K'F hyvA'+77e),pN/Ql/ܜa,/H~"05-]i1sԐ1Z8}i 6kҺ$x% ?؉ )GRCbP] Y-]zIx0ì3NQq I̓d]9eTQJUCo`QV A4mX)<0L5שlaU$<"j,o`ԹӞE08Ыgn~TKv^!o7Z@9AQ;$N\#Y8O0̫RA,VbwJo^;P6O$. A?U1T2Hh)Y /`~00+@ ; w#ŋ g@\xMtHqG;&orדz&9+nKvzFAq\U,w LVX$Aie" U?jݑEc/N`d!!Rs]WxB͂JN6%cV|oɏkgf: %[[!<Қ,**h0~eϓ񒉗piqsϞOBIkvF41q9Pc w)g;D R`C>voL̥+H&~v+*C&Z5! z ofmO[8{@TaǍ ,lP Ǒ*v/;TBEy.XY>Vl7PP<$@,#;Oxb6ҫH:FJ `C3jK"j 34O2?ث_^1 2Krl137N?u+w语&U} З*u.CYI=BY1CG]XRfWYړ4l͇C-]|<;#ڨJBWl}"Ma;&G:}L5hK%l3ξE h9Sn8̢ԉyW'js$={Œn(DoC%N3BqdfB>Ox bKaC9 uTr8 03cmVEdR򦞵v΄0pP_SP)8:Լ,{_0)zC@3ƺ:͈E͸V1FX(ļQ)2bl'3Fؙ>g:'hrr@;>[X8.}fje&N,R `VkNJw׷x&+/^Xq] h, yݨ|Xtf,)Dؽ.?&/>v>T~nWClY7` 8 Mm23/MLC{8MX-o2xDM///, *x|{@/njGu6)rmr -BeE.)POG唨/7N^X߯1Rsԛt&t gQ99˜n҂0WsElB uĹ* @):ztt`;COBNE+)*vD,2(jJnCiyc8t C3c\Gztp ,熷+ڞ`1Bm{ ,gۚe% x5G-(bټ`N&zC@ L 4O 5I@lfpsk)E˨@Xd7mV}a>B'mz&--~@,Vோ)EBfTNTH_F*Rw 2z2X/6jeiS|*'b;j *( [p3:pMM>wavۣWF[ 0Bf4F0Q]& I4XRK+Gj#9K6r)WN큌rKOp@pz=q,ӘG,mT%꫄u}p+Ǖ2R?ǁ]Z1c'pDoПt\XHVP=8z m1bC?60fvgFh]0f51d}כ['3@j{s(}q6C%CH_ء0PgTw.0_n0Lҿ ,Qoi0te QIX - \$}5{aQ`1u`u|:e(tQX) 2DuMfkah2}Mҿ^~q;ix KC3j\Ll>+ދZ$%E"i IFѓVw%DPCa脾zbkŷ٠(N#-هZByrk+⮛A)?е"'XXP?RGd0zߋ'M9&̫C]rkzj| ݔp ׼n* MaqbQ3$S+3:RXd2iu.fjr# AQTACx~k] (> )G#߾%HljN0g-q~т8B}})EvPy[AQRBAs‰‘̚FDM.A2 v w(5ئx*=BI#)66d{tZS* NDр3n >vʱ`vԐmR%mv|(Zbc'Jk p]//zK'ŕpf_y'IOL<VJ/oQ-musCAI.Eb*ګJsIAxpe}p`1$j_qt30<+mjV{dSn:JߦٞXkMOs4~8GcL;Mb#'R= gW{(H !b-I_#-Ό&i"8ٷ^ SaџnZœyDb9qTzzMTG&yp@]CS8x!*"R{).+_cHүjx.(N@ *"I$Q`a[Yi*C7%563W(%*Ml|{դ|Zu8?@Cj4|D^Zq!3D(i+%X%%%/8#{f./KSC3l`pw>_4h4~.2,Q*O6"r,<=cW2J6!twg~6գ).BVrQڿf C pJ>Tt)CLe',P}!a)2*d5zxC3! 4'$AR)j];wɌa:Uf Ņ3ׇO%{tB \dH-_DqjmqKořp0R15|7jv.Nu\j'3oK/##(S@|LBvӮ}ޯ˥*u/T h /U =M!#϶pEUoZ t5lp$,pϸ|:D!l/R\C$iNs5p "+&ABe glE3![E8xXkfu][r8pHv͛5%ཹ" 钯]-XL0 H 87ZhQFsn!JzxYh +'qaгcl~ؔSvGJހ;w nTWn^)\~[a9ybFxE4b8*? )֫ur-ɞLXgnlQ挦` u|@SmKw&+Ͳ3DdZx^]Α[U/h"-ꝆôƳSx4|5fj29ܲζ63lЖo= *͈ -ߧ0@װ+L^z G`%CDlvpf\kWhN*"⩩Fͬhtx<,s1 5⿀W{QǦӗ `bpVUf&&}$è!dh&.N$ר}9<}Ic~8uÔ6sh嵎ɢzst))A:8ĶǢĠG% \a_>| tj98J؀]zu Fk &V۪'@y,PI!TOS߹i%w[ˢC>{ELiZݢk"x3]'0,hZZ JKjEax=݆K(jun/y]%L{;gQ_r~Y%\%?SO,9 ]lL*_ #3:T^;XgHsG~hqH5n#7w.?*_^;P'75suDf*7-fj|-5vڅ)Ϗg[>f_YjhSoz8BV3 NNJ:ֻ!6䝹Fm.FQD;_z%]&.v1ydZ\EpS[I]DRY]['U`w92nt7@>W&Cu˖+Z,FmC (|;?&_[G%@LNžlco`5 )褭43CYAMXdՕWygSBP6*)[P% Ay! ,?p>5-lSս=IFu@]ai5 XcqwX{ bKG-?2L,oR!z\A`[酶(+eh@)ߍx6'7EƫZǀ뒾 kJ4Bx`7{[!Fgziq^H 6]@isdƟ8AMNUqt KPս_cy F :~`s/Z2zPxG~e&3,&<:gJX ;<;t9tOa̩ݽ4x Ԉ|\WRR_?(%|ߚQ[w#\?ـ$C-A'3A /et|C^Xok~.\B&y$xąũ W@U'Z6V4ޜ{5Gu$FVbrFy|2:]/"[\v[#ZZdJP4Vc/ߋb:R)tXS|㑠!W!'ȔZDWsmmUܖGOrBv{|(0O$ C/iv8Z8LS -r/6fR fhj i]V$j8&>iֲ8ܐ:p30A8bt`q9' \A*9oJ{a3Wː?h̐nXmkgni󘹚 AQq'M {GuRIծRJ226U;wX۵ѳfU)o5>.C4Ӌi:Gx6ɠ2Gʓ2[D=/~x[}i``5]Kja+D*E0CVYȾ&r#= 4yH:tM/vm|/rP?﹊ 8a0=Wғ'1ʮ*h05JGcjT6d(!Gm'$awpeM3(Vi%];u-xTNI5˨N/!jHK?U$߮F#[?{~֩ۙa )'V58WH8ۢ,FLG2|8\ȩaѩ~H]|mKU .}H?=Iܽ_1Sݤ[ G}m="5Uy xz|p4 +T:c@Ӂ+fy@hH31{H AM`'UO*Աfbޗ܌ݚS_  2[5Ga4dϋ6kPV3E! >hOMWYIi،Hg1, [l}M-Cs@b2]e5Gk Q쵰wJWS.s0LYC] ʎ??)ԴW0|7(~ ޳ӓvQ[Ǎj AILL;5Y{e{͡G3j2#*(זkE8=4<$6u{z_BbAaSg:j-<Z6Υi &ͳA ;T:JE>B CZF0uC0<(=YFw;$őW8 pm hdP-Gi SX}K N3M ְ%= N+"# 3k#iX]R keA_>PEN:s+[tb~^C`i4&2*fcYCI5j>{M5F[5̢| !qV;Vhi3iR K ;A&cU-ř="Y+않&V}[~Th+̺J"X~|]vOu>[8.#r#2_@W.%!eʱk00s*.8pcHXłyƨk6qP>@k,vwrI^c_I#OxĘjy,fGrx}(_wvse^lIDX G_ C݀Cyv.'3j˚UL'̡z0R$^3'"ΈN=-w*jOR[9Ç㟘uwg.<CGkΣi+}aWH_jy,H+O qJzu?bJʧtW/ ^.%QOl[{ rC{4w\r?xMVm)oSM"zE ܡ`:5oL%z:!|:qԗ2r}o"b{(hUpFH+"TL04*:j;黾-cVY3Z?+! p<}V ӑ7@+J=CuD$zq:S1#)L4eT|1zj (޲OS9yJ$$ޟanm _גy*k*!yL@Z|%:¦T}uC(si&PzLC3hB^̽ S?FB2,N{Aa[Ϋ'706,}KwtċXS60!@gCA_.Qx~V>ނ* 0_ߩ7n *"yꗕ@z߶Xͤ3x]Ga]~` i>s9JE8N<ߘNFC ^/Nպ044Cul"\*j>Qhπ>ӮP%E:qfmꊚue9Ytd21 4v 9iHJqŵ:BrQ Ot%_-/Q~ٚ䓎>J:FdNՔ^듲 %9s#ݯ(\u¹xTIWvsh Eu[J%5lfbAM͒Ǖ (B/6+UA50еvOIQJ?v"f!݄S7 p&V!xR Ŀ?+ݭR1)΀?X'RvSH Lef&fAӈpls'x?tzn|!.7 z4U2b|PwRhl@S'R+wLoݥqZDߘITacUw !wC@TYĊz-esiC mۢ?TXkbT_G؄ɸZ{KalceϷIynymnyMȮߩ}2Ut-\/8u-`+Ce /[xzY&Ԧ;A.O9p38@Cp ҄)_WRcikizY@s~ 019`6i2{ۧ):J=,aꨁfС[B)o=8㋕1vĖR 9r!} LycyhJC[‘DPGyTR 7|> C2?bSS]/D-Kk`>޲_fQQq*%UٳXL܌+y; ύBk&]|ۺ1d08QƏF ;\(u(V)3g2mWdJe([Tk(Hx~Ut (/{PC&$~c:e:~CfC4P`M9sdc( w`?rG K5SO0lq0*Wi=<){5IݓݨYCGg `O{귒7@W><[FQ[б黡Uu`D羖f?Wx_ΰJ%smDQ שU7Y783a]8X-:VL/o8V[t? R0$.Lؘ࣋E?1uQ踦! ZeWu:+- "nV& nig6òIo@bA¥F}1׸$V=o$|Sz+] 1V qqG8Q:>wbI| }qh]!/caL]@&,h< ndܐLV|Q·[N%sR+Wo~6 Wġ9)ك'xKİ鏼Vmnf\w;`m >i6C(+HS۵4 @_ ()B#Pp2MNmwg1ʼnY<$<mxQ6oG #)|a~# J:jA8=y#Yi_DWG+ߨAM$a~^RKt#$!=6txiRUgM5҄+Yk0cmRV?/g87k(DaGuU[ {͝gTg1Pe7 1}9A <`(l $ԏ=>i&(Tʦ5ԚOe"cmT̚ip&tk ;et[G Qt>2OO)}eWmhJؖ"V&O|^pc^K"RN_Hjg!S׀Jمf4RLn㠫҉ي)J*FKK3NG[|F玵UF~3Nm0;ʣ&1&ϫ6/,)Aמ[Z'Z8(AUIE6dCmQikh!"h VGA3Qr.~o{T&jeg#xʩO0u{㊘8 # ):v]>\b2!1%niT1بi>cIQT:@Y<`U4]%rc\Sud'#<\6Z U3+k@{l^%R8*myIovBA/{|b a.u d.Jn0z-wעCKsR)fz4:U;ܛF 3 :(KFo^w/fS|)؋Zpaɠ'؛#m٪s"= :?itvNW\LBC|-"}F@D}pzˉcupE_?BD^ЮOSX* DEHk!rkj99e+4/27=@OĠ샟 2h>M.K4h"n4VOyF n{Bz"8^. ӼM ڸ 7iRK`eS>P ItHxP0,\X4ki;tXÎn税o@*+Xƀ?;lIzW?+ v5ăɂ0Gi4T)+2g][}w]'8ocidj-:/B0ddr4!.so_}y*c}p-HHN_X b\rd00z,gvYSPY@H?l>A%灱{ƈD mSUK7 xVN5{~{h/ ۨpAMY8^{_ J욙TYڸnc0%}$U¬n"%KmU{iX5Nw>;X6y#>uٻw 8 Ɇ x9%z'4eqM5׭ %$$$f 0IC1&v>⯿NKeqXD٬c:V"ct$<eA,jb0O=f9%)' 'VO/k&B2{ibhVAvyEK2Aզ!nS$ !FT.S\D#|4BL]Cd_*w$Pnd4:^RT lt>~lo_S/K޶<}<cUsG/!ËCdCyegG ZfEti+$Љj r.j`x# Q;B6>'YZ/&2"Q ՞_z!X;$oGZc%zWewSznx1YkH"8Npp#fvc2 1k htT8/ڷ^^bKc/%晭W/ Qm&8DʜpPj,ԂirajW85qd=Ͽ޼4 rDGe)h]{c6hY~ PJ0 \4 6G``Du~?Պ]0'/S47"3p68 pیLWIde5י#rI8{V}QS}7e6gu^vb92B%w`DgƊT(#ָ,"v:i \Eh(.q;ُṁ6X/3XT*UJXh¶Ȟ Ȅ5)Jz#RZ)ܿ?B7}y.nT4\ô9E/]g,X(F m"Ը5őw0=ZIAybdeƧ}TF{YCnʲu9z6_cE'3L#(J>AOE8P)hŠ8cpb N < vK~Of4}HFLTk.)齒+Tl_KWk~, @oza3M1;z{k]G8ה{,G$&Pf; }Czh+/]"x?מ٨ ojò.Hj?1#Mo23t_K-xdt(̅x+*1n]ylRjtڐӜ̀t/l~B]y\v;'i7 e_*n)zj@3k$>Ӌ^VYy B*Q$s|П8.ksP|4#J'0KٴT,q]FMsل7:My~3Ȩ3ץ}rGW.%:GG0&5~9UU\Qn^HJϊH9SJ:вs|lSIS>*kgu+E耒 |*9m)Q,С֗ Җq|buLa3v*6^s(CO=6"44i%Ui\(\M |*U-osIm} ԕQM⎮Ss9լ=V<NNFH%Te?\U+kI &i!_dk^U!lcwZ svPCt)sCʩ 72>fSxrJ5[|Ou)Ra .<r_``[D5&URQ_PY E)azܞY@fVJe> /.핳rVzDl=4>ӱJS*f(^)K"f^ ed5+Z|/ͽP1Z$]pY٪)Pu;Fuؓ \͡(~_O.J-)AD 0eMߢh^ln{{:87Op![YC< T=Q j$tǢą57azc}\J9AR Hæ@R跰cia9Xڍ%K׼ ^K$c+\GNs,O{"5OZHN}^}ς3B-)I:>`IPFg.{L,جѼv YWU*h!ȿbBwD6圂=ϳR_ko`$vXlm1G컋8\tbj@Aݦf$e^ Tp#AH==;Ge}0\D[*yZĠ[/ ML })SYq`]!*f_!voP65nWPAwJ,M< ~"TE8 _ ! )a]вGLOݾE_H8 b[_u m /Jؾ;Qt2c7ZnsI1XL=ur12xh(<XqHD %|'L †'a +vA\w%X*XpjMtB^H@ ^]mR}1ϋH=ݝ(0.[Zatw- q]s|{XzIt-xr3&ۭv |h帐 <'bM((\~L`o#bM%a`DL;|xzl646>?ҴO&^\ 8/L3s=-(TA>BW"3J])8CR*覩cL5Ō Ah}3R5}O3b56,| 5lԄ6HaHϘEsJܠ8pZoa^HU UTe#šFK@=,g{nح4m^k8 p"8 4h &h΂J@w&KD oӐq^[w\Q؛UJ$9 AON*D*5}+(Sen' ᗈ[&ܨk^*&vI'_Ɏ;Csere+R 4ND4]v|sD)>ý,jԺQi 8?#z b|LSxY{!zXy{Cb~y;UC@hjb?;0cTF}o ARD*fb27!CY} 3нB6 VֈJ<\AI+D&N}@ō>])x7-.xx;3 ]@~EH #A1(N9Q-avn X@ Ow85f"FҚȴdixW22 #CZ]yQ{g;5`B6C 0\8yiFPсg|exן^-y{40}pSKo^#Mx^Vj@26#@|Vł Q쀨E PBTE# (& `xE-aJc:h hٿJ))EZwuMJԔN|aSC®lߘ6Z0=Pm1jkJc؏*g 0ZnU8@p `*ݚ d)ؒ}ӿEXb3Ѝi@I܁ʉI[s8,Xnu|:eLWLCjpY=I- BpL0iWq(aai|TqFW-P[%k6Js32U6]KR7 ԍ6H s TjӗJ?N:-xirӮ^{ 2tu$exZ|{B4]XEv)GA`[ v%`{ZLn ClEO$pF߀`sŮnkfMR] z%sQ$x^4tBNU6‹xT"2J;hq*M S$]J VlZ[nL c=Tz! ^!G@pTs x/=M#ez,_M3 p:5_mP~L4c"`/L{j]Aq1b1/LǾXbI-m&.>P?⸾2l1JJHBWSa2/ VWtT*bq9X]B=rn V]Jd[%ӛӭo,*qt+; "LHtJ])գ:'76ff&Gn$w ˀ !_LtA$z|oL2yRU;<@xxQ Eerk5VF_p-H 0~7iʎ 6x—Kk\vcg"_':gkz/mWnVӔƾּMQhtd 1~VL2 c&ic> qKiWLX vj&Ap 44݇eK>{{B9ñ-k?TDqޑ@X;=-QKp}RSIC`SY؂ww}Jʈl =azږ+DZ%(NI )|O@ŒƳ-a""4t)>|iCo--dqc3h X^. ԙzF~pR={5K4!@ X3WsRL_oއ6#VrWCfCzcuC[{pwI2K(5# r['g4,L0~EP~M!~ `EG!W9)f;j햨x ߕpL8d(oVGVl@yQk3GiTy32#,ARA8<htww `S #ߪcؾOx )XւрS+>L@iVp(آQZ]>XGFN,lnB#<[nvg}Va"#v@׷Ia1ZMw&>.iM-),n=b@JzjuM T{!ޒZc#LVA)mvDۖ4DB؍ ȸ1pedrM&[02NwKYLAQc@7ޙ`]54Ԯ{ǧ'1zb̶`hS܇pL&tDKRjJx?XKmY6u&]oU? s} nt=,Gi*[\Q %l]XުEQ檚t9|Dc.JKw>iӶD]C#1lVB(9/:Ii00Ucf&b>NXe+E)ciq6 td},?˜R$m߭#NWu`pn7k; 0-u^Z$7~/P&i< <-kr4;-f[U 5kbIЂ淁"UD&Zg5ctX5:RمUqD63xbEN[i)+SŎ#\K\R˹Z7JJO(ʡ\k*Ji5>hyH6bhrh$,#'o'ƣ-5; #Mzgw w$0bZ3뱏oAf?a‡̍iV]^2V: ebg3E='Y"b5xO#@poݾoĢᙑG At T-8bƟ ꇪG]^ FtȗRZUUNaD8lk{F+$X3+;ujTCp_re st=΃톗,K(Ԝ퇣1!F{%5e|XYbKp奃0^GM9ϼi `8JތX?nV ڲy^Bzc5WoOA{XD=,gpy(_2F B Ƃ8c7{f? ^ W̅ ~hYD!qJ^;j:'3jO\O͂(gj}SAoL%AqM8ա EZgYq\ZǨ˪۞P:L*)9*R6PF}.M4cQCƽģѥ" PVޕ~$k-D6(L#}ap|<#U-8DT/F2Vhŕ=?:f򶟴h(NI) A\U^Why$ `M|KcwXi-r!WFkUwUf9:VWŸq4ZN]u]YL cnt #), r;^r"4{SK AU=L/o.⚢gtЊ"],\r-aOQ唁 V] E5R>K"4nSO6̘i,b@ܣV3l߱U3c֐G&'3 ZFֳ=! diVtUDZ|ĸTR% ;X"yEuK>>>Vq#.Y}RW.ʅa,chvuc%?ѯ%TYg]>~lUӔOqm|{;ksqn6D{=۟?<IDGN[˟{HK|~)^K'vףg >Xϟ.Hw|ǹ, enCdRY]y55ɭN,^s*qO ZyxƒR u`ԧQi8g-ZrRպrlad n/yK eJp-Ljpb6볿>V!>}7 Dɬβ)AcY۞M$$F>|(f" ESzݝ_7'չ~tCW ?L|f"Er$(Ǚb<4tWrg%-yz )̌0`^ HI'1*?myE@(YaG > nɢ"\Q\bxD:Gm>` Z?6q?npcd8ucdgq!!* :9KK)_*!i.Exl5{ KSg'U;@PTa5+Akh}1~0aY_|!jò#Oݝl`->ߧiqw&9܃m$[x>|f>0} ߍG<"8a4S(l`$2'jjALqcFYRiѹFA KKG?N(Nt(\ÊC\w;Wz) 6d5^8t3e93}쩌 <fQ$Vk)ϦGӍfn_beV}t[uyW{sRNC[cWGש/F|N "\s`ULBv'5v5>LjnGtj$E셰@a\W5NAU'Fd#(}3zCx]UT "IbBKo{Q$Dfoarb0M WAGP3M֥KmY֘)vke܋ĀNqKA~f 䅿&1) Q um|2`,s=(lzFw&:}N+؉ߘ _-3¡5v[.@4 u#}ހA'碻4EW&@lyM~+]\K P|H tSqb GH4[",=k>"_&C()qrCShLÛѪ?cWL| r{wQFؓos #c['4#0N ftnfK6Mp|SeVSf_1= XR:B%" ιCٸ@M{D"KQx`G(6CT?(G$Is#/G%T_XK /xg\jgN@xUj# \,בXUQy- [a!~l*SHsl :yk#a f'wC ~q}O>*@|Vi]@{kІ55AT"2i]‚E:A|RWmq_k/N!nvhOrt$:,#rE< Ԡ͎mK&Qe_h~l!6w>mm:qLJ7~k@@، ZYT)$EJEm@Ņ z`I%. bhv9 npw`}+c߃}j4+_#'\y1J͎q,2KRWj,\).к_XȦ4[6 %Xj3%(blL3ҍ(c&ѱUC:/]Cq.抮mS=WP_A&8tSxa6ٟ  U5st<}_7X뼊ߣUY1XKU r'6ٯBٱ5BUӏ9K R-ּ|j |'X5v WAx'_M!`z[ƹS%A%S%c4 Oi,4V;Xɾ$>zC =𲁝\{@Yx\s~lNSK&E>Z* |@Hg`ϣO`xknx6~7"D O !rV鐺NWAqGI!%IU{uxV2K[j]K[1,~q \:OH"qqZ-eXt<O Njc7vt0ONAEz&ɽ[=%苔Rm^Q Bhi?_$iVM9mE ::)#Vw6; Zo 6e[dωe!qnƿuB8P6B\Ǐʻ)El7ӄ3mP ߣ_0lq `h뚱HPQ$\b$#j̎z 9J7:PԖ̦pO{fR^0y}>O|Z7UO_|/˔R d~nz9&OcaON|x?޿v!nM|6I@ތ, O((?w(J!φ@$gl%ht0Wrdx[n&FˢZj3hS I.e9TD͢Xx+r>EX<3[^#:>ߪ/4L)(0Od)QKO9̊YxkD5z ^Ds&f,vxw+Z䛈k7{mGnNr$a}<Oms`_eNB5 L!  ikEQ{OS ʗngE; 2!i5tH-x(2+;: VՁJȨf/G!YDZu5KBT凪ȿKxm^뫳&uY0t,tߐs =mXB<`*EAНdop?QKۿ^ }1[Ahd8#ڕa+ q"!ia܋ %Hv35z/ȘY0 $sbSiC<=e[P*ޅv2Zr [kv*@bˢ}PBn]#0#?-XK1>p>?-FLۙ,Bj@i <mH2g%l"_fOw:}u#Xx{,OB=~Z;yu]!U M_oMh]v;Zzu%q'+5ZԢ"r z q/n'x%>2^~|j12~+f"Th1&ұ0'sg#|r5{[7`д{f_"*kV@s^$wM;Y TYvY^ShRJS "_-="fh¾ۧa @bz|vXŅH_Qݗ!kZ 6oRg['%ݮF2R;=({]TNi[uWZ61nf;;Rh"TXQ@&TfJ|ItV6[xa[i8K'|܂{VB= w6,rݡ5iv 5Eޔ+v2ĸk V`MߡJrI E|oXǹ fA5+?2`N{!gbH:2(<@ ʛn=G0y244A&lEWoj`G.Vj|ؿ|ߙJ=u .[s|1]׿F".G!־3bV&>R^odl][Q-\C@Q0A'uR{b*ɜ+k]s^E&Knb,[5t;f_I%2Hob$fKM+P=p~1 6k} xTt}_ͼ ^#j٨0c8UդW{piq!6  ;_ZeWgUn:D_"Mfl.]:Blr:!6jfs *zbDFkUunTlh@W;e(†/NܦU5ª4kU-UGd' =&ϬD$gh۪e6zH G(ĈZ+1 cF^QU rf+;|z +_Zߡ>S.DGET瘟G.6Zfw j=_*.6zN7 (lywjDo/vV_۽'tq?`=hk&N ͟)io+!f!g!(?trcpP~Is|%DD{.8To&\,NCĄ#L9e6^ JgT ftJG g !8 &z2lRZTBzd` ` ݺ|Ӯ7%w>\:m,k0}%QD Z&|"p wFO wg`8ϻްD6[̌*{ K`~eyLM+ '_ͯ`ж*W%c * J`8u>sD9Pybl;3ze4`.%"։{A)&)bC!BNɡڱ}?1ADАGN9g6-o2ak:v[=Q?Uzgv~V$^tM6u{1@lJ7qYB!XIpAB&SzK?;2# ]9\;VaU(t3, & ڮ,1L/#9a_EVL^ImeLzd)*Nf1Ѥ ya*0`+]M%fS\]׭_6H i :/hcq~ZZm(JVɳ<Ĭ(10_Ѝـ4+\\s 4yl;|d~ L4>W,QI 7N>ET&?, 'MU3.ޓ,0vq_ќ'"}pd 0.6܋cK[ROsFȷ9{C?EHV{*yuX)Ry\UmA-^y7\VzHHbvb4$/AQYٲ x ^Xҋ~ qKU+YM [q ;3hRdxW}(v<;ѕ-yU~N i\e&}Hw +1ըSsqzgp$55WG-a:g7 ZūgkA,=FҠ;4@6xΖ5oC#TE`>!PӒօ*NGXMKcG0ݚAeo9bEIص;M]ŞufIp\;N}j{*u [kLMYҜwNl֦&AA Ae1l7`F)u5jD#0#|/dIIsE|a N 2uۮzTmNaBEk#%g66b|©9ANzUBuz+}g1U_|f/ OS' nvr/3׶/ bOyW'b۲2hٗT$赼1,?j7B/F~ DR0DUb>ZBjr%\%# jḰ&BJWWs/ ; \Bhubvp=^|Z]~g*ДL,0`a2(ny#$-_`aWFXZ3a5;Q{yޟ: pp!Ĭ%bF ?Vt[Z#1KR3*^ BձBLdcˠR-;)'tD)cޫ[:0髐9,"޶;O>` PˣaAW;~?ЭXWш˟HPDzL{€hS8FuX>z]N Z5UHիӚ%#ꏘ(s[;npgQyRV1B!-%x,77)1HiYR|L?5w`PtD8[A{ض'70=ƃ|.$J ;'=K7YWVSq"4 l%;,쟀˃TBMN.0g_gpwRQ AJxb'zN f 1֭-!fUt qvVMDe?m09B5#'xQ>dm6FJx)N`x wgJ5>ޤ|H$ map9T6:}~R7qϴGVm\"A1q V<$e3PuT]أa&+ e|MzW9K w^zn?k+e.umȿ{4p>NƇW%hm*0wϩp%^P}F+(6#%@.ݳ۔8K +MB i2Z*aK`Dk/IA;IGҤ qIVnm@,./p*@&7]fU:b=glUct؍Z^5N]P:)Ν0=Lzw"j IiBw_U&G=(,˕r5(MOv AnbYP#!Bg{ B5C&͍RWjdt=ɄGj%n`O?ΔL6%n&MJO(v* p$µopن0yx%:%g<OK m)LJA9ZMáa@&LnEگT?|LO47U`0:kuёpMQ|Ocd]um|BUe|eJjP/l #aGbHijp_ H]_/jG!%ԉ9@oMZ8+O.@@N}1OFH%<05 ˲L0τdt;Raq +YpGKXo"*sv3׷r&NURЏ&o<fh{G0EqK7M5B3KV ;y/tlj Cۅ,m7 2vh7hj,O ̯y=׀q\ j`'ŕ+^$a@ Ĕq c>lj/D3{{x,YxBUs}eS7U,UOƺ04b?Z>`YLHGjѩS{Bq`A18%~ +ɬ. jM̃d~ʤtb6nt}/: EvӅ=.;O\W= V 'Yt$6C[{2@XZ/^xS nTQuJ}2͌c >qz7eĹ`q#=J)Sٸt(kWMCbK7yDzVvEx;*o5ߗ `f׬Rǎ?=eZOy&n@%ēXP1q@/x$YFBU&s)3ydx(wj8:>-[@$Vlr߀jEr'43x%y1a3$1&FnE_D_0Wċ 9_R*Ud})3(߇ Sq$a35d()!Q̳Vgڝb׍ԥ'z4 [h܁7%uJiR%bڶBlY{WqZ"O?˵=a ıIڅ|UuDz'5Š|B-KU!&Wt$Eq]>tjswh&(v(9#cPZ,NMp'lqh5SsCWAqQƕLJ pO,$TNu2lܞ\8qBw'`f#~_)0NQ NۈW5"Uabr XD/ & 5F\UF .'t OňMGIGM+9ڎ)%vI:kz|Cy[cMXA%xĚ vY?k#5@ kq,ΣMsMJq(x }u-0/Dvb[w}YW; 3=Z٩<@Z* /W& 3WY!;&CA[V Gۘu^+vg*$9Ή0La:.w=[z ӓRSySFBq!.;X@,r:h͗Mbk[e::Gn A/ffJ䚼fKPD{ڰ tUؽwY:څnhAeF?M^e-dAT;W ?Ir`f娨6Uu+G꒢< PϓC %zJLj9c(5Qg ~; HfsΨU+C]gM"oK!묰aM}64U lc%kk'Z=ڟnxFC&-;>tmR-?n7gwhpݼ~ x{%֋ -,f G]'N3 q<ė}5~%ҕ2|qNzl=*Eu#P}zlR/6wnQqwjq.2Kb&yΎ'?'KCK:e9eU)Vz[4uLި6LHt6H@FΟmcSo32ؚ[:k]ee : Vgrg ;+xі|?p# J?k]__Za FP:â&N띐ΠBhEgt M҃iL#%BHYģ 1g!"R'a̩1>9";s H_?V hW[ vmw_-? }NG8IZݵP76q5 $\F (+8a"h?qQ? QK&3]4.B|89f8 ErCذi=K\.mѻ-6-M{AEiI.@Ybc G}u\+,񕁼ZOd xN԰HF'ҥ)r6S2Nu;oٙ/S*F|pٳ[OAQwF<\!(c}喺z&ʂ]>>b'`KHMzĴ2yi2Dו-Q9٢=8]gGi5N8_I_.w7cYL?yx/1efqw14I5𳪪45POFʔ}Cʾqy mzؔ2[xJI)tү]|E U^-Wj&r3JWx;'HlM<]&Pzl}.nOOV%3 :u)a|ZlFOz1 A!²auN|21^'"n1{BC 5Φo{y2Tx9kn H)k\ K) /J*F_pݥfp9[{mo#  =P̈́>ʄ8G<ψ^/PApKjnB3sh({*`v;83q.[yk(/w0Ngb>ey[؉/:3 ;k=!ϳXmL,:,NSjBr#,8ks_쬂-8zf-Yb+[>q|&5BVb'LiA/Tq^xRvUk?lK\vOkߥi'8pz{/1eID!)3VE |4oW`;!?7@R-R $ĞBM`؏|&İ+ZA1[gX^odr.D4bVgG[nJr;\rek5ftLk"x31Af4'|3܏SA+DN"l<Ux?嘓I=tYNĺhe]KʏHlf `%605(@dVؘw06yc;8]c)}$5ڭE?`[F0HXtREq$-r >ȡ9ta oՅKQ)%tTYA95HDzee/$x{ I,AE7t^D3+?Dr&/4ТN}#0f8%WH3(Sȓ"%PYT2GՋ=ԎPpYta]UĘqwekB9ᑄqq$'N?sZDg,}^ <fVzoQkB?e++#nǘHP>k$h3Q[ZxascEqX(`06nx\%hZxK|%DŽ$*`~ 40;0Й\k* `4sd!u(?W18pB옎['e; e/xKkiA<P \-@ؔފө uF*N)&xi+ zYI׾`)+t3RDSc@133@cf.\lY-d[H }R: B "Ier rx"\Fr5SR"MNLገfڅr.G-xbϓVVq/u)X* wp?|{bxD~^ӧbs6F̍&h,Na,W#xvnQCqV7LfK(n GqnEkS;ndt$>FcXr--mWi&-Y\6@ZT7݄X,s<\C-XzX34XifnN09a$UQ+Oхjғs bW{&S S3hwgvzp'ux:, 뫛3?_ˣ\KgXf5ӭ? 0W+͙M)=}rqI\<5Jm7DGHBd`<&|`LlL#ֹVLչ8ubhC]3)~bY4xUD01I\Iu0j=Z]\ΈԄISI4xwӺ033ZUR% {45aej ~UL? 2M3Gxz,Vʂ`?M6L"E{b E&`+F H!E&UwF&`viqK|ֱ)%kAxQ0@ЛD ]Ta]4ErN+ݬ @p-pr {=i4 HYxK]ї/]|@Nj4D%{%:nS!5X/201Xн#m߃dAnEd"wW6'i}BckmK4 b~iB`WwmIřTA҉!@PR+ Mw@~WiaSvwU'c!C]Jq) 5¨@y)bK5̉ 5#z(ǢBc)rgHiO`UoxSmWp[aѯSl8s$v$pf=Q{+a'F |"Oub(])$Q 2 kO90ۉߙq݈`Veħ7:&LeI,PtS\Y_a:PQ F`nqQ%^] Dyڟlu9Zw`g3LjP@ߤ!􎢝KK`|*7c~Yvmɠ;æ gKhSD Vs[T#ζ\ -cB|%2tu %_l}o?"+|\mTqa`ï'O?Gu\u!mIKNXR?)Ck;ZI&qSzuoS[E\3H1脶lN#7oUR0s7^&ZT{QUIqrcn;Gc@4ȟ otRh"_ +3}ߣvBxLN *k )Wz6X@`@3ˌٵJzҿhn Ŗ/nE2U:ޤr-?RB$ldfֻUM%4 -+|ND3bzLF/fJ}\)FēLGۃ ރ}QX""kg?bUB~ʘqňpWZ}; q^8R-ĭi8 4[#T~ .J {rE٠sxGhQ <'gHn[T|):@#F<}K,..Q芠3lW*FƒJ}Lwz\Yԇ9xʵG&t` qj8,.x.>k>.<=I}Ϧ%VҖXr&LZtdi|>qwhz<}H,^>/J$)@9ˀz|5}L3Cyn`GOufzsEəi AGПR]) kxvdj:Wo&=?ߙ5|8#=C,Yb2?m5ّji)W"BV?%!#jd3>1`(jż Jrz4[ͪcZT̍Ъxv#=@Ve:6 ;"8υ نI׫tbU_8qEaq2v.}YJrbGNY~{ e/h)L`۷xK;>0RДk%a٠1&4`*[(`LŰh-dL>F7(<%Z^0buKjcMvBH9/2= (cP0n=E3,M*LzdԵw  H VL;rjE, Ql߳F>Tkshӏ<8P#o>a [kie"9\{v-VI_wLǾm@dۢ;߾ao!stO=/m)_^_].!:0]Lj`L{s[[;\oʚBH\ԇuh^yR2,|e(hyC.qaISAX ^Bݖy/L>INFzf<@?R^ZܑQt[Bobz 7/ό#RZSO@( 9DuC7ayj z WK뙺|Xud5oW!c(%94Y>=m Ak#67OlYYׄJ&k`WX5pht :魬Bq1Q8)wAAIvXL}P0c ΓϭDn^>A_ICu =Ҕi?PoZCEwbGW" O^TP][ϵNt*33!6PL"w9 ,=l*%~žV]̄")So}~IG =O%azAa+9jW]Qx B;J6zWśTv/LTgo\p(Q?;o49j6z;PY%+Ax l`XoZՖ<36KqJ IܳMbq(9 &1uK%+> D0e.37O{c -B"ũ,weWU~!UќWfGQ0A/lա1eWU[Pڒ`i󬵩ļqqݠ96R33;vQ˾''_U&+S%&I5K s\ZДH=( WXm:{|qR왐Ed̞Ow܃]˷SZ Ҷ7-㰸pyG2ZOp8Pۀ`ģHPXZf< 7L l^+f+Xڷ9ȹET$ 3RvGt;ϝcvW@ѝ8Kz,\H͝. U響҆zT8Eʍi'킎6 L.'Ͼrvz_A{ VgMx<3J/Q+9u]D9#m()фKH<ѷRYSSu S7 5p[L':"Cd(擤KXn.0}TQ㊴>Q3Z^f7mNKkSbchhe#"e^O&=IqN̳q?0WY Y,Hhq 儼2}2p0xZ Pl7٬c)㿊+0C:t =Q։WMyq]mx{uvJ<֨9{&CnKN0!ٶ#?.9;GY t]+7:>(el0 d&Qr,9L&u7ɵou]4KU]/LXxM(:ZcXUzŦlF#\IJ!f .JX׸Kcr\=K #ego|\?pUײ&B&Gr}]"X['4F3j,#u͢NPOL @ȼT\˰`ɬ,V'b*c‡y:$ ^v8qKu5p4A\WxQNoa/*N$׭ƫRe .T $V_ L.viyJj kNJ\/ve^Kp$ g$Z!Map)):pPtw2&k0Q恶 !b7~CD:(#+BR3Em2V1uDGя?}}k9-!lKS(A-sZ3m=Խ^{_=?|>݇~'B+8Iar/SNhuqO=tr22 :s!GhSp6âzNE03ڥahM!q!v_w@L;M44w傶 e|@SY|^ʼ*C)M賦z֞GĹ73Bec32 ̴+b?B7?;9e\ݿ]>|.-v`%j~aHaS0fWoVyv8 } 9Ұ'硶Ij^,i}xJ|F<"dž!G=:}(1;ctFAYbn@[\Yb|<~Ĝ!t@g&zk2yS:6kKU IS䧒MԱ( N~nMSjA+EWvVd1ͿY=V,Di $JKSSl}-0|@ȻMK9-f^Z!fc|ؒg[kp%P[gGs5,i5x܀Ql1*=VOqdQ˴C$WvWudvkPz[)] &Nny/#hP/PA6P8Cr9+Swʼk 7 o̰ݗ:̱H)v[7gq:UjzjxfXZ1MWg5_ҞI<EmI$U)8) r3ی};7ƎC"o{'Dѭ'q79 !NoՊ,_sftY)Ki}/p}wŭCB(Q88uJ+1;f3H\:πe;M_! Qe'!M÷0l٪v KFsF #T  #mסg(G={ {lŧJ0 PĥxZƎCG= & E*\b^}6yOo'&;\2V=޼8vU/lփঔOK`6 ԅoYz7 [ \[ivWfz/ewk)`TlW|?ǥNB~11!ߑn!gN)#Kgtg~/ 6/Yp̐pܷވ yDZ^1sɝ@[ew(ods'e7 i?bX4pY;M o'!ոLmML+nuC8)<w6v u-R0NѨg+R`S.NG6Y'%7_Y%.⺢z `I;f8-#t $ƥ&+}+ u`p%ư2}3 BmTZ'$WiĿ!W>3SFzIG_aa0!3d'V ;Ko')Y:i2y.a.OJ.+ۄEVUX6H>apU!o ɐƬ\%)}+{KH_{[-ƫV+Zb4VB;d;aYMG3{fZh dF s%ac' 7,kh6DQUss5QƐryRlj&anv>t/F0V &OVeĈiurь3& 'w:<ӣ5_7@Oh`mh&8Ola(%<)ɇrY*?$35YOwsFu+Zj\aObF4wgRԻߨVgU@/9}R$v=6k>/Wib(NVpNۋ^ЀU`G> ֑oC;{ IW{>!| h0g  E}!UU)3h ;%]3w0*nAVx2kVvvF0+mRcdJZ4j)d7ӧiu&ڮ x6}cz6&L&(Cpb&Kٯqe2M+8wj_bEtѿ xӭe茌I 9lGXMc \[b&Ae0||K2JxZ`/HQp,/ov_(gZ Rʼnćl/+8p b̺ڎ'[6jmd}Ni6N`b+1> ̃P]a4\uL$p;݉)X.;}Ќ[((j6`(Tˢ.òɰAeVt@ adVQg0 Ej]=n}- Cdz5;>,nCSv:~,)(#\BG 5`0Lp|q3U|G4 S>c*ʭdĽ϶W?GlǺD1_!-1ho^y, [4^BdY_ J@)\><07kTLz^F(H7w5% r^G^QfǾWܥXGs E?f*ZZےwJuGy+%̫*թIP! 8c& D7-/Jw4mҐƓm]܋i̽ZnFKœ0ͫ9j <:L%Vm0)ʞCRBL4q0Xj"" &UZX~>Ug㖑v>j{/ۇ~襟';8\$LU;lwͳpw<*$ LTLDD #)s.j^:z3UU&|}s5"`d]&ǽA`}[g־G_=a֫JT?mj񫭶R=/%!pZ,vL,=dܑy.7`Lm2Aqz7'i*K? 0ŔM_3s%tg;&\{hK$iLI쪐kw(BYtL",u{!f;r,"\&ǝ$R5 Xq Vths8YV</!J.aR9-̼ĽUb]B$\DQ ['! [ͻ9$&]`ˇp %,k\ [u\P½!s0ІV}3L|?RAluN;pOD D yzõ+ӺBg\lz[@ L'9YkCAvm}`TZk"4n& tMگjAݻt^Kh#U]k_N_F?XTEfa=H$5U))ca%(ǝZ~0,6z3w6D"E7B^o.\_RǑBl5EikTD7ܭi%^.Na Vܪ=6}yiLJ|3\o0dzVSdh NDq\WVQ>i 驅$Į}]8+|\w$[Q˰3@H}d]OLPc&)3jZi~nJ^3E :N#P*as_mS9}ϰ;@Tþj~#_Pdnؔ^P[J(gM,]R4N ?}TlkɊǡuN}<ٳj<&w1!/1gwVζ]:cj9\lq+ e6j0iAVޜBJ%zKa&kѕ Ehz#b픖~\Fh&տ#:AbSk-%HsnH9223Pd5܌-*yUG͹/YBo@}KJtV][11VL1w[ ͟q`qkS}2sW8Dϒa{x `M9;8-h\.€-{1}Kwn6cVzz)?QT\&m{%djner!d".Pd-*Gdw|p[i=zF0|I+L1orT#BQ_cK9]YStwkaNDs,'[Х4<(\}e0Gj&H em$[O'PI<76ߘ.k8[h.*Ƞ錆9 }|bcO-DܺiL|KٗjHM&c@?ze?jdcAYڋǧ8\/P))+B^XSC\=CRX\wlBH@UԍIi.G^ԍF4`Ф2h0|[.gr7=eK e`vBzq`#Ÿ4;"l֌C-3 KGz@<#ү)tP *mO6mlC z)_#Dy*#%6`& dz&rW5HtC Ò`R4qUjcܽ3zY&^[QDb+8v/FL4k_7M<z NbAR3ΥY#nQ‹9hK7<Ƶl7tGi{ Z@ ^ P ͦ_|A0l1opXw%P\Qv.*V> . d (0F:@9J?o&qBد!l>d=smy>(y JՂ)k!RJfHEz13Om]<"9!b/*"G3FN[,z);(/?j?lǨ ׄ6*ڥįӪ`iA2u6 |Nx 9@5[grq>t,D%Y➤csX]y̅cMgjƕ-s#SH)Rl؂_wƋu.enu-l9nV|ɓBBY Ȃry8Jcs8jidyoEg[<]4Eq].zठ+K^ۿTrK o)-/]i5dtbOGC\]RvUH,-k s=Iw&zlQ1&dL]›o_9x*,>c,u4"Y;W}QMB@DƩIղybc3h]stzl%Y2!H-MYK3 ,El>LKJԤ1>7$ _͋r"3PqA޽jO#f˸=q$f [Jp?p{>~E-n\!WB (~^Y甆rj7Z $+5M(1 ?κM׎zI-`\/?2֭syTHΨCe@aBt-?2qӀjBa6`Q:r1&(HI|B8&.% Ĩ>E[Ud Q\GЄcZ851z!K}U;Tql~atIh^Z(d3gfxlg0; !eYGt :=g1A[j62قjz M?9i;ʆy0aqI^͸!K͠Xɂĭp~xPJRE(_tY>ZRCZ6I`cCك/󲏐^ !dXK #b:%`zGyq@#4Tz]Dc7lM/G"ou υ- TT0[-ETͥňF;!zJ[ ;['w @|Rv })+;ՌzDxz;J}`^Oi[Ce~BzP  ]ʃNT6ɍ'#6n/hM0!mtTQ$O$WR`m2g6y:-Xpm~y˳l~CKBtdN>u& |e5 շb4۟\n’:3^52-hW0~֑J+$؏֖8LԨo# $/hg8~֜J*AptG=I]5gOS@|љʏڠx!d5z1}HUc+*빉g`ĆY4I$WED(5|_m fZ@pĪ@3gIR?B/n*;9D2i8R%;1/钩0,X4W[UZ!!6a0N"NiV[ស26 `oIci~ZO (α656B9;C\ѿjX0*hV_TGOՄm%'EBC#"=Pيq3/+ݞ\d;$g_"Ic:Ǣu6EO֕<);f%p?*ti*׶wNے#؈%oH?] Wե9 טA%aQ_÷9g%  z]?r":>nNBPAcs=`쥞0i 3015lhՎporqv)->BՆ4^oL챻B)u@rG nFKZgG ‰nqjknv ƦGMhp l.[pq3)H9cBfo;k Yl3[nlH )7Sj+X}n1ɐh' V^IUӂbD Q2^ɠAVƪp]KjMQiMݿ`u?azQk71gJuD<|x G,zQ3L(,4R I ЗHRkm3+sJ|>o]$7AŸ+3ząuh1257*(+Ї5Ԅ\M4@ 4oU^d[%_uȃK) @)NF&\+_` 66g)2#_j5cŏ{"$,_D$'?p?VY4o7Fyg<#*!\ykY>2\|) iS֙6N& R@b?of.tmu;.N*I/O17mrriRg{ _H;:cFDFl؜jRthZ?Oq)”Ҋ1K7BfKWLbҺ?ROԯ*i F$Xd, w-_ѿ8t)G;L6) ½UFVmuCr:3tcLbDGdKz56>|u~PHNǷ*2`v988EΖ& l̬H+4`R,KNӽfL(yjuc)V"!GF%UpurIWIq0T?)ܜ'&_QOFolCe2A2g'AܼH3(Bm@gW/ C=}4Qdk&e8&GO8o7yw><M 'т ;GB۝[l`XêIH `^@ B\h`;E"͵ꁼh`GwL̪5\ӯH*?U*pO'yI+-4An61? LMڕeH~iWQ.zjJzf&Rv5QΌ?ʻod4DŋpCWܮH7r[jn*I΢&h@0e9D% U\}h(/ -謜vsI/ߠɛ^rM߭|Тzl tqsAȾ:tA8y7ڔePGw@ʹÎr["%xٛE!"F\D=`Y:C Q8+Pyql S8RZ_/ֻc's/̹?1q8sE_bI) ;n-0˛^Qݣ Qx6CUGƽ˯qSQ˜eiM >Ө$w?/fj R _HYWjhb';"Na etºkor^k01Uas2e\^̑o #xw|svP}+0J:O&aRzWzN8m4DSrj4TO[Z%[s cmv&X۶t)oIh!u[SzўZ{Nt֏@LRD"NvP;JXR f{Ggj̅Tɰ6$qx vC!9R$^% ki&m"j&y 2evMfCXp#rAKݰzm'vDCn8hnGA͛E-ixDw!$> 끶ݿpRWL_|N$U2pd ǑM^B\k@)L7?}cj&,vX33 [ďڸV{^Ij%B(wMEm~ZE;DYBHܒ.͵ oa_VG{[z`.TomnxvaJ 7To  ܆Išaijюnu%eJ POḎ G߇qho; 끁#a B~&RLw+꡵vq&/OO&šW Kle[BRL 5ȿzX>%ѓIɺ鳪c42Tz$lU#HJs`+>᳋ Xu)ez6I'B͙UnZCX$1E쇵nimSOsD9x L,"`id;A@ ! /fv72[|]@gk2 lVs[|rfc_0>Kaf&QGi#P(8] qs:-\MC%{rdA|B0T0pڻg{dпVc*NшWyr%/FFQw"R7 % L,"5@W~<_&jCreKzGxk<KZg.c7·{! 2,^xƘoamϓ:ˏEgP 6 %T ޏA/"K soؒ]G͵OllXLA< s+eTݸjA±DmvM13{ĈN#nBRE8p ULs ,.-Oz4 N$4ݏ+]M|s!z+- UJ\rҐx)::o>>U^ -OUxuJJz5Fv-ݎSm[ɻYD5yY62DP7Q[DiKI**l M'cŻyFHSuz2 136 בXr>,,5A-a"Y;<aiB"ys7]M&Iڬ[ $ f|\e Zݲ B&q~c38L | !F41H5WױqKLf{#"b ]ʯzwqtqYc(2jr c#ńThO|qe XȬyW^AΐQ|P*DvԼBmA(P@fF+# "uO$GxrMn>UPd{u~\3( |S.i:^sm1B1&\ugƅ=b#d5սצ[=]7LBZt: Gm3Iogdž_" _nϰթrA1/yj=4.J {݅&U>o{Ex\^,s<;$;(gߞ8-[rm-5ZQ0IOBwf6CdIN`Y;m5~Uf0|v^bt%Q~ȣO@ #IM{kZjD%O_у?˘]p p?QmI:TZ1ջ"-tVe@.5YG :]DnG׫ŠW:vIH8ʺH/%$L,̩Dt[}7#Qh6 ǀM~q[*ʼnh2>W][',>`(}-A ?psmk@o w1ԁ #8q:?v5i^ٱ&p.sdžTy3$U-V%Rq0-?ף) o/ޅZ!n+774V?j63 Z䙿3myz@,&UK //kStb@BnAgI*;ar^^,%75 ]q)x&,j!E.p͞[q"F؛0nSnesnhTj4 !slЅ} r%ȩKo@N+JٻYtfG ә@mqU0c캃B? +'[@{A%ھ龛B0F,/z1:l ,7U|LJczb0mc*~ 14oePRԇ̸!b|Gd-ͨU} ,#Zs1=L%,KA/!K0V=[r{W%cf"6]qiqnƛH=9Ieլ.||kmsiH0;%  O P2\SxU AiB砞oob`;7@۰d2f. ɏ\_ѽ;դn:ܛ9J%'2{jPcƖ^Ij+;٩:R碣 (C=3A. UD~&m ,0):&ciǢi_"v|DeTAR/X$q6:v*weH##*:v3r@}dž>x;H e^F|5VR*il6A]%Cn]=3p(ӅzKm+f9"M?za&lڦ%Ɲzl47`P XC,Fgg oPONCyW@fmkeĚ~ԈLG_1J#} 9;^ ec +z K0|P0@q®{sBנf!eunKS yyB>:YxwM1kQ u) pG >,]`z\!!(艇=] MWޅ7m?!u~f F[o $'9(~O{8'6T"VAD$i= Z6 ؁bCb>pʘ3Klqu |l$Z?o1歖Om }g`'em*ϸYbH!r݄`x6l !upU柢Qo@ Ƥ^.1t]ePA5jOe 0xÒ 0lV/բI7b{ yLKaŃr驠wK?G^ ;`,pfϰ+el@ qN*l FQI\N=3Sei6AGDp sMB^H8 6dqЁ=?<F34Lc]f ‡oX_>ahj-,YWՍnU&J*-ar 7K-b@< /i0ŌiQJjiTʡngJǣצUfV/w-ܛhP~p vpD-|sEp|kd _I9lo`DYIc S1{YnUBVnni?W )f:6/5X*PpGB5t%H x6Uzh~=YGkk8~?5y0ޢ*l18sy$ƒu^A]O&/uV>Ql=ǴS )?m\ȰvB!f">#2χ6pXH Liy Ub.5v8SGe]fffrS>8HNY+!0Jpseդ`N% #D\vnFs;iI/̤OAx,q2]|#E k :7tNYysF\ؘ0yaR^og+ЭyQGELŸqz-Pj 3ZMܬbqr,ujބ[|m=<:.+ s% 0Fn(Wol0x)w $R8ێ6|ʋ"p=XY- ܺ$ ?~H0tfq-&a n!ٔyGeҐ!:W؝fK}GOp'u/FF:uNrZb3q'qZ0v۵v` 'tc딿.'^֐OKz[|ɤ<- q{e. D~P*),(dǩOK·ˀj+ңS{%BU:Rn &6ȁIu0alloI+yPe2q"gh\>Y䭉 Oet}2GC铀OKU Terބ1aO.˛?CDEʋLm:qfw3X0Ã)) WHCm}6PԢPd8o2ϲ3THzKߠ.#Ƞja+s=W8@񮒶M-꣈ smM0<@D>2JAhE쓮9iaDNz{xeQfJC"1j2|=ݔ]Pf0}A  ܇?&)-ad AbL\kUH0cx~3dEL:Qos$?YsֽgH V$x eXR<^o`#,I6+O/D {?Ew`S)&_0Vщ>8{u)c`aSR)}޼S߱>^#!փYNf[D(X޴mA`ZY W+2oBO{.$@ctc vl7'y6pLe( F} na~V/Λ6Gh]Њ( aC=xm+A钗QUZPk!uTZ){f8m5 N*( j%Ā1. =,mgA-ߔRAut~a6aGA A7]պCK7zxTtYukt<^򶉆L[l9j`/;BSȂ MŬ)>N˥tq7vvkU׆ю {D}_),*]`oM|."r zVm9n_c&!)*zLN%͜;d8 ;k96n a:?Ÿ P(QG_8ˊdCB7a"alk>;L/6ƌ̩ niu]ݼ#/˜.~v92^O 0 @ do(4zs)W뚵nPri,?y_4di sgmVrDj!Ba+oK*TՃO"2W'U5S]d4?FzxRjho:E;󫓜{trYhyCT+}PRe~i8H(9"BQpUW+_-:4n֑ޤ vb km(B^PSyOo[bbkI+VXs:=-RZYJZ1뜺mO#7B8j@B.^TAa BU~,->pN#~w%rRqi$O4 .TV``,cEg*ކl$QJx*H^F[]bb,B(T @q5% yŭs$D+x,Ìp>;\#P 4a{q}K?$(]K5HL}mu׈m ,1Oj;a4#᭓}  *Y`{ȃ[Xy?dbHyN%w'itbxK/Txz n0N(j~.L8Ć"yzm5֋p;σ?B^VS6߁]ViGn|8Hsn+Y߲ a [72:M}4Kb Z5\kel]-;󼋕 fvZT?k__MT^z\x!(l}SܜfWU#sMyVOmPY~hu]F0b}?sV*ywh5L|ΩLz(+.aBOKXK'_'5GGIf?ˆT\ZK5MJ҉1*1<;#sDw-419wO*w}o'׍2_tW9;"4O'}JQ=ܑV\W ~|:{'<ˍ,ANm 7(7?珋^ݥJ*J(ES 28M<~Q\nKgC+jZ 6~|:P2O Ed ٱD0.f=?Ru"9,8pQYwxbtŸX贼-E%F2q yT'or0S|uO")$. VS&1'pa'o%E[I0DNJ 68}LeD3U, MJK$e($X&# ؖSOTi8Cd@cs=BxFQ2RQ9Tagtt#"÷ť``׽޴@IPZ|#h_3/ Ndh%P߃0x&~L+9`T8/Z4 m2&GyXinWRu^)xQǚfV%u= aDQ0Q&!)޹4X>a"4J'hLT. ޼HMWBMnmmˌ M=䙘b5kL)zDT3I-ia~miԭz<}W!2Cͣ Ҧ2 aޮ^k]B Ⱦȵsp-=+KfVϑWmlD{S>쓂2 Swbg%lUEgC{3}`:дDŌuKOzHx̜*l3sNs@1ػ 3;l'"v`dEC)KPYs_&8.S!տLye"Y q`qΎ<_wI[jE6>*̕ [ [/5jukC;}%Gj5:ԇ‰=rw< "|< LYn3Y|TٖG1#|t#h۸&\&k4!ۜoK "'^6 JVFg~VQ<(2[9%9;F-JC  ˨ .EQ3=?A1@ ϦC#6k+gF {b*AcbKLtZ0;vn2$^8vl:)U݊%qDǭL0,*W^{K6Y4jg-ow\ 26K8/Mc'fާǧKegr$ppw)}@﷌RPhwy. %~W3{;=A顣b=< fЎ&5.b>gzƇ DcX3| *$c6F.÷Ol\{f3 dWQdS bgCRj}_1^&r f+P* ś ,Xʑ8.T6Z$ܖbR?oJFB6UƄ]!Wal%3jGb> 93pYtnѳ=QwH'JQ!VKc j*x Ee݅ꌊ 纝f 'WOGw-jnBf }G+'s.ɷO/]AcLA-3c%lFRhݰk$) w^%Q0MLaRBh1hHk%(erC RSuw 8%+zΊdvXA AnvKjT-YtMNA8|h0"o( Ɗqt=gpj%#s'F"RfozESyZl"ij2vgW$FE*B/m5^ψ=!VdOg9cJG+[e#*ug#:ڥ3 ա RvTmM@'a@r<(]+[@$cႎРZP~< lb4O N;+eAqҒu"֐7*l6|8A u`bӛ{CkCdLHro=UHtەd`Zɽ׭SPzX] NfttK~Qs"Ͱ"ᆒ-f' Nji QRf*˥F~3ҧ#mVAD|fK2>v;ANpp\{ ͱG‘?g{׿|4oh!M=FΑTle&Xv{@?2v[S\\\G&X:F1oHV\i[K DOb5%2%/08b #O.LJ}aKv.q9 Ux&om:X !^0nWXR^?1._c)!=0{̝܏rm .eUu$:idok:Ogd Ts W} XH'5/Lo=9o `?*t;cZ`4)(XF3n!)w`RTִ~3ihAT2aU3ht2}h`W Ê+f$ٶ,s0ز#_L;?f@EUVzol;Q_|Gjn+?7]t/qy^$RZ≅ ̓rfmnwg%[˰?l=>* R`R^>lqeML`Yh+b0V݇Z8&h#' |ʶoي#c%+}/ 1J,g (!C5fq/,\#r7g-z<O>xy@ _[WqB'T1f73o85.yvaH)y )NK@РM SލmBGCR)ߟ}ݸ:yo{kg[ȍ?Rwty! EX*N:y%ޱ/K`[ȶycĨ.tnhrߒQ5TN;%=`) .!bm9a1,E[7KaEyasjvvĩ1{2x}ji5x<8:6uo^z쨽|We'ϼ4g&g2΃.JBњ`WC';l並zM?o{t%y`EjJDcW7+W#}0 =6Ģ$݉_Β^ xf%y͜z~ zY &?a (:/L7EN`oh_qbh f-< 8剥ͼ;N&(y^7h55dvlY?Ac-o3Ko,n# [w$7X_zYN+ܧ ΅͐Mb50#)+ Fď,vW-dC9\0إ'C# LGܟeH÷ ^mҦ'Ϣ>]~C]BgYah,0GS.x\n}v]"# ȮGwm@QȘ/@U}r6TClJ#E7e|Zd _nJTaTVZni()Qm]{ .|40#H"kժvrM`N/A%}(11^WNC3 (iZ"^V-J1n2*[R}C K̵{m2^Zxcnm"tKYperW'q>$i49Dױb(A1# I.JXLH ~n2>Bv-TwIQUO ߛᮡ_N;f,PR| $1V3ù :`Nᥥ[-vl#ih&Ac YE?4;JU}8OfY(F5N@Ff-؏ledIs|eoLUIyX)2Le4Ik.͕+ p#nLmihQRXR6x>I lz/Hs1YCtY {3xN2;Q/5Dhx @ BTlѲvp-n[82hAl` 7W=^3(9-7ZzX{T~;`cWОjS9/LG%&vm;?pKrh.es^{ʏl<𷬩q:՗‘t?Z.K!T5v4~P9*Qpw9opzƣ m(u. ^;4ژgNp@oaR.`f û7w%chAoyd$$ YZ50 l]aJ;=9OSYˊy&<_f5{7%- N!kvy+ Iyp.t^4cRV?<Ų,x /@„=oջܭ3O+0q]g6^{9ș۹]WTШ}07<A*i< TubĪK%1 `ekgDP쾣M|}ذEGR%01٧:D ~YheddykǬ̂U,$Gƪm!KXE':KEAUZݥǾc/@g 8CӏBpj Tz8JD.3VeRv|>hoK٬}Q^K'v4i^věYV[2_B=B%ސfZ!'%es1bF%qSwe(>. L& >'[gTRtbHx^ RDh缊0:(K:s9ufk>F %$RU*)ԴZJPAav3ʿ_^(K 5b0[4Rp|*0}:|o ^]r%c sHK>{\T`s{Jq*nc>  hy&>A} 7}2kx)XtmM<'b7F־6يdRYoX`(Ny\ Z^eØ?V=^rƸ7\3lt8]߱ UhBwشG/qPxMbUtJ 'v;Aq2Jk,{B L;#.摭]Lv"E3$m[" LӅԚz >< 'S@.;jL7nΊ\$ Xa޼1szT#!N<K-91:;(?Ii #LxَȠ϶ܨ ھn-Xa-܏*>.2VYj |Te@X[,kp8 \F X]CiSE!'i#Vm'RFVͥha쪗~ ۡ=Rꃱb\-M,%xmbw^%62>CH8bL gkS"% RsFܶydYg,xh7L>$ pmgXgfK0{mV˚j2J7K9 PG!5W"D SOwyc(s%^8^hU|-ސVxç|ԘCi2YTX1K_d@ē]5-1u+, @2%p zSOIN1lV]똝1 TRAN&PEZPoAը>KNXb` $Ei&ϱwxp%j혣T^Iw T P O*h So:!03`k 8i,kSV_+k$V A$5,(/ȃ<%rET.LCmکK^r5XHWdbHHU ;P*LL?x-b!H\A Lgb?^A{}#4tD L/Κ4((+Y3, -=Qn~( z.KuCRu mB18P86nI+D֌A}yJTcgFf2f}Nzȅ3LR(R`R/K{<-«o^LZf3ISߔ67k 5l} f#Q i8Mg J|Y0W?X.A@K4Ψzy Mp?wp60I]U){ ѝ?-L35J՝wvb!?3RGoת2nĜ%/A1 V3_akԍ̛,"Ap\3Wi\ MCvG޶W6f)IpfdC,/M_D}04r&yi{70n)VVT,z ؿ Ւ8YU+#H#PUǸBY oAwR׿8ߠ&1 ٘P&؋%ҢQ޵q9ƧݫN1.<~ Ҏבd&f21Ꙟ& $AGHe"a"V{khn޻)07Eg%2M<l βY0J}nɭ숁`!a1;yɴ_ ~quzX :1I9#o%S9COȒh|5Et_ѧM/[!Ձoʒќ(|~[${去ǦQ&QبS,iSJŬ_q|%w & ?el+68DcI}܋(i9aFԝ5GnF!@bV#|E-n*)"Q^Jkl>YLlVh~5I+vficOQ,5Tj.-j?t:[@ "ԜZ ]<.CѬ*Xx1GiAKDl *r7R%8u j>4m4]L) ?lAEOj i"҂ (4)! (II?dmO߆2T)Ff5$aÿ3Mh[s߻} *ϊDMMJnniO$W\-D' ;i7sqU>(( f,m*j9x?qY\ J%h, svEK\{@-h_3ձ/Ԙ̼%WwHD]3LtOߋGʠRGg~[ݪo}~97r_UEMͶ)g|fl^Qם,Gh:Z~ R,"m BGKu^>'<ՑKXM$!i<38Nqk@תj6 )p JboR}o l@a|LFlAI3Q+7-P5`fc)q-vfv1eD~{$/sM*$'p9ФMl.3TF_B,Jsw.6yto<fEf'S4ǠVP_ XgP՞x=^'ipiUM}a\#D)9+LQ;Ъ{BX^%W}?e]rʅ8BjK33]QAg;8~R ju9p !Od2=KFQ7Xb 公dlF9Rފ=`H܈Fqp9y/r%u^D}P+4kHUcic|'eN1e~HM02 o;#Q ΅T :Y-JEHoqk7er j{z DHU]Qg#6'stwQ=D|VfH\+V]M!_o-:aݾky9Rkѻ<}\)5{J8*f:i .`C6)ӸCh@],W@=Ԅg[dm G.`OW~Ou%}M5$uK'h0^6ik"H4T(u D9 -&ːP|Û/ jPfY^o&KF, IuH9zuq8Io|;/#TNOy1| WD&轎r4O"Fl%ѧr7祁 ZSM T^u)!>f'7?IyGIy}npK2!}0:~3+xD4-+mJjgF~)kK 乀Dʊ0c4w&Kѓ˶aw4NzjDO8lepX>,Xnavz,\7 jRYQ1"g|c<^Mi t0"lZPp?5peWgœ39E2*e} @b#~ 2Vz :mVPYjO25ٹ}N?BQoRotUH8hkX釉Vs9VCM'ޙP<^ =GgV(#zjߣQ>HGn1^SpR;\UM' xHAɒK~82*IAa|g"А;C$yD a^݂HEqs3#Dy+0"A; wrtD;Fj|ȏЯ[}MbPhhJ9؍DTU%^YB~(J01<ſIߟęɸ c4ŗ>X&n?:Q֪!§/}b ҥ0uBlX!wD)knt-zE=[y)t! )˜id}r[ r4Rm>s zV73PЏ4M8 |Ly%&(>xn"50"Rk'zM ٳ4>N*fQI|:7}*l*t yhPgb3ߠI=ڍxUvN0?$)?x{[2k,OdJX,*)NCQad!l$ʁ!5?Ani lߍZ1+tE{ t-IrMOn 4eUSdbֽ4Ab=^ Y+i%Cb|_q%6IRn)AzuA}P^#NP>z">o L.N&x7mJ g^.f8sJ򁃥[xeA)DZLO/ݷMvCbY 2)n,6̪]%#$#ܕEvQz|::`ME@]xD(٪+426V! TIE $ɑ+Pp|'e;pB-I*=ᅵ5ӂǨ8jG& w\ IQS!*W[O`BCKH#`Z_@R$fo^lf6{D$pfW>@{u߱Ds*h~X;qRNWKIFe "JkpZ._RY]go,KL9=g3@nRF>|J]#8 跳<w3]B Hp>ATOG[V@&#Pf3qr"S}b;8#vK%{9; +CSw&c1 Јbzz" rNAOU?|pB$M2$*m$"z㉁r}[,&9>HH5:Q./ǛhFV7{LS"a%rTB>5 i.]LIM]p_ſƼ5 6E} #SEjpD0*Nؿg@c1"\!Hs06 JR˙#Lf5bMF=!;ju>J r]$P^=rvާWKdb״[W*"@MU*GDw=QpW`KXPAJwf(l~8&O6A<éS>cwUDV`\Y~m8,S/.b}^dp@)jyd7DME wS_mUX=m 69!RjR Kg Y"/WeûUҀN pξFWr!yS`1ӇV.7aLFGb-+n!8r`*ȶ)?7>:aq"=Bl1Y˗<-qfڮHt n#B+ȓR]1A7HJ g< Lzߞ'Vx4sE6;kÌ <ǯӨx{wCW-,g!*: OL@7vZmOu<㝯Th]FA%oyYDӪQZ<\Z\[V{۝EC>[Wk˻l*_%$cCq+I TA;-c 0]2na  ZG6GtpEy{&2rdI|f.t9GpYRz{¾ 7Edf[&²3)eSM=BQL P f"PzÅn˓eaOY[Ǵ7GȉBWN 84_HƤAGQ'ˆBщ秵%(@; 3^ ,: SF'avHOxs 2ެ%.tCv$=lx;`QUtC. =-KwH$Q+i(VhlRF?%̑ۍx@А#G^O,=zp= F$9S#zd:_T+O"JeMny rEc3 u${s7"E1?ǁWw;9Ǝvbz 7 0;.=W_2<2+ŨqRf#IIzط[ ӯHh\^E[V3pǘUVB4b;*6p67`F0f):ʜ$yTDKL=6F% u#8.ք^9 P[*Unԓ밞5`%bj'\5=%ǩ)(U؃7)c}(p;U>ˀmAL;ayJaʗ܊wZ6ho_7d6~zh`9{r4V] |hGˣާ"U>T<,/pr5unܟ{h`_\ᄬ>` a |elȦ˴EQ ʐn7liEϿANnGpL[ZPA )?^ Vl|pPsۂC.Nx+u'~[|}֯N"=E;"@^팒(-SRVlZiA@K 4fAj&|)B0_r5K =x.ҏs)MU7uţQu{K!š-}*p4ƽ#<ΚlY- _%>'y\rFQd8EBd̤3\C9)O˧v /EF^3FKfINZmZdF0[6oR٩+ծbBעJ+~ˮ{ۨhfVJQ3h~٬xjճhgx_wx'ΐqoKe'C*mW,4Z137cycyqYCTO87$lB0MVu-~B.?0\(p<㞛A{ypE)ٜ,$|@[EH:zڲ蕓걻uL.*^ [̪B 38GKKO*#׮ P,??tnIdznD~W-~ t/õU2IɁWbpof\D, nHMyԼ t<TMJILؠމ~WO%HHG_~$Į6Wޫ| t(9l2phKJ¾ӎ>%p< PEtNBh" {8Jt8ARCۦpm~cf- |ċϹ`3,'m_lY* P7}^N49@:8R{>/*<3%4Ss@߫2̅Zۄ -- [̌dE > ^omǂp~ Q}#s2R1lل;,czp3m I3Na}a&U:C UގD48 j#MymM*y> z[̦mIOџG}kd}SF,18=ZPJ$Z$Tec T4(-k ԍCLjbe$-UC :8&}j6-Yzp9+&_mt/aػvi_!bgA [|~*F\|r*"-C: E:@Qb]dml(S^~Ⳕj $5-}TG?=~W]x́mz%cTs.D˥R$\C -  'dc 8t\WP?2jBd-bZ U/(d!*Uz'/rvbo͊7@s*Q)q @5<^*41ـ@9;-5aZpL}sRZ~g KBzKeͻHM,@.hRq+yZC&{ o[%QSLk9dB<@=2 9b?(c}Ѹ:Ύj>j0zIK9͔DE},° \މ5s:(HT`nXљ"vuM=ʈ3}9wSFbZr޳8xU^};}c!6$ٶNŗ|t1Ϻѝr25`О11R-x{ʚ~kj(8헟NVU eZukM]Y20ސzuCdY]zx-B@3\b^`phEz}]_TMOFEK nVt|Ă0mtqk> /mb6Jh:hi X|^w6[S;# =esz/ܢ3,r:J#fKhBP ?I[ J!#[] Y1~6\' vQJL&;IX* ÏN dL96r*K F*+C:c9Œr5҂wa"rۿ T(_[pyrC1;$`}ZMvUzmͮxPOĮriyqL ~\}q8>V/V-iT?Ȇ1߱kG0]ENaq-O۷4娣҈PlZBiគ6?gdgn*ѽE 3d3w3yo2RÞcy V՗86>#O Msa@螸YbKb1#|HBG)%p*.~[SDD_^=jI=6k}ܓF+PTNJª*V":JoA ͳULnO-(f^jVi.9m@Pˌ31㝌CJG@ Gq|jֳ(8LubqY 4;m;vu7J >>*>.@ O4[]1u{ \ǵF-gsQVdkV¨ݶ"g hCtz j:fmd, ^ϻWC9} R4\/ -?ӕ8R7ud>= Eپc.ǗB_ FS{_x`(HKtT+ct&xqxE~9[5!Oqn'v]djlзp5Q7JuxM)Jix./yJ+4_bCCLuqˬI8r)ES'XKUߤߟY~, ƄYDg$u2ܚ58 b"x.UT/e̴P95n(ڻ*"iв\omţAe3zNI\;.,]LxHv'Zl;*;u,/ʨ`=xnܼ}hw␘|~;4N]lj -;&ky 2T` icb2 ^2>lDY]6 s!紷UGYF杋l8F(Pۨ-eSOGtZZe+[eFTj4nZ,qۼlP6бz*U :/x,.Ff0TXNwF` FKOrQ*2!0K*iW(9lz0Mhc. ;g FP| Mx6E=LM}ٙ'LUslQmYEhl擗C|Ӂ̳uXǫ߲3r27Otoڧp2U 62U&Q-u1Ӄ=Tn}a.zu'5N"HKqIA;Y``]}͊NnUs mnuV wC`g[юߘM7E.2>l۷\y:b(eڟekɗrZȮ94FVp !_裋pwFĢ ]X t[d_]RY-[ o1W:71 Gp|.,NF$J0/bl` څ^j "4[bS7G[z9(tIZ_  B/%}w~!̅DB #&*,a{_lrp 뮑m=xlj|*nxp>9mk>5[8RIZz" tTQ *hTm*;F)OJV_TUf(܄Ɍ͸3q̹nN%T3Zo7_JcmW3)ՉӖ03eI><"l`ĮjRr&x/<5Y*}N897mrEOe̵ڌHApӟ2_f\( ~R&:RC;gRt;)<62K۝.}eѓ/Y0,eQ]_4Hؾ@UC4Y[Ԫ-+.QA,ܡ_OPr3֯[ U_f5S1 LIQ%>8Q+! DN9Sy=F2C#6]Npnhnws9sFXZCߟvu:4yWM k k!醎TɘheGh<"!,REj<~̑\hR5IkdSV("L荶D:aop ?%up87݌ iMug5S!HOOfCCc"%X;0) C֧'E|`u )̛!L0Sږ 'xς!È|4t,,R= 6љIvuI;IS⋄ln9Qem4kiKo4[eHcfԻ'/=|&}\;*MWbSP %.ByHn& A9j+Nt;H}HUY26V6gzeO6u8:2NO ;r8~~ ;3v@,yN=uac6Z|Tp"يJׄב(tӦ>;9Yil \?i2rN[ tߣSeZcj[R.J恅Y#Y`ZISZmK.%ө{m@QmS^XgYڻ,w{ATE΄A)UT cK)KOy3$iso1:*@5p@AVS4 ynʺKR39JBn˶;ξw|!<<|FQ!l\_G{8kn.zxD?9r/u& {&l`k϶ ̩- F{l]~NS2 jFLIhS-)nĖ:cSXt^U+(d 3 $5Wq~ %т$Lp)P촋"̙q)ff zT J J QY,-?w˃|uސP0A[{?R-*VTPᾄ,JE㷈e ney4(qR|CӘ\ʗil<F"as Z/QӜӬKxp k띋1 @2d+͐F ,"xL;FfGeFE?qQ<)ȃ?c9x>Z&^{eua~)ǞVm3C˾Ŕx(SmxD__=p3,Q5N$N͚4_ MՌE rYh<&q+[Q-]&ų% Gcq=.f~z$R #H}3=n_Q(C:? HVQ%/a^;[Ϲ̝) 6Rlsv&D;BgF_4léˆQ޹ *B=+b[ 5JTl, Mtq b\dsq5q!u$]m{5भZAI :hb[a BBX^"jwU߼/"w:*$jk{iϐU㕚hU<[Q+N~i'qߢkhEtcѧu}3(&#oTI8`sg9۲!)Rwm gB'{ݩ҈ س*2ޔ1&18iٔ*u0LRG, 6KH QM?}F3C.9 7zaCX<;˴mH`S%Ȃ G6s!QQ<" DcBIf •6:2n&aZ\3q'Zk)itSX/[]m#L.Q"ȗ *,t<[/}A.I8w;|gE\eH1s9 *JIZS3 ]l3id[+ҏ9V,KNS7PKV"ӚI^{_&vwBBi ,ʄc*̀Dj_>^y6mC\<919UMm?5pz_2 ;Z`}cң$ܕ寮tZܢn4rZb#% -xQ,ܱ, K(o9.ztqU pjwTr,UK aMhO{xj[1k!`:#)WR "=*ucLS[ hSi21g ՙo)pwZ|6:JV Q  ZyЌW%5}߬zrq)rTa"awس,$q-H3ξs}<w՞\cËLM& ]+^ۊ6!*Ɯ0|R^kGkD1+& 9]A=!i W/- l}-y+NOdLU\}mO$M Mtn:fc_oV@1YIӪb$0N퓭Jq->[y-d`g7uO=(QeZo]ɈOnm7Š?qTZnr$ef i@5FJBkjN cycj}kʴFk vB ub-Tt?"EAؖ)AD??]uגM!OZ7#,BC.fs:>*U{E(~ 1>/iO<(K;]`Rglvk>aVTN)~YygIj4Ő̸d8L>dMNkUno29T2N42b !OpxboUv3*Pt :p4r^zTiMNb(iHF}f3=῵ݟ["/cA? PJ  ;B+E̡tY܎J88%k%.ISLi ={N%qmv2Av⠀22Trn5!?$ݷ艶É͘ޡ.$I'o9TȚ)E0Pf$3VOX }=sG+ 7(9viBle< ?#6$Z`@YtX V.(S˼ZERp^NPadK%a7Ą+u4Xx48 5@\ ~R[_.cJ0 ɸV0.]5>9fu_.FPe^)/\h.f?>i'N{Ҹ+G'}◷` :2t*pcq(5d-YP;tdP&ߋF>)eKqSV'k!0 aD9^{kƵ61w*:zGJ!Lc/ضj?жްu\WpZ6pR21H;H.h<zK4*m'tr,/`Vp/}PkD@ XFkUQ6u HAoe޳&+S{T? e 9hvxJmέ,f[T.C뿟3z cQOxPL6+M&halŕ<^/Έw#8eXyW T-4_m#^Y5RN*ܶn =vnN ?ǔs(yc8Ib;JX>ϙKZ#Dv5&4w4~GsΥ}۠)CFF)j߸)EXǼk28d+1 pa ÌSu‘/ߣ#QpacMUYOKLЋf='K.@>e j|eN@m?# L EB<ܥ"%t@0-2\7furPnr޾=$ch_;IH~:Հ>%&Fve*wZ' /kvTѶku,նHK{VB}|fG ~%=zKdmjZ0-2ܬ!6Ż_t}= LMd_cQT!W_p\άd[|PU梮Og9;ߑ2(!]:e2lQ훐E{ Xj$e zl?2u1ʊ$/qɋTؗ82pkt@ֱ10lMXhоXN-.t ^r`h-еpaB@ʧҳ4i忌Hc>Id5Ů4 y⚤»]iYC`Y]ďc,}ZSh 7 LcݟJw/ܨal:[;:sO5*ͫc;qˣE0[օP4/܀Q  nkhrff* 8Q')Rr!O, d. ϶?O. ;v%'ˈkIFw3x$&_^/^ii`eL…7ݒdYVȯ[H% ad^{m?!(;Պ /^Yapja#Z\ǻ`]3 S3;n}={$VD},t)T< \gkκ$[.V=BY!->`iꐖվ neÎ<8[hsִ9mLC.Uf9)q$c BP/E+F̝`Z-d%]/uss]< c<麉j)W+륚aqŃ )h 7B2d:Aza"@FTve.H F=K8{4c;zmdAѮՈ?e3*#%glH,yGeKYfgy<C]7h{Дr }g$OZL2=a FUP}.cM8{ uH;VKwG>jf߹QoQ?8VFÌ]Wj'z;%uǥK.t$BWsK~(obG Q&'iiPa+l+ބO+A)ݕ#Cqn4 oXo:A__c כqʙqHM@ŦKY]$iek(:$A8/eϐiyfG+#s0in{rtm`N   {67#?/M&湎o~a dFM덱bB (Q =$4CȈ"_uw#W(mYcVjPLIiP~WQJ; +ϫg,oOM~wc &Q_ {Ȣcgwdi#TY*7C\S:Ssv$ `iktzȘq$f2f_ YNb:1|YJ@l׬mW4Gh=OL4vX8Y,|)ƨD^IuqwvFeF;wX%8Y,|M6'ҾyDTxu*\4nWCG=y> Aw؀s?ѳv:mjf+ת}7h-M,;) ys/t$*_Gp2H.G5{@d 9cWVGؖr"He"xPfkYW+o\S5Syw䘒:mړ\0hY8\sVʪ>0r)(x&2j7cS7|ɷ&}DPPU'ܲ޲n{vc2=Z~ x=Ϛ}|Ţ^.[%>Z@e+|bM ytVɶ@+FFϝA Fް಼j ?/fɖZ ̊ʑKP)@J`(}@\TR柴*G1FoQ^zH܂$wZMZ `kk7:oz_֭vh$_Etl"VW/RopS_%rTFS}q/ƙmYu-5 ̘.Cyjݩ'k!%IERҾi* ƒ(1uYFϵlƑ%Ϝ ϶_?AhET^[zp¬c),Wk).SreQlW*:o"=~Y~nPcmі$6:t#t;hHs]@쓌gn֍EoQ=U<'?~kKOʪZ,?*ԱkHDB) jGvE EF  ޒD0q^ ނC`a[;-hȿSrFz'vk'XXb9VV#9HElc[1>?Bwk䂍+L$R-8e rFW'o*ϸ>:2ted8dGuZs'T{'RI{C ^'y@(P((4* |.z5؀7뙲5mR[8Y޲ŹXb hG\T*vO1gHXn5ӣW4r4gBQbR`n73uBҍuf;.Ns{ۑKL`1y$؎ 8$f7?\=T1s[jaomQ>$L8DГGd-X<("Uep.^M׎F pe0y~uUZtѣ!|ۣ\ k ;pmQ9ݿt266(Oi…;C"|2d1,, +aP=Ƞ,৯tȔ5%guSkXk@RYIJƊ Xoxݚuǔ*ܫx6#+b@;x*'g9vsӂ x3j _귚^ C;U]y:'U(n>L6w7z>@aN)EK[{(P;V(bDԦ/\P<4H>ָ %mrˆ.\KZT@lMجSS9\KWx“9$&r!?N"HȈIӭS -9;d;d94$xS < cKvop|'`Q@݅J͡y;߷X,TzlsM)a^=֡P{u3*K> *EDy;ra ^0[M#+<4l"NNѕC7qQiAfnÉ_`coޛ48=4T7/}KON60"D-*> f />իcؤ[_o]N? >,g ]HSgbi"͈kl* ݏ&ivͲCH`8T.|p@f8 2fThlK[0HIpOoO)#ʼnӍLƲϤ:z~@(jdf_NV<:eģm?ݸ9wwڲ5XL 9nRHyz=r2LXB>H5Z>^Sfm2|6A5NGyADog?[XAA[VB}ۘ'*Fg$AKr/6.5 H_]IV]Adƍ^]>+S*N&W0> \ *Me6!: x@&qۀ"0!Y=[*fz"tyw")P] ]LwC^T8_DxNI bos>b߾m5& !Xw(8s}mH0Mxjy-|T]F`(_p!@w8QsC:LsHq`͂:pllO&9S5sLLν\ΝΉ8 MEیp#o?KܧxxYr8D2Ǟ< GՔ7#C *K. WnV˶"׼hɐBCbe-w7|>UWt@]XE(ͪxVY@YCdhRX,I] lĒňGi I'H$*+pYt|q$R8:dܮ,4QCɠS(#È۰ :ҔY Yͪ/ucE*..Qlfky=('kݶyМ0kP~#I (zΘ{7 Ƃ(rdQ+M|URg{XUf~ottY9be 3g\_;Pw"*md!Nqyb-`j b瘟"ϰ޻x_7ӼXrVOEM@K:<${GϦUHMՔ7'vMa^lf`avE?i!LԘDd;7:1>UOmm@h)Z,"Bib"(.|B Ĝqd.HK@cW 'h$ey3i9nTj) ,w疛rUҞL02E w\X)[OC i6IS"jx<8u&2DH{<*0 $w{_eӗ,紼^Näcy2<;e-~.1 o"Y%^U#)m,f=9*ۗ@Ze7&->\=&M{(| wjuma6=WPuIaQP^Wf"TEE>wOp#zhLifG߈^A#{&9$Y|TI$y O%W-G'@a[6\Vrm296gg'B827uPD؀/T|h.ʐM̘@wQY^i-5iEC'Rr9G" _64Aa9`g-d16+>w*8{25mxpU8(v- S+C[.l'd>.*SyENo% )hRo= x[)7 ׀` 46w=ۀ!\ACUwvs7xR=gJ/}-V}%:JCijv(/PP:נL@G 2%gǩ[&k db[iכ |掋3no9)U}ˉro>iTj& 42;#Fcvtyބa\,w>~Qn2ԫ+Y.V"u[U^.Bp!nK)″Hamg J{.N gL`wZη|t'9d94IM bFɿ eDJXHu`(#J h>+YF?iOq%6^ .}i&vQnk I0Fr[-`*"Ҧ 憜X$V!ʮXQJM?a .V^}o9kNa@Xo~ſW )Wϊ%.32X@ſG  SiVLg΅PNROIcCFWRg-y[vؓXqnf2;Z wkqtށH(AVՖ?K`6z=ga'Niy3A"S2_mkN)[|k܏ïA )ZmP߆b,?^- xԩN|.޻vX1:^q#0t∼a/i8\ >~ '['fOHl=w'~vWɵɇ!AJ=ЫD8ōZ'+5V+4\ahM Dtu}hX_b25 l[>89"rƆ/.髸"ωil,>--RKYJn5,so`{?vD{yU\ !OӾ/87<nh¤Sf + ׻G'L=}&q4g֓D9=\O|BǭKFsdwrܞL-f`(p! BYhԿy2(~S]>ܮm f|ROLP7uo N^ 4;O0R^"+Vqa-1p'OJ֓>gjC]TyP93Do:Ӊ#rף/I qY /B _="$qe,͍A$Wjjx8)'ER* w%8WBGrr8+aP<[7>xUJ Ys#i }t.:> U*@!}!X]cuUP_1M5S& g`)L8+f1q_Jqtc-@Mgnِ1-19WSg,1*jsDHFOĻS28+idQ؇{ˉfU ʃ!Hܭl-0KhTGp#_Ԃ l(n Z B`i,hkK](\Qu 0tҸ2#ė*>$`yNEXWTxont=*"0Ƌv@C3YPGtyް:<鴸yJ 4/=|@f ~(s=\C8( QiFցiiZcDa j\X*;-ff| _"NO0hwb]{ENQ=P|mT{lx\S./(,lH_&:'c#Jmf1Ҵ6Z GXDDGZ]DZ!T>+#wTV;aQ:Dr#(:B~`$q1bkt)Ԅ/]~դpY]!z t2bӲu01ejGs0^[zCͷSI8,`{A=.S'91zC^rťzklv\澰zO s!2awe BmӲ5?Q;:7Y%;b OM*q>(jYvXp'O,yى4 ב;J#"mDCnŬH0eedΐ'3(]Dgǜ#S;w%zW0|%AaO(~g\ǻ:K[4]lҬa[nNSؙ3r-& mB"EOĹe8hD53uS )Ev3G@"#G)k$ϚBD%'3{K4$ ,ٶrw }̭ߥ3evxz.ȶ`^c%>$->~h_vWp#Znt86PnYl_l+>fNˎ\h׳jGqa 0\롧ڟR uЎ`f a+܂c1#dA6 &`{ OQ݋!u'ć늕,{}pئrS>(E,⦐pQuêUun00ě>cUԉtK=ܳdI"&L![(,{LXBwg]BDWC'2x)CZe蒾5:Z"@2aiECDcC~+.%]AޗUhhjy42Ÿ#5?`b{ *8k"6'0KMsa"ӡ5aeT'7M|{ u#ֆ&T5mD| = !>/mloh1'q7Dt >fAQ\$(y"m96@`.3ԎAI6eL"sQ5uzKxY۔CUHwoCT 8súA$R7b (6٫VnZ } 8ډ)>rԎz{lF[f-Zv GqTq`Z33 .>JuKJow}팚@V 3 @;7l+O[rݤ26AEŸBN %QږTׅ ` *8#yy!HoP x_0eJ( ~: 41犀#,m .܂DYtvyq 1|0DXr Ó YK,>U)vOCYlmj}jLאY%IT6Ei0X6,8=+Q:#$RRmҊt x٥1$1mN(@/a!۶9hp$D]K^Xm)Kďnq ֣]=ϕh lq@f^i"wQT Eɵz)f.WxĜy݈ #,d8cմd,QZAת yěLШk!xwHm=U{%kRW=ғ W|S?C/I"iEf\*EL@`@s_ϲ^@S*nb9f Esazll DYwDʓ K =V1 pE9/LXG[;[%K,,DAM=>֗xKjKEN:C&ad8ܐ Y?;BX \wP=6?#'  :< rT& ozQᯕtP̞ -őU( .#^4swpm t}9j_ܥ>٤ibj-/R)61?H\OJˎ9\Ds+oYo4shY\ C"EEdTMUo>^H (h0sI^[F. }@W%5p̆uyS42&"\,,-ZxS?Y Z,_)s5NCL^=488_NM47.Kc;A#K)'Q[{63a !i6H H \п; \Na#f鎲R wk [C#`ks* e^ n|U-&C QSޗjX%:䀁6sͬ[5 8A+q<™=WEp$bn F2W'-љ64 OM|܉Űa-c;z 6,gxȲiGVBVpv{~EJsSq⤳=p6GELQC[`l?FEn+fK>0E/YuK7 DX՞}F.`Z02' 2TTҭ%t2p7W"w׃G_J']dwШD9wBxB )5eL_/8A2Q ew+oDžJA}ͨZ٪$?L/`aN|ؐB'0=CL'ԓt:V)x|pv_*zPcy*5|UXHKZ/[LX4qߟέe!#OX|NC:U`am'pIn~Y.3+Pڸ\6Wp+4j> -,[Ә5֝\@f~,e<'N5h 幗-uZl .붘< 4 n.qx36So|m'cόz&)R{6Wd3 ɚkMn>OՄ#6uttN{- h7m)lJMhVT|G% zz μ1ׯ OnZQ#Z?>8|t:\B~_=&H#(k%^yt{^ן99cQ#RKDT)G7 A8Aiʪ `֍7s/\sȿWdKu{"i`bN(,w2/njuG*bi՗杅}4ҡ!O)xrY0غh"Ӓc! IU?#Ó1k>x<%~߾sbaI=Q!0:ӱ ֛R(GkVIS˂/QTlXC3*NvLq<;;&gu. ;][jjɉ)Tpd oetHgL4.ODXZxAA~Bvot8>P\Cy->!x۔6}:׿C KK+c\\ڪ7Y۾e!V_ٿpc.&ʧlHGm 2ͼo;Kj3|52Q._r?؟v[]m"G[O0Sդ*n3ƕ!rP-2f ,d Q( %0Lr>=|/GGdC&{Zjl/K=rlBuFbUjzA]1+(q,|r{s _RZ3CM+. TVZ\4~|+g7 k[GS2^#Մ՚쒫v6(^43k M3>j3'u u|/cE/Q -AN,F^F|VE?!Az<:@<4LmԒi8D f~_Ppem,$%Wŵ]>}WT+m ntyiHV,ʷnHn<K̑|A$aujxWyEwKV\/tQ8t|xWsh%8g߅ڤdw8qA{cy~m'e6k `[0%3w{y,R#!V?DXS36@$@;R&-/ ՀwTlLoS; QumDf 7#W;;׆YUzQtbw2tV2(?\*gw;:`Fg(^ٺgqIC=oɬ4.2dy&o.p#wĪ]a0ZNڎZ!VD2fG-˻'el\K;z Wd7Wp <02Lԝ0-J҇{AMJ^|{#H)!#bl/n`;{ZZFi<=([ pT(=-kh]\AS&+Y tU"yOOF ӹ] Dv|Me޶D^WF}2 { hEӓ>|Vee}",jT4=w2GnŠGcohHX+٢U {Qx'> c8PZiA"2vAj__-/LCu-1Q'{Dfg\g═*PI6isV~5^x 3v&::v,y4 K=eco}GR\b9GctH{0CHSi}+ ūU$S rU!ĄTms,ɄѸ9/KÖb!@r 3Ⱥ*8. :Vu) o:,Kk~e9"go(qo g]j㉷F#x Y VS>}-/OJwgoDmsJ?fY#F OQS`[0qE74(_aBY hLX_=UV6{ Anr]m]9o1ËFYx3!ijf=*[[kID@oX?hmg378}Z!A]RVDт-g$n6J(Sl4811%vtjh:-P Įs.ectSRmiBNإu;$_,U.|TS7>7|4.|߭]Lcm) &` ^̧|@0$=8@Ɇ&j[L^0 gLҷz5_:dP8wZҢ2I\Be"'iX"<'H$ :~PqupHT>aԾ )UlyF<urÑhd[KsgYYid|# vkZMjam!AeWT̳ILX,+^=>!EsbAo0@X)(7$=s[4n+tMn92= ڨv^°g#̬ۊ.H_ke'8L?/"14`W$\.[}&Ė)$pO1,N#?{ץXB48s,N3p,+[2_SXқ9 j$f[&>9JYhaσDf2:a&`@;sh `#a&uPY,:i4Y7.A$4/=M&F~\ cYdIk$uΝӥ螉eff+txۘ4PpMrodriYrcF\G&l-c^__a|V ޵*lWdO;L y4;9uV7sxGM}ը߫t>яӺ8Kx+[<O Lh:H+xqPXk\dT8䮡dW`-H|Zw8VČ E::xId;p?d2EC"8-0x)atP:ˆLh!fچ6 kҁڎ&:ʗB*?Jh1 K h{əBsٔ$Ӝż|r t,2]usf]wM(@T2GI?C/ S D[ ^]T9u-YP ^tN1id`ʉ%xDl5P:a@5Ў[Lz+H_6z@ 'P1t}9H)֖,*ηO#S UC!㳰Yx i{!fr;R^&[|q{s^07ර0%2YF76r^t}[b6\to-<9_ n27ތor~}1ؙ'_0qz6'@n>-mфymLj KTս_FZ>9=tU?cݘo*UPf@aogv+XHRa Ӄ}Ut#oL U6?|hMeF ReĚ"tA[R!Җ|Al/8r*u)B`ψ-! )7"|.{ydu<*np"V \C4rr99]`X!e7 *=lu㤍Klrel oۥ[MC K= g=>))a1s"O +Ui g'޷?w4=AeՀr|UC^p1C_蟊unS=f*wJ"-ϲT-|RpL8(̥Se6jSW]Tss-jaHbk=Ť_I: .b]-r,ϋ]6IFiǰvWږ]\Q{>L0xWq Tz8 N6'_R)^z,MTٺ @#%@$3[&Ƙ|(HiWdE饸CdV<sn:s;1Ը\`-n=Ka˔H&DJ\JqjyYK Nt?DE[P_ieKf6>jDOd^w5 <:95,zޘ ?kn7@@#۾P/]*%ՔW~[=_kab(&&o,mHeR;pI}u2@(J+[/\)|n e[0ax E x @]rz qr*'(֗u C(π`B Zݦ| /++co|̈́3a3sGqW_++9"FE׬>HS9@Z]ծv.SoQz[cH"%[Xta\%jHD1Kft]5q9-ʒ*՗όc]~P52&LL 2sɕ@U$]s{R0tcE @WDf? MԤ)& %\51*Jy)] H4'~#H9D:amGYǥ٨i]v<Hp/3m&yZ5>%)H8bhu.tGmE~!{em 9UN_3- {yHЌ%+,xFF^&iְ"(IY%ާ:l@˄#eCJH+b.p;Q VL$$u=ȏO*%?j$1u&A&4 YK\(ZTm+ <#.T8`#NYhC"=/=0XzuŸ^% w@\#>ѫ_$h;HjbrӍ/"!F4y ୧|Tl-=9 8aˉ:kLD(k0jƌ~ xY¢[ x>0~ƎB?V^ ⾷KAcU TƮ_kIN@G!4i9 QyRQ7+`y Ӊ8ʩϷ+;c$ulju$E_m\KϿr09[ڂ ;#5R<9ਫ਼ק2VQ|X[_-h+C!.=Y_AUkOm\b.a]QT'Ʀwc`v́~Qz{%uPK_{=e:5;򭺍p8{-;.] MCⴲt2nO(Y[g뜌/k)@`(ņ)P$[l.=-`D9f@_Ob'w绾lh{,`\x M@:lKjiI G8L^2–?ί罯i9 iNsCXDݾj- `,gə?Y})VRҞJ8@ziV,"xMZ7 T[/ GFy(hГ=ywb9ܲGHCΠHB"exDxYt:%NI|Ğ4`7l̉9T~`#Ao>^ h؆Ŏ8QKӅVJ"?xɳO&*CGS´]L{0IgY6:08=1fz/{Mn|@2GKOɉ ym}{cR ~mUP0Ir29>R63Msd>XDu ~9(.ހ0V{mO9; "kڪO%t'5Rdy~3I{!nq| v4@P-!4 X&m2Hm;Sж '=ɚ=FoIl$?q+"*>>!=AbЧ&cc;tr' G@Xٗ^FeFm_:!ڇ t6 B퀉$e!1*)}t:"#*]ECu.E3\\]SGf^@3Bs ,I&fڏ>8oIvaaL 㾓2.=txAQ[i'J O80 )+f%PWyn\l*v,QdMٮ|mv>Seٙ mhPK 퉳V@I͎{H= q*-sE.oK#=/9\'y.B% d|ac-0^fF|NXNZEÒ4^$FrT xv;qkc&,`hhJmey+6y74v1~lBw6}>4 L`NOKO9d /٩% Ktkp1-xq͏&l+A',8G>vAǾXJڂT[G>" JQbmqvU `!m#z6^ z*wSϧY=5^q5?C1 "fп%' Be^fݖw0o4O~Xنj0ܦ\ӻRkE&݄O`tZHd 2O{JF }mK/IԺz'^.@jW6@u\ e< bCyϏTnʬY`NkiO92q؅?Om>8sOe!mG, Sz$ /-R1"DK`C\q)͗Fhvs"6Xl $Ϳ/=ҼM" Jܔlcwr@>6;1t(^9 q([(.,}%OAkȑJ[>|dP\⍩`@<@`b29pkI[=u"JE^3UsU'5==Zs` hnߕ(pÑ*7;wQT+z+@?-ʌtUf daľk9Cc}6J/J.(Y<Rlq#pC\n=:װ)w,.nm$iQR$Wqmron)Agosٿ^S#>D95u(P1ۓO%!*5#ajapX7^R$w? Gs,"4Q((ߵty+.6K*aEh4h=D*\ǫ$YʆfىDž k_2?dLk[i(-%7ݵ*gaq7ox5 l+_Կc`^2|-p[ۆM+bbk`Vke+7q5UI?ɧeDr~sͶEm[ކԈē,Vn %>ޝt"'i;ZNJc!ӲJf4߉6:_w@/.ԩ YEO2l5J"G8u(tw JHtfc)j_fVKmcsٝ?"x8Gn8ƺix} +I;w;mg]Dsk-Ui]nGax{Y(-fiѳeЅNr4ᓻY^̒5-jWoť0Saؑ|8?sgqꡯfjMiY](^۬x'S|`rj.9!+ >|nݧX/Yfg=K{+<˚_ZyF=94*rY~$Fj'եǧPN׮qglK{`Ԉv=jL﯋Z0zIJuc}} ]?BXTi&0"'~-^\g\`C9=$.I*Lvv;Tb$rW_O1EE[x PPrΦLD wrUl<.zI?UJQtk+Xó$ 4>(+Ʋuǹ!)g*V^Vi1]D*n1+2cYPEIj3A0U2/dUl7ů_.kt43^,"հ/컓K`o80yҺW\`Vߛ]c"^A\.Z7!Gάӭz#`ٴE3.Y%9R^~X"$7[Hy6ush/^ѸA!(2xDH5[ ߠuǶXXJ{b-zsܚ B/i B9Gڥf6CRԲnbbP#N&)Q!L' ]L[E^Lл|tׂWX|Ɂ|ˣ4,xRutoܛQ钒,B&Z8[m+6"+h/ȫeFNuRj[>bІdt>'}Pox'J^؍yJ f*nXGY(TL3ّJFs7H?3'!㺭E<\] LY'YmRwM0z} @ZrpA(!7%t_qPQo''/9zVW 7áC-+=>˧EPjfdq[3"+JD6F$phUD>}v5{r8Mnɺ3MP/.A&dzUB:9Xw?pO_OªI( skk79ܻUCu'MYadANW7SƋq *tnOs7%"MdgMoٽ&\1 1}fi] m{8xg2 AQ9RRX&%s7/ >U%@˾gzȝl\z}}@FIe62"/z) fk^vkd/EelÀf\^A:A:u}zr)$l~!ei S:Gzot^RՎ/OU 9Yc%ɬN0L4;,y+fAWhV <}&W : Ch|$ڤ TmEcaNU϶ <>1}a|ϸ}cpZEukL39-B_"@`? OvlyRiIN~ۓG9 83CܰE[2iBPQ[xzZV[mtU]*MtqFyBGB')~ ]W:,k aXX6=yjA/.#/ϗה!C/WE؊PȥO,` qV,z!ZOE>@y$khWLtAWD5yT|ט3 "/@9Gp>N><+ $l2WfgCՕX;yX!ӡp!h\.3* , +5Ɩ*upq*!,)sT #LçxE# P symj_Bk, 9Ι&~V2 {{ъ]- \Ӓ$U{;h((q* 臘"I0- 2dN(RB+}o̧s&BnD#ƨB#N7X$k'| ʭ w} Y/pʘoԣӁN`SF86gI}Ra0WcC~3H6Us%7(OT~.#M$+y0 ƊDvZj<_hAv<;udj.d7a GbglG%w :|Fp0:[ܐ\bg|M(Kp1D.+/gBMv{gUf J(4DI>EgJ32m0ݩm-"<(=5,i4GBv{gl-JHŠB%19K".Bݳvɶ³\C",a i^R}7f'(+d Ex1 Eǟ1YS;,hΦ'YbpʃJ7|J*Q^Hzjm./B b</},>_&ݼ\xfYis;l{ 21 QNj!X. NBYA8 x}DL ՛t+/@sN]FdthØ(BDjSқs,}>xyF~>d |t@`Ip'Rm3ypGQh-yD YV^ & bHKWl2H=v6L]E BIfE'Obrefgp(*זn`j :$Jl{ܛTöfPv^ҏA..ƨ癶mHW'2Xo"? z _XNpomtZ6ƿHLUd27 t/Iַ.\p8Ko2 T̹2ͬ]>/GWLORDG6:3?]rQ0]u(ixؖrHm YA47[YMQDG7q:72/vD7%2_dzh8txW^`aB`Aȵ3P'w@ lma۟`T >⵼n,$N;HT$`̋~;`CD42?'S^njX;Br p%Ղz{c +[SA/gU0荄r ʒe.;*﷠,DJƲ< m0492e@,`('1kOEt]yWՔٱ>ԡ2c7jZ*!f)'+l ^vLgڱ-'=Qo*!6 $EV hR9жJYFU=JXhތ,^Ur&;yQݒcHlM|_Wosm'0l"Sy 1oQim9{,h%D5s*2&JsRZǟpW-HLZ;jJA xq 52X:h$zy̺cPBݠ2 @(Wjc;? IG{ZEUDTˣ׆p A~-o>Yf._<)gDB\P*9k6ҪG05W_݊-b$>8\.gʫ.^ -cY<j JߏJTL$]|>}m߹RZm՛MS% -׭{*Cw4!u\2:;H*PC786ѲnpCLOzu=Amڶe)p7\fGSʟuQ8c0DR]/:=`"xww\vB`r zvSW^oQAwlo+ ++! >(xQW;}G"&hTx\YfNV)]Iy8 hZUGZKI`>q`UoKƁd.0 OCybvɚhx=I?|l#ӟZStT7JqΗ[Nh%i!q=Y>H EBbP^ttV7쨌6e 3 $c+H6B8l>C$D_[#g!;/D\Iԁ\=;H{&Ύ<6O1otif)tW6y)* خb0 [:(erL"L&f+T` 驴i0}@\;fzf|V0 u^|7ʤP jyAyIbe9< "E|π-{locV[=8̸>0P2>t A @#7WYyMƜ睑H7VC ^GBg!qF_G`ޥk6HC4w|sKeWZu& pR`GeYQCU@' *3ukseE^?K,X\q!}b?zE0;uCr|hmZ`*>ozŷ"/*Vh~J(gSHKgEY9PF!z"E; (URCwJa^{ j')ݹq`=hͲnܗ9}$'x S^{`k\Y!'CQߕ Tx94iDPvAvmͩfOG,v#i~[sC7C>:SA c&]y PTD:o@xj\6ՓSkιcX2] B{RoXSF2" q=Г ښ֕[f{1'՟yYc;AYuAk؏*pHx I %RuU"k\;FDZb &5Ų(WI}iXrWH;] !hHRtj<~_~{DP%:SBS}?dO*b\@9@zQk )BFMc/C48.+VkBQa$ԝpP92,æ`> 3k |W@kᓾ '> aɣv,xfG{cA[T&$A 37Kҷz2xW|4ф|5ݢk]3ҴùCeȩpMhű ((%煬70wi?ڮVME Ez0Sp,V+9y63:nSF k!ƪGg]Pn_Hd/7\QMYf=}0P+! ]w!7Bԭ70ya}uB#B$aFY0h qIn_f?o}9jl{.gXqvt}KR&)4@Qk/?ØVy881TJPEZ^3=` Z6 R3\6x/vb{m6B'vZH*U6/fM#,Cl;d$5f| \w/~%QNH`W !ę&)`4}{A(L%k3~@-v5_`rH䢚6a͝9B'W0 ⥿zcNw9h^{]|e 8V{xMxIq8 q`Q+ 3CЗ_+}ɵ`6竧hNdڑE4Q#5)f& n(S|HW' 4;"' ֯,R_A^Zbbm. J~un}Ze}]'هo։eDQΥAaZ\ZN;J14Z=ݍ&\麋q6!Wv^\C_L8e5e|Kׄ~}Cr;ZpUNPl?03[ mSKM{,y`LVm?z17yiϴ!BJt$%Cd27]n݉-84E9wo)2ͦF4;"pèQKX]7< .F&Zi ^ 9“kToE# ^~ LI1&(,mV4"ᤘSXW 5jЇZi9fށQH5(3Q<+/qed 'd+,52<ٍݦfoTH4)#@\9B xsz)Um O{ɄIrԬh'nw2_ո,!7TdQhtn`+i {=M:4f7D4ZrK !hBQO]u<oC5jW`9(ɒ;bщRj :߭+4īi8΅N%S^-TlQY;VǕd|>ڵivЮ(A)܌We!`ȓ(Aoq( cpo Ɔ|^R1*\$؃ !zvzNO&S6쿻z 'Y@Z=(WaR*wPc>]ʪ?{^r'#,ߺt5eӺu]hJ{9"֑)P:RQ&2 GmfmƯw-xBL,wؼh4pT{t ea>aB%SsvȦzڽ;2$8y9NB[K0%5r{:kyjKZ#u"CB_kįʣ!B tPc *JMMVۄ.>N-ģӚԊkP&!L[FI?e?/ s>kl@N R傌|#Jgbmv#1vgQaI%d|We?} ə]ߍ|5 w"bV)?߄G6g9x> PWDUǡ)+gƩEת L yD|3m|Ls}5n`ڰ7Ѻ +!߫RG(" +#ӁĠ-9=1yN>lw\93:w/x%s/ Fnٿ|}p$`TQsPF7ޤaf%3hĞbFN e z)ʅ`vUPq)4Db_#wEx:|,W\=q("hIW2 4ԙgF~GZ%Z#~]z4vK5EȓOy&nq~ݻ6XKFк4Q=aꃌ(񚦐?KPo\lR@OȊ[_Be<6'#$,D809bm@x?1`OdUju#k zȎ|QV wQh^gvb默qZo @lE2 XiGKʮm1׎:*u4Qʈp"7r-"ER E2/7m =!?ةVo'ui_̤Ѷ Z8{ Ht peBa-6 t 瑕*CNs=UFH{tKs-GԾ{7r7DP&Xt-~ ּf*@͗޴ZUYlm ѓBڶ-GqbFv=ȭ8LT:SA,Ȟ ?yC<Z)}C~ |2]?}'s5:UM_z-w\cn}Shx?(nw$ju$̺&3܎_ =Fhq |x I[6GvУ77fkvnY ZA+p~oTAb5Rj/Grﺍ9aT0P̓^FNk+T|XsUA{Dy6u'?qI^AMRR\ޔ)dcl''-w\`bS4i+B2aB32Ļ-v+3JC֩esz)r"Wnᑰ%il_[HR0xEeaZ:>5 ;xb> WFG$O[ ӅK,3sAZ߂~U8_ :3^ދ?!g:`3;+/uhMݜx.F^0s@z-6Uu%ojC,6+oW^^pUv&CzqxBn;TcsFߒ` fn(x (5iObf 2jO?8\-!~DݧJVt}j#"#t H詟&蠿xT; QÞS½M PWe0ao8#Y+Y[D c>3 E(`z/NNζ-쮁t "Zt#CM$FT;; {Kr͠e;41cPÄ,F5 Q7v~z񴒫~稜fi _,:Z =×Cr)y2uib #¯2@_Afλ:Wk:9" fx%4a8ĢIc՚،@]me0(y/2(0@-yݠCs3s-T:FSV2J΅"O76UٷP\Y1c֞V-J s 9;^jƞK`*xhоOz$V3S3{Twsw!. -s:3I ]_ɅRxo8:eEz>0:ooui&/*\Ӵ-PŹA)! _q6e'`O\/MYN i5{Z&7"|2#kAȨ5wٽ8)SE M͙OKJ@utG>v2 D~ 1_ےv-͎j}/R`+z;Iʆ+V$}9."/dq=B?k߂:8f-Ҡ@K:3Ru6UKB!f]G߂cWEٮaV7!y*Qan*T]LkտvmHuj!%FRpF{i2z=@ՒtlxAĐ4{٤oZVG K0gyo=뙪,v> Ƶa xħ$p\]z]MSFq^[쨭[ol:3R?u6ha/\ bT̯Az  VKB1caRiܶF&{ hضr,#Nl3”աmkg/[aނs8 4K"@.tך0\( _eCAunGGZJtΦWBPB~Mo&M6H/`!l"mΒ-b_҄? dWSeX+u672^h'r1}@e:IgesL-<$0޲ \ /;.%RvNtBWLƴs\^˩Q j*r0֬UѭntEDw4m3uJLjP֮i{*(Vx-Qz Yo1=I5X~FMuM`A,/6zӍO@@h{s +M'f0t&-VLʩ3[ɾzliu} %%c(>oxi|DwBrV^ (&Rkz6؍l(ًEDaTq #if0;^R݆u8 bpxb"0 ikfSPx~[72 "{!Y_Є3`d.-oD+ǽͱ}% |$i޲^^';TS3,ȿ^oͧxh +6y8mcXc}n@-@[=읬+&۷{hޣXiѹhnc *Hx=uj'4]&n>K^uY?aޠ\"Oܗ^LY¤'(D jJ묄3&D~# 㹁y+W1p]ĺWqсIsKEw(׬P5e ȉnЬC;Ny3D=gB4lޜrIrEkd[k{XJMi^ˀf J7t.UL)HRT%mgm*Zԯ-e爞u^ېPx * ݺ˅\ 'o?:-&D}I'*|-zW'ygdL 2 C9_F~% ń0 *@@.[!)= B3y}'hiV]jF9 ti.-6&qPn4@`%&D:kJketfugK$ mlvWʏTV'dqL<7-:r}IFmOHZY 01x}QMC>?+Si{1FFr/? )9簘xrD,&Qei%0 wM5rv( OZu~|4)-3=z!uA"c$s].H>P˞;$\;Zȱ_ yw`03lQV2A8g؄yF-ˈb5fXwx|vA@@4т&*-:yA2Ż78XQd6B+E3ET( vܔ(]Y5GT)r"ljűXoי eA 朧ɲ Aqbmл"PO )!֗ԕe0=_]YqtS872 (/ ޡ}3&gw*l6--5jЮo݊әn_"N@>{˷KfE-e01rb3{n"  <%P (~+"Fr1KRzVx) Tg/ewuS dEnpV 1Rpj~EEKOk2cTb>4W#D i)R:A`&B^ T1_p=Τi㌩!KX>ä| ",e `i,?^AzEhkf)ƭAzImHk5S68r3FpoC A=ai^cMqܟS*-+a[o5˧ԗk=ej:%b/@-4d/b?k}.tGmC5TeҲ[sfV۸ŇsAc[рNC Ix߳\$j7 mrz,?ʼn1Pb!xK½ 24 ;hEgZ k})LRdc/DӖިBօe(6W-%vʷz* I~[YFe-ͅk>ާŭ$1Z Pc4Q7:~Fqޝ^!*jxi8" խ~׃;gBTH^i' _F=h7();1l/_y^Kq??o/Ĵ5ݼcvJ:/GD<٨[As;J1멮d r g@}`L)JxwqQ0aY.--oe*&W-/-Cy47haT#hs%/Abgb_S/5Zka2-qITm+$ͤRd]NN7>DN OZRjDd9=ulhe޸^,x_weږ,?aĠ8\vHM̋`:qLƭ~/Ff 5lDb5j ޛz"YcqnU" Tggk>}X(SC[s\EZύ AO$8O4j@6n \Eke@˩0pHzF^QfWUF-w!g% c%,-ԑ忴rC\I-f'hrSuGZcC??ƽ`p׍T:Dc!0ҺT^'[EҜŃ~C{e`NJ)*lJ:%?nz"a増I)Tic-ZcB1g]^I8LaD}񵫌mj\Tԓ-7Bq#$(\gIϛJ;sl CA-RH1vFm='515;!ʻBcā`F'U(|tu7lees&1N--pǴwPVQdsgdsr&?MX5\W+mvh{!fh@/f9Ōv<~:tTlh9*( {oY"or3p;.h\ND:,c%M!Xs=bK*,,oqR7E#̮D~*loV'.߹;{u=]rD`sOdt{ͺL A r4Ӎ*x 0|14iBR}Dxa+A_,AֲD8ԃo}s֗!!%dG[K4w{%nE+4 8#+Zvy&LQb0@%ق<=ᓐދކNL\J9BBPNקIOh5+$ի]w%\ 5mLjQ68 Z%Σ؎jj.)EqkRhmqInbOͰ2%E=Q\ƶĿrPďcQLu2f[(x2Ksb =K,ټmL%bV Rdݜ|$NQSJ}@ 7Ce$YVψt_T N >ѷFjc= ] ԝ?  `/L%eUZ`8E2(Ӫ8Ҏs;Zs0)'tbAKV1 ;v|1r)N롴 8?!G!9#thmP5ZD}4?GavBF[qsV].Ʌ@O;դuv"toxQ<O(t]-z 6#|Ћ'"ς([wLl&QTm~DsZToW=V@D t9Aa,U{_tsnOpa4/f@ٺ%G4j!}k>}z=Ҵ7GG0'}+`M)(5G3,p &luJ_h.Wҷ] $_q6\#Qɬd Wڣ͑՜Ŀ|l`J\?>nkj tV\(O!Re— Gp_ :rH=Oo[<"ݒЩ;cLE[=rWcD!1@Lٸ*N! axuBc/r )]| IЏbЈrmQy3#>^ eq ]iSl!3tYo; *a^N@7IZ>8~({b '[8N>P9-VטpU[_dSܪ{Ar=@2ĉAcJ2aѽt%מAYC hEZ^9ҶCڇ%y].I7.F00TK%`+=π0‚1W!" l L-zj^Vp0e(o 1"~c5^@uBFX)?+7^XPKd}M.a@I$# .}|uWԟNs+eV?f㝰#= y/e'*ul!Лڌ=ܥCpzj0SΚné\2i/"p=arL̘Te EqoY5J 9O >j̛LQaSo2Q,%Y Z[VKWlO@'j"RR-{X."rK`L4-5:Cg]D*t5QD$ƾ°e$IXů)s >e D :z^lJٵgVٸDBaP ,g.,J*Ї(`TwuPgSXZyn;AKIW +.M+nr ujeW?%# [̑(y.ӑӘQo4b謖x=.RAkƒj >T6$)G T+3E̘`d'H ܈HFK"8um*6vubZ3*´C> }݃% :4,̒`k$ê\lȵ^f6t&9^l/Yf!jFR/zH\@3]xֿz z0yB dj2Nʫ$1 i#>x,v"T^{ZT@؅[p58f&uFr\~ G3"e> Θ4\>&4A_#< NeŠ9^Z9ˁAYEf-pM/QiQ{\7S^B t WEz<LX?\B7[~K|nQb # ҕ!'`Is*f@|wAm 8@2ۜ[#Q?yK'W#R!g_] ʓ*=Qt(&dk^~`XׇOQ@ EZܡ 0-O8 Am }I™NS} =#z] ZOx{Ј<\S DT{F1]$|oWNH-g300zA@#a? v/5xdLjTR;Rr_qh5JQcqf3P}n'~!}"HG'8++)ymH;^={9Q PlЈF1?7DHp#]eh`hS@0S Zܹb e DI$Bʸ O`U$\7[{Z ^ABw=.d=1:yL X)7ZCM1{6/s#aGEU@ hB uha؟EBPOʕ֊#raLׯ$'^D;ZuLaֿ =͈O/ 1Ѷ2I wҷ9ʮ"Wz6{T ,LzWh0ff7fӬ)+YjEUJȬ$dƦŒ\U(p4~_݁Ԃ4ܷA:61I3śPcTѭ{T|p"s_R|tA[s G0 'tk ZY~0Uljz T. {_tnN&F2s,4 sS<nNZ>Ed%Yr `RcyxU4f_j5}8M>.-;7g/pQ\q'R]=!41_eF^l\Є|Oi0qɕ'= 6lبe@&^NX) z #n7Λg1\Kس$O wP?q(>gs? y8jMާ֑R#@ W-~Ş$2{>RF@\Sr<F)!kL֨4c.1?!xo] o,;̭<{QIW}u7y+),ąGA=9TN׳t>NB^eP OTbN"?ۣkq {: HdflqChXKx Jb@]u_1|ΟM5ǹ$](DwՕ,C28ǧ aBҽxOJmd YiJYTIqx,n.scG%p|社-xC?&o—~ܙ_Xmm>$ c 6lqJ'zT&LQO޽4еxS8LghS(1wr,NZ@ǀG[[rZ[7}ݚ7c2t~c$Bih/w8<~5'ÁwU~b/Į ݤqêpq" ;xbIyP؎2V;xL#Ė r[U;jkPĒ6|{XFc hRY( PG-?/fOU疓jlIߌ)^h( WSH t q3ʪ~N¡ZQ٫'rT/Gi2|ۄ.]qOpr^ Α ndށ #ߙ㯍tqZ! w?Me:X*xD~M0 ߏޟe 0㮻w@[2qQ,"-% ,w_٤hgT$[­2v̍58MD)փNYU/&aWEF !iЊ8 x6nxLp2_k%.AEH*YfwxRX{|!-㞚epTȁXտK}ѹ>oJظ+Dl~]^ %4 7K~0CDMt߇uI)6 J,fQOt/6vOlI^a=^pLSgB]M`ҵUV] 2M="Js]|&Ȟ~aځCE/7Za 2QY9d0kHBb|ǐ@-QeiUur緥Oc0y=]nQDlbdUd݌zNI2.JA:TTD'sC}+Bə6gX!-vߴ6 1ʌf^/?ChH띮`>꓉jzeCWyPۼ0qJ'o+^>Is}(.Lx ×R>18?ߢ Wi%hua^s4cV.[fTJ3D8` _~!WKb + @SIS:vOk/uYʝLOȎIN <@gQ*0}u3G W/c@6v̟vu7@TbA$=EQİZ :Mb5C(FY$pSg&sYzjpx7U6.P: 3v|OUp3i<~ {i E2}; s[ AoӱzCU#m> 2܀isĖy?my5R!jbs‚LSinz'0ѯU'>^V]ʉz0R7 %>$U:5ӣIAH9YU,Z =*Nk)Z4uDPt  ȏ g(]Q^*Ciz #K[+yr@1{y'3}0s@3cg 2}gU+6iy^w ߲H3.[u< !εUܷ}TȪ%IqF;i;y)_"at^={dfp?X\ԡDsCyӑ%:g+g1ADG%̲$MJj0=Z?TE$&lB7tZ2಩ͬUxyQzp4Ig";J|ܺ!BjFҌ\=XwavhEXI͋1c*Ѣ|(n2]3~D~1?nu1 ºW'} .Y@]t_c#qnS [?!Q&jUʈ TkNf0Z9/<ݢdĺuTX_NJ-O閬} 5Xˈ~ȝU>f봚py`₹kz?Mp~;(A RhJ%3290!"{M[cz `GܩK X!"u 1$3%@RwQaɼu7=x 5މ,/$}m}R9¼ǼNr*#0+'t4 3>r2ۮf?F*'8rI19}  =V)!6UA1!5n]"-5i6<;r yy xPoRL TR4L 70'cdZ1oU/hg/rInNF +kIl/>pty@DIp}z\0) wl'?18(e"-EcJ6`xV2g_tn#Q'|[ Q EӚ؋ R뗠yޑK7,T0yXD:5A. y'^?5v-xVS߅.j2k}k0ԋ'u.5f-b!$vKﴹ3ZǮ3Z$Bv:4 _ p!b5KtIAtJ|W/FyB4<@DzQрRi34!%ZSsyic\:H"@B|M 8$ IZ"Sa0`k4x)X8pdCZr[HWI{D!W j7B ,@۟msSr*taH_>E^qY%?q2a=tlx:c/ߊrǸGDGܨɇRcXH i`rbՔW]a!J}Rg.±r?K|iCqߊZ*!QUl+&'2oq{*˜vWQ qUގY٭Df>m\mLղH5z4DυJ1Lr!H';KlNr\@3שgM] "z"{9E>\ط'A?(H73eѤXR t>rDh\Tv<ݰQoOJ:@ZGQ`~ ~'WsJ^}Wh0O}w1V[ɛ@%A8̐C.t.o{ Zp=|M%_&up(sOM(ψs䟾о( iy,|+#?mnjHF#t{"A[߄=XZd5|XfGDSEWY=y-Y]LakbA;'xip;Uꝯ\_؜<>Eֶ2 {(F!Pa8o'B?Y2 D@ 1j aA[78y(؜`:ZR kCbs-p9^6aSBv/n1T b^=%bi(CXSV0fYml@/R@ݭ/MfIT`(f51QoTG׈`0O#ŝOwwLbdGj,O)-pջܞRAy +@E9ēRe[Wei30ήty_J2@ܰB]^' j6@sޓɆ ˺9P{con/pC m4j`C'v̤ nsrrZQlakSO;tOB\RTՠ0)9Q_kcNTIKXTp%є~Wݳ gbvVV=Dxx%.~NK~EmzgBP]n n$nɁgW-[Er=gG{Ě"ĊY/D{4?锭n5c#Q]:'#voȇ.i[r F~jѸx!r~ ʟscElhVX/X" HN\-0fdU4T!'!|fTW.Ihᗨ~H0oM5HoQ')h:uXt1&/[x0߳LEDmZ=Nc=W,U Ł[_Ͱ(FnqqwI2gNNIozb J%oI8m&h[|KEot4)o2e*Fn#ϤzJ&%$u||dap[AT`C3) gx}W$Hy)iO)ț"A q+5j~KB)O UwZ{V4]L˘d~U+Yť? l0LL^wK#t o'N5<֯"R 4,Ū7ғE D%Ev yTdrvLM':s~e-#OӺnVQ=Č1s0ިF kp<[2w+'-$#6$&Ve,H:}S+)V0k]\:O Ff+9pجF)B {!,V:]y"5ӟ~u:{t6r`єIﴞ rT{?V\bXə0_6=mPCTV}Tm2+ycgOAqh+J984׼ Ω$rФ:ƝzljQ}5''!4zń#~فS<1 E]% A%ﯫHgV"A 5$P>E3PH繏>ѽ';8͚P?. UoQmX(wҨO3Y[aSȿj0NS@UFb)~2u;9 uIt0^ݝ >mջ Bβ*+!*Q^UBzV \vح)ZgM߱ F*t,\?Մήj&~7a 5{e6En٭z*QH/bQ >>gRY5a$ /ܑElt5⍂&sm{G#9fS za/5 @2U{/%?+YgƬ uXdM&W^:"Ejwqy;(ђ&mq/vCY"e٫6wcw,&8!myI$lWzIaZmz]&8\fA_iEt2K 4Mg՟"C 䫗5zn ,l=Ըc=Yjny\YקsU0 L&-a4_e&e5U<]N*u\JV҅C+jWYO'd8}_uaD2od(CxͶ]_.櫙ə8xP5#Dnb^6lVUuoWIF ۼتG!q˕eGR;ui-&lw\Enj)%{Al7EFPJLS7)t^'@8?w=hG'˹ib ]Z$a#zWq˫|C/qc-OIMg4g6uޣd1MUUL-e6 EeP}R,d?e<@nkl^y{IID;p4= -לo#<3!gcW@5ZN3{@M[Wbr~ąM2Vg,qXLϭ(7U]a=htXDÂwdmWm=܃PWzn56pׂb8"{l3fQ!N:JjaI@\wEzT1[wqh֎5CS`"o`jFV#P*k<ߐ &x5W)kT!3 ES0+2WEMr8Q-mcu=s&%ES 6kE C/GmQ/Wt;p`7fCm஝nO~C- = +*eJcƚUkٯkw׉wUAorstIh\??/۩%gU_OcQM;*k=eq5v_psMgeqklȚ\z]_jX@[ V=ɕ'Y1ѲСuNIdzINc+複>sm,¤"!>ȣ<#No>ĥM,g#}qK1sTdZ P{}tהI=-Ko@7SX\i5$coUpģLw:{ςz{&.b[~ݔv B>eqB2(/5SVV̇UF~ArÛt01lՙ!W,-luaz8812C)kHM,16=bK9n߮*gsQz0]TD@5!#Eى>CgHh~g\TSōPyj#zsMy ql;էųu8 B:-eT~7#Pmka6ڑ͉29Rh7e5l̈́GщJ]H]6ʋ* obلb4 (U{΁ s+¡N @A/lznIęFlS3Z:&Vz>+ߘzeR4prmG"$:LF䅪WM@RBD\_h6: h.1,-N%^"n|hd0g/䩋v[ɭ+*Q ݧhj]6Xn3ڇ% ~yvWicEG.x(=<ãLd[}`=" ݲBՋՙV&(揗ElW3eUA7G4<;2 5[8MKp7l$Z}T+ BK~)$ȅw7#w)ySU$()l0=Ui0j_U(*AYB+0”|PR~%F4Ϟ<9qBvgRޞX@8hjߗKnWK{&d=om:ձHr%"o+^s$6n$&UHPnsœlҪ,l%O?I6n}3 LEI˝댑kfmޥ\v]Բ)޺utɛ/1@d6Im~B"կX G>7\329 QsL!MܜѾ,\x}?*98BWܕ/d8Ja qp}Y$ڞUQ_KpLtsBf(`t32;Ƶ-(A?#"nrhls1YFǑsd L˧C*|@R&[qt}PKILqd?'odQRճR -p'ᘅvL7 ;{hytำn++]p@$"37Bn`6B~ݠ;ӄ'k8$|!/J 25'TOݓD2/60y0is5cj6̸9c(57SOeMnLw9ߕ)9c{Y0#F?UMf=GZ"B_XiʫKښ?wPivHޏ8c+~@sF]R+/.[I5hjFí̪soԿk,MK'8aj^rIi?ف`#qYlx L,9 w,]ٝ8/|yrLM~CۮL>}kjt&#m]{dIo^bUeĉmX%jez9޸b:E bB!3A^H} =ܷeR\B0~azYKf7ȉʊ"q%Yz0|Őݸ<+*3U@uF/cC"Z"}7qI#)m cqz=DzYR Db8A'y֥<0)NP.] T;"֦5WnPBvY1^̭U/67Oʴo[SSj}dp cXwdZdJR5gJo 3e`bfCp&'RAoh%Rx1 F07Ztl3\xJa\vh.WtӞC†+-f){*K/ h-`J, Dafz5wTCk)_"x0lys>`&ǎwH-ƝЧY̗cYM `}!Y5#X\bػN.W6!R1)*1m%HLP@.{P*06F G M'vHEcÏNZa} 4P5o4bz m̻֒4Tf'TWn;7( d7ҼWr=8+}ot "F ZlI)'=f\<k :3 9-- ٨ ]aeDEz ^*.9AꝟTp3+7Q>̠GDFPVG s2;GӔw0%v٨wڡI 2 7xW[@6%Ϲ  N@s\2 P4`ܵѬkFꉅ^TjOo/.hl4::?XC?1y[bYtܕӠ1^1%6kPn=r Jv"ud12oa|Q&*'}hHs#|U4mX|O r$(?*t fb^.&2_~Il`H$r[8|ѤԉwxM&,LcQ>vetAL@qia)tװ£׻n 4 W(D27>r+C);9I?Ae?a92 VN#jJ5}X8A 5]sAC#?-2':mȞ,u@-@o"&=f!E˺_IW|&vQ4;PaAU| ,=+)Fn na"gf4͓ɨhIPH^[^D1فe&ׂ#uFbnjmM:Dlri6mpWHueOA8 +ɿ1CowP c@S=r,n41%nB`cjG&Q Ԗ=6f(w2m#Z4T08 d/K(2?㡒0bֲ˪ }lrP򗏰Ic4fD"4r!M;VjOiwr=V.U A'+ epVƆ!1|^N__m]mn +^^&6֤`34$5酗be|4J)Yzt Y`wq^cc2.GۢܖN_%|xۀZ/ƹ^R ϭqěY@9X>DWE@?!Acx4MPO9lGFD= ;MXn41;LxM@O3 Mo$F_m 5 'Qc'}Ɏ3^NL3pŲouBi26Tg2#{uڟsp>g b:3!-U-˄BٜU%y#Β͢]x`\_<)z1@>1?rn6hCt#C '#? fAvR$ӂbCKR4 GHFsdԬdE KN#RVq7Pb3sqqNK,b~4-\t_ uW*`ajMՍPI'c?|(0ZHz:9T!}Sචܢu`y#7ZTa/[#7X $CbVXقn-8֐gOZ5Z83UJDh[:{eI!l>&UZ3n~)Y&L*:|"Q5MfArD|=<ڹ [.\u_rFk&m5q{Oޘ qR>E6+ib%*cO>ybXKܺY V5ƎЉxw'xbew^҉od,yr*o^3IԂ7)A`ul?;INiFYA4qnR\lYF0fp 3i+ƓB#5VSlѩ~#&DZY u\7t;4|CJy7ܵ@݉UE!\ڝ7# qӜ'cB$LֶyHji1o{Ql%l$6 oHMu0q{R1N1@}rzU"{J:FҸZ2  |W1zibyYO*C-, ʔ9R[]doZߗg) Oޞ7\o-rW6 n',xxv?X.i޳YG SoʂF&B"EI=utob䷦8 M]I`O.WY_5.!k| '[{p M z@ ~9B uXr[3=F 2k: gi5 > Ox![$#q1~\&s0u5hK\Gi5;G]C~6 aOs#j"ZS^\g":\#*=C!2HKvL@>S?Z'0s෶uE8*M:P)1/!]@k$0~6kk{˓{Nj5 4ZJhA:wlRzu1;rʃqJUO뻡ܸ:|ȉ"ޖ=&fǗ KLczbѲ_gH{ CN'!Bޯ@–&kP)tF}욙{D%HөBAƗֆ젌o)V$V/L+n2yvÓ{ƿajJqDH.y}̅ $ Zh.-3壙&bV>Ǐ~.h_sNtJc}MҸ@^pՈ8zxM&A4&^\e!GJpXm~$BX~1eJIl,N$C9Q^va![0ϭ~K`ibV@ ߴ`w3NT^,yH- E/"^#XLt$/&Jb|ٕ`u><Ѝ?.93w$7S1bQsJZVXz9(H=6(6~h{FT:U<=;]CQR$W.Ւ'cU4yp];[HDφXzK: K2 e!SއŅLDٵ.fAZ; KJx{]*Bf` jb[Vh]zxgV@D?1D 0ہP C^WfۂRFk9rU u0rG0Ek"T4lq]7%NY|>.0|9fk/:{-rŵ0RL:>~jo⢍ZG{!r};u%urgqL Ji$,Ǝ8أG1!&rzHO& SPub:栃o+xcm>?k{a K# <.(<;x5&:Sm- Vg ny.N[VM~Q]3e3:>}"%>K.%Z`_-|7- 5'So/'L.v>ەYʦ*YodIKbL1ub{z;{ JtKOGv,n;&?*u^]}Nvx:S-f_w؜npia6*S "Rɷ|Sͼ2x0x X7LjŜN?К>|wBF t#|\σM &D3,xPJXЌi]35A#ؔvoo:>] :KvM,ǐ`,"%tTN5HJPNE:@|:ʗD=~sӺ*~){*g cV9}e{:!znkYt#۾!-m#<1j7 s: LpszmO O-WPS=[TG+Z;<ABWXU;`CK,+_H?oDW8;w) VgީMS7mK~@#g2L;9O!)+x)O3)Sf3,Ui~h#z:*5ӶIVL[Xϔc$Ek_F7]sYQQ>ꞃa&XY,U HfCk?Sr@!ߤ^A 3aʯX('X\kސ1}#֨OUfʹˎ:̆~"E&cGmy+J_GMA]7'\7>L-]=zTXтt@Er6-?h+$؜%OA%>";gg$'lx,;;Qœٻkm:^쨑Ss{6 `lZ4w=O5B$ ӑP"Ua+UIc t͊o;&~ ;,pOdwԀ1frKT_Yp{c` N!"t2cQ6PBlruYFx͚ܪJhS$Lq00 "; B@u HW]_snf)K8nWȡd\ #4e!Vs-Q_Am?tk dֆ]dWH]-(7w}OBE03ܔ_&0}7pQQn"AKR,M"6n 8פӈ #Pr'y_f_.TG{0 b[ Ny<Ce)1E™~'̮] [xaDhObSrUV V[^O,i'OM)[;Ԣ$X%ۑlc+}W?jGv2o  s1R"Ä39[fYL= 4OĈw6d;-y¼x{ |n{L1t( x |hbF-fٚwйSi0L 1󎭞/T dBQX}3r:/pDz#q4'X-%&]΁08VǪ8Z`#mF!SbMYɷM ccyqL7j+3IZ>!5?,k\6Q#h+"[GH]V)3EhPykwxu/5M~l5_u Q|J" vB9(-Ku`qMDFaAY"-]ZYT>p|ipLs/3rDSxfhYh0Iy5!قXdg9u56ݽqceyfbNf^O}~V|–O~dbe k+, c?S0d]^,ل}OSıc,[; <;;KZjĦEk^V>c*of^Oկ|ƊsNQ%cg~UvFW锻w0P Mn1Q%app+,F"ak/;鰠T#,V9┎#2:W3kM"[6 L KBph;ӬR|S">5u){3g.by."!3D$ɾv\,A%mf> VQֱGܰJ?y FI~A=5|ɩ^("pJTנ=Nm,…:[vKHXl^02r!%";C嗆®Fj^hf[w; &(`O~6UL,&an 3شJ]J,r8'B6qBXc@FMtsG^. & /0mԿ]qw~ͧ~ z[,L3 nm.<5kt3zS4/Iʰ fx#w'G^Eb[ 9c_uР;lqәpOm5ל=H)$F#' c*{IpG_v:”EFsD $4-+IW7?(a&"C(Dzg:_xsOq)Y/okX$K`ifٻ߆ 7uh/Blu_>#UHj.*u@M8)%Mb װUjmi_&JOj.}h=S@\`ϿF:3•7>S鮺IM=XSC|>r3Bdn%&i'ŮP< ;[sbF,V0L x<^D*d=tTd.t`фި5~HAuCS$>T4hb?AdIrI&6A?B:Vb'0gpqʠڃ2P}4#p-`˶?2pT7wr|yD4YqMr%?duf =Ovw(^o;ح582'~8Fl˘)l/, f@OlҭHˆv?P03;~k:!V6)vs ]/ $)/g՘ 2o–nZN |QF5kv1SK!>a [Yl-ƻ﹠kGT»>]rmk`HC W;)+ƫYG62)ڠP63"0KmCiJŁd̈|YN.`IkT*;f57U`c..GQY8G"LAvi^6}UUUAoh^e;i2d٭ws(ZnXSy5:*2LHpZ[AwSdF:-_"mOyࣻղEFG~&iޅ_]"Vy%iwӅ~ԙx+L :۳% a+:}_o ,eeZVc`R]2D7*bHÇ2o>\ jn=FxKǀGAEeT%ds31TݭÿJry#Tϻ!g$V#=hM10lUZ xGe|e}>Vaƞ͡oɬ}tJwxQ]CGVoIG,ZRR [WG,ޢ]F >9l[*FuMc|( lS>O=sgSAt?$CjJsAp T(?r,>•H d*iH] NL+ppMw*S~wڌL=2 +V&^4*ѣgfO|/&[2MVYt[l_@6^ 0s`GCax(X0עSo,Pg)%PxZ‼^VchʱQɼH3wɥ&@_& d:S:hS/F+ hU![`Sy.:5 I]DK;*n-\?U (,.~R!blqÉ?>Wb*XrDoGo ʡRR&Xm%x(_1G3EP\Mߙa.n7aM+G F& ~}Jϫ 3`vtIC_y6)C&b:#1o}伩7 YdXه:0nn+'}@y@&ÅL1"m)RIbu%f&/$`;܈(Bߪ63%f+TG%mXEZ'S~TcN+'#〬4Y>P(.L 6v27c8ʮ {-{Rݐyf˔kjw>?2Y,`WMXfDXvqepx)e@kEs HAZ禆|;:spoz.,`Q$\<9\VG@GNBi5x%c#lv˘bAl&_Nqx.ɿXA[oClzFi#FyP5r'[2GM`l5i 'PdZ0ئqo4Xe6o ܱO"X-+`W&ق2ivo=i@މkL+hϡjr$ޜm?wArs=Ku\>}m/C6_I 7ϝgA4&%I&O1j0a)ppѿ}jZ By5\/!eÏ=&̃f1 ==>Q{0۴<_atRKl{M.'Gwҡ[(i47دDR5!~$ }E(k@1K ?N*X1@Nzh@NAo=+:U&_%Zf$opo)cCJ&*CĈ'X鈄sAy6\` Ipzubc4)1o,פ(*՚X=@C)Ϧѩld(ӱ4G_ckėMzEԻ6 f޹?y 㯘¨~;rZMgX/0Q&Z^dEW9lhj{zW>3X^EKôu$('((` = -t!Xo?J|@_̟8 xkW-\έrh^TS"/xmߓu}EG2EI!tQO5*c4 c4JyQ[5k^ut7r. }ZYOIIs{A[ {-m&.I: Cϭc&r;jhM-fbwsY.Frg/ZřST{ssҚ+ 5,V㟭9zt7p&Y+p nzy0R)CxI$XrmJrnMA6? pyMpX5UjL$6Z 'V4+?,N)HpF??I d]Yf'͞M}}&䀽2HFKW5 b4c/*u-Gs };u?F[ByAgQƍޚ< `^X|,HDhT:xl nTƱmp*[Z0j-ccY|(|J3OhFLD]%08B˅XB[/^%k"4=)ZrEcn4_>tu=3J$?.Ң]p e)N\<4uU:$Nj 5"^^Aw !@%J SD`|")-Ϲkgٱ|"e(.g]7Yg$>հO@ eNf|TTOE*w92i+<[dFvG3,"sF C&EVlTL~8& }͐'WOf}t̰%[D,ð3Q?)XZHּY0+?i1 `#L8X?W2 ȡWCL< Ɠ"ѵ,6N\{m@md\`cp5„z&)ICnoi # b4)%D yMOoLRڣNc&rd2FJ *"<yw <̓h;voc B#u8#kA3,E)zY[c`tw$N v0ْQt.bc{0,Ќ KF9}Dt<)yr'*ٺ, cLoMEKNIH9jP]7TCTOJJ38Ӫ=+{q Bz?{Eb:W?xxk4DALxUCAWER/Io)}uY=ӫFy b_=25ktv̆) c*y?OĄ*U}K)ddU7BF+x !lo3fL/qgu)YGnZA;aE-Px Q 1g3 xW>=6/u"$j K N|+bV'fǒp X3ˏ='ІGou.d%%PT*E&6'H d]^ 65*#(q HI5Br|ڒvNQ .߸Gq)w 3f3TYvYsL{ w*AW[Y-$EۯZHغ;#'Q7,D5<8' @Y-WֲEˮm`NdOdA(!E@.:x-giqe┛³V`*7nӕ.נB4c'/Py0o/m\Y_աg|4M85 ˻8ޢTi݆AZDœƕb-mmaoT\Z5.`Cs:R0}f܏8|e(hcL X-J멩ä轙2o| - iؤ=Qa+ Jˠ%6v?ۂ6J$M"z[TɸhyQB+V`R >9Jt2U3gN9DlkBZA\cD+eY=_C*؎К_24㜧CYJ(>j08e,-Kām*}L9U1m{4kVZ]̰ rFuVBCOv'kH}$/gT?#5x( K'43NJ=aKFcJ-a68A{n[DC)3Ywğ'N)zi6f4Y&g˒ *9gnB}6y}QϾ+}4ֻAE?n:VJ)aיaȶvшNWP(y}G_E&DhL0JΪ 6Zh#)!/b my-XqH@v!odOaب18+ĭU5 lҨՓc8|L_g#)7%;i ^ } T]s4}SgއY!H6…Tu]N_:oOԏf*:-H{~˰0ߠ6`{,ıQK!{0\R= fӔyk1J?w9̠ 6 9*٢jz^T:W0{1}1X"Iĉ EPx\Rь N?SSW@r/)m+_x75H9;,HAq-_'etΗ3ATۦA tؓo%`  IMnA-9[Ջ,9:Iu?OV|{G1 !_ZC3"VrC(mS<@F5qkI$0,yzwX)fdGlTZ$:_Q+&t!G;WuIܦ/HĆW>S )MiQ]$w".ŲqDQ=F-ni~6=ǃv#,Zȏ(؛9 Y33G 50hyY#yiJFXܦ+@qGs}#~)z;yd]Q8>icǵu_I a.d ]4+G9qkUU)/r$iwvZTzw>YL0f2{ F-WnX 3Op1xĵhzq`u~}BٳJUs.X΂C~¹Z ʍ,8Dhh1L!8Ng'LO'RE/^j-u-GYuOrt;9Y5sxh\Z0P% |Ȯȷc vkNϷzF*FR}-%7I6+İ̙gǛ&%K[׊Me;xY2!)b2- L2$ reW29QϠ?aqix`aIzj6H?8 rwb9@:bWEVt,K1cv"o-mv^ q~gfQilf+GOFJxȆph hγ+/$8ß; |yLx!bLWyNUSB _B\ŀ=ƶ5Tyvr {Rp-JB1L0vHhk/!-FsïjOgl6y9W{md42C[GwLn.8E:FH8^>S2G#rp5Fk"8 WЮ2{t,}9;Xu.2֐E[S0Rj 9 TDĜ8?GynMYZ[rse$_H`O*c0"#lƉ׉)qc鏱~F`F_QSO&zx A㟔<\# 46( zh X@ %#bc+P7d`xao_!]/!:-$T)4?k&,&کR̮-6E׼cww/(E(^C/E7&[YG-E g-}՝.bQW¯&byaa{'kއ@ؗY>|͌kJTѵɺw8ӥ1C$߱Hn"Z&o=Q gEТ j/Hi Q `d۽'wB3#p ԰CiD6E?>^;H5FMepzw9?Ix:OXa%ݣhJNRgSFK:;йxH\TF5/y70?uYY'xOD,IC1Y0|>t(<M}_͕VhDwxI26f h3h<><ˇ¨(㹄Ńd-p͊DNArQo>|%+^['o\ PZ {jb-Ͽ]tڦ'YԳS~ZcbEFN7g#prq#QԈl-9ŭ>@ax ;IV|1ba2`2u='=B՞3h&}XFSϤiU7 ( 6fΐxaƀL}'ImY9 jEV˜5HEpgsl{G'Pk2`/SL-RJc\QPr1}j4$e4o8*(uw}; @F(QF? bi&8rH)' J},Pe"r ~w@a $WMk0Ab-zC8͏?E?ʖ [ALjb E2Ha_auJKgЪ m@g`aB~$ Ish_FiU*&3?MrM]A.BtH"&\+Jbp5Gb*Bc~y# w"!Lt6Ev)`bGQ=JxᰬidG::dFrAq4Dg|9_-GjTO?}EI)470F$߫jϷ d&"yXSSftU 6ơD= 윕+p ]A|OHhJ W.+v8%HN|I ѽx$lnFݚQtҗӍb_pitup'ؚGJ UBĺ]l ĀD{\)Gk2Z1<1@CJEdTVGͲ !ZlX캦V7Tз$I;Cfz$0V{ @Ult /Lg&Μ5c'u@7VlxEI^a|I9X3 #MXlP|:5KStz~*LhHa+BʰҞg9if{_l x²cY,V`-mKPӞHa1cp贍 ?vQ^#T琱o~x1z'><O< s/"[ZXAHz& AE06J)Q`.F/54;4@Fm+x*WNۊ܀Aڤ&Ƅ@:an:)N_0[N6zuqMkB8Rع^ ˈK,^9l@]5Ѫ^wڸ: 8ܾ$ :?tsWA5<"JS$ݵFڢ6agCr-Rչi|5A>юE=NaUJHD!%,jM"4y'P :RJ^` A+w21~;rڷo_=D,ZڰW:$fE>Qv\CΉjS29F&u\H3}?;Qnab.I*Wضz=8>RjG{[Vp~Un$ALP z=Z}~C@0>7TM}0.Ȫ0Hl돛Y*qU}1M']Zzus0<*Pfpa! gs@hT*)o^گfH D/gŶ[3z݋ -r(˿!_ryF:@ 4d|5 HeWz8HOE EP_/ )CT:6}ڹPbEh}%3@=+bt q{r(C?d] ^Un qD(`.>KeysVJr$4't1z"V!a~>pQk]A(QԖlQkOioi[UsF 4nW쏧(X|ृFw`l\b-=RĹolv\IJs+DtF;N O]`b6'(7YwKQA75]C<5 rG*~&xP[7 J/9lF(l̈8 歃Y rWOaث[XIo:=7d9KKa jEg?L:YgD`iby:3 {O&ƆMK_K{gJhJ:ՐY3Gp!3G6X.ΐe5~s:T|XL:Y%T@8{xAi%HmI5Xوcl]&qhR 59%*%OQb&ETTk aEE.(orԚd {MVkw7t=w$gƣHq&z*=e}7n^nq># jeZ*ڎ)Nˠf n ,[F'Qɲey@,_1N꬐d]~a 鑵8cEn-|ç 1]vea1NnԪ`7.|16 SV*:.f4I 0? X zwQ< yl$[%f+mpiӌ\}|IE z8`SId7I2`j'vOtN68Og# u}ni}*bHUL܎x#<+G*AUTeh- 9DKsD&WNV sL\>NbVаO3Nm'd-0)*DTp>r8cp L37Ry&8eBaHmgګa)y-Jk.o֡G:Y _Ƀ[!8DGX^[քv1Wtd)֠liv/v {Uƌnu Udw6kK^9$adC7Hڜ8AL \k%Nf!X(3Ҟ^Ы5Ad͖eOu6! B/G4TvTjRN+W<?':MlTN@7ZUg('vr_ PW#S'>-Ԅ*}ܹ8O y["bj۬4E̜@ή=xye4wl9H]p<(2vzUJyZau2F=H7tq\b_u>]r!wz+Si6}/ׇm43uKX9͐<**Cwn:xzUz:i!Lq(ӬnUW@cM+e \-]'\{bϾ>uo+;ҏe.\>&8I/i4^kdkES1d'e;(cLESYӁ7)*@~́]6o]c{b~Yòcr ]ɖ9H.TD^2VX3uO-TUj2}?zlׇRߖE#J ?^=VC1p~2A2+Z{,ϥqPo $4<8BDqLQb!C fB^':Ub>t_X~$0A0*k׌'߽}\籠aIk6!<Ɔ`l\s;,Hho>3? [J )1g^"z*rUܫ\,m rdFH0/{-k צ+w6"?n~+0(oA }]5` =Ҧטt.*sn"Oރy'oZd5ߜ/sxJ[_OCKq1W w! 'OۢLL8.LbCJJhg!Q́GS2M ȶԂܗ@TfPJB9$O)b 6@ux灅Xף?!Lfⱍ3Nvsbj߄dB-_hF NDג׻lhޚ1^x-s)hL#q,Ա`1Msj Cڀ؈vK椷ye8V/x W(p]`~=2cœj!"uBa'흒hkeOz0Xqn򏼲?C֯ ذ{ R$1kDKF:UdAexBz5Oy6ߦ` D@ bԙ$W4=m%غXZ`'۪9ң!<1ۏ_5:vՓ_'~.k7<:4d>%Tn'_d&qW&ہ$ (xֿ6nǜnl=ʉo3/ՑDSuEVrpz=It E#b7e"XO<zRhD2\^F fv^44o},NԜ M=]R@0չmhWZ|h}KT#ӠNU.pDfMf' "$wbB@.DŤap@7nMdӰRmYoib%[4&zw\炙0!Qѷ ^).A={MQ V` yq*xA]u9DbFnό`2NuWkp@ig%ڍEäzd\XˌR!*uy$]9(DuЯ3KD4[G3 !Hl'TkO xD.\?Z3DjF;h'UCOUq?Rg!N+BS/puӈdIxۭq9vv#[7:Sh{XBe̗):NW)>ކ gwO@"݃rcaA Ha(>@ "cCSUM L]jxbBnb9`57͏JR_A!Յ{e.>xZH]}nuNdٶ]s_(c̕eײOWq6`5dJ7\\K}W x>6`N#~{>y+^O,W-0[=}O(V1:$3Wjq805zOJ5.pᣩx,8XW?5^6 dE/^X<[?JyVC,< (WqU\k`>hlUoB ͕?gʼ>xqL0K}O狆|y3&Z[#2{1{CS @-0 qCۣ-FŎ~B50_RhD P)]4iFPGUX@]Juqls:m2<"=龕rXGkߙ3nC97 ރ%^}"'" -+ 5P #Mo1MU)9q:aR'=|K^FgE@VhKc3bvǩf!pԸc f]a=%Z)@K~@gnp57Ö1yc2Pc $3P<@v}Z4 D0InLx闻}?JKk{$>Bw:8΃]4q~OYQp۝Aη1f2{_Gsȁ@ o:l^8$85RT[JV:Ku5כfH'nvjLkd%5^ & 8<"9M7tM:gȗό_&JZbqtW%l[ȯ5= \p—ڝBËec\ҬbP&cUD6]`c +e*#ߜBlb_-~g򜘊&Ecl&Hђni!z/\;i!Uw'j+dG嵘yeE^g_ j"Lpѡ&UޤguZr~`TWE$A@n9q௎'In҂!U:JXa˜Sjj&,L~Vt= sc&Ww2|袽1"\5 ! ERi U 48z>3l; iq*( vd~Lw1/v*r"s.Or#_EF" @7ʢq{84>5_᧲՘^2}_oGo"tT EؗC^[H]QƱod} Lu:ўi͌?z!(ĚuF+yͳF\ t->KdO5BI+{\G#燐m.W8FYiU858$iVq'SNi pSKǷLuw( ng}Da^`7jv6HQ۴@y]^{n'~rZ*ّcDg/Nhm9?CPN:d&p&)r軞h)Lq__uWiP2;2>}lcň,w液}O fbgw(Rt[5^lZ%f.$fo/5ԮEjO:zIKM[(`48SN+ vvm+ړ` G9|,~z$pe.%ih:4cǍHHHt~۪:K79,IQ +" "CH20;7KlZc~j: 2ݠ8s*m1Rtv $晷MGvє"WM (m8YCcmQ-2?37>W9 "U.*/;?Ej% C~zfFtKk \ڜ S ~K~ؐd}=sKd 0='6F]3n]Uxx[Fp5.*,7J{4锅8 0az TἎ]`CYּ0xSFzs:-9ۂl׊VRT >]3~KE 9]Q R"Nz H|\b?H^fKrX}Fmad$ro]{Z-iWÇV\ y,<8?QB4ڠv0o_oZ$CR^J9> +Ϲ:.,0`tK_K5P}sid*%( 8`| {U|A/GTLS=% D_}g,c"@ aWA}Y Tߏ .Aemи!}72_^vЀy`'"ê% ^+a&2@e;/h@-{U ']Gw=vZ} BML73lN@̜2[*QUWnbNဵZZ J-+"!25˧r5 qACs0JiILv"f14 tm93?sMpʎvY^g,A9CFr|熾 (?rcߢbI%ۋod8+;=4]='ղ(q9d[T#&g쌻&yj U^w#cfVNOtk_RBmTG?s=;h ܣG/ x?VQn/#xoZڻQk=0}OҬ%FP #G7qdo cu.:mG*i*:' t3 ;fɍ1sqn𠨥jy?u:0<H~ *А"N̤Cl p̰gC5WvmSŢegXmӴWO0f`B<;tϕz3h K`G~E\ Yޓ ˶zRجؘ7hX%^t-$aH{>ċCDغ1@f(1EZANn3•8.2u>*F%;m`_U; )8 sC0Iؾ|j2Y+P{!w">R~'H{O?1!#y, m97Cqa3yˎB2B{̭MQDTq~b_.:x]U1ʦiu@yZJIz,]H04 [ZGJHSu{[]\ fm%2ڽ-#kcW?2wP :{t&,y=9DAڊYZDX$O fh:eUه-s-RvJHODwUDK`/mVP/L _R)g4&iʉp*հ 85oyEeYe6:FYxKkػc1 \HQ%7cOрP!574fO"Fwx /5aDydo;{-aU3/8%pj+b54f#|4G|NnAHܩZ8BQMXJr [#gdBq }!~YBXdӡj5ԇp&Xg% w=ߴEXyhNzԞ Ah\id}T_yuڞ C( f"H1 l e,,@n1INzi} zijۿo,G_ ՎL//߲̎ P6NN+6g [\7T JRaA[ czM R6y ~Ą;B@%k"kŚĈH>u|L١v0x{ˀ71߸9rɘ(h ;5gѷւ`Z9)9TuD(\?ir>/tѷϤoP\U|T+^BHjM2x|xQHR*a.pymwOv9"wn]SAAPjHsbgp~!_E sFM_6L ӜugN֋/׮1,=‹dSb.g+Pq}q(cMM_yA;a=g;\׾#^XKcf讀HHl/j@`vMz%]\RVMb-t2i'giǓeOSj섩H<'#AiM;2’ѢDv/:Y(f. yU[ve!)s4';{΃5[j}Y42O66,\`p #֐[ aBI^NsHk[_ok!a",.EI Mijéĕ흺PwK<"u* R(scz#4z~r6@vlbBܣp 0?h{{v_ezg /Ezm(\b$HY'pB_O0P)ipW];r4a'7.!OOz\vae7'_=`) y1$}ُ,Q]ϵa7YIɯQa$J`]c% z{ZxGf+rXv(6b:,[rT/:#O~gCcSMyj%Zt3"iH~g1,ʘ63|>L"##  @'IJan*'ֱTy^.)>FDE5:1ۮe d0BkAzKf%5̀[! j=Gn[Y\"¨шjԇd;X&U}b%Zuz8‘v&H6<qv9Me'g|N{R"_:O=]{<Xp}b <}] Qb%QѶԙFr!S\%LهBOJ͹ї? 8XkMnrDž6[1YMK-!*F`72oBc^<سXGk{vvy]C^b;m*=uy\0og kx_ t%8L`lgS_&7O>Owt U흒Be3'e%t X9wJo.)(bћN 3^].jmB\Ɲd%l>| ej+p>% +"£@DOwK (MYsu8R'oǃ5ѕ Z'`+S_g͎r T8RP6=9R򓙻,C" [gM8Z 50}]GDsA_{lJWh7 x`-f$Fbp[.]{ذѾJJu;E AV&XM]SX0n4*C.j{9Y#W\h\9Qk  q{|7f>E6D&?UKݼ=@w Aiف1,ك"uߨkX'Tqnpw&'Cr~;08BX:` ;Jz։eH4) "f^8Xe]>R؟h3[8vR>sͥZ3YIOUnka.5uU쒀/*oK:fnJmn\6##~w/q1xщ@ t P)lGgm:F%`tn%"i*Tg ҤjFx^FwmqEUZ<"Ĝo .6b"byansU̿*7|ͥ,0S9s&nL>@DR-'7.`-ImkrLOl(a*iUoifЩ/4/WB/J>6!ɘ ;!|¡V$UD2[i^ցdsABER]kdzz˧Hx1\9/֐mѱ@'s9ip[UƸ7DZJ#R>;NqT ӓoJvpcETV|u0Y5S>4rB[vq'WҀxGD[9`^;V[aU֏к)\"Ց9Sa>9)"{DEA+Kk&^ZgZO4'x|bE\`F xW<` Qj"lx6­$n/Q=,&_OoA|b4B\cYCq1zKL!M:G*$y0o3^}:ObehG(Ѯ!Bv<{Ri<e|zJ 3`Kْl>:;D I g!}?BB>"?ŏ],+߅7U- Q.vq%YXG$bNuE1T8cYJR(YC7Pj5Q^W_F]]O;A!OThsBO\T<|/.Dˤ-S uЮH>u5ི;wF[@QjJcA(ʓ[MgYr;$2OW`oFWCP]HwvB3o"s1;#taLBDC S!8xǒ9%:/<ʋDymrh69k~AL?Ѣ>y. dÁ&[vȵ8'۲KJُʮ="y$,5P@$% ˟a]җȭ78T5*7w( 9ا"Qe0D1ކ>MeЊGFRZ=a#>kR Ϫ~L΋=˄5\(?;5@ *]vDlc@)Ce,2e Lhv+PLҤ֙\݇K.׾)q(ιՑRi٩W+l_/K>$,l"l.0*w Q97Q? FL(#3+,l|rt=]ZTYОE&Y&5WJKAas249k/?ڷDoŒ D Zвp/!ZZuK>ax;OB\Z7@$}s7Yj/kg 9T^&1x(L]Gߢ<Ρ Gu=:6VÂeCƄ[KR l6>[9"O*#@[V%(bY~f}?U-KUvJ[##Ixw2]fm~i$Y8y{w17~2Yȕcij!L!}iȶ^ J!yЮ#oM㰩*m$[![MۭivBd=p^-7SЙ igNjr/j}ǓlyLw)^ʠ~ӘcOP2# -nli=@S/ߑ# ,˝`%cN(2M@QfG ֕&K1Mw`#q p "3*^m)=f 䨦81R6epF3һ. Z'o}G$YRL&v0i` [J>ʛ̭FM 5F2_?s,ta6*rm/Ot'>JGRgcrx/ A-9- K_`"w Xy1m먢Of>0?Pl}2m6q#? 9Bl(R#8/ӑ+akop_yI{_1I +VGBSG"#+T0:t8[Z $@5>*ByC)#:0R0Ɲ?`[!'4G'l|@iUR|@nLj(WFw=HT {3]fE,u;W'ۭɹšʇE *8=~\˰a7R)`Kdjb]Œ*Z"b09t$9@ $¿*%tȂW 'fȣճ#fa&p`!DAgͤtRHèy:?+W=+~3#<*H#4܎ v { |a|:a Bc וZCKj&BY?QP1084YQ!*呂yp2B@#Yx{OlpYh ?z[G]q׫)xdz̥{efM,} 9UKN=dֱc8gh|[Um.O_lfÙy?3ޠO nPF 9;+rsϛW+2w )#tl$0T$F[<6>Z(`yDS]Ьᑶ6K[h@#%fJA.Sij•1c;LN]W:qsU)lu{ݷ4HA޾^dH97yRx9=y6"Nb,AI]1'ʻSra{? ']?LV5wݞwh,U/||I>3R&QmZ*yyW4}DGxZhcY0a m6%WZUDspABsYj2?8{Jy!$[\6m>3b7565&Kxo_=לC 10pud-BH}oƒ]{HvN|,CJwK䖒r{f5kxLdlؾ^Bd ($su˚Cg1 !ߪ)4ϤS8{ZmI.(+7#ơB/tVcΩ\R8I_oeo#{o7R~JxlEz>Y+@_MyekWOYti\'`\R a,`R?_Z .)`u=4oD<~ν[WYuڽ*jhbHW.?{b f5-KfWՅ^a0ssX-m#bznp|㗿^*]lYQy5(SQl+7ƍ2¬_+8_y̷2LP=jr K09 !G?l=XAa*פ +/D9bkk/eG1*[Bi`6{&{hpVӛ*,\mDY|;}&C0Wj޿ Hٍ•dm(%" Y )a` K%O̞<{^Kfݿ*$M#kp(?dGdI2mAt )\pm#0"Y왒W/phf&0\o%.Iy$)w#sZpZD6mRlM M*͈ TLQ6z3 7k2voAz( (c_aǔ6~Yh1a#h2L\i;}"Z7Kk,&ḁ >y|<,Δ=KtNjJi{Kwq戂?dèr" L`Z E^Gۀ,Fk_ܒ%KEK(<ǫ)_#1cz{Xu=8^J>[gZq4䁠o+-h>!v( SƐfe"D'cRDE2y:kl(6BЇR3QX8碫_D?O$x£ 9YhrK{f2ύxG}Fa/օBu4ο~4s.`4rcqxh(OzV7RL~(@xІBq,0\B~:κқ/Yh;U;a|D/Rov uprK-y6%Cx@ⰶiACz6rjiFNn)W 5d ó6)): k20/o~fGFQT>1%U<=7BilgNկ%&.fg{UR? ɳpܴ[yoWpx`^7qm8MBd~/;f ed3oa:A%S=0yDO_nb7ᨭVJmMyOt&cuqZ Ah6lP#]|Q|d$Pi8 f.qqQS˝3TGu% /*߼l>Pu!zLkaƴ(+y ȐF#š>G{饺*xt/7)C" e ]s[胉KZ$kmD#/&[/$>t-CC\M-+I.+!0+՞Hvܻ&i];zaLGB._='dr1׃| C*$߅ǦzB!Z(j23!D9kqǢhk`qnW?FVhDľ(\}Q_Cqv/w(4 ^#SjXz g&+jS}euCn%s,l`&vŏ!Ѽ*6m6S'9ffB4+_FHX+;v\p$T>®u̳K~R`LgDNZj^:pJ&방urvF$,okO~2 ѾDk5sQ<~C(남ܗϜh{rxNbIv6^Ĕ|w Zo}r ~:5@Ny/䞜I2rrp E`Ul0lLZ;0P|[nN}X2Ǽ}Vl_>D5\>_Gu!La*9GKPs E) =gLUؘё bǎ5'^c:Z`J yZ_k Xte ܍ cciz{@g$4ཇ K -@'\ 4@7_E{+]i6 }ӐEdSXmvnya՚ ƅ{:vs_$ѐn_qч v]q=8de\JȢxW[< #QfqSe|SWcgvPӻ@;)@%ߩ1 kudK3'!U mz3+_9!O uc6N/֬+WG m7db\~0f]N.ja9[c yZ"~="/Z@yHOGŁ[7|ɘp[#qQ466YH5 OϊJIJ:p) \md0iA2T4[O2Igf0m\ub/ʝOaAi,u,b#D` EV,) 7Η,7٭e;< ez\ŮD;Q'ܕw "h/s GEg ȳzUsֻE\ 2AZ<+ طsd7铀5f-p?m^w(H*_A#GU.SQe ͶdͰեZ҃Ӽ94ך/\"Y\|2 Ӷ?lŇc\s߭2NU%vaJtЩׅU#!Yݪ8XL!8P=xՁ!d/6z6H*Z .@޵O+J)s]'ZR?g?LEAM3M@I@oq$aC`ӧFzʪ/t7ie#jdulO^ar?LVZ؜٭Lg-u/(CNg5b CVNAjĉ7ɠuc kT]iOX!?5jMu2:KF8hrNӝg6vt |n|i/=LFWw D#A[/ݥޔE_8jVWR16 C{b͆:XP$G=Լ8Ù<ZGНFUNS'>TCGf^O$i_[Wnp&itf`n$%~n0QgL6&ת4s:-ɲPjj֘,Mu|`{hQ=?!vK: B~V$M;QHjYrpȈ jD^ 6jt/C l #P"@YA +#f6Jʥ!Y7CqL%-ZH3*E3mCR+ɬPO OT\Vo.1} (uDW &K u=ٽй<Fv#'j5%} Wm"+v^%~v4:i^f,zӔmYSSFmzRyJ *"9;'fF<^ZߌHvbv7(W1{>=t|7###u+E Kf ߞ  [P@VOϙc:BV:3\+St/%FǑ`~}o5Z c90/rt ʜp% ](0hC{?f>2d]}lғa 3.1N2_*8#'+kqp裕udX~8=!ߢg}C_'IT&xl䂍9)&G^/=-fޢnk;i*uM`IոM$N5nrnW;ҾnAeI2G=ss˻Blf| H?a釅?ܡ8u.9}xXBU#(n"A.X&>e`T3j:RK4䲼hԒ"rǟҽsȸ;.Cb\;Ċa!lawACcU8`L?R 9Fn1UE{X,M@2Os &`iSבzG'Nq*NEt%&0.o]pq uq,Lw^ټD6C#>s1EYhVv4]a5\BH'+PxXkZ"sΟ&h8e5ݘk A 㧀meZcE'Wt4r/<#Bp{;ZGߧT~6/;g@zX{q=k%~EG8e:*~(P2۷16y8n  QÅ!.UAi@Ԏ)Ze d۾~wW*kƸc۷ۄ :bF>EWl(ڌqLƜ87rAiib h@#Rl` DBf bT$2GQEQR1$A|(]E cј_ԣȎ&[*̓ye :a V Ae!#t6^vȓF@t8>r JkWrp}~^8?Mu8*F?JH[ P~gE8ēgS^SH*FMml ݿ(u\\ȰjDlhIAubя)'d lYn6^6]D\l\6Dơv\K8uiްjYXR?\k؎uO$!mq]'㚧WE?1"ZBp#n#/` (c;;֫Q1kGsfm~Aj5\y $PAg >7O*scΛ/:l;-NNj&*]2~,EA-;?| 6&kA4%=.Qt& {CىH -Z<|'A[P)9!zzy*+&g"m-n=&7~KTic{a\_ne v屮 Ǫ`y,Fcs-XC9j{4q 1ˌWhC>ƣ-u8nrɊEyGE'*WM*AS"#_NU%'{t?dk wg[S xz+>js4SP C_f%#f]6)1_⼚(:t5̢6Vⶁ"ԭ\w,@g;N!L(!N>ğKT:GnHi܁wC. 9MJyp كxqy!vcOKRD0(QXN*oճy$u z-KaGNWN:ߑJ<,CC) SgY qPml鈮5KI)؞m29슽4jéݪuîCYCbG;iq=B\+2lׇߚf_wl4UvaVիni*7^ -qp+-PWY(ZFDT#C sVGZ q*#eq!#R9_-yT7INkd]o k.O&  +,u',e3 8jwW⦜EQoυ`kC͍c3Nt(iKecUqy6U^G\K솎Hۦ;vf ';&K}Q.Ol a ҪdT!]|[=j,ɴcDUަhѡ&<ңzM&O[-l/zǼpo?N3ABi\,UJa({+Wf,FW &{ҕ!RDh)=Z]W;Bs1+L2*D"x*/{aMȓ 9&k)訵&1jDN"Ѥ:O=Vi\y%VT@Cd^NA R]-X'tՃ n#&ެsyxIbܙY]A RhԘr;&'p~o"|Ùkf}q9ryf!W$ScRs뷭i?9g+{.:-A 2?3*M_Qb/olKXPrv&|]nZ\YQs<[HX)o]9 cP -, ³"OgL~wy8gboGgIRcPAf<~u܍]TX uX9KOv4l"I' f<~#M\Q X40WN$aaRBR:sl3L, ѕ#B;fA RUf3]f4.tʚJmG-dvqaYC+_=CzG>  3 b,?Yrr0 b$O;2<ڗG_\AD(Ff/&Nx?'q:~:@sK?*s6~t'2_~֝߶C6IԿ9ꁭ @7u2ޗu&Az`x:|j`:So;k'AB(#6NnHBgn4t4gQPS{?бp~x-bp0̏' w?!1f ?4R*Y)BF(3$Qɢ4EڏmA}۳bёwBc?A8TVoX7՜'PrG(}lut݋Si7ҍx*3WF<|?Ҧ_¤3Rk8abc.n8BofJ(*H N#%8{pUÿİRiִHKz- tV|yA^)'ڼDnR"dLɣm +\Ӯ"Y;=}FNT錵فlG.|dg%-*P')ǤoNtenyY,sYrϭ{4*o Kyߝ!gtF'JxG8=j7u_hrdlހ8{Ǧt~bb%~Qex3l4(DH,l;Bͬa)WDm24;Hl ZT\WIU@"ǰ? }=ː=۲Bl"ڹ)ޞ8~(Hh*N~U>tN.բFntmy12"+ÂwQ;^ )4 pnDk^Z%!;idtnSe7lR!oANAke%d nߜ w4,QkNzXvlC_> @ {%6F|^M4enf9˧k]%}u̿f~(', C{Z=lrRQB5e\V쀰:^1Wm1x>ǿX#Q a d+l Ǽ65K`_P.[q:htL$Y})HzjPs@ e%D~1AW7eP;}Qp+VhvOAīg0e*J·!I*/*"Rhm7UO=a=K?Rs~lcAY+vZH$Eі1SM'x'lV󭱭M JBEOgj/oTFVxZLopTy8&'K̥9Jdi5_y 6zkjGkE]/v6ikX:ɉg|gK\Y%*@|mvv6ay(f 대'|HDF q>f#]9 )uű؟=Xdž&)2b^l(]| M͊ĔOEc5a-ЈI0MIv8 f pzCfF=3լ|ʜOP1skBZbѭKj~5 J!0!Ê-ٴT cRiPǘn8;8߹PgYW|i嫆Ļ%V<WGDm,^9c8FcqlsI͇D;:4CT\NڛSt莵"o--ם9f).Iq,sP3F-gs\ 'dԅc74ݚC[)TZ˨.)@}lJȖi;f;cfnmثfWdBu"F ;f4v-`0ɯ'ocuWVOk/%dħd8\J+i>"އ)z&4yA[ NUɗV "@e*o2^YXk7;:ֳ[U|^,$=hqx<}Æ˲Od}m^ar ꐼܟE /ˑu8O !)4c,.݈22@,G*=8ţY!-wQ[MzŨPS|o<_1Xž?Gcid"Otk|v䢅n_.s-ij:WdU0㤉w$$kZ"h qe/HowFVgSx B0$s_ g3eSl΍:Cۗn+ZFI 4΃>:]g9KmP7ƭo yŬ년RNeD0TV 7,bCv=jWbSne)&R*s#Zb"QWe Xw'EpxWPYgaqOMS6eXPFi;DкLWk#Z[l7f]u&xnmZEdYP{ ,S.ov"DY%W!0(q'nj1%c|/O1Ƀ`bշ{AVN+K*8``Z,r;퐑NJ(RLv|D2BF"iH~@OW^:{N1,^S_7?hRƎقaC8z9L^49P@ M$x)Ow=&<&e8y9fw#),}C^*JZ!I SMp +X)A  DԦf¢~dPnīAtU/oG"&A<;\ o#ǎʧ@9'LM~[)g0|CCG[3՗!'|,"'ѤeonfA{Vxeap ~-55a|sSoFW&|ˬ;HH K4#o.).prѓC B[P^q4e=\~zD^a5(yw G6my{K;Զ""[E!~AyRP,2|QZ,oYdDs3fD6:PMvb:u5u Xί ?>ꭵNe}tIV ∫#HK,/~!bk~8>Uj,w Ȃ8*P'.b$Q$ל%9BAΑBvl4=sSlodVd?vV/A6cr:ln$$'鿇fL vv`"aDh xH5rZ>ʙ NTr.!\>Ek#_H,'io<VХl?# v׻Wke-yBYا7d/cXj[L?XI#\6%{6ԓ݊BX^e ̶b.[.#Lbν2Hp*l>aoh A) +0/pעc?M[v7 Nx7mDNhx0 Mv\xoWfAڏ Lzp@9{< TtIŋXOx̢'}pLK\'օ .Y}owB.;JF%w,f< 73U([Ea~`Uyѹ{/gV{ӫ f:qPPۦJx~H*J̇j)^vn, IWpG"h~$8@[ZwM橭8unXYHG~S&(49thʱ>hڋbX[^9F=ڌUnz "$TRy1w\usKus@9EçrOiBvcE%a&Bl6ƒ?'z25.r8N>ճʩo/͏qw.~C94WyܯV~!7$[hj_ѵlZ⒊&2G% Cq7ϰ<0yyo(1SU{ũcu"Gfa:OEXF1sꣵd%)M~e_wΒx0ĥn u0)xt9S>D+"m;ŷ{x;|(qF-hL,72{Y(99+HFSfD]f;?Vv"ZDgI75 ,R6QzM?ʾ*A0l^u')`Ux]6V}ޝc! ʱ/i5?E`i$:zSih (>c i[zÙ+Cd3hJפԼTN>|åQYH3¾Io6!^&[&"ߺ&`鷪7UaJ)ؖC5LZD8Cک.' g` PڎˑO rM-BM95Cd"SP {z!2^mw؁QXݹ'H -ti'|Asx9 GՅxưe{V5Cxl ϮK}6.4pZxt6xv }Zb9~zu:\ho\GѴXo֚ZL7FV?3@i`Y,OUP UW5vy/nEv4Տ1*>q)$ZKtOȜ[@ v0 xm[7W *[z U#FLƒ48d¯*QX.1 )s| .X؇\FÂOog)< kDS =(>PEdKu|Pa\ CIj|[#'?TJDNP\V>^?z榻{ }\DX;"RHAn0XV^ NiNB[0~ǖ$BWxv7<8rdX? ՀN]OA !S/TъB=æk]!)[<`W4/j|YCD.SǶm띁9c;s%E0Qwӗ,OpZ{"#(qvEBpF,BTX*UF~֓}޶2u~×:Xn{ndZKoZ>f5%S# `扃36度u̦j$tPMB;AoKo.qcRv񳋞%D oh [N$觋PXn07wz顄-V"l(n qB&Ogor:czwSP pjoD y Kst"QMA(Oi•7YfwQpqaڙ,G وX/kGׅa s*O*ӑl}S([ĥ!&"2IV5v9EIaKtxɗE+Te`[IHJD悲PsHg%f1v`B>E+C|cz2)ݜӹxRnrDwV̱A-.B S1l/9"ठ>xtP'xr{$Ç݇bCe,xMȌs}w4`536"qj<`, n\ߓ7ylSŻf{EW$PFFYSH,:!\hsj2L9xV=Ճ"k#Rj[n2'򘋓NRKDLȑGMϚҗ*{%qdfY{#wm:B9"Vo:0.X<ZsjsnoY=֫7=fKѝE)8YNNSaȆ쐂Ninbw43Fw CM72#5d Y.  Ec+COy=]uj YF$ JnS~PS=p%JC<3]K[HI^s4$fʍ!@a+@/4Y'r:7[ ,ۼDR~gg7/hf8Nw'5l.z8M `M0I,A W(6AbeWsg?r2si (r8NpzFu3PoQMKdiޞ*jh-¿dBٛ%&#-9$+cye%ݳg U8CY_ޙp~$僴3&0#!?}v a_W[ǰqt./g I FDIGCp<=Dm8l(Qr5*{]pNvX@"i 8f#~t+*[pF5Nqݵ&d>B*:):)"I2 q9GG.Tq U'iPubRw=X̽F"b}ZoʐLmzyڶG c;`\&%UzVXyÁCِ֢W=}i9=ͼpJ?&%O$3it'+Czf.9deCG?R6т\7о4w J"^lg.\'hgO>30$fHL4}s=VPaxҮD0_qC*h;O%7@% bh3ab+ F }`}`d}jKhbva5Gbe#"[V:km9 pW.mz+"5&oj";)46߁bN~V)Gfε/@ ,2:m I)i0zDxXxf P=w.pK^ )Sh%d7]%l?= e筙!^a؂e_QB>3Nw"t|֍Aϥ(c~(r_ ngTru7SoaƦ)hEn8h!n aml.rx%%t.hʟ2 :tw B0+߀T+{ Ҫ#=(OMPEeNtv`IPM0Ï[h%@`-x0kɵwoClrG93҅6)gd^qbȦ"'r{لd9yRZ$[SuR,_hLsC$T@vb\*ŏ&:gUУԗEy{2eq3G#Бh~<E~KN16G̞J,1!;^HX6Fv@j`KF +_37,8хkW4;"d!n,Vm-Ƞv%DA[d?dNpi`Esw.zY8P)JTzDҶ/JP ö4+#Lճ#bfSs.mgxI!]U_sȅ gܦA%uE QۮsP1 OrQ $zZ NfwN^aZ` 2>~,$j$s%Q YWxEl\iP)2zGXvz/Id0s5Zϑx*oŸZJo '^5c#Fw`֨F{ ,0qӆ+6 0Aȿi!3 Cĺ░N- jb=WF1- ڷqIXڼshD+3(74'"]9}>oXQ kJ+XŕWfAWљG"մnW7pr37pn|VKp͞L Ry?+(&2pi;zxA9dgSkwS\yʝ Z:y L\ϑ\`wCB lr ja '8{0SkJGӈɠ Z8AGxF_ZpI$UqwIօ>9ui ߤrbaaL5be}ng iuqK&4۾[h@K2BG&!_A!j҉[^ y 'IM Fpq_N%Q+{W+zqNle!?jO,y\=d|!+gN <RJ溬A8Dcq{Nfʔ=w(>Q_J%@cHK㡑SW3V3 Ɣi;bx Q 6"qby4We5w4r;]?<շo@"C5ͩwmcxuQQq .lxgJ1%IFPp ~Y8je}R·W&cǿWdo [e" (y&' Xg!(Xpuo U2GGJ&3 YZۇd:a";0\s8GKg'3IOG 3P֛n+f5ws,3(-f0j9O B`J\\G% DEKD stmz%M.ؼ450#qX;򥂮yBs(G܇F\ancC_HaGn!u6 Dx{NkGޖѮ6$ĖW@ҥ-œ+}A.id/~Lݛ m\iVr_W52-Ew,jvJ<\eX׈8syyLiezgr%0MfQվCEfutt>qBsm<׻uѧfѵedOG+]Irqu|OȒr{Qrr2<4 ɩrLCTjHĬ`2 +EMAVHO! Gk">vkX/j2z%ܯ#\ Ʀi\^ks|o"A5hRv?[/ BllQ\irDc,T/erD3"@Ros!sRI~(uPk }]V'4P7Æ}L2ۂXkf9=4o gv9B'AXov^9'U×F8uzIp8d)8~R -jCOlZ4H̕>Es n|Of>YK He]k8|Ejulo֝rKrkF0M0"b,L-wT3r=Wŭig洼G^H+ ]Ʃi_ ԫHi[u\?–]e(UϠC}p~NɳdZ=V9i9.Ƨ'g:} _1E+mv ӳ)C2,vjU| wnMJ,}R0R_[^"zMq;S/r>H뒜U^InӴʈzMaq7/%`^i"9,k6zfʡQ ir^`[)M7٩3D;/ȼW={fe>ZEB ßӮҫWn!Ԋ]s*j"e#u`|W XCv"4} ZvpFGT&e8 9Uߌ/57|TO^vg%BI1QY( 1-,jDg i$f4`s^3/rUA|+CGΠc0zN>,ιbׯ <KzDQH&QYQՎ2tFG.$іWrJe渘N$y.U*lucy$F^}>Y;.3E R9^cd2Y2oШ{ͤEɭtV{qe`ڼmv@ =Laޚ,%N"GN*uZO3.9B։PzO޿}-m@(_95# {sF)O\OtQҮIÐrlLB!f꧔$.jcɒvusFBWs3vp)=0[NAOc `FuFin:sv@D'BS=8'4ő#'=@qN4Ũ\zׅ%~]|(GE|셠{l58@^ݛ<0ȹ3r=Xj~.4|6/ڻzߒAOv<_kn_%(1jY/Pb]I3hKXQḘ7#_5NyGHF.=2% ?#:DhtF%˶VA rGAqiˊHp̅)i_r JID%QrĀwA2tC}%m{ bExgV2UׄiaI&|$YB{]@{x p᥁H@|>Ct[ }.j9^qҸ6wk9 dS9t#qVR4mrN[ֳ)]J^|Gݣh `]jB+F~džinL"⺥/v%>bF`w8`~\س.>X{_.$6)VXF:${8we~b(3~d@"m4wذzSaf!S&gF;>A^/z!h6e ڣz!u[T9-@;w֦bp ĂGFRA!&D|*+؈9=xw~{+頻 G~Hќ%yX? d1$1E#3}(w x*D ٱ6@Ivf|eł{v2ULN,A 6nEB0(.rA M QhP#BWՐC T4ĺcۛ'A>M.2\kf.AI +gYC1XqSׅ &&~Zt LmSv8NO;oME TP< T}Y4!HS0`|+~bs{mkQB߂xCO^h[UMh֢-LTI^vua*J{R '#Eӻ4V# j Oۋ6|N㺕1caNqRZ4aJ3 ]RH1Lbreեz˓i;p@m?HzKY(6? o^ToЈ7Mm ;sjMKD]fht ĨR5:5~ڦ;u/ p )0+gݳyG|DU Rk[.d +L3}T(5k͓+7m'<@qPa{{YXJFJ(:޲xyץ8v*@ @YezV-dp4s( ADi(Ò!VO?6!:u lUWt[eŠ `VyY=^.O(ߎc>ytqjdbD"y Ir,IڶN+`}= w´'q$|֒1rt qiz;H)>P,0Nd~PM vРd$̱zD٥+5Do>].k'rHJx{N=$Z}6RHƋMh_K_wiEhM܉aW[4.x&S#4譡 ^[_B?="HRkj L+i0hϤyK} L&ᎈ2#"ޯc]_ yA<@ qX-Cˊ T& 9J l+Q+ЪJc9BCWLtd$.:K=)`B~_N TY0ï/gj|Fmu6fJSjOM@vMm ɸd| }{2!HksOﰿ#.=݂9]HI 5ME߸>L> U5kYeW֨%v1E0bK1ФPV E!zʡ'dE%vOT,2Į(ђBmGsdj/qa%B.ǁ`000HV jې5>RVҩ}ϗk=%l04%H\\Z)3g-e ri˅/$`8w{F:|)6 0 ' ).;nRkPW[QޅA},zښ-j !s4kZ1q_ Ūk0==Iy $498Ru!)g72ok3FX5c$  ۰J-4tL[k߹Y&V~jbH9 -fuU6 'FXS&!vL\ch҂-R@ zX5LrB B'Ҳp|pHx}˳"+dp9xH)^טE.;Ѹn$`ΩWTP. -HFG__lZ¡zp$t2Ȥ@k/]KSxQ Z׻@ BޜNlײ7%DYCt;Vjxk2MѰmV, 6z-2$+^g5#LRU8"Ů/+8TAJUFy1Y#Fy#knZ6=0>zOE[c3CcS،Smlkn^T'lʂԛ`F>=fe??G mrу,Z*_x/MzMT2FMy?ǯĄ8>Ͼ5eW$#&C|bՇ1! OA4uȻ Or3ɭzbKsŽ'}ܠc&u=Ͳ@*vy,oђeiTDvqT.?HNk2ծDSR ыꮹ(0}Tdշ9g܊ZHټ-Pe!k8trfE$(>Ͽ;w~1k$#u[v/1j =ZbQ"Ty?F!}G+l[Z2ؤ?Pxs ߟu܄b -$*VP6 oM# 8-Lbh\o/b=4 *fSy#Q'V GN˖ /3VUf7Bj)G4s ]pVbN}1Iw%L/^]/|撛˔? ߭?}-.񂕡MᤲeU)x]]D2(ٲO#i+ ",es׊qLIݵ榤Q I 'yD$$p5">hƛ[-s#Pά",}A[+B: sj8 ֚ᆬzZsqy:>6{ziX(DaZ>݂6 ,Zu6Cp_n8gN^R}Hc =}IVk_?)=uBj+."lH؋K}hbe/yaQ^';#XZgJ*С<wϚ;)%TyޚmJA8M8*GmI+E+YؔɎ;ӂD^JGMe&EaߵL5>NnAT X( ]F >"{l"3*Pl+G3uh oGlU T+;Ә&6mBcƻAGX? [p,]fϳj&d\e(#u\C.-ayzi;ոݳeW?{Vf& } g\ f.T(0 CBov.YwξuzbDG3Ҳ8 ?BQ63 X#3db94,aL5ƃie@_\CDfCJThT:Vn;>-¹T^wdq6)ny4 Z. sۀ**8fz%ц=k:h>C`S9MV, !E9<ѷ&5]CFź-07 ʙ_P2mEױ~_덲&vp,}%ac5,‚;3"u-M474PH]3'=rrZFO,0p dLA-Xyx ^FA W- (yj a/ή S(3 V s6#c_gN:[GoC!dl9>>wi[5snV?|`85}\ leBl]kuڮ v~d[l\GuJmz3IèUaGcͦ{!!m,Yd0HfrTw: 1h쟞W,}Mo2u!V0q=!w1(ASaq0%|1a"WO{soz$.b0VgFKqZ"8GlUN80 Zl*G)oDK#+΍K J~Ab% I$/\ߟXa.;,k(M7,Ra6#__ڸ+xQbMc+IֽCgG+^6q;%lURqqEִ>'l/ nʩ†S昴I9n2T ǠKv$_k ۠NxʝW\4q =(+d.8"lcoun#-MknCcA=CTQc?qY G}GYrm BG(xLT`VcN *~3#rlUcḶ>$KN QflyAF4P+F7x!:P.?xG2Ӵl,`<_t{۹9_tw12ρn*}K)T ^9E m&1#e aH$cLSSHH4@p{3u!Q79?wСE#X9mhˈ͘UJ}`Hd1PCO0rZ?][{E8Cwd|oy9V azg g1ڤĪpU lOuZIKwwZ*eIȴHcucq͟#XS]8‚x| x:+'^2 %e ""|` 'R%P>HG^:NWB#ޢ{^Ҧ$rkYhW|p8ҟ{D_|10,m'W&xTяTi@5K3j@>,fum[xȭ#5F82aQ5Qs@ \UHU([,HPiڍ%M> SKy5 ?~l9yy A|q}OU:5US+)0ŖsO`mo8'z̛[fD֩D*]-Q&wQbߵ|>vYh;VW| d+e;{gD4Fvܧ3ofUO}bvHnN(n5@(ŎÈ\ 9x"ig0bM%Ε味c!SdI1*9{Y~lk`c/;^m=[,W`D2 R(n(,Z{V1cx *[P,u,Yƺ-$$HtIO 2m$yN 3ly59Od,-M>}j%]"6n{蚓/jm4w z/aΊQMӶ29+Dê t8guBs|8z};AۻؠF>p[UQLƟqUyal`. GQYjx׈`D $8qf(]0i-DY?=7'q Ӎ=*( j SC~A."e/zȆ /߻L`&kq O6_V.*qbeXq5Ś#@b:ߨ)63̚[II{E"wVpC0Bx(lN(dUNnV;O^QGПNcs0/^ɩ z6}$ 8Yݓ>g7pH6USaĦ [$4)4q|MdOwyԎ{LGQ&R9Qfqq8KNʋE'h֥hSs_ Kߴ'e)1GdF4-%yjJ>[Do1 ԉޮrbΒCVN^ 3\bT ^+ QF dK4u =, p/kdI 0{L} |W]l%H%oKPB Í&" 9d9&U@ Q+N[k:;S PYnirS)N{ Oz0,Xڝ'랾w?NqToUPuKTWFQ[K_@}֫!yqgeTS2mlN~>Cstg+hDQ&F +t7@ kf5]rܰ7Oq7]atn=Jm*r NP gqxt2wv$*f!%3ڝ F$# Jt;(g) i0_45!Kc>kR*brIMV,v.?qǫt%0ىf-5COK=O4F=41JPe Nd !J3S*iE4#:DtƯGuXOYn LM$hB5@lX5a#=$ $рRT߾6@ mZfI{]BL,źnÆc`ݥ֋HhX69. Ґ}$n Ոz0o PEQp|Ep$ <;;0kvMOAkAGOeqf2]d@V}Ye2w ɦ>cMb0md"P,F\'-J!pN@JZk Q}ѡP) ]V>41Rvg@x]_?̀ͧu!0WmP͆yI 8=vJle8BYծ< 8"+R!%ͧ m89VlT/NNB\K9^3HY pFXM?:-WZdma1"^{"u)y`kuvy[T/!QID@GvɤѪm4m[eYl)403gQv{Qn ck>ʬ'H,ђ{v/CM9Xv̍wc'1 VLБ&V^ꈷۆ$6fB K\݉ L] Ԟ{\kvSr$=6hn$K!_1]g6]˔w0*%Gi|koUU6lS04ˠ_7a"%2q/jk60у[bzc<#9܁E߁2t뭹,xԮ @+ߕ;%P3!מT@Ov9N8#,!a&bS|7V2Œ=v ,ʩX5$Ӄq)'7T0a32 6'ȕFޤKcJN&XДgG\G l0[^n!!LXG7+-RSvfp9x{1?!E쓐uiV) j[G3EroR}X댹gwqIj>ux 1?Q2&6Ez1 n@s\d<SwSahQBx`Q.//1,v]Dnqx2e~Et-'¯$>Sb#R0QZi+߁&ܖз~0kj۔2 7c򥢲{TA?Y(G9O/So ])n$F`%Ө<jY #DL}GvUBG<F*uIi1OqjMFqmW_dr(h ǚk{ɾ^[Qµ0nP9FilN&N[,ӛ{~= g7E:[)@٭ ~+̨ȐgC~H 2;_(Uv8 c5ysoz,LhMsL5fEv^q 6HoӬ$1a#Ff'S:.4Y: |;ΡKOyrI@ M'o\Ӹ8M@=.5_ңcGs =@¿!lRk LkcY)<.]u._#z)Ksǚ--`cL+ǚmMI \rY"a LۄՍȏ|]*o,{ɶ[>C|}6׍}߄h WA:#9BZɚPǯk'tbO_MO],b0JuA0>kӂO_&Y-a(a8TRvFu;6U]._EiA&qHfC 1l/g%͜2a%b 8BQ#azxw[B=di /O^鹅汌w eS>Cst[l-uEd)g[XTZB.XɬCye-WX}l%O/nP 'l{! &?FߴX*ۡ(Ǻ"`u]f@Y3[C8a!89g̺. #󧂴s]qm]Ijb<6^܂[-㶠˛[B S?(z4ٽZV4BE]y􅁳eY&#[CZa/"E9/-FxDMM&/(d~"-p"ӡFOM p3DYg:#=`^dZl 0"mNePϹ>"W$}R"gj^7HjAL`EMXJlg|e)-xǸ%î2\cԽ>G-GcNS;_\Q߆L z9=kͨRB)M#I۠so`O93<`0nM16'3O~UO>\kLѦ2eLUgVa}WڒV^;!wf~n `rWozh>~MC|R%Ț3SP UmWq=?$x?+9Y/[c!ź*_604uN1pgQ}l˕. *k)AFԻm:W0U)jΖ("'؅ѣ"sGF}BSVp VEft/A5$NumGz3mtw@'=QnX!5Ϲ6m+忆#M|@=j8M[mX[ܹDHZUԑi+}@=4h 7QDRs0ᡙ]l "Q_n[g vdQ`,Q}78`-|4QNM<}m S6ͥxfmKɐS}a~9$O꫿ZuԔz`N[F1=IxK0ԳV܅n@dd["<k\[w$M]%sO?S{2̧jD9Aq{ T+~ny^ S*Զ֙z]ZbATx!!N*7IIw ww2va0yY &&u>gh?da$k3[>Jdb'1Ŧz4byWךN]uvAn~lbPFgx! y ]GR\3ey>Yj{f;jv< o04d:#])?[ $h# KrhqB~7tج`Hq!+;74 `- NP{\4X5ER쿚9J'Wm=\XHkŶCqyu_%Hϻ ]j @O^2I6DP ^b|cS"3 :M&;[=/f=DY' <(⹏qWnӊ0$E @zxtS3rsO1mJ4|p #fZ%SehxՊTzOCi\ɕ62$/Ӈ P[WTdA-7zDc"n2׭1~àWh<`QqKJohVB)GӘ=m4## HoDO9typ*䪻P,IUo:Gw)dғ9ⶆ580LQS>[áh/V?7˛Ś~N v]Ip-beK Mgx%+ V̝4P_X-->a i;GCz$&\E(=&#fb͓ [[]5pfN3ަ9l`shHet$2!N'NͰ?'THjRHvB2 [Y`!e7ѫ~0M}O8&8:[5Q=jcY& 8rLʻn=2v1d97>SЏ4 Y7e,jНA<i bTVmp6mv]0WJq'.d/Cne=Bc(sHߺ!˙=.Li$,H;7쥊e؅3<((,@4$ޞ3R Ts[Zu>פly@12o7nSMɄ֔֩NԴ\.wZL^-tѿ{x&34_z6|NzK#M\[QLC],0n }ʎ W#j+ܨ-γ٣%%YϗUPֱ&Zm؎-OK5V;adC-?Si/`gNY8Z+BhX^?icpfW;W{Mb J~Q',`}{QqzW= kU`Z"]F5􍼫jyXt ;lh6 &}N;EGٵ437H\198`57kMnCjsZ(~RJbd|]D;*\He`q*rn#*$beգZ44Nq BS\vc˸ WQpTI5Lzi6U6z5Cy*|* p@$`EikĨU wYc1nBtuڪzPNo6i jPI -g]Cm8r*@6{RP%AǬ)6wCkHdħ-1(C.[{z)bwivpUy :S|]a38q^ lvzs@0^d(=i5o(1[ xXg| ֔3FDP>^hO*Pԝʈ?|Z@~x q@\:5fx9a"V؎ʣ1@d$a}zZQVS& guZd*mD\sࡾ^F\Qy<8hc؀#*:t FjW5Ia"PEvm3 jr+څqf?Iom~[X/a<T 8"kMjޟ#2%C o9cLehcj$G>4(!t"(Y4.q$##9wZn:jq< \D2Ӌ~WD.sVm"[&nA%U)tC=AUr/#UiK~,A~Z;o[ذKln:/p%ϹDLE&Y:cz U-h[eG8XbEUDHRMS^HfMkpK tmKޡrU9bP֞ĭ*#-0su$t f)6ҿIFp GЈcC"˼oF5%bg72ibytٺ[ a j"qm>}D..U=m&$u:ŚorO+i:9vh +D#2>Sl| Ҳ:t G!?b"ID^ |BInR ;61uL鿀U3AB}?G"*tznrC:[V]"UbϔGoh#XLC s[I=<0Ezaӑ̬Q<f5LP8LJB8G`1p~gŕp+\,Mc?rCG< ʴV|½(!X{Q#(7% )>{S;yi2oE2)6f9Imai'{3%1:HVCLx-5& #ǓUYX.hr̚Un88t3wҬ,@_9/s* \/r3_dMm]QA()O_ zkYVl~fE>¦_=1xanC伲\d>Yq+ Ps8V\OF(/yDz @wTM-˝c QQ!]bO˜l7pϽ0w;<|;\)QU'$vwȏ1poY-AX1֒}}tM~ϒ'm=Nn M "Vc?@o&J}" b82sZH٦w$\Ŗ(QEwN xd7zO#{|k1$=篈 w1K52Vo W S}y+Z;)d-z>v8ްQdr;csf"PsiU!o?טLNy2SzP,K(4H)*!O|bo|c^Helbw]Y+K.<<-S8I@G&=8NyC z(مPHxOW&CNO(XJ an=D*XWKPW8=yPP_"4DU&oT7 U}ɹiÉ `J(ЯӼЈKtNjIQ.˔5=M/7"ZݚV\q=||]V̥:(5?~Q4ۃ tܘT9d~w;j6Cuh~xMOvQ20"E0tVL?EMÏԱڱxk2PyQ BSSYc?Gց4+q36r&(lF5rrKTz@' > Sumv'PMyW]c([0^5158۩VCCNSm]VwIe=kO"T`;?&0Ey`'%mU~"GkgSdbzVte;h jU.7}{,⻄hM9 Qw;؋fPa^ }56~` 9_y&)ruBjG;=gYj |n)@Js Tn@U2C_^r 'ĵI]($6d]]|WA;u+ws4\kj4Ke̅޳]ȪP?!-PcJ2=!㥱0S (GX>v8 +@m ćEFLlI9%q()+X?#R Mr*P|c.R~o?E”LEoK(Z!*,tXt~ dZfM7C@M$_DJ}uM Ⱥ]j'06xM~epͪajyNSm Yv7 [ULv2!Js+hҮ>ɔo_T]˾V=dYe ]I&êZZUp]`&޸_CEƱ%<!ЮyT/&6o( x6mS1"sv:cHG$s9GOcńI@3Bo!q7N&kꍧJy.3fFL YDV%NE,bC.<X~q|6AKU=ʛ |o{id7K%w}v[\60ZDytgd*ߺp"Z&oa"<H( 9 ǪcT03X+M^UBJ,48!n'q]y[QC ?C`B@dKˡ`~ eNO\c,3iuG no}L]\ɣsu%MV& ㇐ruiSN9Ϳ p9:NJɗGy<)zH{{"2oͨM'Es8N~پ + O:=Fvf+?Sς7] }2]+p8ioc2W 9PxJj~ &8=Lļ(r8gO|X l~?gE-]$$8QP g:6?>@&&q(ǽƊK/}i0ron3:[ՠQ!Ob\JgݛR-YK0&ܜJ:ʐ@22?ʫ %=0,׆A"}^a}Uvby|^ӿL'@b7!ˆn%.7Txw_~a- l2VM]<.q S::cQ ]) E^,'O1tHZ!jX }z]V(LJ{/)׮HEþ A@SqΧ]`I @(q|en|CHᅽ9;`(Sm$gnsn㵮1ekU8&vިk>븖<(E~p")[sOƈ؅1rYcdfy4K&[741mp,حsx`3]!^-- 0y?97s􌌰]G=x5:Tݭ: R_ОUʐrpOMQiUйaxT.9_&.@)o]mMvkdOj$x)s^>ՉN3>yCF|"2*<%VS DI%8qtO jWbJℸVƬ=V sPXm5?Zʟ3#;,SZw:-Hb4 ¿=RfĢ+krᦲVGmT73<&SΨBfH\Lހ5c/A!R۔Nexv$g:]"QA*x"ͪ/Q Eay"^AoM:D{;) Zg %0bu6ԟז'F,ڇP-e4VvټcgFȊ8yH+ YƃKc Rj¡+fhH+,x?1.c6Xj#W1ceA]s/Gj=zAWr)/c&ql}&>/ & 0c*uYҜǁ%{8kdJ!k#Uگ0Sl¡ (B1]ƙyf/d#Qp&4[%h^,>IF$PX3te.KSd7i Zfd3~ .JdJ.KZ`Zl lnSqQ:\2N8*tWֲ&1k%)SΪv|u"z]M+fŜ`E+Cp،gt +f9RmLka{BȘ?qEC}CnK~9W&w ?.ۿ|!V D.ưJי9[da셄-;ޒ&pz!MiG@}׶;~U?4]X%Ht(ƼZ }Ғf)eʜ[_E)hÕZ{ANWsRIH(srzXd)-Ln48b0gdՍ}B%Q_9 }sQR&Z#l=E͕(@S"IQաJ2Z (ǝ ,v`̇ KUf4M{0aqє5-ެ߰:pw8r$q+a'?tqul- C!|]9: jg__mwC8tU-U\hĂf/a_bDPd(0"{`z6ʢS9bv\b9:ב80=$*Wn ܯiG y`ۋĒ*6)^%ЁpdfضKTpOe)`bqW-=?.cSq} a|e\4y%4VPm߈g2<%Қ%k_9)~#֥V 5_%3~À[-Bw@l%&|8NkEɋ_/Th_YPH Aԣ@8`\j0Q>:7rsCN6y[pm({7Wg?_(DZgj"˗Z}vTAh1qf)A3Qő:2("i!2T/X0N9"**98cI֌ʸ^"Ӆ}E6f'T{˳'ػm>EcU?"mForJQ&>DN@GȌpf&mkUN ̀tiPSor xvݶX8mhYzH o ?uo{l|c K]^e"?|ڼ~OO./h;Dt,5ȸj sv8dd4OAQ›ͻR;yA:qH ɰq)/%?+zQB6t"D5oG/ѵ66=koqΗ0#j+_i"ȳ-ݕ=Rt|WT9“Pc`kPU"`sB$XvciT7?bC aFő4[k]ۇfd-yS'- /[JrW!;s/%Q"6ey&\}@eUNp5<,h .K59hmlc jj_h'cbU7fDNy;{l B~ < ʰ䝯jb+ ?,j|"fh( #y3co1CV[8?ߙreaM/'/MrChIQ ~6=광;z=5`L/5^]=ʸK۳.LMN*F~FHpFL؇q+˘Y CY^(!pWfoF#充 u(yN{c+4ymu)˂#oS=YAiU]ȯ*HC"g=Ş5nxOj62yCqq5L]rB8ބYo<V[^_򎺏ha=rSk8Jd匮8)J<-uqliqaU=1fiԇe7$9Jű&<۝{YO54Q" "fi#%2+|`JMM]y'0DnlsDl [f荜^cC矯HL/T`hr䏒r8%4!ec87<ɇ:z.*}U-Q92SH^(V;iR/+e6SYG/UXpђ6uDM<&fwIׇ3Y.|8l8jCgF+ }&׊}`vkY)C3OCdojDf`;|9ܕv*jz!@585ŵ@w&Y2 qmEQֈ0Dc!v 4gmtj# j7QF[G+"cJºm豇Zt4UW=Cp<6is5l g!Pf?0' $gg`UH\,.Ah cYRHf~ RjԟVeJ:ьؿ,m=dfc2 :zQqi*1MM\κ1 !bt~Fʤ$'` X<>[@ sCnz>,? o+E?3aTV.Ȉdqmm;Y؍]-LKkJ^\i9"fp0[6ܑצZ]Wc:'tbbBm8n=~-]Πm MGƘ!{CS1¬2`)?' xm*E\Ep, W;M}mVPbWԋ#|y~}P-[pz/Z~d,[Ő0"$xg|}DL3RIЍ><(bz *Դ2PV\̮Cai9xz/h:fwMڭvOo)Q<'hkݎ9ʊ^ocɕd1.k66 WԮ1#/D$C~Ӥ߂w*X+Ӿқ`U,^G-׼<Ԑ\J5H't (Ν<8IՓ a敻D;cI'"wE(5)n-9h7/^e{m!803asO+>%fFiM{([䫬gY5CY?7X~Ɂ[:&^:4`4H.fnP|3):E;^շa ~}%(0: (79XQ"=K8F^;uBfMQ//< nAbXb9 ̌ۑqB{C/7 MҦOQ\"%CN(&>Y1xwrd6Pٓu,FӶ>d3N霟dDq>RCKҡY׭bTz*TS k(eu+i,0{?F@|ćo2(ۮα ~sw6Hb,POzvCؙ E.@ܟ#$$LZ3y,09aeL΀Bdq֙Yb{&-|$=eg&v2oBXH5 nRC CեFRxBDҬpee*E>?q̿Y!ſA3*I\9('6֮TOYUQ) =3 ?# zr]ǣK& ]\{ iۆR@ Ih30pU=l~簡&FM(sa~h^HfmD?uo1J$- j⼂*Jde)oy7[ $Z'j]ztS.#Ϋ@޻­b\ceqx)W`&CX&lODc\lTs0Smq NjyYNU.u2~,>grįLPKLʩu!4̷õ|EUIGPԙJ8υ-C#&j:zjl<ש.'Sh1ʨTne=^eJ-InQnE=sWCYo3.d9!\$YO̶t4Үۈ|{.EG{:U>mܳ`++Ĺ{\a/!E'0%T>}U˵8uMz߬xb5^[ruȂ؋{S}vMCqz< Kiڄ;;IY$W)}Bd |=~ [npr'S.5w Pl׶u16D8bB2ԉ }:#ob 5#l=U8:b O8h[~3Ѽj5<j[kIecENZ"qi(s!IGf6tjԮi>ЇU+w* a/Ι[&=hMzI>8G-tC| n 9a,8j/0Y\$&wq3D~78FkP5Pj,\kU>mߙ܎ɼHu=JtQɸ:ؖ\~wmN)YsZ/?'h'ܽYY}g2;2b.`X3msԥߚ.{xWh2DčK8@bL}ѕ*~3sX?ZZQί$5l^sŨؾ]7"qr1Ϸ|ub^K%)]eڝ@BoQkɎk2P?UpB a18׌ 7oC>YZ@1 Y \$7wq٬:aOWNE]#9,6j©eYz #kGѫMk& T qArÌ!otmZ@˹.6x}voC,*Ͱh(bٓm-Y Nh9Eέ[fُA`j<ԕd:+&7j`l6L,skHN/X5!EIQ9'95Ro8c%vʰ8@J>bC8cwgȗ| wEfv@@(A=X~s&A`[a0EIKdn|/VMZ5 //( be˩҉iĮ; QF/ 2Kgޟ,9(0؀n1E.VB2Br'\~ [q]c߶lF f3קcw0QI7Fx)؉{ ]ϜYJ#Z9s ضH&u[\&mHW*BC~)Q_ I)ua(}-@pi!}0ky[]ejwm{YLZ:zץtyoC'6 QzcT.9U)+$1 # X&a JU,5{`SGL8PW"Cװnhl1('ЄK+֢9ynS]v7<7Cv"&J~}d:m>ԬJ_Ⱁfj|k<CX=*IJD"eԔԓ7kt^: UJݿ$G$d+k3>;1Fq#E`^ UHe&X˯+KW)z932( RPbjUP}+?T" t7PcNsa" 6q%cK:Ai1R(!YNZ);0G]wCg/ٞ<.56|C2͵d4ڄȐλ(6B5M[)-6 ,0ӯ(O"s桫З.#c34֩.lREK`/Oim( h*sngl2=F-6?^փ6E/Mb2*N-BFB'5yܢuu8bΈ{H!ʬj[%xxnMYm얙*%eUcSZ_{RNNs?廮PuJ̖/}hEI`ݻ>O'`Bh-"dO6I0S8%Z=XNOfS"I% ƛn VӹepWє!zOh̩kIO`/l$ ͈%=-$'#m s6E&V_'|[|&_|VQ% zzV8tlkUfIFrl &Jm?c1 :tsRRr2y&#ox?1cE&=@;Xڽ5wpML#iQ`b!n3)QS 8$8h"4GF밂Kx"yk+{r ,RU+$j --Լ_Pɻ{bLjTOmd:I ɝ`dX``#x>GC9P(%+׆mn_Ćmρ# T,Odhڇ1BHvb|jHﳋfH t&{Jmoﱈƕ5'=o$i ;'W}l$i4ivv:'%]=mTjO9mv.s .*0IQ鏮/f:(XFY͛KM5َо =)cZab&)vsѣ^H@a'PXډ.&(JRxi:"Av8u飙w#'߃x\{hf`#:F*D@_at9fldnEde&蓞-Y Iۃ7YO#Dm6G:]:O1Bl?Aba$ZSa6˩THMY<{q@j\e9.yXH%V t~@;VD䳍 FZW!IS~L"/j`Ʋ7+9e'k<wL6TBl'{8'Z12t49!DU۩&n.06 jЙN=xŜ^2:j=Jb3"Xzel#"h :XM`j*}d~?.g1@ d:9To$j@ˁ6@ gt:`D3~<՞aCho~jU:25R -T%lNo_W4qq\UjJXIh:.'XE\@arXaJDP!#٤ M:|g:%įZS^[P|켻Ce78ɛ8@1"1yʜ8[VS6]-0ԝ G2ԜFM/X|:A.VT~%:MܜOo[=|\RH$E{5A)񑇙ư~RthXn?~&Le#|f˕ӍfKVZDq F㖙/OR?j!ˡ쐹~|2KHe,>Sc/8c!Nj?FNrI?FoCY +&niDƒTE[->g7FOn,3+|4F=;qop"U;iΘÒΒaL9e'hB*տGs0~Z""Ll&<\LY}N 7&a&DPkdPK }y Ls60 :, R1!̼Cۤ ֧Yg'=_g~/} 6 d/M.ma[)B8p3`u"cV:+œ?v;r9*k. 3~;аod5pubNY@-Q'_3ŝXlUz GBHbf$mݿ~õ\Agݨ~to-S)&S/:]tzN)#nL2]TØKtգ6 pe i9ql6"[3( *jo-ưRVTJρu3G{o.CHQO_c'Sfjܛx{jW3@&#@'&?Z~iF_a7$~<96ڇ/!a)_6M V1١ [Qx`0ATX@H r3x_H^~E.b[.*Vh4;q|ng@xͶnƍ.RF7UA' -߱͞o_lRo=Շ+KCZkXЮMUցg~(hb^vJ5f_1> | Gq|,G{4P@!kZ/MLZ娟}G(#MM /l]NO@eq<7S<EsLg -4Ǯ<{{G/J-'7"[ :s.ة"wgFY7䦥]nuiOTLD&?alz]vQVniâ3FӏKIgG֚z'r(SNS!bM}s6R^z2=^ÆfOFеcx^D2)Ae;lfVw}kE5\.m0p;oaC"P Jzo=RGUEVK@>5'Du"w;#P6c,dNt7m\IE{xGC~@ͧi+>8Ufٌ͎>RaHAwC3)WB}9L21bI)bWrooe!7l4Z,A<gC9Z O3*~ȭY ?(vY앜RUOHS2)@7MZ!M\Ie@9w[_k#IE,]X5YwHCrf&pv4ቷT)Cf BFű b2'R\>jY76>+5^PomEK4 !'òPx,dkJV+jo90̐:edzKg'g9^*Vl鋥5fϝ1$D."q/E9͋>h# tq(k4+e.d 8 lD T'H4;UX=9L`T=k+(p{g1HDh28#\lDԂwٱ" 8lddf}5sflrp İ24Jy\Goqtm,m./J2E u<sA\i:hFs`T@ ¥sr<ϟQj#~)x:7 I%N0m-\b#&StÚaVD},٘<y[ W@dJJ :n#]+zCxAgZPp@gx,2Ӽt%Ã3ʋh?R(rCPX1J=L2bowzOeSģy |+%tDZZs!uYKܫPrd` blR6ŚJxu|#f*a'Qm٢CU$ݞ#Fo`44a6gWH2ŧb$o)kyo.8 IA ΨwGULrW]QwD*.K-%Hj/zH743Šee[?}êe <d.Md~5h"`x-7BVfAh7y"} [7QiRREUO|ڤqtk3Zd<[[ ln߫BTg*aW} W]3=t?H` /%PjN ;!r*GOoАutª::~4h`?[Od9/^V8HAt ,VnHfC 8;)_D>͆[$A K_w6U=:7.*E@o)=/q_Vs%c՛wk̔!6SBzJ[gͳNNe?J;=ĹfS{IUjpuokRHX[@je2yLA)ڞZЕq,[Wbo7fN¨9;;zT̢Rn$p3i!z"Pj&|̼N-pvU CŝZy7$Zч 8)\Ɛ.VeP&>jg&c`Z;gX\T6N$BpG35NxLRwJILAP"1݇uE5NBRb[h9Ahs!ؘl=F9zlth?&3I_&)`X*<" s$`y1:QzM]*͟>8(DW9&ܱlQvyNFXR~aMCYrCQɒ)ZFD ) y+$%,c4gjۊZɓJ2UjvR  ~qrtA M*C+sc`+Hkm1U.˲p]yD~-k@\ vk-En:y܅*\,?2vx%(#n92,˺@z$)7Qwhdh$;ub!=Tޯ!):2R2?V}M6aoq3+E43 .]MI/w;f(@@gǯ\-뮄d6v}:뙦b_"N`;t(FwkIdم"Ezw~ܭɯɎ>M!.?,$֐M J&YUOWOv_9[;۠J<\)72Kvoى\qE 9MW7qjss $ T|ZpT\IU>m^*?eݜbO)˿%z$s=1: ~Պ)A<1!Lй+ßtq{bvZH2/=뜍."xqҧ)Hhcn8'ƿVPhee{n!@e0j(4(4 RψI{7PX5:Öcmv IEqQHrC()5;z'@[A4<6F4z ͕eXmzп+W Sb{+BcbK7ŊXeu$iurl#e5KoeE f1Y1:2E͠އ |]B!D 0py4W.U]_ Ğ&|J:4ߊ 7Fs`t~cY0_BG#"|,!sbX r $Wiu+,X$rB,psBE?gP,[Asl03z0B>cXѨLԺɋٙ"ܳB V90ks;E` )eS*DQC ^_DĄ=I" s_$-~Lۥ d%~Jf﮿3@'OcJP" N?B 3(n}ԔiD \ Hi*S|c }U;0m+< eC=\oF8, Zo5=H≣ t:c>x+P-\+ X,Ge:ZۀC~Z`-*$2b{燐+Y&DҋģqԌ{$x6Y;+܂E.wӞǞmaeJ'-;qkq槢XiYPLNh0~+gM8h">ϫcpUt.C8&$HZaܳHCNPp UdBjr4*z?[ȇ,ZSBMs*@M/K*~Riɘ[׈`FDg2Q٭;[zq>:ƿQKz)yf| ߫8in9\Tw>O9vU {-CL7ptft(z r V4pafa˧4.;N-\>_+KBIwG nkƩ֞m|rR'2)ץs፡xeOQ9-gTT_" VYPvSv1rT84wtdo >'kKwRpYFHARzrpMl#ACZB)YJ2o 5_Wr?83j vlY,܊=SK8߲A26aLWsf*h~vyd~gqؿ!ݦlgyTKر5{[|Vfě0܌t5#[lO\[FK,^',gBf>E*^Ch0f8gVh U@\֣ۓܚ׃MDo459Kq-Y{GpLޅ$cKMiOq{YpHy_lg9(_̟bqS 9qѻ!OIQUu8ޑFmb @0?"~i̺{N p ])=2T+aBM~r--v)KEF駻/C"J^ W'w>lAXEW&1,Rr`+h%(GZ*z|TɁ4Ӳ6!lXGV/H~Yj{W맫qM|wm:3q~[C!y<ϾͺYGc[j/^T,bV_V ŠnQKV쇃& \'ݍHZr]M1Q>B.`\Fb16|pv*V:3E fBfۇ!e5b*İ8E4Xp64ɛw6uXrLŕO]$ ;z>k$82:ᗛ"!置/Pjk{%:Qj.'Xz-8nF[3.<'waU`Axug1W7,;e!OI33!CN*Z[VCJ 68Yi.RAizeR0`jn=3(2]O5\{ƌiE=k[.*E[워:oERsX1vx^?AQˈр /ê'c^w`K:}ԳTqZ\ <#Cx9 r0Ln9 ta :cB»V@禩ˉ9, |yjNl-^e,!oo~mIS RD Ufu7F#VȈmsBI: 50Uɇ%xEZow6Knӕ\ v|/d 揗|[oԷ5tifLO%;"EWSނL+J4;FBuL)"+hsTSqOA~7ʲ Ӂ1}vpzD3azS}pUb6@}{ H̶L-:RJv:|$ץ 6b=f`ioμGZ?Nӡ3&%AD&z>]Q2$V<鑑XlWNm+kL'ᐽe " ̨g.L*Uv ؏TUF^ot.-BI$"8PW 1!ɹߩ6oS:ZF&Sm0L'P0d93cHX+1O2?~j*%4Cr1t O)d8HGNuRJܿcM"G藭مUT\Fv33'HHgg:ON\L^+8MHe}Ǧoa8jWoƜ U6yT tɸ)(0a'ޔaB(Kk"޲R=n8|Oƃz(hcDV@t>=b*AtP);¼bح>ĝU^x<7&tux` sv޻֗*5ҟx3oj\=xY37jTf+u.C/F)OҰ-i|7=f_&}ҽc$t/1'mJp v#%3A? HkXM\q@M& t~(\mXd&9 %_ fK1,(۴y)C嬥3ei}4$-O-ݍrafu|Ns<U{I u=g5h0fw/읅w?U!/#^?y]m5:k>?8lćt̞f]O}ND;hrqfan$t5p5`^G!fp0ēb%ؔ3>਼f+NmOyY-OY\Y B֥eO]mZ4>m7]}6!E([h'UbIU䊓c\و@nlnam4 NEUfCݚGe>Qa:Ac"]w恖R弄X0i!"G肔fleʏ`9-> ͑[h*O&?B1d_Ӥ F8Jnz֚gpA*{=x.5g EcQljRԬ7L|zt["8\OPRnܨr|!n;/L"J8#q`k{ r7Hcwgݵ q*zפJ>g' QqO/CG!?e 63pdHCd}K'ҐT'7%<K-BZS /S~7%]|'RK5}.2Z -/ݹ CR_X4A5*,'6Df~ lk1D:`;t1Vk3c)1{4HkE3_VN3IjTůۀI5!7$X8|18oX0х,񧠊.D΢O}Oӧwsܫ%HUt8; a7*,Ԭw*jL= UaZCzMSwa=EdD kQ)D$RFI!I4 .ĉctT)c ÷lzPl3ѿJQ`> !X*!iɄoAY}uԺ4#h?Q*S{pҙZF(8FobPUriߨ߬lY:GqFkƆS8+c'q PwM}^(*:+2ʢS`::Qڮ 7061qq$DY{G;N\]FIjN6!U7wxPDx0`+WS]&A&îj=@fR[/T`+—5CܬOXp-CpżC;_aGɣ C-~\Z>Ud$j%O>cin BMyex?֖f-R/y]æia!bQ{ /EmhD8A-Df d~E^k `K<N:[ !(d aWarMGd`~=0}drV]:[a-ܞl(71}uFI(V˚71Hx}r=:gC8xhx˛ϘoM: _LYM~.n*. )&Q`ymb̫irOwȹOqS36RgUƗpٗJU4@rveSwdK+ַب|ia1RwLb?SO-F ˜hq;_6:OnŒRjl)XFR3t!f"yPםI6wJ}!R^Һ;2O'?[UBT?"FR&S~ !7F g+Q%51 zVΦ`8AELIa@=&/5_wh=>H#QB{ G)2)]~S틫Y[Y"YZw85|l#JNr!`@fi^מ-|dXCºx=Xes8m|FpI?cI5wƉm?93^9 Q'E3KNjR{+a޾顬>#O " X+d<#eJB2LHIPa}<A\!o~ +P٢f;b;%sp׶jyνA*(4ޏV1hЉ`!'/ %eBb>vNԊ%ͅCJ*tAKk[( ;8G u6bGB ?οg=k%&g*[UƆuAυGxmGMV$0 ~i׵픳sه0٦M6FF[*HL,rZs +r*EM,"gSqh. 4nG_E'7_S]t7q|PZq4-8+نF[hw$i?(#Img! +bflݼH t{ {nV6"ZGx Ǜ~5$D A6ŧzٶ޹#psg̤oyF72dv6*g@vO֎DZPPɲ5 Tw.L r#ki?f`(73q=*DڍK4iTj{,\u9"rZeV̌@ӇqFL/uR%la[Y ?~ QKdf/72o60X5h] I5JYxk*KDұ?fѱK $E\Da6gNʻ^>nOk00 I/bd *zr<,#S5DUɡ_ycV`*(5q\\ibPCJy9xU t׏?Fi)j=Cdͼ:+>#j8+IOu,?ų ԺWBȣ!҂xE[ע U1Uͧ;-_k1t$s&yVuA`_]*BE@!jF0e*h nyFȻ=fQةhC?vVz̓ c)8/0z |5x{tyP>CU5yߒ /@ܞ3m6EHC7#Eq'Kp O 6>cHwdJ>,9`S9oUWB'6Fl@7sYB/tD"jGift3z myrs9 RykR+{\zc-Otcq @SȻkTw>y 0Cr0}䠫tq(Ѡr47ŸXYb%o ji.M|XU~/"UZ4I$m񢬫VrKC|\kDP7ٮY(F% \ȐTQۧė$:Q,8@Oׁ&7lr%I!XuTKK#/L?1T C‰Z9O*sJGv܄DxYşY *a/]e y|BMQgaozkzxXV#@A}Ls?CgtI'poЭ:߄X"3% X3=HgA x(<}*1OdsQ0p-*(I}  ˈd=^)Y։;ApǗ}\yoqH^*t["j,9<5TZ^Z| ,t?{o ^7Rןp rݡ0zZu:Q qVh2us!)籒393YHKq7bR(хZ 6[[ZP4]H"|Kn.KcG:e4oC>&e_-V0vBJ lB X;| <1lT"3 Fc97Lt^T*tCA,yՄ^]W)gWN>Jn%ȽζSꡈiLK[`*mA-St(v+]IoC"_+8'ϜFÛ:P+7 ]9F TZ#Yα(D`D>5ve~J^fpZxIA 8l~}FPOGWoyC20n<9W ϲ)XqrեAU}Id,q7Hq9ga|k}@^71qi'oh2PwHc חP7LZ"@S_s1`zLQ6Grozӈdu 2+5wfj{T!uL L.I  pP8T6Qr-"W|']/Wp+UK<58}ۿoTKdP Jͣ%s/v|[K$kƼ HWxgG1TC7n6:XHF=M&p1"wׅdjG1 RpG*yp Ȏ{\k,"0~rYG򿦛C | ʼne* !o"%ug6%,&vTYz$%Ʀ_&"F|ɹ@"B! ⑫A,{I0EO *uY3]ޚiDt^>u`=% |6E9X57.1Yt}&Cuڔ+naSY)Wn}pA!c¸&L-}$ra(~G{e/~PZ0IZðO|!ύJ%D9R vTU*H՘p12 wgqc}~dmŻǬmBy$),(2/a$J/U*h%jְ<*-;+ yp:O ؈P+CW18d:[9'~Vj5Q|mI:#~.*QIB' ʣWU;GZ(W'ڷH}t'P<_#G=`&UҁN|:}_S?6X:kڤ"KWzԮcSE_[:.S`e*ZH'ANS,RCc#&.K3ޜ`Sڔe]u Rp#U(DOPLS}՟sgH/ڽhJkr.`i@Ӱh/<+SE]fR***l4~n|~y<-͔qv=|>ޢ~!@plr\&OXy 42X;<mWdmߩo*A@1)QFA՛XCX4#P^V8^N qg?O,Mܐ'߄W0~7xH+A\O27%FÃ2d{c}( DG]^U^ff7h {OOۿ'틳H(-p얬`vP^Vپ=%m!r[g<<1(Σ芑xly4&#+FLZ.ښ ާb&-b~T ʛ-vMna'ѶE!vfD%N 7&kqWY0B 'Ubud4V>yCY<ڐ>a9d7h^V~~וuvp=V%wL]@ 9'Bn5[! βBBq&Vt\E{N ]}(iKo^XWH&ì/ٍ~tf3`Ӝ,:x@p-mip҆G_Jh{[k)xqtEH⦪~ё zarG̶V|bI5JOg5~Ϟ.>vuK&4:OcZ04=EPb nuxC0 [ ۨ4T!"_;I-GI$@X *=MhpTI;)ueQO}X q';6ج?Okq= 0!VWV(V>ERM, l#Hr6,Hʣ~7ӛ< !ָ S^fvR!i@}8΂d]vyCM]bU֩H)rҟ4A#=ܲ%SqVljo 6{N2M%]Wm.$Y1ut'dG[w7OyK5E3B>~*Adw@_DVX'1 v7^x2/@K> 9: (b :Hv˱&<-*UW}bB0O>wy;b! \whhPڲVspns~xU! a^MNTh ݚ$ x@&:zx3Fyn(ǣd ;ƣykluƣg\V5's4m1K.7Hn0jHb&Kkֳ~--<{7cCy4wW[sݰ7 \OZs©@ᖱ\EVƃ>#A>R)6",K-[B@:`QVV&"$-ק9%'08Յ:O%_Z&V ?p>]Ij b4Q(=ѰOrzz$xPAR]6"p \Jgu0(Z pxX_+M)g]ը]YqU矧$r%B)jh0<}sZ; 3lkQPY5J XX' X0?-o}ټSt _..: wzlF=Q=  Ȭ>q.{-ҫ&r]+>}#Ƣ.2̩q:O|[d `kxF.ۀK,靪W=W7 *p7hևDL$I_ x0l$d$QJ"~[!sT)v18_(ecN -˝M9ro¥7WOYߎWDyMx7PHi^__\qǢWLѐwAU)m̷vjPyzY )leࢪ9^%+! :3769URUmZgz`!p@oulI CjSj|7ZBK:G%6(ޱi7h`Oaʚ$%ұOo??L-G/GB9DnE!N :?)n~[؊<&Bm@S3'YkjonWh3tteګX sֿ=7ۆ~ ^Obi&GW3dK#6)\q1ËN˿0nOt5b1=,rd*Nr lVH{L) ^3_% dkZNV!:=O]f7ۣ=ŵ>f-,ALn{6n{أ1ٍY0-ZRbʬ1 1ʠާ@]2``GaU i+LmX>= q[?y}G Cs0'OsBmHo`qQmN%rI-Tf6~{_d(up5wmݷKX4s՚vr !s}$H]Gf&/\h'P'̬ar̻r7'r0Pkxl|;^BN&'y\t_7\9mŤS*6/l16}u4/hģc+ݍOV IŜ!Ի}\Ji؍žksy`ArD4mJ+%8[r-g3\Io;DӣkXi܅9hb|zEBWQbԁ3}Q|\0CԾi y'bGi<\MO DdVuJ)Nƨ.DhJ Rl<3Ʌ75;f,yt"p\%]ٝ%*Sx6ZkEjC P_6gUk.PckH_Rn+aKؖIߠ:'@اrMiܣ0)ռ~6@w(ZpM+OJ<`DA׸I$R&ilH\SCeGh3RQ*6>Y_S':#|d9rmr2-}Ws?6KC:Zδh*(YVCǽQZ @4}X>W ^8GgDìvWdy1]ސ&fC,&_/WUCkZ)c9Z% OP.-BAY |-{?-|s\B2TË,鈦Ή ZKJZnA[(5ϢQS 3M%!Ɣ Ys<ÒdJEll~KD齶SA~bRhUSAIH<4/oÛڮ()}CQG/ p{Ї/1B.le*Y\I]*NseB"fUOzgǔy"5˩ա.ԗi~%D{0՘\6 4$ 8Uqhw{lLĔG6 1IY4߲+Hgk׏kBN]oU ҙJeKyY~#0\e4dJ \4J>ˢ.V(<3I񭑿UFpYԇAJ V_d[8ײdu{Lz7?*跺 ӢQ>1ZfcH7{OjVD >g\c&01Yݮ׭H)y|㙁t2g˪fY+ؖ۽rsG$ পkKf O|a^ 9^,R2"" ^+ܜP>^NYrKC& N,P3j c b[uKw°ؙ1#2 aUtrB;szru%XW苵[rv6W&)ӲK Nso`>M;xe Dt!dtxpc4wD9:a"3 y=93[{Nkaj({'p>{~z 5*YϺ :fUJØ#]x"s;2HGT(ۖMeDCAu%h 뫖oBDljw^%I*Ȫͩf8G/}t+%ĴP@R^n!S8 0%A+_ s#vr1E:>7\\4g9qwN9֛yY,4핀\iACX>:*e(OaJdOIø:r(j$3Uquu@TS#%XWN%&/GD%mihS^;?)N)EYU嫑xfa*m1W4Tná=Y~>B 30-DQbTx0I)]p|-zV;+A;YI0O6X/Iz\.ǥS̫U6nxuyeA3IMAV4`.3ըfw z8~]RJ⫘[pf @ b{P93e p'Aa'vKOv&Xv߯+AI73:Z` }[l4`8[.of]) @ߑ$-]|G!Y[EL[⑺@-0ړ+9PYb^O).+m91|Y'+ˮ9lTxP^%jb8"ۘZu^.!Wpm݋K4!7 > P iڴk o! ܘ$`q9 ̾pNo?y3c~jdm-.SqB-_%t%x݄- Fjw1Wk{MDVz݄w\XPd9)od!vkɁ/ᇵCJ?v.M!{Ǵ(BU^؏=_oEv ?rOreTãd}<`9@jC CAy& 9F~UwKk:6S bKWA[~6Y-8zg4۶>&žUٱ7geHLіۣ[x4-*mSB?'Ք5k3 롭,Gi[佢)d3`pFp;Ӗgpgy! #y6^:XX8*"ɼ#")[OаwK^H?RXb4Y6O3P@n7ih=PSGH HI?&|0r#5eAmv9;/:yX,K*T tP53RVo`L4Gl oˊY-*+'\LM]ԁLRy6&.gwkF#Th`<&OPҞLj@z Z`^.ʫN% ^s]'5T]ED? >C Q ԠU^8/+?Fiig<6zvQYf"jtPjtJLZע9Ds_J#;Zۇ!p,l ՝dYއқdӾeN4D_ocN=U[S%-"c5߿8@4s49rqhdù,,Rh|yV3esݫD#[`n 5|JmrRLaAjD1ڪLÞ]`bY!Y6$!0Ԍշkp:UcZsħ ^ ΎXRfwogayprzÄ>9$Ƣ*dgkH%Omh,=w<(0_#E/Oa \D VqT,P+7zy>8 ~ov]>?뚎`/ĵg*&@w=x[(dj=dQu\Ӈg EPa.>жw[`XC)2+6[w |sRmu a%,D&= WjRm=ܐ wA3>,DYܢRڝ#(OK"E4gpЈ?= (T f^5|x zh9O9kɮs*lu6bϸI3R5$q j8:*)aRn& #;X6fʽI %Yhx9*)?`oc#i}PXa$+b"C)Bt!+SްE_7JM<5g&)_!=&S;Lsj12VpW~{ CIW@9V:RBۉ/')u=UR/rc_TpXb}pj? ߦq.+SuʩWES$-39twl1 !&Hva5J|;`/7~XLS dXt"y?4~JTqV{:0yD'HhfTΕ]^,ZQHX73;awB 0T(I,R Ӎ[ KH%#p`&+fKp^6 :aɧ5I|}ݩFL hv']8' nB XNHA%7%`r͇=A+? wb6j<Ń51 & ?a(*%ULnxͱۗ9PP۵g\ȎdO(r\:QlM&B.|a+/h ?`e/|?@{Iv:xOC><6OX?/.AhtṝAسB nBKja/MX# X"MgibsH爀9/i&pz;=gW~]öhMcz35d2eO$gmB1tĹz Z{M;w"C+ W}͟.Q΃o` 8v)ˬ]U %FlЃM6HG&۱C5)(78áoSSrJXT5V坕cZ8yMHiaw%x3J)^xmq-@ 549.\xU׸EҰ?Z t1HN)<=|io+۫") k5y`U,kٖ.#}&(B1GS&eW9C'Pc Ut1s翴$-lH{!r:TMOb)ɜS109;b{̟䤲8ڵ mXy}8^A10t6\1ypuAz9f  SՈʪ]S5-_c CnN)2M@hͶ>m;.$JW{KRzF[[/L7S/'xeB~&7fbfOl>'C5Pu!Q:^E6x{AƯTKԶ9թV$ԶX*1q"R/uh|JRT=֤)'J[jor2 0j>:nqS\WO*DpT/F?L9ۇ0F^`\KZqHlݱ?*Z xLc [0}p,{, &`N֙{ΛdMKL`O[׫VǸpuěkŷ V*wP3>%Mn40S0eh7`!#1uZqt)<"%]<[N"B%ѫn|YXf .")sY04w L'7?HuVX@!}!.ZoSԫےl+W|܅mR]I+P%- BL]#׏vXaqK3&h2ZgU}2ݞ\p?ed"$X$ʏcg5k\O/z1.:x;:VanqvޜS3>@h7l][Gh4 د`~2W,Wv&77+ߥEB5nUsDWTb0#Go3sҾ"a!y|֘SA)lCaxlsoIPNQyAW=ز˥fXmo8)ɟ9X;6ܾX - ØISҴx}D>=-V>u.Ҏ #o#3D@~' FſeVgumpZaAsMR^lঠʂ 89qؐ6="C{3<7NW6P-eYKCVfo6!$*5S?[1{fi,C%Knr9>Ų⎊,HZ=Js03GAFWEnn+6JjCxdb}T".?yo_Sz;Fx0W{ spV0hS;_HMc#30Pb*Y9μG25`;OָSʎ$ h/E;#O}Ii>(=7W9fy(ͬ23)#kJ{n !H2rl5+AXQpLlV#^`e*HsaIGt\<ۈ nvY5D>?-'0¹ U j6;\8%mw;MW5֥@ݳ1yU)!R979-ZV:#{v2+=J&~gfbc_83E/ цY4]6'VWI,TpN]9'"q0}|&tb[+$s(Th.sR8&V;=XH|R'gFu?ݖ|gh?+⸷̌׉TE[2B7q#Jld&#W,{=ӛJV&)u%cWƻaq 5ş#[ԃ"d%A;@WGt%| { uN8F t(Je| &% ޻\ aRu˜{=@KOyޣ )P"e=$2E!lMi<&< 3=öR}' ÿ"_tslu %ٮ$IM[|Y|8_$k@=wyf*8Fã&rpLf#Y=<26~~<ƽo_xrۘ @9~@C*q&VKJ)'$Y""C&5Ng^!|.TAĿ|D'7B(D0z| 9)O026mu%zA!4sM;C#~s 2G!- 6l^Q +\Pv!a`IEhw[m <R'SOG2Tj.YvKEZNd&|-c!9Bb/lxP}T48=i}VG-=:Ly*(뭒<+FBm('[\ajlEMm(r5t.9nOjRTZ1k,Vpcx8 ǃ4iP;>ڣ|=}yKXV;`,}ord!ը/*$ecBjhyx)'l|B!bj8>,elg m\ۭ&RN7M1w p?2e!}$iS^l17(zR<bH#5^)R%2z Ҥ^Լq qik{]6gM;%&츩H A*%(C7r<П8;Ĕ y x0w8ZQօ}-TlXzx[:\rKHN"A4\UgpDz+XEYDp"h qi?&yFE+g@˺ɯeecⱷ}Y Gkć|9>0fչKi+z <;Ѭ lpz$ !8`0mm?d;2"GBĥ,@-NcefS]|gGAp2;SrO( Q2aHb]8|!_ _9K1޷5U9x̽?/&/Y1}]=W-%הc}r](ڽfwӛldL`Wq=Ae>{=6-* žԱƢrƸQ-yB/ǣXo|^PV4Dn/˵󟲕ewt[' ı]Sby\!n7ڳіe"oe(4@O>Y܅`z_r,IӘ?J7`x=B!Ga' J(( "IPG1&^trƭ+@ڰ0[p"HrX`oZݜmh}# $VN,pTpƳfBc.n~#C㯋afCeA$O+.3qC4+|In؜vŏ9czűt󝄿PnZ/8)̳0]TxD6?SGx1^zՌQ AO_5!/avMT瘰 5[1z!:!jKQW(ٴWіE9m'$8ͻ`t/0!QD̲Rmi ~ŪJm7VW=َ^Ra^<`AT-HF@ E,!9Ce;m&2o4߾Yld9$,3;l@ou z>7!=_^%)mxxwof])/ZnjJ!L"R#4؀@_Y ߵix~oH?h}fƢd5LF/x]?Qndm)d U?fu>f +i.yJe?nSdT](hS|1nƿtAFW=U BNr[ 'dCG)S"ܹ-s{L/e;(0!gJA7]}LJzNz$r|]0*rRVWf;! Ck,*EMck[9"aE]}JᗃhًЖ\2thAtמ *f}W` ӧFC/91(%ȡ|jZxA5wN&dQ2OѮar`~@Bls졌Y|!"g-D-1[P'0HXV|CJq}Doi{pNѩg]g{l|izΌ Jws4 rط[v3,dB+}ځ)N UXTg^ݰ}gfn.aHW] cBZS~jGbv8-XS=}"yDp'nnܿ`tesnf1 ]v'%&$)TmUJE̿."Q-Z=ccQ¢eM+l#g_5զ XoضJ_:MR'  oRDW6nOuyc_&(ܱjc!ֺߋΊ?YIFd?\dJ5F̷\O urt1C"I˂vPऺǥE(o[-LȾCg̀qCdwĈD?Q;R, *?–fxmmC'СdD~5=߱p'Lmd7 q݁~Q[O&aɮ!>⊼V/꣹U[c< ك@ r)߇nCo+9[{g1\v-(:z} =ŽSEIN J`ydc#-E]4\&,|@|^}qye,:L>f";C `-AݦaaCW9CƮH:%]=wPuuj\q O:oh J cɊ͠6֑3V]uae [ߩpߟP~x%]"ÀL'7Uڇ߄'p2Qa3L.fUOkBu,=ٌ*^J(̳Atf[4V}ۢjjب\pyp1 ISEI"Kzg9$F\sC:i ShD?OEDGK$OU1oQ"$u:qxB iuvExsfr׭kꢤv~DG{^|".fD̦`g:Y u(`YJ]KƶO̕~p~rye]Kڳg]'/}ޫ4[3]$/^ȨOdwEpHw /Ĭ _^uDDcׁsA}< wG`dnZtqu++>-/Oo0~8V׍x%[)Y:y]G3LZb|ѭV&jp۪[oAC/Ő!IYr֗vReS:ܝho}u4p Zpߙ]݆Ρd5qɶ=§N牫VI:]ێ kFUCN(G9"9a;Y1lw]Æj\)-tdQV Yb\긵o`"q cKv"3cUTMԄR$<gu!R3,{Z_ w۠taXy3o6i HoZQ¿Ez.oܨJ\VamN@Ѩ ݲ⨷:)@GqFhأ>NF!7& ?Qs~L%ltRh.xvcճT#nn (NlR*Cy ܛNBJ0Rc Q7J;7T}=JϠj6U\#WJ,l!b8*|R߈ Dn5^7 F޾oL;xFGsf m[ٸ6*AóM%3<Ňi篂f}P[|l-Z;t$Cu(S$dMc vfF,A#x- W\t 2@* cyjRiVu t=1/z NdJϾ:#H)NOXia"heo 4i 3&^eR-Wl\jbAݘY3&r/^>ȗ b LtKUa= 2Z"ĸ@smm"te[M6+mPtI+kcu/,B1A[ݏ #?t}5c+)胫dtj'Tq#Kŋ1k/)oOEsa\s >C|EvIc`oXSʹn~K+*B$z;GZ`A㷋=CH@Ma7=srT0jbɻA4oq*=GehGfXXL5vԷ%1uќ@rB+A,2cp3(n7a I,7%6`6+jdJrwtZ@Kj.w"=B>{X@>ZFi n.]sثÄRw5Sn p<2s ioF-?2FZUb$H9iO/xȿrdwQ:d۟Z=YBnRT/օQC/`CR>Ҋv zyDOD%4uؒD?Ĩ!$P2Rǜ)ҌCcrz&|>@Kah Br?Ŭ!߫'g 돜GGR91i[r*TO}jF4V.*)0dՖъ]K[&͂<7M摠l27(!s !Þ`J= uBbGcHfdYQ tS=s0}<{-`J૲F+#9D:vm#N:2ZUv\3tm%GoE|{=R*tGhR%Q1k-Vj%QyN=G$\ G"˚F2hO.ٽr'iXW5$:kBb+VB1Z pqS^E#/&[=6DORx! b^rvw:~DcΣ&Վ؃Z;aZF2FLdG:׽aYx}J $  y|@%[Xq;FJ G]{pBpMZ:TC)L'#p!CF^|{]Gc0k$5ZLBxGe j.jALJm44w$l9P!NWQ:<Pt\7`duT$RU\P7a~C된TA0 eQ+?[d/1]R~R:+ #o"i2|XoTC^ԅ@s+ Nde!)+S;,E/ӀHP:0 }1t.g;N NLulqJdw4'sT'ĺ=Nyhj ~C!A_{~/Jőo'VЧe͉]JǬ-}L(.Isg:x7سa!:jWT*;P莐@n+RnB<57l5$=_ZBpdydk \CČ b=?MiOlZLl۠HܼU+ l au)Yű# m9wwnlFˠ;w7 c\3ٸֵ ڰ qDjv@nR?T=N6Є-8;To0!1Xws7Z( LƧаy/=UKciV:Yb7%V_Z jdFGy u6)D4L _sk xtzJ:jzY)-ͪZnE?.xPq4g|Gҕ`rb!\9zJ`dJB>~.cN.ܠn3ajHa]>al|}M&PG·SxnSVv0iB6s&087 ɂ>E^3;Bq[hKB0XKȁf<ϡUӻ#K{Si`l^;ʙ?8CZzwOV}D)xOf5[ZGUQK֢]n@xoy4R͐izEdPNB?־8 'bƻ/+:6J2!D.̎bLԄ0w筹'rmɵ)tBN-4222ITr+r3drc5ZXnI\nt3n^U!m i/J) `Z($gLP kOiw ݹJ[|y #C|8"<_ȹvEȲbD5֢gup;%mWIYN0Q'F5\m;d0cBz釞R'^$2O'e2+ak0\P2%|a' qYm3bsn}*K?{]2%7;VE:yRJCh`CVk*)rBTR?<4}nCD1tq Gpz?HMŹvo]12_Z tȇhi:ovhkH}+L1tMԳ9AV-YgJ(ݤ['_hq&2U! zIxOks.c6#@FA6ʷwr?`y3aeKUIoe.c=SxJ =EÝIFi 7; َGfޛy'`꟰bדּVgg@㒆R9}ܻ9?awxVT8sYQ+6Ib2w:l.il~<.(E~VSB]i59*cudžNUocnZzϫbk)brh (5{K(aY&Mua ٗut(Ap)÷G\*7Ix9! C)/FAa9(kQpX T x<2, l$!=ܦ~j%2T_ER뚶DKl3[;00Lд tb=7IDO{dx 9ѐ/WvWcul ז:S'<^\]&JE<^w]'&^ ߊSԗo]F$V%zY&>ta?쒀AP h=֮3yl ;*D`̟kpT<5FgWrֿ6|DrV՘ڜ(ٚ6خ)SfaOe3pv׌z_R^*'`f!gע;\V6$ g#JO]q8$H4@^(.kWP5/n01t*m5r5{!@Xg) 'ɮDN7C!&}tr{~]{-Еdb.`Ju6֪ La#u>1p5"pNj <3n. }d++F <\_֗ av&Ty`|^W Gy/i(a/!ƕ -̟f =N$y"KoΆREur nB?NDPwb]{i|;qH+ Wsb :qOZNv#DEZ{MuD?閫 ޡRiseO'J3٤/WoQ׎{J?ױT'.9Z0oS(d|m!'}A2JwkB#=f|k{CÑWKu0bznFOʒʛ}^(Zkh2]4}~Htc 3Fs-R=~W q9* c.kHYYâ3[6njIq[{;3GUB+bwT0{ǽjܺd#2r@]KR ڟMm}p򫇰I^YX\},dӾ5lmD~ \tq]l4%j;Q= :V ؤcٵȺaAa~;c\(IHUϔ4iC2}֜Fn:_Sǁ!霆с[W*ޙQ笊Luζ..^aߞqd͘G9iOP~ '01Eʷ795)`h׃IZ/X$ Pz2`<AyJMzٲna`7lV~Nf5jdGi+9ihcWI#LO/uPZ&d+;+sISj(Wc,L 2H`ZEl3%fݨ$\TM{a}8~*J,EД~:kF8ƪjy+ȋm~!>3‘ HLs#VS,X^>cx]# OJйLSڸ.Mք߿:\|m+'nY9yc4bAIh:^)Ёi@Ȁ$N fCk}Z{S7_"OO?aF#UB?VkVW ̉C ֡0HЃ#pg/sSQJ%f$ٍvdg!85 8$lEei_O5TO7=\H,4r8 YM;ǰqAbgu#@Չ!n7=.. ?P7Hkܦm@uVkTd=G¯]09awĕ-M*~;v#a6 WhߩIc\ f4h>H9W8,Aii(8LWbfbI.Z}ԴM\$gFYg6' dY+~^5=IEL:xLo#uoʓ c>rQfI_kX Y "?a|_xU Bզ>O? % zMwA)<54$I~iX?s% UaJ#<$M)v:r-+:E EHIδ?eG>,ھO ?NY'+) ȠDƃ 5/ O!İU8tvbƻ*5Rm,MA-Җ!2Pj% ߃t潎.W_<ڱ bN;J✻Qb lӺt |?,I`:|B=;E@-fsX* b cc^Ĥzl$p\vJ~l@K)~u$,Vl< v?ikB:<9%L;=}*+?>ƒLItJ|z`$}$aFț;;(!Vb$Y(KJEjں#z˳{VH#fQx:~!!l ✠8 Wtʸ N^l}>}ՙh?eʨ,OCf޾ʂr}FWr=B̚s" *;ThHӡc ͤT0Q~t?$(+@K[b+jCX-=pq]au6s{A8 D\m-"<g11/tQuX_M{?&ze %UѓU@2]Չ Ud&@fӚejyicZP#y'zh›^lJ2(&Z " BVH+3DZC !VmGwslO{PxWG-K6Q]`qQh' kX̓6rbo:Sh)_=f8Y_Devv2)`9>Fʂ΅%οbIӲՖ[c.ua,}[KFZ  GJA.p:C쳿w]:u^ɨm͖v&{עQdtbk(z_I)c\ i5<0 2w`x\3 I BCIkY* *2^u__/S%:Ώ(͝5 4g,Sow}g39wT4cu;""ձi?ڎgsxb'5N)% #-%Ԡ.DVk)m%۹@`r5! q߷Q.7f)iG3)lQAmCvPT gKxRtc*GOp9+Ķ-P=OX[iBZ3f"ޠ9~|Fޑ]xw$7v׿ !E7o ! Mk[ӍN0,PFU{"k]GA>3߹mµ£ւl`j"J`^L:Zk7̻\vV+P,)NOҞsJ AGq%ƦoiqrJkרtmpx'>81ҝZ=jl-!:c K<[3*4l|𳰯JeYuu7g60_~2s)坟qXH}6-cg8 \?#4\nPm 1H&)ltKr=G1+v%\nkʿre}5G+=3W4MM70NT6}|{m7x41 &iY77 vj{vGc9YYf/C>\vL5ƚ|:1A>7y@p-B4b[?Vu3 4-8☏D&U%2 "s3m &e^er{^ҥY@jAXu.Z7s up9 ;Uy"A2%t^[DItD&O;+p;aHњΏeɘ8"_" "Pbuepe1C"' -4)+VNm[{S+zVM`3<49)(LriC` W ,צ_wFe@A_ך/%6!Ĝ9Ej*-Kg@I/Hs7yA_ G_"5/mA/)jMݰ w &(c9Nq4Iv#+JWцx F ]PaZck4o@_wt GĜԶދJ$QtOF8|BȁyDposPdxqQ fܯ`C@aHTbHɂLT2z>Ý KFl2{Kf I%PZoPɈ#?"VY_ե+TπesxDJ ľJQj;E h2h*#Dh5)יUf8&0546ލs!SmwH[6 E_7g,S269 '@}t>P xy}*7h #PZ9loGcc(u\'YX,]5H-pqL[KIeuYǩ-k*u-!X{ r-Vj##xт)'GDE5װ'eר0lg}^"|"jլ-f4/`vmUXZo C9U,5hVf cd>GR%vxNVWURעNrts0-|,>(elti`д~ %E&5gPY*Ps0VpG$H+Ik//l&WksQz*L ++F.e/r^n5Ab$wR )+S0 iq(; (}GbOk%,8_NR|<>+/,AW=ǢϷϡ@M"z:.AJe . 0Zd!߮^>Ejք06UXqZ3UtQ/sAS3jRfF9`%[He&ãOK1ŝ|N]?[ FB2ekm @ja8%vV+aE`WFYI|R^v+gַBT3QL=1̩bhE{`o(q) ^^A[7:PCc_!΂/!ӻMq?y˾GZ|uf{JN˟ `BuR,dD]HAmtRlpLBZQ :&€%ɹHR?I|Zzx*?']Vo~ MHr3Q5G?x*~. `Iэ1!ƂƓ+2eH%lE++&9)DZL{T qQnްgNĽv=½$L;'NuwZG7L1n53pbrc@ԓzQƢtI^)o'L:Ftqij& wu[U\_kӚ@ζ!T|%O,_". ^5azةGS఑,V bnOx>f ؒW𞱏WElrL0m a?Ǟ3N;6 \6 GK3WCVC^ep]0ߕZv׃R!'z1<?!,@R 54ݐ"C<ӏ1w =߰my)R #$BÜThflc%-(aqj7XGY|5'N}T .;N\CgYga?dtRzZ;b2l8MaHTswyl`WT9sqC*ePnW#|b%I*4oxH =&zWLHU)Ge$:UjY1"i(D#Nn$n˼v$ dr8'ꜻcl+2j6So/2q*s3k6l "UR0m)T3"V(B`vj|t!O$)=:ЕL27Otl oјY&8TW^f}J5ԡlADu/9"rEd3TD'v=ੰZ uuLY<-P/9],RCiOSI4#ØNސN#ӧǼ19ޥn'INbgWlCT+YSm9{_[ݛY]ly#4=\zBJa񱓊A iz =Ջ6d4h iGUm eDhƴE/N2;-Ƣ3qSٚR=fRsW ymz_ ^(q;Dv Ϟ^`mBPN4h!PďC XX k!1뭑X蟧#pû=%Zۄ[lygtՏ mq.`1 tp0zI4Kb=k<>[~%R=.Rn ?9B_#.8Sh'PHY2Zyk-ʩ`}?r$rvT [;v.yҴmLQϵTo>2dܜ0+f͗O+$î#7mm[K+5g{y;vNU$.(xDgBV@Rgy"$511!tu"~iBwcN_ǟC4\fߍ>3Fo)]d}lkg1\BѫB6ͼv/"f|;ru3AskcX= Q1paWܘ#@Ɋ Fi]6b8;ѥ/v9jqoIv|y{Y]5NjYK)ۦjeg ,C3bHn+K2ݎ-5]E$AMKΦݙDžwѿZP$&4!q?F8w`y}$b)\B|e#=)Mv7N?QS_cL<=%ooy<,yܖuRdN$zs}VYsR?+俙'Iw˱Z=kB!*#0$/3r֗Fර z{CtM.lIhk( av ]Aj 1cb!MHrIY^#yPφ| -Ιh(|D˕x'M[ zSE2:o lNSy!Fx.hg<);[ uR3ZcOqw|EOއEsdr//#'r΋_Artoljn4PwV$ x4#BS&Fj̓%<G--H`_c)zG?ܯ/~{ZҊ]:N.T7 ?%9pyo+j!,mkhs*꽌E#})qh&': bb|BB~>bY?%M = eEn]D\NڐC P')BM1cQBg Qh<3>o|{.lߛ n /y6{ap3Q-$oP쿒F?OK{ dȼN D `${E'r8ӥ?p0?nQˢi@e7kEmZ`ڛش<2|s"OGf#AzKYUyePa[\YpTR'`@Nr"Gx}V/QF`>G%YW>04~>@8KiCʽ@ RP])@C#|j׍U:MqqUHqssyfރ`nxTnF%ޯ,1΍U,hwf-OyL Lv-Nq6l\G{.}kPVx4iP=796inيJ(.`S+^5_K[d !҂o^a*`x1歌GNKZNA9C?YGNbA_Q3EҒJg 4lu%B\H?l"[hk\p :)L5ǗTAչFvA˟to"PxΝzv\DЫ45/g@+DA2:֪ƪ^ rzٹF*ߎoTKRݛ,,}H]q!Dg. z$^jX8 [=J9! xce> >$!FWݒGN/HD(B&nO1hZ'D5΢thiC;6ř z0XB:1/X$8Wyo)OT*IwTw.lkE5dőhlXo r [}?\IGN=WNb,q5яc<-!8+F\,.>.랏Q_O-$y}! [- =ƚ$0ܿ 3XZ׹a &Vtlf--NNM~n.wh\ Jק?S:j<1gYJ㗴d$KXln3nP:yY P33f1ѣ$*~|YIrٶlWQˋ+"wVcw:y#3[vLjЬ=޺K 2J@8nwtb2AR@/]Tϔv/&i -k*VP錑b<-T QtbYzb_6@@4*Cuǃ X H>w m-8۰MK]&7g$ l\k%(veӳjF~UC~i*[ݝ`)v&vPˬJ |%5 f+[7N~ /:Z'eZ?[чfoo&& 1X\Ѱ4S( `@!)j۔C/:_vXW?)&Ple<\J#MN?ټ҉%PprϜH*e#\$@ L>Bc= ]TC WXYܙϬgBo »OٵrtSԙ#5 Frq#\!r*i5'/bǫ2)3 N藱wܖ|*_ 8,߰D\x:ُ!U&/:*OͿܷyqqGbvpG66]wـkvW8B!p ]Bl&A_ mG~ONrnLW L A|QnS2eW,a $&$Z]hWh7Rׄ*dHpQ̡qE]]?._9:҉MM ` #zl Q=+˰nP츽C9r!]K76jC48WXc;Cl}z6Z*dbPa,יh#Q!oJVWyr-8AzMC47E0HHD{:q^m&9] Mi2 TTۄSM!wZGDc0xI=޾ $#1AÊ }(=!C'$A"_\_L$ nP3{TȕgZx8;F2zQpEƟk=+/A4،2VX ;eIZ_YEup ˹ֻp/X¹62h'b̘hƙ>6.TH< jgDi\Nz&U5|KBBPɩVθ?`|} \ 9T/&+ZO˳6tȵle&p u)6=7;N̞D?am- `~g=aqc "g}9!g]xB2QVKAd,5?&DR˗R2NB}zŇ)rX-K\ձa/A$dI H+KXeU|/5.E!KHhŁvWv#mwrb85)."}q^L'-]!ӴB/*ӹ+oM.RrlaGvB|<Nj;mO@5d8R%7zQ\ɷ_[x]>`{I 9jI^duɿ. _ERc$eohF\y a%H5u9jG"R7m*?qlb KF>Kx\WqSY|=&@ SPLK1[urŭ[+ZWs0wn^4b7 G]G/VOii߲!|0p})u]x_mf[&Hz W]12 NsuB1;%'ˬO yx(J[3O䏥B|RTu5xb1z0Q- đ vc δf9AҘ H薷BQ XD( m=79Zu\qnUܕ}J5v|Ѫ2)96E%kG6vT4Zj^/ni,jO*@pQb9d&޺D]&p-I]pv1[-ȕ'[`Z\yr*.tyxpʪ2&}^Հ?zrxaE F_8#̽)iaéZax:DnA`: 8I4YX1kܼm2l՘|'t}a'1!nO}`J;2\)!euε](T6vvV[դnlӽWOn1zԤ^PHbp6xDsKÞ/4u3i*9D]>mD jp8/|"{ }R醍@ܴ]*T8b^!B9VpKڈY5 "muǙL8y7'גJ(ߑ WƯY?&OoA:|B98v#7}84CnABCTH'gnbyy+^?6K~v*c_"߾n6 0r\ODZ'p@+VH6:3DXy\5bguVWC:Qj#]zΝ+b#fK:^S-\XB 6ìED1Mrgr+c48J˲ݘ]zi*N"R-G7Jrۈ'r9kjԎNHO|DJR(xk I|WVn=+- Go#@DwQTlןy¼2H1:tГ r@`gkj̫#,d\h`m25E },Cp̻YAg4+bZ+7c+[켑aZ 'GkkCz,Jt@" RV*2'$cޓj֗i±cg#Y)|,i  ' l4ƦO4X^B^|Z*8s}"CVS3{ϽgbicčnQL+d$[ JDgI[&Qu]fhBj~p-[b~{9luZ+d\Qpt_ hs̅|q%'ќ&Vza%'~䅔ȡO H;ƛqIP%ӔU;Y`.'ඖg-bv bX8KNn 8|F@Ky+\߭@C;M'>s *ﰄH>[b+) ,H0RZw)x7S T rx"Vv9b@ן  01.6L 4|GnuS7%&GDMDpft GÓb Id썀=ۅЙ0sQX>(%X>ST m2V.3[_OJ$݆#TmQ=-ׯ^o>%>b3L >ɂ!&t#1>Rd;2^]51%iT}<_z+#e #Z?VL.>=0Q`#v$m+ĕ1j1Kſ0= dqVsT>-T ]@BXZwY:Fh y+B`CےH)!1&Hءݕn[;wDvL&+ E2~أ9YEiKe܊vƗј]e$nSUPSOApଈ&kn}0ʬ́J\/= 1"t ?I{q.Z $JD9xdϓ^Jno(c3QqB_=?[ˀQZg8d2&p)uk~1.vJWìM:CU8$CІy(h6v&ҵ,>ѡz)sb ,N_B! ҆W5)^Yx"1̗u  Yq,JgqݫbLMq7םR۞[lcFj@%iz]x0F  @Iں!wh e}=?8ؤ9R !5a]2.,$ժm=3ȩ!(ѐD?@IATocÁYWsZ:Fvh mZ_k0,*!pT#vo %r#\3ڴ%|og,솶19о'2 K@Re^C8}ɐnZG9wW*: <]T-F@Q0{pct'؃F^œrPXK5ޏ 4Z7nz.&'.8G WU;g F^/v՜۠E_DA놰\ !~&XOI< gAquw`ɘ2nĦN&-i 4Pj2KY}6'Nw?6L.$ UжEXIY!9ս-`8=w-r[;@AقJ*O!gԜ"ա ^<9ͷ3*SuTRVdt{S<+It ?*mŐrcQvyK{Q*@_)7n FW๕fˁow5`a?47ϒ%d@aS'1qSm[E.>8ϐ-0l]m?zΓ K}J[KA-P }nShgbg{_657S2H.K%Άq2f~^h{*$/U&dB/wkQfC ^ȵ}G(f;OJ;B-CQ h/$;{9}1\w n0:8l>,$-fI S$Q҄g-ল"ʩbIY USY>tݣ,6|G<:A0_[p ]^Rwʠ|FIzpo5;2_0q@}[ S<)jZ¥ԏCe6ٵWerDͳ>QaXZ,;t;eM}M^Lu蛅91ZwXOkΏpCjZm@#IT8ey NSA%,l{Uq{9EfD alOyOe뱕P3#*o*)v~Ň-n_"B9F=Gd%B= m[ks(_T.Zy5gWH7E[nr0\#@uJp+Hڟ9d@ uhĈMsO섢ҪT=cSgpL{`ϡ~gL=v? 5E^S: bBHk7t\ 䟤qv2rEԙFX mk"_%N'!=Ö+W1q t5,_vAr3+ctŞ0F\`%Wϴ/g: ЦLʹ [i-Vbo\-|c`V)<1Dt߶`꺇 c|#6G9ğ*W]m!jȁ΃e<F_SՊ uޮ3]B.7Y-G!:7`MV džT$dRMªdWyҸ9OvlƪCɂQ2R`*c`"`-}A8W3[LjTGtF<(?]#F ]9_{ݸڦ(@-/F $;DN. ]N6jm<_LJ?iZ.s]5Bbp0 P3i,8I֥G<*h$nm{D ˼>.GZYi'xG ~n"lX^cAM:`Ú`l:I#̂R~msF68_EOmN7tMPj-lFŏNzF3jXkb C{ Oy1=Bd^UM?J&6\`'W;w#2\}}*1-<OOGd%1oI0MUCF#o/T[0XЖߍ=t(@̚O#5ѳPMrs}]|ާ7?"py}Bֶd4fFFD/AB5Jl\3 fQ@̐>8L;mHgivEiex'.BYA u*hf-q窮ּKS S W^YRߺԾ+mYRiӗ`6\WE2Y O.,Gf0f##qȖjS;dJN&vൡ M΂Г4/DUbTb٤t<4`AKٟZJa00 Ni$Xe#)!WX\Fv?]( td9 R}-w\R㈗q?zr: ##oɭ `wԵ'S{:pg#`@A?-bH#Q:`zʜo*A LmqP@tx4n-Z_>E`D V;ŨuRdôX#҆]u/֣nU'=LbJ(oU[ϖ$A y Ҩ ;egg`T&F,r#Ks_!$& 8}ZgR7RcsYhc160uR8%pn'a6#M_oW` V}E">^{̄㩇E9rk9 V3Wb^kBȘZQN6ܤ?[")M:F Y:nVOa=Ͱ p$@68(!Apdw5= .ݬTäׁSf<ŴEÚIK)hԙƳ>h Ƌp@fl4ȋW2=CWd {IЌjشKa,% DŽ}3|OCc,E{V n lN9:;՞'ga,0A@kҠzRmskZ'W(qSk HRլ̒b|/IiyASn-lܟxTDQWt=]R >=C0yo{jCZu&)_I>D( O86v4|WH ;^wڴ5|I@k3oC~@V2Kf[Q3묈"`^j%nh2'v޼Ɏ{.\A"MX n=Uc W{&3`OvR%"N Xrg?oktwE@|#M<5XyCGH벜86H `ll2nȪЭfrxžWR,Kf>az6!\,Vٲ^yl<)ۙЁ/| Nm0d˧ΘLQXc$'rs}שm<P;`͐O(ђEŪ>-q)5GsR"QU@z|:jzoX szU !]T֯>vkܤ,ٖT i-R;eA).;R2 O!PLx,(` .ny1Dd@ /xzy7JC x99wa 0a9hɅDYq R`h~{]='C'Cz~m萕sHu)ᬲ#QڒNgXRU K ~s=0YG\Wb :d밚 褨WKA@)[nPT]Իڜ!P5HW䡛OV@7J.0&aV+]RuB/&#<$>D z.qFVV[|LTB)%ՓtG2t;`aE"nP5c 9#|kIٯ<'I@MÄXZ+tjw jw4IQS;͉ze]D" 24& rR ~0J0>[7<tTz1%T}@\Ey=.RURrP6.s_F`!=b-L}0@5k G8nsc}{w r\xBR+י⶧{r͟g^o2eUyB6(?4^jîp7cl=@ LHdD d =,ɓ.64 i)6r,$_ ͂"'\+(&WSKG -ƌ2S\~)=l2FpL/v ȤMڑ6 FI2X@@ġ8'3x{U̟1 OFMFU>E)t%UÞk-e:( bRy2D{>4en=ʬ6R?6#:R;̉ {_*\Tux%7M(Ǽ$t'!;Oy%sf_ߓG)]{趂f;bQ|q#J/6­[,Sͻ/.F.nA+mNՆ\!y B~xrɅr.֠O406QT{+9L1҉\F+džAUv^OZ@]ˡ7 ίŷ0nM %٠M'Rl 2RdȂ+S fɏzһoI3/ݞ2uX']w (Oa:?)B#piܛc"}A_"4w$ ] ЪqH'I>{:hf39x| [j17 I.迉8lrT֡ D(~ZnT\˝sWǗ3vn#Mt]Rwil\M:I76C[j+7J(wz9zc+NAndМ5o}1}$]qQlX؆)vC$;3IWjA2H3! ϯV0\4i qHY fh d^6W$[*ĄC\5ҹ:$ph9 oDp7YR`csaX,vϺ"!c]!HTBH4 .L?뇅OZyVlGv#WeSf0٤2z qwQfji6-*RAAH !5^2&Zr --` !Cr:8QcꒋZG.!4r/a?}B֘DljCoQXP#\7>:ǐM*e"iuF$;-tJ _btR}8i·!TB2ߧ%Ey))lY0D4x+4"-]J' P:^Ҍ&S"eJG<[V:[Q}7XK:ҖeZx<["q* xK)x NU6Pd Uɘld vRo&5΃#O1 vʚt46B8*}tGl!!;SVL'D m~v7LԻEsS87R[_ ';/<9ZWk>F_$U'eW_!h:t8'6!kWpPpQv PgU~g4zsZ/r`sȶŒ& i4lAuݼ>h z+nB@Q+!V.R3I7 =Sɦ>z`Z;?aB>S瀱DմQbz"-*!qZ'gOsdB G>ɜsȳů_U@N u:OڠQ i.,$10>0::dY{r,op'Ԙ} ܙPyUh(F}jaqQ&Qǹ*A؄zhٽFIP0Tv{M$dt|$ Kc÷,[IK@ Wu7gQ@9;—U[wӚh /<\ A E/'utgSNIءFh-_v UՊ(T?x 1uA@7V+$6 <3л:Tڿjdhzai@**.ъ|<}^3 PB6o{VA&+ߡS#vr#?JVk*Х+`sտ[)&i)\Ck-DV]"2//CuһI`@EQglWx4<[~PƠTbRWQRFp^#Lі"i?LY0B9awp%T\O2&F6pjĥx inI 8BO5Wi_/y,PwdNB0vԒߴ6bis9)$Y^Lſhgl?1Eu\=a ۙʴ~/ z;H10@^d )% O&px % !)ƶs}Ť0mm܍r= \pGy,~*TvriU { (M$-mnE^&gj.^߈?w3G\Ή_uAeSxiۑa LDYB ]n*Y{ ށE:=RL#@csx_mݘ(dv8+,'wKe )&FD^ÝO\&_7g&]A2m>-doÀJ3tc=pL%dR W"݈3)gN{*_ C]O~jI⤩q>)jg}k"|n_'p4s˅7軟#pI).w䈔*]6AשՍNTHq<66qUq+zFU++@ 9+ E[%to]-$%1-992EI+کwֽ/m`ZPyY)aH>AKHFn^c s_DL !:RK)192jm&;~›e@8?||eH6IZ3{N0`^᯳u^|՝]dFzČL[Z.+ځoAbAp(4CEĎGaP*o!%Z2 0g'LEtpnuU/܍\Eq~5qט^9(}H]gl[3r@3nu,_O#γ 0r'smz@Q&~xp-Ђg.S>܀ܫIS=vh*_%'}nH3H{ֵJS6ZVT񳇌(eژ7'_Ң@&ņ5=fB"q(Xn?c,b}OGݕ30^4gڝտkxy[~anG_9&9s96qԧ|n`]\^|]ؘ*a(S9"0/I$I\OCyTላ^3{uDpWa<[= y&.~m(BCW'$˝;,3Xx2guzC߰rBQ xfs Z썖TLw4pgLn]akT~u*Q(|pԤK+rL@ʶ xwQcsQ5HgfƕQ]?Nt2+-I`]T$6=^)qޗcrtik&%Z 2GOؓjR5{YM> eհ#h2r"/>);&/2md7ŀw%Sq/+օf,nnq{ix~A j7!=q)ŭQCP ~^ޒKCH1wlʹh\ 'f KewLL#b+C,jy=h1o9飠 2,.O6}] t;\ a|*!~v x$hȽL:+"lUZQˑ߇[ EXql6=! T6 - % _Iz3}!tOiH괽3aW4v3+*E=E̚4*~QGeD ew`'uSʎp vyMu#uUػs|ړx'}B0`AJrX=]y/m]?nlGkwjD팓HYsin;P X%=\֢WǾNm@~,m4LmO#*:#N,6 tyD'e3Mb뜣'6'Ҕ~™c/*$DM*ld ~d|lwVV7 /~B^@Ft%+uXCg*9D` $>UZٝ)ƙxvw:G jFq ܒ8[Ng%шf<dz1Z>mzF~2)o%INdYYbaobqb`h)& ͺGFO~-:0Ns{QidIKqrb&;KsieWNZiz),a i=8%3OHA3h=6 L-E:f*^HdWRLNu'ǯHk҄ L'%9@[Tv[/4?,& hhSz-aFG`6B!$81\洅/ pŚ!VRƯBG<bXwT< K9~\Gg؀k.4fV/NQL|G\EE=sM AcT]:-~7TCGDЋA2)^3 na7 tAJ#wk<] 0VB$E,سkaeLvƉZW]͍$9QBP$J u z>X:4omCʩMhE[pT2PZj#oTA Lv7V&^gK$fk(=׹nD Y+4@a׽kpcEE\jM vUBo > ɪ%09Y!t|oV|6DWŽyړ< CPʙ=Qv ,_تu!DD鳦Ǝ^:quPWa㯀C?T'EXzN^ДM]Hy$6xh4.D|J_@ea-ǚI']Yct;(&2ܵc9ԅylvд8 A:e6\eI>Ȩ[a\@FkDXC+r͟Ƙ&6Z!MŸ`L&w%7ˡիDzaQF&< 0GB n|.S3sϡ$kJkt6 1HA,)ZkBgn6&ǻ;.Q8y5Y$Q$D(5 BSfhDrKU39İ6#JcC2%H.`35Kc݌ҴYٯ  #:Zw6t7JvbamqW:{y2O CzF{/-0AʴRH#f@ u,ؐMU؀64DdEfucQ4gOOel^jXbnD%߽ } 74J?W K$ H2B 9w%n=6&ޟhݶAgDdQ66w~_cN:PQUW=60|aM zQy jd#RĖ-}{@@i'ֵTnW庶XNH(.;qKں곜"/w=mDVxW`Z : M:QU擣E1<(hp_i:FyAS/G|ݔ"oX#0P' U=0bvr4OH(z&G.(ݧiA2Ώu'zO̹RI)|Rn(p=GrybnP˒dCLB{n(pb Ք/@})Q F=BInT)lgU24B G9 K(~ x`͌憈6I;OZyHs!W' ro*\3'=ώ9EkVm8UT&qARY/LO],2,\C &],Ԡ }8CZE m} ]u+W]5u_jADe1+`A N_ ť_Ƭѿ䴮lzZ*Z7zCʙإHQt,lvg̎7˚B'iL?0¶)w69ȑ|4)ʑZߵK+Y֧ʪR'[؊GgZb.D|lc1S0k0bbCi0r5"O WA{QaXb9rh HZlM'ۑG֐A I@y vT=}~˱cJ;zmţ.t<}iUZ^$Y<ܶݘ\+eI<8^GHpϸE : ްCc|p.60H F / JpMq*d C̢0l"uO6H}5SX;xnoS&DLq6O(KzH8& 5ni'^h.4V9c|8 G]@!=ZJ*r ڻ"PT't[, 8-}aD-I9!B쌏&$c: #DⳫ;Ac> X߁#n^r@iU_o@d09X"ShH9wz-A!7R@\vgHIjq]V2|x- G,.`13f_wiꦓ<2c}m>NǕbvnZ )Sw%0Ɯ4eqo7d'}d,˘dP)8PV\(Xm_;8-FcZf+A|tMr \Q!>3 =O{%}w٣l| bBDF`rR{,FUnɝ5]M \𴪊jD]8_[zNYw1n)DfPxֳd[fѕ.L {6.u\c*pPFdXCrCar oƨL4ٚYڶ8x9Gu,Gru#ܡ?'['?~^ սvz: d,p"}ݱ5Qka{"u= }z@0+fV%`RyK˴q$UڀDIV/Na%x#TCkڈ6I5q;)=" N}kA?2lgkGn-b5P2ЈTHs1 P ԋ"ƲF|aa[N*jy.蹙"\s2sӢs {cc_;+,H |;/57H e3=*%G1rU."s]E̪"~y$hΚLIMzϸ<*wC> DڧgN$݉@u KQ (O3A)=`P$f}ˏãJ^K8M`Oi5,}A :nTk3 2,*8*05ǎ7l]*')0I;&yd^=0iOìcpa$;JtRƄ)LV7^ l9 7' Ђf*ax4"Zbrtb!@P9?ࢮğaK{.<#Ⱦ-YwnٖuTmxyW^9 r%8)D /D@$Cte^AOy$thQZlwV6P N4/nМ G(č>I nipJ90%h KS+F^Eo} k~9y'N"^? 'YlujG+a=.`&ƋR}7/qMݴ|-!]Gu T'zP&DOZft7G|LOR"a:b) (QZ3{ FW eyֆuQЙYU[[*Dz;!殾|\oX^W !l竷 P ?f/D)|𘅀\t?M<w'*?~"ie~oHH!GkOtTI=_4rNb54ǔnI䥎 ncc儽M.`ڏ%B 7^ӂF~GHi'Nƫ/u1|@7C°RKWEyLйea46БCly*pXW۝ɘX XGۊ#Nza Z1+>IӖ>ukG ppb Y2]mci΅wMDZ҈&9 (c?a'mn4S y 9&~; G+nӂit剦 l5⪳- 7_)tfL5sA/kJjZ Hԥz{}֮-1ŅMA'7sE`kӝ.J)BmkX6Pay'_jQJZǒߘhߵ Hv3?6a٢V)p[v[N`"s4 %L1B5ɻXo'NwգBx # ܩuXѱ_\K z6g6O reE8BIǣu8 K6DZs+[uEx$=yP@]Ŋǘ>I?n8# -$?[HP_9:matE<RwR |qQǤhC<`f*I`A݊LcH/N.$Rb:,C^9'9PhҊU*0w@PV_޴)>h q7jUHȃo9̫z#?ף }e?U0*4VMhX@NWYwD:42Q= Jj>6Duͧc^XGܾ( |_lM4:LKwMJFH%E'3!_{Ԥx*:lj(Y120L'j1V!zפ iRs_x$SG<,/ an͈D$ 5YOp8^ Pď"wTkk3.i=3_vK%LJpx2/qx3hC2M A4### q #A_V,2zS٦=ٴ KLSA$ͼrPq={I}l:2YZȺm1I\_Y|H6FgQ7'w[PK,4= sö<\U\&Cf.mA%=Ɣ0w\wXSZN Pt @Kr }$e@`<`!,-۴oܫ+fDk*c)`>Q9Fe* mtM:(%#aԿd ĭ>wcBd<^u QB4zwe!jfaq+YF?/eiO,Dh4j(/n <Hh@ Hи I",Q,&H$@Jg _Q@"P,fϑB, 5%"#I2/ 8ߎN|CDᠻ=[l| AeX%NN͛=rYU@UJ% t^q%CW-|cl7W9A{F"b<G6NlG!<wHW[dEBWfϮR7^D2'ƜVtPq0碣 &"s:䝀#qM_Q4e,4ɸ4*dO X9m껿̂bކ?[@íËp iEݸ/wt #'+1RKqUuU#WzxqII0O*[]d=mvpH/~mʾ \L̳b;X?Q .G=_[\|n]LgXжti] ,Fo4c[rk&,fB@~H}5,W#Kz0*a2YI$Fg;!rL 3%RJX8 :(;$imK2v^;]E"w`ZOyW8 O $w«l:5!cfDr1^~NI!#^#mE2 ζD ؆<Ŧ:@C"1~9_o}x5%Z}틲 ;k;F*1E.Tr[}J/߽\zpR! \,לYm XZ]uk0zđ j=xT&Hw e,|xؤ֑}Ƞvɶl{v+YPyD[`vZ\x2o[6ԢUOhAb@ 광^HLL"g|r;(^vn 4"aJk|D8EK!#K|* WV3'fC pwY[-gokh0%i?GW cRu[#F)&ŗ@f9 '|=:mL%»n>[.)qCJg2dcjld5L_bgh'(U5iN+)>#~~Tf;  bߎni0'&6IL`͇cZ9Va*z0Tc8펖'a Rww.D&p$6cI/bΖnۛzuȦ$o{Xdn eJ![;w?d-}j T)5)z§޴B_ {#_8)o\4Z;Gh)+<,8?Sݬkfm^3lş`k@ُ]/-G4'mԠ{njKZdV)ZeSH|߸.ۖ&ur:' EIV6JAk(ް_q;Y 諆̿UîtnT^r6L˾WIV[ZzPE NT|'QdʲoQBdΞaVr] E;ڦbI No -W/!}S:bbUJ5WT2VSI:]]FY]/mGxE+*{q0W;9+#?l"͠CPgBR&z=KtA,Sqe r9&+) +n"WS CS͋&zEѷ^3L)n̮/ гpS4vݚqXhuMY"p] փ" 7?+2A/0S%-W+Ab.W3vDI[oӂ[J~|U49[:wQq!f X*p2Sm3ȧ;:sഉ'ZiBUd2{#;o'zB^kI04@!en'V E{/[j8_do{۫U3b)OAެ" YQyP! >{M+:FwW"t}UI¼d\U)\s>X[6”fo S_&Q1p3IE ?*mpkvygNufN̒ zo(Ig@"l *cL][,T~QȻHW^S'hl`W[T6X30MEH0]jygatz;1 78r k g9 ߩ ʟ\fFQ::L!-<"m/薑 b<0M@C4wz nqqwu("ZG00DR!mE*^'A\c CHPs"wT۴HrJhƿ>_p }>'Y2q1QD~gTrbvkFH-H 8Bv~C*]JGAurz*zg:z܅i9 bьQH'#:\T zG\ 9PӴgEmtwJfati=,V=ܱ}_7e=n+|SKgFX*@uNu3TvP}iP(WĹ;ꑝDJrW*}J׉Gg) 4JkK=%ea1F Ǒȕ ~9dns}hSl ̮siKᣫ\QKRiWn"OhS?䆀gk:@f>UQ|skĩ21>L7^"c8/x*#"j 9u _?`&0)1Ae-1rHW{!>ēv9K"!cRN^{ImpFrݺq;}/OH- R"(^لPc,faCgy*C K9;s麒)|)[ >{f2P=QDZq†y(FpRhv/|}Kk ;,@T;"ŴeChLp` !՚X C mJJ#ަ!+'A(uz ?鄘ݞ%3nɹ"=|b[rb(o ! ń>|XF6$h%%6\ԧY H1{odL|%#Fmz'U4Lɂ@5؏':H:.mҷU-Z@vಶ{M ۮ Y0˕7鼾uL5vW^gUpiV3[ɻFzU$5]5t@ʍLz7~?X[n\Sm{eF//?@m*0vEɦ+pI#2 >Wr<Ԋjo!e0.xhi@1<>QM[^C[ptidvAwYV(qz#B{ѯ=5<=LV rYII[{Mg#15 0a4DqK [k}'o?ra=Ė,&Vrl74|C㚙nI%ߓ+BSO%חa!JZ9,:aBC`y=P#)>˓ayj󀘉i],1)#g=~NQ˽S v̭tfӹ0u˛.:1(AC6ʺM%\J#ҭ{O*dɷ1ZEE͌׃ p ck8y?)w׵rU&Wa|a\#ûO(x/7c5Wq/՞Yx@!C)hy*%j*G+ L{tk#,Vۢ_Zٯ 5pKөn:uƅ^(cm1tpS[AIrZBb <';ˏRevE;ڔyPeމY+#T']P+%}bDSd( m.룈Y漤)X"͡:ARzR9f F[P;wǨ6Vٸr@r4iUNИgaДWk ݿg#5}ח婢ջ;cC0?55իy1IDecIBܝ!(@hs7Qxmtz+d4y'!Ds^_ng40Ldp@AbJv rXO`)OROt>DR}BSU 9{i +dC{ :v|kY촨gFc= %Ϩ`a-Dɦ`$f:7-T16 ޝ Y?WE"/l_41}oqN Wԧl¾"p>?KvW1e0wQTV FA,z1(x"oY"ĩBWY\ 0a]Cy/^6",yt"N.מQVsnyx篅7$mӱ"WGwQ:=\Wz!Q2 1t5jr$C>1槻j"S~jH&% v״S`]fj﷬aJr? 9D AW Ӯ?e2o/Xf<$HrPn ڬIl3&972A.@&E%u[J%O0ݞ/N~K~q#{PD!]zK;7:t%7 T?Sė__5+. |@/ 6_؎^\׉%` Ri~p$tc$(tWu~{#<ùM"0űASV%^Yb4Uj*Vj}4NHS >CouD,ts](O?v^Z6\^??7T+Qu41Xmۢj+2Mhh$`{v6"$LRt _q? $N fm-ĜZh)e6 )3_13)xMMkR;ٲ8>ng׳hzVPOe]gM$>u?Q=Dg͆vU.m 8A`gE+Bڌ ew  QhO˕4D-C;Bnn^%VTMc_#Fd-U2 µ6ɡ_2I'o^!i Wdς Y3ϛCQ>Ti-g@MYK3Fh'잹C})&H +Hk RQ!ѵb1v`H-,t v"4k~3p|6_moe:u>te$asKh!\dGy W-/K&ݶ`! Q-rCq nLKS;C[@5!#mJ7.6v.BOɳw Š.sʄ9xm#I0p 'k|:|8Z=aɩ*D*ź򂎚ːuu>ԅH&)ŦNs$ftІ^폷 ?"i;JϘucaT w[ zD&bW ĺ={I`@&8.(/Hm5@vDXo;L9+r ?W`|chc7B^{ژ=9nyJ sngl& +N_M<^c1?[<7HJY 2_9.j6. =T͝LacLѥplWBSQ8'whBa<99S bfѮ#qc4MiޛEnJ7.}'yR, d^SG_%=SZ=)o'bm v(6_J;Ӧ {neݻ+fA+ѳ)ѧzK#Rٛ|]6!۲"t n_`'جEq_'ʆM+G6}G=~wJa59T /rUu!=ܩ]7.ʲk2^5pE}xj;84!9 m{gpou7ɤF3X1c= Ko7mm"qRYɱ{wl]GsW';(ɠ(RaDw)Kx?5a 'AhǴoctfO&Mȫ#7FP\©X^FI?Y%y>]P;~ /4YDHa8`KsGekӐ֠Hrc(n;7}?292q#$|u}1YGn+>O\F`9w0l{".T쓾G~( $$n -[Z,j+ܐڶuwyNAma ~r'O?RыCЕ\'y]%Uz E\_I5CPk7j LlzX_r0%QQ ?2]Y|]vL xNlL7*r4@ PW=V 84D+'5&ҭy >ft#Yk*wrR'{B?*sDgF.eJ5 TϻΘb- \|*:XS|ia%; iZ[MB0W'AQ;N1Bthz)) M9fLOm/ƀx֎U29]fHAeQF+zW~h5(e֣b,LJ5kw/1gVw0 U"%v d;:MКh⋄t8SdAux7]5^:*H)s%sQuE6ɻ>?H+xctMt|57i`gVb%Ʋm۠~ zeuڌ`A4] ζxSo({Ԥ}ЌJǨ}mSK< ,%! V?=G}כqOo6ΪX2mں3?dZ9Xrn;{!G}^KR &НrkR]ko[9Xb%1A+QHmRŝP$g*OĆI5V42UK5D$G pSDBe`Mxz&nPQ9eWڠX᧋_zҴiHGI7x6$v|v` sMTpZtѽ*@F,ۇz/ν>95ĜsnX3^Rb&F_|ÑB.Y'7T*F+ڦLutElȗQ'ٞy$DU=uw$} OtCNSEImVS`ڱiXn!=C~Rcm[ݢ3̳MCW_QE=9|'I{`%[%v_wW@T,qO`T}f؁VmD&G(ēUcs͝~+f?>xz]~ Bqsp[Ȼۺ4Q硲|}? rx|9w޼k7:`OȑZ9~;hTc [$m6m'VV%$-- wLt2+]7׌`x{d]qKp5MsBDm:;zF|̅9go1ij߰`vH)G;VXM};:c{HB:6MR*0α`4w;X + T_- 0Ϣ%X=$4%]%ѱ'\,L],Xdvx|h 3OXhᄦ("5ݛM7HK(`csu  q/eM#(k6`=u:/ a0XXseu\v$^ڳ‡$n|ӌ[̓TP}@چ?!gԡbs]4{x&ݿn :#QR;jRY<%͟jf,[f5Y &-Q?6"k-ܳWw\TX;).AHDRye.>j!A<. SKՇ2ȀoENhqIuY!Cd0sys*v}t ifc @PoU u0u [R`rxR^Mp|U5AI$@n^)A8gOPsu~FT]y\:LHVB}r 'ִpun_ 7suab*:jm& lփ}ABʇ3U%xV5{AHtI` kZ&xρqqR܉fT-Xi _-J="][@\f*dĦ; 3 @fgP\MNZKREq7:< blC>= 1,f.eHg훹?Xmk:{3痉zRn_셁C )JY9;uϐqkid}EY 8ߵ?GLø˼2Yޮ) ~TqS>e(˭1 d6SZK!P 7`u.-`U˴:ޱ4Sf Áw$r6&E6;)Y7oWr-7:~`23?ZUP9%My4휈@_<:[h#TTOgXp)lF$-|]>1Qj68#@Ӑ-j0^=K:* `pO?Nҳ lg&a_>b(AgeTGyMbDZ޲gLț!~;HP_Ӂ~c%R\t \u9m @}ᙬbf]jeΞYw:T3GU-2X!_%9򔼲TдWsaz .&yL^8_n=u)hg,&/9D|4ORO(0_yC6٤RJ @BhTr+{df` mUy_ɞ`2e.p]pݘXxq)ʣJ`6_%Kk [KK0|yfRs.^bf]2~+3tj`I ^U'!{(4Ł-r!a  T9\> @s3 nn.M 37|DwB9Xa{ 4K)yRh! ) ,rkލ+}J{"2ɥϘ+~ӀsnHюvE;9 썼ܩ;ɺ4x|P'g P l2{jK)/?27T=g1WbQyx3*t "% jd~hk9}?)O7韅sqe7,`\*5h_  zL$ON*!QFW%[ yݔKh0; VӘ7'0A/H|l r}ȒA SPUF$7v5#:\ yR( ']mؽHUD@ d*I^TQfHNj1\?65YܺUrtаqUY_'n8)!l Ͷ ѶeOHV '$YZ9!=R!L;AJ) !n87:)k2ú6j-;o6P6kLs[;oIX!mE^ "yI9@a.kh/Յq., &BC>00eDg9L\ۯm[\cIPsm?Ƚ%ĵa:gcV'ĀǐS?dZ5ǰ%=D?;Y#ܐVC*(GjS[g6$ƶa;bZa^'JgYق&9Ӵi>L[iŭKI+Jgɓu* ᄁRadoB|e~5Bz.ZP VK|jZ{dzW\ϘQ3Ir3.jRG? rAtaMiVa{WPDƆ6͹߹+;p%yZoFNWE6OoZ{P!Ks-!;-^pB5fK.!%AF)@cO XU[)0VP5m1 q+t: `\K .bh?`bDD;&9i5ok͡ `[2еLy|%8 TS_ ޙoO#*I>7:?=lX٬D1U!)@ Z$8EhYؘEWڽ$Fj "#F (wk9yP}ɩ?9V~(goWsJudO.S -QECV@<%hjIq?<)P#1A^ZY uޅOpPd}慔RIО va m=sW2?džmocE u_yc AVe"b! Z$JQ ;eV J:xu>4Ok=,#oT6轊3!(y^xLN)]jgK4ܒl&9gkG^B#n@&ztͅPna&Qk‘BљqTfr=[|U\{5/w/쵴C꤭hcM$+^fxu _JX_(yʒIj9I +8Eg chweB=#%vDyu ~}OC:ə_8':6EⲂW&RX.4ݩcjQFL!fڊRVS-e}:TX5AEg<=0ӌ4h'9A}v܄2n  `0O%UU%9ex~\mnMR=Xm*f. ºÂoNCҰL?EwI9* Xil}%p:XwԐgAGy/Ňg[g* ld7'3ҏ497Nw8y' y @ gОv*H7%7 FZ{'ĬG"lTy|xԛ [B=~T1d5H 5}z1A2&OJm3lE krd*Z+;YH汏-ʨOF\ױσ 1X2tr/ {;%#HJ>Nx@jJ1/ݚ߬_*h :d8Py !~=z$ܯFjQ`$v[tS{ 0]xmމ{mPOyxAo=bCC"81} ZO$?ڏ @E -P ]:>;DY7+_WO ڞ ȟʟI| j$!SF߮@߽=%?)wdg68qD}fYg,w+DH-s]d nv7T4Oa:5^bꢒ [=򇦢AT@xNd38YfjIi+"@ˈS^磀)VJ\2e\q]ddp$Lp\'J_4Qйb B"P' ޼!kY63쾪l;&},elenCg^A'QXa˟)vp㎤%bs@40wGJ(:?а1f\F" d:K`ZDZ•#|AՀbJ}LE&6xE&!NmqĽ~DW*iE lfk Mv"޶%FG+=FmT}CκkT`uy8v_^gNؿzcEHP}}~<|{ Ŵ* 9YwXlXy7Al4mvR<kgDPJ۠,8N'<pfl| KDa_ 0Qj ԰k6_x~HKa-#9d|;~5tw9fbl݈֘hyH%@Ұ٣4ؿoc#J˙*Ld%Jt=jV럎MܛuRIez ,;NZ3fZOY֯6ӄl=wmo{t.%F].%4g+w>8ƉPQQ;SKjMxuSudznn4Ӿp~}CO螘)5Hg!}OoyݫR/ejX]5=/X3#%Q瞩wtuPsgs)8qƗyw]ڵXH!$ҤNFB=k`.bcln`"*ԬnY|"K(UScȷˮd5&mC5 P̣Uc^i_@.'J:F}b1օ[9*! wF_ݙG6cG B{GC 1NǠ,(ݔHzk"=Sm{s| _Xt\>jJXy؂Ty $ѣyAߕ!7N Ϫ{U=0KH6T90To]'S;I# w%mAv!50Neu/Gxy0?a&{ 2}LR TZ>!5QiXw'hvf^`U!nU[HHӾۥhŒPD:i if U@tχ];qEqKD{"Op7=©y`9,d\/q-CUʨWڍ4f>bb&UT*ɟm vn[Fe.w@ȣV zeICӧ:Zx|k>Y:YZ|b?xx1F7h2y3v `ImKI 4\%3iCÌ!@T&ljyrOh@<>Y_VNvm+u9#gb;t#˛t@u|;p16 p^AXg}5cU!Gi. {M&,q֙!H2 k ZK h\l=D,w/gۀ.®x>i,(? WLSB4bb۳2u YeOP4O?k(*̞jВsG4X  !%5K50&iSSVI 41mZ%ϘBƧon I.LfȌn.yOx%iv+>\R!9Ǿ{MBsD͠Q)Ahp'#|c)[Ǘ@c!sE%*r^Kl|u{˺0Coym ܵtA)~$ݴ Hxu(0<"JًJD`ϞNwP+7I=|kPTfwyxRP܌7 >8N~;;fHA5-^oI:2rZTرO C%Aos+*1QB+h"m{ļIX-THi `8:ǿ\˟n9hODdS(9ȕGuIf$%G>W*$7wjn~r ԩոVw} #&=?[l, &HԌ|ި<ێF}[DZw/E8&&WJ;# M8dʌ4Z6]Kr:5R\z0n j%(H0>}oTnbycF'ZE3oC{M f:$*D~ c+J'lƍr5RMli#a86f*I Ή/=R>OI@C_,i/ `Y~Jӗ+ s źݯ4Iisz4Бb qs + 'Nٖ~Q֗_pMb{8t)p(tJ9ds}azLQ w<Ԭ/_Zfr"j. "޼[{R=9 <Ə]K9;0YM38!}\k*Q]- j=]FT)/{',n16+|D^dB,-Kc栴3gmnpqF=恒s{Gd_r]]4<- f7b#Q6I0cH(mDgݤ@( aHͫ$G3=00b}HU9H0>!n\_dLn_y9.pԡKh9SvѮ4:}X.jh4$CqPՉ++M@Td*jگq"NX F8;3C 1P=GoxhO:e/M5:k3L '[MRB807D>-CBTI@b5s6=/|h  W1m*m[n c>G¦}e qkܠ7KjS>|$C&\YqX|6N}Ev }R܃'t228e‘ش`@#DkDI3=I=`I{yu/i|#}fmХ FI k`<\lZ@C/0ydt"ȸ0\U<ٕ%t7:o*@MSH3i~.Q/D/_ f/5"wo.foG5LR\7u=[wU|JAT5Yŝq@(ؓbe;p#ׁEܡ8[Ġxڬ)J V;(8/" !s٘71B )>GeS_LW]4:R'Q6 JhO|>fA cن*q׎P0T^c^%DfvI-P ei!e$srxE݇"=155B?r o@!tJZѽer`di"m׼pT 9]r {д6,@2+Pdw,뇻ḗat×@fލokױiB7, `Ta !)5$+ #P+_F1MNBzMcA7y˱KZi}h hhQT2*^~w5WÖ_ȸ~sŤ57FQ_hVG ?~v("XA} G5km+Yt+Z/:]$B;ruשׂ# B0K@XNY&oW؆m|Ε(r@` Yl/d쁤yoӬ\Owzr<(8$mCMuKa%JǼuA{_XI*x̏Ӻ} AW{P-9[{^X`EV3^ezZtSG 8a`gA$+ݏaj5ufY=m秮)r3SEҾPN&8Y쟏r }d劷3z\u.2 ) e9mǠs V|㝉>ub/ez$ 7]_ 'YiտfrySlX<`FМ_[oO. J)EG]W'4*|n9=u9-;zߵډr*Cu֙bq/_ІEG+,Kz66JCLvn5^:3GJs`U%niby9Ea\\`T w&O'KQQ}' jf4M-01$Sdi'#{5^փ%2|4n-m6#P>Bp`v Zr(tyGYԢ6F0Z?O0$P]ԡkR%kd OGtˌV-t$'C$t֙p뚙CdKYȃM0SL@s8C Uiɼ@/Q{]^J43f'{`qNMv8,aSvjb/K_S$T=>0ŵ団.jS7za+pԁEJN"?­a]-} i7MBE *rpe,GL fѫ XBBcբS]L+{~&G,Vf{ rŞuvD]g7mZSpw v@z{Xq4HªgQ*"Sr-4KS n J&=7`vZE>:,@o"2I̹Hv),\lRCG\z)tUUȁPJ<#h&IL ^#xŮ^?mt}a/y Ih481 2,RƖ:#hcVؾ&WOfZII}h{-͕S -4P^Y#pև4MEd rl6q1gIg}:!p H"l1Lm ptӿYa$Ju/F<\|37 U{f fT]et-VoI]1 %:/0o.0'* GVpu[tcoUmޯz7'letGxIhyK9KO$<.EZ KUvVr#O.",8I;v\>X_Q#ZT0Sw9:Derϡ"Tkiᴕb&ΨNrP?̔b{C,ma+OScu~}:T @)ٍ{oѵBM}֭=PF2t?/9Y}֩OV;J6hgS) GˑcI;h5O\\od}8lQ"ߣ~);ToN"kQP;qjߍWӳ=~oC?N=R l=1[#qP9x1ݏ@i:M2\\>|Q{>G@ni˂Paݢ£Izv #%ە Ϲxe Gě&Ϫx=KEgM_,8zZ>w0[ [kcytʷ{oG 4$%$38hjuͮ@84~8ysP̣۲ *tZѸ7Jin97 v7[h{/؃-pxof CT~}%qFY`Kl$<؊X 3]e]pUj(*KnA>Ϝ#:nw4q7IQgI{E蠮5˷f0WI^AꙆhCs)bU!=hRoWpa6k5 r(ΉX,-+3mYZ7MAIhŷԐҠHdF6/ݔ<ʵ7giD*ZDrP Ʋeف.OB!Uþ8?+5uKS ٧k?\xbЭDy.>/_!ּ2OWXnTfJya8~e6Nc(Qc:sĖA@RoYd7C:^ %9z5TUгb`oҡ!jh"zqRa 3M8aKư"5֗^!FP!ndڬ 5­ @8YZUf,eaY^E 4_&C][ -?B蛧Ռ^}Pj8r7Xo'ͼ<%Q46c=6VPηo¦fIYn<ת2,~'7ޏ#=ꂚu:R2V1 ӆcD^3HKoC[ u"FmL ̫J$m1*аrʣD0N eq7+^C\&^|iCML rՐđ_Q+b QYC  D_[˷AZgkB 7@?- eQc&eڨ)R'%~dmJOQNR'zجU'-^M:zy!RZKDoNAtLZd~)c 4Vw1n*fZ5BSY8l/w\$)Ʈ8G_tθ2?#ZY*MerГ, "Gʻq/0KH7ofF"㴾+lZ2Jvˀ9|X _ Gb `= |5R.`1IuL$lLJ9)iaԪYwqK/f@Zb\<<-)oW3儞mgvcI2A o/  <Mޥ~o+ILVo&j:|WNNe*ayb j@q{B}ss\-ЖfX~4 ZoX!Ǎ ~X?-@PZ)uM4\ #0 fnDN>QZ*=&g@)&NZtBfp}1 ߧlƒ- DSiQfpm{YFzc/"D'oR*F5:0wCyhbh}F lWTڻ;Eu#>Qj8"dGVj!$Y뀠2w'(~ %e5}fxfɺXܩR=#ptQT%Aݾ$tUyd9|(y!icgҎ~ k>0̵h{ S S'L^7z )k4YRQ26lF"w/`d8[9W>ӓ_>6zITT@m\j iNZ1Hn٨H0Xi¬Xx@ĘBLny1Rt96Q n ~ zK >0ĐtƞP/ 8AfKV9 ^K.F-"dz:Ҋ:GYa2rA{&_mv#ۏVUd(xė˧J]i~`(DcȳT4M ߧ!DI#BCzV{(ڶ3)ј^<tS<AY}L1_5! #D=-׬Κ4jo2P{EMwUm 35n2IOS6: 7Ѵw+03Ϯoyb3+M bZo&,F$0pef-U$׫q*$?E_Ui_Sh:Kc:u뷲8A GL Jpm4k"q) '=Ð44 }ԺgqL3՜ V"WhI?;R Aac&[r jVBd˷$Z폰G`CW4*^[8s94D>z[)Q7vT'%JxԪ~@Ztn\i? n|ڝ*Jcxnh 9R2{Y!BK<ԬZJr9ןm@>8XKɜR?(=uQzo%4xih6_QV_Ѓ K}aK0bٲ?vk t6o!gl35_fX#j8n~ <|ޠV"@7,YR쒳\3gU}firSFTI:5at@_Gc9ߪ 1\(.c8{+|:w6 C:TJZ᳞t%ѨMZcCh!?w\壸2bhe e^fڥxl]LGIJw#%./ X/i#^19%u ){|n/l4sxS6ݩA~sQ2ߌ~3U}␅S{kƍu$5)IvzYV[UR@9 "*zP[E.Zn(;x ΰRy"r8<"ԲKSUleJٹ_sGƄY=<BD&$|Q@#UhM>wѫģp2=*2z@ ӭ cw4s*z\A!hhvq><)YQ}^>]AmtmKQڃAi{>Aj"=^_.jXq ^72Tslx\ yHޙLF!rn(8w\r{łLA W01"E6  gfAϬQ_u3dZ1!Vx11'80I笯C6_xFs*Ұ^X3>(獳.J8 vR "eۏGąx3zHIp^NZBiҕŞ peOڒ`Dď*tٛǿ9wyZ*8y(gzC8C:sesj79 12[8<i*Wa[(G/7dեtJ^6'fkX{i틔hlw]T7W4oNGݓ@cxWӈj+U ~Ȋcg69bmήmPnR>[ռѨv_B~ ;+ruL._&Dg D+ɻ@][XKY.~絵Au&[ [''kP0sg 8 115ql:N( Gs^SwkȠTo|PJ)seiȯx{ֈm̃hsƍY/ {I)$ԙG<}6[PV5i¿^g3b7v/yC2 9+4 ŤD>6|4Ag18 ;&钣K5-= '_DnO9ٲǾ^#KѤIAD:s5Vej(ܞo73An묕xD.8mn|@A>ACC0pϗo+PMA1p]>zGjҾ] SLTJLv#ּ#9:5XU RCsNS;VĩtT4ϗxc [ڧq$g068okN$ aG< m8?Fx =d,;P6\QRҽͺ{#|U @;+@CMRSN"b 1 CCCt]%h9B!UfzLa`(DL0X1L~"/L*?@aB.="؆t/tXi鮈گd{{$3ڿy={ye<;ϝ]Kɔ[$BUW [v}+*r9w]Lp` lTMAUPʤ~v<:6X$7!'d J7R.hGj5EnM8PR8˞6dlY:?n݀-)=~Zm \t?D&j ܤSs⁕5AiL>!Z}=Q|n+.a鐝)4f#R6P#lO݈5B,-aMb_D0>l+)Bjvx9] L>) fȄy$ 1}zM;Pd/rqC|7ϥۗhQB=6 vkG#0DLcx/C0 m7_' ΟZl0  8g7lx΍D;[o'T *b<9x9VP]ӤCԅ gH6@_UY@a52UÁ'4k~GnW}ؕo\nPơ۹pkk|( -ias+=|L# ~ 7'Ź<#DQ/8djИnDQ]Ss 3:VbQH{ݎ,긮Ah0Xki5ERcAP^Vx[Mû&kp4x>` 0ZzO ~wX."yϏ <60^€򠼗j #n<./I8 _TJVYSQ+,ppUKq~| YEXkO 8LT="vig[|6ט?b+-#CW5|ƌ|D *'̬nz=L⮢e'׳r< Q T>^Vrݎ᪷>IZk?+xz,݈\ݛtSđf3[Yh;iTxqՎcʇ;_MܽÛgd^76D 5\w,>^j֔q/ʂUzX5%=~`)LzRsh&[ܜ#Ꚕ6^C !>hdP1ۢ&wPW⣷.ќ(Htﰀ_L.C"b#q"[mkEbgGEZD*T'"~?8;y[?f! FlrQ#/N>3sӓ6_w_ (tZKE+?=nvM*Iߚ!'Wn(*Z#cKG)|Y 5DLxo"J8?+'_OyzcEĖ`,Aeï']ҩR~`:(NrxBYS|j B#pPtgy,Ѳhkv( ƣdXp@J| \"I}=:54L'X5~sv.J_i $Vyp Yۘqb)&c xBY r5OxezGY9a'+\`+C GOCv+OHPEcw9ӿWwA<[n00 }3`3+,]i!: V7TŸ/T"NrGxşͩ7 rH, "ISK`H4EV$lZ$ܜO5AKb嫍M܀q]ZR[T ״^Ƭ ˤNe?"oSDPz=A}Kn= :jbm31]T7 `js`ϙMElқޙZd)vd#2ۘ^`s.5!FaCkb~ j/8vvYΖŀZW$”ken?ݶ7!w v'6ig{Q>8 ~ؐ7ަ蓛fv?fY.K:x/Ν5 6t6_1mݻ *u.\J!T Wmy( 펟WM}GkO'_ږ{s ǃf(`TB|gN,ocl?HAY|(V*EEN)L?cE&UorPa,ĩMC#MRnGg"h,n7JXGTIύ*jIOHtT-|Vhdme'<+2t5VvRQ4oӵ٥/P"}:%Ls ʎcfQ b&i i#Z_S&~@-OK;=wLW55 #-6˱E Ef( z1$f@0=a!_c{ffNXtS+ gkilV.I[̼Iz,T)8;L.. ɉ_ڢhA1 |d53PW#? lQbn@2 k0<T8B|-iqNyXz3eo FKCcK $.I%ϼz iSb^{ũvC( J_j;00C1x9lZ7E0|گ~K=M 9]\N+ T#:)bt'rA ŸbIqڶ$϶91Gv{;5B[43 TQȟ@y8nu96p%G\߹tifgz*:HZ&,R6B^NΣ#_Nj S(iyaܩxa1[S_?Bh7l1 M l){Ɍ䞘ӜgaY8XvH?0}F+(kEQт7٫ 0apUgu(NN*<ڰ !(sg]Gh|YQKk(;iCp\gʧN TcB $/zH'DfWqX [wCXHڣ.*S27e,YZ6B$WzgūZ_Na7_u ,>NAqyQt ¯egE3OYZ:J].%bz!VHyyd6&$0` C/DŽhאe)竑w{dou'mk5dsv-2%賔2N^.h#V[* y# ۃDEb1 N,|*Xm_pxDhCm]!("a߁5җBn cK.%'|^"V[y'V$I_&gQ<+ cc- fNzQ69GMOΝ=m,.{"onn\l:`eO>qdT4Me&Bt^ɎŤ@~4U8R<]A>Ze}6%(q:0Q[=dmhC0mO%:|j\bR=hOqqkF$JRs,oKgwhFT*7pPI(2aUڵaĔC˾H}t6{(Eli_Ŏ-v|6m1JrP(҈0a`h)HvΙf'ULV%wQrUɕE`VwʹΝ%ǻ+rz}>zٷNcwM0w=Ӆ̔mѶVȪ.[+ xE7ʶį"3 "C]H$]`~޾ϨN=G%t]toir*SEW )̻,P rS;bFK-B)+Odl+CQWgOexu ɠO}mlP &^Zrj_<[eSط"$ 4uܖj@F&e GE*W?:l|va>xCUkZA%}afҒ3W/gѡ%$?E5)  E΋otDAur埗4LۈW=EZ.ZTʳ AV6Á.hLZ w}.TwPZZ|wvŞ; F6&h! dH@c,12,v᎟FGTbyaDvh0dwf;"Ӗi۶M;YJs8xmAg?5] 10꾠Wl98:מ^:dkQH2Mq7)z/0+J[c_*>nO$s.y*ìOo۫ϫq|s $3 4-(Q 0,ЏAk9(⅄zs1(8+ע%u,DD{N&ȕX.1}K,;F?ze9 `vC:8Hx+*%?p`GFk~Kl0ͷ˘07m !o3Kir0wץl '0*e fyxBq S<VajDU˪ {7{#M62ܡx)IM>}iwZX)D8?y9O~]޵Cy) Y6?҇b:;vt@~G'!wEX,-~'TpLv?mc'w][`^`k@_z 36]|;(6G{,.rZ\`ډ21pKIDjktbW X \*J꿏SIw<kV EmԾ djxPsaURwviSLc;yWɃ;b0~*hHJvp_"OR&yg 屳BoP$_qa'p0Q;Η5؞"z })uO`7_*SFW6c4J^2ֿ$ffwR8ϚUނAsשA`* GfAV #%dZ PPH]]t$1qul_-)~Aǵ ~R#͘# dMg1! 뒉bl*^l )ʗŕ'Ӂw`lV#NŸ:M<*!w"AaHч8Oh.E׽@[t,l SVq=ϋu]q&[sV.L`˸Lh-%h 1ٷq|nV03/VHOXDin)8Rl9,,4'u3Ƙ'gKύݠhf <厾6m^VAw Վce^'}*X2tD#'79r:1҅ԺďPJ&/%sȡʡ=GV"ޤ/MeCm:Dm#w2t\t*}s' H &6kO@PYmf B3*r YYtd4RV?./Wn13sb),c>Uͷ$@!>9E^sMk:5KC; Zd>a^zIkݵnSב3 xt ETȐHdø4Q3w<1YeSIA<\-y[ MCh' SBM7#uX *)x$BJ1NeU 5W\HPń+T}ѶV5Ȇ~:D;]O):C,5rMBMcЀSQՠ4[4l gO= SzLQ Xz_is}1*Jأ@H&blD7AS7ƣIOP_FVWx=Xc=B>D2AF,%#{mtSaF {\tFNw~ H;i.ߠXXS$t3|]ߩ_kޘ؀FLڦF^K%Ps!c(@NF9u.Cu~!|*!a߈ zDr0v :,h%<@\ˤ krV. @x٥0ا0d qzz?QVKGL{hPWim[AcS0! ^ 98#Qx-S=+Pl>wkI?A᫹u WgBкyCtP䝹(Wր,Yem!x)GIefڙ2;v#o#\SAWP ޓr<;*F=#b>8/*H0ls6̬m:ͽh}pr <&.`gxTKX~Cώ'Cd].)Ą>B[[Rr)^oM vϏC\0FKA=%XR"F51RtBme!ͪsz#W{{R5UEK?j0,>jI<=ޤyhstRyz/>V9Vxa=Vi?F)r_`śjka:4ڕ- ?4L\G=PJ+۴^@U/*nSxQ9b*+/?±|ruEq?e`y!|˯V mb36v=Εŕf`hh= JZ΀AqCc3+Bnǭ":-fkBXRi/= 9Uɠͅ0HI$ض$j?Qg1 :(`;@bQI`7Lƒ|o.<ˬw;?*z;(9~>uI$`s)Rr0Vf1HM9-up+7++(qݔdXe3+8OMDIsTӬڀ6Oy.U,CP$Bu/6y`\Xx#( A0 ?ׇU wIUD5*6T5{J:[搌B?ihzjYN8^poE3Cqdtv;:z{S zZGW;v |*䯠e6a`g-_WQZ~NoR3]RK7a*2RnovetDs;|.T=q\h*،$ h}zl^qcE ()jt$*n!gChhXDDOkթeaĖ-ɰ`gޢ#Қ5JG"3ߋgXVME eG>3k̼ - W؀ bo/ ۙhp <PwXYL?n_7//5vくLH"MxABႸ$%E$qia &\ F3B8nL}uHMBC'%X oB r;a^X&d+j $Nxhg9]v?b~YAb헫4I %sX7]{wHݝT .%`LRGTz C~>P+ ^ɠ@&Ve:iLa@O@ $f!vo0p녡iF{⯭`Y jvyv;֚?c NJu?gZy!۱[6dg# CVtcN :=^iSlܳVʘ7nm\6[AKo 5좰4r5 #L'ξրv:k.D33R 3TBe [^Ƀc\jߖz VrSkkGOj1]=.jN z=%]> U.k_2祽!n&4tp)bk P,b.tK;fX DaݦQ7 ᧵IFpbd^Gv Q3A3a >V4Ht)qׯ5gӐ G@-u`n8z`zY?2$*MB'2Dy 5 wܻvCxwx~26cTm3H?g?x=1J5IpMi>?#&/`Hw"VraDq< P9 ?'*KSUnYc?=jf\ODQJDp̛Ua S%ZJwMt>M?Q],oҕl:t>NMTAjU^Ҕ8Bc|C,D:@Ρo/t0q4Ѽ_!_~#QE ǩMDӛuUC4z9PFڏn3rXV&I-|AOz}zL`MpD?:T-bs+lԝG=V+ktEoۙKXp}=cr خ?ᢸ|aY}.Mq;྆Ycc=t,]B S'.v݈,,:@fL ()ьr["r 'E8Ct+R– CZEx !?C ! Rh ͽ;64O3-뤴^egՄMAh}L+ޟqjqcȴ԰TD1h0D20Ԙ|!Uc&CFMI+5`pl,߽MqHo#eTv}joa6`ClV>YcpX<"P)F0 iٰ!PRz `GH)8f&;ݨ" 81Z&oz2B&@vL`_N-h*.n|@"m{ NF٬ukU'"&/Df.d_O-}6i:W,bo8졂Ž):S$b>=o~7ZwVmLIzkI1ɰ͔yxE{7\O}8ܸsɷ Rs> _1Wp~dL`K=Iଢ-ݕ5>b$ >bPQٲ'~Sbp}( i6fm76yILŤw&88Ulw.V&Ƭ+_B6b+y!k 4e[NT&eazbS0 [!Aiw0u ( ωQy|P>sT y>J@Ƹ}hi}A-Jwf 9vXõ{rxW[CgPdG:X,(ttm9I& -3g+|r& nZ:o;8d{ _Bv_z==L&j֋EYODTZz7l8;q1sk\1R[y2@%PNGg0}f;at;ZT^C oyܹiqh{E{QQ$ -H=8T AwNEVk^A8c,–9t2z+!!R ֓ ݿ `~Atws2aM! ^+a@`#2!v<8SYjc&zyZRmޒڜS#wŸuٳ7dމsk6S}h!XM$^^PW R Iҙ9A1c 0m,ULn֙#kCФX_C8jY;̟.g '$;L<? t@CۗVPI&,-m=Fe=q= "{S"4=w#-# @.&kF<ӱE: Au(#ߤOܻ8RPX,87ROraA?u_S J`:m>`mg͘9+=ckWM ]1ćQ:M#tr'R èu:/|6K` @t55#A(>i4;Ѭ+A^7?R6.8OS(NIbY].\rį[?C^ N!Y%S5Ѡ/=ia EcbJ[1$xJD NxB# ;ÉA)KGzw3УzE,\l\VϻU bbǪOevx2uhe܇tPD+OeGL8j}@VZ O gJPQ?Yn퉊˸eBu1;|Ă; `qTQua\P?j$Lc|gBݵyL] -i78~ڥAR-i`Ԭ~"c[ m qċ#R7PҦ5a {t mTW-բtr:yX :WReqZ.D{x6K;6Q)nJ䋠Lhv"Lh B,Owů\޳! c7,/kѝ`rV/w}2J#SInMGT dYa`gYtQ-NxMt|hV_.vg 6v/)Y2@wWeRЍ Zކb|MBHG샤|c̡5lu7MNߟSH [p$Kp:LN*r]FI q)2}ōb\+|m{[=Ɯ5\q) Q쬥GG&M.QzƗP lU*#etY,g%U\D_0yΐ@OD̵ b13g>v!6nܝm=2Hb*{ыP릍".<ЋJNTXDQ W}w7q6GuK 64Z-1Qw%ڲ޲~cz39R,g%{Oߛwv ,䁥?;FȎܒPVꔃ!IP8Ldoīmp\yylsa|B]Kɼ7WS^9[4B_9zo*vGl`# śu~sc1"*N JkUs}dYҍA |JEf+_P[ *M85>=cBy6vB'XE:Gw%^E񤑴`/C+̹&__D=2!Q|L768Vb܂ Li0۱ Do871(| 5(4sF볝dv_a?n:F+^nMҢii+vlW=u?4≅: Em m/hXe󇚵д2|,I_'Hg1CCI YtU&=V 2QJSR7e9 ,jb~hf3챍 i6o?b$ɮkh֑Gb侐5kdc4f'vGQkHY%ڐW |:B2fS cfEG b9,7t5;o"t$)|*g)O!>ziq kt x~Sc Fk{#c! X/3Mc*2x4/ ce24MN{Q hm#+4smj,}K/Al3!qm7\Ok >ffLV8VkˏPWO)LF{L$ !yEHS{niC =Sy;j` y~2 ny}"h]Y].M]*)~_~gmRLѭ7+\CB!Aa[ (<736т {VA7.Q {207bA!Zͧ} +sF\r_W齥7@FY#\XV-:s_QmՀcOl 5ύhѴ9X0&je` S a/=&DӤj;ۉ˦wqf<0g!)U!ɈG6 Uvz˿JPm?''?x#oc*m0)a9k|N|}YTďjl s#[*([T ol*IWFGaSl'ZuĄPMZ׸:J3dYq :dѷa }wוvF^CDR@g=̧́ 1J+{I:b{N3/4`ʴ1XRЧ`?t&^,Ywh2*xczĝid:݈3mJ)q=Ѽp`ᄩEnn%MμK ]!0ɀF$ 5󣾬rC~p>K`U(AMr6SQJrO¯dI)H:K@P]W|jCc {v}YR ڥvgSL0bR<~O& 4 FnO;!MY2{Y=y7z;{2$U@ۋu(j4iD,NGUIu `at65g kDFeZ1GӳE& r=ߧ\}=v;_4,GךcmU#lKv\:M"KJKQou֊[~bBɋ 3=pd EP*'!2t{] }˔#GUep wǎ1lnuTX2V9$ Z` Qj6 \`::*]$vHc/:Ľq < \s6RRq;W\>Y;0b1BKe(j Kq.%?u(`Uc."&O_eS?!;]Ƃ'>굄qTzLueԺLn(?'b*N?ָ/y! u` a}|%G|? 6Z3;nH( [oI,'>(Y@UR?ʳ*'X0^1،7C_UC-6'!VhYAC5y~#bX(,fS瓻4M~giNkiAAd<$E/PB:/ĉ)7jr_08f]y*C^Z#JFG'Z1_ B 1ϣx w3"O65p\R$2͗zsH+,;y8}BʪIvտ B?5q;MeBO]oM2\;PGp)vsW%S /T(H^*Jiwk. V)H Y6߽@RK礝=s\Oe0~NX ;ImA{ ZN黸i)a6j$:ڱϠ-)IEK 1OdC'<wSuo~u0nrp^~HxjIb>ެʈMPr; %-\ޔx~D.)~ DtΧ3U5 Jv{9𙉮R^wo}9"]{Rʳtb&K9^NwVX%e3yɞ*|/S/.?ܽ &ۖFȰg`׎F!6#o-uE3!ܫR^vC;-|C8_P78:j wZș09Ք蠘p~%u )PX釺e }]&QE6WV9S#$$+R d{Wj,S !|Jvz_݂c1_%"[3c 5׶Cs5Ro[pՖaȪBri>i9ѻq5O7'AӶ "JX(2S!#b`H!lHaHF iCܢʗu/xt5: 0ڭ4رt^0{6ab)M"*7\QZ?'֪o]{?$`1%ewشؚ#ﰩ?95.EI}(Sr߿ƒ}C>9PI8f ăBjȗ'Zs_z'!ӂgEF/W0LA83)ni 3u9W/0:m&$諆Ԭ;TXL[}sC'S,'Q'Z޽]Y< :9a.XwM0RZ>$Ӕ8M>s:$<k0ud2lC-mO"\C R N[r kA,VB%viFQo)XqHXûźCbd{H75.O7[~`M1Pz^BY3 UydUoЅ"~3r ʓ^@#*УL TQe7)@J*FP^]W t+l󊳅ܓc}#F[@ !lXN<|jjc,˽)Z,DDF=ti^#Ǿ_3@YP]k]x| |@^K#c{);F^>؞rǽ\C%զhB3kp{ΏkV8,cpy !Zؚ@ v,'GӸ_ZDz_F?6-UǴ`ti8?i!΂G[&sohMѥ.YW#c>z`$n!bUN&w^xmӫW"H56hL ER;y:Ҳ\KݓdCE7u0@aNOǖu2{qP8@1 S* fF>d`UcWJ\JX|{˚QlXYa9eG'Si;l<8k!&I􍆷i=,E3dD<ލ 2{C4+hKQ\'= u.%"uxBنUn|; &TDГ9ȩEH9l>ϝ-$^q\, ФLmt84`9$.lFs_e6qr:Vm' Cڗyy>pR DS8Q6i@q[0ųlp=M!N/ ڵM=Ͽa  Jzr(i7l8b䆌rw0/y6lZ*({Zdof'>k *2i=)c:(?ͩg4m @U?"kl, ."6}ڷ|&˨rWڶҧq] R3?qοdgNʐAZ 'CZ旰dq*'LXE0F'm 0Mƒm,3#nims-+ Y3 TI{ Nֹ\%Hu[O27RXG7Pv 1ljEŌXp3}cC)=2nL\N4͘?@알r3ٿd;%4<: >+PՂS@fHErHoqH U~эs:L57=le?K5`뮖/h1KTAK5{r,tF;_p3X'r"؈;4*S ~U")nQC@`O#c r mnfAQ'XfLa6A) *T\1o芓<\ei{u^gs.w4ay9Qn!R{, qvLARwG4 K`_؜) >` %N jw3vHոH61'E<'T/* w4*&(ܑk|c (%B>1P=3mK?܇l%(u[on\Evp:F5Qi6ޭC$SLb9MtdVYfh}edBәV+~_"K -H} %gS_\8g&WB5p#K 6cs(th{I,4m$O7& Kc1ȊKut,r{a"cT"Cf]$?4Z.9"}=ǦHP' O1Aa}:1XD)fU9n5K?[/8Z X=,W$hX(rH\z"̐l.@EF9d}N\86 GS6hjTIm~Z$[, ek^%#m"@m(:+9pX9jqCqjAD/~гhb!ܴgy0/Ű;jʸ1PL~IEq'Jr{7i!Ah; ,Q(Aի-RK?^^OĬ>Kϴ35{IA"F*$Ы}: M*?Ktr=}-L^;oq=>t',(W C(C_Dab ws]JWI0eKŭ޳q)d)RƕMݩgq,g3IHϞ WۻĞ%(n̡RrvoPF<Y|8~L? 0s--.$J=*{ﴉr5h**X3 sk2ypxaQƐ{K4W^rNaC{YhOɮ (qxcMCMqϗ%mH7\p "N1 d uP4T0pj ߂C7"TG4@"X9HI{RޕQc8iS4,]4ia'a-"*aKZPf].N蹤9YKfdX{ވj]g;-$ "=2%i"lǦp$M84q&zjY! M'$ ϖPƪ LI]&Ų~O4pڵODd/6hwX $e7%BবZoBջK-M#.>kN6gW*E3?JsGS&6:"}g%3?nBrect@!!pW}6N 8 \M ԽY߀76BiSV-pٻP1&첃SSfHW͠12VNTL^ޅ!s\%CK.sYXr/Qָ^Hp{: bnO?C.+-c@jԟu0`lhe2R܁ffvSEbI[GyV Рs-;Xid$:{Yv7dP1{g 9#LC,bKێT@[!2*;;ï琉x!e\2xe x91Lr5AH;N K>3}h!!edU]g}q͚q'۬5MP F/'4u2Q̬6 Itzo>6w!6Q&x[j 'Df1v7sIE^,y-5,  ek(z1 )j!W2L4&7C,K1Sɴ5ۭra7DZ9pa{DsvjGq%[44p%T]GpnoYƒ8}4tIcKia&(#- <'s*$'80'g)M+]i>( cǻTv=LI%O'kC?1v >=񁋠,- ̵ܗ3 ET~L*~ +'XHhzSuHO2C+FwmjRT!qxU 7Fm0Xz؃=/%!#2[ӟNB=jvʺ#3*WxLZEL.CZ(heA5[៴'+N JP+v5aDBMDŸ́<  UO"ؿۗm#SUJ=P.!po [f>>2uT̵vɟ^%b7Hʐy,((ڞszyP:x+3!KjԽCJo`d{"m5)lVHPˆ"@.Nx Ft̥fށExAvϤA٪R{ L{KwK^U PULBo\C$ z0"zg@vv bw@ͼ.B{VBs?@׾Ͻlt^=>H+@JcC%ˆcXwtGN$&@hRNT3,.XiA'Y*O˶ŒyvuSh#k:N@E(]zJ>D:I uw<) pR 8EFg1 Ň1!|4un6߮oN:O8K% sЫ q w(K:'`&/xS磼'ODjfON8Z[_a;وꔯ!;D*A-OnQ[*|qWT VhED6Y!hp_ o "-Yb&g,#C̉$Ӧjt9>uKMǦvWXecԣL&\nX[ &cP}Ck}C8a*MMI:ڴi }(m/ 2=|XT7 p"##$GcBUBUT`Okr:cR9VI9nVnbLݹDO&q5B0ba鼩1\_ =dӻΩ^rb-V$}-OD -؂m| G2oB4ӑFsm>-~TpcќQ#"/Wa0N؍^o<=cb}R3z_g>*(l}18ʘS/$J-cYE0 nĊK)jJ8*zu0(U@TF{)1MBLRE+ 1XD!lž"b % b+Z:[𴿵WMUemp& IDC?m&]'n 4iR^uEp ΆR Ӧ݆r$MA ?QMm9I3]2%hՒ*$O@>g9'Z0IUHhlzd'hB5/c$Ѣr'O;|Lc,5(7t+rR[ZXelρ)4XePl_֮Xn~_0.>,S7䥚uk/;\Q3v&Kw-Mx Pک`ۙV=J/wFDпP׬t(6{>`ŨY?gIcM1)  dAM! ` ?u %U: {&N=GՠC o۬?>q(mG LqoktJ 5TeF$}=4>E$$A`^dqy(L Ș:DkQN]Xw`5fWA (9, |;x`(jQ1[ԖW7r7w2B2}׹s@(Ѹ4d}w@<DkjgIub4k1$0;Hϱ&NPl"OӁ`>+˼5g@L`E9V=IZ @݇2%#a #-d;MyP4$ugdy9K/8^E-jOez:h;y:mw^I}V`i U Y yUFO/se)@7|DP)TnB"{ @QPp? yMskJKCXO`Og4sA^ԓX(Fy!Zz`\&%[:2rqhLޓҿ ӱKu6>d0.2xGтlʈDW )p}0xqJ'Z(#|n@wLCh Ov0~evNd\.0gDw"Q ~6$)u{# 1 riES }3*D+K=M06T̠)w[ac'D9N`|dj4>%lF:W7;X$?MT.z )לE*jwB:~ $6ji(dxzOGx3 8pC,h'ȕ$tJ-<|KQ'*ur=mm|C;L.pÈȝOo-ڥl]_2HC,{:+ζZxq1 ?Stqgց/Ar/H#ՉfQN71|8F1;LfIb`<мI"lʠb*3S-F¢x1_ t[&H}5Fp@Z)X~2#%-_9. <1X(NDYQGmDLj|b 9[i+Z&G d:\oKV\xe\%W8|\e2'~HJ;ymTot ft8WS7ӆG% Ց!W)Csj!6zFqC XJY'M)8j!qs3p8-Jp}κ+$.3Q·ϗwR'ΦI; $]ƳNFޭjkFI jĘ "?Bb&m| X<}8|P2C{QDݬk$Axf??qcr:aq#9#Ⴠ sy9 E; o87$7ۂ+b]ZFyzG*C}ƔւsH. Y-1̹%DX=\[ y:mKt &93:sg%Rӛgoڎm>ߑNKȃ%c?H$nh'һ/2\nME#0$u;(6n0c{bX2[5|>5F@ qޥxIxu_Lk٠㤂=#_uj*]]9:ЗǓlWb^vg'c<ׁh炛t~L9żā`~Fk:?BchZ?bNjfjN{[Q69Ad l]3}iBgjhBcX,J|;| 4n%8ņ!y-K&}RAKjkvFP2sz|TQxKH%oEP0!":D&i4 Q]fU4 v:ƥL\qo{.,O/& 1ܑ׽RmfpQmXBPcCzk# ^s\?|mtV~Aj7cmr}<}͓}Ⱥx|sSzvzȃm]~iۢ\>ils ܨs ;C@* $Uwq8Q=)hVߎd0f'$K{8[W>ϸJRzus 秭l&]t3Wk !%b>ufBg9p%4J|LWnS Em)em\S۽d\‘P0BŹ/n&i/2j0z7*A}8k>J*K׎ rP*$yK0~-0ϬT@|4i;LJ7`ג!rnc[|*[[&I3J)3*&*ZMa0uq34QBxAGT\oWÊF~z"V@}ϳ:,P^!O\2Bks"D.{m.1 ;#eTW=b'g,E+Gm 4ZM. s'^݁gib848H͝d`fd؍N#@XBY@_{ۙ,ޮN;\榃.7D_ŀDÚv7Pqw,ĺnԬdH. q0!$Z.̅8QxL+ae 5ʕ0I9o5*^1&e]F@!$D1W8h 1]0ǯAĎ Bfn IHaߡ}$[\sf`hMKzZ ,1ޫ/hZ30npHݤ"+Է[}X X"QdG/?"6yiñU>N둖orHmTgH_M/Y&ܙ:|{T: aX4 ӼpHvH%K 1ҥH9[Ar-`rk"ќ[CyiG<ڞ bN0^RBd|tcU{[;Yj.ҭ%!Tڠ VS.iI%a[.mHJ VaxA:Բ:@ŗ| `I[r@<20f,l[S0tZPThFFW,^d|@Ů.*H%ΤRhf9Bu) ӲIXv04UV1NJ ['/}ӕl&cGJ1ۆ"mƢ(YΔts8cKDo!Ԛ$2s`Ǣi6( od)O#g?7GKy4;(!u'=dlž~ԅpC\7ԓ˕r289SE}L)stN fO҆~s=g=u**ܛA%ԭPH>TdhOw|$9jYwTh ~$ݒ\̽<4؈Ўg9gfOˢxSрѻ|a "('*폑GRe-ID!AuwRG{9GNw6ӈ_ ptrF5Gi;P>r y QlLc'9<]ܾȬ$}.w!#ԛz :r,_~3T1Qma!8Ί\]ƮX`8RLV)zr7R>7}P#šIЯ]E.8C"kaQ͗&)XS2O;|g%3e>኷DWxwT;&@`=T N8~ P>pS^f]Ɍ"Er6Z7Q-:PKGwN5|6"\}AʙVC-5 }ApI;1^{Hl~&?M8:~El3Նܢ6QgdU9f)4!6) h+ y,׉vSv܈#IlaqBCIkm>+ jw@5Iv]X&D%ˠXOϴHyI*-zl\a  eМ~6r0k-=$\c@cS jy|lȽq$F*#LXu TzBd7B@}]QL Z IDan-1 2+Xc#j(~f=tvlMLi{GZ>֯,[ .uGjn\EP&0eդIA~p%Q.Y%hDz|s}qy_h]sa+UNmS0+v𢋟ptŤ`>i'ANghQ+2[JTϟٿN4`1W{Ϻl̇G2%ܮgA%wv>1h`S;% Jz4eR{[;1au@q<…" :VZ1.*~˰ (ї RLj=T cW¯ȫMaPX#J ~ ;*&( U6-<,E n2%xU8UBv  >"/TȜ%bC(@}jS[KJZAaXzw3l΀\=,>)vtUUtoղy<.Ve_SӬ8-ׂ 'FN-YY/qk-6%#h(x#p0${:߸wJŝhc^.ʎ$Mf-&"-`HڝiaT7Q!Af! U i c`]$%ح>%zZ>I0lws2ۇpQXӿ%1r]4h@pW*uϼdJFU܏Az ɠ ϳv:PyL3LRwfٖ oцvi6 0ŒS),W*JdXH D<`La!:bz?? @ kk^RG[zy=n5_xdʔSkâ~Yi6 \t‡OGm?_,@gAia8Na3GIn?ע@L׉Ae~ ('7=9u[#GGϴl'6428P@悺{},&ӻ@mp0?I HPH8(PYE{S:6-@rSmIvYyh|Z:7Ojߏd]EZY tj5SoG'(2(<qs:S@(Ṉ4dwF~W4c̴fըR>W"s x݂BT]b n!v 4>(ANF-C+9`Pj9fP)pTx-+ZekaQ\?F܄J{T28 ͒sqZZ0Z^GL~҉LUD\?)Mo 8&22!Ai 1 @|5a4',,zh{$\cDZ][58`u>S-ψwő ˧V3l*+.ݮ*'GCY=t7$+4VP74oI:TGj~0-I ʣN* `yrN1x>uv?*'w \^_.٠&D&W;֡}m\qqa<0@K'*ГHTP\ ɛF0UczMrJ;-@v1~Z CrTfC{afCeO{xf/)pMVX8I2 QXSC5$^ Yxw{nFu陆o,iCߧ> l3ŋJw[W3n:6"(/b= G7AP.l=8GM !BC\.6GE>"ĮD"|ʺ}^ԢtMr1U+|r8k@B wȨ!δ uhˊ{۶Ij\,TmT=X1W!6:PJg&yFrxn+z6|NMt=ha| 4 ]9\;. 9s]Ŭ^bOVnj{3>$`Ǫ^Yh{@!U>=^pf,N"ꅏ/B1: z[z%vHIS8hFaZYpjf0ߞ6,~v+}&V0NU7)\'8ُJ?Ya| rm pwl.tEsGޅd&CUF%a.*Ǿ-:YS3Yo 4b_jl⍙bP{?ĕA)'dj:,(fayEvK cS מWKcP7,@P"ʽ,EC}GEJ@ ={hjG:re.EIT/# 񸕝K&yу4?E)m! SuJ.K~dS ?ɢg(h);+f뻹G>XlM EX/UԹwG=~dٺNA-.V/E:9̽n  sѶ'ftaQd3vdQ+X͒rfRAES12>~:C<8@=ObשʯQr=$'1~d`y>5H Ҍ5s.I@`!,}#ah'y0Ȳ:CA2Lך2@-n4HjN/)K}\JI"c^JU/b:='H;Ɩ{>O*$2ßxLYRx1#65B"s$PqGt2*D5iD^ZϝeY ޫo_u̪B?ޢΕ-q2fboRź/URлC>J>pqQu>U1Zux]gꭖýcpz֧u3P&SnMJytOù\s)Df-qsBZQ>7ɕ?b6>}v5<) wݾtZy6~4췌; ?->?j=~BZh"Lpiw)]47,/~[Fӱ5t\.՟GobdBǢ}ڹv2ճQ[Ъbf􏷩#^JQkݘk b;{w:=a4IjFŮo) ^ bһBzjGK9~lCӠg@HE8 s4dAn[/#mPD,6h9bԨ:׺l2)ɉ#Xy1d?;B#G\*MsOzEI"6 jwaˊ,e֌EsEA8Ż_ngjnrS0[hU>u`]<1[ | F -Q3骐F1EeKx/b=wz_h,i L`G~<0aڛF+7?̇#_·ck|M 'Mdbh KűbF,ވMh;UH1LK Q$ĒdG/Of[m]}q= Pu>|?.9ͧ|.n) O=cˏCnݶ^|0My[DT]1yU~b˿aes\_M,)Eje2e<C#hn+_:e x$8 hXT|-K~/ Y=y?qqKOuk:n(/@s!W6MPdEd>Bc/棧fsXITf >ˁҙc +do W?@ jXQ($eHD3Sjg>SFz™-]yClN]B3qQ@ץ 6# ^g/$xqXhG=2s&fId,Dj֚[Krr::~1/|?(ha-.n@ou#nY,mnZP My<"Sշ{KWC0NJF;~MA%qAO^~vXHn1Bk; ]}Fw`%og0k-auvw R{^A0Dv7 *>lfӀMjeѤtOJOm}V̀@iW"JmB Kr{~P\08'6걆0*X"A3eB0JmY:Y毝C\kG%g/8+݉0&YyE+vjKF#'1-h#C;݀5ϐ)E&"7LpjNX;1>ZI F-=d}>W%ޥq" I Ѝf~_ 8hkw{`vW^`}Hs5DlO>=.Llhjז.}WpguU,o'º^]'8 RietoRm1$Go9x|>-p )*=ïlyu++Ҝ 6{]iKS^Ĺ' BH2eg)oy q; iOF5 -9yƽ‘ Ĭ۝k ۶T~ʿYhQ>v@ ӯ'?ֽB0w(W}ʬMD_߼Ӷjҡ$s͚XP%'jP"=eyynǫ }8D[ކy+y.Hpa36 y`)㽉gz-kHC}oiq=RVF+pۼbMHm~uk_ƃ&579CXMa6gzP֠}A3 Wudxb'I]{4 RަBu:v;Ńt1U|%siq3 Gi(t"}%G2VJ`@>U*QľqLVh'swSҷ(U\nw'T|Gн,c~{tdQ,X AǺݺmAt51< vμ-u[+ ƂuQ`%v_C ׁ෉O*nQ J繲1p;g Oȹ2vė. fdȚʳKvqJVH)\XB&GZQw2"7yf-wS҉!0 4CK}x@7#F̙W最B7>Cxn mei ܲI^ę 姘r$ ֟^!Q)Ю q0[F^y6+Tk0i>_|a/oOaD>2Oe6?\sc&&juX#6mF=U2Xg`E{`!k3ԯȹLɃ:= RB -^U+@>} ?W63KAb &i -t̟5~0uyEKy">rVn ƗXnQ@Lig123*f"dC~7?XwMI0ͶOjGHT (M ?QuXˇ4-R7_VTaH{¯~`*)04&)geAcjkdH*u2Q4i$h(=)Z5E:<{V,Z^f=&y5Mԋ!㆑ҵth'xta=jy uNyO-JF݌C;au^GȒ׌2 X`d Fg4L5J4L"rڧ&Nr5c|2Kf;a]a0#~,^ k|#mNЉDlQs2L@3nW!Md*)fw#DuvA *`Tqŕ< * `g?;Z~o΃ r+B _ߕ Iqu"˰FK}=V2zҲ {! <۠:>I2!Pչ-JEZmlZz'Z9_#859#S&# |%T^%@cpF|Oss4Z)ɦ Ḱ11y;g)$Q ,7nXfiy;T,io6ÝEͷ{>8#Rv jo"&YI ޢCc"E˗uQ0xzц ELڜ ;Cfu)˝f#}Iwg/E|A  ϐrc2f# _kw/AH#{9¤(SVb@RH'R IBg$w/E. )oca/0ZDV 2\?J NixJ#}Bׅm2YCF݌{b䭮8.uǻi]pJ;FgT^d*sUoR5k>sVSݤ *;Al25wO ( BX(4~ I0'[D|$ρgUQ.mr-<^iC CQ7z=4t׬+g7v'ؚU wc/5TS)5Osi X.)5끂)* V) }}T:gO q.=ԝmu5(63#cEkT 3<&ƁtR%ꇦ+yJuIч+Uz;JGsC Aҿ!S?$_BADKv9"49*_JjT)f1hP~%}4v5V'yx`׉ MiZ2;t`hArB؀CYma EE.9M(|{!Bw5G,x#rO@~ħ y7!s. bN4G<" ΆIΛ+܇м4QgddzX;U%Ww̋bF6hx%a{6 #-Q_N66I?BmqM$Ogr84E3e6VB–7:ձmHGFs730{w$q* b}_"lQeCp΍+|.eE}@HEֿo:Ў͝Z2mZ1D6 tI(Hq?)BHS<| _i 9* V$sE5i{a~kE9䯃9"~,rz h;WpAmTZJ 7r B1po,fy}׬Yq7\Td<[n*E/|`7|ҵzyw?~DO8_OXU[-P-X'OhF3ą|7AAX|xB*~` dM L0wY<2Is#0%khM$1-?Deoj(MHX s*Km+ Hc:=&RDjyϵk]op,+2:u*0ābHe'ʣa!&8G1^r "n~ fi(hZRVcqLtG.;b1u0&B;ڑPeL[`[פXqTd0]]hLCjs&0:, 0BC`Uw 72]굈xI7|$tXcWw+M[hiݥ;r5nzvDGfW/izeLt1`qJrGJYk) Ƃ}|K>1vAbӪY6IԚ?#WUex /|c)&z( mGբ0.];dUB|b=ݸz\~: um7n~X\u\l ao@Ό$r\첸)r60˔{\Zl!me{;!Kb/>7xC7zT]] lnEM,oO45hx4;j?.2n3 "l?Ɔ-J5qqI8ovݿ^(ʑXTmh6Bm6so>N^F%Ϛn2E9lq%EDWIq2*֢nv!آ!E<=x(sM3X+9Qc3UHb"qT+gZt pf qhAVnɓ:ϯ@3Lq ^`ŕ bD;V=1#zeGNݩD}om+ڗ=mbƐ&ov  Y2P&b> ,5<Ķ+An2S7J+ bJކXjo3Vls#%-Pm(To|#..^_q*² %i I\QoQg]U~jދS?/OBjS"e?օK! a!b8(g5?];2/*:25Lpkv=α[ Kn`>A9Ѿ&)T)vBFEj*f١'L\~OkFTm+OJUKP.o‹(mh34#`V\ MI FQ.[3O*.I=$Ua<)>=Uћ̔i3Y^i VG!G_"cHN-2 *E"ͅ GUF떞.{_C[n/anCgXŕ4ҦVߎQjGC'N)KÏhH`P =enٻŻP?z:[PALFcO<RqsKt:.e]Ya}xS"?"*bc|B& N‛y\CWcGDHqzWJZDR `y)=.7Ӕ)i[w,)kc \Zl=d_G3LFJnH[IU;pԀ&a3=4h> Nk:lM#KQ+[DuPm,՜ u OTO'b-kF{uVłwb9 ^$NM|^O3)PHO{Zvv(O5c+G&Vӷ Е^CKC g5I)b@˽[ҕ^֦toG$1Jdjn>2Th7haUJW\jٱ:$Py5ULQ-uhBU&͂ >}3mOXr23zb> d-%O[v7`gG ӧ i"VS1mV"Z;u3]<&_Dg*}黭׆9zֱm<{0ʓwے23a L8e}%2$d'IP\uԒ{9"3=Խ*iϷnAAgyf ~~t&/:@! 3LJ<>oEV>LD~ u&7j WwRj"8ՍǁHy;BZ,rۑP!#?u=K5lQIpifjiUE5GݹDߝ莉#B)T˭nx8]yN9 Uu$O$5>1͇R1iߤ]ذvqÊ7+YG>' ua`H'iB /=x{E 8]4Ҷ:9LyNMjY7Q2g )[Uh(-e}Gbs MefZ~mᯗ@TR J@X(B;XS&XdN8'6s0+iPs4o\kà8:$D9(2Tt,{69`-!f1n`_픎%Y!mR@} iӅ2Dp] g(/Vlo'K3 2QyhA? ^\ƶ@, %8bω+ *4+RosP4=6p g^Ħ} W|:-9-No%Xy|wVȲ[l*-}J~8ΩW48P >B9|)ի؍XMd$xA Wy ~F\?(?$Rdk8Wbpe\G@Ѩ=~ Z[UcYCR!QS韶r!k9t;QWFUO-0W8W }k8cN:T:QQ#)=/Um|'{n䀢Lѫ$Uc%X}?U6E">Ek]h(G`bs/OfU, 0y5<ǐpyCP2x0WXx'}b2%LWQL`#u;,p+QG׌\ {uB[n(ĨF6$S;zVρusl!"QnRlDxpjݶ_eS^E,#3b`4 Ј11i;GY]CcZ>+ML &i9f:Nڹ W39lgk xu8Ƣ7U9eHc dSl'4uSI_/,XƊ.vQ,FXNyqVCEA00jcJOyַAK#N4!fJF@(R)~'ĥWWOFEۯ qAx0}g~2aڧ C;`L(5 u+գtM6☵VVCS\6Hh`ҕp 8ۇ? -mG|TF\{ήBkᖤk["PZ8#CdEn&Ǣ}{~E׬t3n{]; <pfo|(ĵHsHWv$4)u5ς}+o1DS wS3b6[O9h8ϡԙRB) 9F>Z*On;_3-^Pws&bx2(Hc`., ./&OoSe٧ Kj>\yp[6,eڗ o<nNh M.sDN3Wc+vX`u\]Ґ&I?!'TȔ_p{ 6Wy37 eb^q{6]wѻ0h۟C).)+ t{~*I u[PLxbonjk !{ rhC|eMH96DJb{:MFZ|@V.!yɝ^b룶 ¥R*'=Qs+n XU8cOM!h$ % pme ,R*0n)!'.^/T)t悧 Z4!t:7b6gq$t C2Dw]G:2_ȘƤ H<'ɤ׈_ ‘Xi0#"jEİ*y>2)2eU :OepXso0ң~ M|ɯk^gD9E`Ec;U2H.q=:tӉhSǙǥ]8ŘRB/i+56_R 5rWy`"?/V;Dǁp0DNy/ eOU4~O >*Wx2/) O6$} bM\$붘J>6 [_p-X_Da̛-QnjMs2s~{ Ҥh'*k:,\`<)CL'Fgr+B[b-zAY'O>s*:fCy[)NdCy#֖h & 4# 3ڑl YǽHk^Y Mc4t'[0uIG͒6oL\BMBq65R)=vtD^Y&&ls"^ ]̄8,oN h:\ZV;CFu T(B@HPE,Ïf/fP.")B'ldZX!a|^M1E<3KV/{8Ϯ39 .>4 HB T|^yy~ƈ;ZxkS.oh\Q3)ﴳϒ]q2ݨDH(2B~voͯ iÑ^u`X{boL4gS(.GǶm|vt4s~T-w3K ;Щoo"$BD,J'룢YU@d 9^nb&p4A9s؝0܋ nWlΦ̳UCS3*/d":~1^ _ąk/{XreE~dR< ur6uʳ0r_#ڹ~(crJU_WYc{oSOS%1~YIՎKkUaz&Gp0ַoYJUĴ/2{}tcRET1[R<[~Ɋڳ {> gWnw:|aq>̤'2SwHy(Kv}Qc\#%ǹF\SAcV4zgGrɂ(ztVgqDx}cИwHD1juPPUߦ5/eu3j+(nsnd -]J@*tA!+`=Џl w匍) \(@2 @2cItrbrNQFH˄sS\{?x|oɻ cp{D6MLIXwκz#JaNz7O_[% Wfm Xr 3[]1 }y^UpH8$Od(ῠNKy 2܍T`d;LLka.;l4=fV–zsY6*ahK?&xOz(A9Ei   Kߋ ϼ) NMwb`'ZrS\aW/lC|H o[h;# qHk;Ўa ao@Uq"J5>(?e-|PΗ_V-TPGb#BN;IT *4u)а|6`krq%gJk5B%L|I >-d,]˔1Lk@.SrKv{"DAX+L#Gj=+5bKBcGy.,A?=dV$oQ> PZPf9ׄ]4mot,3n; SQ]5{fGb9[֛-K`y-ήO3YmU!Q9%kak:,*Cbz/uק<_4P+n dAy@Ðul,3/<(a$dg(e\ܑGrV*S̛RBiq&b.zԞ"F[3[ͮ(,0O}MLh)`т8ᨷt-i 9 S Y9%7BpcsL5pD3ߝ.V-)Y)pO O!DS'_!;36HkmAe@ϴ Fc[+K:]L+6X산uSd,UVrG[d󽳃wp[b;\SB $UZIt 2Ւp .'snvL=ߨH)$hέ]Ęb"E`BB3aG<iNF{Gc~W遇g\I] +cq"P-婰=4<-Ka~% r*^Ө]·6 BLlyigY1K:a%~ u{gڔTΥe[x5G^0Y4Vz)!5I:x J]afČ{ 061h?!(M q-d uoM\VhMۘ~K|"S2d< ,&ެfN2%܍)!s}6Ęʂ=e`#qi>,X0%Z\g~h@!#K7rk[o~4ͪQSY%+%qleo?$D~lWN޸lNՎ#Ÿ)*,v;(#C/*O:N п; =TJc+#|WOM@g/Lf@sf\1TCEz]u`eN^-仢7x]s~>O fI-=>d-\ш 1 cPM+[+Hא6s0M'kYTVP{TiV2ݦ ʪ~2Z=c6֥K+gZM8k c4Or|C6zQY/4!o}eO &MT WEw-w=)9h4ւ%X[y n Pgh&WBi4ߊAT~X,>ssw_*O]ҎpC+A,"<\EMiÈ#AY+t>,Ϩ}XM@ BuºyVjKwf srXbbquJQ~-ٳ)*Dgj-wNU*N3c;IcR{q8jeEgt}fV$PfY?gN[}v[;E_qb!}pd4^al/RЭCHxh|yFTK'i4될KJ,c0`Hd12DxNȻ" 18;YXcQYp9,s%M|Zyv-"zgNe~ǫsN {BD 閍r Rد=ﯸa͉ YZU|r#S0Y f ,Jv/ضAPre?Lݪ߳UW WM 8 )}~)VRP!԰lxA~7ew:O0lwM=+hd!λ'0UB_됡dL9h}dF&ĀsE9I3bɐL&)Zaz94Y-Ԝ7OKːv vM[_m _P.su}R[ps6,$cP] \7'&kDH?ѫtC]9WK孈ï U>R{sf{-?(n))w9S$X;[WI [me:q J #O,&@xp滩%҈ؽP'E\?; 3 %yU@f(1[Cƛ:}>l1KhLEJY?"䏶oXLq0@q\ф)فH]{ w$v>.D٭2 F>ASJFlf_ Sm1Cק` 7)>vw:sPQR`x]K{jTnB&$|6 QVW_:[VVҺVS/#ZhGrCWccK˜ѳU?Gى?}K<[`0r %:h8 y `َhPP[W m*‘aUe7rqG۱ hnϙ=+oidz!-u!aRjʐ`Kbw{a(l=e֑L)]/6HXRRz7P{yt} =($-] (^[[mBc 9<4oѭudoܔl$ B2sb5ǴptjiY]wRH /:k3(x#HI%)\jP1F *l/A/ŻpۊHS%қx_Wd`D* {[u-|lealͬ"an"{k}:|~$4G^^]Et(Ev1C7fԕʚ݆Mǻeؙ`󧣉n5, &9OcB7%~-=YlCm, k.LÙz9^wC+y?W3ݻD%|i={A w:pvAD%%lz Mi[LyY s{ʭ6V,s&qNX0 ŊqLL3eQ-*:h& '6X~UH&db+6 j_ԜJ+QW>ơBK c?2ղu%w2/>*yȼsaȀt3aWB(kP.ܵn?}6,uĔ2Ǭ>Bjő ;POq +[z/9t%x8in [elk^Wz|&UT.n7q-4#tYXW?">`_APk=,LNgb xcrJRl~s'+8sJ7_۱LxI\ȴ#4|z>wJ+_HT˯vg_Ŗj`z¤zA 8zr()X)`쇆C5a$Y)"Mnea*FTANE2۰;$~ڶTg7cyGB^*r&`&q12T{Klۺ貃#<k#*ΛvsUD6R%T1x oQvz#1PL0qnŠX K\7cvKͤ+Sm|Db^HR8 6 4Ntl\u 0G,kM dbW(bx!1*Zn` x>n\;_OBAECMŖdӂv{ pDwZ':nP+r/+xua)]aG"ُ'_ũ)9 t|4g{3SWYxz=d} j搘eILOxC"rlrlcEֱ'2Ua*$HGf=wq5͓; ej/%Ĵ$i= ]:Qo/v)!:4jyԩ' kم^,bǷ/ P?fOYe܆R2gG$v?41[8D_\Nc}MXv1꘿Uj?O@J͐R==7w~I0O#040w -lJ6ty~Uyզ}dPh9;6… Q<8:|u>|1ꄯBƚ}oCiElrlF$K6'4|`îJKE(Z!n)ܵg-iW(%* !HyN θʟ꾴a"dh9rӶÄH^#2Nr1v7j /Gr_e HN{,EQ5on~cAv񬂸\]ިխn/61FJX룿Sօh½ Zk|zDŽV||# uº$T&D6톱殱n` G7%*av/D”fK#L-V^;sdy# AyvGYA_)u6ARy>X\{Z_#c%ٚr6ntn`͇ª' q=ȋPD.tcHT])̰c-yu~޵،.s /@p^ԬO"deR0VS2 D)WI&ypnrU>_AԱl0/d'^y$Хu۰ѵEF`O<^V%}Lo s,F] !Sy.k34b.gUP𹂨y&z Hq~QnB5WKL3Ar1!`9`SeCV(M13[ "yb[OJ̀]y Q:"Yg.%g>&4Vv}7k2 2 3,1i#Q9t7w,tԹe~/|?ƀ7Es,+kU痩8rۆZ_պtjj7w hQlr wmhz&Ԍ(x5щdNJX)xM=9w`dkJmj&:pB#ݬmUwChɨᦀpk儁X\a+](HH1[-m%)tG1PAK{0Ψ\,u1ںT, x⩠d >NmDT XKѡFdK?õt 8*ZӉ=!^t0+JO}XY"z I4E7S8E-Av/Pd%wv2W>QpD .VH4SۢSGYƏw2G#:HD2Z{[xLek9< v|u;LNŔYe"j?L;W?+`TSL{U3?!)Nq(5r@O'"{1 ~,nlx 1ȨZ×*!V:/tPLڈxWLQfu*\ /L 0L% )c]1BXm%]@\1q(Ѐ;h59 Fwiu?B9Kp"R/Ɛ/D @,xT{A8ChxE8 5 +Jg̋%T c*Ĺ5WN[\1n?ڐb=*O/L-&-o;ȿ`&,b'jbe>&*Ev9Νv<-5VMoţ?&RyENEZlG<ȩ@IP͒ *\sǒ.\i=2O`Զ+`=쮃1G;c:|Rd$UNqVAI c:aW9"^ 93g|1}qRٙkxw](fӮY`ٌn4mHǀhl\ C_0)ӘH) KDAexRyx[5+1Oڻj l]> L%`s,b%`-It:2 ?BQ-k0|yeĮ;b3 VTp3 fZ5f_Hl!Fh`?fu ϓ_d*_Qia<c&kILF`V@>)?V8AOyPStV$DmmڑF@'k_$x .`/;W,mhJ/,!>T݊5v C. nlFhS ۴N;\ǧ1#Jd؀90_ՂQq3gWma^7bw>.\/Dc^{l#9̠onֵ)ܞ1G{o߻[\7,h~nU6Lc_i LN ?#fMq}i,Ux=repˤ{(b 4BY?(џGhS!a\ζh>hշE4(#}ρ_0Y7$E0}+oM}R3~ܱXu@W-K,Ccp~*Hh~=nugPLFω9y@K+/ũZBNqr /FdYqeŲ⍏YmS,(;KCWTmK]́I|u9\ɽ}ܾ}VxGEKM2\1eA n-.+@S奖kfΡ2j.!b+Ϻ0W{^ .&tNujm^6L@jB)-hm8t  42 sX6d.^xݹAbМ<ٓ]>:+eR@VlKi=1APQD3[b6\i1o@ɽ'l8Wm->ԗetr;>_ *_=7%oD'O%DlD|wE)b5etݣ o-!zԂ\}e&|[My Iv96H#iϵΤ{79/ Q,v jJb T6qrTos>1D^7[I?FVhTMj H53˷T֝X> K\ִD+4d~c,Tri ' ua$_?t3l5%/B}cW5cdSa0/_ՃO=| ʾ<1(RjcgĕU, ==; .]IAf5s݀}|UV0 0Tr3.LA[>I˾}\M8sk쇏<8| -RsAa)svǂxNeNZN)?٨j:`6)ldȯHsH̴^c !}gOa۷}2;JI\K^$dWba,TephD[um(Fs#׍'|ASj FZwD"'ޛhݮtDO;Wf=f"EyŶg]&6˷ =@JiOŸ}qE?sA?ݙpmI&OLDFUxo#ƹ Űm$PS EN%=Thl6h[sQ *Nѹ&{xUѢeA(q0XmZusOoV|zئ0c*v$ -b7"B0dQ W5̿C2)M@b' 8*"A&QSFgHS=b~OO `=Ys6\ev{c4i /Oێ6]/;Un8NYsA<5fJi-J_H{nED'ZD&O}14 #7:BCܵXB!yhh?pl5l\o.:=Gߗd (,pA(R%ɮn+s;U Ǿ+C(G3^@MA oC +@ 5$TFZ@:{Y b" Cīi}笯WE%_%;̒=j:C H'Q &vP>]Y(-t+{#WdK޸e ̓ 69AjpPM(}z{_sʕQ)~?t{tq:qO#B;>ul &-z @* :9D`z* azӇAhU"陖yviC f4#J'K[*6Qg7lI0RU1A!*LḞߍI5>cg7ܝo4ܽ2ŽxU.֝X *He0YSNG [#7/7aqoXz>6t՝QzM6VVK.Y 0F.[:0U&v1ĻF^wfz B4V&X> #"WA,+{t8ֵʧ` W t*M쪓Gk*& t*fnP QmZXR[-c>!LbWWvoCjEK"ԃJZKXp>}ĂI|vJwA_ᖠ'*%RwV!budiNs.ľta0svrv6_J ~92xz|8ha4ѐ}fsӈHnggw`U{'Bu9DQB̮ |uFr3׮V+14IЋE>fJU8&u@xnJ7~KE>o39Ke֝w{Ci:m`<ܵfj|:?|jf d3GmMUi2C2Z@,ZO|x﷞ v ʹu2O<rPSEcLd H&7>[mĒLJǮ@)cx6KCc<䅢K0:Bmps?6۔*ԕH$Dyxŗj|/,f-t"Xz^o%9EUrs*MnEg twXJ/umu8Eeh6fu쥫~蟻eB.0o}_̊P?~tJBEA`u"ִ~)EۡR 2Ve&̂0S4&Ja%Q`wlGJ)kʊM"ms),Z3Y4w?%T~#Y:\ vT}rm;q|xajW:`?kjS\X!u(R5=<i/k?A3T;/hv-p"-}1-Vï>2{-n;JVap'Z3N#ѧ,nQm=Ge$ U 3-t>$fL J`I.q{1sng%m䭡Ŏ?o?G'rfW'cbU;IiPKez0ax3Bd<|~T]Q$4zN˰4PF'.#m8b!y=#ІQii\ @&J )ڑ8'_Xe~,Oj;bS֖bN9QkࡌJY}KVwIےA{꬚מ BpǣIBѸi*86hN>V m܎O]hw5Li)@y~QBӸDgpNT̮`o9 2-䁐SѻţJGZk Lfp胪O tdr<3[=cFt]#f3~ Y'FtDTUsNms X,%W2Hu Q_^Ԡ{dn3-!]z]-$nٌK&6y.Cj3$,m`AL8DEAL) m'ߞ󹼄8itAz{}TSesyl5͡*~B&U^x)셵 zLAe8:RVc" 4Ó.%_P!#I-j(:6Lֹ{hU[`IUO@y[Q,Ruj˝ -`S2zCCRG߆OUFhC8w16ĜXCJO zsdHi [IFtZVrEjzM &1 s9n̄$Y˲@l1R] ^9,ח-L ~( @Q (FŃC'`@ukr "Ҋ4޾S_MԣɁ:MX3&+ NCpsٺCCD)J"ttDAhv μ>Fl]$!3_O}w Lwt&lbdf!/64G']r04'@wH[(cy9fgD5R7ZH|d_FD:'x9a[1GIWjj7տopnl#^70.[ Jg{Szn0ht-(*k#ma0Q+QQ$ՎZx;PdL=޴֐@6-|&X8BK:r=+lmC u"%:;iuXt$(`nɯJJ5%8ab|H9[:9R ".RQ]W5QzƔ*O51" umsJ, '=QжqN/|Q&6r^r?^Ϧ$EP)"<'ʹꆑrzE˫_u;쭐bE)LXbMFgG~y|v' I[k0)MҴYVyb9>bȰ&0zP2KW?ճ,\UrZhtl2 TYy{W2Zx'|t9 UiL U76 r ;*iu΋&Jq^%E,hR3gpY`DW0$MM{3XPdUw;⃓B{ 7\xռtm:X[ϟr< -'XM1#\ [3ve cF[zTHx̊Mqd<Rg8{]ܲ58`K7fl>XZ:axHF@,މޕ( p@ј|e }տj΁em* C?(rNڜ858Ζ 95☁1-X]>]pfTQbw2dπg=0%!ؙJA GO.-ք/ b].׭[4 rnHǛp-Ĺւ:ZQKNמIF C.,󟊢Ƽ8c>w 0Ľ,)=y,p;to /d'P~hLE hMCwsŖ(  PPV~\hcS@ȭT6/\>2kp(7頇7ֆ/f >F6! ƅ NIf|JU3|pB܈_7B?Q$n=_1'lKs*[(7Da xAS.r8d# |H' iVugm47x<IӶfV70Lzт: _o_=tz7`&mk)~$lўltqze}R z*VlMv s!OmcPQ7#i[k#mp;bX2Y!TJ}~LQж34;]MY>4R&_ĔzjtrECF咽p=Q&i7·SzHRmE>b !X3n!Tٯ$}ک^$MJh!W[X~(4ZћoV#zH qzR ,cSffzy 0ߝX\Wfi[ KkIjzEsQ0b\lrz1SqmϵMCy3hMyA"]jG,RQqjʏnvV\ت"np>ֱ:N{Mwx5ϢjȤ̚YƑX`.u @ }E]4+4:&}d=qimTBsps*+7$lW{Yn#|&=3۬j2i!h,kkQcۉ>Vy9:JVË5d!Ǚmak)OKi>Ѵ__hmtGzj%%}uB'. JW:7?5Yogo莽@>0yhX vxZ͹ð2Iy?ᛤYyKvD6IP拆q^O93[źt~s:~KdgH[vJFP3H<Ԩ+vFV @]Fӽq5"-0it. ,`0Ϥ-nja]x_n0u`_64<7E4E}Mb)̕>{#*݅ژ[lT%s5e !{gml;p Pn.+"@{ǿ>_ϖfưSo4ʤ!CTt9==12z{yN͡F-{n|'?b *=: b@* w9 yp hknʳ/Y`FX M OԊ5I5R.i;9EeYQ[dUu [F>a2WVC<otzEdldij8坔<42@h":SOA@!]7seDMy8ĥ2x:iBQF QGT]5såFF,FJ]J *%k'Yb eh_q)Vo@E4tv,{ۡL <(Ӭ첫CP`EU\lj3THR'ҝNĂAvTHN'MfY+!~"8϶M~" X-Lglb}mYx'gW9dDF{{Pߓ?mW SZ'E3kc(SdX 8Ȯ9G4 $wӶ!a*9N ODNJ蜾s)y gzۋyqpנke@;d*UP_NI"DFhy֮ئ|ek4!Ñ@~m Ax$/;ʤe|5R3S97.~h #MX%Kv.kQmNhK{$[Us%aN!6&;d ka$X7RMm^4E2.k AG Wh WqX6 ;H/^ DŽ*( v1^gt#jQ}}9Ķ&~74Rp C(=̛N=0W,|*?rh>><䈆@>s|:~"| k>"ff_Z TW?˥O *Ad[3S)ul?L<4!ճsP+.B%T |ͪ_"Y' -)Lٻ$E!ueՓL<`+#?ΨsK?xjf}^30,Wuu.6 ׇ -|Hd6[t.nk5]ZKb:aE2qOr9kn[K}k قЧ3v~YzZӍD,s %xY}8>7j x=ZOBusD+"kvú~3 bbbl H~eC~!ζ߻3 )=u;N}5cH:4-7Qt>z/:CBǞN?f ߕOQXnBV!? K 8|fknۗ$f_0{n0x Akjv}>gu9h8X&PmAkuLn^ТHI*=coq3骽-Tl.6ljb<tqsF.y.X&~xzr\:,φ&Cm0]u /x>%׫yO6yp5Wu+ס }JcB{zw2n6W<HſtY';fRP3E6FM8G&z` J`~=&ttR]2N uVm4aG`g4rMj{b {Z$=9 :~ff'@mϔhA8o]8n}g'ǧk: A'%uSUa 0qћ Ӫ<:\=#-zz3H'g\TSyjIxow6ܳGc7tg(W^yNQP#(sv59ihFV˙{`3ir%pO& -֐u&K_/uK_\V);Epe6-hO$Ɇq,P3Z3X*о4vχ;t4鳅!>lGWSxg<ϳ2[<O>mNm:4}M!t5:_ 㗎yhӺ)ҋPpIGnR+n A$LBԀy#n}s3W^|ŀ A~;XKuL l /Z /x$j{*i+P5yQ6W&CC皯P# csPi}qjic،JI-'6dbi꿆oqpCW*^':b696`)ČKRA.vRn/SF5NZel ( TauP0C͚M~Wa7qm < xx{0vM[rNyL5 j*[=hS~Q֋BKвRwDJeP㋕ɓ@|\2)ȵɍ9فΖxf6tcUgg$XSa{M'U 6=7ybQ@B粘aF54}S{Az ~·E_5171A1ؘCu+8XQXURl#sn cӄI#߁ 5u *k&f}HO)'%8=;UF:nA{-MÕV YX(5YaT'r u x' L7Ul3sٝ1U\x^8rCå]98ر֢Jٮ%U(C # RU8" څ@l(T'! F9 1, Ogr4C<cӓ(im|Efy0DWYI!պ%KsNN/ךo^,)sx {;_3~{*Xezj!w+vOMmv;dXkDycSҡDU8+j}~|J67Cڦ<.yޡtZVlm9!T$?wӮ8tt$˜qyF4Q[gto7Hsl&ͨ4OL*y~Vl@hJhK5F8(yɠ6}+V|(fr34@aª;tZp5\LY۰=F2G`AN E\˱,9QldޝI@{gD)v2 Ԓ< r̛R/qK yAF$9N^U RA~V?mmȁ` ř jEh- ֵqh4`;r hJϑ_| õ\/w-s-%p]99z<Ն~D!~ œfU\1W7-VJ3UAW03O]"GFoGv&df[ȼu380#%#M?Iy>bQ^6Y1s('Թ5ߍNo2I6 W6T:)WzNpn H/O ^ۺkֳ|N X|}}bZ̨d8!\#K#lP@s1۰3=Ϥ69N#jm{ػDTyw8MjM<ߚhG^K=CQv󙎉jоp8Cmy-,4&ySG((j~a hң&Q~J7Y^CX[$Q?a b7˦ES ._={bk#hS1>4y͑TIm ;/k NkcyGX~K+#H[R#*uTVؒ`d!<5 0 0A_4[Մ(RU/7I W s aX,*#k():(8g>q#)M #/[8kAS䡒&xއ0 nU99 q&dZ7dW"2I&U-dzԺ`%nnاZ}ٜ'Iq "{TktV,~"c] B֥2pzsX;d8gM$g֬DǮ(~;NJ&'c1}#SN[MZCgInh髫P(5#?sW&}bTa%PLX?F37 $aXitν^,^R) N3pM,Wr_)$ FY](Pla/ )ZQ(Eq6yaOrQW9({Of*_1p+Kg1ɔ 8.~D{!|ex02$) 8_ {ְ慪ߙxGh.5Fp\yTȈkqr+Ȃ AMQBTKմ o&nspYPH 1>;9f8*2'0_FgzB6ʪYvB័(ݟ 9VEG?jzfN0ݕ +5DڹrOHrۄQ4 75Fa82^e(5Ub3OcIH}?]U~|̂[m7ޕa)~rt7S2Y-Rjʰhņgjl;IMue,;9@Ó>C;A6"C_B(<))mKX\귌%!L᪈pT5 D} GL@PIƱlJߡL~K,GƋ0mS0O ,9U-DcE#ޜ6_3s>OR6Q ?(-l2,yiFH!rzX"ZdߑxA30=Bqpuf ʼb[M$Ǹ LMig^F^;QG{DFW/26v3,^V"7j? ,eOJП8@~7tJ ZR~ޙApQwd.$+ZC#]P_9XI# ڵ Ί20Aκ6C)str,Hc6iV/J"*NJge `M,Y| ESw_7.ዑI 5R8ɶC" 1^ a7ׂ/.7E>UR%pcGlG@8#|"yFӨtwIq'[Uusfps*pW >w ;yRMEڼ$4۹ev_+'\/Z#=+ SIĕ>sp><+m^8٬ Z,9("}m0ܹqG%b biYT[dO;C, \+JjT']yGbi뵫 ad:M*oAS1_WByS/޿m&Mi+A9C 3ɚm+a hXF)l)2i؉7m1[p2/Ɓ0Uƌ?GeY];&Mr`e ' JH8 V}s=hn^z㓠XPٯi>S.Y~Y۱3 XbcԮkοiXix!򳮋I6_o+@ߡ(iЂk*ƤYPMsA1,.bW2CCI=eeZ-;..a.z fwV2?yX}aeqrۚ [> :vxO0 FG.GVAQ̨09ҟ#mYvxl"jIlPVe)+H<6 I9(r SoROKkzwr|`kTcqa8"aG[K-LKꀍQeE41qW="Zɤ uhYd*;Ug&U>R`(%Cl*=uJ07%:3z2g&\`ocaف0G%"ځ\x8h_{2NYJ,,"VҿY|z[S@Bq'}LY09د9&O(2J<|B[uJèn WKr}GOEHR-U?Cܓ>\LlA.QyZ"/ךo@*s˖ Xn/Iz˅Rs7;-gގC-ci𭦇:KO|!y& mmj5@+G5U,ͱ[ Q>,!]TfqRӥdxXښ*4mTcĥ IMRY#>J%r1.rC\YNVKDyq8 mbHc<3:'EʶO^GE5kȾVJ/zC[\}hґ z%[ElAY(L \Eлl 7]f^@>'FWOIo1\'{u~;EpXN~z1Hi XF@{P%Ҵ4?T)v_@D/<#oϓ$nkdnx""qHרŸDo2hq^_&o@_ovV2[NnA˪oj&8y^f c%Kw`Uo̶[(dK%'iP7T (wN*lî6m2PYxT}u/辫,2>J Cu :[i%)& [W::ǗTm^蜈#Dzh">gߖU K~E|*:`_ŷb{ס_g,qWc zMKɡ/3é=ze߈m\g{k{F.$0GR;0C)aIw7P`p20gXƥ&j'P%w|z 33c'unX@89rM{i"1|i7\f%m-ckc0T=K 5uX?c?5"/֋}I ܦ_i[ɟ`7i17Q3{X(CC\eCwx`otޙ ϫ&tcc Tz_0űpP =.|xvw&3A.dݿSќ)a:^)֕i\Q0?pp odmh[~@r/pvxɿ^:bVz=Dz43ngAY XorԚe#E>:Ml;]S7jO۳?3PΥF g p)?[""vBomii㱢`i!h'swyh+c`VsK2U<ڂ t&&3kSiuN aO&X~eͧAyNZ8WXxpY)TG)Pz?(1ﯢa.Z{x!]J{2O79$7S+KziyGBE/:Zt,Mp"\9 3|mش1 fG glEOc݊žv~_?z Xm7$=F֗m`S4 齀:tUx60aR/dynpE/Ԡ&>4Nh#V,!QMlA<=]u\8_\%dB溅QVW:D2c$IʞxfemgѧtY4 f&bLH71Voc-6e敠s-1Nr vM+bd M 9R.ݩl4b`f j, zBQN[ Q<ߧW׻fniu )›u 4{ h煮>;aXΡѪMzlw|[X)WH AIn{=z/93&@ĉ4ꊭL>/5m6H{Ef(d:} ESLUjY(#4|5خPNq["8΂ Fd$#45M4%t̎ï(,m hh֞%5jydL+ڲU./:NH1<'Mv7z ƍp\Pd[ QJe絡/2۵?sd;4%~~-C,P2V,E6~v k@3Wt eA_\SP72Ň '%LcZ~Lġg3-PS[k]>X# xrKuXܥ)G`6lJ 2){!d\ -2Op|?PdNjƅG)Bx'MF'1@qvK3%iK i"n|Lo00ت=^ptCRtsj@=(`h O`ۃ2Mֆ'=FBѮ Nk{6ו0:hɇ]SDc87=>s^:uQI0}!rͽQmʮW4VW6P%xm=By]D nhTh_/6j޶Nu>Dg"Wki ."d]/]2ZArxQ^Kv MO/>!ʿMZc\x]p5"%ta./ƓQ Eg˴59rOA$w/矐Ż$Jw3)ޅ9uW4HA)Q1gEZ7/fM Ґ!I\)-F`eyYOooyTcid^Pc;hg&`W 8zݘ<+6J[4JI""1P j@MS$=̯d.r'zؚmPjVG+:Fe-Vv9`OWC.˒ୱ6St`'}mPPCүZWde|ͭKܬi"#{Oܗ~6LuDSwΩI>pYU}ȷ9^YہLyÓ65?Eڬ_,8cxڍ"S4D.P5.3).!vjvػ%¢X k:EX^A-)crCr'1_ЇYRRCVDɗ댼`xRP!ZWsIqTNoTZ=3Fʷ8&R)YV`{/%Nit\-y4fǕJ*Y/|8x4?ARXҭF 8] k]6Kt KK`r?4Sؗ_+]o ;C!*v<-/C^t-mL/˭)oARa  S(LqWGL$4o/gdB!<ĵ%e[$_n2+{@`湣8ހ(/#ЄSGjT?ʹ]F^C^5mrR{fXb%8iPY-Է}YmP4,r4(ʙU8uŔ1fr.X SM}`捻rV>nk.@V6/Pw-@Jk725-caB(0vWBGU>%:~r1˖jS2'\zWY#ultLx:$lCGZ7v:(AF|N5o(^"@bg'݆sy,R#c"L$J 5~ PC*z'Pn5btP%٨@vM8_J~u t!t7pHw2G8@A((5UXG,M53qyAgPi_M> os3>ut$k#/:l$~7 ʍÚI&ay?*J(9UgʳgnN͹9du&/MdFqYgzPq򹎖 ɆlQ8T-+oJ{ ݾgԜnpxQᏭ& $T>zH֎O30c6?O?=fBǩa9 #m65az}ҝu] ZZPo>w6\Ymߒ,Q"Wu?TdIːh;^ͮB+N؜8Cȕt!|[-^wA_3h$<kx̸d:PNčqfuQW-R6RNBT'/%%tRwm؎Y8+c\G!oo0ԃV~xU(;2Yde^zO> zF|#^o=Q`\vZ!Y}ɣfTطM $n^x~΢]%T&ߍQv:Lt ;8}4wZrəMZ :V͌vj@b$8$vlסjͦk:9&H-?Aoenb9iytZ:ApM(@ bq*2<܍,;V;iRr )BG_&Y|EECL"Wpc_=>I&J i >B2G4Cg.]bC29J[YO:J8"q/6 4AapÎI׹^cAHg!^]2M&L{Fr- (,Fy* N8 °%]$ ˭ roaؖzFH4VLJbu"Zu)RAލCV&fƢ)C~+{yrx~5+(^鱸KTVYCk'ODrq:~?:bI7^$>UbT>aѼSiP8DgL],wQ B-Czwy(1^l[(vxm;6Kn$y"O9^)b ʦI%̨XAd0MzӇ7u,P0*7bJ$9\N-3XV \t>z]NDktqn?7U[o?rg)` 0ZM`w$݇bhcPTU%H6>Z_,ۣQmN@k&\JDnUk` ʲ u;OLkM/8AH*05GeF LdMlzV "ȲՎ6AyF )PEza>8P܇mNh-sӈ64(O,%ٿg_כJJܳ d2 ,0rp'ϑr- 3>F>Ҹ"GwY$[g5'_T@19h[j B+Rxk9NOcF b]'`B6Z ^Y=1&]Fcnq7{: $T4-%^%;θ &LI\$#9g61AJ`m{e'2?)C >ԋ̳W%tVyC$KC6o›h>r{J؁ /M(F{-nE%s]lN+) x!VKma?,=0t3CQA4=KaT)`@ i#+=N-k'd B-zHqѶ0gى e&/me X:о˚L1-z#RqOο @%\{6a/nrϗ[B1$ܗw~ fJLq\)Dkљ/̆Deh :Clҧ!7 Obe|mVtѱ~72i 50-˵̂@ML|Ɲၨ7GO WJoՓIu1p_fr_%H?7M'EqA}6i|ׁ".̎?DUcMLxon'pl)Ow }p}OQ\J˄N&FGtq2!5"[ hAx)?57e3O-RxƊn흾(Ы0f,ZX[w.PƙW g6O&F=1XPuz$84ҒĒ3ߴyr ׇDk.ꭧOwbA-Țy)SϊOMqx-Ou>JTfdQц,Rիr,,1b@§h7W9߮WfeR7l'z5NX@ӕVZeھש r8 XVDL8&kW ^JE—=7 ZiJJm˜ %<ۆ? MSd1$eJ}1&2Jl#!#nj#Q3XyUa]Dj}c V>' 6(> KBTG R$8טAΰ.bI3DmFspE΄&i!U_K+:ǐ fel$RQ 8K_\'q'KJF44nʱ8kbTP/E RQEZ^+1lGghB?j?֓Q sksF&ώ&jwylPM~xM:݂6IrD_Z<9)i {U+RjՠMwzB I;]U֯3ErX_[mnS".Tb> 7qyR̗p(bS_c.Qu=ͱZ`c3ȩPt͊U:1c@EKAPfvG lqvlUӻ[̴]'zC]uIuM9:"Ϫwzi4꯼`Fez☩2j :<zs_sb:[*z!j)o) ,kI[1fr1tK0ʭ?Dq{Z.9W}_8I5'Qx/֦Txo=%ҩ,Xoj2oP+f_k)D;?.+c'e;Ṁ`J1e&4A*@Q/N\#l* }}Q$Ʋ~bI_V8*#3`3?9)'̵[i' ~Rֱf؈)Zg_FoTtj \`aN'iQ_.^bqq!7BP:Y mJ *X uᾯ=\YK{ F!.Yow(qT[%r y?(b/`Zv_{˙)R>k})g6P4nݙ>C' f-e3JhTW)=>,H 'z7!0B~OcgEqn'R*vRC:AD"+DoݳA:@@>Bu]A2lFVCW|oEƱZ{a |@VaIHh&^HV;[-B !26r :u3٨w"%W.5)"|a]@фԳsox^56-I \Q_Us.neR@Ux;5yn>eWoڵq則Rw*:Zܴs}ێPCMaVMkl\d$!jlh-K`R.E՝WxP]hO(񙬄1W{#]׫*=oqYFnuBO%ݰu=arR\;{QǎJ԰AQ=]ৎRTzrB @A!ۅ'|DoL?T=>Q5w <߽Y?= &YT,لj}L]-ݲ rUүJQX<Մ  )tkqw%KqH4h‚,;w40P" IL J2 51l.A$0%SJ_ C$Mݑq=Z KߨgE`gq;KluJ9USl+`R% &{ v4qcv{vPm}Bʧ:d6Ke;싯~ze/wn.<H]]1 :heI'\9cGZ:q"!ѳ )LB a?AWķ~x|rEp]ZEnnP1Cb8wjL=P?Ԫd$hVϼJn"$dnv9Gsʘ" "AD_qQs> {E@Cc`G!e8` [¥/&EcE!o0hɠ+'p|"a_`s3J^DO]S~OxX@ 4,_݁BN1 vUFXF@>`7*[CAŵ^|`hȏo^/Pה x3lãV.)p9QRPYW^M'XӇz[~`##zc Zh ,;0!v0fC6%3)ĉc"+I։~/cP)%ZR6iXmm:CGp)H .IwON|Z8\y!(o=T%ôml$ Geɞvl 1/QxYdתԞ '﮼kc-g^Ybs<} 1,QkUg&r, O /. &>sX&e7\&>@ Ps5En%~2p΁^kߙ1-v#0k Ue岑尥n#+ڟFB]+X͙ϼe], {!e GH> L /MϦ+n( GUv&`E*_u]q0Iq9'`/3Bm*rǰak=˙(1m}#W>Y1dg)'`\r?\4µP@AP7YR0}#]w( Hڗѕt:&8"àDk2$~|/gkO&~QwN yXw" hdyQ 9}8OS6HYEr} \_ٹH9AT9$`({~˒_`~6alۣ2}U:el΂:2QU`%oGh2$޸w` wc*Z(F=2ƽߺuڭ ,9!M! |L' 6D7$U3hrb5(bd]* 48>(?E'jwkP%i eQD,^jŌ4Y8qCvX{ƞnj uN#eCh6?-vW{8[I&{Ąqމο񺆍^qC Pq*êa KM#Bg8Thf0@Nݺ_NU]@"^էFdzJ٫{Bϕt }jwG05PNc!}EU<ߜѩ pMT8Ȇ:کPڛJ[-s+vF F4c.!Z:lㅬW^Ky_0IQ b+ vIJ2ءw m^J逝79%k !x!>/S7B}OAmO/vzH$ P 7 .s4N8wqJ8w<W Zjjy$ w&ECC!xb ygҸU 11Vi1Pz < ~^N:VM_W&_1}\w a  ҿ9CvOf|<:ط{P%a&2ڜ4䦞"%q)foDϿ."Pu/KDWmL L@uΦ8>]u¡-Lb /|ez) _nA}q!*:M@(v-[UU|ShGVwX#KwLN>&ȠE/Qwwi0UXy'x 0b CIp1\H+pӚB;Aao?6=* ,-s9ºd{f}9w1CE5EP;hh$0jBK6ONo*x'?B, 2$2\q {_:,>-,'|p0uڣ`*;7?puA!fQv [eG_59ޣI<#ŎA`Bg} <ᣋ/:7'+УEʍ)r}OJ5o\z콘T7= 0Jǭ 7Fc^F 9xPwB5CvFIeX,B۵Gfr>G,4 e[dJNW cfTY)#b~X|JHĢ/rw'x~+@5,ߕJ9# 5A\JO^+nF䚂ܦo_ Թ7ꈋ^qDmQɧVϫ;,Tچ ~ Fc} RX8yghLqX/x_y؎48c#RBЬ *-/'8 j'[vúP5O>yוc6֝IN=sQ]QW8n݄7{a` M @B=ْey!u0U[qa[N }Ǽկk^:jXU R }"WWXMQ󦛐a OIrYp? L3<N!ԂKvSSua[.j.,HÛGcUZz,xyGLkjZp7L-2 26cd;领trGڏu;Hy1BQfZ:brT!)~QL{&^D !+[! =~"H:ێ&nim9t_P'yCBգm}zՍb/h dztqFxU>=Vtqxy}NH_2jV#6hMۡq$oYacIXJ6io9Llр>EG(U6_uLuFt}c9?QU}8Útlq:6Rr'/p#k]Td:4QugPݏD\ vx1eL?fsP]@Z_`q[@Z{R dR] y[5ޒ!FX2Y-<< PÙBiG%qk?jPX_ڍTeO_َV:aBfpIRPڄe]aΔCY'7"<X eD9^w*-hxk0"o8$xj/ 6d\-|br5/'+A\KKos1Ibr6&|;qP|Y\]> P4ƴ-,Oh0@x|ϖu5LDrʈG,VFǛ+l@K$MZBIWȟI͐\xR{U=8"lz6霮!jLhtTZ cӔ ЖfR@vMuvI(?7>j%F<Ѫɮr&F~:9<願Е\z|$%$RZK|SgųF^p}UJF3{V 2FnKB'cqR}ô炗#ANY4ޯH'x+˞:$Qz@"Ilxߐ]T)™[ x mcLi<2jyS0Hy v,3ǝ\9 -0"66Ũ~w;#\s׳z'̬nV6Bt" i+VL3=ch HcKʌC ~?~,HႵ( j͏yľڵHaz13:[nZ; ~~'g5I4]K8A\զ2.Iwӄqי+qUJ+ UbLOO?"d-pUA3Sɉw'COs6sU}Um\@ uExbyU9};/v1gK'l>C+e^窅2Gu_ Qܺe4ᶁ?,D38G2EC? EkdEAwa0'm1o*#\,)zP؇ "&ikgש-PUQ'@hYR"6,v#F|)16ۙ%RV9J s.ɦˋ/ZtkMY̻Җː=s^j0U!Ʋ7;Ib9L*DG@v=i `OarwfDF 6yخm-wĿ;q  y/ud:auFWz >[, o`]|a+B\cNiQ3,( 5dV9mAOO(l3|(o47@7;W0`c`q=:ݲ'V5Z7UD=TJ`2}72q9$ ($=f.UQ<=rI@Ҥ܎ |K|([UATZSoC|-y-Q\T;y45p}a8CW'jga|TMN zҁ_GGxhb-G H)F0=xp|7'<SG&s4M*YY, CW n$ A@nXm1b\Rۉj٨zx19/QgE+8+&ҵ2޿MH[Y$նn~$3ovѴw]3̨}ڬm-1Owk֢BfwJORcH?`K;1q*M5Pl{nm)M)/]p/ƽX/a w 6_wR-0}[.,Zu VeɮE>'_pՊK`Y MU֐`"M>Ҥb kg͝v1DG  fȆgum1t !޲_ Tuklo-ʞ+-ap7- "aV#_ЬTԍ^g"?nhf [3ꮓ+57\ME6 Z$G2|4&ݿ$>\#J)@}8"֖{'lQ_tlݴ8)uәd]u| iحt4%!z6jRZE)Rٲ ./H U0Al}>T'kFdI Pe#崝\O^Zmc>cc-s]iFƁ_r8wG `af~s]Q!aGh,"\|Ub]Jdu][H?P0B{H ŔA=W;T{l}<:&l"7ڜĄ:HА,ȹmo?C+N2@?&Ѻ HӳV&:oKTSG?a/PkANg1}_ALţ{}QK'b;4ek5zӷ}Q`mb'եݥFbЮDߩ-7 ㉩䢰l#r/R!`L;,;x']Fhm 2pZ"ޒ66v0N7޺ QSjma( kcZ &θ0w[ۥUOdpMAu,)ǑL%j Sn -fz,a]qKܩc/^\>Eme qp<.etb\iUQ"0Wy‰f? Ss#)!/KT?neX30Q-m*/WΫ8e~E(KJSbtז64%v6H;e3PY.F]>ڦ:y+J݋l{!_')rB2[n 6:A07kzui:QO̫.5Κ:S^ 5[5/!ӧw)?}^P' 7Y9Tś>tLYPGNqwZ b:Tô3550M3 v[0f '-g/[ mm0?#-&kU$:E6yhì`[?9ux:G); MMmdY kP7NNoF OtUD>cDcE@uu€i_ާnLmZh=ra-E2 "+cHU#/>G(X³1 &g'}Q,e>i_3h_jޔ۴*x~g_)Cv 즪^Ńh^s9TuNJ`8k0;ݭٜ='O | MTd bvcso.)2 y( Gk_,%j?܎N6Yj&H{L 1LI0h踽Œ_d58y`jKA<(JhJcD`D bH{Z~SS8o3>0[j@@EPs2쿺YҏplIASyY簸$.10Z?##,WNAgM3ȣǸZZݐNԻUVYc;U=*}W>o?*TO+F«3 f&=t=3i\Ơ}0=ۍ@>ilyT{z]qHH&7/| w|'q\^>Eb=~k|nbT;m‘΃PD6BQBQ?* < 0U6 ƎV~|A,t^ڭm &&cp4r<] MK)h$hP5Tn9ňqH C9ƙ~aR>uwW?g.DAXf=0_lOTx $T4tF|d6ųe qkM_h4A1fc#hfToEG/v4#]XIA?ԏ܋T`LG4\vb42_HlߓOaռeƇKudqLF25C/Q;,L,sb.Dߴؕ(8e٣nxP= q!6k& TyAnЮYTs]}ВQk~wiOBݠ^b{RF s270?ʩj{ⷺ1yYx^q非+t\O/bJ#xXO՟u]Q8M$1ԝk\ QQq?/uv4e2¢O" %jewPJA#S-RQ`W(~y~%}?c 9tW|B{ >> /2܈UI$o5@*M:h3Ë"5 )xZ'~tgֆJXc](jXѕ[b[LTcLŎ5* R , 94~%׭C|@T=c+\vQ-J3fcNt(b}))_5&u:ՠ z3IJVU+1Y%i <5:ݚΥd&ۀR4kR6[6 Ec&`B>WOnn,Ds>=Dz8T\R?ԧ:YF_#Trݟ/Ɇ%ga;ΘZ8(og3;W9ln- ]FVFL)$!d[7P`;LHb<.ߙ+KusiOPq^v] ne'j=)@sحwM- |5A9^tͷ, 5p(!c|6ig}̘-=ˆiim#wp\8Z섞J9 +Ou|yIVoBc4T-aho CLet79}@&oO7fqK+2 n OHQ_3r>u nzoӃ̜TzO:\JwI;(Gj>˫]Vi5lBAd8ƯOY5)'\F=w*ȶ6vk}UGGi- *vs<8YXtӓ ?15 Q 4mfFt? qa*%m^L jG>\FYM65\zۦk 滮[`XhXu z/Q%Cfh.NGf_UpJi' }l 7VZ 5NKd -?6T+ 1mdzy[93s2|ڋy:s@p}Ln f}?yO9bޜW%y6U(--Qk CȰL0Nܒ8A.Ol ٞ*@~](mTzAݗa Z:}"aj_m?o,a]Ħq+oO|ڢHܥ5n|ڤdb:%㔐OL51_fㅌ&~g6s_b:׍(3GL.<3̉)b},ed3Jt$F-d,[xnkƥi!јi;085vH*I݋tOm9pCM1z V] tGr%><> =-wx`{HVݧ1Gp/e~w(ϣ E3ktb\g~=B /|]OWe,N4ϓ" ^-k&)ے*׉]Aah!Pe 7ߜ0U=ϑ{NEFU4qiiJɕydխT>JA[p!!h!{:KI2ԡxS).- @ytl/K> DI!!T馼+asnܡdnӃ{nc򁶠kgQ˰xc @P5; r!Ix\,b4!oBnE@~:_8RP.e\_s@cR bIkMʊ;Rg 9UASc  >2؃~s>L> *2܏@Ga)h4W4Qn/3*60-L]Qy[ȭhkVZ]YGwKC{Ezz2}[%FFbʄ}+aH ,EtLԼg'7p]6 ٯewyan; `{hm~lѐTNǸC^R^]\ցs,4]YtOq9l/&3<"/È9?`W׫,\e9]SɖO ]T8T brw'.]d Yӆ]+#Y㱃fI1{ yz .w I;69+0bVd:Kq!`%iN\INN>0Gy j )> 5 ] n0'`&aåm([Ɖ-9͕[V+DW}1@ T%x؇7RHz&Tlɓ+ْٞT"c;c {';+öl  ;ڟ= uQi)Vua~C$EFܣ& K4u OR ucipGv~(OAb5#eXnX{m -]86.N)$X" Qk r%!U g DOVoZݤO,[g"p~zLsGNSY\ ;4M* '1 8N"AuS/éjRQ2"wu40v"yVok*5.`~;RH.7v?ovAf2vLvvf9A_!Q¹/cy,EA`Ȕ6ak̇P{*5kx_=G*iT,4RMC<!./86@%/ BHYp(UHU+O !jk6irsKl`$*U*P ~ܩs[,<Е8?&^sˆsXLNMhҞoE>vq;V>,yeϜB2t}?&5_pp[Gh2yHJP줘DWG;oUi؃*ql3YTphNzvz Z>1^`!&WKP gv 3paTL rka P4SX酐ENxR(bj 9da|R{l f?8݋ oN 'ϠyHðΤdaxJ9y*87_UE#vн>O5O648.|ǡ/k}"@0K\-@,ϫŭJ9ˈBbpTP!@sΐ.t֊1ۖ7FG΀f!|m5ji;߫fX-քM8{ J4F  PN-Lݺ>ZINɳ;HZ`~lWAF@E>R6%cX#P Dӈ_ܟ^CD8+2 "\7׼/N\_怸ꌉuMR⇅Ox7ؕw9>?,?@ غ73Dw.*?lpN/ [џH;6pUp vɨ]?+!-O@RTf$7<>ӎL'D9 t㉅aә]q30f\dxKiчf|!Ҁ@] 8^Kz,W$QebHQ L;4NⰃ_xZ\rv-3tcW5ց &#83-W| YaNf ʭ Җ: YcgҸhrhW[ĖKqﯺGA<!-c;^(9wtD,Y3LՏ@W~,Q#$Κ*BDĔS `eWD%4"j7=w?A5QA&"xo`Jm^y8hpvq]q).7#x7%]c3ײmu_F(;dOӾN _ZҲ]xγ9 ć7p}HrCLaE(͖eO]Up<%VKn'`.h4=a*$^mM (^2Ӆ{eQ.M@z1?q}Uuh&+". MPms(Qxd9cocoBhl6>8jCi)G TLc b=EY}Q ",#ɋ;7!n T=!#rn/HSn~Ϲo#=nmt~g >B[z !x+'ejC̫J~RG cÌe-%sPJl׶!RĒX7bW^|1_ׁ6"}hS<+.̾}8ta_G2JDC죬1а%9a\D%z&[@hľj s7̫T٢GMp |..Z(C@%Unߌ}n'wT dۦ~k}85_8B01z\>*HB9 PI4˭lU}K)8Ggs' gcg%v"kqznf-MlA]8ʆV;bqS kU9(HL(퍪!6WafQ/p]~y$FZ- gh|:Y%]˓!F'kE(xmAQkU0s5^qճ*EDn\F#*P:uoTKG Eji{\A64 i7cC[*1DW\cTԐ@ƚʫF w?X߷ߪ7?g542<&='a̒CbKZPntu/yTmjUDJ1Y*+ /CF)=9۩ Lo1)0ؒ4gG*Idpu.־2C6+x$sgUwtE __-_/ lmX\KYo)wt喙Bm$BIh 6De4[' xړ-@vde9jem s &82H#cIGݾǴ_Sf+ǽ!zJn[M.opm4cP#ҟWKp2Ó lB6 |jVuWAaNۊ3; }k* jڀ!}%^#r*؋Xk.U@+2,}⯥sOhY(dq}iqV? '_L{X nMleA(7} #5yPƾb  u"gĂ(;al"GnbZ|$ľR{xU n|Ou8428zLDA]‒kͦF$b†.\K D1vwnyJY ޮ" eR:7oO(Kw4GjF{lUb0'w.Arw{B:A^4"dAo,t{*xO=L܈G Ԙ~4 O$Gs!TC Pc~R@f;Ge!1ϵxLh}q\kiR' Cs9yXMDlBـKN$zCE4B#k t|O#J A?碑S]fZ*P␡zE=8i]0ឳVTxuDrxx43WhZ"k<s%p)Մycl QrٛFR5qϦ1@;7 r%Y 'GbwFɚ ݧa $wM} gѧo96YC[39:$ch4Ɛn}"2IhJ\J}jۉ$Hzg<˥ͯ>"{OI?!5RE !:bᒵy]_:9d}Zs2H}*{kF[Yo62MLӡ̢ێRvg=v7I19 S>KWz nᡄ)6gjFz0-@!,YfHTq3+d# CT*þ( ŹCLLtr}VdǮH͐^=2^q\㔽'z _lrboū2CsON5XD]9dV )zUI{U"Pq;`Bj 0csk2+;[N'AOtLM ꍈݮ\h>@%vnlFҔco:rPɯGd(FH+]eJOumǰGTgpVKA|T``P2k9 .cB#K_Jo_ZF3{Yo]?Z &4΍C8O8DqtήgHb|F&K5W`.sWi}' AM&؟9ݬK"!8eu:z@ר{4Jx1\uM7:Bf2ҩV,/J[$ Q?1 X$ס`aH$^d|Q\O^s ڸtKZ̖ ^KS9 ^[ŗVbcQaۈ]kGdJeiJVpi-+$DMoOk8VƳt,^ӶED'hC t98}݃gFobT)F}s*Ma:+F=_X 2]6 }:2 F 'ėal%g}uY#YEnoŬ%k.uJʴC{D_xl .KmoO_W#S>L8K\fh8iz9?f?p-64GpD-Ap4SI>@p)Z\}P`JB=}PTG(v#ll9?z`C޸w8dxs(ܙ!%Xԏtm T4[vJ.gxMAt8C4E?ara"pT%jzYJAGոݹgMAՓq O#@PVR'LEe4+Kpdt1KUV"MπN?Ycsp7-0 `bAַ2vF+Rq$noh[EGi}c6$&A @ґߔkzTue MIs>A[!A'߰Co2FI!:N990Pa7MFe[:Ѣ vOnOIM$%x,}e6PQMð'̆] AE 0u9uX-}1Pro]9]9UˁmD~{(xz0b}1k.;U^+3s bycҁ#[(a( 3,;E+fTBP ȕJp)DΰLs}/))56T OV( [w>tV|eT鄶SF迯~?Z ZνMl#ő>j$<J>b9gʆʗK\iGUA-(ϾWG+b3OCDuГkWEzMi<kTU>6|u slA 'WK-s@\HO>J9銀fLJapNM`$wֻW4!krrf'}jN gh~n=QLjA|tO2*%s)|#T7%Tdg9%^~jk;0?W3RA8Ud%f㚾/:_4Q?Po90hoQ˜ )*j5! !p,mhI-r!%IW^G{My6Mi-Dm(0`G1Jmyn^h6~|\K'">lD҂@!1(lzĶjxè dMZLjqp|Pew>$ 7'Yp;ڂb!/1~A RBd; 5l8P]qEq6P_~xnL`11))9QJ UF7D!QfkkzazJy[gW[~Ԃg`\>UmF&zsGĒWҟB߇lY^|ŒԡXHtS/%+N[; @ e崡b.o;`F.x;F N/sc;fw;eL8C^Ъ]7{ Aǻʰ&L6`}amn[X5.Ů"w{H$mIRv9"4T1vޘ,5( {؀J4\tbCKN_x #H?8R`o֥^\V @GZ7 '#<3cv08jԎ)Xw%BiKʀN]2Gv/Slse{B0F.FXyPr)™c_ h$W=ǯ6ºoug tCBTDq+ې*\Fꩁ\@;<$O: M>dt C'/M9]/1hcO ,ypQ}Unټ\V ^iNxXo;n6`rŹ%(X)gȮ%ƼdT]As8 r_{Z hڨYZB3fd UGicB3l?dC~{!MĮ=mn1>ZxFW\e w5F8d C>g/1IWi>?:v ?sSf79vF}vzVnoxeЖW\bg+PYYfw;e1qy~S ΂Yn5YZ%̺SI"iwPi8|x\TF4uRP3bGlBdmBnaޗKDO/P50ͩ+*#ʝ,rtԅtxrVlMO%!t8ѹrߋn]59<+ oij9.Ck?_}Iq|=Aj,4İmX??rxO<6.Oia9?H<˄̭HͼU}2t e>yK 5:ޡ PdRg*U;T]y`ޗHA"ulO?VcY_&\y.eu]rX]"H]ĒsS弍LNo?R%jY!2HrClzQ 5nVv:ψv)RJe n;Pq*"XN]Ck1ؚnQ)nhU) )?0e TWSflG{H16ŭY'y%|Ut(S{|U1\\KI.vh8/.^(BlF?CBmIvP ʶ7W.hՠ>"4q[ݢZ ϡ%823˾Ü oIUL &׏DF .Gn ACW^c&M\W1Sע{?"ľf`Tvr'xhrVhqZ, vs~hWs+%}!%aEo|/jwJ>@0-w5SW :hB*x7S܆!׫Vr.me{JrQIWtb[`V03j EScNYsB.3pb5+X9:. ~dOD)g+O^^HQh7N_QJ ad@m~z6vz'dh<T5v!0M_8'Eygl]~v& K.EfQDSĨ^fvH7tR'jq<Ƴ1Lig GDǐBaN@\C7(i{ )@կ]WeАdm7Ww֯98 +T D*ʞFڏazz`)޸aq=g뇩cMzalcn  ^na OKWjҖ Vpu;|L`i5 Zp:$7sp'(tr+  1ɷ[eq',Pf/gJqK LĤ_"m7Ȳ|uylѓѸZիv6FW:{$Km:;>6 hvix:OEp9w(:zu/V'~ߚRbINUHҿAר(Ýd,iEG q\YELn<؜+Q sbUu8bSn?M 3L1Omqb+f(A;p~t:U7ΆFr5\s#KEhhR^.a=n=H6sf<WBKs)2?|/)epkF#2,.;:sd| 1ġP*;mO:4pIvd0)zFs:u3 rw얆EOA8bF|Xu-)l:PyK(/a8 Bv\|Drcfsv<*%g8v"=j-D+jQJT5ǧ WVo 0P}8ڇxVc_ve"AW 6UbKbym|:ZiAAu#k0E|[DA6YP@ *w+ V}C{wX7\]`=)=K7V)>dq:^kX\I o=i~%*˿;}foF*gݞ\Z$`EZy7Ae`9pD^|UpC C1MP,|fky^jzQfQygߖIcN}PgP ,|跛U@!+`\ ;^;vVQG%YC̺f! Am *jw[(M PgWF5zVZdWftCLdK S"=p(00҃So3K,x,ҹ+Gßqt+{47dgE|^ ft)#2[37IͶ8 ^Į#.\L=BqU+yǶm;e`+tPY&W)ƴ)QE!{vCU(4""b<#7RJ3W]ِ?UYӱ1,YjphS- 5)R?g/b-D?o*ʭK6?%w8ޠ5OUV]|s)U-eZv .TĬ`4 ƂK~{nLGf8b׭S &Pr&kTE. 4UMMJ+8vc97RIz35$P,FLh_3dY8_cA0{sygiK!Ҏ6``t.cL7ƨm5LvY02)+=iVemþ;瑡F;&C@HB!Ga3 d(_PnsƆr)ЅJy%dd 9Q#z%&21*Ga\1SݙncJ+Li;tUDDueJg ИkFsvo~<98 bx915D)[%!X4L>ǝ.r\5C9I:hBU@ Jύ0'pgv#e:]phϬcVfhbWX5HqX;5S@ ɓtWigP}%W6<'xfxk)CGl:G`$}Ee;j-(ܒ%)8IIn߼zeӮU*ίx&vA-BkfTMwU~=~ۂs-¦dƂ+"{ʬmIzap%1cs2ykQqyŔv"qS E "!qL,pU*VK u!KѼTĞ2o9Rh%@ЂO?7$GaZu]YUKڻG ZYz UCD{@0+21ީNfIp2!edX(׃@L?ԗ7zqӗ? BGzJ2iu5r_T)kI`3-0ڼa{?Y:{FܙQ< n Ύcʆ~(CSp;zϨ?EL4O܋z%?C!ԀT=کV;XiDJqg*,_JQG'Qr!o vr_3XOZV1 o&A)TiekQ1rlocw3W;H :i92*n(t&@`eaau' 2{$ϗřosH0Rz.CyjH Xkڊ 1zU_t_<޸7@f@uBsaR\Vbkf.W+~o/}M?Zl该" A/ $Z"K`%bE?1x6$WˠuAN;@dB uX厪\|ŨZK3IZ}hXbNV%Y pZi/a<7pPq-?}@x߇hV~BaCCk, "O,MҮ DC:BbsMdd& ^G6_H8o[#GD5,,SlֽͺR0MEqz3]ߔ?U P8gHx>IL} ;gTYx&<\=í>/vZAS8m 47*&q>/Bum|x,(LbI ?/D2u":s%\^$ ĵ#cv߯ўqh}Jw9FE{,_z;Tߞ%6` _tnHFċd؛"u,+ZODmMmrG+dTW>tHx`4F;x$S(R&f#26tNJ1ZdKk O儳uJp%f]>QȦ0,+"w҆C.,r/;Rq*)'_#0Q>5IDc9_jR7 ɳ@gbT )bhL$?R!#]Q pIQ !栢~N]a+̼(߈2PgT̓ 7km Ėx~;M,$#>,e>jw)%sH96Q7AkŠMCu( 8B\_!< YvԖ,:ཱ0-gzxRނ◓!u(_W#4U7? JFxz+8Ï%$ H5yhBE3F% \ܞZ,o>˃y0;]#qvbYMo2_|!/iH!D81nN##t6mHdd(?I-#.$̐ARddIQ9aq?eNp8,w - SN/Vm&.6s)HVpF/a *n{GcfHm;= zoSgj2@#sJ-\u%wUd׳Orsݧ5ę̱1HWmNN+iR1Z|#v{/܎'Yʗ!c+Lئ,օΆݾe(?(/ l2E}n¡_Z Bsa[l7wO@f ͠8qӼ˪uoqu"eHɥrMm|R#dB"/wմ/f6&8r 1 ƪb3걤sf[mzZT,5?%ZC+e#>nr|-[S7똽GqjK2³'wrXae.k~ZJ:$#;qro'8TiaJNJ`ͩF5`*QMl&U7&>;Yг*}E€܈A2tڡ+gR` "Sf>&au$/90|!Cv8yI+ }ITrc}4 _΋q i%m 1871h`mȃM3h#pj W*_d֍i1E>T+T"ˎԱR5SiU|'VD@m=v8 Xa'+h-Үao{FltBL&Wq ZB7ձ!Aq q2M:)-49 :ث4۳[~CvB@;M\JK<a{ =yEޣ'W I\P0Xɼn)Op%),8bJ$&t!&ﮥ@AB=MhdAQG';f(a_>+H qJ$syDM;ig(#;z2ĔW&(J SY"Eٟl%~a¾L6p׺4kro- #~kt@/0..tfbJC c%S:16cz"}>DMvdc,x,1R~J Ϻ'dj #&|lYA)J7751`!dpH/I޷|>p~Ea?"*}fsۨ$S  B'pD!!5=8Z>ճ٧VΓq'b:iHcհ &la\hӦ%mQiH~qޫPG'("3 &]E?odCJJ)QoiL'UEQdUk۶ 7D7j"jl9 '3ۮ^m&\A'vG5qvh4@'zz3_j^qJ~@5v9j K"ƏU5 R,u>#M8#7^4a/pɭb=>EQ E-O`<.`e@_mퟪFa 6pJhr(/ u\\ fho@Pً (+gdsz[7093.2x_^l 16(MxCPLõsۢV, \[EZsME/O5ntoO^;̡w=Ba5)|]bx3,OF""m8D=4H7bE35RIclWu͜L(q̗i,^I$V [ ڑ2].a->`7HϋzEn'-[Ԗ^|Anu rzOQ|Ne)@hz0ëDzd][#m‹! ~Ѫh?wv̓֯oW̽0 a0^ =6@<<)sQ/ijȸ}|T$bDI-oh$əx{ܽF^[˄6[h-OzQvVZDa{6lS~%M^锘Ug+]!o|8qlq xR9$ңz}(G`vd!g*'* P\fi Htٲps"_S<|͔BW=|V}}n⺯ʠMDm?6$߱+'=so$ōH[iq&HNX V yQ23I$p ec3"N{w`n S2g q;8OP.=J n9AB2&-ָG$ r#4fɲ"&80n͔"L&ŀqz!M$c~yUTnaj)\^rŖ魁N"gc o*MgXIz˻n~:oH~iqdOJؠ&}7oJXN9bάz)0{Ĩlґ8 JSha0+9Ź^+M\뤗 x)4:PX=@h=O_rM娥*MoR^J =R=;&- bp;{ zSEO(X8'wdUo9Yj?sν޴MTF 2"b퍴=8  q|GY<@Ϥur@Z{^Z#6wq+;=0;~?s R\Pl3V%LtYEz:=f P+F6i(y/6(XOuV/^CE k8BŴL?3: 5໸Zm}-ݪ` t&ңexT N+Nyf3 d>Jg ^\2 9msl"(o*} T}K,W*5*9V  %#0˘+Ig@1+Ubz W x (9yojE(꘥mejMCVYn@~ tF[Xp" ΕY@0@s4nS 쓂2*'8eO@2pv͕><@p=>d;iR7QFl36&g@UkiK#OKN?1{B7 03ISLiD Sj < "#F|ndYvOy\¯ڷF?b?iz1oS=w`/e׷< 4@;֪Hώ,mˆ[jXvӡSI>Cdm}Ý#|aBo`ZeI{ 7Z,Z,z%h@ 俌P@'sA$q!ݸ]vgO`" /E=^G~B &)PNlhhT"M2dg-bg7waXɚJAU O.wCs$d`Q̓x5,7A B}qEh@¿XpG[DHaDFwؽ0DFE܉OF%GdڥnPIKD}Ɗ 6 P}nfn`3ö#%Ÿ^h 7F"! ѡlҽg? I@*j"Zy! c?{ރ:FM1:;qS%18!bPn²]E>YNh+?v'9O`?_J{NNoAY4ΞldgغU+9[ :􅆚1j};b hg%6'FX c]Zߑ]d' GXw-_zz[z_sE~~/)x{qe~sɍJ-$ h _fL q99>BP!Jy'w`/ Sӝ/(P:7\n{^S1\;]6c P޸6V+XG봊2\KH_jyv]+O5iyՠCZPyc.[QC"D$TiY6Khn4:=덫&M2NS0b&(5@s6>%?k/3aÈŜRicaf,*d E`[=~T {mrTcP?YXAם9Zh464M#퇰# 8 i͇e6XQ(וiԙ@Gjk7EB9cxcT,V,w9 ʬ"-9e]UW| iߤc (Q;/:i5nnctg'wɬv"}Pl`" My܍*|CϞ W!`XhG8 ^qe&|I`d"xґrb^6?W/߯-aP =ex̛f:om [ at/uptx“q0`Y<2Qx&D=/Q㠼]$ Y˥<\Xnq.7tk#؉sEXH]XJ9 y&䋪0oxGwymR= }1޹w^oZXL9־$YءӌZaytdq ʬ)]]vvHzZK3~~gPDńrTɖa }/WٶoIl0|"_\.ej, 9?\ES P!(BnLՈ -@~o~?+>w*6ʂg"*|zxʒҡ[ wT3nvU?Mc}qJt$[Eye@cm)<|Tx2nlz؄xP}Q[/Q{i }8@,ݰ/]~J( w L-s 9sQKdufH 3b3tԣ !=O`teKF$DgvP=^ n2#\\2leJ Gqu oD%`rN;֟;r-JGOb~À0C߼SWeie8I^n9V#=;"ZSx`1Ǽ9jU}^֮z G] .·qyJ8#g8 3>pAm.<퓁>tCb3bk+I-`g*sG˥;壴4*uOLZjNh?m}kܜ( .}ؓ.qdlJ$^(o|"wcJVݬegե|>k!SFy$r@=?L=4"~JEm\)bS5XI ӏ9ѷYKށǵ|)z n, X[~8u+?&lJq.sRO#/c`&0j}^wEW&R{{T/5=Uk\oopw!Q,BdG?F&( /gvd0OmLq-4%6k \d>K3F@N(1>:MכhS϶L^\T# h1ڈč[|zov؃:bnZ*)yA$$>#GH֐t:~laSmj AdD\"z_ڪaPM! f"-Bʩgr%t zO:Qѻ@-)gf{GgEۊ-Pacӆ˪o0F|nj[zmhȗS2zİ}8 oCa\ )kD)*X+^u ZpBvb#[ϕ1ٍU௭}|}gZ%u- ۧ":z]RB5ĴA *L 1t;> lK+B~2 J$`f)|>Q`dÑK3ǔp&%# CHۗ|xe&tzI_Ko6B 2BɌ'TȆa;-N@C aߞ-̥?pϱR?~da15!aU[Io=a{gnY6B0Tj#mO-=9vĔD6sYUOB;é4!& #cUc[/mSCj>VQ +Xas \)gcOFG(ml:.ȹ7]%,& !M%v9ězjDHs,ek)#uGM, ۻf: 4=̝ÿy%6h,VUZk'6p1:nPFgdGz";2!ᅾ2ky2D?XEVy6 =l5jqWGs tѠs ձ l$𡣫E=g^&PkÂO~_uȾ_xCm;ď:ԍyV]Iܷ}pL7A` 44t-{&p7JiAT/bzKc"8SVI.ksaϿ"$=j=䝶y!F`Óp#L/5N8h=1fW>/z|hꠕ܁C]D. rןc|&X1!!CؙǫyZf4`:~//FDr`\w ݛ5ao4=vV"KXP5Xn b@!j5/ sPX n< Zz6 Inb;apGsun-׈4Xo!a08A  $َכK9^\waYc0vbHO3P)]yrʹ-a~ތ<l!Ql4sNxݠɼ?'ض<rc-Chqj1u8RM֞Rk 5V1sP9UZEkoع^MuF(gHc=]9,<ԭْtݔ0v ypґ].kMum_N\I&Hs/DlCx;D]ѝ\&H?mW s ɠ`mwRqvx_hr"H5H} q%GN:?7PYH"0r 8p{_INb:D auDH#eN0+XqdRdI ..&=`Dr.GPC;Od0 ]豻To3+\N40'"sot0Q }vS yxTcV\.:~-e 8d2͸ѩjሂY" ZrxՐx]j~B_-xy3ȶM3UƏ-K\E"$}>! u+9WApISEJ$DT^v|kdOH\ SASEZĜΏ!Pgh (ŵtg_){@-|9$ewEoWDRoՇEbgQQxx~ TVl&P{+T*̓PT g|ke,T粘YY5[ }*,}\C?鳵.Z>0qZWac΀׫ 'uIN 0ah&'%'DkFj݁}hie9{ގTc)AȕX ɤ]TmT:Zv[[ǿs򂠱d زk=& o(m+M7Ɏ>#1yzQB%'rOX,6宬'{Kpx Fc{f7 <]qakd$#7Τ>ޱ`[:ga)CF[HN67_d[MFAkQvK9ن6窞\:K\NH${ʵ8/29 A+m {a1_S(<D(k l!pCU~|ɿ.jq\`+zѥsWkN $15&WO Dd<BN8IC *-aJZPrGUgZ!BRIQYpʇL\[_Rڱ6?XҒ302txMLHvut<]^0a`Ֆ\`gL+yPkZlnL;f%7q:o M)rXQ!lP⢑0Z*Px8}?ڗPBqk?] bM-ϾƇ &? ,)jy:Pv;Fb_Q)lTu"5 ]+Kl^"cw\<u1@Ƨj1 a Ji5QC%M*'VP%޵ĶbP;ZGSSlV]RL1+N̴/:$A$kF[;0Lm_NjX"Ka:v?@A8x 0EťZ^Wڢ~305Eڴ}0EMIXF,rqd!Ywp~j DYlnY4iU<ԊU7XD;>$Lhn+N%wDbYI*Ku=/t- VN $r\Tyr0Gщ: 9cP qIz@y -2鑘w=,jy":|G3 b<,J}2ќZ<9E2q=JR:wqoX⡨C$Ne!A N[PsCǏ>Z7sI4֦:n^Ac Iݴ|X$$ XfZϬ%P˕jPMMdWP8`g[U%i_jWD!R3"eGgv,'&NN@ HPU&T3-= A3PeLمGbҥRGtǫ rFqwՐ4gFǧ >_fD%(зܤjlzg;~1 Xk{@e}ї0LNΦ1(KĥEu0@ K8(`.FпY[;l }AKpo'#dDl'z9 4dyVzlÑk嗳l^Xc;: _YPyd>+/g%DͫUd |z/&wJa6(אPsUjF:*dYurHlrUG_׌ƟsK?;hi9Tc5& 0mt;A+6Oa܅yB&Wf*X@4y,FT6}|qRi\R!N܁6ONJAZ0rufD?}w;W!bJhkuA S?v9?^NZUs'5=PG6JLFQEg#nOrL;R:Ri9/6yc8fHڻIYTVnU$.u9rd0*S cQWxTdH[ [ϙ{&2>*dH$n K6U(M/]Z b)$;OdLiTpڑtO4TNJB*ڵk YvC{]ԟ{4Z F]{ͱ3dEF"n-{"B71]'"|tӯbReo}8c*()%CvH>\RP;8~.zys m/ͼR':s8Jqm2<҈'\ GRoAmc@wiJbZT[̝ A^CeRڥcDg¥SpGט,LZU+ͦ`3Gm7p w&о-/ P9FADgI@+ * 0'5>$J܄JrfgĴ n(\`B^[Pr#y*6Z1sM})S" |U9BAMhu)j]7M4yG83%̨P6h(a{q1RKlmy-~tc` EP PjGvŀ G-૰z pWsrNZF_ 6r=m|{y潙SΫK uuONAS3gE.^ܧȠnjUELJ<Lp,'?qOK\,:Fsc.q|r/Tk+gUc}l׋iT*bѢ2N)aNlaO 9d *aklt1׹GjZLYT:^$s$Zb%1nh4-TEBrU"EJ~s,dE .fc]Gzʏk擟-]cRdqas3 3\w`|}=SW׺%.Y$KCv%0KyGd_ Ў>&#MD+hʗsOC.KK5Qn0ԏy^XcUiK9u ?V/1R+ +/Дkn9Poz 4'"6` ߷ ;[B,uʆcO5sglh׳.NZ50΢`gEHa|n|hXH}R0)ix=Xkw #&Y١oo+WvR. t jGBZKPY0". (á` O~$'mժ*~c5nl5$g6Lr¶BRɅ!B]qJH#kӧsx3T1 >p Uci]sg1/zS:??80l]k/\u|%?`s`WODy=ꎦ0&as7X]c(@#tŴf xVs@r[L\aWCɞrScq^>r,{Oj-Q6a_I¤Ô w:cM`ӗdMUDO3irjnt[LOCWZ]D))uǭ)WQT2_?-kLl&#g# y@q-)+R~JPp:$4h@? *Q8;3#e Q7,?~/. תU#P)IأZ~ܞ҉Myz:m۲:D{7nJj_h%*-xjV%cU 7A29Je' 5G'3H(X0YA*)ҝ^hĭŸiZ)hpuD}s ," mZu7hO4'm|fBxAz#AǹuM=?~cOtZ]o@?ljo06cDi&Gfyv+׿ڎו>9(qJ_X7ypzO -=ɀPSd%s@(ZT1Z-MNz$lmEf!_8?,ˣ럄;g5 v`HCJxOQ' =9\3şK ږyREDNoeX{GCmȥeP]#Sv~.S9d_:k2OK|2y *=ͪ$f>f%3|s:B:,vb4r髖  Q*} cpv(օ5I\f  Ǵԟ.b0Kel5,\]" 5aURȱ]Fﮤ)'>h/MYɎ&G*?\R >ԊR;חRSJ[l=OooKpRj/_ 72,,SxϹ_|on!M?d(AaRPoLjtV}-z},߈q+LR(Dȯ[K% AL߻ǹ7?‚_-ܑq'ߪ )R"{Dm-1Kkh}KKEn#`rKUϠ :kUIaE>`vс1 rRft ғ29:Kv sG'Yh,+m~m u Cmdw"t;x^ | د2#A\i)esԮ: U!!֖1cb3CׯJ[!eU9Mr3w 0e_$ԣiX u3/=;qkN@DУUym,b!xC}1cij/ISUoC . >[Y{XK̆i* Wfy[8BL![G4zl@*L*guǵ댙) \fA?LK{*`IB$%xdvbOA"\x8?*Z*aƖZ`ٴ'->A+m@Ā.i᝹f+v@,Yߓ4ٔq-3v:vŵ'`o$%Ftqu'/NuUD:= σd˩ 'zzZsI_qVJ[28  Bd < eT,1UlM-1z j(k"irWH]s Fjd 1bf:$RgjB"*W.UZd=wU& LD /\_su-L7p}* qIw"91FD3EtD4{;]ˑjgzOjt _/Gذ %`4,:HyCKgp}5.uL]/LѣֈB,7kXf!C9rWK ^=384iA&s5-Yblw !\>.oՂ/[=<*(#1fme*kDT& &}@| xC078HoX4O\)56/t8R:m&PsU2L]i4l\%iS֌F wbYo!X`RW *KoWr`d)QGҤb% ;K_82ذY\bLTvEGIl'b,|4޷  i5>TqGwEQň†{SKG 8 <|(^wț8/r U3#1 H>>ޱ0+"AL_{FFaBR-v CW~3,a[{ȵ׻ACh?}Tg(i"L<?U <[ $23uSbȡ˷zcRAd3G~~'`  =>Xn+W5=l z9c} -h0~D'J}6?O4]s`,و0ˠ?:? S+Y$C>`T S|+usq"&a+0ym c~,MHdyQ4~Dix_EG((Uu…Q%e(EXL|lG]z^8Cm7z'-v꬐F>%^,bHI SS k'ͿEX#%ϗ]AOTg`45Q_(Tz&EMꯌE'$Z). bxMTA7F7 GXzI&fu$*Ͻ$b(f~2 e1(G;P V怴3LWoJ##[_sUyeI^9#_8lt1jsRl,ôf+ɚCɣ-a@P4+k> l몀t#D{p5(REf<( ;UuC`59dt3uz{{ǷH$/-$ E19N3 PM!atP@MojvqܪqglNJaq dyE]Ϻ g,x7?o$x@g<3|c|Tzmf3Wȩ Rn{jkŀ/ .-)嚙n0^ƥ8uw"JflGv{@ENR:9r7("Y>Wq%IȧFּA$OYHɹj$B>4Z zhj6=v +| 6 Tw8D_K *嚀~X.v{̬ޡCT l8tpzMUڿCjl_nHB! LBͱl+h3E guJsw99䦥YMUEN"rwJ~w(@l3z%nD+k<4 Es4Jôw7c7Lp/#/hPlW'o ü #3BV]Y^F'uXr ͛Y;a^0Cc0-KTuY]Tn;:Ɍ' ZD4֚1MmiL˝\ [Ѫ[ZtnEst]sLP%ԝ6JO~Nqbz:jsY5}h/YȽ_GD}BWFmת}H8g#z^OvAlX])@#~ Њ5Гx:@¦¾p~ /z.{1$@//SGT>*?"9RU!| ThxȹiUT3uسݷux]K]b|-Bg֩_%m\CPCcNPY FNnybE{o)|%eeAD,a  {`t.4^dᓸpDk!CzO ث6y޷Ѡ7?/F+oPѰ5_P ]IA%c"T0zg.;;WUR>!\K 93 2~:*eG0֗¼:VE{#l_9z_P"W g-vFθk3з#CރMK${ȵ'9Y߁xvu4 ik!EsRVNUd)[54{:bQ;|b+AfL u~+~2~AcX !1K;|{O/N9 usXF5`r: _ >Jb1lO!@ h8&ݜ/+]b~Q3k#-b?L,.%dO+`Fޜ/Za^I4Tp@,1T'5X <"?cH22T BHMlHA[?_c4@͵hyC#}TJ婤,%ߎhHd&eyd,Y;MG̼v~WC빱˕nCNjQ}=hݐleCf)h_GSGHD:84x?1pQR~AhhyD"/( &8U/K#Qh%QA~As]{|  #FFԑ)vExuHխ ı6SݖBMC뎎/tUܡ [x1@6es Qk(\&>?puj҃D@k <04N5ٷlN|=gpvP2P7˭myѵ2%|H浟 q*x¬H,Ҳ>ѯfbcәP8?0 ILsؠwu/@织rӂԲJTr]]{3e8P|PGyGbΤ~Ex6Ki3GEg>8 ҕtv*|wB&O#^C'~=Kx*<[?ܥ2N0r+zgo{0tB?^v\ℯqAڢ@mz;sP?b.;}J|Jǰ. &ΰd#t2ɷkc ./7!IvS5f#bTBV53:Di䑢2MCRlM1kl6눥cV#ۄWg*/|t8-uL~{Hf[ n鶰FT)߆S0]7ooKRrD|;s_o?F/hъ_@Hs+ʣ [7ꮿYa߫6As5 ?ΔΔC,X gp+Kvmn#5]X3%w[! d블ͷȶZqC*%b5zFsC'SQi&vӕJ0\n575U_## s&9=%<%Uj.5.8y^?˜lUmsӭ= rVAжm۝i?6k/?x6snoBXD򒿂{7E77${)U ?Uq:nnza sY_b0'W8ζ`+T]|05M tkQl'8/#<VE!KQoCY}cO8|XIfnGX2Ee,ZՓe P'q8 ,+1`ΩmيdEf*nwE\YnƥB, Nb5`]f瞹{vV>5z$ȏ*{@UTMLGsvnt._30C"H@r#a\K8$ibzp]DCtJ6_&(jTRM ]PާYK%n1R0wl\<+NʕHV|/BEJA.h;me(3;秝g4r}b-rR$ymK<'\i9ك=gj{Bt rcĝ(K ҧrP@\E*2cT0;x=58 ڞU2K:F iJH5,bތ0f> ğ-by Dv4!R`5d\97 ;pEGM솂Ve#3d)D]m4ڶ\[%h/DMg/SvPQO\x.L? _6SJ5DcσѸVWj6Cb;i *d_K| Tǝ [TAđ/W \pV[ZS>S\l)߂^f'v{nfߐѝw3NdBV36%rgE ~b(7k91u_Z4Y^rmw9.h(} 6<xQ!= BLZ!7 OBeҔ-jkSbSbB#Cw;[j9*n60c>"/V?c9{A]EM3EI{cU!*].8wNtT ib_5 *A3R'VG4$ C?o'1b[Àq d+C .ߴWȏr z6< %0'-3?wPPRH5?p\@ު2B@QPKS%W帱ե5F6u6$ɰֵ8;@bp.px0m2֑d/].i) {3ڌP׋YH-NXA ظav8wsP#M6[SOzJ=[60`Aw5Ea=_B~8ұ5rK"ٹg2Jj'HEpn`'̍]YtDbd{[Uxnhe4Vr4kUᜲ`O2;3EO*~QYZ>V3KEHۑe=o3JLz}3a׊KnvsTnMҽ[jl _<39sciRwK~l}i)tIBVz/&"/u;Qa±E+By֦:\viH@~OOd='$?-=&ɘz<_iܝlp86Fhyοͼs=6'DإeOIS2E2ܪkQYDOCv$kwl0q1 rs&&fnk:v/&Xozم3r;Apzd:R<FkCp!uC M8~Lo{71Q|ϫЪ"bA­0g\ kMqi@ d-]tӀ/2+x} g7CëfX`~+b'eja';J!枧r;9=D0da HvgkK[4$cQZˬ\G4O;ݘ Rrg+jJ#M'N;>"afw8KlԱ!f.ε>1& @IB:A_\AJ6ϩi1({::CNZpsG@N]h]3OSom~94TNJeAZ M2r!{XZﳺqD3giHIEO ҏ2IO͓1BY;d)pLD407ȒW\#`t{,Ge?qBE LT+-K&YFw4Ft1*C# bDz+ &FW" ;1[ M:`u,W/Ⱥe;f1#0zBk2Q z w9Ӌ3C6P,_#4u\:8Erà 1eFw /nr:5U%iT8K0/=' f{gx12'xx(13Q XKm BH>XoON i*P쳉f$1Y!:^6ёQI%;.FɾCFc{iX,,ך~\L &sz릇oMs/1(_mfBȸT!_0o@ 9DkT]_jMu~5~n417z|&Bb~x;3;qK~ov߸ox H 3#2D;[ Cb魪MBh]dR$i.0%G~KrVOh*To*cYI&TvrJn$pԺ'ɦtLJMtPSTۣŽjV3vm d-(+=#̘D.|Gtuw4Ǫ]ӭyHN$+ˋ} H _7%ͷU\9vy+qybvL`~+mW둥MP'OdEؼkD&ժܒ᚝y $?idQX5=:Wv云Jc>/k:Gfnyi3BfO C2%@QsOQdؙۜ9 Bf!8qP)A;H:gzon + yxc=! R͝T J-IٚhhGH*C&b@ b_7z-3/@:,1&0bHrb)mh1 Pt>Yl$Opbp+=B|$ 5@8v/X_! XɁDuF2)" 'b<%B)&'{uwP`UnqG9F?ZÉmq`3K(7|fGu%Xw&qUzQM'*̕.Mp/Fm~Q>W!2Wȷd[MIL'̤)c 0(I1#˲*o R+`P͢Ɓ[uW{E YH<">2VܣA{\玬R9<[lL(ջ3;,\C8j DQ;|R w\5m ֞;6q`V~}dF[oqgɉJ fw&;ҏ?f /k\d^45HQ5S.PW?R>} CEwJi,SNh +Scb&ZZN r <3Y_J(~v%1_y#;o[6A Ddu#c&k#~T0zcͫyazTKt+?N*D+eEύ$Nx2B\^rYna):N4?֜é-5xuNy"?ik'z> ᠉Hd ONEqg4 ]8~' 䔧H#r tOMNLyL Q^~_apɊQ_;q rvzjn4mRYk)S-qVqP!+ᴄ 8jb T4K62Cg>^<]PTKty" H& coq">ixKDq.1EQHݠ_"w`umUK, Y>W2 A[߿o84k!jz$Kz!@TKUL gڟdޣ؜_76h& {`ǿ.Ϯ)k]"L1<ل'Y`sfLOyvr2h|Kĩ7";ZbcVsnV0GS: k<^W…Ibq[`~aFھ"'&0bYyLF}wLa 쒦'R/uQ/=7>qm_ov/\> }XᎃD>N:%'dk >&goֺ_ߴF7~*.z? 2i Z"[\{h=Nm8(mu^sYbJkV Rq{A TPC/n} sA޺4oi sд/r xCDphskO*{ C2ZFTqFY A$r]ŕbo/47dxvG ݸo8>5$=U<<,K+?iFS$tjŸDGgk̥_q[l=1ao٪k c#sXO>*1(U o-]9"L%Moxjia6֯H- )_% 55m*8R0BԤg&;v(lӌ7mh$o5:^c<2fpLc,?ܚZ;֠㲘M.'?B^%?0rt)cÕ߻oѺa,̨ .(1nB=+pAy~31DŽ2/QKG=ɨN.F#yIs #MEigz,;mݮ B@,O0hl.u1ORTJ L%qff.yBVU.O吁lžAKo)Ƶw}q}r Oj_JA.̕Y?եՆQݷ%4HvIbX~ 5kA |ɾ\Z&l XT9u/~e>Q 1"@4i]~V+~FY!!]*-{p  uy۾6,m&_Tu%G?{#)"*!b+x˟d0 $;)>7zflGͮ] c4qtΒ\̶; cJUeņmj_zrFXQ9!18x/L$I!|;`SG-(3!AfQp,A4Bej)l=KR*0yVݺ^4KRhIi!L%Py:ՠxK0k3x;eHCx$:Bv踦mi~9­HQ<@Y]]T//`_?i W7JNb9 +E\दT'#p80UYQ+ 3%0_ނ]|g}6SOIDcu|Bs` *2ɄȤU5(m!eaRu;pxiSi"z~z1^1a@ .tJm NeA*c.rTP?]?ɔqpy`?]?y^6ܼ;фicX* yγc|KAnA;PY; C.k,ơQH,p(B?> F*h$ma6KyCZJB2հrWq /x6"sƜ޼'rzp'8LD{ojUW|f.$V cd\LsqwApb6|Pq.WRfGmC!^VJH.;)Z/f!5މsH@#"!I3`eFB6%%Y]<~&4]r;*?#څv%w҆YXtK2 'HAp)!b@A$m4JlJX/Sclq=Jfő>nq7nSK M I V a;P¸B8PL^bMr`ϱÐ G2 7 .QVFfTأXB η<<H86;\&W~`b;kޱɫ;ygmζW1g#U. \&z$g) zDAT^ne{C! [t}F`;xi2[B%ͽd JZ{'X*AheT`j.r+эmաIF@/}i;ܕ\ t)IhDJD3ox9qYN}s/PwWܱQ84vs[PiR:ꐀn1u/ט%> K^շJZ֯17/GN R_Hr bP~)1hi8"k[o[wHN>HHiHa=19+%_4L)P:W *'o?F1'b4[,Ƿ.T4A`.{1Utj\-9hDoC5^8X\7/0vLL ?8񩝹1efUJ =t "U:O3 F&sb&Q{K=]w?⊚ɚ!Dc(=>'" @.Sg)l ] nB"@ ]Mh9(>6uaoWʖ=w0GS8+7 ̲ 8V12h{F,b'RD~fgq8Xw ceRv5@]<o \V z0V~m0)W~-,Mt Ő|oO?N{_|XiP|BHߗcV qM1+u=]+'/ԕlNߥ>jYIh2:z yh$(Hkd,>bTav2Kڂe*%ٖʯl/%U !v&xAKp&8O;JoxI}7NX śPvZ$#9+͔ 3_;AK#8t#Wy,jiTANKRWZ*X(1mX)(  3{lOəI!{M1й +&YMU;0&/\Tuد2jwl.gjy^+1t[B_ڐ0*PϺ No\KwI.V,(1p|: .9:%0}]R=ثJAOv1Jx(I%C[rfN=fpt J/,;ɕ8 ئ?<kR$CHI3f/r7B0S?`洌dUjV1[AxP~)3:ŽzL3Ά%7v;B09(A`N29 sU #XE,fj'Ʋ#;Y"duv g˗E=}.ąc ]qI/.ůIa%<~V:jr)rNs&ܩҖ;|ȹD :9EhIUҭ,r`5q#!Ϣ@򀶬r)doZʰIˠ:u@˚{7U+xnlo6)|EK-s+9l!{=/t%)ygMppNpiˏ>S9a@N"al+kL@( ŋ1cF.S5:79דmAIep)Rb7bݤ'⫊WՕ?ذTw>ɝX&/U {"6=u\-:yu=p:0nSs1nsc{@O !p&?tnsPOm8CEF7g٥ߐ|@}ljKL+yyJJ$]aɱJ$XV2K܆d]c9ėܬFy!GF?U`^LFhID{Lo -k*X[Jn9d1XGs5қ71>Faޓ$)I3 j\՗qȃ7E@I+_P|*IAB#څ#׷m[ֺJ]rl6"0د\Ԩ{5/!YRy TXivg=T&VVO{Ͼ1EI-^%Es_a(a*b3~9OS}ߤ#H,X#~~IGM|w5Y6`O;ѯ ָu]BcK?7^qG}^~foc{_1ie?4K+(\"⥐*f}싍B.,lo5)xg*D9Aua#\|;!@r2U5r?˷֗JI\ 2¦ zԨy0MSf"Ins@w07sMNߩ4,}q6PbҲa i^irB:A!? muoiCS>iX4y} k!0jim69,~6Eˆh{Ԯ|䅪Ogݶ`P'lQ QFӉpAeME`g\?Z(tӧi틊Ms@ csE+@t'hcQ~;D) bO6ww~:lϮ?\~]Bs[DF0Z9pI+'|?uL\%4zFAIA8HdY&rBn5~IsWP|E{}}dP CV܏wiq Ap%Pn45{`%~Ũ|h<.qJψ#`rJg+K $cwʆuԢbCJ5#QVIO* :brĀ]Iq *ACNxRo FK?"XC*h#jt~̅Cg]7y1bh]÷Лoo"Zc^b]m@ᅘlLAgi_mjs19#o|9NBX'Sw`i/ݸyMΦD4Tj$Up"M&ro6Kl S~c}+;R0zvYƽ!mŔGUTkaGvފ QTLt~1 G$MmB7YGH*/7'1gv J.FLڇpig,3no~/W+w*1-/N# ߿jYRPl-= Z+Hac ~eDU+ \ n (#5M0Wo7[C?े!jX#D5GۻC;c&Ht# pCr66\1h#~0K6op[ 0@%H~$4~TibrSg\?3G Z`?J)l+ 3j{rJtþxf^jbygA?JTøѱǣR4 yWQj.E=4Ә,W6H_9Pg;:s!Eg*]C.Vy6oY,AF:%J){!tTt|[} 76 <S2d*kSWsJ_۸Ib( _Z4עv's:YF2,ϐ%VOfA? H-lKn*2dXiT;䋠{8bRȭ.`^5\({X̸hh(Gʭ=_eWpq4r"Ba!Un,["Mt=k̢Kacu7 bW4bX|pH[M+fFZ[% zn~98r&l_ο&#tNFce*i=*˻45>bIE 1g6wvKDPnUIo͟N?٥ݘPJi څ9c'wukx$^ *Ƿ[FJJtsڔI]\@rଌ# f̴7}݀DEܼ`,z5^03tJW88hQ_} &bO`)1hz YhoM*D;2 qWU W 7IrBKP1y& $.TpmSg55:`W,I#.A[J3"˸Y*Jl+Kсl^^x]B` I*! O 9hU#9Wl.-U kkBnR\H(#)yJJ}s8O:z@rFA%K-V@G&nEؖdU730e;?oERTM/D_vN#4i n{ܤ{#>.0 C=&3njdUi_$oENOO 1 lbr5@JMQHA hٛk9  ˅t4}ӝ' Ber]#i. 2bUw zr/5AÍ&rVZ=uGEj t?+>j+*,ڃ`3Bo<=2?t({v 6D:k\x D_rjK5ݕ!o' .JAаq Ù vGBh!T pԨIMɀ;AC=EËZ€=ݶLWމQɁz >{u5MS2h"%3}B1*Ls%575W V4#-LEkc!x_4UMSb``7w7Q>cό"_s-B@esw4:@cʣ>8]}H۬FrƉ媙fAUQ#m5|f_|Ί]y vjH,0ֱ@CƖ55hTh E:pC\/="+et';W6uu1>Umꏢ،qҔ&5e2ߟؼo1M%f}v% s!6N@I[[f+ [g~jZb/@RC8\?ʮ0XЀϜ %z/w5-\/KPPM:VW6>NQωݜr|ZEuoPJ)H?6?𶣯0HPT5gObّo-lx-gOZ#IϣGWKN[,Di$D҆%҄(j@scm `I s;r4ơ苷NopyUyG3%DMx2X `-~"ϊqfb;౪Lv4U_)WlMJmiû& 8g:bՖ[u MMu P`δE7Aq^+QR1Bu-P %MR KhI7 u~<-b;Z ~(Vna2z<kZ*/WBGy)6 (XEq*^Ž R'v(ds'I*r {[%qɊ9G($gX? x.f+ʜguB*Ì#'`rmI6  _p+IpYSArM2-8ۘ'>70f$0M2޾>K]کj}==T4Α,窭mBzQwut:0!d#i^}U ,=4Z4*e^{rԝƌߐ -:b*ٴ=J9@a)L&C2 Yy[\KsN;>nc֦!Z-D|e9v$ `HxJkd-'X"7EnRl~|n񉑛/r}QtUBY@1BEPU՟ Fd(2`putO9gwg8\r ϟ++5Ҙ]_^zLM_֨X -D:ǭԬJ2 0Xc*|0ɟws48 /Ҩ'iKؚ Jmn2w=fjufX kS{COzʼ+Q"^U2&mTj3]U)k CT>R`oh';lAˏ4Δ)ғfJF>Tdy~?]o(މRzߣg ZwZyJxO5c룵A#騖<`?Z,[G6%W$&]Y|g o x[C[;sh~jΔRѸ}A N;lGv9]S.w%,^Rs"ǯFNK.ԦK3د֠TȂ/^դ\ Rȵ)цbp3qtMVLJIصn#35h[cŒXJ0e^D$'}rKq gG!Ym"mޓ {%7?oJZT Vt9[gs6v؈2p)$h3CRv/ia@LY I^0srJi3ש;_4#ZǗHh1i Y z0iBcYe8yJ weL&S4\%^HC/ߪPO-F[Hj &f܈lϣEϭgז*o;1U"f d I O!1>CPњzr/e00m+!Jݝ Dk=PA0! ^Sa9 x^qYH 8I׽DEsOthvpGkIm=ٿ˒4b! #Ga;1Sb_r<v] =$e̍q36H?z@QٖRKC7} S=R+A0HemZvCb{)Q{/+^P5|j=[(8= !^+OPwQ׽S_@Pn\Q~ꢝ&3]hvP(=?{ q]ґ_> qf.'!^QnēFf³t.YvZog O"؈$pJSp^f\0=fɐ1U >Q-Un1G͈.`X8>Je*!mJ k>Pw)Nhr^ˤ+bֻQvԔ;0!gW _v̧#Pi:73DlÎs9a}WFPm֞-݊$mvH$?Z?<XJ;ၴ;tg|=f9>WHqrw$됌x11L,M{j^YqR@(ꎤ.aݱ3A<3;jҠKp`i8έh2>>t)ޭ)w`O_4a-VH|ll+ގ lFQ˷]ё$ԛZO"W 3I(b8y~[UlLfmZ>t5~`sЌNCi: @%{{P`CUB\(^d?oyM}oԞ_l@-`f=0Ui$fJ#Aڻ鬘4K$bې*LWkaP"`M)($cv=_܉aKEЗ.zEŷp_ 4_s)hKk;C#XVԭbM"X|+ l Zc`Ye@^yff`$e+ÚiQC4 }j2Ġ@@Wl3&PK$kщ5牟S"MכBQJc8,x”Wy\ilGij*Fhogztf6ꁉ!jM0àȎW {dwO H}ZF>Hy#$Aar&ZCĽɍ[ xh) v?KjbK_A3u{WJ 6cH#H4;M+x`@vP΁qj@)^88,˺z zO,KǾoU/xX4.w$ħwAƉG=Z%IO`Ep@>^=,NMAکYZ_h 0P@֧s}kG/v+v"0QhkɸW~|?BJR豜Uri0Ў\RSg2Ü"j<C:G[~(d)?ȰKm Ęӝ| Au~yˬLk4hlI" uI;m\ԙ3`~yL慊Qʋh-F 1ЋqG|U]g/NoTn+>?&ia4Ŏ,Q~Z z?Q*(;{r8,vU60`'Ms0Hof`mPQAY4''x?tX02f ZEMpvo*Jhn_ϖ]׆pY@?P"01&BF ?r٩e1h6w6J`%D | ڸu#yړjzZ("M->yWMiB,:իg霏Zkj!?*#8%;Gu3 &w o%bcrFW"ݍDx;j:՝s0BC^~3GC~Lsa )xF8 p<C3%` BS8x*A4ڹ7>]mp ߁OI^@jƪw@!$:q9 xmb'j\P}$.r/EüYڄ8ɚj=CNڧ/Q&hG3|< hcϣ=]N©؈ؚ.=Ar%b-pj*6BBFj:Li,u̡ir鎌vqe H:e 3]Ca|n~Uv9njc#~H \|2 3 ;QX2VN_$JQe6%s.Q,O&H[AtD$[ Y'D1(uÇ-*t6W>Uޘ/_~47,Wx *䄈 s{ؘi`/W(uMn,'91 Eܺ 9T'}߻E±͞$}ila#Ɍq5#1ސ3:, N;:111D-*^!7Ns\`8؜ό?B0 ]"̳C77ӣYn 6Ho dg! 31p`rzGr?[4:t=l%˚'D ˧7@w7@AM\ /} wzy}Q)9A%~m%qg|\>kRkxvՎT s,ͰE`2%١ 7_uX;O< Bn ME&Z]rndKp9/eȎY,`^}k#?*tKq1^[V]*\~@y%ørxw"vQ&^SڪIe vh~XчlrK6!׭ hd-Pcc&NH pN]lޒJǀYdb.Or@ J0|- 5IA.zۏ_d Oa C,@r3+Ɯ[8yc R(|6䑄J[tbyK˭bS)"uFZt?}4cua4s{x1NXKc(I֨: 69Qv[TƉ <r AW0b%7KQtb kнyH0j7ڟ M@ ppl`O,.)H".\F7bG08h밿/[";a4k"?+hbdm=v“\N}h."͵{X1NS Љ2t r熌uf#9!"DC* '?>E^KUvdYA#*Ot7!2Q[RV 6N+WJ缞gnAϩl,R?{ҹ+DV`n>BwJy8{AceUY$w(˖4l6]Ym\&W!$; ꭓSU7o̿2$8}@i9Z2*t0zph*[ER%2pQ1e7IpwrOL9*듹}Y;?u W 5K-fhF֍y5dq!*ŬIY+DGz-Ze3#^&D0%m(¶EoIJ;Il! :&5-DɌ,xDe$3ۏ6[·~ [M Cg=-S1 ׉9;{yT,jn4:T~Z|tg13wY?6#9S$Z aBrKpG ~ZGPA\f`y`kv^ˠ8.ĕAyZqNvI"[#z_;:'Bњ8ȷeZGRn㑻*k ۏy4zN C Wwb-V, 1${[fb_ ݃w`6A>4Wpg6Q!zA{,dWTEkvoO"Tی2.-E- 5 @9YMdM/}ےmhiz ԕAb@3VEx N$)4$jO?e!J@#*#`ŊltG?VGRN mڪIo :?ݻCşȪ᧾~j<,Ol R2[j~a-|$=tۆB:p/]i..7^|6O4;#' ;#O3y{jUBh$k8Yw./6}M땾( ;TeJ3<#U%m lM=U66I }v.jw|(Or=_ZCw!m|TcT#@J `O0O!lP ^̶k2?e9]9"Ts3Cb%*VÒ FMm-bifgș9l^Pi j;ypkG"~FM'RuT=8'N HҝиvYBڲjq]@DՒã3T!,L6̨7rY 9&H!OP=e֡cռmAeU5yCgma D/z_~KW3Lp=3{&7#x1Q/V[ 'ߑ͝% o9ɶb€'uUNJ&Ej@Kna/.ZqZ“Kc3MМ\A)P8:#`qS]Fr` ~90`H tM5Z`AaU+_o3=} *ܠ0[ *Kɘ>.c7SjdW=JᄯI\-O{~.qb:#C۟(.u):8D^kj@ӪP!X.C^a=(y]8%tIDB^2˘A@qIR1 nD"%O+* .5Z%ﻒ \! Vveҽ@MMq%y24Ű˨m@"b[įxÊDVNx)is. $8IPT&":4iw^䩎 jD"d,e+_q0-N( 壖W)$֯8hڅcKcIZ[7 %Իjc0gtH'I8Z9!F#Ṛ fNur_2*JTl@_\9(c~c){I$"w_KuW 0IO[C=]OpO*M>^/OB 0=af]ZGV2 RmrIdg2<^Ҭޟh[yt /ۆwlRʴipLf5!oy] Fo#h<[XܡWMPhf`JfЮt.zrUC9wA 9qi-Cnn[U_|AfVw~æAy3t3UYh2fmfbQ Z#߻CbNgK(6w,Up^㮨 q0:OBlLz@LmV4af㽰J_agZA:tvд`kZn/ݺQ֐D[rd]|팣,;ihA⢯:I$ؓ`#$Zux~BBReޒzn+ iuzV$Qze5J9~ކȧ}-U|rϲ=[w`=G÷\gwid\g&P:<]?mc#[!}Wy`Szt/j6`!-B!ɺu ,a$8 D7˵}S5&F/GǍ$Yh m@ H74xp5 wGrh¹#zWvxfM]d+]f7<)&DVU2Xk]\5;@yVD-BL V״3HBsGP+ӕ L;(FgB:qvQZ*5PC<çW%x +G Gs-%/ Oue @pr s17IŜNYhֹ#ɛ&$5 7lCsG2sD&j%eI ߨČ4,Ni57${ֿ~ aEI)w7-qU\'lnUuaWذ.~֎;ݴ).D`%&% ӼT)@7RjGM٣N ܢxn( HO0=rE%h<,;WS-N@qhf8R!; TĴ0wc},)rX&{h xq`Uj6  ^,>Juv^ w͸ʬ;H SߑX 6ʄOnί?8 ,(U8~H1Bs|]Зmv +HlΘ=SpdbŴBUpˁ@&ހR%SFr 0h;4r.ȪyB H2,AK*:aY mcƜϦ>S| |yxAfLaJ3OH-l,k2fw޵8/JH{klt ܨ0%h*s٥='Yfgu7jgK2Yc}Ü ʝ=A]6) 2'H.?ל hUUysc{&2}=mYqN.QkȸZ%F~;YKn@=.Ss|&l 0ܱҚ1"4yg?7y4(-r6=U 45yf*7E'N>$"u\Qk$CV|:& \3E! @# a=&z&n{5hT={J#Ks`;>q*9e]yT&fS (Q,gl θ{n@8\e^l6Z>Z/lbQx*Җf0FIE{,P(?P{9Hw`"tН[':2N_t ~rz:``-dR}.bk1LA{E&kz 0N+O1jCgH_Ͷ _/g\e@;Q^/^]ߨ_!aQFA1w|ǃj 0#pTtˈEzyPط` BB [XPhm@kBiE}9aN-LKdҽL԰-エw/I: T}3dqKs5F+RbZco-F> se3,O5"pjqE(y0wa>SUgZwr9H7~UkmKL{ʺ*3QqM`?3c8Nި+fD_Ju"MM4_j X`xp}ȄPC6|ƳRa@B|d I<=01uD*ψ B(/iHɸ]Zgmk9ob@i{Q^HM :NI.vL3}gW\umͨ~O<3sTBJ{Ӗ4k*ϑуG  mS'TvXId,fq剎x垣!GSPPS^b\;vē'񧣡r5uEB471v2hHXZԗs{HŶإd_?l3v6,k@&:Adv Đ?N ܴTJ8#]{ӐN\%>\IpErH 3f$>zlREE> 2Cҡ rr=B0 nO%-V`W+UI+'>E#IE|A#d60eGDLgޱc)+4L|׮0PP7TlG%>w;+"^жxcRU @ײ{?;R :~<3W1GdQېPԄB,ڟx3i (#*Jc\{|U} j{lg2ڷڢ8bBTֲ.hJT +S ]]n8fԲۦfGGɦ'XXM!\M u+$γ?4/?˂S]]:B]UGۺE%nD~s]j}(_ݳ 7Ȗ'ǎܜ}i:fB:!#nҥԋ?7'yH쟨V>eLQGL?HqDid&CYYlqv;OI*r5"D{)%kHe]41stpa>JW aӝ ʨoy !ne1rT "q7TkQOJdC;;BT1–)*wVX]'O8@ 0a5>ꔏ2Y3MOJFtkgO;r>z)ִ)&7YSuKgI 4Swr(Wy&*HJ?(;y {eSVگD6 T`-j}qbD4ڊSnmoxN5 GO5JmٽUˈ4^H".]Zkֱ':^vD#S+܅YE6 vk_:y%:~ @W˥JPoxJ; LSNlua8Н@e,$NVaH3¶nDŽ3΅rKdx;>*oٵu%# jId-tI5y'ΥB%- BwW O_w,EJ;]m).P٧WrD ,g6G`[4}IaWU9YV%ȫƿ&Vo6M^K)ʝ$z~Z.dQ\gAN `TM%$#m)e0Bĺ3<#NQb}ڡeGKcuKtOZ浨oQqtƙN!娴iD?ء~0P4٢ҥnF{RʛPtmE"6L#qŚ;֦J]΁ wäBػ-+cX 1)0}#c1d:B7aZAk;`^\6q854=z=-/դn/e) (u/3Gso5pf {!&Ȋ+, \5U,}) p+I::.Yͨ;Hv}u5}XW52h}h)ȬyC_*ށDAntt|V!TΨI$wev6S /Loe7K=* ܎hd}`00byZunpZhd.ɋ}nd8_9 %dY" ώOaorO*flg71ݵNFYEUtFM*ro~.z,\3%G8Y\! "$3+y6;rdge+mI%^Ə67EQK1QZ(M4S+`(w&'}$D^`bq_$lӓͯXTD:RIb fg6Oy:]\}|OU_u6}?2e~R{ (ߟNUȣGM*Qäx߫wi騱U%AJp.E5ZӸ 8)[fIS>b\`nP˙x՞yf{|mQl.5;<\eWG'7@ReNtpW/ثȯ~Dv>g)72~S GrꆭTg,h{v[x71J,}2F119Em<y@&i [BcS8^ 0#WiGJ:=-r0mh:v֜yϫA#츣(9S>5V.HJhP^X4qZzw(m _BI76y϶4<\z^8këO/J"3ΙPXikJC`31nMWR ,I$ptXbb'h1ۥq!k/ @z묡l卸*7d2uz4tjBڀs0sz!4G@Uܬ},yu;+.|FҥQUZ+1Oa41mny㰷 ҏWprșɐߪ%TU䧯np:+x@vE[ W*ވ%By;siv_M.Uh}JuP Ӛl+]-o;b}m_&lv+zXQh K N8<束NkC}h(xmA ߻mt3L1oXSbOq>@p D~=ksRLllpBj)Ǘ4q m |B:̗9oL[vOSd]#kB\z(}ARO1W9 |MW9Ha$& QɱΓe"";!>^x&ҁb/*@W0\g T[=v=)5ڕhHT/pm:}|25Ӟ+u}t N36kWyihHmDN"`okBn]D}cV-ٰpML1o XS1珡m(-"1dx=l1_ _D;/KrnȒԮ,γVgN8nЩ[2ځ~Jо{|rft2nmTZ:ʶ ;7аIEc_XzpV94eFnN}ɰ`ӫ 00ךD'^` Y5 )?>4NgL;F!d C \+KUc,V !eC} bs۞Xm_͎Xd=-6`t$o[ .KLI?aQ 7 {$M(.>=VʉZ*sWHR0f0MZ~egЋﺧuQנ{ %렼[ ~flװX(U/)g`bkA0rv\|l7?Cgƥ D5ymL!dI t}>9r|cQr,/h)u61, ²/&";~>"Hy7mT v@ w/^6 5!2!b. 0“rr/v0qlglX)c})n~Mf|,FN'*56@ Ly5Q~yT.oKhq︗<3G0y 2.OEi7鋐͏њ_,f)# >]ܯ&&%*=R*,obAOb(-D>x S4 tL2KX~߉UEV1 o  X'ziz,L!+/!EJ1AM>PJ74obCfM^Ws8tKFm]\xW<(ܳ};=&GQr+,hpruG%u 'Ϲ:_i6n~ӰeҸnԐbk4MmQބ1PKI>!;3Lߨ3kC 8*לS7I;y(wN ~; By烊Rp;DLLt:SR+MD=8y:@i謱 ˜ c&B49Ln+ғA`[&g1ъz+͕NѫD-w;6'~Kv"dTh**fKj[)_>9H#^ 24k"}|>!oVFMm7)x3^J{N؟#V-8!r/&t0A9|y0~`cqo?qygmޒK9!8%zhUk 0Rnz.Tz&?2! 'P7׸X66U)T&cD̯ZVTL*i]Gi|-ړ' N{h-OSȚW-|<0ZRje_=RCIxOIUΊ"JG%!k 0Pfr϶&{حI0I .YJ6uPS-_mZ:f-$:ӹGUShDnփ؜ǬTT <@ H_yD\o 躅`S RѩozD~xcv%o)աquD|}Mn眐_^騮ҩŚ٠a[&y">[/ѲTf{ÖhA"$XJ8zB Y5 )U^75l4KFHB^>I?qΑo?>*͋X(WO|ȑ^>Ӎ'فH9{Cˤ@iyN%$5P<9f@\`ώjj\3RaTaiP+*xx5w xVA,4mId{Aآ`FLlx$e$GH`z"K m(\),&wXuN /M)_nyӂ89<QTR'9N}Q&ɳ rmgFBt+\2*m]<2 ^QA*"Lժ:5Ae:a]dWpͫ,4VpeME~ G`(OI왷ǝ稝1<2aZ5 )@%B$$ŌB |3U ^r^6-8`ab[7!73J 2&T}pZ`ղK4!D|JCo\o.spȎٍ@'scSD4];yH#&L;>Z6Z$yfBf;A.SSBn+ j^KTl@y(;wLyv&HXl3I(Mkq_m Ƙ. B0b(#{_ͣJUnSVeX07"OmKGcdq}V#H ~M%}]"Wi!hE,Ry"ui1Mс!izs>\CyBY5 u|fm¼<21kvB 3I^h%gSlK_h zubj _u v9"ؐWT ra/6.?E~IQ|yph?(D|]=|%tސӝ(s <)@ ݞÑ.13"pt E:Y}N:\Q{ه>1غSѬ \߹_2j:WAPjH2@Őe;\SH$DC<79܌]OUجtN؏+zdMUL/cAؗhvyY?!&b>;m'ZbQ+ ?8٪mu[oji4`;UK#k=T6䇥GBE,ŚcW8*w y,vb;Œas0^fl@ .L#9I0%poE٭;91 _H,оTu"Fr7NO51A`)8YlH!qݭ9 (p'9teU'XDNPP&·x8Eg)X(=Oװx0]Nu9HRT^v+~qUTJS,\)EU4 +fLt B+Ō>qv"X v%sTD0Y{;U$4 ?p.DrTGh?OL64{3&?;dMMrO^~|FňEI9ds[a3Q-5xA%NƟ8nd#~ |0r@?K'D" ByZh*=\C̙T`ܚ|Tt.$-[ɂfs` h#4aI?f;DWV]1(!ln|g&_a%w#wHǙWeU<v|db G'aɽR[@-!`U Kz}:+z/#]n)ksGܙaN|9,ذ[*7FSKV1@!p~M]nۀv֑uH< =UHtcPOE],6{̮R{^Dyf{Vϩ]eST Nإ+R+ &b}RPUh7ra@nEMF(6 %7g[ ^tzpGa2mA<;qzv2}sc4"((y< e2ھKܬ)!v͟ћV2+*H[s_Hz46m~ta#>6!5$w0t J5p/NǘdF&i;K?~}m rFO?72~p:yj`ZSR*;j% ASN{&7zPz՗tҝo*}bkaɫOj=%1$vT=UuRm_F\q/_7[Uc)=8C;G^PKpSPjhq{V{):gi2W/0+GbX~M>jd3i2ysȕNAƑg;jt =5*/LWb_p5˜`j©ЯAso¬lto0S-)Oc6cU6)"/%;c9a < ZDQWW ̸ohUWSouM6ZGU<5hӟ7TD]*%Q"Vti$9xT_Qn6)%̢7׾%uҰΝޠ6>uOi "=+WϰKH~K4khA+"4]nzWAfA'įUP7K @P k:²!t{IZe7Pv84B Q3#C/V*K= w'Q8$UfqϷR)AУkqj}AqУAq*X?URSgf~>FV8OI C?5eZX1F^ݗΫ{"˷Q5HwӘҟŪǗ`ӵT)覬{M6G 8%;G*7n 2q?Wr" "1ht65sd_Lp]eL8SP1 a\Dٕ2/>leXaq[c͈-lR_*MjXt ).[\BW1R8^9V7 aH29' (s$xD>K} b =ٌ UYpv~7ڳg*X`~Zws7vaĂEq7+mXOhަ)d e+-9fW%ʭmɫltpr,&kgPy>.*%j|g)iaoSXXgDDroHL'-4 Qq2IYO1WeN |Tf~_sʧ\ P6p6*uNj;m kbؑzTО۔6ԶyvkeW Ka}$iKX=+b&[zuQOk$R NDőv)sZpJi'psqfb?ub^Q<3^LB(B:6dB'h0T.eK gЇbc7R;8^c~I\r1N1'X. f4K.c mwU,9eĂ73sJ5zhɾZs>949Hܚ|B]Hڶ `"`^˖DKFMRG1 NRd/ 1IilM-Q8Vo3fԌSs.s6`¡@rO:"ȓ;v5&|"ϡk^=2vj`p_g̬s?)^椫 60wN~hO,Ϋ~꩑Nd:O 2OQ V. Y9LkHA<=' ]\AaO+TiƉӽ7EBrL3 Z2n;v&\pɸ}[p^ax@/N 0=4=? ayr7O cBrղ ܐRsP&òxfR?,DUtTtqk9O}<昮C%deYd8vfu ;9zwKpjvgc#5LPy;\jR; amdAV!<8{rn{`VĴԼڎRɇ.jv,y&3]q)q# vy6\zX!Wr}1aU;w =-?߫@𗎫dGɁZPC-:`WV-Q" 6baDI ^>Ʊ<*nizs1RĴI7dPPeV)6[Z&䃮(%ѽo_R Ek|)g_wvp  _x $Uw&9ء40q̮vA12=p{)h3'ˣd8`!> P+Ji&Ο=&F;&qiM1.ZZ6M9饹&@ZC_G3%^dAkuAL{#}9_F'Fz`cm+Ubm#t=+`Sm:3)0xמRPQւ+_t$=LJdQI-Y-59:W?7t ǗO!'u%Z%N5׆KKpop=f?y~[6:I;14!d&l{wFT 'm}NwBLe̪t\:Ir)=o~ل~>s⥷WZ}m! L u#=s3 KdL;4Eһ Hg>ӹw_tB;?gvJ6}ډ;pn'rh zy)JX1ʆbkKۊrɼ-#K 2ǩtK~Ga4,z7H?jO.]M?Aؚ V$aCZ3@zf4tC; Sw"wA3[7ݼ] ).IQ*#l@ﯘfQ^?a;} cs\Mdwśh B=OAP&!_K$<tەe %/8W<ҷah۵Rtg^}㬱NN~n`pߢ LtgZ(DU?Bύ9# 2X^m;@ǽAŻ`R~qru vs 2+Z8qvw'=㬑D#v3,Zx$^f xydgAY5Ke7w:`wˍ}U &3^9֦SV`aV+K+ *k%ݐC;,oRF)(Yv02%,/Jp[M-O;A ,"bi\($qʓSOyKR_+n-j#Z qK(pM^ ==;&n L HT^z:/`ՖSy=:H(C܉yV2< `^nA #t] ve%T|J{%e{tM/%:6W8%YrvQeG7D1ܙ(Ek;ӟBx1gm[5t(*/\,֔wrY7m }–fr?kDGtS͢\zc ճ 3JGsXФ@Fm{Ǥ,+|Z~_c?. [U kR-i)Yub241uE\McU蹡cK8!kG a)$ Ɛ ($65na3(%5:{J_l oP ǍWudcYC0sy%\ꀞuOc|-N!"|sҞ4Eb2[xQJM.06VW`!QB';փ VHi;]M0}gvfDI?XP_)59% Bd6r0u֯xfwB3gbsh= `tMS¿"\*b#12:y\ss[7Ƈ°PW/{ʸ?D:;$ţSI< iK+> qD!6 -RkO˭p$P03cԠ?le-CCvަP-}[WOT}\H^~<"6*?*aEf2d*AWtY# K hdp;8]tA޼2hꐯTG#;_Y|}zj9GƥiHM 1|̪kJ'pF$QL:@ѮڈnNvCC.ꂰ|=WٌVyk^_@Ԣ%HŸ!Q’B;S'U (kg€DwiD \:3-GL@ZZ0fG4!9ڠfT2A/| bsw>EqEY q@7;x\ŇP}sݗpV 7rEE^D1${R`$8ϳ"ğWK̩MC[fO52s bSHpև~o"C"8e3obhW٧1-lqXECYF U>"d.~d!RQ*3zc3bҸ$IK>x*{zz9Q\t_n/maP,0gR4_ԭ fQ)HMu3h&c !mReý ݴ9 [RKr'۵?y҈Hi_)j?${Zgd~pgwqq&s+z>8lN(v7G1\C} h-ђx('~{T4w.][H q؋W^$尭e*uv](OviilswKbCJa:ɯ;~VbTiIx(@'<3oQN[cA7c>m1DVŽ `剦䋤ee.[e.l܁ Cr|,Ҋa5zg%#dy3IXIh|&5J;~ 9 qmjWsԣ:bݑ8I]JH]%"qxP?ktKDu5&u#[U##ffRM"h+R1G`AXʚ*_?tƫ/&zƬż>l-_0 예u>pJ/QZ|8aJjf៳敽g |1)K;KF=cx6QG!(NI"r{yUr[M|8Roق#Xgj0$ن˕YGd3*Q$P^=3h[P"?cl``/}@_I/.t3-ǂ?xaDn!^W&!9ZE}P͝N~OC?Ajf;$ֵPU)"qBUSK?حkr2cGE3t3y9yʵ%lwJ͂ob;Bun6wOT`Rnj*C$%X? "TZ׸ɰ7 jIq%NYoڡK*%F* [!(jH? 4ȜY9Dg̈́'X1*h

a{ӎۤf}?3̍e o/FNx(>=.'KH jP7uqSKr-F0d|jZO~zVoFl9Cmf´uqmٷ']Ws[$n.- gr٭L ZG[݄iЯ2Ԋ|)Ik #6QG(WF@.b2=ҟ|F@Dt,w9(=W[tEcodAC*L FJkZo~6YA^8_}'I+<,3cq.F#X>.]$"/hn͢|'^ 4Fbȏ#dop&klM4?'x2zaµ!gدH3=t vhEnT3|)O^|pVK͹8P \wm*PV)V:L¼D\RiwMϛtv|:297v*V hTs$94!Fv7v{z'Jlw}ppp#UjZwKRdIsWt(W %ջ_a\p,y_ayc9zsJ\`c"P&q\I~ OEJt9BY u\IA? BS B(&> $h /ydRGShL\if#_O~' :-IocW! j9zX=ju "s(^|z?x א`)q /wvΨ'-߿:1lYnoPlt[Hkr~>h?N5o $E[ߢbͩY%ȶV,mt \u.%mHAah@Mbvh"a"ǾY)6k$ŜIu06LEtÏ҉go"4˧ŝߵũ5RWPj/'G{+y2&Lodİf w-fbυBnL-SbV,0$Sdl깞21ecI ItRKy72t#"@KZgw?ٍ?3UժW~փԭ9b46^ku<(璧[h |HDU]޽5UDo<\fPυCuڪ\q,?5Dﮤh pr7WIA˕h@B ,dP/Pa=)rE&BLJhaR@_@|ZYfƔHYO'P;OSzE?;`QOB`Qri6?kF4$]HLf@h-[]Q{)/u]Q}̑L~z H0.5@oYHB㲋% 'Sk$;JңDFsow@u>SMM-xK %k4Wm*u )Avb91OK2kmys  t*q+:VURd@ .UѿĜa&Fp&@Wdl@fy9Z`2W.g7S+# :K/,w o;9ZZR;&1 b3x:$K; a k(Y5.}n\um_yQ('%!{5^}-74T~)OYtE۰2l$(3»sx9\֍wb=7nyEaz %H7xZjKx܃m=!]J97.'Z4b43R耺x'%%sms5W9}2~`9n־Vy@OyDb嚷Fi اڙ|7ځ^3/hYǮSD,w]oVYK<@~܊SlFtpz@u& —юE(;(\Q ! tS&^#?O&SgDsށjR5MNW8,렰ܼie%<ñ^axH _IH**KחH BX? jTzf1|>x>!JI;،7\d`9Zdꮸ׹hDg|2``8ʍ˜Xg +t$Vyےne)0&\G zCc4a+Nޔ3YL{'zQ3"wkvmjޔx(4II WZ`ދ)[BPiyo*IQ"fAMpg;v%Feh4y.W…V;Á: lW X'aX]R3akl%DXYBX 8=ИayDZ2& s .՘aCYI p8j vqnw'"ByP| pl+i yh ew~AH[b?E+.z6 y}\g$6!8lb/t6V02w)3 ,QK툿>Jt ҁ0 c~ 99m50ӗW8I(UnQ5&$(=d+%:KtW&2RԃwʋD)Ƣ&ڿ#޲8s1h?z_}hFM6dW)&Ĉ$MA>G۱q?&})8=$t9GԳn?}O]feJ" 7(f :fYv%]ckMOmb?. ~Rs&<ҌP4Z6w)*Lr']=8p_5ޟ^emjk8bw3x )ADAFM@Svٕ!P []q׍">fAخ \]?%b7(<aΌK䚻,}zM*{{Xim$CiLښʫ#n&]YiI+(0=Vf m$>3$=ƄK Senj/5 IlG V;=[̏Wb|I+p]Mm8MRn*$55 PP, AChemg,?W;l]N]os?VF c}ȯ2bcdsJ~C,LDѹQՃh)GXjIgָgEafvFGYdm͊7 T'/8ӟ-2w8N8.c( n(_{d0iм#Dm1.Ėe2u 8|25Gɉ! "x|Lw#A]#<{Jo~$XHaGI5vWC1j~[,(xd]z+"Lp2bf O2"C^Iڄ25ř{a05J+}"[Q &ID(L=AP t6Ohwa] vwiK 2ԧn+u"D\[a-&n;1xS&RA"}F2Qo\Tڞ+SB8$7Ù>F$ }_9U @RbH?ڃClX4uýE;Y1#/_Z VFCucceHT#xˠ/W'J2hA/nf_2AһC"60#m>. a-0PyZ- *,@vL ᢬ͩ-b+[{2PsMRXwqc? >#ڢo?P} ȃ<ugV91oASU~9i`>tr7y R2E+#9ˎw1GjVc&lfLEDh8K#6\P0V~[q~COtTV}&͙ jRhFSֈ~G\"-gLʅTOY[ܚjAR$gu">$x]Iw\㵂 {L#/=n3G&8>I֏F] 8WbIЫYLasJ55EϢؿz +jEnH$&'scfFH'IL"ȐJ. -=wqQ6_n//Oʛr*8N=īچrA e#gʣhػ Gf2VKğ}:%JdsILy[Mc_ʩM);\ϖ/- Ee\q,-_WfGFJ8*F][BBCU\-k/3-QK]Q}DQ)d(Jxrq&֜꧴JxTyƳ&DXpDr8#,T!=bRR.Ⴙh8]50OpeTW2tZHx cb-Qbb:M47pBIa蜣7Kt~nM7O;gO-)UCl70uhu^@E2W`ֳ7!e p:mp˿8XƌBq2rߝTGȢn! >G/P`?yT8!P wN(a{8t GWpݐ+޷/*=tR˦GiGYLm]1#STq[Wu@$Z[U­Br=_6lӭޤ=g S'_*EQsn6W49MҒm3eա5$BTɪ~D^O 7}ן9jڭq@/eȽQ֪[Mk9$yU;NKBg8x{7Uy˓3Aڟt+䃭Elպ?ӜxR*LvRTkbٳI wiC+,s\BSysyጊHI>06aw˧i2uu xfXNbW?!ڟD heW?}8_ǹ:mA< +&T_L?orqRR4R}`uϔ{E%ae"*= hĊ_V,K: ̀YN?n&ߐ⳰4B-*iMUBǑA&X;"JsAیFY鱼 wm$MKl0$Y9F8!s#:0#QuW: Wk^l֘`g6O:Puk4j0s!ܔ)`4"XGAX([r&#>ᷨ<2d$#)}Y8\/5Gλae,kCEaEvPV16WBFrҗX፬Z;Ҫn~aD||btXf^gVhPfH SqVm%%R=kr+qy5MV/ ۄ3kYv4 'LpBN=#8Dѩ_B ڵ0 AST໹m#E0@'iߎy]n`tb{f%a/6Eysv>9g+= rNERH_m8`f/V T!8[Ȯ$Vj)x %$Бų*}JsBN%hzC'80-8&v}!I{}U9LXF0k}96. dAe* c".Hb=@ğC3Ыy y*Yn6XÖxm9MdYxf%,#|!ůb@TJU*o<ڟacz~z3:&`wܤ7gB{^*4鈙]J*shl:t9@-H)<+w&T6{#6"IC談Ā흂$bVz[FQrdr#־n@ T1Put[?RlP; ̼uTNɖ?W(^aHKS J*Gm%U! -ߩ}C?F/ӓv `n78);#CP#aFڛiJV"GJnI*|>ߥlDNULSM'itITt^kfw"dc)T8I?R3N3Y* h"MD%$(z Â=C%O_1f!K+rTs!q0S7X} 9{OɰW[OѨz?,},Nz#l%9*,44ei'7aPk jtgZ.9j@J|8"xB~۶14A1ŏӺjn`~xFwO~jkYY6Bhh,46D^T|˸ )/@":be>^m4X>*JWu'i8GU( ,)fZOo@2Rhs;i6&qDqg+QkDӉ^|j_l~:p"7]IgKe¤#[T6$A\z! D5 ~Gb XUl3[.[oMl-&s$TNݏFHdu+DRt{~jOPPz7i\C=oa'ڂB2 eTѥf]{ͶsD>]=7Ȑ_I-v Z4~"AL_3hEEDwvL μ} ͞Jѱt?-0vĚ-.yiLwu(;9t(Fffrqhpeס@k+6//($!kK5=i2x`,E6Wti zN%W hV'fS*J r~^H;[J+$/㛄H$qS-,alsD[$v/XGl1%k_;18솘V:?[|a[ ,(H9ӊ!GhQH ݥjüx»-rAJܴcj3F;zn3L0wĊ=r00 ?x*qej :{*79@$ݚ3[6LM?'X×ɝP6UI++_{6o[K[W˦Hlc>Lj'3iπ'5e_:,AVo c :'prqM-("i1]B]Q  gcTJ=03]vdhJBwbehKWRԦ;+@WV'K5?ed藰ʺ{naLů;u}IXȖ{`~mCXUג[TY%L o)5BG fdpk{+g߆:NiZ킮[5 Q-t};G?[YR/([m~x0QnhVZqVN1Бb#v5a0IqtUsA;Ldq]WʔX%0]`(XKޓȀho`jVxM{p#Wmv4JxrqMbI4ᑤp@<$L꜂~,u]79n9& ]=ۅ׻[s}5>T' FM]m6r.?h^Dӌ$OjGB@C{\IFh$zX{P#6Zѕ۴IdK9>P l6?Jr[rrʞeX19԰%vj&S$P"ꧻʐd#OD|d77BGTԕ| q#bN6X8FwTـ!(J q;tl򓗥 e%ݙX%Lտ{;6aSc@ߨ1fG^fi1üwNiQͼ|j6Mz1J{oj,Þj*tP㗸)CPwʓ+AWɅX=Og0i( < )[y< yLD10<=#McG 7G8C.YK h%m]blA2"غNJ(äW=u "V D[r ]a76Қ;9m=yڎO26z ~I $1Y x|A2" 6 ̺K,61nTB|[X(YӱZ ywcH-ʗhP+EMǥ~rJE4Dy A|yd(UT~`t0"BOSj-wzuTGCyQxTZ j~XJ:$ÜDE32WlA[s~G$5"Rڳ2C)HUXī<(~UkB}hO>IW?e9X ONBrnb>vQ?ni5gC62?c˜ŵ=~E8|! {]3QNGӝ&ATSOw]E[v=*&"T Ɩ#u v90-rq7wёH0 :hc<>(1+AٰOiG3$ 0zLQU(A< ǔIYQT WkڵPGT/*^֊F{T7&wb+H8lm[ doXR{!&}v8 _Fn[!#tifg9+oxy@(V42 ߚ,Kio 쪞EF^ 5܍߹Y$XU1yWq9xH"Yibr l2f3Gn a1U7+i)U]#U/.Y@(eѫ0KFʔ&x>vHFV;ó{bhbظ;x0RU0muO@e+Ld]YBĤ CD-Atwxr iřBUBi7+ƧNo\!w2p?E:TOh/펞!q O(tf$!b@8##q+q$ /A?UmXfHv8crI= kgLpA00ee[) udKo;=-Ӿݧ``H60[Ī?E10cѸVV{FUy.~C㣳 NGpp05J́E] [Ɓ9Y#8c܂^-F{wŏFedkxKhMPqV:%/R|yslOH.W< @zd李3>|1*Kδ]BS%4Ia*IW6PIBFXM-}%V-?̘V]覴3gK`kKg$Pjv`QwoO+`-'dN?=lꨜn Fa>&KvI(>U*uYzV#Ft`!Ƀ-n $>h4` P&|S  x{ʽ=pɅgaDbϐCd1_Ta'bS^a@sk}mɘqNa(O<Nr3Ip=m*[lH2E1FY(ۓtb-u1:'靑m6REZ-G$ ü+P9Z1N&t8͝7;~NWX;ۡy;\1nWv4wpޚRLuXi+rwy W!~mbhh0ggAJ~|9/lket*δ\kUOwZ i׋]L>l ^ 9yqvyl vB K"فOTX0DR'kh6ܠ<bo0Te˅#px_ 2T-S$S299 i)N inJL$XXb)og>r@ҒMXS İ[ >aqHTS=*ct௏ce vtЃ oz yx@;/uљ>X ĕ@e:'Ktq}CQԋغFB;0ɗ_g>L;!a#[@!잉{ҦDbq@,$Y6P wJ1{z?dGj[=xj/h痭mI 8yQSXWi)xg t.8M$80P\~2stQkMbKb+9bW%)BA(ߊ;yIOlc&pS6;hّV@?\~쵛:@ K` ^NLҘ3fh| t'.UZ]+^ysX>FG rrqJEiŕ /{1%M ZѠ`LF Sb_8}cb uN&|Ns06WI;(HzR'Fc0;Oe!*&N۵Q3`.bYN@N2.[ai3 JS_#/tq-ICdP6 EEs 0_"L #VpELlwF@܌l-9u>KDz-yu >GZesCzP 2k؂u gI! mAt:TKIKZV_n ry'S'WvR`OP4=+Zm!/TARwȊԎ`PMŶujRhyfˤ]|0 Op.17ά[1B jӈgfr%da-]VLѕmqw u(eJVU,V~6S j[4㹝F|V %&1 6vjKxǤ rh2H0JSMs(l3]ޠGVˉ\! ܁(!pBG.;+W,؅c>F !9\ƫjt6 󦜎Y&ٶ׶c9uf$}G%gsx-YfF~29C.ECʬlqLmUH_I02GTe 9R2*E e\;17tBtɂ`޺eKNBrПY E[#Q}:WK)ԉc yPxL %lμǗJ<#r1j!#;zX w?!θr,{2:.0z$ж bXkΛicI$B8mKҤx) s:C)CPlǪz)44?TZ6}O11O,Bh$޶9⡱:Qfl['ʵǖĬ!X>#1ştAuGIC ̒ Vr=w bx#W7TZ-F7-h+-9" @'aq lK0/B7Mw* ӇMn6 !;7$agJﵲGtY!zƠf܃Ib u MػOP9Y?^ `1iޤ؆W$Zz?a r/ZS xd4/ڌLHD'/ ZN)1Elsɘ( F߯ȣZ&5Us$Zv-!5+3}Tbaq+Tr.mڪEJfw:A\=#A6Js튺r±DŴpgi"I+ iS34lֿEhRsg<IX\.l'u.k q1mOH$vS*Q;#^Рq!|0%ADo@)WΜ= /ɶ v́SP4 0?`Wp<|H-.df{YSaQznLXEU(g TLc/*rNHF`^o{_h\$ dmQQ?S3fL zlX4(Zy1$"O|;3<Cс)Ig37!&D;O_#T~i&v)D{O=ABu;pkwr+qM.uE&C b^.rf6G ~$M>jj@y B H{E>#@56`I~rsC`ygG9q2&rN[c7](0D1@he*H.Z <.n}|Dh4X |̯$,UdCiNĭ{o3»$5J̭[Þ7]Qݳ^)dDxfz.y>C/ɚbڙ0c".jYu|W;!2W$b(HE/tdJϑBBuTa9p89:' 0t`Z>s>pQ4&Na+9HY'0SQvy[o\ q~<'"I!+M@AEܴ@74+>OSU cw.^T .N"<7Zzӱ^hI)ʇ?@< kHQ|i(RpY7`hgܽ%MWfOt[ exZZIn^Z=nokjAޢDt}h0VȆy:LќRBƛëyMO*.s\_Ba ˆK:cɬM- dC.sW;#cz<~% !sZSn|Q]کlK&+.6xEsg;pU}_#q0lٻ׀)>8sZHs5. otn{8<-nfvȒOR! שAkpg/Tw_2)?t= R: E\ZUg$i ) x,W|u2 D'|q )ǧ`֐0LWrA%4`~YJ+/wĄMYxbuHϑI:C; <[)<Z9V9ۘ@"0y7e $?Af >ݫiԙQj& 5,ҐwN7(m M,S96'+HA26=BU0޷ů/K>nj5B.y\$9zƆoHT7yKd7 YaW$QPF0W4b4ޓwAUCHuX_*. &a 3vO#K8W,g&ui[8.)n?W/^xpV9rQm.QUc B+#r8<r\4[3\&g]mL`yqb<~m W=~mm͗<&Wp[Eq3j{uk6nE1Ch,Qlvna vT]raV@13>B"hHTeӝ^tmշz!ɫ! UHs]*)m &S5o Ȉ_hO1ˌ?Xg2OqA,L-!E5/?phŠ\ߝUN/qѫ"59d"ܯfBۦiP 2HkdПAs'vkw:S^f᷀(;rBQ5B@fo~9"K4=3jz7'l>SEcحӘ`ˑLoy㷸3f{I?.R^ܭYQ&([ww:>d  Å?p- ל -IWjEo`<1E7:ӿւE򞲞#Œ('̆5Oz7wqViyMm B|chFA$ȖS4r uą~I%HJYn܀V~)}04aqϙboh8MXR[D`h=C1a^cBD^?7' Pg/x#8_S|ݟ?7n{ ^$8kW>r&f89ӇLQFu@Cd7+/E#So$q7xo|P{,ys-HK2lE$>6H",ک]uMed(sT&9c x8hU&V1Lqb>Q* uS_1hX_ H4E^,GB#8يivIgo袡zڲ {v*G֬ݺh&ӶЭ}.hĐ-~ "vmOh[!q8hJ|]vCvi!MRZ8"mD*+jVS/%;}ff Xk^`1F]mjY~a !pX}ثO?SEh|)Y*-4rg0wXf|\_dYR>0 " HG)dnư&xõUfVLӇlQoh{r|r?>LtaTp?inǐgPWQCkSf1}"oRRX 4h:)ƜhU\osY?kѠ;bKq|Qvx({;kQF0 t1*5ȓ4yF~|5l!愌{ƭk-`e|nC{JXu^/j&T'C5B ˏOz iNFH{wQ)<P ϣVWfC,rcGêL&^z}ROɩ3v N *R)M\ CH]s7glQ`in/C-^ѓ(|}G/?wwK& I90r+W]85DE=*N!B,'tF,|HV(J%HCƂ3 Y:vhwq}1ac09)/zBf Ot_J8I͋>QD )'Qb %!q[rY ԷZR"@-gm6`'>ّ['>#BEu&1}8"HQu|.9("* lĖiN3+OߙA&C.M0=Ě}K…T2.՜aƮN )BY#7a!=\AL$\#]֗<|(/VM\P7Epyõql[߬8_ɽoqNB{WtW~?/Zq5HV46heWEA4_ސlj2*_/լSTk W XJqV.Tk#NlupPO (촪wUMSxv Oz,`hțǙGWq#;kN;K H7fے&nf ծ ?9$wphF.sK'm#W@@?l^p0?$3ԯ9!Yj{!zH۷qp{QcoQ\)kΤE Cܔ[`mD2~8OI.`ƯpjWn/-{7m`]A_S5|~r&l^{4#9) xE[̞Y'`0R:.2ײ?i7YvtyT*`JUR%FpvVPg n8͝ oI$9 =iNf m!N)FX`dy@m!7D<4MNV.3 Pʸ!uԐd0!`Cûy4@X'\O7Eڷg{uI|#'u= ÿ7ݒ{lBU)4ۏGRyyn{Wbayi7bFeJ-Ak_C 4\wؓ)rⵂgLhX27@&@`ea}Rp>~D%H |# =>>)ljƪt:AτAb^*8Zާ`S?j3 iPzІ BЇ;lLm5{Lc+Os0:xSX"1]܅T$l&,EN# }Fm&hM{5 ά&UqD;R5^N:*aLPZ 'l @QyC4D6 B|iJGQ M[voOZ5xEB7ѯD V< h2lKnLVziyڌ1{j˦qvClR.s.|;ztj3paNT7j_y<,WL|ݏs3dĻI [VeB#6$(7h&ib8|9nv~|hq->L.O68(ɾ^:X,BL sZ1ٕuNS(H;=p*Q&@ =e'}Et'GhdOrg7,ǦT&аi"MNh R\Տ/ɐS ̼5iE|x/_ⱎ )k]XӞ\ofebE d٢pG ,L/@[#.Ȉf]~˺T'-/s{Ib@v4WtX\|c椀Ojˠ ~W5](1xl"PqBmLCG">4@XOD65lϾk2CQ Rd +yౄyoizL~gII4s,5ӵC͎0՜Ռᇤ))S3Q06T\*{"!=3#TpOl ~읁)>;{OHxmQ|jy'KbUE|coGe_q{Wo?ʯ0x_zxo%>jiK](,7DjCH<"+KQo$]o)4n RD.SK2coݜ4 C79V>S-L})eu6҉ZY7smVƪqcqc@Μ.'Z77gjfoyyq3d=y;4p]!gGjc+MaцoLh~03F^]>dr .`Rt5{בnj~lwS&ג+*H' 0)]9?\ebI`> \/?GxW*r XKp> Řۧq6&eL]׿Kvkc\AdU"T(^I?p8U^PPIIaUl 1) W鑍?Rء^AӖ?"( ^gr(zY*jI7hL+Z>Hؔj8zgd93Ws |^.#Nra-'k&Ip)/`#\@pfXm;~+SBd>q6o&MxJHɬ[/6CR-)1}cf@ʖSfKHWHv]$,wJ_+D,@3CKZ#QD9'pO ZyY3V(&K\s -0FKj)轧W1y0&"VΞL=Ш O Z_&sRӭl; <,}& e &XZ3Sǚ±ל8}n%XxV|]ɽv- v*{!E:ڝA24.N&A5TRG:!-h(Pl:AP!%\3))~\d\?uVE)$[Ał&S`#)t48q$94x $%Zo~=4C46VT 6Bv5!o畞fcOqaХg)gI`< I4H^s? | R2|j DW4~HkӵdB?پF Ղ 3u^8œSbcJ72&P\9tѼF`m/m;p@3*y]&#=bKSzSW'u؏#8wQXߑ CRޠG2]&5\X%1eR41Ի );ji&-'撽,]4eBfI yxe03Lv&:I)\eӺ-~[]n8l& *P^M`9lDdPwjê6'hRs)Wǰ۳3%%F) )fosӾdle=<(Q!wmtŒ*oeD\ZX]O_K?bD~]Hjô9'2|j1]J5 P<0"wP];~qjx:y]V ĥF&)j]?}'|qL1 ,6bc0 07&/;Cc3f[wҮr:bHBCVS,XhUrl[MhЪOx(Ӳ̄fƪxח:0B<̑]턟<TզQP}]daY:*ۘI7ܵz%Bn_0qB^Cp:?Z4hp)Qy 6!l |^<Jy Ę%dAD-BT  v jo KNGy4df ?v/sUFT1bhk’F| uͺl\/*oYN*T\PR-> 1x[:€Q 4B63:qŤ_.0AZW_{R_Y`Ȏ"h1Јd(epSh!:%_p5uLH'ݎU\|W/&w sL `)FO;`{}'?NVNzo莤tDY+Pf29eq˻J7ۘPKȾeZ:}zT/I-IeC}/$VtL nߦCd6 ]s7_ZΥZhOt-[U_J74z~<ӯ:ә* fK;B_V]U^F 4KmMi~0S^n4.Uup*mA XDxAi:D DӣYچfcg8>_3{d"8z BAatwWul^BKq ObҭfYӎ e#z3k&l:1e[EKkhޮ s⧘.ANYQ˥$k"zotW_v6xхh>ב߿L/Z DXeNu*fd F4 Qtbo-eyI3Bg^ĦQV_-jv394+#<H졩y ۋٻ1n-P:uܕwqG&bp D4׳ $x6C@_%"e9t=MԱq8O5ϚfbQR rg+[ 2 6 )cٽo3ν;U|`?c.Jw6Va^|$&=Dz~19s ϋFƧ/ʟp!먓3tms}CVh|:3,,!)y;"AQK/>TX K(Z7w{7kJm䐡kk#DK,EbLVV߳fmN!fJ:ȅY`*:0#b%qa0`Xjm3ti* &`>3|XnRq|&9k w[pN'I 5޳rt.CrEuJk,jg;+>^k_TϪS:@.JlUM8qAnA;.XS m1Y Q(]ax૳_BxdlQLvES kLo. 1R &OkDOZ? ?饈te56~e̛K-P}:u>( | ?18M- 1ACQ3^80dZ8/&\薠ڦ4^EO@[X#ئX((#?uI,Lݭ$-",Q+Sڡ5!JjOW8pRPjG~GfԓSg3"(4 Q\U؀bꯣHL-M/j^R9H%}WL-pɃgpukeN u#;C+pcL' kclSD=W1Պl,!)4Otɵ_fw9 $PRg3Pq(ʩg@u.:V<p 8CږE#ٓ2t5 ^.p+*8Ls1ξ)=}bONƪxh1O{PU';-,4;l-Yu4dMӆgZbv~+ذ rXXJ `Nȷ'@F9"V=Q++-DPf ٖPZکRb`^{1ʠ[U5q<:X ܪC}ЎLysq2.7AB7/6WiiGkԼk 68IL zn2m]%K8 jnߡ!0DQLzƢaZr׵晴754rQ峞}⒆ג$ZGx%ONRk/T-YٯqKCnf$xZZψ5Hn S[=G3:Aκ1>64kKy}׉Hۇ^KR7| Wr63_7K_+9sN>1Փ9hƓ:,|9">`^rĩ/kZlCc Yg![/h[N܏ 뜴7z+E$KK(m$ 9qi4YaSdHqֽӅ2AiqԑfBBlk7HNF+,QFPĦϣVۆՂX;k@(E6\гQcaǦڈ RNHq񟱁"1ZSCPRUqX kx|t3C "+1.Eh Za]#Kh} he5!WyJ +)3+<( ŵY5pb! 4xej_pqLvj8ϓDu>hgΚy&,n3DnoXB3=6Ud2ܨOi˒ŵ}Y rdQe, IHɅ6UE\oz9ķ0Ơy۩׬i0;n3ܢhl?z?OO5dg;P͏)=MD6|MyQsbw[`H "R?Ѩ㳙_`3p])@+ɧy2ҊIphu)' &;sDy3+;CQ& )*fz!72ՆzE+ E$ H">' =>e;фw04U-Tt31UZ@ebDŽ A]>lDž,'"_:r{\mOSW^$7h>*8!ksÅ-íTQQPTm@Yۯ%Lܛ95b|&GqG_nMhѭN.xؾ5n}tx? ,(ӆQ‹-f]20\ӫ5u1^]]lW=aC! <-> cF?R\e1Ree;rWV^{ ~9TAY_4?tzȊPwnM딖y0ymP]:n\CAF:UUOKV򵓂3՚^^xT`LkZj d`? 1e>PBFZd?OWv0rklro _:]Xx6灉mS e:Ps#3ObQ^~rB%$g17H)#gy4M̌!e~M1? |ΏhPaXr/S9'ղM ʈ.w'$j{y녅knV0*URWA;>?]SοSn)V=- 9{}j]JhڭHĀkWM@@tm$ZݟهA)ޑz@ޝ!VfxA!!?W8Swn\r8gP&$tϓ_CJO|$UNvb혾<@#3s ЃQ(ud$GVp/ BZa$F~hlĠv.KƧ*\ `yAF KGQݻ]\L݈BֺSfvC(M@ś4I?b*ډɩs̚R"U;>_` PPC'ӖI0A\dXڈj.mw@Ln?e!$ 3֓zcGypjz]9_tM4D)4<Rkع fc zݔc1W퇙.:Cpkke~f-Uї/J~0-?lI9{ZjXp1o=q!Yǩ[r43q!'}Lab6fz8'nO+Dk^ek:9CF&av!/?J;zpa:[N \ Cm}9R5QtD:L2*9Jj0ռ]<^<`mA@&nC$ Bۨpl~U+0e7YMUu|~Z@LK@BFZNո7Y:Cfj6([\E*=g #uae#GB:ApkL -z>W\\gtH_AKl< P$+F,߻=$;7XJ;!9 4 ܩd6q-G’.d/FI "M3>4GPqL#XXR{c 4)R8%"@1e* [1d.Nv Dj5MDRUZ{9/An-b:ظbޡD᭗;'߶A&v!$r;ap|Wh>P{4Xl #iU="j:87zY5ڄ3ԗxn0gaSI@E+% (~/^#4G{ B64rf]ݠŮH @Ȧ{ ggV'bgnh\mu5ifzT+ܻ:pg~yM C SrF-29u쥩goGu>חN+N=L5b+9GFEnZ  LA2@_&{/eE,;s9 S +Uzh:%Db EAmj.4łz)Eƽکv5Pg·H?ԣ[4鸌ƌ]!L$)[ 8m|91I7~kztWu{ӧ^ Mý!ԃh ^R7Fd0.H@!4=I#%w [}y'\{hn)'V;o$a18knEO>aFI~)R"I}#*8E1óEnAJDW%\D"^rWxy6H{Kb$y2[ QmSHcVs+sjtDDFUuW.@= $K f^5ܜs`'%5 p6e皨Rx^;NS~53s2CዷyiTފxC5=\jМB+<Kk U?0_дg Pcݤ\cu˵} C麟(AL)l5MFB҃h]wD&FG4vɏ 7 =| BY£T”tN-lu-8nmMRQ0z Vy8# .ŠeOx]U]d0O"'&U~y4@w>b!#ب?v,~~ׇj{wa$e^@{gM &p 5E0$ȟ$ܹlxd:_U`3YCBbab 0)Ex0GVBUoiɌ&rHk&И.ິ9k7cv@,|dGw_`H%{#8KAU.Ufԭ%3McfPuIOT4SbQj6=^Z^mOr㊁Ia^?j7#* ^`я˫N m|@10PKۿ4E s{ikqКk7_^2F Ÿ`,!CЃhjP,Ӡ%`Ź22[!mHRRfT[1_&fE3Vf8ԍEJY`Rv|^G $SU'~`@!q p$t<RG~,`3~F稞ĹbRC:[CU~`nf4PU˦=6k*nB l٩2 ga$A{EVj bo4͘E]:m8h 38>gQmM]Яcjq`6w&!]FƛyzGg1k5Px*7VR!Vy(m;' epH ;d]-4vB| AiLErs!(m *H:ݘ\x(X?j=9J{\.PiCoOy߮ s";ER1p8H8]z O C۩6'E颶5\p7k| ]d<9ta\^#zki:y88'2`۰>1~n4AڐDRc,D]⟵.$f /pBZSO=ɢz0j C9Fa8}חXf?9Fy1rjot/'-Ȧqե~hSW|XȈʽt~ӑ#aK.B@a("xPlZ1rɨW($_ sUIm%UqQ;ލ; exQԘT?ܺ͐WZ3\r} ڛB Jjghv 6D+ ^SElz2 ~8 ιt# Nd G'Jx:9;k>AUb3`B5AKtWKސ@N٤F$8B*&aUA^VJqB@^jNߔ Ijb$ИՒO4X&_MUjneHs[ p Z:Gp3AiG@ls¹[.82ҕ]Y2EŘ B_]್׶{'!\\O:u N =͑,MUێLXz _ ^,ުtT W!xL͏u(zUB;<~?[| vL/Fp6tӵU k?d7eSNuD5bɛ\^7~ K_F!J6FD֠n6%lݡE=%Gޑ|V#W>:41g^XR`u*چU߇>@!'W1'I?ZҦ(㰵Cf\ަ9:}FK n䎅$"etl%/lNSq;yظFm(ت.؞33$Ir6Wp `QZw U//?Q o~/{R-ӯZNV)zKȧ{WaKEҾZȠM&K;X1᭝Wi)W6d.Z epͤIuҊْ#iGhQodR;"@..ʨ̹1!๸ W[kaW[ {0銨/NJ)xՌɾL# T}nheR.\ 7v=7G޺6Ǟ@I(N v 釦ȋfk5<.-s"oK]G$dž ZH"! 8;CgYA8݂9|E7vs4$O S8 0X 83|-dF3iȳk~V9~ Dnwj!}6I?("`Djr~(e5O h!15dU; J#U+-6FyIuk=3OΛԬr pqב|cz0Nz[\.4 ]@YT3_؅ OtS),RB1?,Z*"[%3ZT^s]0C g 0sHjFsmK kl8 ǻHlG6yԾ "tP5tvjY $ O.vFntK˜$Iy+H>0T Q-ŝE=U޶ήIJ@nK4s/-dG}RONJS uEX }KWh ]e05Uܿ[(h7b;&> ?ynomA XMY ,7A+cGlԦ0^-{佯IΉ!tTT3Y43. o-H?k^oAYþg<~Ɂ9zrhɵ@d@~i.VAWΎl*d)'R7 `$ŏluHhTlX̸GEAO{h{3mc ɸ֧,H?Fu-W#e)tM9"}Rf@94L [ی*)G!ܴz=2v0A$ :蟁|K(I`fq59[^0z@;54ֻJ8.m$ L2f_5Ҷg' Y_(Yq>=u6BJyJV|7__@)A9Vɻg'sLkp:ނkjPH~V Y`qKІIkV&| NAk.ǡcGXW|~;>)ǤMTf2 ,ҮeTi1stfG嫫]1 WReͽ(45 Ir./31Qh"A3 F5_/=*$r:W`EO Q^M`m׀{H . dՑ"eR1qQnT^z]Mq09x0!߮Ay4] u8lau(MwI9Oc{Ц' 8Dc骉 wX"2f!;:ȓȫjђD^V෴2ߚ,dtD AܓrXxh,RHZ?yNIQ!R ;uyyD"~pK .~#vtМ,_'EV5Uܨ~ R@ޒAS@FS}8j \@sI2`Lfu%8HFlEoE5'qLj_E䬄M){35,5O_-]  CE Iq3{alЕnlKjvLyiiث_BRHk wF&3z_8Ms{x'.ݘq7 JpF1WbVޞmm7d^W>aLrN~}XAdz`f'h.mbZV~";i&JE).OQLi5eZM NW`cdWu=w%jy_vc <3LZzo߾=ʢwoo`ƌ,nG47F:ezpB| (s.^\Eq֭&E&T&4^%9N/Ĺ)j",9vQʧ2m/F$C -gD*v&y䡗 Bq3Q/xVPʹu/$-$Š)]qmfGՔ_ƪvȣU>NknR7@Ԣ駵[xV[n*qM(#pf5wHp "J q.s KxZrSy@"~J.ceG$ @&/}rboTt$ʴz4Dwd/^甉kgFq!8xyr, N7A'Èg2K-VKd?iiaL$A.ךܛRφV)oQbL3gi7gZNmir[79,憱xi+]LXѹ2gx"X n!Q>Z-p'];M <%)j̵74K|4ya ?ɜ C[jD*{3iT=+ꠙebM9%<X&yV11rX9ڻ.`\R|ܒ]a?ѤJQ Dj1!qW}L*޹bȒmΑڇn7{~ :K~zjҸVPO%|I龿Tha"2GDm3~߾ھi*セ3跺\pxR6YSzTEd85>M t#"1Ghrn06.@~d.CGH^⠜|h.{t'5I&WϖOi &T vTkshmoJSlT__7ZR$J[/-%cbJ%SIA*?cE3PV@Gv&j T9b"sfQ$-S-_x ^_MF$1PZ1M H@PۙىZ9G[bg=8,zjJ ĭwKuZ'hp%Jz/ay01 ϛ$&ŽZ}Ώ-իzjA;ͩb¡zO",S)Th@t.zyvKf-fID1D̩NNF8SӿC~w!1_HxPV_^:+2sl )gz s1m͓yS`mNvE{0. gbD,Kj(bePkOűC"tα8N/wgBj~ -2Gl' f]6ݒ&ʻ$YV>'20xƇI "H饱h/oZ4#K3N'܋(EwDS"M Y++)31!vz*Ŵ,ɾrBibiK׫^+]~lopͭ:Kthjee^d?d-?;ZĬr OMZň;AS"*iW6r03] W\D_cd9U6嫰dei;РieƲr21c[szO  (þgu3Z=$&ރú-P׊5XTƍZsg>UGk"Qh5({w+/lE562K8ûG .(Ɖz ;eu 0"BQ%^r+f\I'GAzvZ*S׹ba"h>ԬsYV+Qް@f]wTM6㡩7`کmt+\|-h1M؇- K+ܦZcxO;G@ma] (;Vy6VA {Wr/F&(2ۭ:VG?d6mW"VRbJ zZEy-?G7qe6 w㠱P*|U<&:dHl}C䪱 19AjOdw wvs.IpƹRƭ]ײ볙v(Z?@[F@6DU tkY"ݝ[(P8ܿZ]Րr@o=˜אKX,ɢ:,TL>y{-cC%ZOH~&xoJPNa_ ws>[:y[Q,"P(H=en<"Adn`UܘtN/gʨy_ 9 -/9&ٍzr6 ]tt!d6nkmr[=&95 '6\ibvFf<ə”@x6\P̉GVr9 MdeԹ9&LcH?7䧔+A)/ o5;;Mr BZFLh$E!c^~nG׿Wh;,9γT2;Z2G\ёa_n8(KXJ:=s&[3Vvì)7 ~@f'CvL6VXܐ[q*M s~4IdҫSwjv& $rAb nWX폹XmE qT~8Lqp+}y%#؇),Sn "'ӑw%~Bc3V-I2M'O\9]\Qc.]ō_Am5}:rvn[l  $iٛC0  J5xW$@Sg0k90u##8Liq (^F.?ᵔɋW_|m1`/ $~_@v#cA'c436"mpٓ^rL:n[QAe.WR:3%Q9:acƚl}V>;` ` OeIEI&/հ9E`HR\<*aݼ?RN؍m+CuܱR7@umUjm6NDڹQ@o#MmH3k;ÏH'𼦄1Y"[Xv(@w[8X nx|>xpzJqr w3<#_bGlMsfBH u6"pAIxbOBHa;֊!j̨*W3zQZqT/"p\M9P |/kc85mre|&vk0cUzh%?O$82 ٧ٴwl?~^.e(.:qٽ-#P`cU!j5U'U$aQpC^$ڛة8ZXnns|nnN+UrL!i5!MEgf/yS:0;):*y!^9$^&$ڕ;|/7MgSsVk I^݄E5L53.0|P ZrVɿs.9crP)w`^-ā7#8L{+|<_bGQ\*xBShnzEFH7/&r֫ Ufh,§[-CGuReq'7îа%. ĬDʾ*0g*L- eT-!ni16SgTO暴"%<+AefDv6(Nb1^@a߳  Z  (i[Pv=H wf4W9҇ޮw.^u1g"11 :fWo\e*-˘"N~*< L ^E{6f0_T\ȱě4Ռaո Ʈx~F["cz6{IM@\sz5~p33 ⦦tK{|vb>O`UZLGSi^Nϋ=+:=LXlh@tM"I6¾bI+=IE/fINB*#2 Axvd~;zG" 췥1d<+3bpڳ[z+9csd**oXy$&{z`UA55~ٜuvHù?"2?[Ѽ=~F-wa!ˢV"=z~V5WbGW--.2Ii!RPgKqQKl5kwGp:FJ(MDb"͜ZtLI^`5FQ0B@1BwпYmxlR0w>ˉU5p%-i `E byfW<yx).l"ܗ-,iUmKKx"!; _: wmK*K]UX_P[ZM v/#ZEr 漢O,ճNs }wK_-#:%⡸̩cj^RF52|d.ê߽!%ǻ4nE'چG./hgnEEpS`QQSˠ0o k\ LbnQ)e]utz=%f\n]bP8x̞u:˭ Q & zaJMruѴOh[&@*}f;,BFA&IƘP (ijB5p^òhHh#Ũ Tߺjv,)8L0Kƺ"e0)`^%OGnԚeXk/Z}$p;]0$/˘ރxz,Y ]84?T`M>\U0vs>X3@G-!P3Q>ZH k/ۙ\WN4Gd3s?IA>SDk8vee7JDJMUГy4wg-2Rq5@pSzoj{қfǘbu%Ld<;F# v=v01hȞ>c,!gŽ<#낡.|}/O(ry:PD|d͵Z|y ՠbr ʉf![kJ~.xV5LR^)YkQwld-#EVBtv=_X*a$^]^ɋa"3oPB. <j|*%.Z-8NU(NyW\$7 Nh`,M*fxSƱ6kV ?ñTd5#;{aڴnȰ(ppjZN։S\)v"% J+SӲb) Ghndx%o:Ox2"u%q^W~R3C.`L: y^ -Xpz- =2 $*d@kۚj;ܣV؎a7/` wN&XiOs 9V']2}uz9+Nx-nPy=k̯\XwWxֱ{T2H~ц3G83Nʄj7{$e~$XJaQZ.l( jy {~Ɵh}*To?\u8{knBXH)Abe' Yۘ@$<\ XPo{CbqMN8&׭(Xƍ[dU~RԇEWք|}5 g2GL40eydpHѵ2◱…uEf::Q1@`UeP ˰xfWqRr "cn$Q!ODR}N[=TG|o'>y"s?՝u_,dfFF3Ξb 0~/0[9%Og_Bf lcOp+-u>I734KE dx@xSFf;ī9,Rg!i*һkEꑊ[F(ԌRZjo GI<ѨZ4\_fΙ yѱϯ:Enb;f T6M m(Qv.BGrsZq nFTmd.Nm)ՁTT*ڊǔ@k " yFfe=\ns܃fq1;BҖr1/W䨰W ]:'ƮJNFLFx 8 }Ev5% 3įoW, =Tpnɮh ؃l%~~W'qS[6A7/pwl`+uwaرހ=I|xryiD~} g-,6b)t%bb02Q(h& ^sd=hF@x0A#IcDk57; H+ E!6>`4\I43Ev0*P/ gTsM)?pMKORM.S݂z~+-@*0[Uv "DVXaxH!w8f&4 *I8̱As/~8W+;:C`SZs:/>;\5H' BΫvwaS֭">j '9}orC[>w(y]c> n“>2h`BU-+%^ʓT-((" \bX i8y1w/Coɂ][MM\6[\ǣ1޷ȌqL`S|UhVUvwg rVvL]`;'y,Js J6&BZUˊSsov?M 6\C.̏NQwE}sR1Lq2IϖvohisSQINI8QUԇ}s+? 'S>֔b~y8GF.4zuL4΁̝ǫ<~ț7f ,a@1yS~SŖ<:P CG/I zRoM߰ҼS1ih> %J꟟T4 4.oH Ҧ? DyUh䧴P{Ƀ3!mRb%7ɐ\.[X1 o;qf(" )R Ba%M;Ƽ&|ulj[yw͖[6sH!munF43q G (Yk= ܛF@d. sʉ<$M>$v>T53% yX@%AwhԻ%a Z;=nW1k9DXۓޔ +gV?I:迅Dr]^xi􉄦5,ż:]*jd?+XSo@yic*}%$Q"``g-1\ƙddڝF"'xԎr~s!npmȱ v )1q+ ?5qh\jvڇádP3]~x aUL'WMh+zKS*L:X@d"h|fM>P,bɵVQ_LdJC6KNENgaP )z,yb™0>.mB^SM a*9;ΪWX_p-9.F@l2qJH삼t$VּWدޔuȧqn/y|T"0SVƳVɊj$6W''0wҳvQ Pn&*|F4 垞Ƕ9Y̻41 'K s50]0اX ;7D |:D!/4JDv/+ R(a8;4g r9[f/(M^\0%z+;$^!pZ:9 B : D3#%| S MeM땶ٟ֚$ȃk CxŠiAOlM̍4bGzˁ vR F .P/BfwguB\ 4fݝ,Iѓ_t{] ,7iSurC@wEzy.ߋh>|&nJjT\}e\$M4-FC/s@8ԕ ;̄`gֲKESK))`[4QRSkEw֌w a~hʸUnZv gOOmg5DOt?sb^!{,"8z&tEqNfsgňHYNLU4"4RIpAB_GkH %';Rr /~cy5KɖT$~ K~pUC|>?W-z&d|/q4]U·xT8D#pl_gl! |`/۰o*B#;sԂJ MS9Aޕ }vjdO[5t5]D)<} <,JV-;E7^%Jgt K,J<1Zj0/%h~g|b8/&K,p>/ NRGު>EW350#W.l[KU 9ݖPJx] t/M)^ǜq")$|]& Rpnc:g1xxB,]I[z3,͡JW1[e:Y:}ga=$q3Z1a݉ [뜠NlޭAD?׎x  ;hw^ڱ#~Hvw zW0ܜJ-E| (ll,R0 䭩vRd/FG5[(p#9LH uƊmEh7Y5< w~AÎC͈#\A8__1OjKjNw.;zm@~%z2Thi*g1\$%Aj߸[ND7sqeTىӪz'A¿KIye ۱U0GoǘBU춙}KGpmJLQ娋?֕m |#wC:65$V.b_&ԑ(/uACt|ơt4=R QL<sYBpv! Ȕ|h\> izuM0~@w͛1 hLr[ҔTג:>0t PaPN5&sp-\:}aP4\e1&vp\Ipk_|3f\_~"b5!"OpPiܴtǴ_ROO Mn\\wݎ%p)s2왃NVhmaE 刬ȋܤy>%a%-@Q< AJmz8iپ8G.P71^-ށ *tQ>ek[G;ЍJ.8k snI$2$ 10)8D#LͰWz ;Z o|ad}!*̲f"5'S M">\k})1ξl-MVMF |[ QK5!)"_7횇?*_ZJߨ "ō*Ss&\=PAbк?ZLEЭT5εֵ8`2wfLF!f|Y脉c~$gtm+x5zR~);A5Qbʲ؆ñ,p=!)ǥέ e+gLJ[0Z65ݏ b!N .|(^v~ݵ狒ӭ!{ ESa ;̚-'T/!N |s`' Uh޸"ᒌdʩUI|Y>O֜fY?{xU˦ɜߌ)PA]~GeW;\[fF;ILQWsgh!D߰{ B*EZAl LcI݊),Cm2CmX:O1vL+cVD01Madn>ؓB1濚{J;?rc>vZӏU@P3p)-̔C~$Õ9o BL~w{-C-ή9b]*lui:e_*GnTZ{PԴ9Ҕ'LXq-;縗ń}hl Ofqj{eozһ$yL*J15ؿRy-ümU|E?g++ښM.YP*DzV*e‹m`q,8Z673;*kce {X^EJ(+5&HHcӤƊAHZ|H&ޛ< ]\E b.x(hPce蹪Xűms w` =ɡf45H#bR!짚 &Z)o) Y zP W䝰j:a^~r Vo?˘Q=O+( ODܓӗ(4بMuVX4ށkBDEf:~# TfLpc6+$,h oAt$<+suY[ln83EQWX7CS,pcd>|GoSO!ǂ !#Mtmڥ۟yokWtk[M]v(Q(컮 +E>݅$qx\x)ϯ= lejIEHUvkRbYҐ_ `Wf.;jǦ%Q'N*KAKC!& ۵|*(ʻ찃fS{*cb]%u ;u#+8Ȍh 5 0y_߻nBd*YF(aHZcE_}{3^oAtƏ _0xMF!Չ'~G-q =J,$]GP_6[h. WMmכ`$zK~n݆?f4ĥÏK/tDh}lr >|_5Pci$ۃ@8_$wWCY KeƳ8&J,GI($|NZG̑M.Uv\ٕ_t<՗۠կcdpos*{p5K%^ !Nedw&2Ho; $J]^ V`+1.ЈcVWsqs:]le A!W}^u=%)дKdN>wya9|U~-21XGs/9f^uCKv**}] Z/Wc>\_;E& X%:u'a̱ѫ8.Q5?̒p.9 h|$l2krx)U^%C:sٯ^ӎ 'VSlH%3+B慼<#r=yW&vJs|n~炶z"5Uxگ Ϫ𛆡bum(aםb&:\ $٣>  2 y6>J='H82#=ŦbFK9(Ub+39(`_g,NkbSr\K67q4wʧ# sTCVggjqi_L|o<"6xއ6[ u1 iy|u:tΈPS|l`x"P:=ۓ۳ZRz[f/l ft~_d5wWc'ɩ~4^{^\ׅ /&@Jt{ '*P>;GTJ&)mfOO !Z@zdD/׎q9V"܃ZQc\یa3%9}Sޝ~`:xZf1\֙i1=5ߝ9%QF()]1?}YU"XxIuĵ0'_!!llrLOWJDŠʂ9&c/sBQV~{F4f?`cV~z򓹦vN3QH~pڛ)eߡA9o&M?l%N=8$Tnlڨ_+$èCqc?tf;F:NqOS݌2@|CKaIH7)U*I U3Ԧ^:=I9[%욃Ax^wRjLuو9^NdR@VtL& Z;eS0M'|_Rj/0(ZIasq;]7 iB.7x#8#Xb]yg IX>90'pB[q?kZIC?{/y2 + ߠ.v[GInzYQ3r:ȿنhSYp9S~zpf5zQ0b{ aHjYp (bùrɘ= s¨+ x Ō&,RxD29 @&W}R[ݘ؀,_PZ#PJ=g/M$(H `kajaӡF@JL'] IMm]Iidu,G2GPmޛ@Up'Λ N9yf} &EC_Pcot;qكzP#OgnɜeK1c6IH@,)8EN]~"P5уL ufݔO7#ɺ:=h@GMrsj~nh+mх̱t<=v/;ݏKny[9Sapӊ'R~EJSsգJB?%uM"EȐY 6oVєpMšCߝa jX)4*>eA>#B{jqjGHfkR C/2Mј2Ib\J`[‰CabŠ$~xYCJ GW8&ŋ͋_ faԪĀ"+ҔFn83AiY"Jh֮3[DO Kt%Z>E"l2N2tLBw 0[2o낶{n DzLEqs`:K^nUJv{5 =Le8%;Dk9aLF^.v&a2 ٛKY;R+ɘW۩gF| N Γet3n5 D.vԑJ{YC#Ń5.Vupx 4˗PL<#3Ȧ%T{(>C:.1?^yy19ħ=d& J~c_v_r@?iۣhXkiJv'A5\as`$Z@!1LXtLjtk:_I ]a :s!O gI!0%L[S^P? '[7LT:ӸA;X*|_&'_Ӿ(n_QVf|D@Xi*6ӈ@ڮu ̾)=BM 2+CǏiWޯ6Ȭ^p-e$3ˍ֍khQBDL.UMЌ\d>O᫔{CuK$9 O] ևߗ&:#J˜Czw)w$~wp~خ_T'a4 jujYH[vrXWHHJjy4t89>m2G}6λ?Q*-qk9AK3qK/2ף^eBjLJx|a y@DΪ[uyT<)QϣT%pZFJ-`-@:^P픤4)pnu1(gUyb ~\M16D^ևu=Ubkv>qpE;I g҈fkPQvhXHi2MT6cq`q@Q+g D CbgdA- KLtbn 6?4-ץ~Zf6 ڧQJvE@閂8 ͳ)} (iԀd)6˨cj*ANL#^``Cmxax h8w[쉙ҟbm,u ^a~g#n(FڄlܒDnxgK/wsϴO0Ď]C*MPsʐ"8 )a^jŅ2&p+ &b26~9[xY$TycLşw+Z- ;+ٓT푾hUtX:^;x0)e="p7,p-'$$`/Y|:>֚dM8KE1|Ü'|2 fgK Ǎ(\bc5靺zi*k(d63ǴS* '|;ʊDFwqP)\p_oS4˶#}%ʌ^dLV#kqz#wbOˌRa0SD Wƺ5KdWq|*1@ v\FdX6z^7^ ^VĮgDZrA]S|,3 'Tnm&i2ꈩ7~-|`Xoi>p@H֞,i @G{]TEtv`+cuKw՘Sa W!ύC5NoW})~4{c%]:]2AsIX.uYm\U;x>ǽ{~_jexLj F»]7R(`ӋkMbY ] er NA6p/,MbjG37ntdYJAѲE dX1F j2@EBU LnqMRDRe|=A)u)OpDiS^SZKloHӲa~BXJY!4VjP|e TM{(0^?tGetiݤ;CԁK0ri4[7<8aWi /`XYQnB}E˔3B`g_]VFAVϽ >J\EӜ{>SЃ`/ic82`G.Р&nᰶ/ [5DaTͨ0v=n-4@$ָKrxǞt ϽFj 1[?&C2lI zM0 #m906'E>h^O=(y`ەI2_k,NF ;{MQ9wx7 ܀*?oGV,Eb6,Zޮp87~nS;ƨ<8qN`yG G i'bw16-q=$?tK&x@Eṗo+mbL'JP]pB^OMMTYXrvh5]ma֑&l=yE-6>&15FhGhFyW bٿ$N<(&$[qdez8=:{:;-h B̑g-uXdH%kX#FqFo)eu~*7mhyj=}IW1&+CC0X@!8? Ȥ թ>ՔZ2_hS],$b+@SC؇oֲ wYL)+sEjzR<015S)SH橇AY\}Ϊ}餈6vo5-U5GQ} 2FT `]{\"Sb@5[m#bt 󞈪,CͬHX&L|bmIש-z5~S* v +b+Τ8oN ]óU[xM?߶#HCъœ#g+'ԙxOӂ1I"ۘ2=>S14I)*7p$>{piM ^^15!]@k9hAD xI7dP#yAX&Ș&CXVtFeu-`( ]hدSIHY>%vEW0 ׁĦ4I0õ{KNuĩt۱H>GϟCГ SM2N##]Cۑp%8o2aYjkT$@V̀m;-1jt*x^}@J o1f-nsifaAQ>AG:}yw'T+6O_B`;^Z\@Pv\с{Kry;Szatue{QN9OuPѕ]xd짢8 b.j+b\7Ө0;vAng݊$yz""-]{yv&?)40d<6l !6;?K%X'eRCgb VKti0Xa<(2bPr}`:g 4;߬4ᅏ֪Ao\3`D'y%}Ҿe|m%PaE*2| -r tR>REy! oF|-G,X3x*SvB1/5埽fȞX}]IIvȑ˞{O ]).$1lj͢4AL1i7gv ad~qu7V{,:Jao|S*".MYꥵw-Q_X&=,68f337.;ÂghܑNSCJ iV tma LS"1Guqm"M%*}n.QǂjMN=@q-=N|?w4Ayz57K=lG\}Z 9@?y%q2uw*B+-g>8$?(*9_`SY+clWRn-/KGHLj@K,\!4Da#dlA>!W\@ 7-lW.AV24AEwznU)u'Ylos ]}[\*85a*Бb../-| ݁y|#[1?q~+4-Pi$,<8Ir q-~p\ ljWt,j33sO#]'iN4*W'FOk;G:a)iS 1Nm)1vSNc6mJtO@V#PbdTwX7PA񥦠1((>^w'#$ˤk %)15]@vMv`SͥF$z9HPKQM,gA[##C!Ѹ4t%~+/)Op4Q(@y@r y TG;\trXMVE `\ EO*:Y;y!|phMٛO)A!oꏗCoQ̓@X%MB . s֒a7BDY<~ۿyu#:;9[ ]Y|bدҼiLM_gv b|"#&}*uqf*} PF^ il9>vh:HF٨iXm;>00|ߢ'${qg#oX|B[HAQMUO߰. UtI¾d2㤸@ kUZp.N IoqZiPDZG؜&)Z\ DKpqÕ7ґmH؂Tpe;)9/gRg=poӦ$}KV-˜M{-48(eD$w"[Y->O y'oߒ~gvD9]<&s {=N@\2ZEj2x!̩ }c˦n¦ag&.D`o != m21<0&Cqz8 b+oz G4@膅̱֕ @71AdͱgaR2e49Xu3&y~A!8QPa?I3D[KdWzfgv˟,bvg־j/NCJwF3-ʊ%]m HĀY%:$H .v']6jysZ"PGX8~ȼ*64ҨqG=1"&\9=7ঀ[Tuzgag*ww:b1@ߙx'r;i5Ym9 dο$N GG;YN^WHI W8 \$CmR4} V'dbj)_G5zXItϳp V=|A#[ D2tp/݁j/R$/_̶@9X8 ?l+!ٻƅIgԡƉ Pь`FD&/iDɠ\)Aaxyd+I5qf{"ɂ)dLoyvn@f+ȐA|MnbKROH<ru7ɰzRL%L>{}!1#6E{y TĢL< g)`v]+yX`7v# rJfT/᫄t1V ̣^l?rUN6>,&@%Ffu6 `Oy|+!i0`lTZ8OPX=N 19U:MoeAjˑ8 y )SCRSΝl?V$7+~e ~bY_*7F2.26n@wb?NVI'LL0lb.e'+v77k28ha:>4H* F:*Gؽ 6Pc$o6|v8}4&\έk ï| RZdYzs)Գ.+0[EPI5 <*Nune yg$y>={)A2QեVIOlY|1<%V:`;A) 7Kw*D*(jL''~/3hv.A@vR[8ͬ5|.Sa{A]gLyHΘHH5'7"|+LEC%W];hQf'ѭh$(WQDt%*whE $y`]1(+۠# lt00Q>xo.vuK0"W/G0ߓzHG,Cs"R ̺C< y~LO{KjG^p[ƸhF)( 8`>P c&*E~zw(±s|PWc ?+A JjipNk@EHzzi d~ '/vek[l&UwH#iTW0$ r!8&^zOA[nR7wĝxbjoLOgdӜj:|zՓ̮C*^m[dR1NY"=m90UT]`YE39θ2Y-5""{sbAtW>nqMy)J́pjƻ8 12$(υJȚ*Àz.3JCd^Yĸ()h# DzV9]1v[Ou+n_va]*9|P>h1~zlI1Dq (w&q~N]>4^f_JL m;I+E DUۀb+&h]U@^*-,V3w7')sw_IzoF2-b?ګ}{?ux[gM/Wk\Wщ=saHk` b ]ŬfG*74|> ̚ey4Ra.'fJ43bgc5lSE%/^V>\e;EeYXT@8'r,s cn)8vZoI\:Ɨ1KήS,8L$}' G0OAGQeJ‹FӍcjU!iу?:rJEgC ".LȀa 67`,gqEfIJ`0"oLWo`=R~U&W%]˥e1ȓp!ӭ%IP`蛐 T4װ3`oiա ˶ IlǹQ L`SMVZcjvRjFF&e wb\[@C qHe_,(iZn)(!0`7Tn'6E$9=L.`>A!'1[еrJcE{nD@j[*$C{/$ξ0M2*ޜ!b] uOwT:rL~"uCb}f32C.{M6 %hNd"p%# 4v]FGTT_V3Ty%8Q"US.j-uxl M!'h eqEKfvUXÎ[?f{{k^R S\ <ȇMHL )*NPth;K7~0wenũ7HfŜrOg Y ¿~ܨSf3c45 ^I.,rx1 1c!l-}KD5WqVk;쳀Y~}>Z+GaZ/lXSe0Mz"zn!3"~d% @s=< Զ\MLJdO )JVprN0Z΁:NQ_]Oz4l Mwc01KHXx`vqj1%ŤAuY~y[$ SÇoiB١iSϸS#ձ0'VL,P![>K`j*.P56$;G fc3T44 n&4v8T #yQ9 nw1H"nEJϠg8|2#`gGYݾ µ FNlX/v샊Z{lЕG](8M +JD|6Y9˫ǘq+3 jAbBdtE6_U)Dq(U u'-҅/^a-!2c*4Gy7LiքL[Fg`w cI@.e$.ǎ8iCPJz[=6]I6!@.s;2ؓ0|gnwse04vc PwlL&w^1EnԬZ^ 3 &+.@߲#l*nDdHHR%QME@~ UMkNẒ907x& vr]uJ@#uJVzdZ+箫ts"]3xd/,A b6f;0%R/fn8–Sc@ny|=ֹЏ˹σy"w AJ>e!6U.ZY&Hg_[b835zV; %F.8 ƷI&#wys$$XAS졹~tVeenII}]DGHe '\M+V3 Vji|r~ߏֹsW'5VMU'*MW0[DcqkS*kZz& 8K=g!滑گ6Ē0ir߳ / |PH0ݠb+J^NnYU 5byC!Nt2RJ6*ן;{]SU^JRLw[xנhVJVjꅫYW N)6zZts5fazmeHwVӭ)Ĭ9ͭ:Z}. =7SEn O۾tei*e\f:-HܖwԉfOF|7B-5]Ƶ/0t~ '˄os/gވf]MS$Uܢtᐒ\Tv ]UǪA')udfM,UJhVTS`ci6}E4t$!]WXH耼oagEiK CJ {+QՀZ=( S6_1]<Գ(`GN 9o":5q $d͵@K_Zҷ{>Nh78gy]g Ԡf~Ф{՞$Gt,XH=^g* 2Awk*Lv.>¬r}<~#Wi*oȭZ7!ò#J LTFDnOX2b ".x7p]A!t(!{P7GD}W-ZqOx7c"9M |%.|>u!'R._́?'d =/ϵUɖ х&,:-oہmxop+8t90&Lu8JdVhIaIش'#L0`57sWt۩rβ4n11$ A{ʹ>3ѹ=JQ8l:nag0gWO'KZDRv+N;2Pf\=' 6hA-o#񓟪=~)1$n®׶wK'njmGwvMW& RdD؟!/+&Ϫ~Eo(P❪.UA.G藉+$kh#fkP (E˻" (PL?t˚Tljie̢$qahƏ Qө+'#K)>d-'znӓzEP4VPCV:v!~fk[]R)w &k/߅}҈Q{~G^L˻2G!j]7/#C |{#8~g<+^L;Ѵ+6Mݼ b0n9 pf*2G *b/o 挳VpB EmY9OʽKÑ蟁 63(txv )mAXxo+s m>]DZ4bi,u1lk)EejF` ;sE"#!߶^W5SbWgq'Y!$Tz۬N?д-k4e܅P2oSR{WIg3>Α7f+(}V<NPg9Dpn5ڳ?0Jsζ+J" 6RԅOURhݮO<??-.e$U9Ylxìc`fɚցutVTu^z9_nW%|r|8WXN[9:ϧ3V|ʠ~i7d\h1'}ăkP=+=Z<XpK03L~ ~JtO^(?iXP^U]ź~Ӓ;`>ўO+*ir훰J}I-pLB%MSA;kjN6/hiG;$Wpgz?"rG⪕Ū:Da"\F0ƺ#1v\d=iɠ;=Y9oyv 0Ķjud:4OyultbPZ6;1Ed<tŀN z2|beQ̽K`BJTx*#&0 +@rzOWĨnN#_!Me U=м% uݿX#P%[N]j 70b!eqX^楧79U4ҁ*>pr7#;]3e$...#PHQF: /x&1)]iMͽ<|wL:趡n^mM A ~tp K:~}tQbNWۅAcU Iﯵe%`z^.bMkrD*:z kq9钚 *W-1b /=k yZQTP=a";-簜AvZ5Ͷw#zS$U8 hK嫀*0++;.kY@e@>`V71O;3Zs՟DvH5 t:=,yF:i(jkbPdl`UWl_V <:5hG!` lAo_;U\5L/0޿ ԝz(~ cQUΞre EP%Orv}# g C4Kki9f()YHk玘C>ʾxƋv110l,J~rvC5XO9fĩA \PWrU6笙'}Us+<u!Hf&$N?$wu(ate;ΖՔ',MohiןMm68\>uZD>=1Bm`@D!c>,f;0f^rX.3d=o r>רOßCQ܀GOBKߕ!}jk-w!WW0E_ 8Q8ޔqq 0ŔJBzAUBg qyWxw(W 1yIN$@d.G!hCa_"^//íhQܗ+QW(^&9Zs[{cVn %On>2BIƘs:dynjzVcy7k 0 UYGkjKF[[%Z3/%O}"HaVDl}uk%kX%@KV*v,cū{$fW);"sU,^QC[Z`ayFJN^zl>dmLDc"Y}:ni갲PP(Lbq4<и ?a[Eڏ#I'uk ("\yycC+`x űic2 !CZ9RdZE."b=%~=͟a~sjR2eIq<^?C0ڬ8xk7>Ƚzn^#US*B,^_qbdgFxJ\HK(ĚڗXN12ҭsf@B*8.\1\ɍ~>Npd" V1e-;j/aC)Aԫ67]M1>s>KȠۥ,9y*Mֳ D,< jbQoɽX,!fpmJ.@WھٲVYqg{44r Ԕ)s%l ȋ봺frA!:yTË0U4:/Io*H橬y> M&潧vb\9k^ꞷޘYUc :#d lkE~9O:e7\aXGd3Zķ}\$y,O*9tXTW 3MEqu毂^ɤU-,>忡+`p_@Ǜ^X2?4tUl-gn&湐%8> *݃k&$M^ qh>D|J?J:8bn~ٺzui q/ż~6!HSq0nt=Zr"M,޷fO!ޯW~̟U do/]0eeF %mA]i0G>(Ax+/ˍ,|b=|7 ]LwؒSc"iz?oH}bͣ!'^D-G] CFc gTO(26a>)Jn}UMG sW[P5qN] Z 2ٺbSS\FoQ5=-8uhp܈ˠ},e]T|նvrǃVReP"92/duF1UUW(,t#JVZ*+piMZg[B]!C"jS7>P<]ZⱹC=jVBd9- BZgվ aBY.vĻ7yy`Sj,1N1剽޼LуmṛotmV k,D w g:X݀Z缾84ɣLr"@ $;h~ Qpn lMc7:/><1|pB K V S,;sGVɥꃝw,3$=UiCƢ-ڪ=]]}72r9\>`,T0PIoO2@4q;M 5f>^Ȉy Nz3*/ݎK9?iqﰻ -|3>8].vƜamO`a/訐rJ> P;9xS\pHtP35V%$Џis?72A- s4}V.֛=q #PLƇJdY`< =vÝp$/MKV#Yqd2tVhKFXy!t@+BV 4 ׵u8W?i݃K<=-ћt8į`T}_4[FiLBBg*BG>@ƭu_lFFG5cOm)%O<&~gtbqxx>1SG(w_QH[w#C_~2Ɠ"t-Qv=):w`mhS &}ԼQ% Zi~-%45wMھA!u&ټU@NՋ1Ai+W!AeM5Sr(Vsf#SL,G2}_w$3ے+;%TRboaHF`Hz {|HR6eHO-8Zü PCxx9 #,%,)&|0ppt͝U!d'Ë ڽq MW/b(і垻R|\5?Qe} WypERSfƔ@߫?{s6z2Buиt@f<~;ΨLQ3XTWjf OiEb,d 6`Ɯib'K)$9sSotZF,!ؐ"oVZn=O $Gߺ2˒2UUpÐECP點Zxh@4J5Jn+},%oEӧ)\<>4+̱ .[ҿ^ݗ!Vb9Å=#"09Q  BgQ8c=ޙnM1 &j)sO\Lk%^R$"T%PTдv+H!4O %ާ]&vUp i*Kudf4q|4aC/%e Ζ eV46q6 }J"1A!5F(0 5ҲrqhrМ Kx[nH[ )EmDZTJKI=Ρ 3up7p 팓6co#ǦV(Z/ug]2JBc.|W]%%z l@ 䕌?A*'ڛѧ1TQwauiU7”8R'D^Y?H>xu=a #á<`X!لUiCAё2): D\i7.X{ɐWR(2`i21p9e;'o;,l9Y}ڭu ڍdN~DXO2Dzf\REy^LA1Ε>7J'՗UgAS&s]k:ਯ(E оbJSmpNC^>|sg6989↾hnoD84#7EMqʃʎLrtԄذ8٪slC>SP"Й]}%l?Ιd rj,Feۆ_3 W={K6 ! FoJų'M*d|zhf<Q[ق{HI\m@Em2+'Ǔ2 {v1qMZSv߾(8W(hCHZ!;^CxP60@o!(#O//>\a.T14aV :[ՓZ.dW5(E_/ȫ$7j.Vz 9<~ܝ\vP|-rʁs6ٝ6̷;Կء:*n츼}.Bœ23^ @2q𢖞 ´fRQ."/û߀ZqM+iu/!#j.{TvC /l:'[dЁO֛n^aEˆRV'@c\,>saM#.yK2}uh$yeKxjmKP/[Ii*|YmėɟN5t^D ~žt'iw`%K3ͮ(ɰ;E2Z:~o<4hfʼn6]NzŠLWiGŸ&ǐ_Ɉ3fρ64?Ed 4ſhFǿZ0!ABamo*"bhۖ$̅3G%%e;qTNr///7\f j!"}pr* F[ *99f!/M|;20hi.7rrF^iFCÝi>T}f)Wyy#nl(P %$Ԋl7+7G9e93"G%gz|RQ_.5B;1Ied9s_?N&Ȥ]ЮhnO Stv3xb:00>ah)XV6t+nYBLFP)+ƉE]| IqUa C]h y3TJIJ_oY%b)d"Y&EOdT;!ie$K!^_$R4p?Ho 59&Zͥp+%72o`\̣xW1R.n7*]R}"W JCƊ\HWi fPWKs /5*`P S#DT'YWӈ{5v9 zdNE"Vg.,'4reB|nv$zJ5s%u^JBFSnZTNxWpu#E)ʾz@lrGC xP߫"s z0xHZˈ[WWnD S|S1>XH@nRw^ l[chKN[,iug0-ONfƚD8v*㉉y.-,SkK/1JP) |RTyyӂSwuqQ)< +OYU܍ f7Ovw2gDs^s^qAӋ4%W \ Q0wN1wP~ÚIvSNlgmwzy>瞎CsD:7J$h7Ƅd ޔiPK -hޅ¶0܍MWmeM`n9 g_1C".4NۛOY1"!0ze Y?ݽY9,6B9@=506;=uZ(I?d0zC x=xr\w9oeMB;OU;2"aF#/[sNGE..j?y"O2+(]K{4Q]6zj pHSX"kTN]qKTu11G&4[hv<~z1T-Vp .0qޛͪ5G_*8#}[߀I]IA\uG蹨߳svkHc>rp*PXrV 0:@1nrAN77H\@z3dɦKdgRlCr4W>eh}e̓@>sh `DNI>ܸn{,H|0˲1t,f.E1os_.7;+@GkvD·d\ۄ:(?kBε@djkZ'xwvʫ +_Z0#[0Et+v&e‰kU'#A1OVHvӵ#RSKe-EV/<]>wq:cPu?]#C ' ZGrv?HarzS)Z} Hanyw*QZ(k⏊ɖI70bBN[,c&ȢBn'ۙ027j5x_l wd,s8%SN Mܖq0`su4"JB=u"x7(>k.CYQv|*?fd%AngEb#XkBݗp0 &gܿehױSiX+rIZ޸k(hA6v1OT@OC5%P6<-$9RL`3/NndktD6H@;u3Qtk<V;_Lr:>xW vN۫+MugNܭd8Mx!..ndHVƂBBHرuә%sX7OiLںrxLhG&2Ce06AK&#=)!tކ#% a'Ȉ߈(%>"P a0'Q!im&8eԍ<&kA e%/`zOhm5H[Ǭ, ́S?#ARW_٩)_q(!q+MQ)=LK|Cu#s.C,VJObBwIӽKy2*Je9N7TNΑ)NGt!rXq-2X~ /[ /NUVYu_qZ澐]N'Z D{;xqlcr>+3@Ss\}+6 TVjF;Ze[ V3c;N_n ;Gۥo?liȶoQd_0& qI*.ədʯӽ#Q:`LyyTyE{%e+8ǣ s1eZoM:1PH)*çR4Er @DBJ)3evj6\t ?Q/?Sx3\ѐְwvl;~#+(,0ŝ0ONZ}u XSJ#ud![tдɄ3:tʯW5H٦oHF],ܺmʂ+4aNߵ <3hQa/rq=y UZ dΡcU%˓@71a+ial $b%?" i;P sg9XZ_qNFV/og[_z.;ͅ7^$X7 eA"{Shzjj@?(|J%3A(Y !K\0!Q/){<Fo B AT=?\;M(`+J.QbmcQ:hGϹnJ\_龉 \eP|`+BRDRdbo&[!p+!YI1B*!T!0̺%&nu:pQ Urq3KISCdZMκa{#;PK' L*p0C>K0uIw_Jl}YDn AItퟂưxn.E>JƱ;29񄦥 8e?YCfoKtIMg7#^OƲ aibџB[+lV h\rLBBSxKiB"OKꆦ57&q"֬z8?B2#rX*enI~-ȇ0叱&$2ȯh7@|mU@WfR'4rZf|Z{cEfCwtIKؒuSHcofix7~\Nj!j;Vp4)ET]B"WYwɔ,$B5ڒaɇ :?{`Vi?ﻙZGdw;Py[asHyo? /lf ra7 y8'pתjRt mRu>=O"; .)x.6Of[yfk 63+S :X5D7B8T;\T/(]㙐 It4wݎ;{7v tu / J|KQo$C%aܭğ d>kORۉ{ᓫDRYh]pR5 /bZ Cᭆ+t E-8׆ibA6E? vʽԽN.S‹R_K+h#%?4xpS_Id P0l-zX۪:*{V+GT:Ly+’Y*rnV`5ܚ5r7RjiCC*V!`JN" 53eze.KY{9LbR4%.ЄDHXȒ;0 ܵb}M7ӡ' "z/' [pYHD/@4S.q3$'99cXpA`~H|}/5-V|,e\Фg$_؃zeNt?CB?3u8~@)*b<=v1 6xꝢK{vY.y8ӓޒ\P2Lm+%XF)6"vI:r-+*W&޶\d؜ot2' \.|6*D9g3*XTn*BJ;`g'bN'k<@XtyB:6|@+5kI K;)Aq"^,$Q&,>uTS+.0̝1 m2'#(oE;T-WHg*u&ARuߛ\;1s!d"1%=v L9a A3J2)Ӓ _2[ CFʨhGIbkRd#ci!04ZnRs%4 -]7 VV-\į(E5e^1'oJk )!btʨ#8`!FTFGng5BzI!?Z0øib-$*dZ _1!Mr4j*<͓n::b^&|_A?5h F+GZN *DIk|\l n+d)Q x$q\XC;Z-3 D3&zz*w`k 34jzĻUypM~E zV72| ObM[\ũ4v򗦊`(rjRPXZ["ػMzz;+b1/"a߃sR@=;ߥhTOŬ81t*KʜbԖM[Zop en7>cܴ~c‰IpP%f u5ӷ^0 1&ˍm PsLwGg +=_®7F5ty g!ĐnMfDsa^]E[!]i(5W =)߷+4ikUEQe x0rdHsn*;O>h[$lM3@$ˬ*7k;z:I9_WԨYc/R=y|!R!@b$,k d,lSH H}bROD|]v '~L)G}hwi ̻bLi r dp&U,of @(Yk'v8g'_pC?elZ&NIO(ܘv`X%;R5cE>P)965&VCVih3wĎ,KSY^8k"8Cx"m( x0W/nude;aYr.o.jJBU~ .>G *RP1 PCPV#%<t #m)*Wo2D|:ScEgc*EmOlSn޺oЏ}.;$#uOWf]hSK`}iMz ~9G$v}Jr ɣK[3i۬%ch`}UԵK-H# ҧ@jI̥iY}Q{޼ŽLFQ_yK rBk?Iz Gu[z`[}qסYPۄ;{NJGX=HC#,Ǒ8G2䬟fAe?<{ av+K7g%kY"Т5f>ڦ5],SZX'ekl?vb~,M ,'')%{f]o=>oH1 $Ј#AuJ1B$n)ǰ! 7;ۏlmǜjf̑ȌQ<`!%q+̰8jIzDt.tdXNI[YVZpn @G- 96Vs4w[q=K g#{qhn[&t;Hvs"3cyf rS"}7X5P$7fQ%O]{:.&>jЉz^x좸q K9_`ڎdNK&Tb_nfpI =s?*Lvu)ؙ<=f A_W圸ݒ]0yACnVLsN&"eډe^Fԁ|I0 Ǧ9̔.QZ@XT9qH$,qPsL% M c"OYm}}xqq5%t=8ԘVguGɍbɞ;׌}.CK)O-mm%ӮF( \ :sճ"(mj]ӟ I &sa&)y͑*x$ ,q‰.< XMeҵ7+RM8㬔kkbJzN54P{Vdvkp dc͈`t-G i);8?4o(UMuŚ{4.'6"y.vV" ۵fe['qD0̼G]&zuLj4' AA~'3䄾xf]};QTPSaq9_':v$GQQO ,+K. zbi k1\0d_޴^4TY$JAc|;V-~s9|%BGPԗјcǯ7rYRK`Ūw?Oݛ4ZR\IuzicC!50LE!Vڲ[ʰ0X~ .Ub.1/[g g[&zjGi‘*p7S5Gpw!U]_fT߀$k_ Q`bʆ( zXg jtB[Uge-},2F*`ɤSHH2k<ͯ8h85 N3W@rfn$pvKq!6bZ2Q7×IA}oĽى86K>rVXQp э`a^*"1{Bns e+ݐ?ծaS5Ǭ,e8(f25L ;1½CȃۯaDR:aUCk.} 0;arFLlCFyhBGIB}7H~ȰO=?k.#u!P&h /f<'[QI< kluzߣW>:<8 t 0ק%#{ĸUO;z(}?XbKmJ 'S/8TZKH }B_ٺ3 s,"H-r!UIy7G] "O^SD9Y& jv Jc~a;"ÓPn]#]$Afe6!eڍ ͻQgUFt.! PjQ=l߀6A|†󷷲2oWnh(9`!G>9_qZgGuQMw:aD;+rD,lVRruÖB>!jhJ.C7(*Hy(3Z 22.rH BȮ>LKPLͳfeN o*ž)d(nZl4bŠo2iE՘"cE{AØ|e"AS]嶛&Lj3jPθyxv (WeGp_sn*,Ns\RIM|up<䊷 ,WT7! !9!I4WFJ 2̧T 9o 9 SG-OW/ၠ|:6"w؋YDDO7 Ws w:wȲY䢌n9֝x<wa+딿<U<C\G,LzLo{qW.I9E8f&NN1Ӝ.׈\4ea=23DAoAX`Jf8%ӋH,Gٽy3HcΘy邞J]0;&ghJva-xޡ C:`A(O-q3}&J`A|f!eNU^=4YͮM9(Dֲ$H/,AW}o}Mg  Lp!|(mqh䌇wJo A"F'4궀S x*ޠ̂1jQzw- rΔpS  E׫ jD auiC`UFhG|۳/1svZkV){@.)]TYZ!t>ZA tOZɏg(??ú+q __ˤjXbKޅJm V%n=| IHY(XP)>4'2G:v5L;7Aix"!(8>^g Mў;B$r݅{WR;_;JAe֘hq\,,."_AS!s0}+򡵫M咹ms<:{)P-L 1k^=3_EBB3 2^׆F7Vg⛄&UlZ#0lG\N+-(&qUAaPg)e ><) q؝Ld8@Uf ̩F@>Rm԰OIw@4bdY0iD]rHq>O絬Lø 'zՄ!&yjo[w/jgm΍@e|KqzLAҊPqQ @A]\JZ|>Ulĭ)8z=(m2a_o'0`ΥJ (X녔M Lνj3d!iSU*0%K~Fx\_2$bHU^Jd/ ǣS2XΦk'4|GRLpF# TLo%eՐfw 8N@iiW?5LalL=wYg$\rUH45&=/d=N YQJ ?@85}{2P-R= Nffdx@ 1UÏV,ĦLTh|Y'm{ 0&ZZ]|:LKs: g7.(Z| Rq?i`['`IZ2^]Cg6v.WwIB,q]$-XOgZt0!|vcy`;̝Z {uKFaiA%%#吣r+!QUԪ@ #k9^RxQ[`y ɘXsWiCVgʅJ鴉YŰ?QY4FvMo7X6z'~d^xtO_Hי&?zؤ`@aE*d",ɰ(=Sˡ4xs*WeC2i}|gEe̙t:=Nږ)=pп "ʹ:?ߝ4`>)|*?c;y(`Lyz-'rIXoЄ2J;>9M}@  Ӕ{6N#9QpA!dV-T lWF+|L}Voe/7hw~zDfd CnKz!YV62x>Y240l/8S=LHLߐaw1o&&UXnBSEi@:FW۝o;oO)QSQ #!Nb]hOJ-۟j!\ ˵-.Xj0|(iYP#4E**'<)c3j#L³qOmGkv"2]Ǡ4d ̸f(R ;wY d^<~? Z )pʘ^UVޠ kX ݱ`/M;MDN /%L)TW\> t?$ ֒IN=D=:N$uE+)ߌM5)V'H^^JE-.K8?YRϺ^p"j@x~.#Kk?0k٨/[߽/޺ڨY; pp> ^,6=ʲo d*[0EV٠bLDNG΢qDA^r=9ӸC.xH<M_9`ǯeȞ E/KJ0԰MՂC@$lckAlb̛⃈Qŗq|ͨOLwq화E^{IDVf N_䟰*J> =Lng|~؄q~RT+'^(@/~<:))O <8bܤ^S F IN,"T |Yy aor4J#0< ߵmdJEzL7I'.< 蹊!|R#4P1  _Ԭ _r.[/%̠x\P51ʅrQV 2GtMoSm?H֛x=RgK|C3IPkiTKcB7=(,z5A"f;} ;j7qė,܋fhqkCx!9mQ)*G.띆} u\M7-kt %CM7^!m ەyߴQ*Wξ̛ ͷ&W0R7Q%ddbGhSR#R{ÕFWN5.iE&/8뇾-EN^I;&e6]=Yyr ϪQ(౼1c) ?A5AuETuM#]/kxzZ(cO% 5=ԑ{myM!G\he^f!c$l~񝕯~Qd lE,?v\CWܙ,+goj( b7ۤjĞGɖ\bY2^bu.5CXrSugے]N5s꬟:ahמ8'"1ג~$2/?Ú= j5fčV )3qh=td,*OIv K~ ([B2U,+F%0T`'p<<~z8].,7)Wӥp>7 !Y 7'ʈk&4Tݼw8scآ' qM!joЅ"+m8ɓjR8%8Y9av KO>~Sc'.F44ZWj}0(MGa@f %rFUf;.>ڍ֬DOسU?䐋K B mh^2[=ݵdR9G6r"5Xen\`$Xcy&30']@cLY{*=G*(9nþQUIu 2 oDv|i`! u䫅WM1ZbRYв0ϕ8gRaI̬/MNoR hͨn`(X*Q+89e]؋aY +Nd?3J=@~‹cghU$j*(y+f*ǒ32a m'i. dOLF*vQ.󋋱.]C&(HV,.UXϬT3tQ|zPTTD0+tTlLtDb)1Go&IcsVYFfs+!&I M%10z8+M8S6mTA,7|nqh|9 ^9@!x7U?dtuK'n cg KJ\re!=W*Luzi#;)fڣ8q% ˇ wq0[w[J\QP^Pg4} 6>PGs .y!ut~FVLlo"GM)!&RIL+P<T6yF,pqw[dZ,58JHF-6߂z,lᱣk\\gl6o _NvqQ6V48 l0R p-ǪiܮxL(BULÔWO(~ՙͨӒK++ x<˗HYw1\h4;7_sGye] i:[Qz,ݦGt'gJ0.-}r=|Ll:e<¥B NzL "z 3tPKXTFe2WurpUӕ {(I!wjjA{]p3z}m^/K,۴ڴU*ZY^O_sOd J͒Y9#0H_lTE] ~QJ\71{KZgw.u 4 a$q 1E3jqZŒLḒZFVT󐨄 kjx'Qvj5AEr2BeQG/ƌzsʈ2/܃ry7Jf^C%SYia!6â y* gDć+ϫj4Ye.~ۿX\vΦdNdy𤽗}R$/μt3Fs#729.S]awUGCē0ĵ9sK"7r%9قZ6 PHsMV5U/mog쬒5Kʤy%\K@}:G_=];JfD gZq}:D0Ӥ@+8Vq"C—^0TƦAN܆ݧv"Gx"B ,a$W2|ihGPp.`΃3Q/)C@Sm=4Xs{ }oQf#2VP_0su]J;m5Tl"n'X܏媔$ltؿĜ\=$ntGzFR2h^u kn;,|X.fEo왦'9H(f^Tˢdz2b9 ">]X3Juɣf~,HEhBsoVeM-@jmN| Ru%_$ kqF^L}#S5f]t:3a9ʎF\\9 =4 _pbOƖ]j^ f#*; HrlbO1eK3]8{]zwl0~BꤔC4,^%!bc^%jƚ\ ~#KgRħS3;},Rze28cwx8~TU}˷Am\¼>R[fʐe~ U#1گ\ Nj:Ü3i3*5D&9Q!u{bT4ΦV$/# _*S_;`c8s-'}^%o^zTZ==]QA'.nȚОc#E?-ZWJ8JnBXʳ 4}s~ Tع,#65[!բ$fd׿s(Yޗ rYo^ȁs4Gwme`LX i*ǒi gpV"ۿ&u16,8#Ь]KP! HF˚wqZYa۟8("W%L@p93ަ]܋?v^"ߐ=Ȭ V 2b GzT>'G(ՍemLpE7Um]$PYPEG=m"T*,d0_H'ĵk4(Ccl^ܑR [+]褳'1%CO.g0Y5v{M&T - T!7cQ r6o<ɌX^[5ZP7Vr|N*udIxד=ˉU2@Uc4)~jM\~!JGo~ՙ#FlTK:*}"9"hgOF"V*&1Ԃ,aI"*JcPjKorrE_G ܒe_r嶘Y|(K$F ÆUx)=AEhCAkĹb ^ PZVټ0L+|R@|́&yS6'Ho+!Y`BZ-cqR J3oE z[&n1pU$_;ѧNt'(TS {a܃)qVA%|,| 9[%-*;oiuWpRv- XfJOG$7"͍<~zgqK藋ckVT=ܾ5/B6l܍SQW{n~G(@!ZiR%:G%z"#yY-JH!A^J*'@hy]"NK ՙE0wؼ*5O-];q5@|DZC .jka!;km;= "B*$'ȣ|)US 4;pVVI]oﳫ[.V3ms?-nMGSE>"W*8,ІH MۯQ65unƝ21vgVcn;N-3JGq6(0_ZRg2E\?7Q2MMdVơ SpA2pWV7|*GB쯟MUI[=+ %@(_|Ab\q(Qu26P_{Ѥ]hg2es rJRO~Dbبĸ#)6pgfhB󇋜N!(qJU`f]켁֕Ov¬\Հ)95g39w`=ቮ>)5Cv1 *6KZ 26!Nı?]*S' FT&-ss¨F<ûogL>42<:5.j[RF;]쩥|\1^`] zjzڧxXKؤ.CW),Y$RH ~۾+s`m-pld858"O meh=%j=f6 #0aJB~Ɔ+Gyeu o|Z0U}bӮ]o$݌:Wheľ;}M.2le 7Ӭ93g R@S~0E0@kW' oU$e"ލvdIZSa%qLC@(%{|;ih$9 _p]Um'z^'Jbt~|e0%:1"au9-&􃡞Pc[7JEBB]qݪ1HҩF64s1-5юuM?tؤ(CY v4ꯔK \5?$ J+9XY6.j0fJĤT\DN'Jq'- 9Q_&!"F@HB̏wb}u޲÷pm)j*5/E ݴ<K,e6,]ꎯl"STUk~B)I83]7wXosHBp@NK݈V ; X!v^i.Bw&_*4p5e#oWXbq2"('L9AҐxG~Kmc@Y$WG? -M0f3]R& $?t41$k锖m9ԥ@:3,0Sn#z2E++3m *Iq!ȷ&LOﶕAǗ:a t4C Ν\+#>>tA Bs1h)'q>Jwz-WI l8mV PAM=)A65cl?@ݧ-$,!A9 zX2_ n՝<\tjI,UdއWuH!ܞ NyECl!NDo;„NPnJf"OeWJ+LE?&yC4&nj <r \7H͖ѕ շc;NZA896o#0z.ok8qk40>uYD3-elZG(;AEкf@+' iK"Bҿ",](y|{S+C(t(nGūKSƚ2%e IJ Z4Ӛ!v009I4\jUX 7*{ ߯WGKzjJa򅢂({~Reb.?6M!|}XT8hDk阮A\#‚O9>VJ,6+ѝtĶ/ #'dnw %KTrsbs];%s@B_D$CظpKǛ\*Q;Yg yI [Jt9v]nx7"h<3.͗R>bOzLPa$d>o˦o(֬m~m*uYSx VumGe6@QIhgG|jɼȜ Pe9|v4S00Bd.vD QjȸXd/\l!ǮТL|Jח<)z-*|+H'ݗam~6O&ث:uBq/ C'6U^ / H$qQD3bKӲ`{D)P`]UۺI6r=LcvJKiW 2=ƠPD>{[“ynW F}0N)t\4tG;lCAE:d~*K[]kfu/(Ai-,;Ue dkǙ  Fq?%HBZTP5?2Q[fs0RHÂĘcġ (_P 6S̾NOw_]5~'fp gp<-s!ՖxF6~߹!C-I.yBx(-6DMl:)0 *`KYu*tOFvU4LI5&@B8뤥1-$=߾ ,6G[O'-bpfDdOT%Ah-` kC&Q]C3~!7A8r I~tk2B] x [a 3݇rF0L[bnxY̋izx OqM]u/,[k8Dь}kqw1x fGǰp{uKIjS$CXaoGYG[~yn<5H:[֋(!%Ǚ#*.ܠ բDGS)0NuL:W}4B}<~Z87X hbMLfY(WS+#Gla7l]nZX w3)in' 7oJ=@o˂d,%bs(Yc7lQdkʪt fGB 3^q^&#qhړ>sYv';9E6M:4 iA ^hAm;Vܓ. Ĝ@Hm 5V7%ґ6.w65ؘP=>PKj ĐyC8[1JHd0ނ}K,$rC:D XShQmwmWCdzuE4ycxuajZi{6"E=n{p}+KX`[8g/B m"rE#0n)~?lေojpT9AIr7^۩sPbT,. ɑhCVntm/z1@y\n2s`ڵ}>02try8w]դXc(t`p#saJˌ[y 8"!}P@'͠zq85 iMq\-:Y#;ux[^F/qj!(_hnt5T*;sƙf׿x?{QDR YGK~;J$_)@[d~L4ksqQvOULlGBTQ*yΥھ/1q_#xz i=@tD'Zb4#W..T9@G-=o0O0_4ds`449`pK^f)0MVd1f@&TVS;ˍf~zʢa#LA6u[dCgn+;&4%Wz>΃~BlKoo#Hzʚ "|;QbkmO口ۨYi1@+'f - βosje?Cq{9qCQ 21|c42Dq FV 1Vܐڛd2n3/YΙK%L7D“ܽ{>@D%e~ݣ*p`^P3sTLR@ Ą|:XS=T$҇{z< }M@7m[bvx0NbZ Fg~5G"UՕR 9SIV;[u) X|D\Y?5?vsKRk~_ls$`-õ~$9V;8#|3q/a}Ơ&lÆ\PqJrXDE^-D<23|)4;wQ&C p2=x2F܉ 3 ֌K]_ezl{G_3ʩ?$n *%01vL;XAВg5ds!:ZZ3CXIf9a5}*b sK,TkĂ9's|VxʆJ&X/ H/R  /O< z|J_k;xFKdQJk;h8 HьB6E~rŬ A%alVyyG_B4^Ǧws^bsIyMJ)D/ؼ4639xdvqw ['i[7JyyM'FD{':#gwEY6b蓳x,Eu =ũH80Zn@jıE$FN{qzl~{9K,Y6zgr'α Vċ;9#y"pHk}O@c2fF]xo<6]G];B}B^D^IyGT( {ZO&qPHEf+}2Ԯx=a$ Dc+!+hlrC%luYp´ p/^BSkUCO#nВ|߰*ȅ}VzE_78; @߮U)t=r_,K(bՖ6 IN{q7|^_\;UzIKXF 3' j1l I:m6chxw,{~PP" 6\6**h2bn7Jzv% R5\]y<9؊z~J =էPkdg" e6u)xLlW~|' u}j[i^u7ύTuϒ d6R6_ Ky,z-SӅ0y5kd__klד=L[y[P'"Uׄ)񳆢'A P 4HsDKhB@9՝)C+JgZ,|Y%OuTx vat 0~uSUCbQSfSB؆-#w%G"@ݠOtRF ZGs9_:s)(k;P٣< }V;!3m;6LұZ J;JMWjjJی+e]q3^ & 9.y6tKpFް3q/JV0)kc'+`mX{Jh#Ԗ28,}HπSE~ą LN ./*hH$xSvzѼ %QI6b .mpcv!N3nB)/,8Vݲ2BѶiz@+c|getB_`7Yg@0HŞc-P} HQeRCL:ZjeעwX6 HdY:(C%5 "*Ɇ(~ YGEEm{_3fo#Iיd۽GABp缊UjbtiNGC8ڼ75: %,H&eb LO gğɆE8sJHؒEm'.=9iC0܏GJ?:jM ەEI5]Fxzg*,,2T:9E!Vkh54hJO\S qe_:dirlB픳T1]*˜.|u s)ڏA*k(E{>݂=]džPos#a?OeǀTV5QvNoعW;|hM\UA =x䝟رUoPK lW)I^lM? O؆0|1zf/޴zSJ溃;A9B@>ĥ1zxLX^Q3 N۷cs~aפ^\ҕ#m ttyGpUKbyء x4\7?U<3kH231CޣKwr [r2.!KVo5@֓6DR)ѹoh__ |wX S )Dd07' =b0<_ jhf4 [`Z 6v꺤B-h5 i~Q3&n`oCK Q&\.*c#I(+-bѪ+ZuVGf5y5/X1W0|<}txsŬ˒N5Cx)<02䆺_;A;rWM*Dv+|BEwf_P!J`TƘ#[]F TllKvoJ}u+3KG\?$_mKnv9%~-lڬ}Q6)|xL'n߹PX2nCOggX#t\kԆcIEw!~0 <QSj 'ط.d2[Cm J }ȹ Y%7^q 啀/.Elб^jyjuNkq4Yb rG!x RʄXIYkUߐ࿱zƚQaT5&s˿ TY#f@YqRT9 `[9NsN-Ǩ@2DerTnq;RJ-:EEb4; aK'BIRś\$8M۞( :_'Й*̯Cp#V9knŚ Lilv"RV#qssnD=[9umO/x|E?\We2g$0ᰭk1zgx/>clb9Iyr&.$YE<} 5Y,>B\NzSGn|[Yl S<s!֯Ѝ7I2q'm1m xm9\W`g'W@RGY)e^ꤊt㍌zd`#x5kѸcPt&f6eTKK O7^l0gWU P6ɡ C`mm A&2A!+Җhv3PW=\i#I A8݂| JyB‡YJ$$xHoKzا]E9o&ʻqǩozvI&seXIFXrKGt\ }jL)?u].dM&;S /oLdvw\VYǂq[QYU$x %"5FThE!ַX7p.4,k ]8Ro '%nL 0jTl#F7$S5X''cWdW#.3 k'x hÕQYX(Kc"5zv gqr5-D;nX=~.' .ܮY(VC2,K.JtPPD  B f>x @S)T{6ozz S2MVs/T_}:[S ó7}(bLF!a{ \ 9=2n0ӕAq"hݷfHܳ֍8Ĕ*h / 5kX4j>ȫby'/,+9@ ” qY YiD-U+{ :.j%"qlwүH:GBZ#LZd3qO:OqmoDw@Ia-Z~k r!ڻg/;n]n|Ci:Tmޫewgl|WE#w@ڷͲzZ=LZ7I->Бߗ 6#qnxNvJm8+dnMVCMAޯ`Rv^4_7wfNU[Fv љIs%·Bhl.aˋ.o|nA`‰.\ R9gfI{2jt93ՐG/)$nk 7e'eB]? ~?{{ZW}ޭ8̑g9bJ' 6a> 'w˺cSC[U05Ra)i7\Й [y$Քܙж}Oza]g%)b.+<=f43{RnFŧ9;DZny̝E?7!O,@0UcEUhXuD 8Rm!r u%$tc@8#;(plMHYzj|xMH1(J[gRiQgB=L\IWtZuorc-iwQyjj+躲lgO뙉t5~TBu/\՝mLm YzqbgM\]61ϯ*GF._aʇ22C Kmhv@5OnBnC",}E{zZt>{mpx@V(EJ`0Oque[!$_@H%QS1BD3;@!\Zoؗ!D 0C5Y^tPTٲ K86HՎ9*<_ok y @:,nDT|\1p!M~gPK=&W!1 <~Hf'9]ɋEpdxcn_CԒW!@ 042؍cVx<!8іc+DQԑk)R_[8NGM^4MO%1Pe_wUp9?qtJ".M/q !i濙^\>`q ng/i\ڥ]j=6 Lx8μJb}'c3=9y$ Ə`L6'/pSFZM  FvliT):ev,Wڅ J,"L8{jOXTQRx ebZ;xh[!Ȼ39F8Ȧv Q' njxip- .nhAiMMG,y,W[]} rW&RU ݠy%-sDB{ڙSOpef?+>ܤJX|6}%%?M!D׆ݜ Sv ÌF5 ŒebcêNqwcv lKr5tm-ywEI( V0@3KKPRJOZkqdu 7/;5 Z2P\z@xjtl<ą&V3)Ws0AE;^jL"9H΅UIQӚCG{P޸jjUR7DmiV'3y%c4}|d[Scnl/XjyIR{\Uä=FclO\pxJC-@ZHv :RZuBS|`}x>XGV"hZң͓ ,CAϩ8TͨO^+#%|j=vKDN\t:f\~ۯi2S_t׍ҋr047:syw^er18W}GQ E3 wi8IbxE*aBzjWVDC#$Qj]!T;T%l*WXٱKL GRqcͭBL࿃k7gz=+ ս0N_~Epn"9^I4e Ŏ+6ʸj-'0v hL0R3OS6gTLI9ul# 6ʱ||(\vԃ,EOh Ҧ]~eTCBz_qƠ_^1dUf*lh"[R5sY%[ӧQe]vL4SD&({$u{_ό9Ӝ>s G_Y=&,W"n \Yre5gj(Vl8-0~h%CGd&Bo[ZHt ^Г$ LBÒRs:_s+&d Xi\ئgP& X .jLXE^<~gNJ+({m_?O>C]ֵ̟@ݰJ2 &/rSUF&(M2 rһ'RC7xD=W.IHPdqi{8Ge a*چW#/Hb&-IZ,e1CXh yN ؕtLȿBBf\q Axİ#d,NXoPھ fɑ58p.b;{#jRz.XFєEwt܋-xƾoÓQ'⚬|%G&QsnkB^@ ƨIٮ;gd$x%g]Xfg;ǹn;HxR9ompb ]/Tc\X1Ҝ(wԮv}(ҁ 4V^:1m&3A.X5.!Cتkq{)\.W؉"!\?9ѡO2@~-؜n.FS9Ȍ+'-旵?!Α:U8meh@GYt´Q8z( *ʵ@۫AQ*/UxwP|&i *f!xE3Ty٥B2c{T m#DVwrHMWQ;Aa oZU[MTǺUn :+fd00+J{$WɿHi%AH0|=?ퟏf.V/D^I ;@׶)%6ٙyr*;R 2Xbݩ:@'w@a2㋤Mya\uJ&9c Σ Eo(xxDFDѧjzǽ8.ha,70 x!Egfw6 6SP<%&o05©̟)gGk1"]p{3دѹ-ylڶ碎'gzfB.',ޚ#B5ٺ;/*0I/̺eJ 7 E24g`㋡&c5ԺbiWpoD@cqm]A'6s'|Z Ę&vy`=1m.ڣV:ԯzێ[֯p1" Ѿ5ZnFYZ5!  ew*zcipgr7}fiTHXaZ˱TyCZ %uJ禝Ys  NLzMt_6 \1(r Y'>܉ɥ( /;j 7LaE$ڙ/j8{WĝL#V. Ap~0^g4r 6ljgL_хr{RX P:ݑY_ߝN[eeAj$vU'DLnTkq (A 8jz#3;p.a+mP%O^>#cYģ u3MBc+Yb+ya!\0tTL24Ϥ^ 8`"i'rmdeNSD @<GjHL?L;ى Ɲ 둘&j/3>,^Y+?sxAi{RLT๗v1e@`JY 1RΜ"](1M4nJAcFt"=%}O9IՉSf'ȳJLTtԋv (8tpyL0&D %|O< ބchnĠ`ÏiP]l 8H;U3ɑMeϊ,ǛKi/A*{s?N;< 7`Z=].g?HϜ[~>m'$lrU yǔtEz  P^1v$+$qyǸ\ Pn^t ;FWM74śQ1SSNXD)O8ٰWOnF@Q~ (M~mm%63`:Q4̚)yJq񶙲-DOjT1z Ԅk/ѓ@sv42Z˝NKHJXq58X7sw3)k̏e <)p&5aDJ캛tBZUF%B l~#>|LL<%2n.-; Sp)iCt)@5S""{8;jt:IF!'.9`50|JêGΦm*dՂ;*[C i|J)hg/N$JDuzqYdSS^ڟmJh1#UY;njdhUZiYDO"^'Jh܍oBPgjY*e#)U h\y0Y#n(4, f"pFк"㷬RQHUݒe' q1WI4eYkLdU˛Uɪa;x|Jz0L\M{ N"^`Ck+[gMnijĴ(%)g>F9A@UV1_J` #lTg}88Da &L{eG5JVVK4GdkPR> H /()}k(s\QMM'6c8T}GLr6s/t"!qo^4.ܑ֙VkR 0)mPbF`ʡѐWf7Z(s˒]35@{KjF߷ C"{:eQ?FM gv90 L: 6OkƙxV\y.Z Fp3 d;FcH\+< lKRk>K)(MkJOz k&Ky^,O:3D}ԫG۽|)/oP"~ʓXt< x8= )nb/ coiRXvU2hoN1#`NDG >g\Wͨxbf.-LM Մ~ƚBh_qO=Z` ےԅt='Ġ/AuPhDєe tՔ,UiY.DG G6 6+pL1j N:\F/ò"s\JOU~ eZrvR/eޝbt/S'Fsc ^\Ȟ ɳ#?|~-4j[sr,[a1%YgT5 'mw~=O^`Q]G?4)' ;L!O1OD5`<)ɬ4pߥ<-;fj7c/:tғw%2-lnOٞ0TߑVe@/D5㕉euo2_KƁF`!+,VNoT7gLYm#um!0# Iiou$wsl[sJ.|>[ S(/#m~0~LzVm Yrp6[;zѪXuBJT6r9S7qTNF$XZh-+M6g<nk楧q|9:`S'Gdsߏԕ+ڑ2t3y`M+}U@A&d.PJ.7HQH?V͵)L/.t2Jt O W9RՅZ{dsKULMc7obj6nJ\R2Ao1] !n 7|A<}|C`I&,< VF%q6I+JB5s-I$F%wb7/ΰz%R[QXukokKdCהD^шV?xutY~m7f]=ƋJh%z h!. ԆF8#?}F [Qcegp/MW\ތ9p \Y! 1@-Tm@MBͅyU) ] 9/KEE6V2U T g^mkKe9y{6P\Iezph(4B3aD%XiNqNK8Mz=&WɌC߮GRyav _:6zii$/oC8bυ{ƒ%,eʤE=# mń_x'X`ēugŵ2nP0(@wG>,5֗8:Dq=j_z#K[_y\wi:Ia}:tbc7b?!fWf4=}\iMԑ$ĞDoȅ0M3]S<CH+ 0g;61r݆tQG% 껄7'kQr L⑘Zč >Zd>$A!ژDi#LNȴv Q*rHa<@E6H_[i{RWŅܼzYh@~.o_eQˤw=SWKXzqI<#mhboԝеֈ{@ޡzp:ylxDuToW&p]wW4Y{ˈ a%†OD me{/ɾ~nƂ!(=_Z|9bMbP6KqRMhh (4*eg0ǒ*/6Ϧgr3IM#p )1 ճmob$!9u O?1){<5BcqЮ~m R(JTߧ pbQr")LBǫ a4m4Z#eh=N}Zx Vd,~R^WiH%o+9>EXNTt.㾼3}Y,aɛ0+\S-:gʪ^n1dG?H9v@TYT[w}SQY񥐙h+KZD.̂&vk',v颳#4&5I, \Sr| SUɵ/%ȋA]bo>OJjg?k<}^3U q FlyI$m5(.f^uuOn@%h@JNjSηGqy#?:}_VŵB-nj w:7IjMޖ_Eyj sH`׎ ._Z?A$,|Q)#->GdAY 11{=CHj(A:FJȮiR8^JA{} .,U: J]7 ϟv,zy\LB8ByIA[C_ւIx-c*}z5&ss֎A*z[4-j)+yB/GB TSkN>Π.s3y#UJLW:]X m[7g%-fqu+j|{ehZg,I9N-C)])P-"&Eqɨ-G' 2S#ʃSweݨ&KXՔ)@qݢ w.9gш>rf8}6JT!gq{iW-=f)Q Fo' KLJa$Ybg;@ǕC1R=҈pIN@SD_ı_$>Im/j5̔ E%h8` IYz90 - S$UP a\`醿UQߛd+Tաqln/gιl_2ӷўvZ[9i A0L({:=+  WވѨ =) #]1x4Ii3U>?Me %V sR=0dQ=|?얥ytxU7X]1[1{>q2ηEx.FF؏P}J`588HÓ*gBNI հMmP3~ {c!R{;$c?=s2B#Mg7׳ϊIrsXfLuJFjsa5c﵄TrIRW8/Ox_2ŲMeV_c{| ].{U>J-}}W t%IG$ꋐZ ^gD['ƘiP$2Rݡ.尞;>NNAT+ JఠZ YkUD3vHIiZ|R=I}@v9 3rfw&o}rD}_Ԫsr 8lӰlb~ïݹmZm=e̫ܴh0# #ru"̔a[oξMBXݜ3Ē[s_I+Ehy &u1!E#Eh|] P2ioFs5T|LXk J|HWܩBqìF s^oғk{8$"j OW<- xZ"36Pb 6Pe3Y#RFy \e.f"Խ4o9P/Uޓ4+K>t38{5<cWMi`LD~^@޷Xw癬 xb,4¾ɋh@Ă!G=rJh`/JRd[QPnp!1clj&5<!9{JNI?9^ Bݜ~ o!En=#lӭ`K}8{!{#_zt;߄N.qݖA8fe|;#ՆIxVP#8,ζ3CUe RfO0F"Q XF^ҭVhi 8RփTBNb9vߢ!WixJ?>6\y,ZGtӨ*:TUd cP3)PCJvٸDG#gQ](ZۥB7C'p!(KZTѝA제)rd?Zi%^mx4D,j0ݯ[V90$$j֚¢erB=@o ŗ[ y_-8=1+ en-%Γ cǫ*/#FȏײKI%K,ŕf:j4%OP}M@HFiMJdm7I'ʛv-+6z[A.Hs߯JW]dڦQQ挬3E- Xg٩2>ܟ+#/Q E6qOA GU豤[ڡ,LcVBz y?xؖ+B"'.3C_M[={[)ŒQuWBۤFITт$[8FUdW# DR:=kY D'`gXC3-0(D~֮eU??9WyG:d)!4na΢,JF:$M'dj{uřT)Oc8TWML'(hG`?bsLtp~ۊ963Fr 5?jF-8_6D?U, b'>k4Oywڥ =,_982$Bn/ИJ:d a^sh{a Ze^e(rp3f69$Џv2vHQ'کDŽ"MD]pq}WFO-EP BՂxv5 C#rq8GKFh patx !*5{vbRL+5A%=Tps,Y2iG P 6g{_ Ӌ,W#+E$6OG<1t6N&6Z >s h!Fy,US(8(^7<(LZoI1_`p4}kAZr|XV NB8lr4ŷ1EiUNIUZ-1P^bF 齳%6Kx5=znP(;gc7.C)IWJ- NG7E^O9o=@ iۧ+Ö3]C`v>`6]u-1$oguN'xj($M#krYȃeiܲ^ƞsJ,GfLd5w;$*iܝ [i*99"ϣa-x4'Hu+*G+hiGd~xA87bm#h" θIRL א$55m&dv αH 0q\\ð 3Ny0v^_K~Aʕ#=-z23݌#Y֬i'BNACf3bü>FT=jyAG7sp" M-bhYBRKdbc:J>$Fx\w Eٻ8%lOHMU{17QDHJyQeBshr Ü!y-c_6mx|aVʵ(]O [ɼES©=ۘ& HGq>|&HO\cG g?u-%L!b?V@ޗT&!*XRg~0c$`YENllXL Cj'x,ݑ܃L%,E#4}y{5("d+({'_݀.G;[E£>(' 2<{i&qϷEq728}.4:D[:>s6+=3:#|QݝU9< Fώ꣍X͵ `wO6ᦪ/MlXStī*TD (A_}w>\Z6uCK9Mjg-`鰚m]bS{,K1Q/ tNyml}a4<#q3,+XiglT}$~:}lSj`/]YlKVvY:$)d+N2,O;[T;U}Rj"k "c/,zM 9=NH/:`\P%M[Z["4xJaށfK:n[Zi,Gjsu9yat ` $HЏm*n;o8*7 k'R擲s..\5YCiw~ԁΨ? #Puux}n_B,krV-P{嗣Ht P4B=t"7~D~N$A态[ 5 JT]'*ⒺheǂhlX9*&6j 0Buйbqncz.G P/DH4EgÞ P:1ρH10LuY)<;?zBAg~51t쩯v]kG'Jc68՛>30R%IմY[yHnl|$?864^ @kT|N͌Hi/u8mvz]P1]j 2 R)VGAp/zpDžy4( /EAoܞq]3 jgf/S]<쵱Q-wq7,ʒleW0LY9am:6[} hBkXQDcL]gadj[uibPbY .QITb!r/ 3{>Z> h&YGO}1uݻ@orY!/Ѭuk$ƫ=pawvkໍҤ6Nd#2zg":5\-cۤ:m\4E=;+X*"CX(U~# vok} >4<`79O5t9 F$ "O$7/TV:w5g-΢ X{{S-8vץб[i^:5= 5o4]@Ɣ2Ҳ cGU@z=Џ:/1OS/?NЙu?pT7N^Kia t|oP))Qhq𜎒,HxVNR mZisѧ+e3LlňUdsvbfnEZ#24^ f*nV$'O:Es$_Mc2xq\w\2d5Gz(Ƽ^{zIXOAu@ߡq!>rir9&~IG j9,;{b-yV/\y.0ԩ~y b#&"1?.tA 00j-JW'/Ȫ;ҝ3WkfIRkS:w?j1qvDoΞ {Ku0XSĊ>}5@Q0B@F".0I&.7"蒇PJF }c-vlhLv  w!6VR*.c7mbW߿ uk]7*$#(0g\{0KJ&X70V!`4vR[ DrSPZz+.Vs|oBSa4q/^m8DdC̊͠ ĖMכ0 'v3e+$:8_ Wln`޾l'cmtR8Mxŕp!O)#mhGOGCV=.C ~z6!>VKpCI nLxT')z ?};=W5T<" iE!|w+@Ml!TmC&Pu1$DN>` V;!4ThHBlLt:,ۼ JTzS31^(Kzr(u,OOb[Nxjyo$֘eBz/kI) F/:i/CX'ٯ%uOәbV2 ]V,k<F_)F& x ufI=}e FCѹw>%'.6g|Gfl@eo,Y_mDOzìΗa?/eR%6{#%wcqu/dIuV߲ZoR\qÌ$!)3нq+Qidf"mgg {Ym@|ac[Z]; "6r^VZ(ЎSK4Hcz%ueV*MK:LSHFן| hm4nXk+pD戻{o)kϞ;P_ו\a]he\Alyd Cp8$jѺhNUY7ũ)I2m_tp(TGXAU\ikݔ{Xju_g6„Գ. k9:%NsC%\e#$sy:leJfbU诰fK<`IFfb.MFz_mVӔ,d!K/ ^VB n #D0$$0uv>@˔ U>&oVg;)|>V4kr8S0dvUHodUu `<ЄH8LzAP8*Yw< <V cL)M'%McvC AqQJy}5u.KT 36_lh:;)>0#R6AnvLz=9,ak27MSU(=)qf=x3~qI},g#$"v^O/M&[0̶<9VO򣧘D6<F/ ųET!.;i7U)ebPΚ[hP*ɾAgo5 t8a믍Fg*j ϩ5 * zKmՄ7 J}}VTˍYLvjCX]qB8|,_:xXIk!]lJ̇VJ9zj%͜q:.| ^ry7Y*= *1m9+zbWhhU2_h=!IsMdzIp"Iv]wdAE\4.VWPUM~>t[Z8Z=]=SLD'd9k.=@g(3,mL2hMu<3[Wz 9,cOvŧB2m',b2e m[K M ɡ6{u1_+{ *s &/4A)E(BL-1|dVYӞˏ7u5ˀPyf*Lݔc2`v1 lU)v)qkպU.j*$uZmIqbzx‹;th=NDqqX0&m|ZZ ``v<βOۃcƔ~ǵNg`"/A;"\0gF?9`O~~Tepnr\@`-O*g姎Te{YCA~E'p*Kqx|qO=an>.:H*] ?BXH( $\`0SH_Q+e}B2S4k. b(G{U"=/~R~[SEW&I |#k-W$[f,L]"jx[]Lu1]ݜ-_uVHnֺs^ Zl"JR7p#s%*~9$V:N)OGY|xL gOI&C['+ i%v6{NG>+EB)Zua*<&f2{fh`ƃ 7GmƛVEQtMN^ELݱQv// s7Gۭ^!CcI2:[ ;\,M$ؔ=;1g bKz֚DWc'%Z ,Rsź++h Ά1\D\ե$'jJbz<.zegw#/ THt.XRV2$lX2SmY%8$ Fߟܦ@+ZLwKTw, s_%bj0zfWHrevrv f%s|0>U3 }i͒#YZF>'u&Q&NSp'؄KH)CS+U $ֵ+;fM%폿o'g! 8v&y^ WJ^Kv t.'.BwGTQվ8[Ϩc&sW_ut~c2r، 頺G5aBBh*udNwaW\Kq#\;۩Q/Cp hc=/G%]L;D ֐ޖ .!LER2\I,.rޘsoHް t9`K۝HB5g>6!3 >TMB< `]"eFu55Pb[ʠJe9$)&gC?5&yz7[=tAW*Pe®`lGٴj.DOB4qUMJPP6 GQjg.cfÉKz.U7Qv<\R7 ]C"xl~t%ZϽ7s~fyK T w5 I׎0K6<pyhhf-v ĪݑKv̭Ue2JI Cx̞~B;@(Ly]!'qE G젉}rgY w Ȥʆ8vYĀskd?k8cU|gs\PpÙ_4Ω[] h Fja'F"bq %|Zyzp!< MkE=B{.(baW<;U@rUs#"EqPC{R-~w$1-& m*B=BSI] :7Oq+A6;)JtѤ0CF@|*oG ̑dW_l#M?wO@UeGQ}QB|!YtMiab(cdoy$3C:PǷ7DDi&4Tp,1H#e8GrvD&+ MZR}C/ *T^Pp-XexEbڹlm{!\ً4T /W /5~!# Yl6!t2LndBP=£or|exߨC݌C5Bhl@k/I9AOLd.rQzt"ȪҠXvb9(>iHQBc̅ TNR] &-l`yC| b`N4 dMCjg?,&u->;^Kݸ,eTD240{w%Tݰ)i)= MjbC'Š[e>(R?*;~K$xUn^Vj{݆<%_R`͹E O^ y([Av0V(R]Xx+-?ؠ1Ro{V cϜY>W[ied~(b遡 F02 DN;pGi,>FmSm_1Y*sfc<#4 bI!\έ{ca|IġDUkL#\V$*|Hb!V' Yd$Pz줥#}j 0 gڢY8;mXR="ݟ؊&''_{ﴱq6mp qAW*U5 5uWIF/Q!@¨eu s@:ƊJ),hgfP(rdj1 x2@Ǥ^aL~, /;m$}rTq%É =jTK 6RERBR, fy1#Jx1*5(L `In AH;ݾW'o^PhR)|$_\$s!X3eΐtQ~-}U%a{d,!@z._&5OqGhM(J@(E$I=ؽlπb;pX\~x"cFpzrv0iLK/H.m)PS92r/,W}KNF0(F\.I[prO$5t$)#ܳRVFG~ M$Z~ eWm+)+V!KS7L4MMZѕ!1\ѲCT kĩ䔂yJ=?nJqᄣJ XNLy=㿀A47EP`>tx_"B 龴v?ˎ8swr6,|yl̈́ƣwy,aP_w%nikc3tLʷ}iKw&"`ɱءٵx¡]%%d(idi|/ $ly|ei`z h'\Tw/fU℮[f ]kW^\2w*CiP*Άoa]fx\2{r*t7{~ ݴĦZiß5>ܴ<9(ěA!wHm%)H4 ~}K"8eֿbkzکn(00/UE?V5y(Pyܿr<Eb!;ёb4AfIwL̩m_Q F?$|&Dudysh/ۤe%5e%yݕܜA;/BV ӲO@^PqMT^x#֐Aju@ iH?#mD.3Vaz9@Q>ʹ}h5'2׺ bӴFii+ȁa"}ShCDbg02+p3LxË5IbjYt*]BBdED<Dž6}I Z-{u} [yu K4'%uzV#4 , <KwIKRnCzn`{2hs!þIn9"(C%9{> Ըx)-rR@C[mZG(,_xqx姚a;\s{Ou&GwF;6Q+f MfA_Q).Kcm$OYv ͋:s|q.?=9*KJL~yKԴ{ؤ9e0pa$߭~+0%&)0_}Q{qeLJ͘KPqC&eW;Ed]*e!r 7@>Ǎ6r<"#ЧAy`+g;K?tТN(:6I$o-F?> ?JЗ=o1/XuL˯qƸJtGlj ~)Y>*I#7`̮cV,p&pzȌB( ȉn᯴BxK.Vq$Lܞ&RLmqkGD=fDVR; =qzl 2\ܫ͹#xc2[ǪA[dބ1e d4G#M@ISzz<."9t (L3#ZNNRm>y)>O[i<ç5OvKZ<#(/ڡ?JLZ}jص0֒dGpJ/W ȣ 'HNLpH'f]śxkidG|muA|h뮫fppEtﱱ1 :V6F"p/W;wjGHq@4S DDcC:~Iei P R#kW&\0Խt̆X,kq ۭhdc5!;gg0B?i#8c`+/*n[t &W&cL9h?} u|Cp${= W;ɧG^eqㄗ*ے EG>M dڏZd^k/{I4 'r\i/f&{)neK'p%(Bg<Ý= \d_ksc?r'V'T!`H>/IR` $l+´ KAgBF«k!هQA!2xy *EJ /׳^sC9|f =wOZ$НDԝ:gq:pFIOEw)穨|ȻISyI]Oh}qI.Eb?"$P-dӭڭ3UE*#JQ-wɿs_[XPfO'׫vԃ%k S-p(glTUD'> ;uIєh@ư{]Tda/wa^)fV4˨~B#b<ȯYOaDӹd$e0vkE%}OM~gY\T&K1V>xc0Z5MrAր`S`X*%1/u=s+e=}00+;[&sdB+v5;(P?2>R):FM=;+) K\z9l8bW{grKIIF,-ъh"aY3pʩKjH.,2|ۋ!ӇՋ? \&]ȿ;6Db{YVX0 :rEeyS?R1^kD&~M@-l, Ɛ1-I9Ḯu ?ifM},lRv*(JdT8%g0&X 4gLC#*v:l-5YҌcd _*-ճrpڌDuWhx2VEʓ,RCW:JEFCY?xq}5h9VY 鸵l C IՆͭVpq𵕮XS0SoImŋnx8ʧx!ث/y0. G,WU% rjOW<^cE$yy02%GYGMDGi')YMY- /LJV+5bjRhzc9y3Y2.Gُ0Fm}ϖ.Iq(lCr R}5{bcuWow>nn6wȚEGaGt iNq#pLs8dВht8#.:y-O݆\e:H's^a% <J4`7$Qlx`pbe/O.<}N_gGH}69wdsʼn} ¨=F4~8LS#ȋu˹ݺkusD;ՋS&o oVo$%祹#uИ$׻TL/Œ)R" u9qnB}JpDs lwSzQbL2p`g " Y[XAe=W:{ Mv`z<)?^0ָN@k'&]n39/tu #H =z-BYdJH".z$^!@\锞SV,gWjh:g.zΤU@nѪ .i{OgKrU)𭭼;`fCE0͆ϡGe";,u8X t_{10Iݿ|OKh7k RR=8kFmWhPH{zj})5.>󜉻,KĹ|EDTw Ƌk4B#@h [BO_墋LuDwd'c.ŷFa(=\K%BgץX?8EG%y_k18ێ284Y9"'C:@ CR:#g) a'L=- dZƖha!sANAMSe٪~>0p.̼8<{e-|G9-A2FKsVR|P ]z\~h=I+ ZIG0+͜4!^}:j L\ڄlk#*'>8cYdN՘Q yQk80f$s.iN`l[Jb+g\|[f0]Nj{h^ ?/!*xdTWz=\Tlbd|mbSQX/!h8(!q`6+6UA%,!eiFklV57tJ=aSL6;ۜw%z&emxڌS %AŘq(븱 hWg1%6P.Qn݉ {AQq^lMՙȯ:SE@=m=yxD_N!&82 NxiՖ@b9`BL0ՠst$(\P3\D*oZ&/cr3GW>̇'.02K=. ڏǍ_etF3L I<"ZLhrl72gB>UlfrihnuaCw]ѬW`"d,er]F0cjG Ʀ/E܉2Ʉ ԋM|&o>" w-XFy_+gtرI^FVXyT➐+,p`Ybw2~m]\RAݳ#FD!Q_e^HYL=gXtOyȯW]S&M5FF, (KrC_^6: E%16N۲M@jEy<&Z\hCORe^fWKwv@_V2vt6u\݄ Կ.|Jf,kOP6} a=6+ =+xDE]];a_:;Rڊ`4`L@]T9usʆt=GH$˔ΊH*M)3ֲrm5_^@[Rg5E'}MRHȍҗ51d(h+,rz_ԥQ[]TJgIL8N R|&s&v)PPV95lf NUiN>p!E]^o wזKpwwgE?2W(h1x/eB*f N0`,GC [4 A)("TmH聝AǯbyD[*,! X@6'@Rkk2[J drx wO5sL["VOpB ;wLj ]^8' snAQXr]8Mݵ ߯WDI:_^tj(CAo;o7D5Q ҥ(_Ia8=`6О|P}?#e}Bʮ ڌ姍JFqS ?L7_]"Lc=;"br}\"A|SpN ' yu&4m@A5 lz P~WENQ$吢o*!Pϥs_XC[zL*s*+d viժͪ$mJNi60{?~PFtsZ4v?W"]pB [#V,)&խڡ!^4Ks'!wU 9H)C"Yd1LZK$Ɩ|ʊ@M>SScTΏ,lRmM'w"CȩQA\ZaT?+T>۱Dnf- /eLD`᧟cʰޅIϢ+?T_ެHNFjfP܆w?s}"vۉDPTY%BoE9=Y,,߯wO&^Aa4~DL5BT+MEuqLcs>=ώ!”4dhb+xcf/h23bw MY.^xwlp OmЈ:?/bVsۮ3j-7jWHm͟B!9Ny ǙC=/6BLb-;s(+ f T`+sԛgNQ@ sD$K  &ZY2:/DR.00dlL4cQ&@^ reK|jUANCVg:Cy-4z95!5GO7ya-}h}p >,}-˅yG%̯bu'L΂`hxٟ`|XjWM?%>]>;g K1̲ c9jO7eV 5nɵ,;J*P⍬ٚhS:LU4iשVf#ڤ ҉}݅CZ@7if XdzSAE2lb%pZŁ8Wd'䏃?#g<֎!~av7rA)' t{`wʪ6bݍ̑$oM˫&Gr&nujفIW#- [|{ OxBH' |~$>-,=<9tb$ K2κ`#/'V7Mx`1dZws_6 u*?42)*³ ϟx'~h_ 7wW>ck>%ØtRuAț 4N3~[Hc64KGe]EҸUfɖ[c&(nrƔM3}CEV͊a4mtXvf]ƈY#&% cOυ[Wbho55:਷g @XΆ Ll`dkjp.'n0{^PShȉGo$N`Ϗ`"zE+A\\rZ$|:.xaVw(Uz^<|Z{ba${N_"a#Mo|p;51 ϸމQJYF^ }T*#R1IpͿωUhxIG&k: G:~kFO.bS?u5˻-+ӱNTk;6`!ANYzw!9S{gћbleiԞ?2Sv` cw2 /S|2csK1;ȃ5,&)2o"1HEesgu~L(w-A) - IX+쏅e ΡWN_gLcYI=1IUii_N%ca.(sJ,RpP6<+NY~60^,q6 v6.> 柣W8bZ,ʼn5TE_-{gQ(H\ZrsioeM}ʣU [Qq }Ƭ.66C햪=d,yhgbڍ[Mtq2 s8Y]/SUHk*xJ .ݽ )2u59>,JsG;,nmo&Nɷht7?IO3wAlq"~/2l:ΰ;h+W< jt1.-1!ث#Ml/y1ǐ,m+vq#G3ۜ-Zcq\ۇ;#}f z,l;[uS܇unh{rl𑝽NoJNiҐBuDvJNdэ|j{y< qQY|LöD BK;"⯑EMx$۽7]s&֝<$yԖ>njxPd oll\FlsWzgVg^t ds39Z3K-y֞@Z8;Ow:*krRbp œW]_?5Gf+R{ 8]E@Љ'1eIhdr05F)Q)".w:rywC7ӴCzI辅$XzB772 3y 4!Լ>ѥA1B!E}nuN \QV]&('I&|ˌKPө.Y[?샗k\O8fp?JJ *H:"0U@pY*okA f3ܱ\^j9!Qw-d%_f*yJV5 Qm ?;k) pge0'?1ihsb:RțH9=Rm/jD zH+Vy,gJ>|t!:f?SWI~8A J*?yFY/X.;H7-,P_Wet0jBBl_=v67A0{7a`(=Y1щkIC$u88r$̈́RȒVU,Gq? I3o(7&e0Zg_;8}yVٗsV:iW̤lV 6hZVd]ӔFeڦ(oS՚WNvjĒo);o8h`J]Rr r| \;&\\7J㡢xEYI{*m|=l4ba (LWzOL"Dj(\o{/COzᵣZ1߾+CGЊ^va 0Iz7}3f a|o^HT,bN‘96UZ`e);yFj =&dHU29|KOMu?Tu' ^⤇ͻ? O;beF#}#,nj@')9W5mv'huϜ娄H_rgmpc:(^"g&Wyr*Z}QؠR>1>GBqvѡ bTv,,K2J uǟ pnQvGz5AN'SG QF'S4aTmf٘\& Bwŏ~@2mh?rfgP$V' ܭmtilz)-h}]-VIub50@ |ѫMq⃓b#D]K;Eugc;φS-(9mU[Z@+q@Hx r9p.HИY@C ]1Y?oI.a, ,#oƘk۱Et`r͗ T!2g[ՠN$+K#U!16\*^v>)J9̿Φ5* E8=zl_Z]šTl2Gec_dy7nH[rґs1ʂ=G5 31NMM6ם!ǯ7n01}4r_ׅ:;~ʥ *[j=-IkVB^ zeFT\z2Md%>$OptOثbjw.NSB2S/Ӝ?j:*{G6~Ѳ`i⡵,:M##q5%M>{acS"纑L}ރmoXk,tDy#X۶ny"̝McgbGÆ&꟦}ga~F$:V"Q63 VᴞJ-=kB@6>nooQ]` 8iCpMJ)t/H>.<-?@9KߡLYr sko6L#e0Un0xgG-+N)I֧ $D2ධhC$50NChg98:DV8.i^\@mM>Ŏ5 B Ѭyw1 >BKLÖf62u@U>i~})OQSr8gϙt<,"(Fi+K]. cV y:_At"<-zuy{_CV|7@cU x ُDu7;pWE{gct7/W뮤9BXd舘4_QHѰVѾr7I&*.x Oȉhk :n{?Pfj&5LF- 7J Uer1O3<~kXRLM2]iru6q0!B?4YVq(g>h7e+wfk>z݆c4* ǡQYC-Og4!;2}@xUc nۛdgTy]& X(D/;4z4׆߁R53ƼF2i2<:YiۼHy3Y_B7.KkYpH.~ݞGM@~e6RrX"?+d8iKh?×\dXטMՂiΪu@Ez;taM*+/HϮy|teF:GK>*ܽ!uxc"MƱXdƸQ(qU l|)$s^јc=1Lk dTZl,%*?ϝ ʸ^FMĭ+0j |1a}V[ɟmbcM~VRH~2A Å!+?8tal`.Wbw\SX D*兌1 JNE [ V4YZ0xp*h,T4c_,H+_, ' U&V29M?1 %-D.[a#,҉P߆j\tA~&߿jJ)T*oa%ReS.]:q(Vabc#QO=+ *!soGFu0-vA~u?U43zSntҪtj"5eyT.Pf k%yWw}CL BpPv$MVJ^\7$ TAHKG}V@_Odٱ 6wl tj׷iD$A!&@ۑ .:Ft 4CJ.& |&(rMM:,AQ Ml٭Ms%:I*+ƒa14ys'n9F ]2&"[=c$mV~da_tdTOl(IzSr6eTH{ZӔ.qw?釮gMrͯJBd`@J\靰V8}u (Կs*ž`_m4ˁj&5]Ii*?ć-W!s@mluzemuԽ_ќ]Q5E'`Nj!W'`-mT3sm)y2kΆZH* Y..]s+N$^ S6r$\CUWzT` ҁ1 2G'@''3ޏ(n%xOYz_ҭJw|[6<`DV)bv!2g.6s|ռfb1ܘyhC0o6ZbpH:> n s j}lzId31&N7!v]k:~ѥ⬚w0H$bV` dD!c]a^>uÝnZKu|%c&]uHJ ^GowNS:Uc1@ Ll CM:~T6ArRqר (juَJ>n*b/%o=QaRn%)/Hp^4rP5\)נ:hT{5>ݩ6ڔޥr=˸pzPD]"hu2\O$ȷxU6.Kd֐j<^bzvr'DyXVC,#F8n& TiW]*5I1=w`, fzLE8}z/_es-Mŀ>N/:t^.Z!$Y;xԪؤX@ @˘H  .zL#+=寲6mx31_Bnqk-NQNTB~Y{%:W"+E^G@C3!FgUC_v=x:kbjvSEA8rm&D3A_?^Ww?%ZV{s|u`{"\1^AA !:I^qשW$(칅<HT+^"(mLN9 l bJ?%E@p<AzY>h{0s'ڧcB"z͉ΧocOpcI4^}8'0BybO"Hnx"ֺ.PXWR_X&w.YHevHv:Ob?awz /N@oP>ʭ&%^2ET@mD@㣇 qY~1OE@4~͠st$}+/Rfjd6+#\s: fء[5+-j qo5AR ӝzp>RI@W߻4@Cu"|.+azw4v2tUcТV%CPV)kIACB)W`YY" &#8`6avR"rU> ¾ #$ -EeWX2Lt]RVE#py"R ӼV?%cOR| {^G'FgB27Tڦoa[灮fPA7W"oN2 R!krZ ~ oiXiFK/.  E%T1p0 cZکbf7&|%<5BB)1 >&pC8)L{/B9)t]u#_ ^Ar#,Z?!mP,LYEڴalP6ubQ^A wv)_(4gv*ޠ=@$Yӛ`>nT d~C,cXf)S6;M5ET]ؿ:Ѷ0#7Z䌀pe]׸uZ~u1D9󮉑[8 $J&шWwT{/M/O=t bqjeW*2䶡ԆRxh@ֵ5lO/؆^8zj BulDYc^ .U@oy8ff7~.X#^Eoï:>PXD_oKf6 ud`1$Jam`,Jd?'3 kXҧoi4Ec<7pƮǕ9f.em{!EۊF;6c^$xmZw|џr|TL"gNmuR06^!9I]ϰ;b!%ES`:@^x_Xi!ZY1`#w:̌+t|&=`{MC&5،_-'x.dD HYQ% gnmwRsFSلڝ騒 tI" zWd׎KNQ33\ T} 05 +y% Q. u|* <-n})RT`Zʏ۠%X97{[1:>+u+jdH%IO"<|e6C A cėنk6kY xm%*Y ѹЋN'y%JloVζo.L3Sxa-,oUi{Wi3*Q<0H2rjJ\+hț¹ fS_:BYgǭO)rY{_K9t,$ .qmZ=XoPuht3[ݧZxI"I?hЧݼ}Csx*Kj_Pm49эͧ-IA @t1dzwaeggVi88uZqRJ,pn CYph",cMNf"8VT {SKlA8.;^^5`LUh鿁v#Bcdhh[?avs j !zl晶zqЀnKpW~Vipҷ5BC`"9Lf%(&t-6/[&7 Yʯ,<pH}DGb[!5=8YhzQahu7 [{;lyXb\E&DzpFmq֮|lJNı6'o6R81)*4j2GO$)׻r eI/d{ZSZv2ҥїgdخ:[jBhǪjvl."65vFuj-gkZxtUĮ1`ըjsQZZر=Z8@^ "hwbׇfYs Wg 4 * CkIbw]V(ZiaU~Ut˛0Eo0X_Vʓ:=2rzV޴~%sIgn-#gcsЏ*"[ГAcڧ TKyDL ^ft1$Uc+9 F,`p|"1FJM5HwXL\~]D>ZtJN{B! ׊̄_}Pg_cvYfHz]a>~$\aD2"E |>ǜʹW0"ɩ07Ӌ U lsPumZGPVG`a%L>xtG2Tt2(NA&"z^|(({fm,%"BlNng&huY;L>nBs}E(y袤Qܶ5~"ͱsDa> }^6+^( @s弍co \i& 'lZBAa^t%:*.Q=}[t+)*A,>vҚM TX!ekw ӹOw*}n`1{crWl̔j]XK ͚cKHkE>HzMF8M̎K 0lꍯ<恄)ZZx%J ag\T£Tmξ$8aJ呂P㔐G9 c̮*S[x[PN^U3ɮԋB#ާʯQ߫."4}XN w4  (l$ ؙ(Q-"J, Ʌ1XCj2b.1to² Td7є:_/ EHɴQgvoDzn mƔc*N9+LXͥG[yb %s|=wG>wmN W DZ}Drbp;!:)rEܗo FY4!GS[ă44`嗿# sh+5 EA9_8FRACpoY:Q?PuĮۋA4S98d]J\ )k~Hg;2n\M~B$}TQ+lIgePJ =R鹣,.lM埄hX`fd4u֣Gad7foT˚XJ]xHTtuj՜,4En$$ (`K7L`ĜSh^f?-B7h𼴲eD&791{Ɠ~_o":maqfL4u4bcBtKuئb3rwL`h҉$rᗪO +L6s|+Gq5NhV2D8/<@ 0nx|+~m(cu&`N {4~/na cT=Xg)qƼB^=lPtˊMov /&PH|yyJ~tonjz( }q:M]Jerў0 "0.ĸфts>WueHLM?ᖞL xR:3tU=G_OqOt~/Y\X憂Edc5 vg1 0Qi@A%AK#ZvWpˉٙ# *g`ѿZ)#h+iQKT+-LZ\NJ8rٸ{/ŷk?U1x7( .OnFK02|&xDJ if1!BXx6?i4S~~" /yML(PTOvo^I]!([X%͇Jr56=\bdt0a w*ucs!{Y^ovv6UjidLXs gNn !8zDU6.g.7cf>j+.;*Hr^74ܲM^kS:8Scp#(iZhΗ x{^ﴷ*jraHۧ^\ vflKI,;E2ɣ(dmݺx~hq$e2sJjt,9kOھ:a;F«֖DOZz&HұUI"Uy3Ln؊Hr/*Ztj7k]#(xGD>e.N$`Ʋ,OJiDyƲ{[3LۄGmzh4UBPJ9'䄉]o FDSwr٩啤FA܌O䉑N//V,n޿\~hE~ Fe69!5<!q&z&j++w!w,k}?ݢP~KQ+*ϪoW.W@ <5+ǹ/?yU~\)|W@'mʨ{U嘺LtW*A><<_- n"ner,Fʘ3D$:VaG 1v)H/Mﴦ:_b҂%gK@$4\9\v jNJ8 ogԲٺv3d ]N m#k1X,c4WmwNy5.CGEc|}fY1׿}<4EH?w"yK%hH~:-{" l 5QQhNIXWyLQ‰ɔYؼW) ;0!,{-=zGJ`lGC'l5ۅj3!S[{zgRƪ竮!kA]r~}yv"#fU4tSɰ~nx"PH!5&0;GW446,UͦJ0|'L}]YPYf ĔH ZPm}c{`VIN*k- zD* TMl*lq?iqo/!:y_Pq_uA&zw3sc^ -'+$s +"U1̗۞lt3*FǶtȢ1X{կ}砏 %]U)vAQ=vV\7A=zHJw'ݹIBO G+~̃+.,]X&"keYto7ŝ-J& dMPH"'`l,;y75ĪW?~ n/?n͡;徰DJk#0^xHX+.23F/GMˌ416Cвa60?kHl9J[qs͆%fTlTDY+*+-p7/ϋn=G z40To %.!,Q{ArBm){=7ⷵEBJ9RȳRQUcs AAX0enA7 rx]ɜzќx?Ǭg@-YՇh$'tW^*GSY5XDP?> NJwQ_ %&ݔtp.x[e:'(#V*Vǚ}6aqkˌP;q#syvQ:sD,T FFmD p7^w}ejr1 Eg3%בe.1S# 4%n8iVrܢT-{)rŶUs=^Sy$> LpuXu}o+n.nȵ׳&+~N\g\ 3n)=zr/=~!P>'$}l;ߝMcc2fg$|}}\).~2.1#ˎn !WU[ /lKI$XD^+YH~C:3.x&*sxLv5Z")`Ay8c ?먰}R<<ԭyA@"Th%2V&rz]⢔;]L4X/uqni:/2Lk-Hm8BP?Q^hU%PԮO/ )lQ4R-1Ⱦ¨_,|hkvc4%2/,j:v#e>J%<ռɉYNj$h3~jMRXƾ j95EOk7nC/D/)aܠ>-Q_gnǕ=@6$>l4 kxNtDM uyOdץ`Q}XwմlC3kȫ&-:,\ՙHG"m(#|t~x2&{ l起=K.pO(3z| Bk"ږoRRmSP=`_*xhp&@ToU*P%z^dx]P5b[/%"dvڢ.cwfX`s`Gyl;o0. A7[/]WA.?jNmoDHD;CtRDMJĜo2F}ܚ!6~m XfҞCN :CPn?%lՕ7茢E&-Tytzɐ3o΃QiW[=,׉µC \պE=ߴ U=j!~aj|5<2nlIce7_6(| 0S \ >GU / W13R eZa2QGa3\Y˼əs to1fҭ%e-!T]P;(v/kᓱ NTQ֢zh0J{Drc |іe-鉿8%]4D5(fY.z!K@.=}Ne&/IacMST.ڐ^i58 ;d 5I#\B[ " {]҂Z'V! Ja׳[wP$[^: 7p)sp|9E>h\}U8Q́LEC_d_Ǻ4U"6ؼ} nR[p0,nk4#/"a&/PbH}{RACǸuQef*ӼEcaKfk&j—^p]d+_ClВ/̄tha~y AJo7 3`õ^'XC}B98Aet> @ՎRy*?ZAl5$AѶc"565HdfjcwDp-\H+ߠ$ f)ez$/+h+bnfsma"Ǹ`*'xo=`h+_> s,GV%kYC|\!c6J\QI31/`q$,+Ք'CZ,n$j#+i`4ݻ,n5 /%o}kIGfR-*gω(| mHɾ\L䪩߸q  ְ8._^3XfNurd2oJZ6O#( )xA=S[(Kj~]zI=ue#UV9_||YS-jh{Dx>hCMhשe|RƋxanzq \t;ɗb #3Ԫd/w6V.AF~Fc{9-}M,;ji?il>n@Yfߔ <<굘ǔ,:CȀD<ފBޗwWad1jM%q%6Wjs{FZ XME\T%pim`sRZQι)ޛ%zy)W$i dIA//A= uQerP_5Ħa: c5z-<EG|Bg9nzqKHEx NZ>hlẘR^7ߋR="LC(Bo⺸Vɷb^S `N/AiJaAFRҭ0{IWҴ%K- 1(ʞ: *X9 K?=#jGUBDq''ves{rA")ml,3JZ bYvX*b+[.j ]/H^:oC>x}p9NMNGEեIg)>nF|T<ĞϯLHԁ(ƑE yQA%Kjme-11w{{ć{.r)4sk,XpBHL{2;Dz2 d\u%m35/ xՎVU* p1Z{0R37>çc},LYn7uc =`U7|Ff lfy eMn=mtŋ`b.."5J$)Ok L^~25 n9_9S&Z@< @z3n˱9R,ҌtXNtVE\b..Aw9D dp)WPLnY|l7o;n̈́?u0UƬ*Ơ'Ĩևc/TB~p2a#$rv; KeRP}١!7&Y\4w'Tm?)/̟96{ 㽣Qdl89ɣTp>^7E g+ƥnXpii=.8:?;J] *솻x0GlH`q^*/Wnxm{8W\* FzpnDS`ߎu(ҐJP=]/(PLGP+uW?.{&mJ$ -Z$e;@)2Ղ&%9Hih([:ǟ۞ºtJ/eXl.,2 < }"؏whҵKi.4鈵KiexiIt󓓘n0AdEF4 d޴l?z\d+D7r}죕;u ~$q4h]rXnov%b$jvMI?֯Uq*IrbN:nQԫlmkӹvsTJVzMuKv ۃXTY,O0 57VtQ=M=)^e8e"9 y..ijgk0h6M/ErZG:ݽO;#_-` ھ_(E ޼?o1Ͱ5ȶm-N-+ ٦(0s|<lMnCPtKU(<=ߝ{rގl gHB̗.ϓ< d}EV.V"jև{crjsѵжUNSqx(5F6 慒p64OfQ{ɍWG5_uN?ĉ: )=~̬ ViGDսo* \IR bwU5ЪV)24Cij%(2ė`S\KCSnRK$4϶e9s: QG]EPQTʿH(<6"Đ1e=8oP}EH 4`7`%l-%#60ap"N?s&@^q`@tw?sZF$Tb}Qz֡%^NS~ ;5K+̉VG #5G-o+PC{P\/KլF4XF|r(O,v)ڜ`Q,|X1XO{Ȣz+je\z))(Va6s G|\%E/>d"vmHS Ч?k<+nikLϫ[N2dhW%n3M؏NY<[lcSZ-أ@խj4vn̾Յz4(>"6EH,3LKni-Ta[&ZW !!`hTcܙV*$GHحHZ0q3o?)u}m2HLI~".Ԅĺ_z(Nթ_|;dϠBd %#zg ?j^%#Ju(Ih\5o.ݗa|a6El74K&2Ԑ9Rj}Rzq y>PȮ]szu|d5^&ES%݅}v_iRX&qpC6^tmS9YSR̂-{T6J@xv~C`+S۰tQߦ#woԷw$(ၘʦWۺYQi[kї+:ESAD8@76$_ȏe@LZ66c@CNpI'̺52%|.͞6@\sϕ9n iW>Yr,(.PHZw4.욶uȇ>̂nVh"frQC?N:/wFZ:ؕǫʒ%POR'ȣ!3XgRP͒gPYy! G?Z?9={:+0 xٌu]4KvE#,$RЯ6}Q.P|(qNP:צ]5| +le>*Qp$s"eb V! xG5lZ`~Ɉ9Bo_qC͡dY~$m_<~FL X?#,7jʶヲ؇m@!Dru"Bһ fW#zL$Pt&嶓=יmcUjӻidӞ>&C.,tpAD$1Hw&ϙ:RhހET} :oC՚P]U|it*'k?!@JX +dDc D- vV-z.h$S)EDPW1\qI>]ĄDa;E%#ޏ+OEkTĊUPԥ']٠LF7 V\oEUIԖa?JF+U_T/& lRx ٟ\7 UOMY(BU7]ಅAFp!y3L!~mYac-R~vKLY uv"*IUF V\j^h<*ÉKԁ=\j͕5j >Dl)> (}ߧV sSTzT^ALٙ`w;"G3cҸsgwhs S{mic%SW'X^hz@X7 zk^.DSs wbKҒX?(ld8B@(l m u\eFd;_kn -S)D{*nluME lPUVLfqw3%Xyz@Gӟ`1#ޢ:ȡo_ $9xr$=^au$˚ˋHwQ$=*Fv[`}H qTTa3Ut _qK%g 2#'UcuJogH!]7S/P:ptI-~% vu,پ+٠W;^L*.Z_o.I9+g\#YKɺ#)@>0}y%ԒAsfARatiQK"77[5oǏ"\:X3@ :\W-hmGY )OYr2L,_=fj .S[su2^&l-S]yH$o7S{OiIEZVN<G[Y(hR pR[;yhq-dn\){;V]٦q qgH]`"N||$L{RlEG\d93Un[A|8PsؾCX=^..hV57kmtR zRC%ip엙KgD_a ׹UVBf#--Bא7ݧ#\Bl!Zז"`(̼ٸ_S~nnkֿ*뜷-RJ|ە saS^EN,Q)Wmʬ|HU*L h>~7Tv,:OskqýIx>PvD[0tRKKCZqȖ}?h+y#Sc?ND~!|+m xJ㚡`8V`a7uf &z ^oP^Z ^bG*0?ԑ%CEtg, `,\j7)P&`RkH\x G|~Bm|mΩm ecEj,-w綕gjNSQm}uwW12` hzG)^/47'hTCxqa)Ҵ8- uPmf^|"C,ewX^o gBi|ԙߖ+ ~ߪb?̖ΐn6W$;!m%<(WmAyK 3[7Ѩlz]},5ISujp]D$q2~S.0@-KM$GV1sRIF,s^z FΊ=v&Me%Clm h=~^R :\M/8~[%I1PI.w4=Cg82d W;ȭ-?SgKe@f[o̅0 ]0>z\"^9 Uˍ +uc M6sQƖ~_12tt '\p:a\H.W8,ouslX(U E(Jv%ÉeGD9PsܑP8.Z  bJ)mp櫎 *PAf2*(o71\Pc!7!~ )1/[ЭjU'h06)js0zC huz$jYV <6<"~^cvgsq +PS:HU3]3,YMD R1 p{u (bCD1pb<|E)@|Njڐuw'W:bq11,$s5Bbܘ%ev𱆡)i>i} $oIy),wkZn s%$ZZ<s_N%- Aq&"zF=4Z#`JU] mr"ͭ@J֌wXuSCۑ";R00ք!)CuӀeI F2Vj`9&mI4JK<.,&x7AxiLa!Vrlt|3|U@wPUh)eMŋtOOg,>@Ŗ8Fφ*W:;oT[lMMXL31H60䴧nbvfwWB+"w~|=qVlfeL kuYBuS"㓋}]kDY?:3"sw^ӈ'Ց$2O͒FHt5Ⲣqg~#) ^: /zːpS*,DÇpX9*SG3POSU{s9UJA5F8IlL*QV 3Q*c~hM ͊ZR&A܄g nAYUPֹ2Hj nNځ49HQy'+`7[MŒn5UY5!qO,VQ mNB_T_ N1&&{p6 \UW[,P4!{];αrw1ɫ^z4L{ͳU1js6ɬUɄBl W_26*/ a+ cED9;B' ȟŭ~deaV RzvTc$ܩ P)wnuґ Ed7"Y܀dXvS= z!TG] Ϳ9EpL&;F<^Z~1U+[>$Ǣy_j/4,"+A1_8聴҉8y9@@=)kT,8y&is0X2dxhy@)$铚3~ɑD}$2 \8Ctja.\TP{*Mq䜌MԻjR_J~ r%tܵ2"5Oa.ɑl~IFdM<\u[ Pn %_,Jg}8cnSp:Je$B!$1c]X=+ӌ[f, Mphށ g#noռkrޢI=О/Z_{t nQn?.Qh}@ң*m .c8Vv2(OuMIQMiLDCxSJut6TL Ĥrs ֍E :2U%ǮjXˋt\p&2&y n=EÛ]8|,ǿV% cu=MeHw.O- I-V?ř@1vA ˬУ(j0+0W\!ͣo:5R@hqRG6K,cSڂu ~`p9@05@>\m.S\EY-!(o9 b?;[O^1S1poFYYz~R ڊkh3[{WKspEذ$P_XM[SAx߆ew}%CvWa;6%7rf`&B\g90u()sbw!iR?Pe; Mwt)EXkG( )ǾF!*9qec;G) )[[EPx Ͷb)]@#XX|Ȝv,Ij8]+`2D,%BjU21+3EV)&&_*hI"8]wd-@+ 0@=Qi<ڼJ jTss_f}oN:f?D,'+t-gMj)'8-G_{Ϝ='nkD.ڧbzZ :3*xM 9Oُ8ANosk5M}k8#X#fZJk M+Șs؍_71t~HylB@p]f/ݶrphҖw|%8}&n6{W\" Ruicm/$_&'o s"snq5I&AA%F){z_zbB ]7L ̏j=9Y덑2(bq*@Ń*h_٭FEʘ?[̈́4IӰE6`em2g9qUrf"u,;;i(Or[uͦ)X {V)V/nHS7شkm yAc'̸w§p6@`eub#xZ*Qx\aŘ[En6p)33FvZ}p>E8$H@<,3r@z?m%T`zi/`'hV;# eP1$xx߾YW4f-!"F*` 8Sw Wއno|D4zh UӖ(gT~ @V,p$hgi% va(-{#5 >ޙOBI']mkbkr% Jۼ7CNL:XEqj"x3yY{HcnGŹsH/;34nWiD~n'Z~``i꿏6ӯ[W?'Ge*WGñݪRZ z|MQ>e`r}T3nv%N=@[Aey'LϝI敞^ w@g4:z "Ks.w]lkG4Ni "KeNA 2 XzGS)qbkr<{K(44J9ه8 R@kojZt+!2h%@yБVJUÛݞ46ô#,(DAefb;B (z͎ND@qxS,͂B8ۊ󽦤|伏o6TQ=rS5Y(nD73[b#_.ay?R(l gkX6 .s2n_jO/ +h;Ad>|׾8/22َs)&Dq%nГ:q U{{͈rdğzS)T0et!'_i/KٰL5 a-+軻x􂊆,J_Rbp*NUS] -f!2w#de/$&$4 L7s-yz^YWɯy !:)):M\x$nG;`dѡ١&l FoP)H1Qi9.v<;"fb^UIIܵ"S5Hs]"5jTX"wqZ I|ݥ*fߌ_-F^T5^XL"D^;6?IF4/4 o8q3y+Fdʼn3/]4s7N۰y\ O>(F<z tIv%M;k<]7`QgS If JlT|&)VZCbk\HL7F~N83&hOjuEǧ _D]L!#A,y'uCBky3m 6ң4ogoOn]a[6PB`pV'8\fBm=geq&xO܁OJ/QDҖq8}Vɐp\=AhcV֞[u;nQFk!6卬>Ve ~7wprYb\HH]Ji#eaLP i,ąCߴQ۹rup-B[ a)<H6F)kvfbnxqF4b2IL:6- Ǧ=>] 9!8LAt:ϣD88jnD*8w=ar33Okt-:l"ΦsݸoL/K oWy8瓁>#)ş~MG~ z0Keq4/vWt_WکըO!Oi>J&AHOhFDg@\M; Vt< @r Oo ϻ_XNo& \h]8~p_h:M"SEZG0I gEHf*qMb6@j8shgIP]wt[q<Ѩ aeV>Q%(sZn)J[-̎G 5x>(+G0N'Af'˕I߂) el!i7eESwD9Q2_*gX\!]6$vQbL.TLxB8.|R-Zc*F/W6b w&6Õ1(Ŧ> v! eqA-U? vX"jۯ4ZbnIڷ)xUa\IGaм+q^MB&< WԢsG ioʆ631" V#nPxfXOd>}TL0LU$Sf GQ;F٬׳oo.gQf\ṯYOQ><巆RSj\d-oOڗxza\=b/WnBHb1<%'bs: \BML~tݢ{hA5j$>lpɑ·dbnsw%>I)ͫ'cFl@ŚD5P3סˇe^t9=A{vf؏roCp&B iu6*נY(,Wǫa 2ڦ_g!\眉ސvųN S, ߺJZ% ְrq&yuΩoiߋ{U#>{KY#D l5ffl> }ަyD3VMhgIoIT5PtN0.#ވYGVۯX|XG y@q+ AcAQ?aLFs:4?WO|R=bw؋KFBs\[!K6M2}4RGB@~5sGTpϕaxyCc9`Xp7~q4mw2g93!{ږ2Ψ=E+?]`VهY\>pB=f)8L7zZb4Zs0%oXA30oQF`tAnNDwk#NLp>FGKxVMe}I$a:!#,;piFL|3ⷔMK YHX1IjQ{IN&Ih%UêֺRicg *D$O|Xuܞ;Yݱ H;P9A2+s~JG&*Z3OJ I^= = >㠣-^"1dM~|[,<4~EMLUj{W$s >^jS^/*ϲ/?p az7(Gʌ*2AغՉާ OWM;]37Y\W-![j.׮QF?o*TfY꤯ȏ{$u=2)bFUWO:NV9Oj 6ť0vH3: s$x^Oy4f1֋NǶA|4] C;"PfxFqE[9G (Mg}}!-#ԯp Ѩ5QyBDZl=N"T,?U?U BU!б$J@̨k]t8m`CrN_G1{ev]XhQ]%}ktEE78s5>W fJFLH67&YC(*`h"oV~1 ?M%!q$!dl_UJ-j:S3l|Ō_|wr8 u!ԄqzMn |yq"]qm\p'4{尉xpN=g+πFP>LJAgcmAV(ȃ6ʟ Euka$L—u-8"HN67Pc098G_'^}QeL_-᧘s/%uד82qT0Ko)*FZNf:ҫuWw /A5 :y_mt~ۂ~+8)ꩯ6vdj׭lG@H;'$r8u9Cr{ yʛX 1 [wn w6Q SO2.!~NSlCzvE[;{u瘵5cXpc9֧}\m$) e&FN}lc4@ /% ;AW/vRfx:?2|:DP$ 1|L w:"܍uw\כ7O畫fQ/dMߞ7d [D4q}^"W#q}~ZDY$RFa>]0XWT͉k9FGT\ek+<#ãYb0\~:1sLYъf הM3T/c5ޓ ԯ !lje@p-M^Osl4لX)qVb& hyYʹ#F5`N3gh_ϭE%XS6n,"Q=mDm#qr%Tg4x<)}HC>o,hFCXd\UIyM5 ARD׈Anhr IQ"Au-fEYLBRݨ8sIyڰ V⛳(n?g!I'Q}Wtg8LVokisdsI VP"kkN3mF,s|Á0)ͿvzRKod~ 5a Ĭ [T:hsj]u:FuϨ-x_zpdFh'Y"RwosxKn5 9*_Ynƅ,RGfmbafW0?:W$p"cS'OGhˎ[P,Zع$\EzC,hέ]psbCS*#9=i@Eh/M1/,WpdiN>> kli258kţp(xDJy#WMIY?voޟCc z%BL1ma{w陼%ҎmᤜY"d0[j6ڽ^b~MB4FH9NnHMaC3m~Mr*]2U,tVX%]Xbq:SRFUgYPbv&Fc<'#h˥>J'\'3 0XL-ӽ͉-9VU.*- ]dV ,8e?z)uI+da[m^e-'0@/hX pWh71xKD?Y-DEj rbGx5 ~3_G:o/ U %rkzP/2cU<Z(;*(U͛8dT n_UzhL"`<}6ڸ(XHrH8ݞ ߌF>׬ C@p_.VBflr;a!V+ˌD]/6< :g`59r2;xNzi }lMM|WR/gN0$7̿VMآW.<5Zj0<'&$$g!lNF0:WN,IRw; քRc3T6{kE?%>f_]KJoEߔ/1g}6]Ȅ@ƱXȂȻ饲"Tit@h[cZz0To p+)|4N L iu}٨ E8OAiB#tRpkb!?= xiwӗf_;&&jAc" Q DvN=x6f0Vx,<ɗ6BΏ0 hqD P;޲FJ0 3j]GNabqw$bO&´9o81ž'Ǫ r14.KOm覫cy~_  ɬ8 +A{QoiɪȅuM?fMz)ɕfeJXkraÐyp<^UR)ڌSCJK뚲QDl;$WE':@BʨsD;1ޏW+a .Y[x53.?c^iݻA 9g)Ek-`\ JƦ34X\[jПNKK%vWuJH&]x{}]q#-;{p^F4$_xd]7:$zvqd⴨P;üfǣhk @c!31):Ud%J$܆2?#X&i~yh=-+<tXS.|ucdHS*]m_Pp&6b';LwEy,&_Gt`8SOozޣ=xiL'ns Lny ElAYC,dI ,r3j/76V^:miԍǯC n*3Ȉ%7T H툝ZobgS@ 7RrXA]؈pa mPӝM/g==5P3b/,Rw5] BJIWQx3=BNo5nfO _sf;I &x[ ,0”'MsV/~Sj@XKh1οj×*GIvPYaidi7udҘSpp8`ůarʄr BL5:pu>Ĭ^QfWxzpFlKSͨYA~dw7ظ7{,FxDGTu: Rݕaύ_)sv wbգ^ai^,aEXMr3bGJ 'v|3:O_Y+i90ݘN{0l%=6I"Qmz}(t7[wʥS֨-c,%a[/:c~٢u0sKٛۮjX5BnguF1k;^vpMV3m6mmB i0kxAaあ$]:C_R'\s]KRR42_Nmx{Ĉl-A; G׫ԏ8iR?o\m*褠 @IŬLu#皀Ab 6d0޴X6w=2{P*)=.'Ϥ(pJ-w1$eS:-GȁʀkG}x;6܊`Y22f]=X {+eBnr lPm5sm 1بz[#dyIr nj% 1F7 n+W##4 #`O/h#5>6/ L( /R]oNuBqH%@7.&$$m-c,W-dЈG:ug Cgo.S@)m%9EVQ3#Ƿ&?)Frs|Ogur7TC64hpd#\Km|ۊV %[ MU&?Uß1 AڰjB:Uw=R܏(@/C>Et^*Fv>0})}ԧt)i:bѥJo`WQѾhKvVkQc7%+OٵqpgZh/(Bh 0)I#hZ9>|wo uslFg>FhJ=ӆx8 'cUg 8븠[Ia1qpb xE_ )V,4~-j2Z0f/v5꾠IK&wLA'f|f$ueC8^PcB -ɚy*~,嚠=*hU\JPc ~|xY1|rtm͔y{Zx׾ q)}# $Sw$ LHjR NJէ\j9j2\52HtXnF'e4B)w۴Ÿa1r^o\؇ڦqȷ4Y" O$Q'ȁ`#?k5Sdn+{'NжҀ|/WymY2f17H}/}m>%ɌFT.aQ5"m彩)ok0jȄWImᕂW.܃ћ4>1><3[WaO]CǙ:ƪDAـ$zaԥ!ØW_:v7}8H8ɱ}J1" f.vpUIY1dfNAQ9~S KDMtc*96s{5yڷVExN3EPE1c4S!d׬piaD9z4<1_ G}.NJ Z3 z1s9]ݐpI B~k5eQfSoƱkIˤ5R]<;G !VeʂW[TfC}3L)GWdl([oӂ'I1s,H1IAa`m]q!2cSac/nMk-5J2+?ppu|ųu,uخMo0 T6*] \Hj*[.Xfp\7SGX[O=v?g.i3p{z2tqS"3HٍMtׅ34c[ց= b`iw;QW 5bCȴSX^M;,9S,\ؒbqX<ܑ3hzͦ [7P 7IgFMUt2h6m"O^ iB)|oX%@´EU]ҊZNWYxV5VT:%峌M mlzѤ2\%3 'LK(C%a0'ͫc@ [Rٞ6#JÛ#a`Sb68L JLx;CW罭l.x俈1Bc_cah?D91#叐8spWXNZY6>? Vx ![FdmWSM\[Z-{a~.h9Wqavn8D^RhW r-"F"S-T>̯mShi_c4TaSV3 Tھ[9y@Cu?2gm/=&'H#th[Q͚yEJ#K %"!__nDV1sʁ`4SP{SEef?U}$B'r'  z[v)#9}sHY1&Yt ɝ%w>ջFbT -Ev, Oܠ=CT:{+x}ؖƝ7t fę늼=I$ *[) ON)۩:TEv X˝-|a o~8wJLs %}(T&q{Ewq} \"U1q. OИr֒F7ysn!g3 3)|(^  nt4PK%n>V?PcA8:HCsK2nw>FḴ;פNC`R#Y) I6@k t;sUa|Sh=~. 5E~/d$Z]No7q|Id@߿&$nP`]hg#h]ŁXtJn<[>Ճ@G/nvPk[HΆgdh~L [Hǵژm]$RBhF!{#Iszjكﲅ}珢@Rl V>kaHAXlG%NnN'{x/lǻ)ўQ@2ȼhټQu`ޏ^{kчpUIN$Q92œ_Ά>E)>4T-=у=s5a+K7S2ܚO3sx?>ݹ Kq .glP È֢SӉ }qۺd h;Iމ*A\%ur:j ލzV2j]]j'Zw0AD̰d]9Fj(t,-~-EL딆{vNR){;ou2pl x9t 㽢'ğy y5zWji:F]Ñ ֡0kO bs4'\oHE>9b`0_R80lLW[`u ,*CyX05tPMZ&vneFlv򪁬bF½O2LS3S;0t*R }`\[ BLѕG>:%5&3k4`'/G AfI~B-R s[10&Ա1vﶞ ksB>_R ג^nQ²4 FSwa i4CN;,u-%F= xU*V*iBj{u<=LI!uV?ɍVtacSxN%b,7䫲R8|:uP(x/r"mްQTK pr: *X֦;,HGI%j ){{JE,y`=K[ÀߦY]iuY+ltO$^Kp]'/{eSIpXnBJG%ӵ/jҬ0A};{*h@can,!zc(b].eAQ!Ճ4lG?x>k"is<W!AK H*KpQg;Ŷg[Xs`8\ .]^E},ԑG"J iY}a 5VEP$;t$أ,BCMzOZt ?Sq=20bUGJD_c8`PuM!fq(W,\\ lBƑk?^V/{ѻ8U-q6pKz=J).4ZOŒ]\&IamJMݽ}w!n۫'(`ZXsnױ'uh3fwlF{>_8;h3^A%ɓ讱LqE溉P'Cle%Hw,; YU{]旺&R.f; E9?yOYfӄfs/'x}-ǚ8\藋lr7}rAGy ޷ ]Ae&ާee7PfAa1 oibHmAYcfdpx1!#-*"Ι\A64flju 7@:Jby;G/? D#v/<^q_ʅuɄ=5_rV^_-[qA^0:uu$m1eظ8"ĆwN+!9A <~ j8:4Ue7} )+)@"O5PFCXtĹ*;ܮFEo"8e(vԮF UG$Px'L&P~:OAy;&.YmHxֽ{AT @2 xsc#:*n.)_W,L=Qn\U*DԭXk|ezkd66~Bo_XCD)ǪFʄZL o6\73,@J f9Z4"'iVxK(Xf+G%\Қq// 栾\HhzWڇ|ݒY&+uNVeIJ7f:TZ9B Ձc|6ΧH1Q̗Y6|}%b6mR%wnP`M O=USX[VܟJ*dĂӭYT\\8sd7nyC cޅnc&DAZr`3cc KaSCd; U P;L<8Nuy`$-Q)n˦L<]5xg&=9Y'z˵hqHI)@2|T<;aCXIYHa6LXB-+xf@n=j gXgcAuA,>T%%)) ]my\zpD3zq5R> >t6n'ۄxJolܰPyw}?\@0>lS^tzo~0=V0R"/8lBez2%m)3ЬN $~|ˍǿ]%#=km(c7バ җVn|mY"%T.[Z?v}+ު曅lv&Vh6! WxiƅmaW2*TTrFM/$FƄb@jKZc"s0aYtE1X4vz K<)KzehX֧NwbI_Ug. PyI`T;_rB5sKc֠~\`M"gE ZcٜLt\1n5˨Qj S@i պ,._!C/- 5Bh悪Z䬢) mN+ĸ#j D蛔R)Jn.咋_6<@JLP,D6y6[$c:fWWZ8=. C/'n gNys;;uhQIʂ *=pfA)Y ,RkC|zғH_ڲ,5HmõJm(2BgArR~O—ǵϓ.XVfF4|Vq4W(gMyjRQY1};~l~1$b!BH27{XU$j*oxESltCM.eBM/C,IK8J iقHs$RH${7u|qÊSiP5fRl[6'q1 ` .q/xrT ;{R N*pΚ)X6:(TҬ4p|9"&ps>TCWJϡ5`ܪ^< lKa`1gvΥ'!"GhFm7:BT#V+jVʃTX< 2&m>]Rum$HLʘ9 {k@]0>n3[\9TѪ\-Be~´[Rlj*"$003EtݏU7zNW}rCln+3WbZh7Z@L COT.~]hrnQ<,TB+n712#7 ~[>MJqaە4)gz$]Q(qŎBK<ء,^ԢYҚM{qub" U4`"7}S,]"gne ?>#"cqԶ=!?}F=VH&: ;s0Hk'ٯ5-7"AD|ݣTbө$K;UWNaLGN\OHg})cM ĥ!1(+P-w ¹~gFD},FI/ǡ-fH{i DgSuc5.O<=z?yk/ ҂cH8$2(ʴEIdn[Sǖzc |FF+idTC|'|KYdXԡW{lʲȱ0e dC$H}BwWqaf@ ᙥړ_K|Ȅp傩+q?Vf1jdrvv\ѓ eiK8 RVIyZGQV=;Yn70'?"dԓʿMM6$qi""p9#;͵PbK԰,fQnVLg@քLV|ٴ]pɌ7Z|$/sa0f*!lယU7УRqۏ'9,x@D=*RFEU"P:)p,&~M*Oy _zրf9l{{\1?΀3fn@d!?1K fߣhͭ >!,zCHQf\cmgϹuʹ+j[at1K困y"*M(㕷ǡ{˂~Τ*V( G6唫 c|9D9䤀 /1Oy1%4Rñґ|N6|dlo58|@dk?kNslפ+&}L}$'7g1A騚0X6crh1cXTx눵 /` "Q/,zJ\@R`զi&'[2yq7jmvG13<:X{:cBKŏXo9҃ #NK:reЉ} .$fHȣ8' 0w<=1a.L a/4NspGB9 3Upnh  xFQg XOwQ|H޻%nnѼpA}649k#_0LzMQ/[ u4{<"žco͏y J ub%BS|x!g6JkPw$1``զʽ2fU~p*%ByD/x&:/7FSji&CXPa5()`mܠ?qkNWF7 u\6F1 %7)b;vw @iۜG ʉL;ܶ~Q 0RzK#9:kl]D# nт] )YyԢ<~e86^MRvQǂj9|>Xjܫ~)ŎY瓽2l lC p;" fZ'“A5(aٝ6;ʈ$Saϛ4p/̼x{Ir~BÏ$JФ#PU}r0m?!n.,.u\\4pzOR+aljI>D:sYX.BmZPHz*~$Xȃ//t:4Y0,Wӯnd"# OU6p@x5œ9|!cQr*:Iv1L;=~hU]ROz=$&]pLTyYF0 oQl\R@ШYEbDxKHUv̨s  /{9Ci) ҽ_;6G '3-vj#0ViTG`>J?42JS=}OZx*uH MAdnR1:W=8]1g&WE枢DmKG\>f;X"J4m`q( _,EޑdK(O% iUݒwEcGtaIޡj]s:!, __j4 4#\L2α@w`Q*M[79"!  ZR.Gr;au>8@d7c?MäUMmh8/G+TE2%@m$ *XS\xph3N(Dw3lY G RrƓNx4 89cl&&y%K|e[`ޅ5QhH#HJ6EEᇦ5Vʉv͊ O/JhzE+ L&qic4DJ.1G YvSm)f2mcu>n>3 IfD{O? IsT4AFWFb $Μ5<T([Qъ\ m5|rQ{#"WAC ,(>Egjx!]}sqA[0ޝA "$έCV"+P]Da_t٩?P$%T  h Űj:qs"GОҒxLv08(K|s;XްPA:Pz1^TuO;ckRM`S RWm탤h9;h (9׭ x$xy"2hb=D* ݺ, eqI.`)jblTצX;tQDqȺj AO[x:0oN^t;{e*u4P!s!;m8 `71bƖ/fC2nfMз Zjf8$weV)7p ',^Ϩ|ߵDHٲcF!gp-  n{;`pL/NjF`~D1c8j]5!coR[|l"&~38CܷSf6N3b-8a@JsOZ?FT&g%?RYތg' FYr*\ {O|C]3 N,b$%P`2T|D!)n<}<5 lᶱ(CFwG<9˖JTPgbH+E?JW6)ü)J|9AFG0Yl O蚮%MǃA.7"'bt gH\&FI_nɃ]-NU"`O#Bo?Yv .xRHDN%lЛM 0J1[K+IcF(DH#N3Zi7b4I:hZJb4#n]3[`K#?)\9.?R+|IN}}zh}~vR;iS.p>qtmpiR2ԩW(G ѻR BlsJl_Yf/PLܶF[!JGb({KgQ2-Ns/H6; e_fNJa,8}K^ .2w{б(&vNjh"G_nf?gl|7iz <Єvӵ]pʼn' 3u孖od( `5e 51B:O_&EUk\W0z":tb Կsi=@~l(LL5inq&^1Ig7RqBuF`MمkkMҌ+X>dӟܟǯa\v56;06}zu55 v/mf$[h)( pQ[p_mwlWr[&wbUXSWIe8)B(T-R^3wpv8 F֥iܕcĥ9n _\,et\%\Sk![xfhJo8r28єg8Exw@_f[7Muf5MbbAn tlGxfiSci Љ9u:vj"jL%Iܞ ![: ՘՞fX(*cD7Ɠm=Өr'TȔsRq+|,(e.`/xcи;EH>jg:*7)H;8GĚU\}Dw"r$6P[1d+*F5XmU^6VQY Ӵkq[N*Q&\ @$ly'_&$(T^a0ڀ~BIL=oCs)[+/dÈmġUj=WIyPp0aby6$D?qq<ɋ.&Am?QUU%Đ. @3Տ,Tz-| 6,&'9+><u#"ul:o`mAm*d Oӗ)yhT8I5Z_OgJʠ+zJ8ۥL0 p*k1UI^CT2@uфΦ#ܷȡY!6!!pvq)m|FKeb; ?~|o&fȼkVH)L5x:qphp;To9zpѺ Pł>5IAXwYJ :H0pW䚝LU??42#w%z,j'Ed g,[0^ŃULZGv9&WO`_ isozc(-Tw>M@X &hrP.='1 Р ,wkvvUR}ɡw92ΫM)h*- .?`U{9+e$FK1ℼ ]e9"*)Nlm^{( w)W.+.@Q-KJ{(_$m8'Y8 aG3yσ7(н1y<ċme2?!@W Ɯ; b18A6? /GYk\WsbS?t9NoT 7#\ɏ2;l&CpX xR$kTZi-3kـZTGuFP%p GxeUc'Լ&2 ҄F(W-T=qȢsD ⶉ~[?"ˏma߂whN@Jc! !#Q^"(Q7#! ^0Lʇ@'a#\c T(}at#kX`i2;2cZS$ y>)uweH$!oӐFz'ch 1uF _ &"0EHލMBTlL7ơ3J :-mY`{ޖ;wDbSbs\u;42D@zmڄ>l~_#Tq~)7;uDRz]0 *B1ҧfe惰Q!9C08>fRCApN,?ҁ3۵R̀6jpH7u/;t30oВ+u0A&r:}ާIPnDkCzSlKG'o_+߆omG6[.cU(p-D2ZsMC^i8 jeP$1OjtqUC jv>"j7vNS?yUO-7\Xޕ|"* G.-eK;lb5 ;<{Rքal,oj]zrT}{L*=qnY@vú [hAL_Abm2f$ڌg8UScpLPjiW*AY">rxTĦ.?Y\/W42FNeڰyw]ëPj6BZi.zmD: .h"ⲐS9RK5. i/Hdޒ N!kBw6xTT 5k/cp%c]0R6Pnq >LS 8%gFrⷿn{)]6E:Xyo᧧+-ĺSCMb:WЎY+)`}F)19-C[t" L0'e{`A^Dy?=|(u%U3p#bѤWDA/@QgQ.4_\!Q|z* tGޥ ڸ8B统BQ?S:P9̦ƳP1ձʅP.*'AaDy[]EZ Mʛ+>/W̑9MjM}wI{ jBj^Cje՗ \^RLZsa\F((0as:ti0X%3j)Z۟b%FDؙ"lՋmf$0F0zOSt/p؛ nІ'òHL Zq?HhXԉ޶UljauF䫒57Iۈ{mM H1! MjWg\+:f_}. 5VbI&3A X*W54# ^lNc+l6lLNvEpV6IeӃPZ}fbN'{ gbM+4BOp4"[1pɰ \^}阥"{Z8<iq=K9$_ S\TDuwW4&i'|Ӄ Ղ= ~qZbvSXd$Jւ#,d<5P+l{n`7вmq/<1`7]ު&sʯf5*!qA:ʬ;m/QGLoQ7=hx, bV*d[wW * WY|Y_HqgZmRd>PV~ A ڻ`@Ԡǎ823jFGL=EٳDUO3kf^GD/[G<ħx 1 1%S|*]:\t%׺ ZGcX,&1Rs\jRWC9vU9 )tW8a(; dDw'XmRyUڹ~w,Yk:EƎ7"i5S|:MP1 !%MGޗc`ƔXx b\*)WֲIoZ'ݾ2Acf drʲbfA-[[>5Vœo 0Źsl4\lCFBJfaٽ?\ߞn#$G)\h:D|ִ}^*(FzWu2b?U'8K? NKM(1h5Ի rRdøv*:Pf!pq vWEO<_A;3s q™(f\h)8p,R%3P D˧-Mŀ|[WUTz$[=eq,$9NEkGL _| $M[YJ/D}<>:YH VY/.g0.sww)QҶHZ(!+&! jF+qxߣKi(l"88p>QhStB8O`&jFhvda!;^  ʄ'`;^!5B1f텈~BmD4TB:55>#1ؚEɪ+B21WJ~=q5ZX-Z w{xc:H⟆{wQ9RPg #xz-sc UH/b"7=bW>g^eCGD?RU^C; ^6{.?m)\/ UUj!_xܴxeRoŒ6߅˂/08ZRr! r?CNLDY0DkG@1%Ras2Ӌ z%UZJX{֒f`56>Y*G]L1UDJ w׊Qabd'm=״A_nY"TTк) JH' Q>;}Q3V{Y"bWDm`x)z;R>j N;D)[`+ƒ5&P˟TfaDNK(nٓi$t_ +.| t;ٲ =OJʇ tl d@6Qio-cGj{/%a?$cny/_k>m#rﴈicM$5K9H$lOcnlҍGbfHɹ F4vF-3GGr@QYKd["ťcs7i#u\UV'3 MT8I{rV1"%Q1?W;X,%QmAЍRj"ƓH5z6k kьp3'ꂧ{!)[^I}[n1xڗO||2ߡ"Xw${?~{;I*DUGfw&= 'fs5X*Il!&E̪NR1ZVryWr;<3=wI})sQha34Z6q ':V,D /rN| $ Pi;. Orl)h%cdQGQ=N4ҿ) ~CRB;x"S\ۃ!4 7~Wy,Rj cjД! 0;0 cq*H;g >Nbb-A­YUȤs. ſyzB&(<} ~Aɽx_4a6d2,3GŶ@V98:,}M) f ٳNg*b+W F]I3ІB[{6x%$9ګ~j&kjxt=.{E߃.mRGXι,e )7x!MVmʡjqJ6@ <ȹб]ߓ@7H`XIdD@wfǡ2Whmq&ɋi_mƵEPQ|(lBe õs^dbVC5Ȕ߽/qj P=eV2}&0|f*}i,IWϥs A! [HMY@} yhbd(`6A1ݷi:4@:cлИ%8_޽yHN/O+J*JE 9vcy!vgdO!r6zQZ7WvqrR\֭+_u̗\5^48h]/#:BO. 9"ο#3M ?5;[vWʜ&P?Ć[j{><5 }vFa*S=}$5gąD^LSzUF- :Yɕt1ey~gbnR NN$# T()8X7Okfw"n?&τKlN]jm<\qӎ{2V E[}WB<#ѹwxBRN)8~S42F#òGOK!ȢÍ|zv)}ጀ-j5̕ hAdy/'9G a rY*T>3&՝>˩hJ^ #H'iqp ۤ;EtKW`Op w,ey#ңh5?DuWΟTB)Pv&HAz{@Mxcp)$A|UāP!+73~/v*ndܻFg_?ثɔMa yEH$ωJl;Դu "B-=f3 iQ֞4EUm}4&`Z!NG *r`|?}C_De!l4 |M+m }n6\p%_['' Rܖ>yicm z,1J^Ô钚!kCc߫#fw6K4%GkKS9oDm34_V5$!e͠YB0a"PK^K]<\= mO`*$?YYԦ^Lu`+}-;2OҠv<ӾKILV ;F^#:QH4FA3_4wWO珁<9S !w9@"bY#KXAuQORИ8FBvw7RdvMauU~eejSJ!ӗQD9P2şNEy] $H]X&VI\k.H(xQ [HvD:æyE~ ghynσ7#X`Kw/ #'y#TQg}bcLV_ےd-EP*_\t&V2&4k]ܐQC;r2yxa(w|eQǚsX&;oRqx:e@=m[mrs}?%@:?VQuu5L*J8!RH I3䟂}A~G#ޘ#Y\掐%iU[%{>ѷZX)P! aP^,ۢzEKn)W?ɇa?btc{"!jKw <{NZxj)EV)1 F݊q|5C1ʌ+[it(FD& 15-=mL's(9guANAg q_@4ȠfxG߸f&Bu*kʮ*T4R$#_q㯍4DǤ  0 =??xS p&cSOhKԽ#^-~g7cc)9IOP-d)KČ_jmO ^/Kܴ\`3Lj/i,:[ 4tvvqHTϲz˭,MUx$RyQz'U{|y1/%U<5aN"OtRbia ù!g7Z*#a[1K:8smerISQMJ0HRr Gwap4o໿tK-10m *ڀU-K׀I=2~#9k0Eun_2"U:0?vUlGB4Tr&sC '8 ֯PAd(}' c᫻ؓp䚋1}(# d nF@Pc- N8yh Y_S'\p tDmzͬlSk"%@~lfhmHO10L:ŷ1XJ_!Kv0=H]4SRѣl!ĥBB^gr=Öw+Sd&fܗ \*&J;P=$R~7@j^波9d\3(U G+ Iu̩>mcJ(spWR:yU!,&[*`grMMw{7Mg'OS}\R8'#5RH+9Uߴ႐Vhe|ir!-v^lCsdPA_ ]toïd$Mkv5<œ>*%;-tJ|9͹ǤRg4=EbLR{dmC(Qhδn crM{J4[A% MNZ'(ߓDfTKn'?!Q\\wW"LOl=pb(z!9~\o~XD@!yxl+ŷպ'g䃲KV /b%^|wgJӄ^wgqDʽӈ11*;,CU3 ,|~e.׹T04Xm=+@isE)(Z֌y"XDYM+WQ{!%l'pY'oH[ k(baִ{q4@ ^Wj9D~+^ѥˉ}H]ٱIYDMώJx2d 8* =}9̐KJSZ<&ߣ:pLTuVR6 mNg4`ߪN8k E=.~[pZ@e>x&;]~w;qĝQD@=Sp[+ 6["rAUGmssAaUQ?VheoAEB^5CPV RWHw=]0<싼TuۥP-.gD'yЛ=p8EXAJ}- \pM߈-3eOrfJEu)B9_3SV,)hƳ*:R*1> ȁvԨ% %Dd{ Aַ%tW$}7ON>4ggC)'p ~cz ]|˄bR|r 31(RKc>84xSƒ[״x űlỰ86꥾R04+%ߛ]~ܑ{/9|Rx*HՁm&@C|{ Sl0.jLQ{*1QH8ޏX?f v9MoEbj@f.,M/d} |DfEHB;GtJ=46s|\`V!FStİqBck3NIZx~`/D;dpav44ˏ\yÖ;yFBO!ɋD#/I[_\ wPN=xϊH+e̢jzU3f!2}0uiv|^ ߷52 ԗ ][ݯPq˽3SlBW)aq)j1p-ؑ1KAU-QkЏ },#F! UP 1w,4uӴ/S'Mqs.K+A${>Cvbp6MINpcyx<>~čǎL8j+LH|TGFl`;-Eb` NWE $.2z[WJ~>Lcfە!ߴBlwt&dVkQ)9r*XH]بmbwN ":Htzuw Yq uܠ 5?1pU+4E0wڅicQ8cC5!~,*bIP8Mj1,=R:Eu@5s# U2;J~azSC}|XnB1.etMPw?ul `յ?e/0{oP P#u4ye { Ҳ{h<D{3B4b9;4[@3FXˣ2"r T6*V/h4b㈄`/BE8ehY ܓݰ+ Za5tiA'0>7-0VܳkęoCY03NgowK8z^.%e4Dr0є֘4:*f15NQݎy*JV.U˔="wf|3^|dU36H$q&kX0)n%Bw`:g`^h\;zZ#:VW=̄?#ap&-_eC$~9d-J5GZ#o E ,e\D FJZ!{(9HV.2@#g#b9(#ܑ|l馫'H< ;k$ GauKRBw?(ٍZgK)GN: c+j sqG["b4 K;if3x}U%-Tb]SvEXV(uEv a@BWϻR"C.>!ܽݺoo'@[yӫT;dy曰h:pG_pxIJ߭$?v kP-X+2djT>& A(8lYXYW$] 4~CfqN_0e8X)i\+%Y 7ڭ#&GJ=iФ\t9^\ʼnEfL+Q^ƈ ?ҏ(9nX}.oDB.rf_JCLRw&`%BS5-'<N>ȁل__ %aoXy-WP(WA4`XUifhp4l8{xfuK;E xBB4H2wRvKF w *p!ON7$+su/ɚm2yw|o'lx姏WhbzTE(IrՏ12 f(jv 0\|jʴNxDzAN.gQXMwÀf}`{ )#R$/#[T\P+ z* ` ғaVX`=X&=5^&ڲ؜!(GdTgFKM0I _ku}]DaiE"J_n[~PSEqE 666r% P&wI~!(@!E>On8zֿPeYQ]=tP֪SFϦ&<%đQ-R&l] -D>RCH4><'Aq U ^,Vx7ȢJDJ?KsG+laJfQr S ҁjM{ kmj($2GGsA?I<H~e0B8XBF[I y>e9s"f +0긐4fPدTpJdHƲªIqΠ͹v'O޾jѼH$}i ~K{ԁ /; )u jtgzf "amqa4#GFhOWѵQ%GKB w嬭ܪSP-sV(}gc4fT8"s1?@ hΆB&{ 6*ađlk(RbC̘`CLԍ+ >˅$2+c[b[@3`eNƒ+BT=`ڿ_?BuP05hwfP0@fK& >ً#GZ5hr⺆(9[1'=cvxh@o>$U.о;$NIv36C9wrP"`~Xl>Մ+iʤ79!YZ=ހySVD_ YmEkǢV1XCm!6(eҫ@z406"i.Յ7\.PRǷmֹ"ݍl˥^܊CޥE;g4F@ZlqjSӉxsr|Phf3ޡϨL/մlɪ`7x/ 9 R/u>״A>e_YX;)/llԦ p k:+-d-Ew[! (7REK>%F&Id֘GL h9@5$UpnVI w)<*2A3>m1|\]r %_ (M=Cq&L4}&ǁ#&ݸϒMYF ^$b򈾓@ڇOsLW0j9T?zŽq.aņ~G%Bfn0?O&l?;:n/݀6͕<3 #/+'U~&YL{)ZtK]ډaT.%?iV9ϒ5IGht&orY}K  F),8uW?VgN+b<}.ثˍ4lrٕH6e53Ʉh~jbO9:jU<#x谁X0l7Ⱊۏ 9nKQ雋U-ă81m;¬`j7JJ0wL*ReXbڕoN;I) !9Ss~$o*r۷;~\OZF4L*V#Ya7TPӿ1!/TBۖsV)P+pvٰsγ59aFؿrm]=)n ic+wɁ\JRHZ?:zڥQZٺUNBU'oC_NjqҀ#9q#;s#ylkTS]H<&zE A.`Fq_O`Db>/ NUfvF#;Z}ʻ+۸ܣ?L[u~z)^B O+Q.XAn 0 S.^ (,l$4\7Q;:WaL> ?e +O+"z6v( pv8.o iG8yݤS0bHGu̐'OcjR al>ڈ Cb'O{rŋK_j.l: ٺX'H?=2Q&QI-֍lc}Z jUПN2KSYxaoUVGYݒ5l*iܙ` "]syʄ%)34^h:$p^}ʘ5)0ru!eZ5VD|޶ Pբp-3׹B6S@Kh߰x0K8 DvCC ǔbav7S7 9{ޒ"SH&ۉ _+ecabhHKx4Anrr b |saV\ \XȂT85lTU\P:k];޼<R:Feqݹ.OtFn/ꏏ`=Y S%˕0!6y3c k̉\7>`=p<"/aC͏zʮY[6C!8섓'>Kt4xGg3 /JFr=sUjOER֍F1^_@y?w'X])R-&q㏩#*@~6lM|N+JqA*ӱ=?n `piҫ/uVŒWoV# GR b:'p2JwY`CkNAP֎5fKJ> @]-İ~g%T0Zh|&*1qZń!'1*עpm%g|_sp)u0Lq(h}7K"aɛz5{_F D[EԊJ;{DJ1D\e"{s3[wxJ"q ٰQ`Utd3F\NJ/pāQS@ʑ %Ms=7]hvS ;E&[CE`ǹ'QZ#`L`"ߜBVkrH:k6%hhi@gWܜ]'=}6Ԣ*<;wRS\ӯql1/*5WxNXv_Y9CNB B5$`^mghh\12V-qh)vܗϏr笩8`zHm4Q比Ҏ)WeT^8MX# dtz/!dxrjV&ubl W`D%ݒT5$h韼io0T,OFhl_tSkm֚X2bǨ5_JD6+@YXxZ+[Nm&!OSm3#/4];s*iޔȕ?U*^b7g%/2*\Bqb IXoz# @T@g`)fP>g 6SZ\ m29B2@T|QsLۢS gl= 0D6.Jfq2.$RX'\=7Ij4GnYj5S+fvSݒ?Ҙc.(+9IˇUj`"P݁GS-6/[qg5V*2+ Aأdvv?XomBX k-j1֋Z^-FhSOe (s\ . cKf4 d@vgTGBJ1# ]\-ɋ;OtcW_F9X&T'dO^7RJ"SZTSY{T9ԥ0^\K%6F4,o嗔kЄnhc/s#Kn+uՖn_V|Ўg1 [:(]-KrSr8BڌNHyX/ Z0Dk ~/W[$>L{p;#a̱S"Y@:nBZ R%2EzUXpfsdc]zy}E8)'_{h=)yw^ô 5BZ$b0 %rlLto϶|5M:<Q= Gğib98)Tyq"_l?Y\)y5-*ЙKc<|| 7c!wTh!^J}w&M#¹c(.fii A2oGnj5|f H& 7dú)ph= lL=I9G`Z7w`֑KI B!?R Uo7bӭr}RM4"ɟZyVe<}~SICSDG8\Z`Qsi\Jqjj4XE^;FpO6!3`'E0}2x7nI3@فwگvARt9 Js4 2|"4#}īa v}TX"A "3^:!P&v=FK(Rf )^fNx)CBrLKsU \$¨4!PSυXpEk$LzFȇ Pww\  9FuŠŴ뾌I_fKu9˨L=I#0e WH/Z1.Q&\-Rp̑bW9?lŖ:7MS(0??D2`4 @yŅV1ҰCxͳ<-6WH+X^PxYQpº3݉AFK= m9Uxs*!Q c7ȓR,E!;r3 ]y{5UjOcqg5$[Ra $-|M*9i*9 k yA=* 1owQ#wa49/>+ %kƑVn$TotUSƇC#'ǐ[9Uf0*ཋ;lm>AS,Jp2/^/}eL<>,(w5^\5zWdFXLwue]^lԈWLT'wR#- J?l_l}IY_ /F f՟׋]C8lazU( m쥔߸^OUy.H#?7W;$䑆!T8j7pq7]{{!'1jnÂaUp,0X Rrv ,3ɷH/4Δdٜ T [tnbh Yr.n:(_wG?,K t߽)@c=ƭmv].٧yF!q4" RCW-:価gUnjPuFC&+8/k/VdCE"'H* Bn_ؽos&7-0͆#oX"NI k>o[hUቃgCEctliUa|r6U:j(Ԯݽ[=%nĠg)~0+fRu斌|M"\e/ӊwAh%W>]rS+иMb3ji*|ƑjuإiWGi\ij͠$Wy#Yt7KA`O"UZ|9{'#oLM]l bis0l6wO oӓn.} EF =aEE3hu7ᒛO.DwQIbX25ukg,B`RFiYm^1 ߄#?/6rTMf+dڡ~]ܔv,h[Q9=A<4 2]~91nvDǎ6k+--W9f.@^\3"pD!-(w Ou9/PՍ}LN(O+s&*%M ݞ!p0\U396ZHbu5w4ȩ0nM*H3)q"u;NZA{r>PI BgaRˌy&ӔbFjJa1b (Bp}zǩLpCd-TkB9a]J12'%;s(D^p sd, 3w\'Z An&ZA.70ύX FMtd{`Ċ3$2 ]M⠻ [J CB5 0Y> |@l81TV;$9 OcW61CT&X` rlUEJv .#KP:Mh79`6Nj;H@9_3;%#٧}b7Fs?vI6XΚVɒZK%sO6w@)|Lm\~ܺWFotY_6vX̡K9A8[ FU{jb 8-A]vGcPU08o33ׁtu[[9hYq?:^Emp nq<@:r(Y)vTozDt@8ŭbitFy Vpp@5F)o3h Lu-d\E1g(H,KG)N*y 6\8֥{K+֔wĥgpb%t{ /9u:ΉN  ߅MٜOGTc[ʄ) 1i(%6LT? w}Jyٟ`fL$e]at]\e}S z媽uc(c){KB&Z-_z<}X_1iP<8O[LϞe*F<9EU:+cCS^^?2s?Lh8¨]w!}ۘ('JK{e1)XiXNd}(S`m)S;qH!3D%pԬ|;^!4R嚝:hybT,tD^(-H%;V%b=Ҹd $bLpk.hx4׭ݾsR~5b0G2ZTeݐ}Or@7C_4GRe~hz*gt~$NikkL69u Ʈ_78y&M*R& Vcm;M~%&s`ҩ*lSfƋrJ1c~MH)D]hZ=!T"JyMLR '(' +d}aMgi$eiF+pr6H*_f?B@مueS" %ף+/1P<$|=F&vCsF#u_cg ghn^Pz hl ss7]2izj_3I#E#o`q~UVwBa*លӶJN,N2ht&]Sc2c0)i^YZ>tIcU͑0 :q$HC xEmyMh=0KR8.(ݹǟY),fSA:+EFeIJT:S?Bί 톲D݈u  f`ISLUGgRi&6CAOo3vg,Sbt,WM5WnܰnjcfOŪzeƪ izHCC JkmXXޓ˔4}lj hj\:qSd]@pPO&(Ph7>`oߛ qPٹkaVqm1ui?FQT}uTS,m+4Y:}yȋʋM.o7)YQp[r*Óǁ!2sbVv@sO/L-Ǩu_#$a6M?=|Ǥ乑qޟױ[`c¬]EJ!R(iLRǴXhV?ڟo+ q2>âsҸhv:9#Eq^xC'堗rf_|\?K];8X6єB]ϩ \@}1UgJqUoD}| Ve fI0*7/W_7˼ԁJ;|u[*mYFfNqs,cx֌Q#j ̂IfErD&M,jXY 'P:ߧ/uw EI^Ӓ{ǗEw^Zuf:|GR24#e/o\"5(lVCAOҪ]C'бrUq`'HNǰjyG>jS=#8I)v{$|fәdCol[ݥTs@P,P)ϮTB_B6Gx ๾X+hHYR\Fvv7@qKƼC|AxAs2~nms,U{d;N.5Y֪rS^S̗C<՛ke<4z2A*ˍR ڡ-͋s]k4,賠Dw dr/Th:G{# F'J"fݲv׼ UV*5=X!S-A|uxٙ {xȯl9IӋS%R$oR_s)Ƥ QޕzovAC$X܇0e"Y$ShH:Mf "RY h&S,"ٍx=O D[3#0ժXZ wnoDQDf{1GC X_ww`M4l:.ʮ",v KM@ c2 *^p+X(}dަ-0! >hoe\(*&ۭ4d}t:g.4H X|RJ~2~yNV-K3ڢ4%jl&riAڧ jcH:Yp_'MH2[4($*#$'s0)ҷ2Jh(4U/COH e,yk>Xef|k:jW\ Zq`C@8-3ee$tp?M0ݻ?Cعw{\ U _oT2o@kMGK`CDJ<ީ1'4:vbUu W^E=>؏+.C+_ V#[񦸧op6jDߝ\x^;U0a- )u02,% ڶ'I8{/$Ue C tѬQ}y1pG}T8׺KU{L%nyQO[e!3 :&bAUCA?Xo/!+`{%<,bhsQ3i* 3b--ȊͭblgĔ Yv\xGgGٴgcl@q>5:|OE8+T8ŐkO;TgCg\J,vy 2sfox MQWAͽۤb;^d#'uQ5$ES ё&;x`Pa;zp"8J›k῟o bBBJJZ>!;dX[6phfKzɪ5](ӉuЀrMW$]aw>9/ړW[GG 5o`AQ &4"@`i+8`#cקAyK{JwR֑Qvo^ τ}Msc<(B.IV3zYߐRl>WT/~!n7pW5Du#9-9 o%FK5/ ?gTD%Z[oή!rz[ίiU%aAK[>m0Uڬ*m␬.H'i˸䐯 UċRjb/&M1O2.˟s]i&˦0 7ocm`: y׬JA+CH|8G{S=ܻBQmf`tnzg - t‡<ۮM:SzP.K' C5.| ' g8CMĜpWbǕkONVϘB9{db2)ؕ"{1 8Қ\><M^lDoH4::BQ3um/({FFBKH0D-43EA'"xy53L]}kC6̂ly\@oFiь')oC`-'O%ExؔT[2FnhٿaP hj\iA=Li܋8͑6ĢT 0nʙfhxy:| bn$m6 WM}j=/1v;t8,̜+>>HwfxqwE~q-bա:rr!& &؛ߥA]x(~RCO¿G[Չ< ycdDzr5ă0IP!sVul nΖF$,XmIcfc9Ѯ¾&yRnxϋs&MaOl/{^pȼHݕ!Ҭ@^& ـdwUPo .Jd6gP݁)Mmj.G =8`>tv -2_ u,ݡ.o9لds|1B"UQ)՟ hds[0רcJpo3|v 3PCG]1&6uz; LrDǛK>\eWJN; y9l@6oVwΪo|pNXCQ8I3MnGr(lE3!-2ȵnõ/;/^; G5\jMa+ZS ZjrdptLw#pP&JCp3Seq 7ԚpJ>l.+Q"v|6-18y5A%5oI3c:^Nͳ/'Sn<.W^I2SޕTUx8./K(Mc! Z]1LZ$Z&Ez7_ kh:dӂ1 8PbĞ~nPU~p8iN+{)~>n9!KnbAyaE}0b:"j'U@OLI2ra>w)uK͟.Ԃxl-M&<oz$ϐJItE`Pgvhc!msehyJG[\wF :)e' du(NlmO7oZapZZd%褙,mAj'~b1o8r:[0ÍrΪGls͒'C"ra*9s}!~>.-pcrl#]l:#6d2Y,mNWPd&&*eVZ"Rӊyo`W4/ѽi&;jt RRk*2š0i{&=h6&U:UNE%,.c&nŧ%2PaD,l:'6Ů[]k44>RϸI?LX>!I3]gZpq}6=g9s`e{'5E\T]o Q Os|ި |cqٻc5Ij9PQ7bwp<0 x0no1nm ME%c;B4t鴘AO'?Ն Jϓzs-XƼVGj=aŨC'$W;vȝy&dziO  l-b$;I2i0.X5kY4 E؇L˒sgM!Հ*ƸcTnڞӷ!{r;-@h:* |>'݀'"OC1K+[q>'iGИl9}cq#k8yEl.d"Vl<#k#9\b79y Ĺyp%*xvƑх,M᱗, dx`IL(wdIMjtG @{P$G䯂ṙfLCݻX nS!vUXS H[އx%gFM`Lx\i& rK/I`P}tZifz1Gc_V^oP!@paܻ2B6xk&s7[2RDqz>Q)8m>Ocѭx2p䝅6>Ő xwX<5q /%cjbN%Y⵽"d0t7H ZVP?1(5C [D"I s0HQ&Bc \7qY?w3 0¤w9FȀu8d U@JZu{s28?L=$ i5N9(#þeq0IjX3@f[G;{xlYf%,aI%OL)G񷸯R{diHlD{yz4x ;45 Hܿs"Pʽ 8n-iÓ6"2q=˜y J=o8?j.FؗqJ5H&p5 Wv"U$h{8bVMڋ) T*HLM9'UcDH#`f+A90q!RB\{/8,;3|kkM[G?~ W+ok:8JK@*o35z% x~iyۀ߾E9랂&H\U@ F4Rirv/$؜[>y4I&p?â4. ,6F >vw42u_<=L^dHJ&;I|YuU)}fx|FM=pB\8Nmf3-xy߁nC؉-S*ܽ*|~щH2b֘esɾ6 IVGb+TӎSχ۪[n1G>r9'5Hy biq-ngS~l+x6H,!}ecUlh3t;d;↨};RQzkt&EN%W/ET?\S>4)m\ڤE[yu=NZ(ʝQP{3bLZn'me͓7:4xx́`*\?pfx?ZջˌRHׄ5&2Ju"f2Dz6TPQ)ukϨ!T\{:霘>$C&S 6-9I^:mLtl5B+zѹs]{ qef@,k-_vz5E!%WtIޟ6B~ dR"K>SP*~h|İƌ`| кֲRG%9* 'C&d\Rhjb-ӓŏRԹ[okˏ^2u%-*~'9ŎѬd~0ԩWĽt%ӧq 8Șk:ץ'dV^'on8h+a`vZ^N{Tp60g>wim( M6W_W\2Z#mχ.F:KxV@ if%+Vc =cuO4SyrjRk^N%qS搅W17r6:J~pCZI9C'qe_), =Q%lMpOH%.ZvNҭ;ޏT8lm(ɋ6GFb~]Xzݙ1IҩIAs}yz-x\-l\g.|*GRL(} 6p6o_RoB9^s<@V~H݋QP(kYu7s7?gb0_*u67r` k1߆CBE0P5R%M '&N35I| [ t+.ϣ^y;` G1q(sQP/)ήL, R zH 4^y&(dI8(jlv*qZq{/Q}|H{~K4ض~3'-V$rR3W:_H01uyz}[l58>Ue!vے5L_Q]^ʦ_6uDe4-v18.j 2YW;|B7Jv Pw&IKqh'l֞a0cṸ}n6}\(% Lm_Jv:YV!2ٍ b5ܡ~7hYaI^\*n $^*#_#з㓯Qg1'rڨȡbPխ%|E˴) SN`]M[޽ie JzK$͙ gY#ޅd)s_%̥`,Ph8re&\,ʀ([X=xZ V;{e٠gvͧR%Kyb; _6|'pima-y(jD<(@Sb4}Q^9  Ѻ#)7X nb"'A&yh^7pFր$S]jd-kk=F6"SPӚ \M(M{htB)`(zAыMﻴ%4/.\_eh6's.Q^:6^ܷRMjD!2|a"|qUU~3f\KH %Pm8‚ e+1rI`@yZR@rDZV\$ͱ]"-!oրWß+0mHDq=q=h&K7H㚄}ѕx+ЍX160F)uۏ '38dxWs ξlSugZʊVD A(8]pDs6\ڎpM+ZRU3fMaY9ʵ*Q*h|PƾYt(@[$);X4u7DQyU3s%A[s%q(lE>y_ZؚYu,MhmDaAP9wlQox:UwXE /FRRvwi^])V>(r Wp"aiHE{Ip=nWdݜ <9^{wy"x -$kU\2 Cbrv,hi^%ZH۰]ƎױGQ*fh1{gC-M)5SPA{ j4@O! D}qTqԖ+;[PO jR8jFpČSRY\fDMhtjZZK]Ρպ pyЭٳX U~Ra3~dǻ pEޖ\mRgQ8VNAg} -2[)u -׌K.\Mv#&k|Wvj֕9 @5]zʡO>7 NSKժe)vlQ-mHۭ'}>ȭ-)5!o Km(llDt2&Dd\7r̳9WKmԈʶ_'Rk@,` R>|Q#s՟'$zPɚ=Y)uD 9RT\A:9hlw-=1r8+O{OYh0 8<&s.2)D0 -ż@~x+~(D&gT0 %N&@ZLǘPDKPPk/],{_B4m,_c(¦Teϑ(mcLO7MNMcZ=.Yc-#@7;皉ے*UC7;JlD?fGxlUîW|jB쑯 H[%Y&K?^m2!-Nɜ0N[hzxK|kMMXKY7/]N],n. +7U _1E-GHVVBtr D>rR UTJ4+/tZoZ̋,ќ-*5cJ7 VF=5nG04ũ#z1Tm1P+tYFlm~oPUsᵸv'5, t`7E@G@[TҸb >9d~sTbD4u3L[s2%zBi-KVPfoб-H=pC%|. ֓Q[ABۺ%'Dm&)oC !eL!_$U2ßMqBQ8#omLoԧQ㦎-fgcT"1C:EW94p ZcӘ]5xJw3Ss]-O8_NV/IgI޸m.%t3bb~gO`qT dm,.y[50͔pW/PقcN (c֋8;)Vp{4C[:5QcQq aR@H*FlЕ`߮gs+'DE栙|ǃN ݨ/&8[Á[>r4XJ}͍~J|Zm])ʒU<=yD >H k* C!=xm'!kVI:r(YSfb)#n N֢i+兡:XCC)!xrXU alciw3,I# y)T&_=c搝#ȂO5SV9R=`\m ?Mpe\*GưTDGO5H_|XFTa ȥ.MiNɴ+ rܛk%GcpU(advaeN PzȀ |QWܥ= BV1鷷*0:^*6"~U_} {]۫gh?DT+骍1aGq,F7%^^nٞyrcI(cRP)8"(e} B-[Wo+(s5_D+q g]2GEPA_%W8q6wɩGǮGN >ybpobZ9<>ٌ.#"HޅN^w  FA=LYs!0bB7~ߪlXvr| hrө332:b.pD A~ B%MxKIzfQ^+цa3ÃvO"s[ӁcST3 o9 3q׭E![{&sL]+%Z[컮I|/4n+ڑr\ ~cS&j`68v*ؠ~HaN{}ww;F|4 ,wun /Mܼ‘'`8!Tƙ;Zн'9m60]垦kpׯ,,QC1}Ȳ&J}ѩ!rŕQjK2 kF|SX#FyUn aC]RԽ^|z<_5@X1yk GfZ2/^g(U}Z,䶊zB 1Yl˟iBj-:Z8E7@ѥJ]˓0@CnZʻD"]q 4r3| @lA9&ܣm^@yrȋp(oWO*a:XШ Sb'^wK 5 U0lnm\VϥW2D?&@go}×g/';H5m!X?-S=?+@*qM1}}No)FDXT}4^zqrmsmE;]5Sgawx &z<1}{/}^*. h|њdCs1wSIv -O'_o4ܝ8 t=Ɉ,0! Ħc*QOeI`;gϧWusѕXc@v" %biDJkFCmj0_6rՖcGlٲv%ͮ/[%gsΧDy_ԛyNi$?XmP\?(Fjf /:1s4jJe]/.we~y+~b`53P%+}wq,)(ŧauKRCh6e`2I#( $v!fRxAgaؗ'sПm.FU&tbYQ";c4rJuy(Նjݖx.Qf)ptz<Dvs~v tؗelphqsb[yʟb1^sksY]z^5/k"ߒ+UwY7|*~8QDNyPUJy;)9_YZg񵁮0:pX"H>"T gp2Uշڋ8Z^R\sQ*{&2{8G"_$?`LwL6?LAƘ6DeJ淳ޱaػͱIZ-e l1dRޟ;?Ip` =|68ÄݣerH{t~dOnqpFsFh|@OU/=Voc::ˉ`Psm Y֬mv%O'.rvlJV7*|[h)#.c`U'ӐW%Df/)PMJ3` _)= X;>^e<S+`g&? ?DPJzMH |ǔuho$Y =9L ctjs':͞o_OG~Ԟ.#m.ԗ.lz޴'U]m& rђ`c3ΑajӼf $-ZF/AE e_t*[­KCq"i Rc݃0Z~r$Fϑ2`a>c}vz A Tj p:yE(ل#YzFJ΋[F^-=$Ym$1e5t8cLA3\` *ԅWHktrDwT<$& >a+'y,w7햿H;:n72<=lѽ}f TtHsUֳr>EH_.K{L٥s`Ml4  ޼Ohi0&B qhwnX:"QcT{O 6bV&޲>ӬI}W5uv,y$ΙKzz_W^3ic̺!˲r Oֆ7,(CWMZ 1Q Hc@('?h.S sM6g}38)Ki~~OR = D>(TMVS(cky;> aSH{ 9ISƠ-6/X TW7 mv|,_'k^~E+#~ʗZ HmqDסZCNcO)xbj#A탨?(('7_奘>q L\ߪ=׍ax*f7Tc#UW04вٕصc()əh)\DT uYktmkJL[ڄPg H9]wGMtxzLojZofD\CD;ls+Kqe\SJy5^A7i,+"#]j4K{w՜[U tܻ.ȋ3IA̩ }?' ܹ XCr 5;ZZBt٥<>kHNx;<ӱmz$!PEBE(EOK`:!G딡R p'^ב< yR9ᯡ$(Pl> 3D6'ɗ+l`?+B Z0:E`f[j5AfB=SdN'W9ĮqҳDZ&\ ~1TT| R‡tfp{;ioaa= Epvj2OC3-9p jem fʸbܖq y1JmFCX> rWa',H;$g#Gt/To !j~%7R}H27zbL Bԋz\"!`a7$}nHTi(kx& `3ͨjl"$pԑU9n/ޫSPRr5PWTB3M|(W1 oO zNyq{kb >j2FF p4X*<'[ 9~A^}]&7PjZ}j1th,F@4`龨z,eƝYa^I%T`C*}nι95ѯ6Qvk B@Dՠq-r\i.A? unbS{I; 7s J1PY,UEnITJQ$ XiLM4D7>)^F洭S+1ʟR@a`'oh_ Peg(=cP 7}+0FC5( R̨2(6*Э)ÔzMOhY!:qn&T柿 [ ǃE:B:!FY!)Qw~S}Q˿հ=SE xP"Y,X \l͓7ƠR[w!fY#ޅZ7f[ڵx ;/ {>™>i0m'Kθh{ )fC}f|~:ry 1 ɓɱiカ Uz ̣4=`Eg*C[OZeM{;ߋReWV$UYR<:*H KQ(g?M|"(H5wf+]稁Q;N`zs썕jfp*3տD5"r85/u!v.{nWR/}Oj;'t>-cZ`W1=.r0Yfv 읠wzVԆFGV!+7\qH#.~} #7ઇ"zpTU]x2u))'UD!b qd[$3^sXpwM >v!M}΄GoXw1} 31fvu@y1y"QO']@Q[%;ήHo=ZdmxEagŠ@<0Nb0aX&%_!OH &r^00mLk?'`fx)!2|P?]WZG;]uldϷDi?% N u]q_-GAS:\pex:!f-M _wP SoCD;a]2b(c VP4+$;w9gpq[@)RqD_!5;EDWR:2*(ɻބ'7)/9Τmy ]%±Ψ,V`ô9&_>aam1 oxo/ 7*[eܫ)M.nV)uT\yNacJ{Pv3rlcUN7Uq zܟu1j6•uY&p{̡^@DM`%ʶhUF+u,D4SvGju3t;HmNhFZ-;^U`qrT$b-򇌮lN2!XlN8uHy\uvnAVx6M8;Pg U$gP|3E (ՙ:WKLmz kiJM~-YkL$咈-KW[qK]JE+B"R$ aC! :WI,@ªe QS<9 ₈ t,TMqu|ۼ2Q [M_-d~q'5&~.x۷x!ʋU/K_QMW{O.eWf݇m-\ygo^qRc{^w֧5G/jɢuŽOG9"Z~!xH<'Erz9B5x,=MVQd 'T٧{Ll`{v+vk6[e"sĬ3]/"⣪FZƀȦnT?N1?kWԱ"=T<^9}wq 9ImG=jTI)ϧ葸 pa,p@ " Y=ºma??DiX+>>şPD #u[Dow$?E4΋[y>毨궦 v7Q !-w/d]춉5,g,oQVRdI".%%*#dd+$OOA)5`IxeJڌ!|Mle)חFhe(|nP2{'Agzz{Λ:zoEB!'XfW+$Sd^$b#bZT jvJnB^=ۍ̯PR$K\U*S.[[X]6y5יFZTlRq]h!)WSDTj(٠f9IE46c''ƖP47HTx{|ֻoo ACbro*kt9TVM0f%8.bs/O̧|(RNgJh1Wݙ5eݒ"6@*4Lk4R@%\%J;JNmv@Hu6.3ٳ/{Stݷ?vLL I:*7 6)=Hq.3֭W&_4h̢q1n|`#JrM,kw}(ILOu' %u(%wtalf\@tXA YB%蕏{.տO^l&?l(, 14%#,cZk;{J'5;o%J [LtZF4Z!mUST_:H-C"YwH5fYqc+c<y >|d` ހ_ rtX0Tk:4.m+ѨŻa,4%OբJ-ɢ^8Jᕿ|VnDj!jBkr%74%3-ҏ,T)L&85†( ˑ1!_QVEE h'Qh(hG_N~lӸ0G w_E9mq2WuDsvqHC"`T /H@v̋1`޸ Iݵ%y4kɚ;۠:]| ɃV9.>tBmL9- do>T+$|Va.P~?o.h}uWҌ[r hg}|&˟'ӆE(ͻ35,Xl 56ӰHvTWO?>شTwz8(P$^赡7`jZ6`I$ ?;#EUJB(ҲvB PӰc_mnG-ƴo?u1];deƋW%K^ga|3=:j{di#P<9]?cF ~U\qO'bZrS~eTʽ<İwbUe4W#ºjJ-XFeR2M/.?0YX% *^x.mu %[@_liv+]9ta NFfVOD ODzp9(N!$=:pΣ}Դrvxڑu]9BmގpC :?ϮͣZ\CVm] Oq&#Fp@h>b2aS>p&k3^.M}y zL ghVC4, )oi*jc9VNŴ 4ch"$0|Mmla`ΆRɄG3Ϡp3,}N4rdnHl 7ffxAUnD BSr,g]gˉJRqCZ ]k QX!:OJ( w3` =" cE-As-H}NTr== 8x- T4x(vpuRPC1hQ&fg^<~:Ʊu8 ŽUŸ\R0-42(ޗӬR1eNѴ9ɯQY.M[ RƥgW1NZb$pQ`U׵YFvQx)9 a0m{<c*Cf9{)huq ,b܌1yS-HHz~τPM= &p96q)JwXXݠ/C?L\Z{-}]##ΦIgZj N¡NnC+|9FF*d#E3HAHa~w]Lq=sSẻš}=3z!^zRВU7hhA{Dzϛ8dC>㦼#B$!ujC!Cuake[B>{ .ōjre6#G4p:AhP_#B<5"tC6)g@mia߲уy}[)֖~7ܝd/D8BaڂTBHBwr/n9JE7Ԝf|xOP!#4A0dt%?cr8cqOpAxP_7Q6K RZT/YNݝD8*5g(")ZW!]UBYrYu:FR;h>|aERX;?! mdoŠ)&_@]UM Ta%1;@Z}կEDȝsuW`*歸eY274.m}jtŸPJ%ƃA+7tniP=5\7y]$#UiZ˱PAf@"cJ] %6P`-0=u1W~$,y"{ka38D,ZgэOYX hP723xd]1BOb5C^H(:Ŷ=Н9=5 e@mdT4"iB{PnҁYukٸFF=$\ਹwWo˱ۤr gxǾDDkyү6emc;6Oi~r=bKI/O]Jf5= oe.d:%1oVHr3E)0Kia9ԟ$zQ2q.+1@F#.$:W8nxfu"*mt}7w8ƻtO y&u!o: KI<`uxf2$ +sU2 aÞZ_8ÝZNRkǠ1 o 1u4]Dٲxu1{F  0=wRD^1|sgm ~:+s\xG:I5z$(Dr2LdrGH,*/37 l=wRfk5{ͺKDco3&pF9mn(`&^Q|Õ h }EߜtmuO,) ᝡ=TD:_~]_84ypg(sڷ6U[YMD8߅&it@]I?nҵ`Q*ۭSvY-g:G+79gS;Is9yy?3I 0&Vl%J@(wee=:7KyGB׬^Lj73Dqr $mZ# u,fNm0R6HA)] v.y+8=O-qƥk?tJc)#ި+UݭLzf% ~8v*PO1ar>䷓n-jfnߪz ,4۠ 2).$ˇ!a93߷Lz }>Z|'ތ E'JArȯ=fy!Z3:;mpy0.zc;/B/_,8}%6V Z냠fi!EjalPs@KTiEK$n#X/GNTWՀc#  Ph3" i 5ӓ}yPG*A1~v6}<i֩ʟ\!@l@Sz-;e$ag0GݨP1d# KD׆@等AF$d' fOqulk eA{' < KgRA@VKTyr\Xe8W !!vJ7"ȝom Cuv:gH?,=@6kg ͑^zF@c1L/xӶrh3̪B#fhBZ8 gAK -e)@ڍJɥf:)%0n,yD(jA-1ueQF}9@|;DgDRO_gp' >IUH}~ʜEýl\_)v"%.enL.FZWW=Z}Mw|PWdÅQ-mnHV Zܗ~UDE[V>,a.}͚ƎmU@3)ܥ"q3]O_XF> SkH /*8ؗW3CGEf[ d&\tcN۩.ib7Yjb0oc0[qAMRq.z*=3*%]Ya]U/43#C4͛g T!v7/d8,/|8"9AԂj$Kz $`QP8fih{v$t36}90:3pקq7ȚLyQqag4fC7%*k7e2e_/9,@ V{tyc\v|.D,cyg"n`\dB穱]l|@}ѳ}d\b-x JÄ̰(p2"yTpo7) T#%?e h:2~<헦g%Λ`]I0X^wxN\9!x]`es$+z̛ \! N(3ŧ1y=ݑL2 :N5R&M(P?Aw'(l諭t-"R́?$uOdF7}!XVQavR2he$4OUPΜ cKĥmf7_ 洕/-P}J! Wpdo#T3bgU ZϞP!IAz[d8"q'PV=siom0,'l՘(V ĥkp6ĽZIZ 'b,8@f0LB%Zulk41FN/xZ?X*T:@XyǪ:GϬDnY447yElXZ2|GVsZ9w>YQMGYo0f+37u\֜7oz SvcW"|~PO)| eYPa`';}\ܱXZmY(-+V4O&G¬lrl1v 8HgsԛÀϛq{q.2Mc]؉ 6C ~O%1H@ܚ"9K'r9ۼ+mׅz&C;*췟:qի]?fM }K}5 =\<#2Tk tMaġKtAk˓}45m4ՇϷ.Zo oSAϚ0!k D"M߀}Ea})ږ:7AlSU ō4֊@Mn/FmtG.ԣo&U1P.q=A RVִg-"s0Wj*,!*cP_ fMheybaENKLMkV~`* 4H^+UF PG44j9) SAu[|/iyс>zO9l&{7%)i>1=Af+OGB(`GyAB>]dTaҀ-.@_]MaVo,l ЊJHVu֓vwP%~:E#.8'@)R0;Z~tb_K SģA~ޱ阬涐>&zϋލn dqt_"BmPb(M1vaCDR&3CAMs11JKNJ^Gz[Քpiuzzk4sbntFMDYX,=j>8ѿ6uO1!́؜yzn^Bi(c/DBNu zۧ%Ú{V^:O+s 'Nx[XI,aJG(65ըRYP:5:1 9eAўP"ϒ >93N!oNE/R*Z_ً Y-` ea3nc?%%vy->+oٚ,G0!賦E$4pwIRh<Ҍ<]Viv$*Eh4 !Id Sƅ3D h kYokzciLUOmLë}WIy~T\{3>1ŏ'32ȑluR}a~{V~PݘPCsd*\mw7JB t!Xb_F?A~$1`RvO)338T#ÏC-lpؘ~9ڧ%Sg֤6R1 J*=S9"w.+%vB%谑{>FdP?rk6Z8U<{5ڽ뛻z.͜BM X,~5cWBU[X_.ҵ b",l0#wݩgpIV}Nxhfq=0i"/͝C6YiQRsHfM;P"ڳ ?<ݟIZt+2.+[:bpLht 8To|R7@Oa60Ӷ:T844.{Oj*r B3mݾ =J=|b2J xY=!w1Xk҇S .vHؠ ~AՈl|Tnz`$FIu xrp=*z;n&u4q:>D B#v֩nPvw\H,E =hUF)!)GEwzJS,~F@RרV>tڻe$iBZ8/Lh̅`dFR&t|ȧ yXCLcQrʀYL>U8 Rh;C}DpޓlXkj@g;:ٙ1!OƵh:ThbJ+GdТ*V`j>iQY[<Ӥ|dσtzoUV4 i[NE1X?Ƚ|HBFsQ?Lb_˶ۭYQ϶z\YyHR~]`~u%V9{/y 镕(i}AthOno|&}&׈ [}l쏵_YU$,noɽgZkΊE/0R1gHa i6hy6m&#jmch~ =pȱ؍df[՞,k:U~E47(U$)\UsHݷ9w)hLi07ga3O d<.\/:3=#{O.Ń\E9{Z+'DNƔ]4c*y7 l+"sZUIb2$P~`Efqzvbg4V[s.x7u;ɋ ӈYs*9.VhsLn+&fdec{ F[2t"W"lW;{JK?gu:jCHBiHhuNkq]#PaFYOZcUTsvv2Ph/z<9d!l-_|`$q Eݞ =Ejn_!8@]JmƉԹp 7P4 A|aYXw{ mURn2cK md$a?u[9U=#Y+̎r|P Nz֌1) gK3NIW)k&v- n3M mBaz槏E~k?.h$O(߃^mq "(_uȡoij/x@LiK 1KjN emEWnlJAPUCRb=(1Y]3ҠJO\>)L4ThܤhtiKE{у' Zت(A5j&Ŗ~%xM_ r4"*S"Jz0;Ǭ!`]'**@ա FyV>bR:aHY*+w JU1#9O#(u{xPpvىm섬V\E9h-0nh*Qwމ^.w5CsvvvY4hWB (NlB8联Hg˧İEfBeW:`p-^Z^fCCNTyE"<1ƳE#4:S:rXua8Mo4[Vz*|\SSZ! ]LoURDeg5žXAQ^۔NN:<}kyg> ֭YUY-K;S8uUsnؾ^d_AM[y_|0b 4knp5JGފ) ,gdJf)|vgܢPV\@m;4PRjؤYJoei #)35%Wz.$fsFR-RbmSb+4CD^p\HZ'v(\3Jb"⥰ 8J5ރlnLeüp=q^X@SB̉d`kJ>F!NQ))ĮJ9)%S%%ߒs[^`҃)U]nJ`!*PJ<Œ>\Z=5@ %*4 D9n75|7B'7hHf֑Rx7A99ͤ[$r/w8 4Cn%'f4Irs7IɋJoHʸRkl[MY#<[k 99% p 1La٬[_GGf BH#(4ͪ%3bSct'!MӒ5y{<aLceb;p/8] p Ks7Z*`v0q'ho~;U,-E |*#5hhHZN&4re:5,p"~y*J:o*Y;i?”Ab)Vy[e|b;PՏ.  S! //Ys3y)<@ɧAڗv|ZxrjhZϼ$4Y,hE_Ju * x\6\ŒcpIA*`M:M']̹?OgH!qvϲ4㶅,sR~1Vɯ jkHPQfzt!wK+M_œ{#n!7CD"{ZzeX͕o$b =Ơp( 0Fٶ5ywfm ":j[,:bdױ8QI:gN^}ilWYU%x%l=nx+4֫Kf8ZNXc9fX:CohY<$$K6Ѱ>xo c,yS΅lD<Ӫ䛇,>P)WlI PYcp+Nj#OP:PG&C\o";֭:xV (`\?s2eK&[a/ ;T^g[)<d(z?9ŕӼu|K<%P5ƫ4k ,gɍMo@DSE7 /(#3dI`prp.[ nYj(IA&':4ĠܧZS\Wဃr3!k6I U/+׿V/!HbNDjt"+6ddb_j=5wMաH&o{5(%xE>,#cDGB̈&R SNjKFnkma+ǁN> dNwb'@=AWOR=pw*l-oHwRGIc9Jaj$G7) Į8ue f̭n*H : )C5A[ܧ}'*[ЇE?^V>AHcLNڕEq_0&-ZB9]oOQ(~" ˘ ->eu㩐v꡸;7Ɛ 4wv(Eؿ hȇWyǜ-4-裝d |RG K=24lVϲ>xчvaF@rah ZtRd/>6i%![ZrrߡU>ɮ<-4 Ѥ{.j0=.+!}[ݼyk3XpZkf2"\hx*R˕4q-C ՘.sPCl\H[EuQ6zMĔȦ?VbZ rn@~n[q!ګ ~^ݼL am-3IbqKY'Ώ9񏸔m՞CԸS͟QleG{O?Kv BxZ7Ϻ(Л9ɌhVF;wzQ/|s؛zqh]8ZS%-2c3_; |Kњa+MuxOW'6g~.`ɒ,+@ 5NM+Vu|$p,0YhlRrƫDcM-m[Xtΐs3e9$bY] T,pƨ]zF%Dx^$O}oڏ^^~x ĸ\}yb+BX*bo3;l"V3]z"5 M ]RvŢʾL&94!!ME_DU7g1=[QzԚ- ƛlDqc_N^F<5֬6h3R֕)2影HA$*ͱ:OݕJwʣC|q>;"6S3u1Sstk)@;f䀝%>D6_G1GRQXz(z 6N QWHR?W-/э}]|~6N@IR8QBUdYh̕dtƧ CFU}<%`g$ni%tQuc6Ks:SF}@f68ũB%RSEh-vCs; `?3jfZ?|F=С\RU-olmi /~+^ (9Q*$n%4e]qI!W[|ɍRpe >H2UճavܷN9~vZ$r9cqQ7qlSLnQ՗E-@Ct%C,A3Z"n0kӂ EQPIcA(i6Z#H4ײ..cfY|- ek'!H>3A~8۔ߊ\5i6BؿE.W $-xbujgdhhD ;. 荼DS7DqQ zt.5.[IӮS:]XT˵|sk-qhZ3KO2۲4o:74u*|^ 6^6*,% i7\*6C7-reҮOHx\Fzix㋓or6z4=P@UR ڨ:W҃[l_`m.]0uZ1G1kvjS;ܤWWh|c.!U\0:!ɯSaN>w~# .0k"G:DN!h5-2=R& z)͆vq}<+ݦKwI~eLx lќ~KKJuYtn=C)ǫ YocN8bp6*(lY];l_!㙧of,=v>|ܜ@;gt0uWRN&I$)ZZ=OVmb꯼ٕ}) qBt!v\եx̏]<\.0NųۻP)>t疴*.IЍV8$Yk@1Edžfr;0"P^"Ûc=(pv,򸈖@x|Ӝg_o29+CP;:#<2~%VLwa,*O_E_+mjΥXޓyS+)9~@Hw6ie>.}ځ['D~1y3#41yX*@Ѡk`M)1|.-*}b'= A՜"L|޷9W)) A}*WS 6rC{}JXbMD*J=|^o~ӴSZJy'؟BǛ,dy+8l(%7Գu撗<㧔*{3>. ۻ_Tr-'XCmXa.;yB<2D(xWiEi}1w}q[]$ZV VhS<)fЁ*oIX_0H<݃򢬖lwm7p5y5,gb혍x *JАwl/=䅁u͹$2 9'@& VJMشo Jc쯤TI؅鷐"MGtgL/Hc>K=m)ڋ9^Q:7EҶsSٶ8e8ƈb;iF3 I|^nH/,^]#zhZhi$?wzs F̤pO Y%LcU2j\fCKye.|9%ك⡹~lk boո8<,cٷH -.dž- m3ZP{4_5iƭO^M'򵨌:1U>^-2Y/.'23IŌn%pTXIC`O %\n 58aqY!``; e- E~A™} ޣNcVacY.gs`2UrҾIp"7tggbջk+ʨYӤNOEˢ]9!$&EgMBX2Cv9$>(:-Ԃ1DF9(ՋSjNmOĎ %EcoE҈#tDyuyS%5IqKUKdn~Hc~4Y뙌ihnEG֘QpZ5pYIN}̧| vL s\kT.6}l|Oѱ:3TV/bC}пU;+os?Q,-"Rlg?pңlhM`#3c"7ӈKczZ-s7>V|%b>?_YU)[| G?!ڭƢ S[9V4 *G7Qx&t,Ech) %~~х8@@DZ'Z t=x+W20jzG|*,׊>Y,SwaiC:|( !3T2sZ ݵbAۅT4 C5֕<^e0QN(l|X~ʯ E2Kd$촃 f-ܻgi8@S`TTi-qya6F>4սNo{1:C0mEIw{U YCPΤ!c ̇Rפ! #6kc^<nK[8x"f2a/0QF5GPw6OQv%C9XRny1?Nom4a 8 a S\LU1^=ǒtcoAȠ駎Eq(\vM=##0Q6'BE`oA2R*t-*q]]^rpud818r/{dʪ::SBJIr#MiJ4A%_dEJ4 VU] &: Z:3[&bϲ{Jf3=~S Js>,Mx־ˈqɸ5ÝMv eI=  ZqB&?K]Ko$#&1FpD(O0!Ĥ킪}k"S3Y4 ;)[;k7؝Մ]ivjP'L 󭴵duY@l{z_{,'rb z"e/{YaCP")?AY$QVdCj< _a&.hcLht+iSV/?u]? b ;o@ O=5n#Klcx=i7lPE$gpao5 >>JѢe~zmCE`hcrMMONYv,?URUN9s-Ϗoy01rb+x1Nq(0g1n_lrC&3W+.Ib aQ²  5{:2qLgt8zYVYu!WSF;G`<,𝴻\uXIT\o,5&}W2y20xGELܹ+-VNkXY*xc[x#8t讃(zcwI: 6V-~X5 {fDO]YVE2\P3bĕR1FFOmP˼p7ާP5I,zs셕g`х<;~ַ(a IcK/}xI.&dfsw( m(+Xǣ^ngR̀0EXn٦u?Hvz1[fϛ|9żQK= Sru Aϴ:sh$rXk[x[իnigc_z[GNayɦkA)ôu!I5u[%xVt#eK,M?m#Ȕ|kQ:Y|6yoO| QFN=Ugj/t4MO٧+b Nb菞ȚPn$?>+Wx9;_QvM7s^N G~H\> H,K.Ͱ~bVbsG?F'$29|H}I`ɠ՚IQe~mhbj1Pu3G`Ο.* mjmyLj+Ƽ/*o(TT8 ` ~xLJ3MYͷprRɈdؿ#U k_=O4%ngFZCz(8om3% _ c(,FI5REFʣ*` bn7#P  BTJϒ)_\,2ouzz+9H#*Prд&޽EeozRh}lKEx5ז??{!aNZ94,e-ݺg} .ysi7 2ޏKTK3 jc!D]ard@#g';΅/JPq}V<$Nn矩ӂqʎ7:Nٕyfjqt> g4KGER,+6S1{ySwIE$oNkFڭ+IT}>jHShGUY(5ˬ'y*A_9_FP6 orN4k9$+ۢr,N|+xF]i*^fʱ0:F4%㇛K!EXO>o$QS|m%S_of $n{*u ъVXoY2-w20щmSh JOw)xܴRMɠ Pm)U="!,UOm) gjm9vjLD!}!`2osRtEeg-=b>lV=g~xUj4('FL%^r!ch] r*Fk7#E;'#>hQ7Eg!D4[fDQ{S%WrhM5d4Aˏb$4( rfJ6G> L0"\w12'6D9VˣNy#7]3)j\oo*//13Qѷ"J=a7irP߅' (Avpfj$<8F+ZOLKa! =\gPZkȽ(휊N~]ֲ0Be0L?Ւ>:4>zۥVa۸W.Щ;9לRЙ3ч!i5 K~0 ]`\{ΜgQVwC{߾݆Ǝd~7Y`#@Y.k6ivbptއ0. ¦Еq$F pj49zlF|=H<PB#6Xxʐűy u&d*M78EmT (~E| `tۺ]5JncZՋ5Sw=NIQy:yc3.e|&gE'9)h.襓Į@Yn*EȜcoَ -Æx=ɡg C_f# 8v 'u/vJwKWY-BroU:E*?&hG@:]%;_ܙx-+! I㰄W5􉼿Go4 r89T^!-| tI0oIs!g,9@`uL['Lie?Œ\77@̖?|rO50v^}g޶5OV52%]YQ`kZ0:eBw! xM2c:j4Ϥ8hJ w#bqPSnTA5ᙸ<ܤRZ)'%j]Rf3{b,Jnͷ^L''O']$DA8 I[[.CѪVڏTdix;$AgTH %jBncMg;ovkk]ʾ.Hzf33BҐq<4$vcN۬'N.vhu^4OǔJ[!-5w7WPsySH-L5Mk٥LmP dkZegG'W"sJZN|$EqnrzkLTy'4Ĵc2١i)|?dπ<+֚>{O\2%ҡv= `iOGH #^^k›ҝh`띳;DNBƑ_;mP_޴:m[&1ndJUV:wqERd8j;]2dp,!EIvZhN׹],q]H4G~+iO>~ V(J n4MQ@wd˱ ֌<>.[F I5A39$P)eYl>NYٍ7wZ\Z+zwl*HzͪU%CTc'a +RH9!*y&FqMg0{Sڲ㈆p8 Gu m4#1؃ΩYLW:290{!zVz^Zrд0A8ü%޽rfm7*6oµxӈ~|NX|qvC :US7}る=lHw-h<3O8xִ3v#b {kk~yy=2+z)Xm3ת5sZ8"E9D1e"B9I3ic^8< {mЂzT7:|u7 ^g񵪎bjDfw7eXci`UnL5_Nc9.iaچ%pڕqL@)tY.Y{=}s˦.TGӍ^vא"Qm4c[.Q?f AO* Xݺvt!w:?2o8,{[Iv7bXќ. _Ӡ9 O^Ű!\|>>3[" nYqqYjT[<&|jc3@ UI u MƖ d(OugO2߸`g6l pўNL0[p1yLdI F K)↋yby{O#-̂sw2b&~n Jchm{.B+M}wR^ώZ:y M8{ڵb.hG YBb:ö|O-h)0"$ "22\@Tq8mH"f*ԓKHt>ouT-mZIaQ#w><3t,k Zvf@Qz&é7f,$,>ZR8|ja9Yo= S8r uɻ.U,p I(WGJm$V`OPjFQmg[M܇I.R` f~m lUH%Az}(-;dәN4 NeaPi:V: , .%=ە d S-dq :u8v~+)X,St-) TGF(]ǝmcƀ$[ҷ;HwNstG"6#hL\0[(Ɏ<V@ U} W)7gswM;;oٲTѿKBOb&g\Oި9ezk8םVR>p7R{6dՊxc~44O4z|%krCig2{$gP$As|K-MIJB ѯN{ w~ڥO7Vm{V>/'V4 T[+.%'\RWaZi?kl ]熑l9R.N1XNs;qNM tEy4M87e5𶅫:m n=U.ih!- zCgiEtQ %-zqwAZHE P4 ]:ԠNsYN41[HSmqaMWi HbгjqdãVG9 M+ʧS{9Fڦ#Fz<WC'{AC934Wayb*S$3!\D_3~:hh&CP@\<"[*j{Y?8Yχe 2cub_t$u?񪢗wQh"0?CSbQjeTU PX밥pSX5D5F6HM˨~v5LPwģޠp}D|WlAd9iJqfU[F&Hu؄KW=R1$ˈ.-/禾j~y&+n3@3䟄6Y y#󑆤/=Ů}ʏvd\Y!d I]nEQd]C=LA:-1wR/hz$>H wzMlXb!P1K%>7CMV=nN7AV|ŭ@4 fEkB%65DꗲhZ(`ܩ' jrLhy$6o?"{hVSg|L? {=WM>2Cs@Oٝ XDvLdIoH_Xj/َ{ιF·Q^lfV81K8txT⬊|mB1$AzS3 xf*҂y1:d$&=V淣trKVmyVS˴2 mT -5M&]aMkJh:/rkLJd&6~];#CbgWBViʰUޖTxF%Vnim':tJgz/.ITpXB2-7=0ubtfzw_%ыx+]c X5cϪ=jtAn<4&fӼ]o`DZD _ ]VL*nQdK'I/`UD D~~uV&ۨ<v8:( n" RݰpB*ect5EJKrwd\Q&N'EJ-w^t{oo= D."h]HOp}DZ,CCfOa<"<+#&s%Lo]OJ=`{'z:VS%Mj=ִC 3n,l3:7[Kx˯w*֝P]E\ئC; XnJ:].ޅ;pD&q`csDRy/8So3JĬ"$E\w[&Pw*kGvmsiC=m@l~t7VHaV :jQi $lY`R6̽n75qр&aRTF }(ѠYAo1Ua%4_&8zTO5UNN980cU YȾe \>aq6xa"2=-uh(j 5ndn"r=yvWnmog+nM"C4Pc7z :Ŕ|‹ieH75)۽I bsnߥV3Eʁ:rtWDZ[ D0E{:u/2zsX~ \){b]W?!Thv ҃x\pcb<A=3@DѬ) C[ې0Lzǃzt{&U/~vA C:9vN5@YcQOE2cfh^W3hB+s!beU`69;K^5|% #Ô~NזXŁ QNϞSjV|qѩl |VIϾhf@J$DP8"yCdIƑZE܃o2|'jTzXC;&מdakdm_Q"jkG,O D _JR! K+-릑Nlo5‹k<.'R ~LEX v*Vp>`nWsLK`1a-%oK"c+QVYK3ՄgkQPٕu v֌;鵄.N2>#bNvۼ[ՖJy<%'XtUmI~HcnB2H8J ð>U"p #q=)f3h7/?t+'>Q` τxqx`m6vi8tp\QeƜُTkL ( So/Za ERy}41<}lT^?o06ofȎjjX>-%nLs:c #hQ*KMtS% zO9vܥ& %lN^ ZՓ^KO=`=c dXs^4b4!:M@DmR1uԗ&3&z}"8 k=R1Y  xwg5A_ /ꋫXu/#3p-.0@Qsk5x?G{5씸#4s Y%Rc-mMߞ-?O]"qqAxv\@Oc8=;,γz9q4d·bߢTlst,}!_u3Sd!7$2RXl>VcG2Fphiw)C_PuQ"za_F1l=lIGC2G:S6{8y*azl<8LXs um\^߾dCLu- -i]|t&s)NZbŒ&בOB گF8LX/w}d|} L0bcJ&"~Ki=gEQ3gBELWBYk~tu 2Jq2*ՔQ5xcװJ*>̫w$0T13.SXMx}]wTFEĒ.f )F1FNkgbv0b&%)=wed#D%j4?"hB/XbᬔK{'!kPcw/Ѭ םlFQDs5[y1Y::b 2ט5br{AԙDYZQ? yÜ?}U>\2E^)`~h|C,e C¦A[l;6oCA_Dtr$8m\ \ד-@ɔZ3?UHCe͉ş#N% t8*r{8C°ZTe>!XY L&h\x֓q 'AΉ2pH1z&T7:=x^V^,CZVl(<Z=`#Fһ(Y8o`^iTF9r(v6j ò $ G fZ9+FtȎ{* 1.ov XIvY_9+!|:U _5b), K⋒wwSJ6zy6r,OFbeduS_#<}ٹS&l:F 1Z/5\_I]bn* e}|dhfU>~d*VBB|}?tH cNx~dj+C8=wȬXi<U3Ě HvOJ< %*hb+3 G k"?+AURpYd|}n$8 Ô@fv@߽Mmz]69[<~5E57_r.&3rUcb*yD :ރ;ZJq] w!hU+B&P6 \Ikl5(erX| C!s`%f\,]#x&+.BirӇ\ !7^nXYnc$Aܨ BV`xCn" `79ԎD|W_ÖeʽN@'Xɰ#Q[D3x?`J`@~?.+_ =TzE,=SO '}X[Cjt8oWGG`Vyه&̏ @PɞG,_֦E;%0V#8q'Gȍ@=\u *k9BNeA]!Z )*X ,{IY,v jl0s4Xw=b}ѳΎ"sQ[c=fӅc4Rz9XD=VMgfq-崚WB2E*.ᡫ,EĚ2DݯgeY °+0.c͂:)r%dAvo0U >0#EHȚGnW{rp+h\%(n;WuvuKx0'\CƢMOrMW~c؇&jи4Gd 6 'VJ3ׄX>z+ ]eK:;y܎):+Bli M,j"%gt[Cf@~ ?9_2c%{P`DI q8!hh4nrd7Ѷי&HYZ| 13%6Y"SdP3tG4@Eo3 g$̸sS]p$j`NFYlܹ =Bڛ 0wW/)(u;T MrXW)B`T[Y6MK0'ngHsٶYnW,{d%룮>wcH҄ +"Е [ EM ĵxoٌ9VO.rN۫vUDǍu>1%nr9oT}aIpSg;-? 3C~/;Z=jxÉGKa^Fu̟T7s.`m ? qPe!չ$n,+2?\w%y;/dG266fw5, Gis tae:;6EޝJƨ`jzςGh< K9羕AA|#9<SzӒ, {^ {n~1cFݼv?\򫝀:}Ӭ {(@9ZF$*QDY[_ymt9H ie._LVVEOقCϲ|i6 ة*8H[Dq#1S̥\OP8kn6u4 W񾺸)G찫[$w C5E`=,RtS*821n %&]/mr"Pq`QKĬ#,QTeݓK\st҈R%us4'ӸR)6_w廬^0TK;S?+}2r OYowѺ5ni6*j3~%IRjӠmԠ /y}~A;y$AL[ʘRRʜĽ({a@q4@c2xI6ߓ$LM.ȉ`/x?W{u=쳀0CwKMOޔiW-N9 u_'VHnbum3+xszG b#$a$ùkݞvz?0*}eg.0 D9U5mAUi^bI8F%vC3R|ƣLd*/N|X,CvX^s# k=8D,OB3JlIVtLv|۶2o? 챝LSַ[Ӿzΐ(kMDIN[(VAU/$DeD1XW[>|+,X+򠳹& ]xn!U}҃m"dž8euo PPQML _Ya@v'j6b"ݪLoJ9Dybz} ?lCrCDbz7 '>TF} ;s6y&K$LY`*CbW'iwe,,2>#` 2ߠY5/KLPh-ԧQqmod2!̫[{v3q7_83]^`Y,9,jhX!| 8 -Aq3pUzxX,ICMV(]z\~͕k{}YOZ($0qO<MF'XIC(uc<`$S(t oRHڜ\t%kKVjv 199g0&9Ǵ`%uɌ 2M `؏M/=- \r޼Ő10jV W"ب*R4w#S0I?~EV I$%b*)`WOVvGDLiаYl7t)9J k{C錦\ u`Lė# U VWBVqRa'I-n{/kй:NH#.6 AO1wExdJ#E3$"\G5&DPdP%!S&~&ewƚȅ!Fnv]<[pfbĵ&Ė{*0R ǟ8m:nĂޑg<̈́UR)Ov3}ݜ340Dٜok jƕҬzMW{%%.JJac\o-WKPMBw |G(2 F217cΘo.;h;(FΩp|®8M}`Л|aO)G~|YcCd=~*Dwwrb1fa6qsR2vpu[u$ŢA6Vj^6H?-nkB?\_(\P+! Bi=psO{?t`Pi{Xc|@` BUQ,/!

+YϙGp&##eUj|;cֻ_9qYC{+x %\L"J-ɬџgrn҈恇w>hsB4FgE^ſ2s= nXN/jW) f14amKH DPE\&&G{4:_sQu ^{~im[ZiַD܍b $^ Pœ!Nkn"3+/709D+W xtMyxUGnǯ [?恂>}F+=չ$[+(vT@<~$?Oq9hw( `[,t(|wJ4n}AXְT9"0!熧A"cl-MMMQy&[&=`X68>k2hZ˙換] 9vzu9 }<%a@p՞泟 2݉B}*^su%pW!{Z7 T 3&&KؘitvXr Y,xBײ6{u(C6mQ6I([3=毤 m4W KS X" p%=JAO .Gps1fpPnq CԵyft5l?4B ϐ K'+PU믪ei7٢9d> >og](<~!gLuVL#Xz6G)7ڼ "1z `Pe=:uH?͟N-a7)7 %!ΪD{߰bPFFa^Y|ٵ;E^lˏg U <l/§{hpjnb"ZyT*ɬ2+CfW=$@` <hr%<5xix@֓7=%|g(8c͖p؏-$%64+X&dͶD1)3):`k.ÎNFAl87a[`j+L1d@TnV ov}&/ލM3Pw~Xt3`"'<3,b]P4eٔt%$3!+gx0@ewHaٱ.HedTMr#Ah" yMHjUjH[3j~q O+U= oi1oN[ ي'Oe#R}6iϥg&AR8j2G͍cE*ٯkשӂA,ycf $l 'rnb'lQa 3k$6 aR9:c9:=|`B3:@7ֺ2G_s ō]E/PpLTzY? /ӱBy݅0n1{(t3 Q§/FU8D δ_R3n}:ʈsԭ>&ŤPG3#W<<ee0_zo3ʻԪ 4V%IBFsY_*Zwc2>$tpEQ\بЍbM7{nbb\8?6Lw`|ӘX\h4AgZ)?SUQ\&~o |˞c|_7|aw0d kX9 %e^_8b:5x\! 88=G0e+Ҍ"[hԥ.)]p0D4{`X.C5zl24}D+\}_o7tPA'?FbT\xO'9~rWr$FtR:yEUpGYyz?)Oֱ7lgi[xE"u0z:o[XS~s):SY#i*]' ]|E ɢqwU>xxLL܅g=cZzpw}.`YZC&Am\׏wztuͻvs_7تr(zSz@@EF{,O 7#s(<05Jj>d!|~ߨ_)"IN 'Xgp.,uH'U}}ܽdo[eAϟvi&vu{F8M_1\kh?\-ֱD<2_:-@= X ]AQ:*fT F&CYѬ}P,O8F$YÛC- ZRz[7nZLZ ]q9qrcߏd9Ri8J'>>` D^ڼ%C:I9 +t6ǦwkFw8kEvqn3۸sVO1D{ˁeb :]q j|׫)wN)$gg6t*ꗎ2vfpͣs}ZHβM&Gߴ|ȏg;xoܾ.ceOaEu +nsvo*x+ Շ6iD|D(o]PVltt*EXo 7=YB*@ktN>:Ho7^Br1Jl4%Cļx$7M)CҼqK$ 4=a3NikJ)@Pa[-sDK (fiP5e_ _ꜦQ#f޷Ba' ~5һ@2!l+gs3Ox  $%e]h~\vI6_ v,6)C2ɵnP"^Y"&PgY|L9 :9Pm.?LAO * "o; Z_3Wr^jΖi\Sp P9ëi*7Ӛ9?]=?LfKHR|[Ax>Q9Ip 8+`?FM{.TxW3F4t0!/Ð#[p^Q.;XUmʳ㷽P&DF6ǫ_t&nH"Xq lńk 8I +_;\oI(|\+ ùC *%τ/$&bf"bahvͱ;ꚛb `.tþ9t[q:,ݐu"A,T6?kVr`yuÇ[!*A,9e怡oQh=]3]|@>Hi9̔2 bGK%Ldh,eNHE;򮷰Im Ĉ9m<,>%.nWpv/wa/ &j3~ ѕK 4 3kO5}//gٵ*s[0"#WiCZnKrUaP<(C8m嬊lȞMk7{TF(Gn؞iIc*%27C>% M2M*C L}{oTqdķhOy=~ZAP'+*J{yU9;]O 7oVb!~ce޼x0SVkFBRcp>Nkvͤ6Y"PR&Kkc;N0> @4:ZFY Br-[M7Xg1N:E4oT]ҽòuHYbiV8S?XG\SJG6 pD)Rӂ  E~MeeM(uj:)E/-\OA{T`d;.Ke$:~Q  Mċ?W+2:#pk4,/Qӟ AnBׄm{U7\}kge'uGsPʪO'r,<$ .$dJgjŹ:=Tix'ITθ >;O|K? +pkK9,]0K:5!L%ܖ2 1swoV2l4.04hE.0 Bu!wNN Zxoɦr[ D!5Y-&u8Ś`w M_Y2eɶZ@_^'uVE_EebM#(xsϔV~0+6pRHtjbu'bZ P喦gwm*Muң*%vn:@lm=MҒX(٭:]po{(纾ah"RMħEbpDEaF6c}20祥n&0X|cޏ1gDl -P7Jw1P*<]4s`"ZWȵprm8$$e>-LM6"@$t䲢 fj 8蜵^2i5MJm40˻nc%^\}~}6BaÙ 2{.嘐\f{+܁2&Ʌ6?$ wϘ[$l`v{PY2?1)QɛB<0] Ҧh rha$DV݃Fb&xqbe**xt2(@ʩȣW!\4˄Y{2'"M!}u勑V֚esE!@|PB*rc2 "T%umZ.?''ےܠ[].(Wfvؙa<حP.[$FXඊХy%)ʗ bQl묅ڴi7aH7 'pj%[4{cB=ݒk/e3NW 5V'G oSzn;d si&˅hu؜-Scz,>/W. >nkЖ-hi We̢k'zHX kdn<~ZNze1? Gֺ* 9X‡ 3 Y׿k$]+/-#fI0cS|^/6>T dU!Lz~ZwmK]qB~m\gU=( a8C#DGDY %l),`'1:XFv[)}Eej3}7B)MZvx0l&l0^0b$n\qf6ERsgԹϝh)De˝NNbyYGJ8tg SN[ /'^VW'R=a*>DDufe%ZՍ$b}ԱfZ qi؃R/oc2z,6BCbK)i&lR$i Ou*i\Z*2|Iq~QnB6翦6Fړ9I\ux9gR)ɋ|'ԍTr̭"4sj}6$G3]~F'Ƒ#. gmSȻˮjuXy Bq&r/[Zxe< }6%2S$<3PWǮ &Ii+0.63|p3c\C`NsݭgrdMF.49`sV$"yim)m2L}0ڳ&C^ۼ40],*9ڨ%aixQ-#CdOm~Ac+y/q^JdtC3ykiIb #6ϝ|I^sFZnk8l-)Жګ¼ǀܝa<Ď [-&mEn5ABJ4*[ju:*9TpIǍjd16oVU-(b8(BQ&r$ly$xmr? LG%g4Fw\ceY̑>ecSPF\[RBe9v ?i 7%c#}ɛv7GKlV:] +41g0Ia)t +pQHd)2*x[s*=_KZYr|w^^m[%C^'&PSc޵Okq͐c{0+s#a!UD8æŞdaq5䖭u<H(\v[KR0xarO%!L[LA tc߳Ş$[LًˀEۭ;.bgf3?TPP@񪡒Oރ:9V}$՚L՚#`Ia6}#o2:5}1˹mGc3pQ hĽp@İS 0^ZK86qtA! ȟ٧PG)p%^6GITbf\\WWݥM{u "23wz^y?V/pY)@kpٴ=%BL)뒴D"XczGh-2>]m`[Yb5f&7>`G_kYb&c jQ|puVЉ7BH,ۂ h[>'pw$M;2kNOdIO;)h4m¥ޛ/ <#m|tšbs$ ٤~Nö]5^|R\t63ۿp8'X 1_9|[ׇh.zl\f j_NDEaς3%UWtB ݬ5[*vϻ2RўwT_IQ[/\Im-Fp{ \ug\-mVzd璈 Z L(啉?O, e  j1L5ĵg'#`!n,nMyȋ infvcKO}9DJJ[2׻C!sЛ]5j.E<'g@ B Zyow_.7w/tlTA#pВ!F Jr?꣥6n77e#tLiN+aqI(>׋+ЯX[z&칷AxM[*wS f:3$B/}@ܡ> +SHj}hö@'ogPBJVpxf<b̆B|іR}vAC$yTY؄C^?]ߞ?aQ[(<[̓c?4q3~cnW0ǴMsnlxNmŭl05gRfPG+y#WcWhPʭV0^t~uNw!?6k|a5w9Dj(?t\>pT !m)" fX3"}X-~GT5R-o͚YQɝ oqbN5:%!br-iAKl$ζ&lBb0Qk,kK7궓iT]# u %l JTB嵉OPWE?͍2l9aO-%zp)5tB#}݀3]椘 eqm#FpU2% ‡`ywV]FN<J!köN;^@)L"?Q[z\YGHe`ַR jIăM %/T_'_bOEԘ?n>{uo\R;4a!J P_(~x*t kf ,YrT"goO*O4In0YEu 09uPpB}2ؑDW*kE^ sgE,DPꒆ ECx-8G12,soHgY]ki|&Ʈ1ط2%Q M5qv?A?A{ ?\cVysusƓ:N/:,'yƦ%6? "*#ˊɬrvᔡHcy?}Ou-g\S9L-#ݼ50 tG`Ivyݷ!kKPI[f&D`N^BRw~ؗ8,ԇ \f.p#O֘**KHkjXED &͟uSCh`seyHǂLֆHXvU D)'O8ByW?_ Uf'Ls2sXE`Hgd`q><ǧ`(9M@T e\eq!zciOA\U،0z;{pTνluixZ hw3H3Dń?7#U7'U\%ٽCgO3)\e}V!*zwu&]NmR{PPʋZ QњoJam'"YR@&%f vݜ`a 8'~ BHC`:%dFIPj&=[clӣ;s[0{7[=}ٻ[w@#ߨ}e(-pxi\iԕ\&-J5]Y;@K7)qP |֨x!E@æĵAN?JL"-;t fS 5Y^@~'-n/Z<TzVVWus dlb[l0 )mak<;IZ~RA+·1FtM~G)Fr n Bp R>XjkTcYRQB{)!rҁCNV4Ȁ6{b! RM@6^% pEXW}d6+ tc~q|lD a6 R%Yw5CM[1Gv{8$R.80UC}rя@ |G虫v.V>f Xq16RGkӉ@ST:~6ܯſEctD:ȞatN)dobsP /2F S!Haȗl-\S,FTjG2bFG8]ȌHƗ'7hWIrL|N,\[L*ȑ]p|783yb56ğ 5s:yY q\nUw7C"z=!A,z`.r[GN3qTAl~\WHVИ>4޶8IfLB!qģ FR OU.MR1yLMaZE:6L 5w#w=LIְ'=WUNU^=y~v^Z 2/+>ܑR]mu MEf:SOX+D%h?:Bkoa-W=V/HI8<ׄ~~ Chy+ #%rX6}p Ŵ: 9gW4c.]u_R22f?}e,O EBgÖZB?ץN}y/YCz.T.e]gO3t⛒6[8$ ̭SF;Gktx_@F]:+7=кNq?κYsm~LRg }b@#[S!sة6ӈX,(T_of[ݹB_f-g *9uYfp֨ܦ]dӱHT;,]Az0vd+jԴJONC5X dُ˟{ҧ >I }={2Y)Tts7+LɨaQ ]T^) eK,hG1KELBΉ8K{ev"Fݏ1>`C^ #'Tu=u\2$0iVXi|}ˣ;RQHpӱ+|t9EI Bʖ.v0k_יNJ>81'J`IE";avE,~Kg "+([$Y3p?7ɅrBc _*Es%ڵlO,0.PX|l_ ʾsnҼBwwlTvN#^fYl}AE:2\x+٭]zι` 0y|uE(1.zlQ%ƿ_X_^_٬<ů(&\?,M4$v%}e)_ G{zȺAI$6/]6Iz#vNzE%ͧ[`xcFRU*i{4Y8^rD=Cפiu 2P5w?a̓T" 8 ey8e6Yh:Aq=ֹ''Z̚X %FW:܂  9HBď;*lwdl, MXou1ȝCڰVˀ|r2m"#0֝w]6(X92=q:LT9FP7v1یbAp3wH %\F[SV_"!tF4^= 䳪CAֶRc +dp&52'cF,s C\EjsLSiTQHD̳=+ c/Z@u~y#C^ q^9 MӨ]K"H6K;H#~[( Éjn#n5,3H!,B@nBsV:]@ղgxͱfɤ*{xu;&ĊU=H~V$D fsu cHGߐķI'YVr)Ʈ ytht*TG.qpYD@%,`Βx#Ӛ.jD)7Ly)3hn b7EpDeΊ_c%Y]%`!\)0ޯ-K]q$ϙy$v1_y#A9F(LSh/īR%nD_ɠ{$.. |4]tK GY^\ZZ]RTg}c>IL.J}Ϩ0N'^sn>j<6[go}ghgZ'B5xI  9j:ɼFhFtzoZo)0RovS;Gsn2\ ?[!Gte'lO<my9 PӻJÔjn|cĮV N,L y`hV9:1'MG:0b}qsnc/TNFŁk)Z%>llqNXZzZF$gF%q:h!_jx7őn&V"5N$"°e{\SPVY;]%?ݽkL|L-z\@v|U4 fE^ ܃9rJ.H,\?uĕ#ai XV% ^c-r)9#{vI9h(a=&bKbIO ^su igȬUUCӨϿWu~x/aL=q.pLS"Qqfթ纕 D+B}j\v?3W1yE1^8@Ob%p= Hɞ.T+BzY}rF8P}X%hcH/y݀66*Ge,=GJ,Qm|ubC,W6j"I;0tu`DGVݞd,frmi'( rKcP"jjVjy71[Tb\) UA0WS|.T9ҚK} ΏVŦԋSG}c,M2lWON> 1^_er*Vh] ƾзhh-]+ cga~uo80id!&x0?aL/HJ xZas鷁oeǥ!vGqWEkz9n,"ó#^I4@S q7حu1,^#P #d'I'vO6+DL%9?Oɠ:; ЦwS ܽX ~ _TqHAHE ʀj6 $+oPu獵j]Ccۙ0%gUם`OGLCs:T6]b|W஋[F Q /}mmf"G+P'?8!ǹD eW U\`3dT lu"B(JiijiWfOwVZ}Զ43eOZէΥuᾧ"u[=U9tϣ,Tو-n#7K 9s_=Ɓ~WyO_֦>4zePK'/lpu++U.W*H,2a> :lk鱯U_~PM-˷Uǐ;oicTzHkTfUB%'r%yVIOƇu_b{PC+Ra:=ӕƦ󣁦 +x-=٨yV,F `h_0Ÿ0fsk0:d:~=gI*wZb+׹4oF0rkJA!RS; Y ߞOiRY^nhTio0` o$+w~x4YG^LU ڨGqQx J}0g0IxSV͉=hg4JHAE22EtB5RDc eޟ@;`|q?b$:R&^.1fIPfDzmWw MӁ6+uyxƂjӝn#P9I#]I"VQGS'u7-q'z#T-қ"54i~n%^ۼ-ƷS;gV3;) +]İAx}DV%96p-b8\40 ʊBNt$̺7%P !_s>LtHwPWXdR[P osPvDZqz4Jcp9{EvZu/ v }6\Fkyprg*Zn0'L(.7edSBPRE`iƘqM.|H; PRc8s̺LE֯;d_J#F) /yCʹ mPn- sW3PV*WL,$do*{AAd5VJbsd!EgTRx4O$W~\#Nt3H\b5S_H y%jn 9!n[zWء^ &13r#v@>0F{MXc`N}s:ʑ$E xVWvC5#3 ì2k3 K'rO r`0nLezIKhu)w vnuE}O^Y{9vl1bCn*]~tkĘu`i%ў&'O6TV"'~+^YH8p-F"qs~ԅpi5#;ًw"]4C<Ȳ&$=O,nd{җn냔zRƂDZ۶5סڙb"*:,5G3,k)pGO*f0팪%s0H^, Q]jFF*-5pa#",i݇9sxNX~0ql!o MVfO]fPL˾2&Pk0#extgrԨٺٚvȘ Enoi<_z1CՔ(=)ՠJS(:$yt"a4ꘊ8RM/<2ݓ"FD0rkO%m kBAvl~ hd:]>w긢cUoH3=v0ϤZQP`skaQ 4oBr t0<]9V2e9O`(^6X uGcЊxlr]p(#18IyQ(oleNnڻ屓>b Y۝AoL.]%iMQjH-:-e7@fACbUjOWI XZ"1ߵG*m>ڷM:Vx5ɉ u4c/ آ>8O[ 󂎝()Z,zu#,O_VK>9gkɼ^Z74ģFYjI^$L wm9չNmT|+s-k^O {,F<~Cv ɾ=$}ufC1kxd-ΔhOUB T-> HT,{t43W=..@h"R yO<иU-b]9.Ybvgnv!#E%Ы]~C-w[XdDJʓfWZ0rݖ)" k]BkzB[v.}+d*漝zB !C.$;,ٰt,vɓ2(f+yo=?V@45/Fi.gCWU77d[wXg]3X.ON٦#`jyFds/4ؒ|aZ~Urwf8m9AV?]~wobG J:_.ʲO+n$q-]7(/*KǷumSCnD9p1x s&%kŃ:ep} %U36_fpw c7CDe0n~@p;ޖ9m>}}NXzY!` ,g[+p=ѿݣ-~t!oϭ;BgS\>F%CqG՝``ﺲWRh\.XFTkg'?Ѡ;?RX=h8[E{g}GFMQ_/q$}C 4UQ1a dFrg3[ᗉVGV3©Jk>QʲGzR#LV{LT<ۜb@i]DQP{Mcgٕ2V& $L>'c2xaS 4t@_#OD\uA䮥U; @oXKEGbps=g3=j \9Auyqhc'Bu'g@OohD8O70e晅Yj}IX۰ÀYSU-GNlA]kTfI@D(i"1gzUx>$8XfhWx[~C*sfK镪?! R(MpY,m?DK5i- :bՂ e;`j 4!mu@A!?`0+=YS׌SpO5kT> t=i2L>cA3h<`m?:4)e <$}^iɋ/šU #0#$L%5X-%0j&OO*;*DEIz4'b^Tptidv~3.gZ=r?lcްC7c2U3#>Ma uV(SKDoND% > ᳃׵CN݉@ ɳۛ& US'./??%դ:d ̈h YqDɄZ_1ćjrn=R` , ^_$?AStJW)UX?U1f|hrz3vaT(VL`K:E@9c, nO$kSxkeFm?F{LyDy:;NjJ\',|/$;U\ѕe6e-bH ~&u׏AJ H6f[RIs_ƻ}ҾĻPfe:i|蹯փ0%EXml(CAA16OKMƚ0]P[HI%-zgޢ/]ٽrJ^pc Ssm)m#S*P(җx 'E( r X'"YQ#٘AB`yKP [*ڄip U=ku5m`IڞͿ:,mŇ uvs^+Vl5IXl!'1N7KHGR#hܱ&x7?;=k Fj@VCl^KwDžVF4PnAsmp c%3ѫLom%b\KD5,7qts%a@@{dBRϊȸϔ*w{X5N1v8CuGޱPƵu+9 GIS~Fn>;f܂K_7ks$߹JJH7{i*<=W  g469l* ,$F[5~AqͬU('%NH'k/WS'yn)&r=cHgէ'~q^q:$a&8GnyZnxuOSN\eY f!؇v 5C[«weʤ wF$"hR 7uyUJ=Fkw:lORנ!DL.jUGx~N 4+n%+[&r(dV#ˀ]"ۛs8{M+q󞩮eOD9+CTdCW2i6ʼ_Ndop lq5Cu] nq5a>,AYkĿ܁Ӻ6>S R7w# 2vmkd@C9CTT^ęY]Lc:+Ya(RM>W3GBjdtdNzߡz@@M2ti\٥9X a Ny*eJm-&qif]l}#VQ<]Mb7D= '~8l2MMpl]&O))"! an`5Y*S4^_QD&*#=^:[muN7@tsR#vӨeW;O PzӤLbI:6-̸.#odGC(LF<"u)8_WC\;`+z莵[+ʏۋg oi*JMcvpb%3QR~3swSi~#_FF3(W+.=L:HNP9a?~NEdUZu\AWH u5d5dh=7kէ~bܠmc0L+~0qW>V<[$ Z'{ğϥmZ-N)ee63bY>i\7M})`.7LSc Yl2h} 3@xNܸlW:jfb 3:iN1!-'ƝW֌:U 衺c$@f„4i逊%HgO~Ѷ@z jwOI󎷆uEDh5]pZq&Nmٱ*pL( ~!:!,˲Y:/"f!cU6IhiRj8PMB~T_(kdנ`w7v)207vdv#Dl ^ʝNǪ2Bzr$"Wj j fEp]ǓhwfrW|%F|] N`zD8EV o}חej[".b9QEw{OSP5?Y(ĦE[KCE(@Ĩj !? >{l۾Ȋ1_iK_>+Q.'8Vة0D\sb[ erūm՜!޵'LR:s+پRk(}t k>tY>{z-'1T,'4B!n(OyCOWAvHG&мޱh㝱 ٶKJ oŴmXptR(6efi ?͖e1,>;ҋg#QZ C> /3rY,5=a!GhV$R^z ' [țJo\6U.Ү l}E/Rs*;"W0|[-H|`nDk\4qj@K ;֥-UV(uKGPd6)*4b*6KRX_ͥ#B$PMrqfi=ϱ}j$^nk&2;>Zf?dx"T}'H:UE=v_G_SOIBSh (I]6S-O8 _oЎԮYzQfzT*Ln큡<D;L4YW%>m.j6B#HE ym%pɠ͆3pL+-\Si[zݹfn݂:=7 5_i'խ*9n*a&\ UVq軬HGZ-[g}Pާ665tm  ~4 (Eg-YCղ,6 j~|Xhu[ae%re>E nEtY` $^.[Z7i(Ġ|xhy 68.,=!й9Z+ׄY{b΀V_li Ͻ\Vjpګ*}l~u q+<ə1fc ]}-:/; )Ag`zY &c *^S6 +L6ĸwAѶԡ(chUд^qrRbs9J:A8%L[ l᜽M d` y陽늅,Sh<~ZSmdSuќ\oԩô_kz J-.*9m.~֦QWc#N솷,;uS Vr-uHs&N6cX2P ȐRLY3%鬗I9ocӟh627x 8yZ8>Un"{,)+eQ_.;G<9 HRQAT/W[8f~Hzo|DCH7Wj^ftw4 D]&JL)CDBgi1Fa0\ҥk'tʾ;3hf?;h.^nU{NMWE\l^pZ711{E)πm>w|i:ZztzK6ce\oKK~ JgĄ. ܉чxJMo7Q56ߢ9: +Ȁqኌ+s~.syhawK-HIS@Zdm|-K{О ڻGMw6tyA[ŜgzԪ`ěpub㱘r*)݊R62jz/^u<"F'W/@V)^suNeGpX7#r;.x" zw4D>Ӏq"?5\U=uG]Cm3 Izٽ86eI NյO\;4[grأ?dKޔ_Nmg$05>{Bk?]-֫_ YKkHi .vDin7l=<}By#>f %rY+#pCGPZ3)Yx2j`OfvvcADQ:aվ%Gn0C #vx.zl{גxqLϒW=x5Lf4=l取J\årIlqT\:2Ϙ-;?-Xm#}404ܸ.&<)Q!ke3BFI&* t=X9%ȢLiC^ǹm{öPz-O;L;apk[7n\#q[b3iGAzFx`Mmt7uUݧ'@D KmRGx>b~ b8=z1**P6N/8|!b2|̹ꡞT=`TGe9`H죧-fp>,ZD_,ܱx:Jyҝ!跃Vŏ+i4Q2h@cf"2tTGjo4 i@kӧ@ ܺB%6EB>Em:FQ~|-!j7qt횰ѯ),_ VsV;OU?Lb5= wP(pbeP [=8l\mN(WRVP?AOq 1r4\8cA@$HBl[G<66?%ls!5o<{_oAb-gtWݷ(;?l@\*z^(qv]Y]쟪{7c,FdA}Gr^muܸC-}*vמM~K x:qtOğ2ntOHp{V' LKTd-AuQ$8gZ#|GJ,0G'hfSaOu'"ܒ@6l,]xM=yWt9 ;)shv#[n ([(ݑ$.A1W@*eƬ SH*: jWRTGuAtNE `T#Hξ vW֥QU, 8O(aWޠA1X˿kʅ@oȄg)@+FX A]'F.Y>lƁ?sNK[a##XwjKPڤ*ŽTpZXX-3B9-9lQCԷ{*Y)!ۉD7˲M`{3{؊ٕ!j"nwãT9"KXnM;2ªV"ThL66QvQp瓇0TN-=8bDsg ۲Rq z52?ӌZ}qqRVp?-Po4IayDŽl#N齐h_1Z+3[*6Rh%|0L`tмO9 4"ۧ;'PㄽMFU; "n]ѯzԓb?Q`E#;EO+|jմ٨tD^Xf!`yٳ6$xsDz@7'&eaV-muW("HZ l˞+あy`XFSc}^ЃbM?WmG;J+R0Cx4$Z:޻z\_#8^XyY<Ԫe}w=n9ڠ>XDC^PLu#7ݴ !_T~N,HL'ý˓m@)x)" ^#ܛ5+}9e (KÅAKSGb,BI e {MqUvġ/H.ڠ qN_8".Nkkw2a\sw]L򉙆{Zn7񳃪Y%1-8~YtWNP;`: =˺TU>G{&X)Z|ku M.@O ȎYqRHgc]()To?!Lߖ|Sj{" y$9_w%jһI퍈8Ø%؝,'sfchL$%#NV`kv>vա\-$Ȋ)z#B&lafZ@*>C9u>dx7@i ]_\/\[DyN '|!aJ ᑚҤLv0/ީu%Rμ%Dǟ^=3`RO{hV~@I؝0 +5Ifmze`]J'$:;M)ǑQGIq`!w/M| CY` 6xE; v $a? wnųMl=قǸVmzW3DOn)<ewR=u]1\LGeiR˜%]L`լ()(/~g[SxƲIǵ.*qiNzψүt=:.een# I<{f/5뀉A$p9I5;HPФ+x"FgX6266QE>4CE i67ci <¿x`beL,N/gwR\0tl~2oqLy/=z٧>T5/4 Y'}_YCV/pUegnM=xbR[mK-$G:h\A I3Jm:^idIXͷƺ}mz@Li"C gI齟 Aм546 # t>7œ+%eT+X85ç)eCBԥLw%tm3:\f ϱ3"Q; w3Wi7&(0C ڸeσ3xƣg<9T'JLO hJ٥\sMs%9zd@6%B5sU]bG T(^AUgaB7 smVyOmD^ mK Y+Ұ4NGI]\F˶,5vXU-` [krfQm7ȕ QT %Ph%'"CzU]$v ~&ߔ~gb!=3>}iw`-p}ld gOL1]2k\K rYֶD#7`Q"JݕDN5MԧĆ-RRj//"l2^/jPu8 u"u^>U!l})n#gG7?}YDK RnN<(';qKEC"4e):JЃ{N 1[%ix)'XM@.t&W6>k"o0-<)P|=ɡD0` ,{l-bKSW]1 ^vM$NCĵ/Gy'w"x{GXCr4ւPD 2=lC/PL'd% J蹬a "|%"ܲ2ݟn,QՆbtW7Zt dA*sFS0~h*܀e2<V΂psTW+$ ^t<{a$̗E3 B-_E$>$ Qz@l:A'({fjwIC>%R3t̙4*"R칢 $o6]Y bн#/EcAU4qcW_OP -ej ;Tf 0F, {ͼwCQ Q.2  #zz v HK| ]| .;OlYmRT+GFN_*'7fp3yQr|l9A/rhC`lF0EpΌj)'Tci}ZHvؿ#xdDXڵujpUg+\:xG{FY^ֶ/ }"ޛJu]#WeR\V @^ :\\'s2fعpd}x`UBN)pu%~wEB}q!!1:]9`WG, ^1e8A:m{F|IH OL(o8 Q2ɷI 88] P2LE?xXi'l Ϊ&1=FoCI v8/TnxׄGm52b#8TFǍ^^5igC촋TTbb Mf rƚUGue1醄mn(zCu4~-1C@sbh@ gLR 5鹵.Q6)nd9Iݤul> ߥd%ry{ep4pGJ>=<zԗt%'zTe|~r鵏 >u. 8"jƻ.3ި쭼uwK-+w&k˕C2yITa*2,j7ߴ-Afv'^{T@Y (H~*_=6b0?^J' .Hy-!%{/hFgΞ+a/f;7eHݦ"|5P4av_F$9J.·Ӛ܎o;\4 ;գ8y"Z#傕6(ƻeIQ΃v&5Σ4e,4]#!ITJ6MZ P| 6yJZ&^@.n_FNhRP<~`G92W!IJ`7b@- @4;)o0E`͗\\c)gJx5x+OQr#$R ]VqmIv WgYery%>SBq \Hi.yZ:9Phi|^Â2k٭7dq YeCR)j6}@ [- tr\P%gl07C\?&̹carʿuBPI&V. g&:?U(6xTu~|hxbM]q4%Z^%i4P`]%0Dno}dK#"[' v2)-[4 Ӌ_Qs[ρDl0ZB4t[I)t#/ Q$s%tG/k}iyMٖ~KoB.d: T HYb΢>iXA*Dvʬ'hZ̬`P ciw2YM-ܰGZmp]B D߬VLz.\Yx!2XNB~ #}nqg-$_T@kox[ccgg3[*.)@$vLoFE.e:; 19xgeAz\Iq<ϟ(lMO}5 2j Lm& oGr|5 4~ZJVb\MӋ!^SGֳBfJsS=f!TB]#ͺ gQocy"k }?KD1YŨH'V=\ysfA<ɾMhл9C.)@qܠ*  1I:l(k əT>4~>хħBPyMD^t =MKů~OV grhۉ ~MZ kqP")]w -3,)S29߁%Ư3 reN8;d֡K>z!o`Q8e+avqDNmn gfLZն c츽nXA 0d Я\ pq}W/mbHla<2JL-Vr ġwӌ$w=-:lCtD%$nKқ>dADQ'3K눞V!( KL//7=mh!(iTӍqF&(h9k +m0(KZֽKr^$R޳]HiPd!zcM!}7κx4ԋorW$l| vv}RCyMY%T}& F.r;XUli2mZ:i?EfDi|HmQn*<-KoI x@(CTdCTZbT*q!}]OdoIujի )ReDW[×p.䚸Ip( ڤNk{/cL_\c@\@O+<^+*ZLuKEP)8uӓ/+UMkq>}%5nJ_e~cci) ~sD+; ʒ qÅݗ/ijӠ!{cH=y(5GoX&gmCLNQ~A)0{$2Ni>(}&NYSJ"wiyM<3(vlќdYVrE:A ?UL 9ϕ *?n282Ąu*R$wY?匃] R K罚ɠ̼R6_#wZ H(i!}{6? % \WtLGwUBn:PKڶA8bV(`(<+Gc0 R" tpZ<2$H_eV/*3E TW/?|{ F^dcypt k ㋤,#ʌ]}񎕢NInHݬO գ[+!^2UKGv Qs}꺣(B:XNgR;>{8S@LqDzז˘>W+GZNU(jBď W9 +pygQ}MG-ގ fΚ#=My =zO Dc;ygث+͚'k)?';^ecH\fL쭛%ʟkWѦȟr daYŚ2yD:^115zU/R_u2kuXF, P#'q0˥jEZ^T`=wt~w%r#@ $/|% .SQik`kS<.sz.5HDExyӧY[ʧ&V,D.8''ޟnKD՞H .6yFhv%`CߠGtVy)ަ*p50 2 mx(ݤgL,nMrqhvHM#z|%4J3b7IKU~OF>"-1E/8sJ~¤ D=w@8 䰞%odN;8 !1~gG.9Tp TCcRy uƘob—'(/;HR\vD[9n1j:͐#cO{o4S;;o-еő-7$ /Z%|H5BGعSbZz'GDO4S[l ~gt+C3E|PHsQ}NѾX_8욳4Y} z2ҖfhgǤ3_oirX?VySIlo"GgXNp9uY${)+I15LZNYdF:Lgg3Y!#ɺ^q-X7 G!|%[Kd@sc`w]p1CF.Pى۬]mLLI8D)"rd֬k8|` M9ݏP̆?.D9Cp:g\O?Ʌg9&Rz5Vvu6k573}ej,upr#f1bsJfLd)Ly\+Qˮ^Oe# Z6Z.:=p}L0řN1_#8edL>wU H~.>n-iH0a`ݣ,f .uyi j۠McihrߧhOeu}[q޴J%0W ʊAu2H>HQqODހJ\)?S*yK Mv6B3zF),+yP/01P5g8&Vi3cdy u[?=;H~ hx:/u|f^et~J>3Q g4f+kNRwjaC(\$qBTM31zVvp T<~잴L2m.&:?!ǟsU pwp60[h?6LcH/UA 91Y޺gЎ,j/{<#JLwβA+A 2qrUJq0n,K\8/`UE1jG8|a(r :'.4OC>!)w k9籯YMgCA@ :z'w9Z#3e.^O04  ЎEp6U\Ew 3N5'3/ryQ78M]wteV3dt1瘿K*e+rTmئDe]=Ѣ>m!pch1sw ï*_|Q'BvTMBФ,o RQ!N+Y,bi@]GE8h\^i:2py.g줵5gu 7xPw}Fyx{(V# 2&yjPu٥$^+11W}+(AL ǧj֤eU|e.iO|XشwoFIΖ>D[ ^|42A-fxׅe22'(kȋB&f3[Ӣdgz~heAwvAV y$RD2^h>+ *DlQ%k0b=A˳ =waBŀs(C~)_BO.I~t hRDo_B  o4kj=<%:n> 0gbY٦2\@-[8P SM+OLI۾7=Cz# -GrC:.Y.1.Z>OomL9;l:eiw'm,FoאVrvk]; vO<].Z h r4]Y: i}Q c|ZnH]G\w3JCҥOaI"BԐZM?ˌs]şR@{j(q_ٳS/Blz.#e>ߡk&}.rwVj4Il\?W6)24lzZX3Z'UeZ?$Xi-JXS2@(XrU |LFp8?5EM?xStj^4lOz+Z] cV=+hYe^Jr!#pyc=:&'#4ZfGט)x 7t0>jf W^?w&|G4ߜO(\ŽdtXqnd4XU{{fg]Cз畻:_h6Ίae:6(mk4P%,K|T1 ёYVEggGhK.&E7Bж˖M(+-s[AN*_iH$9vv|X"UR5ΰa u19jD>/~ FQvq蝌À`W,irL, !py6DŽroQ0-fcp! NG jQ"#l̀frQ[RJ3czcpCzWBD Z%(#<!N%+361.rR(yBW+7.ߠ/z D!W/\bzngX4tUS1F}Eb-{\'zF,`-g;s#")jCIE{ΤKWM@ņQG u t3-=x#U^hI7hbT݋moWNpf(w F ~L-to 4UA/͍8)sT4)^9`5= GgjOKqti(cP*@ҽ8O y@!Rlw>ewF'u;Mu[)=?Lχ p+bCrN|鳬-uCZ޿<|tnr^K <hJHylHh;s9B=W.Dt<,.Az{hMRCq*9 aIhӽE-hviMAWJ` \Xsu&2e̺',[BXY L ڑ{pmŒ>4 IvNOsJz` fwQa)urLZheS,R2y8JN3h~5w8dG(l]r徹 an 5!%O^+ҡV~Y3FMTђzR+ރFi~+0)B 3GgnJK)!Sh('T0.QD+k4s92q&#Ŏ`Q Y(gSr/g> gG8S"Q=ptE|i h|W7Oɱ rcVvPLC* ѹuN" hw"E~R5V蝷pmRu-i4+a)@Έqv(ahyR54dj1wA%VZj.GS7 jlڵGW5!-dω k`JV}~B>f d ٪…/D*}rvF(*8OQk\hl::MC^o)jX 5̼)]t?wL]H+tM},CJ) YgWqA4zLu-vH͙{76wRmNGY'o}(D@ a%Ng@`$Uι^܏5FZD5L|1241 ^Gu2pm33xb%RT5!e >nYkbâ-;B P u &< -  AclFmB)ZZ[?[Z/OV?"y$3k듃5RŚ2p./M &v(ΦJ0a. p%]ʉ1fym.6U}ڋ=͓ !sL R7.ү߅/qDc zY򮕻~E3'FԔPqA~L4بʕ[(ҊCIa8l}z1 i2tevAX'R&Nz՗T{ri[wvuw!C*KBB٠L{l43@tz ۏ/m3܍a+NF ͘p:P:X6{Walx4i?:$x|իJp >V8ەlZ^I/Qfvq;k\}ʒ^i. 2VL̷jXkTgkf * X?8RCE!-1tI:濇#Xl(yȢ̴P847`Эi/WeD%C5(~ =9*BUR8Z$S"WJȯ4B0 R:qCfN{#i]^9|'xx/ 9]k}vus/3+QnVr_1 @$.ХE+ ҟ>l_Wyn{B:3ffnarάC]ĴstO/x/'珆eCrk\³}e4bXXskxŪkeyRf*bkj`W߳iP!_UôQՐ4?)oq+4Q~`%#}0iVDs4F_!-n)R)g>,Kd n)z'[%s 1'~Ti:#eʉ T[Z$U>vJ5F!W6㪇2BonǬfp;]]t؝3б\.  FInr&Z[-DydUd=01eFAvN JC\L67PZȔ^t~]c&*BüKqc}x0Zw P4<7g»4O7}J+$}4&AO@x1rLeVM(!hL=W+#`mξ܇?.7iN?A;6=N!* cG1G6q^&pTޔ?jeqgAr u yp0SP!)yУm[*73m5Mx+"'XaK ^}l?G0>fgQo^?mt Կ'F'jAsWS MSWVSU%[aE'uE '2ճ1_t^N}:>,n' G&^K]x{v0~/jg&'fxHj"zl_ 80f@ctDY_Sf0K*3:чB#!LGyih~RLhZ0Z Ů?y%K@rRb ݗ/τ잊M gMgp|X`eUrhR QPawMPT3/e APV9TQҮT}ݛ/QgXӅ E̿ķ)zLp dOY<0=v*t!4t15"ڈ^~QSmqdc P߲w:^gHNIEwO c+PO L8|띇m8f .ɂ(m5ϋbSj"6S&bFu*>M.r 1R2XQ\8_-۽ Hۈx`enW1.;Hlq]HܹCnxѱQ. Vi1JA߲&р jXx,ن1Uׂ8Tfv-f% }Z O[gw1}7lzG2&U c>̈́c2\pWBeD,C u5}ʥ6 o}A`H&j8i5@UXvD?N=ϼwO^%DP1"#M:HF|{=Q3}yM&p =\.Sx(B}/$z=%>~jo\qѣn~{4]7b0M雖Wǝ {ק?0NVj@SSAJwicQ5N%iLRyG.3czJ;9bnyuSu݋O M2m8zOeZr%|<.4SZ/K\W*i;ar7[fJJQEg;w@<^5`݋@2؞tUF1ea t} Q P^t71\x=PC. /w"gDrRx,NV֞Ǵ6z,б:nDIhy~sDWH'm,ұWAg,(W"DUP^6LƇBG! X|Ѩ|_2vMXm2;bxC]TSbӨB]GAg+\6`_A+B7z˽`=DüZ8l#${|$`49n,3=N c%d."XBH:~;OylL7~k u ; _ qM 0eLh 0NB;%(=#߃٣/c=2Oŏ%Bo)^8l tکP$vwASQ+Bv#rk٠~9q$N>{Z`fT$[5}}P-oU5pٹ$+.4əT=!U5UEÓCrj#_(;h a_L>]6#.;rѧ&88qIv/+.?%}`"(cTBW'wu㫩:s{ 䋦UAVZOYwr"Œ!! IPЩ#&`\L<8;G9g`z|o _ y(0 y:knh1{1)C-$[4w$KV;#EfҜʈ)rڠ?g ]+r]D0 A:^ʵ0v!>*L0KVE`2zVUu@?pOr>,8)(a>#|`l7Q ˲\ G}f3/}}]sD'T[p4$̗ʥ 9WpyZ7rJk+/cD{ĘegWa$jDPW^,ql&B#J߶i}g,aQ|fboA#T17dc.x*@ozd˃g#:,ZP H\JG@L,qh#N"EVohr%X$y6wuolSu K8Sd@cucC1}uXBz!:'c-~ bLXBۭXӢSXG."w!:-e"2fF*S eqTp0|9,9onz*^32JU:q.Rsqx l(Y?9R A-VŏgO\KFl%Fr?hmq a%C+wzS.;"CϘU" (Xg֓K VxԩZ)<q&@,%inx $7U(_1ٿPsr~Q=]T-*?Zj12M5XRu2,$O-I_ߣZS!gSׁN[RrcSg4,{Or%(Z`&jgQ=[lvܛ!KY+ ag}ji}xgMM.˿^3M'|6oŕOC$ F<ŇzNdY^N%&ThU8W:㔚j6#,RߴE8v@I|٧IA=G#ƫ _NdV'wut?A,780)8i)d{?kˡs5|/`Ɛm(^QYu)ψ$RӳN-Z=]f "0e Si::g.09^K.EgQjOĂjTcJ4+pD_-4* N>ak(5Y]6K甍n!ׅMQOfgTYaW.X~c[:JLF\ȴiΑoCvK`SL[W?aXXlCa McUILr1>C1zK!q1PMinR[їJ/(+^̇~n  T+?!7$ _D#_#1\߭=C( Xx{n x&ԥis]d0_s.Va\oT\Z)y-Ɂ ̩AcػQJr%ғ,*z_5I醯s]F {R.4X1cZ*kNihF/GwI$Ǥa7LZV-o$(Eejqu}W+Nin2Em(5Rˎep ;:ý;5NNPjt$ThˤyMHbRaB'#$tZvd~Ԭ|9 +,/a}U7hEf 2q*2<2wσ=X]FU <F>3 #:,oZVqw9:$,:C~ȟq1|aԶEWses}cD!R d<[Uq&l܏Tcv]ȅSx tǴ;n҉e)S6kOܻqXD'Vr*iJ1E X:7b h ANa,'&ko(H^S{,z'/1j%sjX- (K]#_D*_ MkE^/"1bgQV Ҍ3SwR}W(ta'kQH0T9+-nJkwdR=raj|&¸6 _A[=nG%]V! z$Zbe|ˉ^bmiyV{._BҺ@1, !T05ZmOOfcrin Cٙ}ʑ.|>[hW\M/VV'9it_w *e׊> g לsp@^)kf0.\;?<;ȧ>E&#e8@ޙ-^\Ba 65,Fu  cj]_8Bit6T#aXԹC}+n1e9Jpp9Dt}Ag3-ı7k?XK7?9u>m, iyױecx,Įp;LXoDat=<QB?]ob7jbMNfАvQk+-Au-W:>k҆1. }eN̒h0OveȘܯcV1ݠsfvnš&uǰKw<@KCoS?,CtCeF}#`T!zRuPmHuKXpP gdXUfpX C4[wX0EN;Jt|*( Q/N6N'$p9QbNxI]/>48D?ÈqBD&ՓbnW f_"v (ݴg2& >u$菵oQJdm\'/8pܚ".85h.wT -P]x:\S\䌐q=,]L8H"$< ,5к#jl;[5u| HФj[ bSoU6+-ʭk8irczȣNjQmġ]s[m7*`#t?Zj}? QjCK,=su~w?.A>L3uz`Ք8N>:*~gٲ.™ϺSh2HR%6McMt(E |%9.^؎-G ud6` !|5}a@tA_\H-^t/ӂ-%\Zx\ !<(A2)K<k'jn77t> Di$lQHD[@H7/A֧ h!V;j~,g9.dT;5WB1k#jxd8i4$%#lme y7܋xSĕ}$ؼ-))DqåbXW'z2]kb1imߵ;Tכ㏒gHʏmvu]QjHWz* 4a:+ rWRu{Tӭ4Ӌfy˘BY(EVG&3(8Y@~pFvTeIȃs{;oHv z*02ˡhWV&X`,OD=თ;lb##?=I$8, 0=^)ñqx;aaeޥé{09k2"U>޷|ÞY -fS/9%^sW)Ei {^k9| Aw-=0LVuR@R=s*chnw.eΤk3%l٩i-Y?K)=QF8Y&Ac{>j(.{(uvM&$NC ?;1䈢=y4 ܡ|#3`z҃&u>J!LGy4Lj?m)X:o`ƫ U\g({t:y(G)p3ZJBU, wax2,z!o*j@2'i}`SA@Jº^2yP6~dO1*MVmEKtpQp񣰑}"tJ쀐,^Lmσ>lJA ?̱0LOn\ j[5z!  9TkPV]G}wKF<JA-)m} 6ΈgX@݇qrCd{/8-qs-bA3)֐~8O/R(/YP~*#Qk,@ş6Q}!" @8?\z9-ؼk]-n,R ,0f:Km33v)а`a.taOOo9ˇJT`Õl[>_ 7wFE%jipü=%g(/a(7WcKi8! 5)W]BIC4a/4DFK+ PFF{PJ>ۅmM)s'P&*͓Dm@)PPB`SJ֜ l )7nx:+T%LX ~bx9 6МЅh;799*L~D*i! (ne9pM2b3 &Ŭrb>~i]>PǀK)IS$,~'څ ;ej3ݬYSCaYZ<2-H$,#rI(:qF:T x x UKrNyV\˞wؠR D&`aރh|1Z,Lr }3E*?dDA}}>ħ>#3_=z 4z/I1zxbǽ,O0#q#pp c5y,xv@q]:'tdFGUHΖ(K+odpڨKH ~=<{Z{F>@G8~>淰_/vJ8IC ,ׄd҃ldʓDflJ4ŇL@!]9`mRtM΄xy),f~-%jq؋/q']îOOU:.7ɇ2SGH]htV?;T; eP2d.7޼Y?"ǂO^!NIu <DgZQ&,ˊ9pãJIE3T?|x?eYB1j6Y'wcׅ1њ-)d G⠶OKhBx6C.PeE|i:k . !W+јCTW69&sT!r5i]X;R(&`ꝲfJrs w=qՓ KţiɢE/=O$ՇBS'\L8ѽZ"f7D%0nU7"V'K~!8*A:И3TC>ߨQ,5L=6mx.vэW+[0-;/ )MveɃт૯'-]lMmiDKsEѶ}vq[ߢ3#^[~*7Kd{h<>(2VV2vߡ T&G`'\V?RaHE/-Z02#V,_b!O \8xbKi$h&Vo3'>> vLCu _lU:0kNۦ &JIۊBs|?UhBwd3X*-&O4נF# EA9e0Be#e92Kńn)hLZ$B%ѱRz5(w;$[ `R`WɬpdCX0vzv Y|`! =&v7 Ag=BZBl,D;Dzt1d<뎃M '$$H[25фD| _"% =hcJ򌆳pTDgW v&xBX:}e.=gh]nbJP4!v8`"[+{76 3 XNW TK8#jv(q6h2QwO/6D`O?&6D0^,ҾǵC,Webo#"Ny]7-"⮙ #ߌOiV/OO>E$R缝>vS(`"߉ˮ|Ul.yGفE"*T^Rix]V7 '09LWο1Vx Z 緳'hQ''G-<u>G?aԊ7$ub,-'TI8ti:tbH5J>IK1^;\=6:ܨl~h,\}eᎍ{\p[>Z?',0GS*(w~‹-tHsr},\ZkVV)xw~E -IMV]^V4c?}+S| ݓpÎб;KGO_g7nV|.fi95QWNB4Mܾ 0Ä'Lh&_r¡W%PEmۖh*n)DhWIa< k=ɺy.4I,?6ItiC==V7@!$~Fճ3%z=pתA7 <:k$]s\XQ[taN*su2?1#z\?z.l1aɯzCaߥ/%=88'uAm|= #n"Ɨ./2E&9%zY9Q {~2y])FaJ;&]RBN:3įٍAϚE&bؘiV6$.ߢY͂ʳ*n]ACHh/%:~v|w[קo®Q`IBKNc>m"Q;I%pkW]2xks m 7,s)zW 8"JpZ:'E4Qm=04H({DŽ#RfY[f-Y;OCM HtUHVv쪗Bq^OߡE5yrjdWMfu`cʆ66:je 7*9 mFt/> t&ڪ Rӵ3ӽW^$wt~$iL o[h;B] CxEb9EZg -&I74[)~x42Wȱ`3䖐|}ZP"B9Oeo}^u, HGz5|[>+% _hL56 x%gbG?t#yÍz -Pv= V7Dhvw&.uozhu Yikrb~&z[§sV2h9%'?Яp7~q:lCRIghc@@SR:t \]޽Ypb1&" Gsjt'ƣ7 n6<3e6\JΡjNCnm,~uzuh"ruGqӼ2} hv _%1}49KyFXp[q9ў1H.u]]\Np:̋T{{NT 3:Gn6[EOo%vxf UЌk8eL*H""3k<7w} uj ~%ƔگĶOWUvSȉmV5gâ\?hJ+uL?-“aQ64$p6Э^U ̠~AlEJ P/ ΜĢy.8#:$on&Iq]gz*aW̃uiZdOzYJk>/rv 'ȵ e3+aAYk2&_Rv5/:o!Q} ЭF#6Bvޙg=.y$~wWqUC֖\T\o%P 6ԍ,6]-ۈ↓;a2{- DzM6#ɷ_IRd[o7,ٷ [ּdL?EaJn)Ex/ؗJ߾2@&c۸k1Zz]<n}^ &'XƑ0~v69|J֗U6L Y`5]I)UD"CK PfwcK~WLA~l:ݕlܨ;*WXDX=L:Mh1R@,%]ĔVoF`T**-Aɟ-t o%4S]ʱ:˨ہX5];4 6>_w5 .d?zG G(c/!0u㉏Ԣ;7tn_flT"[X!6rP҃Gp^gE; 86#9q>}'^6^M :?ʃI#=RTPrs"4c=C 5Lzx vM; r!=J`:.2{+3T`5\sV0Ѷ]^#+̤-7tjZ88+Bfc+5̴?p bn*'U}SF痔 !A)ݽA^tMtL~plV@n8CV2-]iV >7ҴTxEZ|q&N?5(/pW|_0лet,1Qf$0) q *e A1# @̳Y[- QAnii^3 KuBa=[CQ4 0S@l) /;kR[p6*) ,aQ3UPmm\ЬE\r jִ(Ӑ nV(Ooq{/mts_ADBe6[͊GxW (x!V.JdheCq"KÏ󹠠ŴAIo@PU}aXcF(@ʴqt3-wm];F~.dP/vlLBQQUnid0}J7ysaKTG]2H}3*FDψW[ܦh I;:v{1OQFǘVSO֜Q g?5W2z|5 5ܾ6&̦^c0V$ioaY,SZ A"rWV?|A?'l,՝Np@,/ȸ,1; Ǖkpn@s opyPFKxEwszO=hWbJp0o߼x(κP)C3yKYDT]g=q?.]]GOqѢ}k %zS[/Q:ekc1-LΠPRzyFp~@KP~N"vS{7n?|00Q _Jx/זFcT )Js@RÑgxuz `j|nn 4{1`8Y˜dV@-Sr!g 7t.Rf:͘L17 O&9@,vYW [9ɇE{N]tR-Vݴa'4"g-!#gJ|2 ƙL1dh!QcY|% q$$eR0۹G? -)9eW]ve$WW!@L-VQF UeUG m;$.tCT H)i/e-&3}[tÃ):QŤͿZcO ,e@0kVsi!E(nN i`M2 3@{xۼwebx|SB:x^*T#F|\Wx) L ׇ|4+;^HWU Kc  ВqFz+(N 5Ad G{U؂tX΁& ~>bt$R|ji= Bݾ[D.0~BR/_Xz& گ@CnNmm,Z'Arvlsݚ^g+&z n"?4ui] vIb[(Vl1hR՛5}fTQSyq*4Gz`a%Ȩ ŷ9e g~`)O8كp=d^о|uvfcOD&)ETv$ $ ,T_Femh쇜'z{%SgIN^-"Sh9!Ff*>$+ 9A aد%PݫJF@Y5M:aέ%*N CHBE{Ƕ ?tfa4CE^zёUZ}CTcӓQg¦UmUt )gmtκ՜RGȌe_27y)K.[&6_ VB/9YwNӦ - ,m%z^ԒK c jZ+)%>K.>a]u.".7P_4 iXeN’ޤ|SFBx.DT ~UN[T8*Jiez0Й=c$Bs^M*j[f@T \[J#\"W *HTO{AGَrGT>]n%O qO᷍jT%ƅDk6|H:&)S5da 'c0]3'm|7):$An`i7O؏3\3gJVtHe 4>Wc`vR,k𧧁/7<0qyI\U;m6/Bt 3Ec"!|6%EeX^i "&x*b"ؾ}?&Ye: q2:n˳/r Lss&E6y f#SE:?HZD1O֠rJZWu9W]5׽ 3!M،8"B9qt1P i-;ǹ,Y1}pݼ80{֍\j9=̤8I㖬\AoV_i " \iؿͤa!);VA`su`ĵęHgP~~ޡ3DTh^!xKXm|p[GlϨY= :TGA!Z$ye #UCr#kfMcZ:5Q2op>sK*qzJ 8^r!<S_?m޷9Ei9oeizI(6d;l[(6{?\_9iSsASvSGԔ3pt:Ԩ;3`(hltcʯD?ղNA;I( _Na}spW'q탏yE e96]TaOd١/ ,Ahwi%MW{C~qhY/H3Sy_\@>gT^Bz x_8B\̀ȺKn"kv[0Z??m@1BQj_.{edapRYv1A/q(\7 Oy΍Zb"15v:XSKD V9{fkWaEApDL-d NŬa/_/M|;~^Ƴ~,"?Y(O%*M,`C„10}Hwu7R]I7 -IE$c8:S%DƕXB?B4-Qw4EKTQxeHi MXe8<(EMc԰ vg *Vm"|l7re9EOY+I}zЭܣyYH[|},]ª+˧ǿ+d)xQǝ'p*>b!uad_5K&Z0_)xVTdha"s)EUN-af<1X}Vi9z{2|a;GUzQ=v"y zEW&{VۛLʷmiJ3ޤlG+YwK7RWi1`hv1FyC fX*<29,Y9]\A˹,#xfh ֮Oq~]KQDCoJ2)TݖrIޝ(wF !L{,~˅cbU xq$_%'Aπ!Qwr.̛ۙ l.D3'GYccWS0 d%$L&ċ`̇a :L3y ͺË$Xռ<euV'l)pZ|ԍ$ F-6F-^7O!YÃt1sn^A]W~і[tΥiW Kxūa뤤AR?O|C-tDㅳbG>n1)R \QZpZ|)qSM~;4s- ly^ t4 p37;~h▇E$ ]e[S󆀳Ta59%ao!?YTҭOX^gy_Ս}ujo߱䷻\W\zƽ?رh^ Es1<o5Q ʚiʟly9;%OQS7})V"'A҃I'Q V1E٭DZv'u񜀾I~q1a>_.$9b. ~iE$ʳ!TK e*~U䆋l@3j+LJ4׸׍{<bCi>cGs ?P&KNONN('w)_ȷ _ܒYM&yX}i@zYv^+9h%:2g TpIO֣*̣4ȀSq/.Si~#_K;XeW*n( '~2Rq-|OEGXfl*kg 0`fs{D/N9ȢvN5蹣i %W,4KD#$Zi!*K0iٸe2皷|^f,z=B~B{8;a@J-ѱo=o Mv tZH"S=e^Vm}q '7o5C b'K>#7QF."}/ =zu P(RxA68pBCkPQPˏU,ꇼŀ ՜Nuw cBNٰeLzf;㋎*F_ eYWE*/nv6^{i rGl7! ]Xݫj&8gB$0 @fqxoK-B/jIMG_/TE\h,Oi;>H0EJ^c-Tړ a EGsLo ~V wS펊a&[ ٵ fmeTCn S*6ikȝh}aa7&RQ^j.v)5}( ÈMin1m1 k뚕 nă7̰-?@A(X[OIM3 Y'oaΒF81 6ɪvI"k!5J vrU%K&JI[OtZtDDx3lUÄ$!Wi Edh6>8JP‚,EDRG lg#9Zmbmeo綈vw4 n%njzWj1='CQDgfuH 'P*Bύjե-zRpGbAN\z[A{p+:Ie>QL:Kv{~tn(vI82+o*H 4JN)I#L>/& b*@GJX.c{ $6cͺt&H5\-q(_dz~ߑxVh\9$Y: =V[lDde^vAzL)H? _6⧄-khpK|Ӕeƙ4rY#2K~ꎅ% j3W Nʏh`nmG٬!i^v!$.trJMU6)==: <liae ?jߝK*b9bPHtB_" c|é)( =AV©kB Sp+m+lе`n90Z{:,`x諬P^` wj%iJgMȾwt$J=P״cm+/u4X ɞ Burc\(0kVj<Q585-I'j58\y$W# wb)GOos k)l499V/`KƢAA rHT J|UA*r=E mLnh[97Hl-nplN*-݄bx"޽K]W9~1xV`SM͠2L-G.9%,ji_ %/lwU>&k3_4 j.z:&&ȤlU Ws詅6Q:0<|s _)XBpBV 蚙zA[KI5:p:gԆV3}O}~_ `77b0X)ÐV0ca:КaCV*AK.wuY(u ƕz *DdMя&H[U6M ۠*XML"Ʀ>A~&eA9'қgϕJsQʇa*BA6|#+ȷ+!Oݿ덽B:@Q8y{c]MȚ{pnx-RB~/*2_B'\N5`xhhM\.RyMK: ϥ7JXHˉPނr+JV\/0L@lפyz蘙G6jx".nU<+ EI׳@}_K~q2X9b^ʧbX-F!ꖥB.yTZ`n]hFpNZ)G X;3w69# ǘ(5>b]srVHPϭaY9O͡F7@H,Sy%Ht4diagXr/lPĉyzrŰ:x\xjDAN8dgQi7E@w2U[!7o9ku bl{ |Y~-\byJ*6u,ܼۣ)'_$ߋTpUTGF՟ N6^ 9,Z yMdlV -7CK)P֞vxB*䇉ؾF}o4|HӦ(Ss(=Kv4Q. L. <ߜd@Ů+3q~F}lҢ[w rM04?2ʢwկ9]z:D=u6y}Cj4b6x zj5 d"BK]f,xoFB>GlfNW$"1-7  9G4cygNjn)%gSˬ͹}x!q, e/v%TVܵW>UY)ዴUmpŮWWE(" y1ҒSH6NANyqDKlx 4@Tj(c42x.yc>o6`n11Wxω6-Jé^EXavj΀{{^^ 4M֎Cؘg?I W:B+M4ZG}`?c'g!ە}'xc+ S(@3[{Rw՜H EIw 6$%ge"3lp5LԪj#׫x:a{CXX\3΀ ¼O[zg1Vs/zYK x;ϵ0L+?Jib %εe+io~SŌ5~U>jGR#DfύSal( fAj4C٪d XѢ|c /B}-qy/2G-3xO% ?_@AYi{|pWE" &ὅIIN?\@e~+)p(+@xWT Ytl=eP ~꺪ǰ|r4 R1+^ܴ7 Ye|{l! ު||Qz{ƥIw 4\lp fq'_i_rU $Ip_1Cf ' mm$9KN`[[nܫY%${R}'$v=l]w%t8 '@7T;_utAd5+1Nha>=^]^xZ)CU^rI gt!O?󌗺&&#Q'85 jafq_ȓNԋoۏn%D~ à%hx^3MgIQΡi;4X<qt;h_ib$GjxwտSÑ ?{/O~j];eLLZ4!?QMW烢Ac?Coq[͜&I褃*U34T8;Jyd^X;yFh#O_ G&ϗe 4k"鲼c 4!t2XX16rNzLۙ@kaK.n0~mpc8UQ<#pƂ50xw8uJfRnpt:~es< -뜸cI*U􎫄4Pf,Tl-3{ϓ|68xD*x5-$9^g{z^_",Ǡ׊G=iO{&npˮwݬ"Fֱiؠ}' `w6]*R1pϽKOJ0Q܇ֳG(iTtP 3z,g10i۟[glىZ-:¡ɯ5HTWY  SCgVE AsτcHc>*pM>K)a3Vg0Gw5 KMN{\0N)!⦚8ٽD 9Zw?ZgG:RrI,RD{ji>ْI۱HB#EPDP琰NgNG,7Xtҏbx xYʴKqx|5j]S/3!:_E tO|F`Mŧݒʀ7e3 m("R 1TrЕ y[ 04^W;􂎤olX\2 ئlZ~iS.'΁xiE(4%=WyH-(åe&{\l}ٛ4/cJ/f`02$M I0HG/# :'m,dǧ ՜a\࠰.K## HCySQU)LDY\E˵}Á!9!e;B,ӷ;o6$?92O3GBt 0HЇVo0w-AY{q3DLUͦN hrHz3m&wsb1ϓu;gؕ(.{,9ZQk=?T>&uSYH^^Y?"~NeJE> b.D ǻ$)D?͔mvY]c LO*f &l YxaF}[m\b1OҠ!bd*N=: .?s;{xz[_)3h;ykV(4`wc&;w[LۏF`p/睱$ԑadjλVY>ܷ!P3qXp&E-z\iAa_W^vTC.kSg->' `LnTo(؁0wfytQtHS۱DKE#9,>G&"䎏ΕK,[>f@$gjx7?og LqVbhlM=1J)y|Is{y!. = -# qeXy7rh|/'-tk<ʉ`wWS;.1ŎoعƳnMZQЉgv|wSY-OG8R INI0<\ Iqv1U/jLF/ܺtJ~PVR1h]3-?qr#i Աs3E/^싻ڻfAr41Bk41ČZbޤWy$=LP;v/'pYKÇ"dGωd6&'.l Gg ` 9y,Bp׉UsSajJ+}ի֘HO7,+m*h-] Çde0f3.'s恄Yzzj<԰/8jnZȃͱLlv. =~w,S}MjuB)#Yn\M5)YzKJĦANN 2 bC'O!cZlY# '^Nx!LLUrREm *2\k<ĝ([؞E_08)%[ȆS_B"qDYXH|oX,}H/ݩl;[dgh)=W4R\qY٥% iܜ ueonˆq]a=/]uіC mmyԜp2XgO2UNrر *_M2,l~m55IV48Ng˃Q ~_`pPEd2 B1tB6)^,zc,ڡpr#uɌzbn uɘN&~&_Q~t -?Ydﰡb9=nF0r!9&@$SQ_F_.G{+9o,G4Fy09LWƾ9pez1n]v; %vبQkcej;h[ޏx'rZ&`>Ln;v6CW=%ÇtDsKLhn ԥK" ISX4n7Pʑ 떪"5sA!+*% &ڶ>ΈqPe=C$_IĤZ^C?(~J`1 Z˜ WgP5`ܝmӁ>&~4͍S.؉$s;]F-4{oϝM>_ͅ|%Ɖ_ov+-bV]I؄CguÓVFRjx ƋtGQ1, dDjQcu< _9 LgFvu03ev"ԀFݸڨXcth*ռ9Ң}wBAɩf=Zo*YZ _p+3MY$?సps`dFk1QN݉V8Mx@/"L  }h y՛l]lT{ߨ֓ +RH=) &OC@gϠ&jITҠ\tLQZf(2ifeBt%bˍs؈э|S=(eP]^nܭrH#` Kqg?RV,U9Gqr._]7aHƝ+*Ee;z_ fv꜋ \^M۝i7$.8u)W ~KmA֮\ AmtF34A - N1N3G2>Lt#OZnǠXF\gƏTz]:Opg~do豯/O.K ; Ǘ{wcFd'<B&Ƒ_ -@ԛDƅ9Vo5Ǿxa5L%]G4Ƕ\SP Ecм}[(VQ@8@/c (1}3w$AaH_/$K8VcW,` .'; ٿTQu<*gABQ1fZ5[lԠ,]c2r}T ϫ>ZȀc1CyQ~'.* oG䗱Q#\S?zC(x袈irpIKD_TZ8ӟI\?"<] /П;׹Y, hP@~wN+y+5wKHeM:?F.lo#Zc"Qg^aؚA1HxfFIl4]ɻv |Oב5ԍ)5pA1Lڋǭ$P{6ѽc ZT8`9J!5%<:Dߎ=O$WhVW[T1Ѵh`I~yRfk'jǦa87EGA7$#i>HhOq*XKjYĆ ^mu_h$)[֧UuPg2;=8V>Kb !dFp-MPBsͬNW/Cn ]bUNyx;:V_P1G":xp>Qޒ'|z*/ |&I*ę 㐃T'ߜH*bb#tG&*驘}+$yE߰:8<"Gu-<>/8ֵBɧъ3W dE}vqY[o0DiW *7>]C-ըGk7=J#2H)#T/3׮ROsNO! EhGviNtKGؿ&O׊e$oZ1."3I[bz*hkBH؛K豂FMa\by:4D0qE!yA~j0I&3I3b`8j̥rqM`Ԑ2l2$jY%B뫅 [wPo%vv*2&@}So tM~KIø6M[mm.FW ^OGX5ԥ0cJRlJPv&p?.˟qɱ&<iJJwqhm ~]Ku`YhfqqX5n/S]-MɡM!N/Viz\:cBbn( gG"a4'(8qSlk"|屝}]%r1Ԯ0[ba]<ǧ<7=ӟI5 dMs0eF71BN]c=i.ro҂;8ŕMo†s><&mlsKGE,;2z'Mr?2&wjut#a; 5 .2JTj$)^,o"#5vrM{4hagIK#o+{S헙iehHVu5^v f9O,:3$J(ZnHm 4g\fo 3c\N.G8svs&k਴Bc&#]dU[A!4A\"?0yrtoQ3RymJZTmpY:쐗Vځ7 bWA;qPK-X)H,/,2" B|gkϷ|v=Z=c7%LpثXB7WVμhyGCieuЊ6KZ.rShw(0Nq3['Yuo3 \/\p -K1O/ol6»B8C 8 9뮄QNǿf|Wvg)HxNeղtY5DǴ?9Os۲tx9-5IoCOo%ߩ?v*0FH*MlRm`K(8!2bb{ChM20OOޱfh'x?cslFnatQ.q-& ORfk0t▓]حKY[_'RίF}'(1h0;7y0nsY WDaD:!y6"&# nDⰢxij\Al)]Gz>)ޡ5*|=,A9jNdqY)-G88as+;}8N+MʵsK<ӥ$ΨH'ک}_soO8Y[mQQ ~GCx\kX ,-208>B6""ϱNX %hճ?~ЍI-LU_Yܽ_jD%s&J.{'0mKPD[T3vDk+c@(Jkj"TkzLhxz;rO$0{+eB#Ru2H⒓Msx7Q5(#uuDiZX -kFX%IY4๺FW%`q+gVPZOv{}~;vr;1D1ALr?NH:Sס"U1t̋Y.$d ȿ{9-6.>9mbZQ| }i|zA_wL}x{c@b.d5P !ovܘt5m*}ha6,|x p+B18lxhFJڠe"͍UNX>:q@_E8vU\.3`@‚/lD U5zy!#cd0͇p+BTQ:4 TlٱŬyx3kY-p7N^&Y?-|%kdfm).J;ܦRE/_cC  iL2k,ԁK#Zs*Y«ssܐ8I:HG< M,Ua9jx{1b}_PЎGFfY.9ӐNA8L#m (4].6)ym¿AP\ Fi#'7Ν;sQ.[|MrmTf Tˀ9PO¼xy@ $hwבB≟ _+BmHxJ4 1\> + ,z:L3JZEo%hvI:=z¬@TېY8mfLUߕ2Ooۗ~g>px/lE_j> _ȮR}$"fx:~;NS{١I^&F~ GKU m8p?&z6WH`^;_0_P`1FRIi/ G%nPݙp LJJyWs"aGfϣ䮶"4S/ շ(B ˀJKr` T Kdc6qF@EO.UgcjF?Ъb {[ߣƺ&tBE oxsZ,TVa@[̒MZB6F{e~#xxOOw¬zhUڰUL=KElC}͖ĜVL߬n^rpZ-Z p:f+~Q8N4;M,bعっ!>sj%2 9P9w^tM=H5Jo+}F[XsZO(z)j FU`R 3q8k7t^^{mSe$ӱ o̰(3p<$H7 vx6>\_,79`.Thu nl̞:nUG!s${sUu<UV8GӚ꺟Z$.-MA'j__a3DA祉e hpLhe` 7}k4D]"WA5m&ˇ-0 x \~w2ԮOU;(,f|tXΕ5Rpܥ _1oHwR.42p̷ ]oBs7ņ!*Z4Ar}w>?1D;P>"}#e ;l")e}xk]Z"9Qo9v-Mj'Lq ЕhRPI px0zjI3?'ՇfA0U?$*S;ٗu桭7Vx>qy5+QƶҳdrP;DUN杋s .} G7N[W6ex@p.pP&޲}F6 BsR"[(~ѓ/ ]Ws:DwNe@Ð {sVt$U"=v Fb[VY_ɔHuf8T/ n(X'h.xTmgXzv@ދFIȎ\1a3rkHobSɱ3&?`Q R=0eQLe**K}*@QOBطVO`.RVnOxF,!Y^btq74ni F%MDU2v0 {=>wrȎE Sr vbgu!6tpa ##ՙwƅcRܳ;`hDLbQ~WmsyeUٌfg_~M5r>ˁw BE]1g18z\Ok{m(P.Z0 Kq.&`BӯxȑK)B1bnƢ(bj4) iJk2nw4ԙU]fWFC6:?K>RI `'"Քv̿k0((]YU]t !Yu'Z:dccXԈp iѿڗ)../t.@F)G:_OcW$WB:x5g)\nh_krgDҪQ&Fwiɷ j{xK\dXdӶ]q;ibFBʑ8[VkM{rFUBT4vOQA z@&A倲Tfp@lExY | ײݬAP@Mռ7oS|'B'HnZ?b*xNyUm=<1}]o ~$V* κdEA<* mNr/V1o/?s}8|Y0+9JO?@rEkSb~ad]8w!5v2 .dil^J|j)^@]?[?T&8jFFLMp͂JX @$[{**HAD4 M=~*K۶6VUeYиR['*0ZC q'(^c̗Z^_ev\jtJ5:u[[!1I.ZڑuG!PVkФ"QPG(-/9OzDHb7sЉ,j)ҾI5(D x6Q}[Qpw(p.?L>[neFxʖ0ԳbA{6C f0hД2bwlX[ aCQ9dX5' "þ/#u`2ڡLuce8To#N~?FԓK5%.CGezo ᐹۿka!C>5m{UׁhUI72LlL2 m֣+E۠1wF 3ܚ_l7t'ܤv蜟#R+GSpj%P4.t#e}&:n6+e"͛+j}><ԡ5-/RUeȚL%Z;cf$^&k7Ij}̂}l/ڧihqB~KX>q6ou t"5E7gj.1 P!XUEFb,)>. %zX{olo ˂)ѭҿdةlEȃ?PJ ~2#0!3yPN([' pG E PMivBjGIzALI#zp9N{z|GT.ԮHzQ&76&:G~V#];sv͐|P:{~6t (aIlGAe"ViQvʪ ~IT^խ1'휨7eϺ|RdfnrPKe,SѽPf&nY>GBۼ+8Hg'BZ.?}Nա~[.Tʊ)[(Sh{ϿSfI.Vcݧ2n*KB ]Q`y6FVg~sT z;zIy 5kD~62Q W)wV^@iR i冒;1&>3gPm>:rZ9N~P5SDy9y 6x/R$a6X5ܬ@v½7 )4Ɇ,,񍲃#+vdP$ބ5xm㠉ifF^n]^+C=c3/}RS[ :!5ğԤE-d4>`a.!)7Ɇhy'̗m0ƞ,Won8 Y ? I8ɇUJH%n(%NcE 8 v G^Kh;"/d{6wGt@j1]rE~oG 1ˎ!`T+, MzNb@lm܎J^`Bշ~Q'"7vZj X+/4]Ldy,նZ.ZeO׋aceSej/k|/3OImH ."ⷥ3#aڠ_''! 5ĭ&m3r+3o\ʙo&l($St%yd⚄ƈ}M~Dh?= T?H#VCSNBP2^<^}beC1%%dc,:,ĕ c*7 $D5.f0SA_F9W")?01}_HZ?>SNEOumֿ֡y0%_⾛8.2s})QC$mPm?^fVO{>F";@f$Ӓ5)ݩp%&jb1CrĐ2}SP>[Ho<`{fg?%7ΦT:r1υ%ͻ#H{ȶԃ4.aGI]2Hpx2kuff؜;9Z)$M/&F5?po#5-5~IcƠ~>q'Z뻶 Ui "/X${5}kWSDgIجK,6ȵz4Y_\~b%Fa(E#eUiA~ 2h\nrO?X,FQ1'R7+z~5:A8d\hZo_)HbmN;o^sm0=U`T.z.. 40pvH OKB;LQ7muG}uRo/>91f^ ?h-ᙻU,=^Cz'+ cm1ppu(( 23gF4`WL?ݔ8&ud)h|I- u$6;tKtJJ' q?)\|ѯH8|Au׬D6ʤ>6GZȵ|۶bo2_Y7Vm5/C% mFwP{CB#seoݓJ3Al7|;k) ntUlD;lz 3=]f2YŐkȊ;N i+ xoHQ p n4`SV /㍭fȺ[0o6sShI~6U@a@"צQm_3qB$ _åt2*w3?32Ef5es% SYobBXFqkcɃjkR_.F/Xe~qbƕUv6@Z]+,"x ^t-uD캀TGfF$m RnM3֪*ߟ_Ѣ9SU6wvr=:i )0? .,K*7]F}*ƾIW/L˞Btxwz'Ik_ >gcdDkq?A/wg@-Ԁ[B-O7&Va RLcWRǑ¼AJ߲ V9kxN@* pi0SB䎺\k;ٕF 3֬aaLL{C fGWyqb+vc4!~y>{%&zkfD|R5y(1 PWb\;i|8"lЕKtrb7 @ Th]#=zRZ̹7QWY+&r1JKSAdq+[wKw63BkPj$pdOʣ~OԷ[|؂fYxH](i">HN[o>⊛i-J:ӐE5wW%h,SrcV:]݃({:'EՋr< +^놞'{Jm ʶߴZrI4ׁ((n)꫎^qռ A00P!hW';zvs (I)%xE*銻G.w1EO%!-k|psX~T0_Ltn@[ h%J(C"-f%QP ցM8Ksr//Uwz^&,[DŜ%.C]wd{D\sX+6y~{\Ե!+}/FY$-t6L{A0]2eXpH_`=hXpĉ,NI 9?M^ \BgtQL^N2+[)w}̛AnKT:V]$/b‰) Ds<0/N 5bk#?D;(8P{aL5OmCc5ijV=xB| qA*7b4m{P+R3, uL!iڤX2!B~`hs~B)-kVPBp0cIx UĻw ' WFU^$kb˯' rI[i $Y<ے).ӆ w/:|P!fwsЩjCD{FK "bܲ =ǻ?-]zw[ :]gӒxc/y*3$D C_L^M e, 5)r5Bݺn=9uun/̄Yk=ʹzH;še\NwdpcFYЧԮى?;D(PC_sfp`u e[A TBPהRP^e~lL3ŴU3̉5Դ d꫋86!O,I̠w?؝Q(<ĄX& [8U/sWgRpF Kz0pAέz\>6&<)u;[WI6Wl;_O`D#p^WPжJY>;ŊĬWv w{ϑf&6t/AQյF %- u(Ÿ[un]XgA_=R 9mP|s;E@3|***龏FDùq1E秖0?hlXv7L5y$vl ˬ'4#JFqwv'<n"YJizqu5XYjP^@\2fO @*|u/un0b9[ԍ*  ۆv-B+BhsTE(+n80zaE\P6L&A21u羉+vQFb dv>Z"5dm}%#4aCx)u ,ඣ/o D9C6#3NέaYU 'Xu)>^AIоM搠z`h(xÞeYqg++j +4-y:(g ~+_Zx+x*+7bUs1/By|+K+FO}rJ.-ʄYO@^J[e_#nSfD$\_7BX5]}0e.&lB)FPD|5E^!ͩ‹'4{8<0&3"{k<|2U (%a=pyOxd/ ;,UD! L]@+Pl K ɹ+3Ui֗FxCl~KeY/T/ޗYųg\'̡:=c?SZj#Kzс~봈 } gZ Jd#"՚S!,\vpCaUEOx(^iŶ{{VlEo [{T^$_|ԱaT&O\ޞpeNZq,}nl8f Ճ]'%#AŠĄobo-lc9gfM׭cdNU޵)# X@4bIw=7K%;P3%`S`lTF O m4t|95(TmlnQXg-:(}VtSdUwɖc[oy*8p6$x]k6*QEN_ƌj[!` ւMhDt?qkP/EcM,^UěƕfumȻPf:w 2Qy5j_f*cs G떓)dggk-<2d̝69l(rXN嗊S>T$9/5YRMbfBm-}@%Eӳ9 u~ s<>0$"KH7윊fA gO=_9.|7tWo~%JhWUhAjy)鰉~;4wVl"#vh}Omp0ӞNKsb{R鸆FG;nBL}s8raQ SϨd[IJzjB9RXC3HSBB4ymg2ml+^"7!\J 9n"%(l:*N6g/ hk?V|' -󢐘 'A'sam"z 0I{{_Al)aYT1#TvkBGSM|NV>*zZ( @LR5e.nUO4bd+*ܯL U6;2&` {*q8l ˱)@B5xLE xmhAnB!X < TlGdxpDT]=ˏ!>ͩnOH; O*(0b1G%jb.:tE8Uj`~H$MJRq4_\|$C xDqW}(f "$|Ȳh PS7(&Lp#B); $\¢5+EI*fw+EН%Dfj`EL59 Q%AVxa&WWJ+0uր8z}(q8W?O!OFbU 5s{q?' : ~L :srmnJRg-.fA3甅6tzsBIb|ߣaP+ r;'!NJe cqFuTyar߿~H_CnG]f:Uܖeճhj4Ŋn\V=ߴ,K\ t/.õbb/:kWWpLٻ*s.3?|?sJ7ѽIםqt(,aflvmDe15 sռ AF@) 3ho(eF!=<}4Uo003\%] `x>0h3 P md\ dm3C; fCl֏1cӪ *rHR?Bs,c'o)Q9!72[hd(LGNE/q[lm~4 Zwa&#_EF˙_~^`,n{N!W8UpQ@e)4llM_ydŻ!i^ S yAW/8j N؋X PMsdJM@+cE\Gp-6CEEBA@y5yF截W38F۴[h1MR^Ja8 >3'Ʊ*?~A碝CNQؾ@22wJv톲:aY˿jm :u~1d25 `XװrAz1V-E[L譝PvոMvD7eԮsIEгz=ei&t1c^8zޔȄ`^(0[Q=#! ט+GO{ϗ;dw;=@%kpX ^ b.߀K-U$Mm XzOV')&Z܇m[fYq)u:ǟpMMug8SR|`_̱\5/:Cyu)G?]*uT_x)Ą!#,IJT0SCka!LsGx"K䔝ހuw 5C^j*TM{TC҆('-<)^S}scSwl Kd;ppu`.)j r.8Ye68<#iP"P&it@&%:.}t(2I@yʡ]PlW ;:B2w}5$[Gc6I0^аwV +5ۊ>$ikJYgdbCWY2&yW26ʵ3,oBprC2a릲 nPv40Ҽ8eI?3=̧2?!r>&tWa8z(/*?nܶ6%tD]x&m:p_JNe/E8Cc(bDS|MṫTm B`lPPڏ)Z­IjRQ]xfO8ކ;1y_ DbF͚{㮱sЁ卾L9p+| /CT):ʺ=qyH [?d/s0dA[|6;*|o- A.:%zdGdc ΢i۵?6jR[*CǴqR;3'}rJCR_DA%-*9VsOd;ShU_L_Ú=O8 Lv?>`zR&Hݷ's bSM?ĝƬ^VrHw`!oz36Y[դu Q*t '#/3[Ics2Xi~=zb-1C3׭|r]L;UM簱h{"JUPE{|+wRM<"*vX!'y5RΫU`&&/Gs..ԵDHrr@#5EZnuy3| 13 A-׆%#nhއuvh"㧣(za)xMjA6/0=.hJX\~[f U /鷒5 b4MQ$ b;Vz!ocs14->PwyJCt%},&H/NRJ Fr`>f3 fG?;ڵ`g*"štk[#j-ҰT{XI?`^1Ɛ)a%MrB^5@ڠNEe?C(6a1{!U5.r{yGKENW8cUmr\HS"YrMXa얣LlMb8RIS *2qIL*!i\l#gn.3ɖr=cc0u4BÎW{Q`O[6wnPïoQcA?0v] YfƖ@O>u^Hjp.չ6H!m$ @ e ҆PKA>@aZ҂*vH,piLJC1?9]F\` x|ܭŞetXs1rl!RģDB|? ա,790WdO)΂k&?#S_+ljĿCgGODu|ەRh\DT^'XaR`Fouwo䎡׶Wwh.WDS2p²Qŏt6Au8ϋ&:. w_ь ͇l?eL+ڵ79uZGӫ+8\{8Lu 1:kQ'>bvP:;യ&tA7EjQnc]I;]{ұL٭ЭpX_5Wlbߌgxz-op|{} R9E&lЯ )3c#_(2ܵ`P'wlAu712 þ>É~`3vLjU[?hٜTި)G ` +,n$ pQ$ʅNjdGW( Gդyp򆽬D 9g#g2dt琛 а,9n.{7Q{In~r af\Mt='hhP*zՄBčDH(bRL"ֶB3i9YZ`G'^(u+[Mo̸\ -v3pJ(rgb{A%\}@o_F]w#V|syгV"JpيYz' B"5꿐Fd|Pa%Q%ν!aT`~BZYV~joq9Xqs,H99>3s; F-n>'E& { WPʷidVk$M;[jF-jyJ!wĠT10$cs1;b i-{t1Iv% ;RI#Ô!rw j.q哵;.{YI|#cIq7%,+S+UMM*C[@Q$Gu[5$, C%fH! ~ifu^P `4~U la0Qkңy\k%THTsv{Fb@hzTUJ^;Ĉtjٷb>?nc1_fKXm̑G&>,&!*2>ͼhtY6Y'v[Xdn+mmgWߎ_D:!8>쫫zt6pVq[>h6 NgڼVH&x@:I;9Ul&8ө+kok'6׊k ;&^PKeC:> C n0;R}Rݍx}^ԧ/Qn *2 n@N79#a=W ,(~d[AnSI[":zL';܁uldÀ鍢GzT@@,|ҋiI2Tc_⇓ t|=לײt/81bJ^A >Ǖ,%F) .8}%]A'a) IYA4}Y !='"2w}blMAxB>Y50+멆a  yJ,:A98[e߲'9;2LD-ܬ›"Ks[@k: 2:BWj Qlzhc#ʇCw= Ӎ0; E㐯":VуjU:6zG+#sUB4T/&̐Lr0pp~9866NM$r=W'7։[ /1&lKcLE6(MTCя.qQw6 >"YKlXh~DI1LØ@/; ;aZPi{CEC'g@ C/{zM6@r]7s$űtviT(т~ԝӈg&!@,r-s '^}?@97M#.Htv]&RcKX$7_jhZg ;DI47?p>" l+.MtW/JMQ|(@s tp$Fcha@-gbP%E:'<|W!=|1 4_7bkR;4ų﷮pS Ri2;oa@1 L"΢^|l%w[HlH"^qW3Vq9:&uMvy,dnv^|Fz:G)#|HZF]5rI^rfeOœANZ 5 2"M5)oM,U8༒|غ^]璎?Z|B !C0YZmT ) )0nBolao[Ү1Mn}V,ڐ| 4[Yq(}(oDBz@O̯'[U$61p ?or qj+L۵o'3hRa)~ E-œ9֟ʬP|xHޑEz@ؾ Ahtd{Ym96[Q|ZU HG;,vZnlK{UµQ^'@Wݥ(m >爗2c{S$̂vo-c\>`E`̡=? ̚IkoR˓"ܹmGhzqH\bw~}6hoXh`e?]ӲWrWUGYHi=fԄEКgNXA?0%=;Rq^i߂V&Ě[J7^ޘ9AwϭyLrm%LX9䶊n:gHCrgESIbИKG,yϠ!( &tM2T}f٘2ҳrĥvp'o2WyBዻV'@ZNaF@2 ,Xv3tpCӘ҆dszf, <:ԁȐL̯tA/Cot!;M#$U=^4CĺB9*$:}$bKZDOL&rs}?(\#?J?˘y.oEi`1&tTX7:dR%vWuKvN$", 5* AG3+D%[^F[ՏDHt6LRy BEȿnA :.6w?xkӥ fݥ[ M;iᨸVo'=_۝h%˖zwRe|]M'UQ tFirR"t?(:^UMd&yENrPkk䚉 {7A+,S>a2 LȦojN }y襆זyXs!/ )4h/9B,#w :`kasd at]qS :L.p5nǥ#ƀ$/T$r|>ڭ̙b9"~86!(B f.9^ql{G߁5_q6JѢƾt~߭Z95/Wjԉ$4RgV l {qE_Gǡm7i?%"<@g6wA=XȮ2r>( Vw6f T$~w7$2 OrU&dȞ:2EAޤ?Cm͗W ݘt@ڹljV߂H,%90ꍼ2oY7uD.+ k9 CgUo:xwCUK2C4(eN{&cؠ~h! v⼺pJ/l?pm@Yo-P\pZ|Ys<^{ zmc19-԰T͑4nUg_&t3+Sx sM%Q8c\˷Vje!O@ ,O׸Ff:&=H})7@b\q]TA-.`%m)= kQcΕb ϽвLO{Q7(V 6Xd󌝧!pj bsU'io&nsÏg;_ћ,g3qM_ua]!̖ϗ X^λ#f8R=c(o85rnD\gunT_H*UY3G¤=y6#XKN'+.~3!"wuFw6guTҍkKxs\bgⒽ R!ɳMJ]٣b\_ r7teĢcN{kzݥC̆,ҞimOBAAmBNL\2ʄU x^/Z2=xg Q{:Z c.\NkzUIͅ^l>4cx`Dn8>֗ b@]1ƽe:Y\̈́PC_uk!K\xhڽf%a^{n=?clsʕuXȌ`욆kQ/aT+(&E%ػ-B)qzWܧx0?.Ȳ%dB< 43"A68-çhEl'_L3-fe Xݕ __ 1Nq^tx,%C%MsNu;t{:NTG,TN٠is#_h+h&SzsJS{wi.@ > )DW'}!܌ɖ[Yꕖ֓H'e*[mgڽY~Xn]Rir5"0f |)o=qMKr/8wj5v|ѩXwN'qu޳M<|8q-7X4a a]@EY&ebC.혊LK4@py=ȥNa#vD wEc-ıgC2Yלy[tT݊lCv=d>_;(';9K 0PY{Z @ cr.Nv%TZ`?aQI*W˟"B47vFzx;XkS׶'hCVz_N }Tmb^xJzƲLޑ0.uf t}U0-&N4cZ6-԰U=\㸸 47>`QF] E~UV>IKl 8PőM02GgFe*,;p'uXU3f9zTp!e\ru8G="?0]$T܁\Svqf[DYG/r WZ's>?6~V7(G?d 4%Ь.AJC< 3C䅭SmBd^~nHe>gp0$cNv{P>=&xXrXTνyUZH&q&RJز 97dLHy[yg Fs]QAhP$E l@_D&և˼(a^;ۆ.J\˳~ Dst_m{"ǘv LWmE >gƁjP?o-W\ w[ȋ(W[7tJ<&{W@";otŀ`3)')0_AB&ȟ:F: ~1Ӣ+sogin_'[MH ZL; JEQ:D!7z$\"qgi1w}:Dٺ:)-@v$u-Es"\B*N,@]6} +gݨn"/_M~#,@QnjC?  g5x}M!k/-"7 ֭;ԗ,ǩ4oԫR6:1&1I#nm6RoTogڞ.+5\cȗ]@qY[\#J4S٦mhr;A읪ߺ9M)=0-(U# MS T.1;5:7C*{;TȪ R KK-W7jW hɣ>zx*Bͤ} `55 :Rj/_!Țh8+hTXȶ>ʠQez:"5:L0f ZU'αܨ^E*9W*|{bxۘJ4B^W_79;\wVF5|A)7M{k,NS؀yeżVM3u_EiZ+( )CG8:Ν;x%";Hu9q1Y/Gjz~(7G>'{v_yG +D~~2 \&*-Oqᤖ{i j69S%9W\ʻ eRh(;MlrdЌno G _ZJ| S1W0t_E—JN)Dof;@.Lv  ڰϸͬ^ülz{%Rc~az)mkZ.3ǹ[sqirɁUT$;ȎOqnIGA%$'qT:;H DI I~gf~~GúW hb Wuבgl-^tvGK? >u@\o滦^a wD(! LGTݳ9uE&V9lSF~N1m[) %|jqrY%3ó 23b7hõbŞ {O4oqBXIq_}%REI5r 7 -r=e1oBPpݽ ^K#_GZ]ЀeL(e1,_er̥G$㐰;J D:{4{wD=")>N:@dV5L!gKJ5`INF,N چL>W;2vIȾjO5;W;Ʃ1.rT8S$_ј9 B/ ׂ)T+.oWӡ&{`50ޕҢ \͵ԊQ+>V9<>Y@9t*D#۵DLǝz4 ?xV*Nȅr" \q<~M.)~fF2H-t3jYrvinJ÷Ui1X 9~Q162mݼX7J*Guc$6v}q㯆T`'FOz|=y%S܎ `㙠pnd ߰Y:DRkg^ی>뎎z쒣 ۍCseB"@R[^Plh>bIVhs80N g$9)=F@Jz+RnDT0 F*hA eh+--`Z{q7GF fbcF KW #|z=n9usf´.kIa%nj%ldz NBw`_%Jj$;\=(Nꣲ9qf*=OFݺW*B svkSԒжSb k<ȪͬG],Bg9ׄ/m})mu}&fݮ$eOCi])٭[#nj)$\>?e%6"G&2?- Ty X72degJ\~B 30\%ߝKhO M*|e ) mԏ nNrI@]z-!Y-]Cdvz>Gį(=18Ki6puzv:3--)u9YUJ,8nkc "j`65 owZŦ:\6[RDo } wZMo/Ϸ-yb"DGTL5 As;d$2h@r~lm Q1 P<),!KYOr Y̅JԂ28~Nd!~˴9WMbA~t9A@bŎY7}.'hM9ATa?0 |K}ЋSWpG!ug%z# 3gƾ9|I _)Ly3aL=5ɃMjk۸8?f;=q~>qrLO 5ppM#wbf!^cPGhv!:{%b| $kryOYX_t'ss_E%v>'׮AXٴX&Rwkt=\M{.|d! $kJyLZ1`$MA!DeQPRtJѿ*ܵ*%*#4 ?/  lSmj,ng?0!pTU=!xrzo9ҚMd]EcrqAb/ Ƞ9CuVÉ}8 ӊ' P]&Oވn_uqPilۃ$9z!+kAWKU:T?635cI,dT6j{d0X΃\-PKAEw53A!`ϻc5YxU,*o(F'gLPI_z00dc`&+W(YQ"1t˙tlX~v Ni{3 d FT$e0]o Bt/OP+22zu?Q6ITGq (_oo #hrE f#$Dϵ:#f܆҈_~]Xb,${a₎a~!(߶ 6ۛjn reY ֊(+E>mW%=mytOg(#} <= '´*T:,^dMS;vF$q;M20b@-Hyюどi{*}tN mu*_.1? WLh3)\'a%  ҵ=z}%D^~ :f5maz Sg *lb]_*s{9h=/s9ohd74.c`3[-)Vׇ{m4ˌ¨{@pJ:BYw*kZhMܓwI:*\98%] x+;\;;PG8Sx"T(n=Lz>E;w&mHD PMx֯0^=X 2pneVN,'gʤ۩9.,zC؈IУhwmkU:/i0}w0# T9U7(;9$$ExcpݾaVguAQbiM41/QG{WDJr>`"7K >gE(8~=CPfAJ8P)ʷ#2kT _'z$?78t pVCׯ3^2PLXJ#Gއatk,'@RyFw(6jlWzJzA3gwoa:R٣J%Ao%DupW\4͗;CLCb[\Mߒ%IM뺾qZ"S#"yn2iY*~2c-{c`O>[<5Gn\;``cfO9@TXoz-C?bn1y'sK![b*)[C%> 1 l,47d\ UmN7v\0ыDO F*섕ylZM3z*^R#vc|?iҹ$@QD"ym] {6Wb,P ?PG%ՉNӶio!\7uŸP|E* w>r*PE$*)tcR 4*FKFӃ[م,4um [u#WǿDAg&ۊ1sZj 5RFVITr)tG_`,Gd :KgFWyrنL eQ9ӗSAKxyUkXfOuF4/}IG-8vDj k;aۢ1jUF̬MSB{GcfA)6V3GPm/PitZ2ͼjYwV)ELM;W6}&O'NYzuҲTV,e6p%Z}6+_7P$LzKW xYh5/ 4l2v䛑JafP{lVVkj&)~r pH~ْYxЧz7poy On8׺6՘zo#%I +bl"+(\^y#>M7VTL Խ 粶 $!iNa Zj kjW:쎣3= TxO$N]b#5I^/VFGbˉ<*lYV< CrL+?:/A0OMuī:eQ ˯$^$>}{J`~WMDF?~0vw^fZ,T&1{#PeP/;mXޒA @u43X<AT,L!$^~SZr<3bodO, N%MP9NnZ iXs+/"bۜbɚ';lӶo~<< D%MT -e~%Rsij:*E_F7e|!rˈ#֐Lc#$O:ٜ}Ԣ84 6ep_$roȺ68:Z'V]+ΙJ- =Mw@L[](LʿZec`ݷ5+EAaun쨚ϸD6Hctm)0V#bw(2^ ]/]4#MHOߏ]r$S,t}}±Of%$QFuqJ0}6HR`RRq։ 8Ս<}o:{/@F ms>&ڡ;$=Vy68Ufr00m4tQ7g$K`:[ߛخΧ]pK$,xȘ ?]:H?XެFަ*F lPIaLCe^k8 VKk#Y##eIq. ޡQ$DME -\.D=l1p{$%q0׽B|Zъ1߫@4|cQu~$)l^)ԥe]FЍf l,07 Eƒr6w_LI^KxDL0|([vu6gd R}j|FMU+VV0 #VznJMI mI ,@y^3˺Oc򫂲pEO 4A 5xJ럱)o}F=K@M@ڟ¢f-H35)GKgѽ1FTr $т]gT- lr˽:]]``D^շWL<9rlzO־)qO:.U:J(hknɹ*9i7paXELNF#R+أ%5.L?6c gx՚:腠9M훎U0 $˟PC_2Q: VmeN;tPE/%VHLQR垱F0 %}`NO~>GzDZiPKSVPB-3ty8o ]ے_uQ= *E\jΞ;,oҌ9>)`#i6,>$R!Ң'V`n- dLԾуP J\fM;6xn6m?2%コQ##m^1ꎚyC],ZhR4Or?\pߍ=l*$Iߦ;nK/dXۢӐ ,H='E lyżuG㑛 {& ZK!3#u'{y ktry?W03QӐظV-SWDm5*qLM5pm08T3sHF nԐܺk1kŠ/>CPkjN&< yFCc[fGG^LU>fDF;nǤ[{pQmZif[zi:-]3F0Xp׭6 ~V)ĽGf^Xm\cSգdJY- K͂L|43$0W^>n;NIq|(={2JCǷ (\#=On_ٍ%Ua>3rP/fS8]\kH&N]0't|BPh͒c zL {P)|;8~wv`֥Gb'T48JNl14~ǃLvA_&̖}$~-ݫ-;8HamjGӬDL,y͋~>E֪ìD"9I#^rގ4M[ dMksSql3N#(!Mt`:h(Ah{&p.vlC M\h/ӡ ֹ1;\&v-i$/3S+.Tɺ v_O.Uԑ$h[IH6j3lAr2hQzz"hV#cOՋA޶{iՓ;NjB%4OW8}]X`s;/}bѱ'v?UGDg0A*2FwKП3,WOJZ GHXBiIݣDN?y? BTO Z[GCX! H3dSPM>'-,#$4(!kv-o$}:P}̐"Я4UUyͬuD6DuWһܽ!^GTtL+ a~vL9H̑z.- *$"/3C L# s^EK!gƞٱr`K6( xW/h ƌ#'vY%D ̤>`S}fm6L|7xȨ\/Ja؍'3j &4MB\%ii*eaX~'+ ' .{(ur/ :MdYO[iLtl(Z2"[(^lk9_:io4$RZ,I˺̞OXh>{l TRɻ \oZr֖l8FbXdDo$4BṱF^ZKERN|^?.vpz>qu2y?$ZtM?4|Hߙ ث꿯lHb?+d$ZzU܆,3$em|c,ЁyZ͂)軯kKè/N#ɻ1K<$_MQvZNtf{Tzq3w!<$ʊB]H:-Y Ż(wyƒT؃ȱ*öP/(rV[˼ a2.YZQɪ(jpm~Dz~vzd/|p_kÚxCyK'p,xRircщl= VJ=n} 2F^1-CzRlrع}t8e@T n ? $3rAqZ\y{u̎V`=-^1VQItA ݧ_L )TxqU?%15nKx{l,Jp'i[V|ooN66 $Ns&-]5gy⫇V!t{nXc'ѷ QB3'/O |ݱa,6`rT*nG)Bq/ልҢqx8oAB(^/vB\;<@(;1ːDBxaj2 d{rY{2_TRzIii>T(IXf|eK=r%~e@F<d(H)6ʃ>A7t=֕춥QЯ8an;QqFs0E#eg3Cd`10ưāS+K_)"L)_G{2jjM-n%c'hrN4=)ivH{:i̶u'+]RMn~G-`<_d3r7ιrt\ON%Uz`d w4<کFWnoA<^&NI[hO\`&F۵5ػyfWDk u ц^}i/a[i2~sW4KsS_! p凨yR\q y} Wdt;I-H^[m`W: Cd}#"freR x+r=&+ݦ:2\3Xga*v 5kF>j˂/P>*M%o*KEZ-VKV[|0(4 NB'3qLWeHl@"}TZ׆h -:{?m#^7z ]䍦(-FVyjx:CӢL_ KA#X& Ƃ]ݲ,}}L/~Oa WOqKst%2b!Ħ{d9A+"5i%OȽxghC݇4 !j7ʘۀ"?*rM=#nk005™Lj. Ɛ-)iJJk b7&없e܁wF  jBŲb/$ ]^! T7-P.uP y *oQ۾ /a_r},7ء G0o78pY{B^ňe WwɪEPvyy0haQ)K \aؤl!^j^՗{ 9[ A(JG҆r_cf3oQL[Б^utiHjzU_##^_?b]}!9C8 aQ&Z(ӁE8ЙfCG9A:6Q,"L /.ib}aI9> 5IH/rxsY4ojn4<3% UOsig;qJۣo̧JB]pqL]l5CVd_PNJNpV𝬗PurHi$%Oi܉>0a"R [׷ĝ&VUULњ ?2~|WEXԄpܩ"i3G$v,x5s9ٕx&ɾLA2K") ]̅QQH;Q_dg4j"Hs;v=rx"ag]"j*`B)iP,YBDm jV9ġa[.q^?,93woGg}۳PҧYӛsK.&RssIVUC}#fL6Hb0-v-MGe-D^6:l,_ޢ:J wͪۢE}ݟZjMY'‰:U}TV֍ ̿8ѣ#=9GoQ%_$ϑ"kI-L*ۘ6ZYPmݔʶ /M8+zelTM:(3zd*X~7t?W+yѝ8V)1h` HAu;Z7.\1Z-$TCxxUkٙhIemSG+R-=%:/j;呴TբK~Һ~dK5g]2Ιͣ@\h^Xc/xp:JidщM(OaWd7'^{u~Q dVl"*yv{ >} 5HZQuEi[4̥aw'RL}-^p:y|U$Mߍ YGjrA:ފ0\x q=Qd?A3b=m 5Plyx'["1ALc@aARiA?K;c[lF0{ c`;*B1G<C̚ %5ֽ) $EZJ`Aopf'0Y2[-? yB#H`@}R!{`yFO5` gBI8恲䳯/u_3Z'?< s\УaB\9r*EXQӮ0S@|EaSxv%BDtn  \X?%&)-(~P9-nzIyjіͬ<ߐ)ITtf"I&5]ϼH!.s ۋ|5@tn?=!v g{2eکDӺiIXցyI m `ݓgH +_Qf}\cPJHU0Ȉ>j,avleZ1gak$ l"Ntv%ei[&Wit1 Rt j7SBcn9MM:Y_v$|MG pcK'[:@3Eo8!Å~"58vEHSMFZ?s+x @sT[Z 0qiyïחP$Ϫ 0cC7^֩?LX l2ĹV R!.^G*b|%Gu`)zu )j+emfHoIa+lR][ӕS$Ϟ܄tn^yG Iny@J9D2h\AXZ&Cz7MRKF&]X5_W@A2f?vFT~J02ߘ/ovEDeKt^QEljt5P;eMO~{镁'͌b)xNC=nw?ac]Kʩp8s],ps6e)[f&,aާLaji#n {E4:~֫ڞ @NTÁHʃrhNJi$d xD$^`v:]+߮mDˉY͎뜜y ]ҐuT)X3pLjQn;l1: JKjsg|@g^"N~%ljHn/[֍$Q1ɭ ?dd#[.2pSo#)U":cbd%Ga=Ó_耄^aw?A[vٞ|f )aOѻ8s13ߊ_|"2;P؛ ߅R}de_4yXV*C=t`nMM=P6*m,@|޵dț*͠Lp<925JT&Aʐɘ-dRѲTd'meb*|- (I6NeLSCUx?\O]\k-uIC&d)e(3.CM e !=ǀ.hBthDzdf2x i@㋨l~ %j}Mnܾ/bh.MSfLB5f vSVKU{蒏ͼvS%3A- ം6:]Hfon pV{j@v!e)/ $Aq`yW̢Z@º %pN)$wC?:0 K+]N{ldjt $W'/4P+<&0M;AAp^-Gɒ>j1ZEljS]tn;4 :.%#h]TϬM3CPwVW`詶-9! 흶H$j$A`A|OLWaWm>aso$%ݣÈ0>J6넳=>|}VduƬ]/ZVdʏ,O bŐQ(A=-:2j> f:/zR4s̅p@l+۲} J?D"y8U r5,e{p vKix%*H pSM!l}忡}tEɝ6]N8~4-S% A*7.VmaHh|=t.?Ve4k*tJIj;{2BZg{ &mqQv/8- & ! RPI#DoDd`ה`nV>eƲ2őVIU9~5KLs}Mwu:ʶuZޕ8ȷcV-Ho 1+p>݆l)9&[sNn瘇&[bxj;L/U 9o)mZ[0zi Tu*J0U;ޛ0<)j- u٪,L\1֌0'.0xLWVi_|[K-c)>)MmLd!B(̭p^>U$YaSڷ6[=;bv'U6kBնz0؈@}m!I)]ǹu XeH29:-5P7%FR(W$ZƯ-H$セTx|rHiihfXwRܱ*._NChٺVeZR\ۼ7%Rt~[O_UIZqZ3g؂&PJinwleED Xq67 Мapq-MU2KSE+{n$ WqV#mOb(ƿ=73/;KSY1?(+><@&*JH}EE}E~d1>(CfKrG$nv!=~AJq{"T0o$3Hc`u iپg%ځ}׹a?ԲJgxH(cD+@9xɯ=0YaܔcMut\nܞ䯊V.e􊂚zOE8>$.89lG$L~۷W" :- T}o9GXD+Dr5(?Uea:},>2ÿtUE lZ s N$R,jf[NI Kt>\{o#,- }wChu,-O9?kf鄎y;kd_>qq֝$UjKvHg;3,Arʱ a*6.P1\Z:sȞ#'ڈIY͒Ӿsd G'oOuPhHɼbLAxb.s!"ҝ>of"=d1;L{{}7hۇ@F,$1ߞ*Qݪ_nMW+dTR%寋o, hQ<M';JTh8=x=TJ=^[+[<;@Bs&7# !ȁANpW=< "'+Zi݋W ze?(C`'<G#ڌ:*\ӥ`D0J]>qvd%/s9 wlj*jYFҲ{ljq-D 2{2NT`wWmҝ%;a=/jqx 6F'zU̝ϒ1ylyM(tدjU51Pm z,arb 6 &䡗sG|8+.&G,O@EN\y*l[v:ejֳe\ =ڧSM  $U|)AL.tV{)\'&J^}PP1k.CʳҢ=5.i=v0'm)aS|>&D ;6Y=R# iTȭU,[ovHJ{U}&?Ǡz;8ǟ9Q=5w Y[si G4f̍L V^' |Ω ^m kbp-6j1+?P'@p`j 3x4ٞ/rI$ &&3/͵ҡICbw!8^؟RW(We{cG[J YI涕V'|\ޥaFq~;58Ut1PMڷV-<څK4dېf>'4spcBٺmpcVf!BǷ./SWE'@L9;5R{Ldk4A6ӧ[7p ~5(H= Tsd^IpUo]~)]\T8<"λѤY\Te_r7%e 3t C*jfN l7[CP#kuОti$%`5s|D ;:%YVx|d \GbП~銥WSckEs$rrH8V9;ox ;ItZ)F= ,Kg X–,H~ _dU" vOdz``/ɼ`%+;BɊSOȹ\ lD6mۏ =ˏVC+= 0u$$o?7ɧ8#pK}%3(&7IW~q f mwb&x O0uUze3Z#6!ނzZ81}b $2c\>w=iGejH-m~85-q=>RȚcxF>ѥ \RS^q!G doix=.w-ZwEв՟ؖR[|F.u,QqΆMBD{֤%;2~] 1?K+[͈)30A G Cqv2T {.C [buX]F["u̇~9ׂ%{Le=<3>9T} dG`NKLJ~n:a :9n'n3J`"ee+cՌ{⽂W'8BZ6JC[x!jK ME;>U0ļg@щ(T3&i9(:ĥ@$-J?; Q1Mgd,r໽{6 !,پ 3s =~oWz1i$Zty'BAr,(l]aGTb(g#bof '*9ڶqvy>Z Z{U}VNsuEvM8<f0EJo@h5B؍S\qAcfk 3:Vʆt\x8 ZR$ p@إ0?:'6sl,;4K€7Zokbx~'|L7pL.kQz.MTM*w< рڭ-A&&6W P (Ee#mX]&Az W,hj@Z4go)%nz-E+z>t]~ y6IQ[Q&*sWhC`l[7?'(tkH9^hNXM9lq?&tyY?WeV# * Jd%p:2ՠY/G51cky^pڤ+GM^niU^*6 +6^AFY~ \g ){u a+_ Opz;9kt|Fsǜ'd[# 9%^jC`F> hOL:'LiUX>Xi]-ŨGf뀰>iK3jn05иx!.hWw\a:` 8e ӆ呯u՞A^C g.MwobΨQw9²tiv' ݠT ts-Qtwڋ(̧f NgӊGe{?YsF(11 7S ;!Ohվ4_l -Uȱ{^RÀ5ǭXp>7i}K{Ak#nad_`.IS([W!Fw{L{+3Gn qvu c[EiFZH?d"X@);\d]`W=5,7 ~0J@)'~N.Ҥ N6H0ҍQ= 9s]%1k̸ 3fF4V2,:|@A'4\ dARuF1~?[,52bҒP{r/m.a<[U*5#HRd*Iv?O4 " $/vD}w&MZpT) k>\6ZxFCn/$PـP~N*aWtxm9}ĿuS/7뭪ZrG+ 9Z+F:dh-X5Qh{kMq6-`JIuciKɲAU0ذwLL w$1!1noHvgWZS.=M *C ;>Q7.BзzJ/)8-źߎ2$5 [x`EǦUvP3\4[gt_gxos萄 VW+M4۽ZYGx/xR זg{)V),~0g8=Ns)d}[BدH2Ys$WP顼op!gA㖜Ip;5t6$9ݾI=V\8E*9uQ ggU1c!:Ϭ{< R 6:sd%S\ 㛖yFf6L)ASMHfU|fHŗ:b컸)h:I]jxʗDIƦ,jpy!Ϥm̍ժǿ2 .ଋ06cTmi4BV)p~n0?"㇦&Vܵz@DC`W6_uS)L31=S\ܗ2!,qC<ߥ1p[>.Jy.nqt$~>PZ܎/;B&\|qlo\Wanu^̭|3iza{z'-i7sz?<>QbՂgDzݩ7W1239?*?f`59(_"Xygs6ХGΟY$ueumQ) KWQgt>9K̹M`Pȗ!6"Zױ+*Vlmʩ/U )蒠Jp[MBYj)VjDN._2& YnuEA Y-HZ0 @7"\uNJ&IQ,7#VXvjeWV SbGUdJ//>21Ŋj\mfy}p/U<eʰl9q"`!sy`G|&\?s 3HtGR-ZRs#e);9>c:Ѭ:릉[ z߽}7S 1ٗtPǀ'+K?Ф_c%27__-!lIK8ԟ)Mb BO4k!#Kd`uL݀>6!^+~Q9&uD檆FJCxfAӜq0bC;<'bbKYF%!E|k׊>+g ]Lu]Pޓb#ar?$) vU[WÜS^n-0E18-x x,\)3? ~xP9 76TKIQT7u6%s7>J `x==S_ Ѹܴwl@YUWeIbs'JΝibc^aRB.>I߱)C.A"6.QF EJ>O-Lz}%_,NwtoQHG~aƍ}/o Wy ͬR,Љc8~ z{"[DP/(Xk 6hVJ}ڭ}\&@xJ>H`i< Y&v%U5ȎjTՊl`@BlMXvʼO\gЄswقJLݶYmNeT30PtP䊦Ȗdģ.L Oĩ߈ru_1b?Qg2Ma*:pdOa0Ye%stVC}vսhjDx^6gcuGNy~Bb"[ _4+;'t:w6/lȫ$\%9M鰾uڙ=_1QYeVx0+MV' ޻s6pɵH噘NmSAVkk3DoH~YeF»ǟ@Y/{]`テEd'>!66}*X2i,"S,WY!1LlQ^wGO'o8L14v*\`B@],Ct.`7:%Y?%^dI`ƾ%0X N] /S-a.fDxi!:{ŕ@O5dh _]W ՠ>y)j*!`9 )%5Ov{E5jܞLzۑ 0=>J;"Ɓ*zwϥ˙s>JB0(% }SW47~ިڰ +ɯC#"5i_4f0^9}ЁB.`% u<5\:Pe/$VHu*e Y ],en>(:)&]MWI3#m{sOAnv3.xd_h޶5m1b!\5;-s%O6eΏS/9F !̨|1s{䈾=S>V*V{qca_ l\ipPYjcz%4,~oOaLJ9rG鹁qjz-= MAX#=LiU*;%G^aMPx|KuTY'c6y5*rRMgWgYERA@t1VS͑ s;]exq!=BÙ$8M&5wTᆴt/+*!g"HK^Ev c `=#[և-/. BSXn^ʌ%Ĩ4_Eh{TNL$qAw%-Lf=Jvq6~TsѕXOڒ]Mw=&o#_A1c-GƸ D0EqMpփ6430WSgV'6w;hLet;sz0=8\80^:`ɽH0Q"oxWJ̏5DB1s1ܛuB-<4I>n:h_1k-E0*J$ "9EQ#|JI;kuB>V!B骕c};4B/9y]UՒ%fKH4k h |xZ0vs聎M-#5+UZKy_*3xfӂPcge 7Q1x_q`p!@jNa|ӻm*Jf<D`d*GNSoMClD-nv]|4g*M5@`Q5'la0Mc)[hO]wE$ëSSZr '0MwȼLtTu)8kմB 'LΈ$,EŊhͱ:jb[*'dh%\ {!b1ުqfM?1m`o->nepT#/fKxv󻯤s`dQ5zMB㴵>VrLjprY(pAɗO7Ԡ:+N]b%?dXFmO7̗ W'M4;%&k݄H)?wh'"K-8[z)zDp'yRj01#i$>z!A#"IPm΢-cU]j%cRC^JCdC"g?&3ɵ(~H/ÜUM, /@K ZݩWpłK3g(HAe끣>( _N(_7i֘W+1 (>CÕy#u5뺜Tȭ?s1 LW((Lы.M:-5бp%I4˽]H!J1#3X+@5к9vd񋡖ӠM}BZܠ ġ$]DgodY x[r}JCW x$H _ֶygVK6ipytvLc+%G CMJ[!ȂFm`-bE S,z˭kaCwJ0Nȷ/V0o_:]yg=j*8(]@ЇtYG_">0eJkK/?-9(#>Z5_k$:fS)ˍ47vZ7HeVÅ"1Ke]Odٟ5IJvb39Ǚ9e&xr ε?'elr84l,HYJ\ZPw< ŽPp'Æ*d&zT Ubtri.,`x D※d3=I}sB<ɦQ[ڜe>Q: >.qa"8/'>Z6V6km鏞6ق?z̴=[<~Bv{;oX˒ѽ^?'p;N!X 3Hd/% .QZ裈_Q~i{'jRXܾ|a;A'J4$rV{Ù% hE| y ) KcSofBp~Y۔ѴLQc* .\>ǂ._%WXNݿ$3CwɜKw{Qd΁ARku 7Ll5>,Hu&P]NtdRY:u`P Q_ɗ,B(s Ֆ0fx\OCR>/߉ Vvf l ^ց/]Rv}agIJ Tgvoc#u״~߅v0{ Goŷ2vY+ ~G6܄y6~H K^jTY%d 2*|* Q"`NV%Mm ,~j4НI?6=n<;{Co{ q6S7_ZDL KhRF 0f̒.;(D gǫwv"7c|Ԫ MR#^,9%ݏyd" ֜֡.4`#2'gwȦi^Պ-ƪVaݮPФD"btoW)5wݏ"6@4Ql;NJnڟ&A[akQ1( U*Vj't+$7>"H7 ]Wڕ8u' hL,Y8_KFHWKH\&؟^Lh!JY狵:OL efZrW}F@(`3k5a~kÊX9xDkz"qQ0p5֫JfBFOLdP0=Y' w`s ;tYN=xȕF]6oH(tt>MSiu<\OlkqO_NeyxJj+}{!ʗ4$ _{'95/=`?*S+'/CTEd}Ԏ=2BBu/5-X3fLQ xVmFᦪ ;[乹k"3BV[9:)sݨj)'!ppqcb?xќ5WK6 ~'>Iu 5ͺX; ԖMuJjϟu6pb;#)PBD04;t:lX^zG;_UuGX>\vXv9ڷBB Hɯc+XA]r5y&LDž]9j^Hh) ZwƟ!Im;Šz(#GM=; "6Yh b$nlY_0^p 7Y1)-0:xȄfQYk'R*qj%9m}t5me9fM2Accq] 63Gp!A-50|dUL9Mޓ1_Za`_1 mDKܖK+ +18:ĵb~ǜ5UU,6Bcc>,|$oñp+U.;o^T!E6[_MY^[EZDuEvIi8Sz(CW H\@ĈX(I;DI &R{uB%0 kK݆䃜}F$zВ= $HB:Iٴ' ^습N 4|O5@i%rMn*Zk`Zb7o΢5 u-ؘK!䩈,A)K~zx"0b27RDA'PLȼ'ח Ejº{઱S*-w*. v9D(EpRFIV-=gTqFܼ\h#ojf-4 o|$Sn:>! tKuL愢pZ%t_]bU r4\ɹ6jp"ҦscsOqw/RbEL1e"w fU/+f0"YC5']( tP[O\j1q~_*A |?Co~)3HbOh'ʛEmqZW}H@))+zbqJwє Lb/s52|4q ,o?0?:# y%,$Snp4xGTyT#!6lJb~\8[aب av&ELGwMՎNUy_{JǠcۛtx9\?injIjO9&YE4 iL+< eBPfDH`IHQ&4$`4M:[юK"Rxv '"v&sZJPe#X ?+0PJ]؀~B)T]c)}`=R]Rm"uB;F1!=I߷&nXuIi_t[ͩK5M(BlB|x韴Z'N`9ce|URioexj"HxiK]m)LK4Ņ=oC3 "Oû~4]Yl6;o)7ʠٷ|{&,<v ”UJa|d%i\;D_|wrJ}Nu|Bљ / 2Kz#anD vKіF eX<{SƆv;Qӊ7dxǎjݢU+<-S$etX PAt^.BN.o΄vJy6E\eo.ƌs,2 ?b焱t^R[e{$}[iK[ev4@_vM.i^feYaZhF|ÃS9ReKh<[nŜ\z&|e9.1` "ʃI?EJb5KqݝJI.vBx'@{5n%~8X]24Xj4{gf|*mjNSn>v-7m4jykW55B=fG[j^h>Y` vhKi.x{5hjmm1J3 -Y slP9cc0rlߺՋf/0hyg"NdR+88ٟ.G̓V+8"nJAߛAQCnCq.nRqt `|\ZmV3W= "ZkA>~X9-Q&ě .|[ } 6V`|b˻dLLm8EJh$If d<D{3%Gh[Z]%a+y*P: $/59!U qϚ>Ɲ ^`7'Х뤌P<S $Ǩ]҆L) 3.\# +z h;{8 C\GWͳ TG,+7hc5)d߀~|%`~"1qnLøTl|%ǫ]ȍ\%Dthu p!m7QԾpJF "wg#{ (0kJtGu0Q/ru4kN{;L$o~l|a~)DbF7 җn:Vs:3a-DPp?2u:?W¬Ec J@kBr![pZ1;[.- Vun8D=/BqyƏ=<dPpj#̆EC?u$"cfY`GDA;AlR7 aܘtA4 M˲Sa,# /)2CǁƇQ *P/ y|Ū6z{G!RAQ9MPfQUV()E>;ΛLE*(J.*kPg%00L XĤ)ˁvr0D!Wcˊ~ qY7?;㓴"pHnjy4-#ywdҵo!{veujYUb#2rrYްR4NGI8Wc0[v42)w&:tHr[5]Sv釖k#``* )f_";TrM;/V,(إNzļֈ윆vmJY8tVZdԦ7ж1 46wIn 9FN7F7p+={垽PZS8TtGynUyou58(-KK 8ūAXI]ղ(6tޡ?H: 0U#VS%"af|O"K'ħs}9pYw ŬgH̽*.%ʵ:Q*;䞼JFM=dp%^Ҵ#a&._%৕ԋϢw&&3VL!p#eJ uRW^϶T:b mco4_E^] 9ͧwQOZbtg+ G\\tvֿ)5{ZDޖ7w(+R*WW]e=8 <܀8> Ty3nFѦ03]qԞ4L`޲7r Pt+fn1:~*ı>E/!ᨋRK̍أ^F 8/,"+~cϫ֛Y#|#<d-cbaOb2W4-!;׊$"?5⳥?3.45መ0cK]=;bd2;]\Cp_Mw͖7Mlk^G𠫭ePsk.91/-wnNhVFlC篈̖3>& 7 t0ŔYV/N;;r)Fc gyZ6azEi~ -STH' _xz3K0$;3Z'=:  8Q}?4). ghًujir/ lxK ȶ6W5Bi5- >k=ءTX^̥|9DXLrC0?0-nPm2v4[e)#Vye-sG_7H+JK')ą}vQ{nzjWW Ɵ ȵ> Q#3]|z~c$UܖrI9 q+#q{ (D/n"x)cY6@w@1(`E}M 2UPjG6&T{Q#Nc!u#t/ Xn=9߭( pA-?/jFl2^EiRn .v#ךuD-+ BqY;nqc1 XNw\0vv|UQwMx 򓮔P+¶bFPwSQv+ayP2T)_~smb!ݮgg',aO(8cVKC!j,qFr1iHdNLbZPM6z;7* *_Acg3<(;P_mt[+k&,W6bv&)8dʕBMIH&b__)-dC"3"x* T2qdE\=Fd ,B?yKL Yk̠)ºa[&'Zs h827?,ݛ0lͫMşPC_LT*zq"J< F0B-Bfׁ @GL,_笷l;dj/'mŮ2pV'ޛ;}Klw=+g+{3DZ6P~ :F'Sb3 eu &ָT6PWu¬ۧD3W377j31\ï4`| X gF%R5-~Y5ٯNɟrX?JI;_:.~tYT+N*Rh&Jh{s$YGqJ)a`~ݽZt:ķdn& `)%|MdKfx>0@ t"7 )F_gPC.:.}Dz|'`dN_VO펏\>H:N/\֦ӓzf Nx_kp>.ɦ!nBqx"\^KO*SZBx3a`eQbc,ʬ@HүIx)!Z[|YA puVlFjE4<7mnWP ;UjkĥrҢ$qV;7o;(8Avk>&sz깬d^ 20rOٟ|H'MB{)Jp7bLkIeV!xx€(*J oJEI pulsk<A\hW,wvyR[=#8X>N/oѕ+pNr-F4:^5k/-@GJy.KqD8 @C> a ײ~y!5AIf] 휠LrDz7pkW\Naw@X"4fΜ< xIyoクc݋]"@4t0m[^[\)G=.XD}5rK_0SU?a)h%⁵Ƕw+/[E\X =)C[A_\BBtOH,Fe2yD<::u"m$~vzOuI4@yY)E0g,Y [>4W-}!\U%Yu(cg"ǡ~gFViOOtI6"䞾09.[ tGfJQ4[|va gJ$@ ~F%杤ܤ.]"/(=[&OǙ) %6Ȇ?l4d$E㏹+ՍWVf9B=aP kSSQQ=2xp UQ'-XFR>lcvS6&Vy@lxaՙ;Or!r/4a\J-*_D@[ >IɁk!>bYZV7j=i;$y,\K4MufCZ|rm#P!~K]=G_0|lhz$<!婍"kӬZdZ<sz#$$<σ-OaKk ct3.xRΈt}j獍,2_W-D[LTt#߁H x(q>6qmumoFCth@jJJF}u%U5i 1=NAVحDZu]4QNJ Uc@t bE$xg#$Æ%ses^Hk jXW}UiF11K :Ns. vzfwˆISX֗m6EsdbKFOzp-Ƭ3_5-krD`kBfxM~|Ֆ@' ~$Hܸ¨=*%[JC7ڣjފ5=u:Y4#}~k|iZ2Dj RtQ :&7)Ow} }a)6pSR>cNr auE~}1NM᫠1EqnH18|uH<;%%N:90ǮwSYڗrVN"IjQ]`3/on dZfFmU}% Ơ{OtW+Cc4V5;@BZ=_vp$ .{usZ$ӍA?_CP+#NSFev\A֍Lx/K/pC摆ߞg5 w8[eԗ?^hS8PKPAhVXph(bdp-N#7kW~۟c,nz:AYLJ,z5qz^7isjrFF*^{h-71 ]?%Oj/ R3;jy65rp=ɵ'eY m]N'pޘ>Rwt_KYJ/8_K?^B;y=%u].҈|ex,;jO~l?eޕ%gBcUV e:}^ʫ!NCUd0"cb&CH>A0Kf)O?C*u川ů/WXjJQ$.l *\ҷ4a ľSu#N$^o9֬?ݵyFL7T!HSm 逡PcG*&-Œ-^t֗VcHTX`f)v1 㠁cb:.X`p!i?uQ(#^bZ@(Jj=GV׽ a_7ǕmF7^ʹ3gLWhESv`b_*?moŅOm1HlԸҘOx5Oo:*FZd.Iш~rĦttۍN('"_#WJ@Gv\fzG:-ڢg)L\V/:opĔ \&dE.H19 s8&F+å(?=4䫒 ~6i0q.J}=U-Qa80Ze >E#M$ 1_Zr h7Z1y5UNL{V:ʌf lkI9P~1 Zx/&D"tNFԋ)8")8Kf[s{$0'/:GZ{yEbQw7u|;}gL]oHT*{ĦH*T]-͞lG0ȵb޻L>/;-Lw1_Pe H5sh 8ºKALI*x:'q\\[v"X*u X5h|uW ^x/`8\NN9:ĂeZ̤KYMgH^LK>WjZI< .HD?N`r뤆_E_!eOPΝP d*Ra 3<h3QQu纤'&µ=T BAm '%Jl 놲?*`Ѐ.vS\yHI +M}s%е9QL6' F;ı$d'wƿF+՝!rΩ~亨(>Gb8oչE[_\q`d{(THo3 )-}]=CO@᲋TIӲ![OL:R%?!OmJV=5wMZWѧvks{DiE-;Y:`N)N,oNʐd&$Ga-Ǡ ݹN4V4 ՒIC[i(9ܑfaF>V'΃[pY(oRFhzI.ude0 e@'' ΀ԣaoZ`q0N%6,0{CtKܹE4- `ECSaF Hz\t*;e$|$dfL҇ [Y9[c]c9q` ̻Ҁ!VZUBe"/N٣w+yhR4y`=_#%js~Hm$+Z|cXHtHCn"nfq??rC^@͑h&T> Dц`O9 hE_gV^yO$TA155pF"=E|m:/\|>)+q8%^x:L;\%b.1%CBɅN<;ށK5q>v}ǥUGTͥ_-Ĝ_UR+]]=A xC?PY=4!b"u\W^U,Ipw Gg7*`nm_%pxJ1p ^'^mo Fm&?c]"7r? ru8Rxsy"OD+R8MAWaq_oOTO*kFRY4w6[ |>JC3*?_*$<{L,@xn[sՑz9?ӁE4[x]Oަ{ 0vOXț& ӫhGSQkt-%Q'=vAԴ'7uzɫb V]M-c9f4ŤCȯK][5gz^:4ڟɒ,򻺇y@4.b^HԦD_#}fPh⌡ތ&v/0宿S I#Z$DQgQjy.ct"#"bzNj0&̙19!L` l TK-C58;Y|:71nY!7ǎAS1RvS{Ja[[טE^r45#& -E5G 2:oY[!j^s[-^g i]˩nM- vÂ˺| I<;(u8 J#U*'z&@%8_m%4?FWüQHY`"'\Q͋:Xf;|×$C] e՞!:V#ʀU6SoǕ"SXؕG2U{`2HUDҳZdfET_OչY4SWVםW#e%Lt߾)E.ڝӑL1[׽ uQ1@?!ِUYOA{eG!{Qdیޕ D@D _~ML1_1&FHذYebZo8*1j2 9} ۷#(\TVƀxʤi7:VցoBnα4kz$]I2g& 㱨x~ذϜNBQ]uZ@tJ>t(z ۤB݌En{d@jӿ-.jR~y*@gKZI_r *x*H:aڎ'J s @dNV{Lbq)E,fL̀~t]JЙcM*JqLf1o@ 6 ԴZS}Tj7r!'7C#zoD3jҖׂEwCb; IU6qq fMѳ+үSAo";_+LzXC^hRr,W3c8 ۰Xm&t!􆀺"#z"mM'_jū =\c[ɾU(s)pͥJa%F&<l6ή@:=#[$Vnq߃v)"L>xϼ,U\^;k^͏Dg``q(HdicV;Ƹz5V$۽?L&2GwC40R&5e"GjwHW"1XҧQ}diWo)Mr/87^K5uLw}L]`?AbҊM@|k"҃U۸ Ej䄂]b__j| K͚B-&ׄV<%Զ#i0!iYS?'wU{x[2N7Q:/ta9-cłZ6BXmMu9(v .êCי_Ի3R w b0.@1#>A<[L%JJSheEwxlK|r,u[a>!fxNzW/H\GCzX"Эߝ"0g:9C5 HZi(wQKڋR# V#oy@I_ڰ>r`zj¡i!/@191l>*?Wo(WI9kTuMUٔL٬QȾ^EjE%y}d-=.c7h^ݢdtMqS0 w-bHx#UIkܱق,?b!unU(rl/8Li<ЦTQw2/VIp(' i'U3IUW SeztgPGѷKI&t.MkwM8ɀw2HSRW - SD#}ћᬿd}OFG}}#bݎNI( (#r}|`t [n@WPGuk ,Di%%HmT10lgp m ʫ+f97\- U9X~{.[5֢+MTF0E&ibB_˝Dz}%m#暣l%DҾ&f~ ?.{!|/+"`:&o-Ƭ$* yYm/&Tj@J|BM{7#alH*1Q@Kļ:2:_O7h E`*Ī\ص[// ى[$N J1;kʗ5emʝd) kp-P_,sMVbr40Y)g ,-FNwz}C"~e}iyz8z&-j'(`lx`'`LۗEh_ѩYD<r0}B5s=NgYmq5P&̠[F8BRl.mΑ_eߤG"WOn1FI#}XHl3]-jP4,kmj0 ({D0U&V 4hrD~E> B@h!U*uo^)mdGs`}ڬt[5'*ٔ(sZL&[㊠GntFK/E62%MIV#_ހtr Ɩ Fl50*GG8Wg,y']I6v98O@q]Ф}vSxb'w"N;9:Nwd lιKq*0m:QtsuDۋ+1+b(fexA@ۛHݠK q;/y$nKeLϐNOf1yKJCk i\'yBgʊ Fb|ٰvdR`.frD=nwic`k8ji`GH""CՂ^Y¬|wejp9(u1z>?яyhlaɊ0S?r˦;vh2G( :a;TN8[Y}mp'.|z;\:߲}N;AC!M^(h Ǣw#SW-evfSKeix85 aϑjX,q'Py4]eѹq-TYĄh!uc.Ww>~rii0f%v鰤;;8B)Ȏɑ,ChLx:~mq)Hjc]$xF\ӇLJl!:348@zB` CK{V]drOF\IIU9 ԇzp͈l.*;paf| ^\ЗysW>M]b IXʬKokB'5V 6O#D\}=@ǸEdyh➁hVI a] ƭcp+YGȹr큖u"(M-ؔtqx_k l\s.dOrFˢ?%g4ّqL|QS.[d{*2=̾~#}ȹ gR L!CϒL$~sܤ!xY*-?VXcSWӅ2~BcNZB.q,4erI{x>czeÓu0Ԡ(O; /I27!}ݡl/yor>T;d1q݅ٽLW<%* ?8i-3=DZSNY!)}r!f3$#߸rLEאL =bWQW7!?$F,Fžq)+Z5II\.\,{:"*Z;#2*ėݲu0t ,P -]k6ɇg[ 9̋z4ruXe:2ШxpB5 pQ {jfu%aD0LHi1 bv_? Ĩ/ PŒ!Ǟp^JXϿgNPf> (~;2Ͷ>713sHcd4JU0T-$S{6t׽ n SpOW0L#^1=B;pu,1C=`l_Řoh&aCrCe e B`nI5dƑl +.RR5CB{wꬁ2F`Wy .Ic&g^/BRJObv#-# ī8Dޛ X0+=bPڧD4~ߥw ؘŮ6XMjm%ȉޝ -EL] B~-Rcj bWH֙5S{IdXG/dMK9o 3A+@ (9ܚײ"S~rP^@fT~fEUϗ$X^vQ(Y3a[@ͩ˽/0B*BWnR fWޏDZ'߳^gE; |r~ r3i;</y_Az:b>)e+e?U3/%A,V-K2J#:-0%s]2>QPkbPR:+0ꨠSwe}ײt7 & *<а;IO}`m^0YMeC.s=X`d:0;~&iE_? R|޷;4KQ"RGZ#ɽiL` P%V`&18A԰k;'Sş_?O)i+t'K@ERx0̗>Qrƥ̨ץ[4 XB}m*  f[Tz:T*tA+8,1hKX7c@_M~ uL4VYz1iwDJdN%~M,m*u bzq ų-80Q)p?iXjkQ(fНеqA} QJi+\b}5aT]}5 =Ky ipFlhV&.$f(H8ĆB)рn[]Ya uƖs ɚw +>&[\߂Y5;Ub=BhZ/ r'^ʋ֨S1٫*A+ ٕFSfd/6@Ge_J@T(3^T'V<#QnkqBRkDe=3 L#G~촦Z'#(g?edVeڨncԪ zV:V> 8vعvk&k^.cu6iGmnFX 8m)&Z[cG\EMr %-,2t/˹q+wk' PHn:yHBVTfd/G.*{#vO94c&=7lG-;CW'ZU"ɦ(wk=V*2!"Ŝh#gJB?#JVqE{E~!pY,Ҩ+ѡg{r_3熝GF|Hhާ山 Td ro[anϹBɆX-ҩ,h闥gu'NZqURdR8;lRv i@_* Kk&к22Ōa8zA5 |lV8[;OϷ:Kq^EV<^Q4QCб5G$%.l(SR0=IzaSZ9?+k(ܻtWH30ObF$r ˏ"ִ\fpQY e,F8\M K~Ht=ch⟵CJ"\0\-dXe ]\)maYUuI)Z=B: #.)iLL5gq:jаT𧴬%MGvfڢPk2:& &햇]WE6l2<&3{90& qWqz67`QmI6Ly)muB)2{-PIL'6|bУk%/-hN?U0`lg9HG ;G;M|LQ2rU{ m eUx7M=VcfnTݡe}:҈َg>>!ا5faW'a_R SD( evZ$MPJzO@;yoG7^ď8,ڼFU?.TZ̕ y9zie~Tv2&XTp)yFy֡vE2ו^G2a[ea` +$37-ajq7`dI2sh) B$ymzc g?6#.Qnʾ~{+L$&Tq0 —x~FRND6) 7SMSE}\D8'zŮX@|w*w n++I6ꮩ10Qq56=Tk(J?x][`vJhA #\gasK󰿷 )Ewz( f!JjhE#o2ʙo{MzGeLԣ2WZNGZ΍9.Ici Q%2+cuwYdץO<~Ut+\'bV\_95<#5Ƶ_~+VF `ʔ`'V "c?_Lx$.X(^. -ޢc!g[ o',<i cK׿㄂ǹϓ##;mvXҋbN8::'vׂfu2w8v7Ȳ$u(&HzSX[ۍ8o =+m`3Їʣc ޮF5idJÛG}ٚ8zWwU6 ʕbQ_{<61lE5^Mc1I˹l OjusMEϏS2`R '4QBǙHQAVf zB02N2a.ytEܹ^cO u@"ͤ+ T&B83Uߖ}C3H2$f?Bn=b,ǵKD*qpUO.YఔҿûA^] &($KI:?bI0h!qI::*rXV:4m[ۥK,GI&u܀g9]dU`(%]1[{DHծ}mqJ&bRo`\]D/T /7q!"ߔzu*eQ85$#2ܜZfǹ?M{|$W&uQ*lN5ȷnd%372܌~}Nݟ6SHLXTVg#"G{,橻o&: -%FgBywUjrػ ˼5:z=:e*;68_V*jj@X6k/FTZskȕ w<[sc5SW.ڻg* m @ ƤhY>S`Xn0;L)[ R;f6BQ2AMF3P=CEr. +/BA$q&H?.$^xx94VzfW\dmLtbDe4ƸK#b=7?po3ucO |>J?Ԃr R| %(Z,ft*z;=dWCh KXb"f:>PW'vdVe_ 9،I%~#:lri.i\|u~7Hbٻ{-d!ә%J;,Jq14dO (2.̘IA&0Xx4%_{w*y982]k;* j*}@,$L6YP2 8xc'z[ 6@k+<7` MazP3Z40q5E+gT j tw(ƭu5F𶃶<.oAӉX0 mNI@$1Föws#8!%=P~Ζudݗ DK>1~Q=C2zUHn{;Đ" cu89)$EOw`,f>lmT`e*h9&9 U53I'1eg,hs`k2Q5P'X/|euF>o7%jҢm/e )ɜ#;!(j|+a`2`F.>^iA0Tsͺy=N]qH I[>N=drvwoCK/ ;~MGOR"=#|O9_z{.uqy7b)nY| O?D``EBd\_U/*c[$v&gh+ {=@l'J#4dh 璻ov3birn`:DdU9#nP3%2l)G+PTbS,ۅ۵ Yd;xXY5H_vۋmٮTgRg%ɩVZi?@ݗCM}7mMRΫxu«$E}q1nSEMtlg8Ȏ ֩M^{="m?n~Uye巇F!)Q J?<θ=ǀ'7TT bZ0cM 9ֶjTr JcFOӱÖF\ӵ]7˲o~4=QNq5zfo5z#W3*2,XY)BI*i~z ]^ӳj뱖+-5<6, h*lU1nw-:Q[cpPk)oLN#"\v\g eƈB.LeDZ귎Yw@RfzgLRV, 0pY20\.Mv;exrHA(V-cEE(74 dp%42ng (V5qqzN8)Bʴօa9QW i+]^il 7Ej-$bS] G7J#Yh/J_"!Wb69NbfͣRYEeWY .c(,ANʄq#A urΧH^eyLͣH}!69K+IA- uu_H\AO aBh.ē:d(n<ɢθSɥ̽mDz?:h^Ab3"JlMP}74"ҥJh`kY(L©U<ѦzԲ_x&i#ߛϲ!GC1[6fv1@Jb'c-qm8aʴ1TTě~3Yh&gbSpoiztX,gTãsFg,d4$KgA0%~_R&BjH mT`=FK,oEH/[M+ϘRDJX̦j#y4I?m-Ӊ8-MlM(QNp+r,0I=w3blݪp$ +T n_Ύ)6⠈3.\5dp/1$y;*N\ 7Wm]%y_KU8l rO-KIGR)O yl;-b<|T^|rWMt1H;wuljT`j Р:3^ZҘ"%k 䗷 _KA'q-VSWDYF-oX7cO {Nxa-.y[xy(V,%`SyVv t(H|@yz1Teà{Ip%e~{ pg̏}&Xв \ 7c6Bϻ 6J^3/~]\tXr:-5taz]b\b[+0֚ܥDL#Ԅ%(VsT> |-؇hb3%92ಫ 1=:1nPpD,ycxNZ}XD;p0{¦KR^vaL`;h//fryy 0?R0)\9]qhFK讴qPF{4_ ^a;{M8C5^ PR?rɄc[[dnQaؿ "՜Ībtf#M#LcSuw 8N _ٌyIؕgux~<0bs$!)Ms957P G^,X׈{ #]CXUjǽaיr>?;"-lÁ ݧe%!2fBW1c+|U/|l%9NU!w w ɻ]k?VxuN|H@?,d%NhJw@2#ց39N*5?w/*SIp =-RɊk=D1:䔐&: nU(1JU:^8bN* `8X0ח^%{8zQ+okJ|T0]jp{6A_Mv U_BS@A4j8>n)N&vG_YN^tc +Nh.ZIL!'PY6JRx٢g*H/{SɓZ/{`PSP[}>|[9װ,LkE6r8QXAݸU=jK^" 2>ًO7iy>DҎoA}*ng[聃'; R:ѦֳҪa̽9ᨽi;aaNPE<3pNr7xi|k?wi %и7oH>e7vި :!ogdh zQ}2\Y~WꄧN6A9+Qs(8x9Лu{I 5Oi~NLP, <=Maץ"G5Mmth@.z-wl0{I(7k.Bum@E{nzȻX qHm1݇l0A6V#lOF_ z9W4-%ɩbk?綈lġS-g^eu?j $0h!ZGCvh3z8}T̋rqn76GS!*۵Zo'˩u]Ӷ?WNrMUs C%%Q }Lݟ} ~J5dA'r1ھw'=kJUu[HB Pmn(q~"De|dz $_n%fѣ3M%9H@~:SVKO'q⯚iI%(G}a2lealLIt<-3:!?3's a"׭K1-A3j&`lw 'EP/Jƽ .ho>q.uo${- `! ˠTV׉2sh8+?ӉhL dcD@սwQ" a,8LYAʦWK27]s7 `q?wj4k c %t>4@z)OLWeYs@6E)!.мwy tY"ϕ'Mm8: ^W_bohj8Dg**F#(\]{ nӛ YIg q>њSFË\+僢|Cbu7B)FkVf@CS ƴyL '3\|ץvͪAX֚?ꦏP4vlRxi LVlT_stdp!oY {]U433^j泭[*)#||R I5P7aeY# }kD_H^2èg/w`ZؿMЀ9$HSIt⎪t!HBJNN:P^JQ @KMDRh?[=Ȏ[;QĔaޡB2^HC_^Zѱz_^da,%A9ĢiEb=w ,M%ˉNj@.$n)bS(**jΰmת(or/ZLs_mOZ]uu5׆V|t$f:hT%H4zseɐ @Lblݢ\' -IJ>:%@e$":aG8ǕR'O:FA Iv?:7Va.1 iSzS;4p"e]^>¨g]# 3zn]VY0D)j Ƚ^>!ulĬTj >\nO5.UUD`W:uY}P[G[8H!vsQ|>4W.@9}05Pݮ.%4A5+|Yʇ8L7F@_81Q\ q i^Ꝣ&k l]W'T',ԫ֢OfE^DL.X{weA-rBW.::L?5` ~ml_XFa`*޼jp回|ճ)tO$; ukOP DW?ul,}M r/`9G&U6ML9z[7h+yS- hNհM8E vaU+ NKMwƟek~eug!87C Bim+lM\``M^➧+<}p $bPL#/2ӵMU]ĊP߀vgB l4̕"  {Š9Mw?o;mm:.1ӶUBx:̩RȽܘ[-$Pn,kV/rRI5QHدՄJJS/GY >ފ#h% 2YowKaȹ]yDeUoe3'7>JqnV-vAs^Wkh2~J۱|KB( Ko[ٱ{f4*%-y'BrVc h3G_Y_ҴQ>ǘ //}` mO]ku6yrp'~t 5UI*A(e}H4 hW<{W.&ouAd]#a Е'RT&V?04fYg#ݑ-7DoeGP: FfcnuKΐ'Rec-ae+"W2t R'M"9P7I#b(1ڠA>Sz [N\Dz$XDET}溈 r5- 51h .y.0B̗;qJOj/k 2ftۋf%4SG, $X)QjWRtn. 3Ch걌23UHj5Cpn^vE=J>q#5QrJ|~ԇ_ҦWB$v( @*S`oOR _֪ kؼ_c5`[\-t.Yn"*oXol7z`+ 6g?x=2K)࠘5c 0, 2@xR,U xmb)q( FRJ\Ǭ']Ez^ 9|/> g1mf>Y>U*fu ?腱==n桙bx ;ysDg@g,C 88 +]'͚BG8W>ެ\8;@&%l K+~@Wq_CT ;J  &iPZgWbҏyu}/k|eC n٘YvE7FMdCfꄨ# -fAfs?`.c:>?Lŗˠ肾v%ɫ ko*TGyR>VBRxkQ捗S%CBnWIx20'2{0:햼1w%U[C ;e>.(&MO>crb}.jtxZŁXDO T2V&< mPb9E8daJ!ۼ^+8:Ӄmű*XU^.8`~8"~;V| |>{O݂InueՈ G1w,qktTk$+ aZ5 sg0=^ ՜b91J3&z))}lt!04&g'{=H.*LHs1.U\xYn{h .:1 Ѿ=2jCFn!I1س2G4ټu>P{n #}$Kia OfꨇJ5\2o~/1.W_NXd&TD`L,0Fx.Mz(I= iؾ%K2ܟ^I kZR}<@{QjbM/=S.v?mC9IUa (ގ>-vfD6ŭ/1?,R+fleIfa8^Dy!jԖ6}AsUL=牼{ DH)RՊ>]AvŢlp9NQ¾ؿ?mU[:VOPvzs|Vu 2t.UZ5YFOg&MpշSݓ!*HY'* {$_,G{H&^rd57P&GHRnJ y )4[;GW޾yb?q" ݬX>\bSUԔc c  nFԻҞ:T1 GXMa{3׵!0G1BWյCSZwEqzj:O9OqD\0)ΦytY?Z궺 2BX}#ӜosY&um/gk,T<8S[˫^jeaYdr$\kWobjBc`6T*Qr,܇G)ŴN<蝩Kx~YE35grzڤO%^};TD/ )-jYo~ίH lVVVX,҉eRl_̘4Gd|a4-hr-~. o߭!@I\H.d&A4χdWjt5\v}մeRa!xLc_2!L[>l]͕ *t/|[hq:ϟ'{M4 җ4SZ,ιHT˛*Ug^\uʀK¸k4so).j1i|떇`j}':"r_[s$6L9@=mz>7V0f2S ۈjYuɡ~n^$@%entf'!{P./I*>(Z)Ug HC,w&Qs ]5)8 Y7kHhSnPtژeL2&: (S~ZΩ%p*|Lc8+~IK?+Ɏtv 䛢9tT n:|qԺ ʙ~PCdR jP^Z@O,fB-wR-ü}ԬGS ﯘ~1:i JlcLo'c&[p4QL,*1w=;DP^4H?< Lsar#ĶN17 MlY'Eax #R!W'j cnghw}MxىRJ}}Bqhi h]B[7rmtA)SUX[Y bu#M%ނP5ѩf{XMz^| 1Y L<'i=y3*OuF&K2A5V8{d^$vP/ɂiޡU/` |"qrTH PZw @iG#S9b[(ǰrPCO3D\{#0dW7'$W77튀dXm+^4E{O5t>sV k$lkk0i/`{A3ԤrU+@݊mn*"Wrɼ]qBء6}Ն.Q>j#aX̓T7N)(kJW9|Cd2342tG$R{yX6wQQMJObԭR^ x#ԟb~٘P3T 4Co_r~?8Yr/Ӱ}`ⵆe&rH4KA`/lPJ"XD@kY d\cp6aqn*xsz\@orUH[Pd=i<43+JX۩+s3eM0Lw촒<9J0TPqJ{Ć*pG8&zc>{,.%%oR~K=> lN)btaf/SE+:GgЌ]&4!` umO X|10gm(V^(_r6i(fHbo!I@ٔw !BM/n0M5MAԕ*f.t,e:3DirWݕ!G+p"(T!~-#ڣ K֛6e{*FI4({B5tQVpcqϽ[ov|`ŠYT$Kעű+7lǞ7گ7&R b>s`@+m ep' dfBҥLߊs_Ѻ׃8;X+ok3?ꀛ9Iӕvh%j[PA4nd XS_'FDbK[1_6 {F٘ػISSZw;s(%yRT٬ë [X\_o;ޡ|B~iZqiC Ԑ ?l;j`z*)i>^ϒJ]ךJ7V1TlZ oR~FOkQV~ 2U{J.W%o'5WK#f9c%bCu!E2K0:hE@G;B_'x!(#G8 t==F`3`ރ Z%}{ʬo6cDbff#Qy~o2Z.K6уȲm"hK-*rj?q}<=DgR/nsd&65.IaS]rxkmJHh)dӱX1dV@培/LEa=Z _dn Sʘ5<'8WX/j! 93c@Z8 zoeF{D$<)tRcSkfVKYkqS~)<ݴ %KjFΙR6_p4"q NY wȥfDss;Xnbs }%⢚C}GU:^ L3Gq*1dyg t_ ~r}ur *HcqTPaMn  C|y;3Wx c~$g!"eQF~;  QpكSCpulORte:v)`1DFոGT;ڄߟ+˪&[_r2y2ɳ;b]aѤq k֢9 lY7y/&LRej'% wGBty^Ov$/;z+5ң&=hB-G1vO&˶W -ܹ.+`+9$cbrӯ˜LJu0CMmyt ~ҫu芯&UkZ'V m(1@QtzGh 8 ߗ-s'q4t8\ F?$n,E\[@fB5{$K<`/ ]ЬgTh?v&-HF,>JQtb?=NIqg,CĒظA,6!Vc- yU*굫x<2zA pߐ?A{tϻ~5

yQa[HX!%;G K.%vlJ>& ygŮZW桐UHh#̃>LXZ( ~H nt+jPmf~\J(`?eThxW ֹ˳|ن2KySMHh ɽT1&"PIJ C=4 Cϛo;n.P*G:zNKv(#g\:RUq)h(&5SCq&^9q]2bN0JJ+!? -$ sa@?.®hv)/^c_%Tcw0Q(ǹ?x^$ ;ϻy%7tTP&ȦATͲuVmMQ\lv68OEݐZ2]স!Í/kʳ@L9<jE/GV5+_*H fL~PnɒKUר{i-LmડmRwP \|L~6o܏4UVtn̜`kx?Ec"Щߋo &XN5>Hs--Qގ]{11ۢ[Fenvy>x `? j0kbcM4TquhuVHq-֯*B4_qP# WvHʸk{ RӇ:}JѴyIRhch Ґ\lΑ}oϋR?鱏韈^0zMO\&D" t&13dQQA{k"V燻-NkF`[Ddt3{Hm: *Ki>ja30^Q ^bU2~ ti:C1M*:DfP\sABVS{gg8u2%@5p0 ;}f!ӷ%_ xʗ0C[L@.je) >n/Z\JҬt(zќ`X\aAyl?xӣ,D=(GTZ@nὁh.P\p$HF\1&ގū *oqy?5tv|=qMLb*XEp /)v݆ ^8wRm5D';8O]KEDBG19(麯 !`_z)H"x?lnT0UmpC57)pe&prGC8P賣v= PG@V| q5*VJW6EaW 3lb&H }iϹцq+3`[$~{fiWyY s[¨lO4nf)7]t"<"f/K)eJ3`p 5E#YeZ#ڽ8˯`U!mWN@_dYDtY#35Ч'g~0Qv@JZ1ɒL<Q⋈MbvZx[ jMnM&(s& ^QVq| B.RHuE@rW8vpXM'OB6{ pN20$x/Ft*-^7W%3vػԃZ# F)A,8,=u蘓ȅdc NvsB--YSMQbn]l2KJ})87:",v}&;yB?g,8k¡ћ]'{v!!h jv2vI1|_`7v?jCJCW CAo4^h(UfW`l#g%C,[EosTqPX0k+D{nH8)ZL39'J CuёIBڕVWh(N}漣[Slmߗ9q_L3\D87Hc>GcvǎWC2̄Z<=@^%"ȕ\VMT4DURh,Tr燶?E]tHBJKuK쎀4׭w)P~"*_#/MPf ݮhh^k챠lDCڇRCKqBa<p\Pdpw6Fɣ בe%,QҜin@Cijc%d0qݜN^bz]7lal` N3j$NZ խa2iL\5pS|7t9>'jD#M4URYCU v".KC2_1]kW=ϓa<ʜo^W% xGS9wR?颚zWuNrӔ_KmHixK.E}zehoTݼgK^\Ϸ:R0C?L+̕~!CҽIyh ?1\˃wU&3KئQWIi㖀 Ⱦm-O E;53])Qlc#@-u Tk_(|zpm32zj.=uXH>XWz̪_pB49I"- m&SDbWhdANHf ݘa#ℿbN\>Z!q ;.kOq %`,8b5&X1&z]L'p ^9MGljydWm۳d=Xar tQX֤wKa}Eۡ&t"2u D87"0 ?kn2K= 7=f2/3 |kPFo=n4T1 瀨eۅn(1t߂s S@zΙBZadSZlR (mvg-7n>[I>Yf⛸΃)T6Ż/"^ zmPڭ7Z MxBJZ#4oQtuu/ɓv%VP7+zA@1 WƱJ`31%7ss (@U3cL>"PQIG\x{5pO,pN%N $0:Kµ&%Ȫػ Y+w`O.L ~ el||_R-|94XAہ4-E53+ޱ߼+辔]x<]p/@<]-VcGVIp IZ|vYfF+CvGD%!RdX) *,2cR#ϩx[~.\</} 9!2XW`fMV־stLWwѠZM;㉄4My>9JMڭ_Q=z~[jGeTO{:O_paaK8P7~-O{"-UHEC7Crd&scFqfq_K_c' k^ ' ]]")PD ֒h?wSK Ik@El 'SEXBښ& Pjzeۜu.]:"lėV?) 8x '2Oy[h+rQ gy.x_Yꪓ&= s\F4 ]OE˃1AHK*— t_EYd+pNz,e1"A9{~Ɍ^l:K `E޼!sXtfqI<+"կt:}LG}}cog23Ly9}_G'4s{!+W>VMj\mg3^e}6ӥBpۤV#`+UnVB]h'Ilq2#{J'Ť(tm Js_۸e {q@Yt0U*{G۟g9 ^3*fUs0qXZK`M0Z1n X@eݞ#P극REN:/[N?qEVE  ?OD&$ 0vaiXnjM{D%3űyV?IKGJ%oyZ*W {5Mn ,9 ]*KU".-iKY>PGD5w~K#>!4w@ ΰ$@0y$D-O{ ưL iW9d  T8>|̨{y!P9A1YDi|ֽgDu݋27ucB&U"LH2:%zo Jt"+řFwjT6D/n VlML@c%\H?B.N7Yk!q5^`mV^SSu-($^( >&K5p$Xׇ,LJ3?Zr㊰6< `6r+2( ֢Ӽ OEd@qX__2Z.`+:B OSxkFzYw GO9GgɨgNn unQ!/Xl1"|KRB "Bb꼡u-,b^gkmm鑒d=2 B 8y)@< eҶ:8R7r&V~q2f '2ަ픘^]jmNPM ? # 7JjڽI%!{0UE{q7Qj Q^q:p"='ɺS9jF84@EnhE-I}/pXEU9"ۋY5p'#lv ՝~gNZxU`ZZS**( *TT $yړ>zQ5C{t6#ezg*9E=݅rl|E+;nPeFs}bhIP ʗ^t2^^勵#½XКG*z[& KtoQ{#^G }gFo.p9azVt]J"#Jo0sK¥X?<۴, iF2t=c8r.Q67[}dOꟐy = ;n"Iw;f |,C496đ04wTjRsa[b2acynh ʃƊ\$^|=|I.X[ѝ9q|^a 9oעy&LdE/ |!WUF=m3ev/@\zq#B^t${2Fs{g K3VA vޥ^gl/uu=DQ[̷,3r/ b gk"9TS`Uxatϵ"JGOTDeIitö@ATsAu%^QTV SP'm0D'(ŸR!"Ŷ-f[ ˆQ@25j`7ZAQ_j+(;J1wT6`Y1mj7*tllSL>T+,fט^,%2SSAsi_f^WL (㵃Kը {bkEd6*jьp@·EBvR7ڲ?'["j5 cpM~0a .٣Zz#qtZ]$tkLczdsũpz OYjqs٘?,UF6@ڤ+' q4#BzsB;d:<Ș#CsW#!?N nj+>Xn~ĴP*ei~ `$I ]Fibr+/ۏ4G.u.ȱJ@I1\wEX3ip୏  Ug~gd4\뮙??Ěz:7"%MYB`V {S =Ʀ~ g~!Iw6pBLgLQbP;[sB CL \qpX jtXrGElq pi0YrH~Nl#p<Ϫ?۳򘜁A![E* R-J**ipl5Rݩ!jY(R!Uu\b8=X0O P`;YfEήb>Q|_Bgl(0 :9@>`"ed:*6B੟GU9Ē끓 _&Gf::j[*:sD[~:{>PIㆌw < èH>v\YBqM`9A4ڦx Qۮ[^R(ΒgjT_ [[ 8g |WnTi]v6 %kMZx 1n\qj+v&Q}I/]>̘athX:7Nٟ>Py>JafE]1kl4oBG)@Wӏwb۰vHR^ mRE>l "k$Ņ?ԗ,>X를=NΦocn۞HpO ox7Ȥv}0Ldð.te6Z&s9z?܄hUNTJ1l'%@7O30D&Ņi1dhdbF1)εF<^ek_KGL8a!(t.|MK7[! jfO§k_fY $/hFq]kcAED 5;|*[IYLROFYa='w~8o_188'.PPr;0ng6bB& U#x *=eA')>6%V-89՞jՌ cg hx>x /a1A$"P 2noT3^wF K:Zq{˛so q{N^~G+OްClX喵ˋÆ@NXxl2:VJ=#o64ƙw;)VWAk=k,J˲}Jp&32^/bDz4 %.ѫed*}zv(@o*v~oؓ*A>v D{VFRg2}pC.B!Ң4BEeu P'2/ŻF~UxH#Qz(iH{џ՘%)HRf'J[ki[ 'P;Ӈ W>9Y!UL_[ek%ƃk O[S'e%,[EfqOX)\6\VZKԥ Hq-L IU|}d,Wk7ΗȌ )w Vі9HF6o5#SFW=N77xl/z .1$Y WDaG3Q;^(>~mr!D+ mnX+()|0Ǡ A,D?ǽmW] *#^sTx*;Bؔz̯@\~M{<0GP U^'ڐչ]Y`l4"Wlf3Z:Zt.YWmZn~duEƿ2̕F(ca2?lV8Qʺ:j7kӉgZ&)K/ ZX ia5,x$<| ;1ǾWƔ'n m(L~>A"X@͝;[6Eo+GRkE(kJTb16>9 qNY`"_Ecj< ,de @UH8 d15rj~-;\'xEe۟T"Ұt$z8W\kre4f&puyY>(Kh "jɸ<1G`!k.YN8 X];&0*~"ܶ؄h JVpјf)n'%CRJ4\DAIRJz+ɹ)gE<(Z{w-ΏF^_$Qi_SBm;#F)4pmoimneNǡ1_mwcxLWyDur70C X_N@A]ۉ Hb܇``.xKکF(d#GcM*MXU.gFMIHM%T4cRlᚫޖ=gcfˎjmgХĤzF]Tt@qBD+fuSh|!_ΒPYzd>qkv15D{Ғނ6!CeہN^n r+~|B *bٟ{W3m)蜐ndz9WmsF.^Sm6q }vnTEN;ލTI6(j>:J#%Dw>֬ʥW!嫖 'Gdݝt+B<7nG( EXTY<0p ~:;Xߖc]ye^֪% ވn)6l'PJ'Tna;?2 ό֥xS-[GYUSi˭&"bA}JMmm@"'O/jڇGSWÀ R H͹vix6Qukp2Qr1GqՑîפ4>*&` 58XA幷!Gۉ?  뭆#)m""eL9Z(s x2%YMLE!!M~/` [;PUg€> @wA*rX&UjOٷJ RKɌ%W[5VrKc|Q੉xKjez- i!3A!!|l)xUQ^G*ljv48-jpxQX?rMGtděrtt*/3NL\o }gAbS&)Q( =H&oYj7kȓ(}%-%Xt= X\%q% ;SW;([HH@@:SB1QX^ \]7 Gp*m^aydG\_k~X_'=!|A4O0"sRQ~~.q K6&mLl'/&x ToM7\L)J/]C?m| {RLpD>yD+Cs*;ɮj¤cER"daM%%UpRJ]o5#r&+iȰzd\] Gs(n-iT&rumb@s.yB$|Bd`k-X S?%\>heѷ'Gw`Jİ}AW)S:8%_zzSZ# 4ի(RJu^]Fyj?r{2ğF "7H.6up2<|Ʀ%%(o%sIWJ;*bE{.*c/N2 9մV W)ķyqzSԣLNj AU8:(F #0[Kxv^C-0ؔ p-ԇɍ`'y8}wԜe.n]/+}7vTh)ohJ\u q,$53ȘqmZ:t2`^4娮/NJJkblK^BsU>-z0i5gQx`LY@* "3l ;P!#Slr; ],^V4>>\j?np I}Wj\*(Q}&t4D⤟yany_[H!9wm U۵EMl̘G.7[x^ n8|Fɺfr^Ѐ>Iz8jw\꫕7c2 e%W@!ʼnHfa#DibGhrVSGYpA&  M`L &O5/?Wm=G*;>:%v˙`[x?`bc%Y|QڴREa4vh^]P{jZY &~;Z7H)l-я _@Tc`E)$ʙġˇs"'#^Z%V "2.ֻo#Y X/űɩ_w2'E yщTby{lڮTl2e#nc-A-"]DYh>6iG@Տɽ۹  /:7}k)bjyʣ؂нizEGuT2H%?*T!Z19(5+!{f;; ?h1l"BŜLW]b}5]ɖO0y BgYIpY$}VM,|l|.iA\<.BLkBG%pF{"۱M-`};Ֆ?;ynqAdD5$pBo3D0]1\hشtFfj>f6c; { S(D3}}\<mnXW yxk[+0>X 7O&㫃 OCʟm|kYHF7-aǥJZO{oGϹ׫8c#|=Y:]Ɯ%)Q._'||BZӶO:jϺDTcK֮"a1[dT9;IPSC.L7j Ҷ vA0<2.Cw7E w3r_ -$.f[ AbB-/aq `fg&p*(rkjٕ[cS=! Q5S 3;ɼ%MH|&N~xn]zΆ$؅XaZ? 68lh Qj 5;O*ؒVf"x"q?)ܚo\>tP+ XsQ8E>,J^V* iA}mӁG<=']6BEVHjY""ګ('Xms_\wd{TC/[X=ՆUw?e-rk*wM2^lQo swߙmr%3񯉷s9$ cz.ڵPcs,=f`bQ##G]mUxuώ&[Km$[.M=ۋ`}_mcZG  .S0dviSCVn4b=y2xECe9%3'@!tmj^)lMC8NH2I'ܵ.YTҝ:郡/-c/Â-7)k/M}=tS N7jL+Xm]BISh pJ 4lr-Ճ Э _e9eW"9ؽR\<[R! } aӾT&C.Lj d//uobori݁1dn܅U%Qh4d&3cf/])=Na-xN&VP͔lBDHhbM9G+ZMt%y:WErcƁ5 w8މZBVNLue,-VK4NJLp9ҿоeeIQ5c,i{X|A?"Hy5N~E4ok$_fv³v4K*D+;-wP~aeͤ|e""]jqgQ.6NK^Loʼ?3#aѮK)x_TKi^c[|h\')RXzZ k@e٢dcBXDVܳS |U^BM>8=WvLbtg]Iߞ~7λC3Ĩ|_" U&  LkbN@K#h8› 46Ƅ-i<2J`En Eڋ'#%}kFDžؔ#?P6mu߂|1RaVe[R)\6+Xu TZ]1 I$RO0SԭtN#ci(,BpU=P|WF$ĻzܺؠW"РZ?n5hxGWR9ԯ"}A&A+z>)Q4X='.WL o𴨂]Y'zvIEᾼyպz`mSn ͡tvmK]\+LL:_kVBB'ߓGƖ%-9E"lU =%Yջ 1u8Q㡭WSZ:;'~kH`fB+{WO"F@(Gl*Z=M㽾ok4F<<,=DM7oQq 0l/K1`|}FWky(փ3"@jmtq՗P)vPi̬Y1>5)6obm`U N*W/ViE˲0,^NHylhaDZ8L`lܥX[|%37fI~|Zp'H}[v[83ӥy ZZ[^ێːfJπ'8uW-@+3d ΃YF{'4'i O g23-pa7| Z4ݷa͉ן`D$f%D_)ݢ =6 n+U:p.&=7v]\q; #R|}e)My7unngfɝĤ_&.![TLѳ4A.Y+2}Unh 0y.MV ;~8eO&R p~,kqm;WvZ%we} i ga2zso (n_S(mjϠmSnq>FJ {#4 vrq͡>jLNbR"Z?J{k+$Bu\c-!*@plLVi4&_ކ5BLׇ/6hdxkǫ0s gݚE}ڤuW'CʴD=dU,d[ezH#gw\Jo}jڇڱC(I[Y}N9k)dt :+h`}?f,XbwAb=;=D(`5Mwuf'3P!CJԢ*~R/0ye.ՇzlTQgWPY;w2I k~)uawVv*۱3'XœoJ)@;[^jȝ^aCAN{uAAA?I&\W gtLخ`^\OJ82~.QVrۡ8Fq`6|9˧f^x`80;3Q^M|W A.wѬxU4: ?]/%=} '}X8Ȼ. ukYId?D%TIc'G`ka̳*/Qw|r~nq1>"=]'6 ?r\͵uVC=s@.72NF5Xhx_'S&r45Q^0.39M!EʅxB'Db-׷ K?)BYo[^P(/%jCF >{Yi rȑ35y߳u!D#:Fo =ڀvEQM)2zZNzN4.ݐm19#퓛]EnbN}器D(0+%I3E*Bg΀6>jӐ(DxxXaP37RWטSlm ;{\KdX@l_u^V|x=xxbx{ϋ?˝wFԂIvn3zm>nb_eŝ}QJ6:@ة7gdž[`YK8#})_C.xŊW8(vEW0f.n2~ٶ?.z|1--?%,;\Gj.t׋q %&QyuOoZ2(Bm*VHh[#zG{ɋkb sx>% r ^77Χ6O8!"q>l;0H)O@(h7ӡ88gi0$CG~D/._Qd lqhybwIKKGKpmb R@= f6@}'*ke.Yj̻(W+@ K6?BVTrpL9}x\P;D53KL0Llz.;T "XYr*Rm`Xko;)AF{p2/3$S_=H1 YU),B'U3:NT8B0j.dYy.̄%`x^)uΚy"Z[+46w*_zUcS}ZPV 馒j` @ 2SNI ښhB2{|WB8\~f-Q|az+62i$Vq] SugKD^8ˊj![;( ?hDu"kQ`n<<*l譩vrgeu7τ9r?<~;`ï8Qzcۨpg͉"1&+VXtdz&2Ju`qj#3p*3,jCOC>g`22t;1KtRcae۟Rwܶn;/*S1&ևF[\9V >:ܴX6~SN'`UsyifBޞߎ> T#w\A0*":㗷0)(tzOr7tK<*o{)V{۵,]=.\؞`UY(p>>tժYdԧq?s~+8[R6l'_>qMESS;~3lgCYAهeݿϮґ)P]̈́7Ŀt e2Tޓpe??I_&}J[辽Ι-hAfB#eT%m |2B{j`Ov8xc^tR i,{[׉Q7.1],ZZB0}ɘlߥtvȝbAW'd*$˖w6vs+e嶯˗IAE/}%OQ7N#OYkP P}b%=SꉣJJO[ިty^5fSYj (v=[}Z4i#Qߚsm1_ Sb荍ц\ ּ8ĿLw9fwPUF0uO_w&0ztHѳ>Y-Ы?}NqX7J :09tX$'Ke[jȳ) 56ugKͨ}Fe LE5%KzHC["IW.lQz)ݩ"8"o$ QJ?,LJ*~8*g̐b,EMjuo(qK.$Y~ d4Z~iGO 7z\3^) qoMfqCfbㆡU f&0m}ݑgblPfN;tI4Zj_vr;^ز <:?@712!yë))9 7˘#ŰQp(Fb'ǧd1]4RzRs-й>qZ';tP'Ru:Y@*ղ>`#dKCΣ$iUDhSi;`i֐-FC[Cy|gW ].1HK` ڸ%$pap9Wd^0@w$Tx[@rI2!)Ρm| 7~?oN$%*]7eXLlcPǢ {Sp.ω ИkqY \>[:_̨?qpc"X.#Tv5+U1o]"${j(BOuHrj@Dv'\nO+WHv2X˯B }#.߷"'֋߿S4`Qⅎ_pE8S;gpM2Ԫ4xAQSjtg+Ջ9Kƺ'yG}Vw.<$+&VݸVµp⨫xdEӸc$gHkt/I1$tt;2<RjVˇڄ]^#F 0 Bek98'd/vAVu$6m068_T:#}'' '|x#IG@S+ Og9xYQS* K³,ik=%mK|7td{K?S.)_.%JۏAX <:oOZ2?QWely27gD+>AGPԾy>I\hxx)V|+ bL$xi 84>?qJ]x&,øq,Gw|@c7cM<יoEmUQ8ɿatڥf"A>N=ZylvF ȯ9T*=uUD o:4z͚G?^ uo#-?kU[YbXO(҃E8=:d",6gfW^f5VSFSCI{zb=˖XF:{]#tFɀ|ni_+ipE3omv;ݠ;<ס7^q ժ#MHM~; o2f,jE/c:fXe#O\Tlr(wlZjۗRy{^߯SB;=Lͷh`d̍Z3(WZR ޳>_"[k22:@%9@42wF&^pNkZG?I@䛫Mc[saYj+?W#D1 KW>'y>YLR{1]CٕeHihD@X{?wC7XZ'6V:)m=vO9 ].J~ǜՊHD:eKƑQRHV yD$_x,tp~OPKa&O=*vÁ"G` <no").jbW3_rjW.Z~JDGG_k( .}vj&6DuÕc>T%D.e_>()ws>ŇDA#n9eOVri1*tA?N~IIFsi`H(b=8}vyO)zp,Ng5wshgq:^:_af*DMuE\nc:aۗ-wӈ%I9,dPqFsJۚ 9T,zv@&56z\1 U4O7 .-NVC0M @7Y(҇#9sD&0Y}瑫lJ1ۚQ*΢ՇqR^ϱӗӐo(µ7L50ƎU[έ)yI5Ѫq/#ݟTd+>ˈd{Oery7n`u!ӬLF{t; f}wtɛQ|x"|b KS~,h*}$`6B!o6nôUbA/{&+;&EV{DvؑWVts&ֆK4OWsYN2ZRW'[Ypt+Ib@Tó3nnӏ^ Fe?Em n-7,Z70 P[; ZoF(Vݷ筒n~Ǹ-!Lu(Dhi}00^۟w'Ky̓&7ɉPvml5VVIOA#_ H3FJ~M[Ws# B 0GVص|T~g0kկ$8XRAŧ©IQtO 1ru 5l=VV`"2}CwFtzE9skQGZcq ,*[{^W @w(I`e'Sy%'^B= 2rֽqQN..(=לk4fcw i4)|˾\ پ.[ &( wtN#\gq7kZ&&o~|vdj_ftw 30L ihS!z28;4[E݈~O~zSzvi ׸?* 5BORS>L>Fi$ t*y>kT%[_Ňӕx~@+zќj`>@w-X2|OPК>J9]N9$i4N]f!3~x' 9VG˭S(p䔖JBƄ$xi>vH]X8:@ >*znU_4I݇/!.oay)eyރч g~U /&M׶JnbcE1}`Dx4Uኪ焅Sv"#S, &Q7ʱx WuHM|j]Zo(SN>YKXZCvD޳r+ ~bӗy?b>h$`tp% V$#T{P˂6R0IăV$mx1:8&V.\T]rdd4vXa *]\X-Pٍ&_$C[6 ߈Wr0:MwDs m?z 籄Zc;T2SX鉎LN' 9VJԺVBQQkp1nnvV4ex>G4-P#_rtWo%Oj1DVH[ :sŽ$IˤN;Ե4*Jnp "jj?^NڦPuK`t-yψw6n C4OFH#>&߰bCc -鰇"BEy5Q4f%f~op%[ B8a^;Fٳ[X[* }8%W0mݘ#'QN^z^eb3w䍃ƒ̱656tl=G|G QƗpFK'+r?LD= E\H-tjFx'dG+RT^Fn'ܣçMIĕ; o ]x_NBsX@Tp~`R:-h]Bqspg1pMRXC'XMN`[Ⱦ|Sù}0ڼ7J0 fWoԬ&vѨ*^V6!H$0z39Lti'`ڲl 1WC]^,L>Rww kUm:vbwIc`W5CU#SN3QAf$"J4|b*38VV`z|oIDW#Yzr="m./amSfV@jq%RN Ȃ@Ĵ'ۀmaz?3]3< +2Pزҏ ¯Sw8oA>w 1TtՉq"|!d;fY Wl(驿%cl;iG?[aƧ5M\ݑ|yڢ2xŽ).諐UTS蝶&oF]0|+3reCAbuY\]]^'pwܞ]A\iTfɊPX2xhg7%r%-u#|AsAH*oY=;Klc&)IK˸/,f|RV0EZ,ZQR$-4a92 ΓjfM*[厯:""y?q'f;UXԇ m`)ZyK*]hHtˋ8-(A r^Ӕ>g_@q($a.mp*8)ϵȈȺDԗc迗Lz>VV;d;Փz$ FiiiMO1Tg΋o䍲D׍1rK@T kU ]w{ˌ+ICiwwLx- v"gP4fyLTp3'(!5YZC]CNI0ͣ G5D:.)/QmJJkZ4nyS"ui>PGDaaV'^+趃>ox;5"a 7y'׺,I []΍T #>#o=Thy!dN qRkz}:zĜ~@q#W?ﶟ> q˵mQ,qnpQBH1NqI>K͊ FaJ={p%B&]T7+"ij]ؔ]aʺp:<$mXWIZ\fﱱi.bm^lx $7 t-'.dy[,%%EJ$BV0%kKƉp|>;=d%/ϯ@_9}(8sֶF4xYU>aqK5WzWe}1#nȥ'Gvz@oe314ͦwF:Jd#0JU..LrUXπc'7e4_&@pft# e888]-qXNls\* ?0?e\LBaWgijr.HwI(4Q 'eX33g-Y?L^̝R^@@6(0_ ^TgC{3dNIMogid т DnF>Kd3+UoQ힟(6naSJLw+k(emCP=F@ʜgaZ;'WDC;G `cpF\qfii-U'LEyZFAddN.*%po`xG MZX.2@,t$e2V=0 ؄oZ(pdEeVT)t&E_7_l K K7d}m-VL `.Z/ \@qڼz}/{. J2Xk#c2U웚؍4pЉ̙١#?~g'Ɛ?vW\r>hN3Qz(/DY;ykJ'm^A0vy4Nj)g*&þ%E:mMr ?8+9.槠an>e͢l*> 1)G+UB'Jyw~(Luh xFͦEAbr{yMIy"+l5SF_s~_ɭh17{ULOQUq3a p3GTammԖ8z%y"軨NnUdׂ9HoE/SE-_`sVSetW [Nc_o,)#Jh,F~KVd$n, |6T@ᐩikN`U'(\, h\1sFUmΈ}JGKB 0[7+fc#4G q_ˈT;ieLtYdM[ZNȾ4z`@H/S\ETc!u^g}Vj7K<8O*-x{hul ӬLO,$gxL&"`r;_nBbbM<eH-΋+G'Ս,%~xW% L?P7Oӱ:Wr_;}0+TFPϳYqeN>J4=fy0{Vҿ 넄jAͯ&ETU43 _S;L1IJ/6&;GJȡmw|I:X"~D,%a ij=y? < Ź`iiԭ&9vlC%Ϧдi6JZ gE|+ٍͽТkDGAS]>D3?cC\|"x[ftkɤ`{H]XÊeMT %Jglu>3D-S_#!TSHJ&lԖ+4ѷ29^L#B.P%[ ď]FE«8XϏ^AEN2ETP~/Xb*ҩX6vFR1k\U(φM ic\8̄rdj|4{X̿ Gޥ]+? 9E&ׄLnzro 2og]T&`"ϴAb!yH)$ '4#2JY,EiEs>2Ӟw{Sv̕TR@%bPnEGٹH%}z*-<^4{@2}AD1Bj\eڠEC4Z#mVqm;lZl Z?zTw.ٍ2KbI-lGQ2H[9Z V~/8hՔf1>R2O!a@z 9>¯b4>-'tQNjK?> i{C90y02A#/ze[⡋i?B^DmwUE.r*@Gs'.fr $ɩėxb2(Z 2d tEnyoAWA⫰-{^pdHʸWőҀXpQ ryYIXDVeG70͐)lг&EMoC6]4=r̔9{'>*޲N_ಊPDle(`s\yL"9-gJ^vQ|: r4_ MMft?gitMsxP.q%j:m\PEqZ =+g̲'Jt:̲ (c<Ѳkvb@PIh#,^\B(>{Lrl C0s #XʣL3>)ܫ #9ciM<91imY㙼@eupǐV5V|ƑL74a\C)z9)Kf9gF]AI$^t"tPףk7PT̑М GY/a=Z%W`D+gz D)`x"~u16R0y|⑄aLRl$TbvU_wXozafno_I 2s.X`ܬ+9Jkh\_%7/bPW+E\ȚpbS@o.̙ TIgPv`vFRw})vS>PK$p6$<9s :jES6Lx>ѵ<~ i-(ʢ00^).rZȣSG $+5-x%CAAZ -?$8V>7O -WA; YDA6ۧ[ Ψc龳7E VQuR {@AX[9NTGu/-RwTت؀ff rULqZ P&7 dudtn }X\i4mY#hK nV\t zS)mAJmBM}/)&滟r2lG.ӔS_>pH-\T쟈Oz4=vaqfD l*Sm&A]͠NWv'Lr~ Gw ȗ_{N *J[Yq[I=By@DJyI2s)y&9tt D.8pBIA,95D ZvZא\ H󉻸b\h!LCKA+5ɛpr2 WQfP hKw00{lN\{~XQJ\ʱ6`XR䓢Qו 5TqK=Oo0/D ̺6+9 ]ӓTaL>UfġQ\^,88iN?MMC~׶~q?| c?|MsX)V>J:X3#H{j 0&~PBr tλݖrCZThu>b:aU+i^B#ǂھWpUt. "($ķ9$G{"|Fs-=KJ lIޘLNSPARp!sטц2Y8_D"S/י>.F$e 'b}!| FT QZ߷u'a)kBh"4#u:?:a!rK q/Kđ$.L۫LLҵ %{F%p>BϏTo O G2eSX?(~Ewy~xD"1$VD.npΓ{@|=uq4|$݌Y|E9zn =#}hH_e !ąh [} 4l0%V,,;Tt+l.$1"|W18Efb[6܈^o}=,wbGmzDpK3@PEb#_$E >1Bu S>9je:%-snŔSS:@# *ku Ά9W*]@Z,LD^c65~_hxhI&l-~7M,s >.kz ؠސ}9Xf]iQDF.ǹ`},(QvU܄,m6%hd~[WcO- x$#;,_q ; 坨@37HO2*&^8P,пu3L$}$D\>ߥ\gB? cndo 0t\K ZsAfy7SҍРq\r2TII•^$Vm_ki "4]p\= s)lywc"B$xP˚$iTN΋(V.' 7_g &.u"k$/z7*^Gg*FJ^w`eӈ䦜to;{N҂n@;ӢSDjَȄ Yrv  dzsԧ9A bSVqZ`V@lSo˩oKƌsa !XZ8xzl0UDxRunTy&6UE>k> G`0k xոҟϸI&9-u9i? / 918|ep7b Vl_XdΣ/XFWj.u:-=rJa[΃ߤށ#?D@9Һ11]|9tK"CaL+YO\ʈq;wi~hFKo SC8<!٦\H[8xz0bkWMHڤL ;?@:{"qD.-#H{5DžuXaH.y1PkZG^rG6OP9MS .*gG$:#i+HyKCu=IPYd1tX@_iZmvOT,kb%+uQ?-p"Qg-馁"Zp/rfo6_@K[H\BO@oݛMq)V12nHBp7t%j:Vjn7Ny`4kT`eHV.o"^IFckme o|AtpvRB^`/( *~wWP y*; JW'B g^U7(+x0bmTww7*H.Y5bg}-yr?&I2p!ō]5>h!d!.p _W l)˵5O([)vC .:Kyt鳯K"!g)\?+t foY)z_͍ն&mbGm/Wޟgv  u?a;P]~,Nҗd~6a kjqd>!n-?p+՜#k]aؗ<_@͉jOtl9JkxgS~0 >G>}ScII:`v{^/Tbǚ9)F[/,9<3/1ׅC,YgX(~jpdDIG/ mog& b2ZADbO0Hk)M=UWrLz@̔L[LUw.q("C_mMj5yl3LXУՀ2W岕 l WRC4lܙ^¿Mv$ECy"ZLIl(#eNE"tڮuCh4JZZlDQd{Os"\zfGU]!jf}F,T0ʌ֘m#tr D{:3: Eljtz<2Guwd2"_M򊼍er}pnLw+4nˈ#Lfƒ=)[2lEkְ#ҕezS # YCbd["Y"p8&鉚bVBLՀ41 k)@IKaUAp4+rK0H7Ԕ&P-gX%,W\9U}(ՇZ`L.J} 1.$Bҝj(/5\pR*;|>er;S!殄rssgՊ镀9c{k_9p-huoq;aۜJ_&<,/Y% ʡtIF:|2ԙyMp7WvGʕn\Mb{dRh+/:zu2圍b9CC&p[1'yl<ԝgfn$&9 JmXqAVrCaAA!IJAABDğlGrJt)]'z^'WY[xϺJxvM;<¡#KYV KOˇ4R;UX^7011Ȍ.;*755 +ټHg?mD~zQg#R_N U3Pw\j=1.V::5ޘK22 ĢKfe;PxrkϺ/L' Mv 1Sԃ/'_ܧ́ڌo4231~Ġn:ѮCyewH,4Lrl5S7İΧq: /8}o bR1Y2Z[*b`ȕ2E d\j{xfrC)p/waszp)pru;[͓}F04ȃ NL/K9gO0Q{&SY2SsC+5M ~<.u jNO C8jU v.]D!zFj٣W-!p^U]8bUے=q26U%O#m7cXD[ņhdnB&Qi)RDCа-ZT"59k0pPk|JfI.xsǚNxV֐<(AC<*ip>KN=X* 1d/Vkv&W^$߳omUY ~ID,ʭ6** )#HMﴸ0IZݠ h@cuSLv\ j?;y SLy.?jE_GV [*{4^E hZ|=,/|Yh:BfU74I nDq~039RN:n3Iez-Ql`j$ٙL'[o7F)Xj{J}~rw1)[N#Bɥ9ߏz-\ wXp?A? JՁKY/+D˚_k'Mi(۲lP6=݊np'5t^T_x=J ^Qh܅.Ƚmq4M?WPi!h %AZW"ݘYHR;ɱoc_W=܇_n!2_ :nO Uuv$gYe{[ Rab^Pa`gLHM)|B3JuSaemyPMWE⯖0Kii-Ps.\ '%x 4R4om\iU:W;[6l 滛Sig6J|ϖMAr\4lz,$o6l`,ER#"Nh97 :'_$gܭa+Ϳ_ y7rԬ[H:Wvp n֥d|"6iD[F(`C9q@:nV|Ҧ/BݷӏU3V? *=Gf,~8ŏMm d$fٳn)V')$Y\٫jE-9f%u雔W:7Viu;L(mTO`7H)Dwnj _|xMcr]1DHVĻﮬDH5Y:ZϷoVr1g$w0UǗ'$-%! -b% N_nεQ9/} s795D'F7xHxZ=I3hPm}5հwճ%et!lf; X\8mo 0m'/Y"{ЕU_.eVfF %̂p|l,M;O6%+ƾ)}sk )y%{TTm>z(H~"2=^clo}뇉6C +ᔯHɧ̐I@O;~H:cRkV=!a>[NW1< 9+CbFo1[k%,5Ǭ&-G1yHF#N9i^ό\2lER{ [ mՈ$tu'Dא'I7-\WXeR!M\h̢r^p~?֬\j%@&[Τd&^,f:hbuD/1\OoeCڱw_AVS8WXxFTa<9*G,f2 >%<Ka9(Eל %N֒3e[S?j*Fk{dёHYB\ܴWd)lCx֠f,EMLK'%5kLYԚH_cTOφ,0,G;;@b)U%yiZ 12!\ٶ ?{h`)j(QdˢblG[R`kBb -ߧw?8vB+x&J~ E= E1v꩒ Q9O(|ezΚn$#C'C iT$K?2wM;.U B zaԟrMD! <]5y3.CX 0.mŦZ8NQ9Y]K>^8g=0~S2B<&}}~{NLY@\Y NGjڐg0 \OsvQc_+u\vhUy6պL&DusZMƨ 0MCk+b2&_\S 9ـ׋5Όm4jBK {#iН ioUH,HmYdHNa)kA2?NrGW$A|ӊxEP_zRf A3kon3>i ǹ% ^;ST@GM K]'ir}?xT]ŒYjTjZJDy9@s8z8$]&n۱Q`Cjq&m?bm#W7G{3ߞr[깵wg9B᫈cOf=~ uW5W5!"羵vyV Җ|%1@=GH}iU9/M8NxUKe3$"p67lCn4@Pcby&b1K\D[Ó"/-Q6YK&de)䑃g6kB7؅XQ,?— I< B}D/nXMmgo2lKKۯݡɹL]wj)`ן?t8Xy7k 79k{oL^הڱXr`.f~UI+7,~C|@4b%hSP $r*z9&ۯy"VfL%n'{+,`bow$qaRd.]2'v(@$(^/'1@N279H$&xoWi/Y*pl5ì#N)RXkt1N7#OZ4[kR>I4i< ~#s[Dbk3"!։L<-Ī|{@3ḷ2itTf"XPu^㝽Ubג+׸lڕ_$W`۸BWy:h~^d`RWEtc]oo;_4)vχPU&2y(LDd@"lL iݙbg>5݆ťcKl8 䇊]Q!f`bf<~*9@:M(Y/m!qAq7s~)9@%qdPRڧEN JfhM_#t- P <dӒfMV\զ]*Sg͔bxO7!<4R-M;n3ZLү I:B: J..dK4{~Q5E&[9נּZS n_8o7,bhQ! ڧy̓EϬl|v:8(;KC6Λw>'F=> qI]y)W~ SBX|f?Hiѹ>TJufa))}5>5Nt'Fy¦S!LU!Kp?݁hY<9V^XbΪF#ߟZZo/KA1<wؖ tDz]ߖ|Qz ֑ێVj!b+(R]Y<bbZ%|%jIoBAWC}W4rG/&>5Ɩ!2{X@wE/PtA{>#4ZpB_02ήh;}lTܓUJn_|Z WY͉)kED޴/\ p tMHsgIFWe%Q%R~d/+Y !q.Vr%T Wsk[r1gι_0-5BȒ_FKTgJQZrk}c8Ȣ1WSZ^B3y~ Kq.ai/nhI8B UxƝA6uy?h;cG9.b)!"jhTw*AsYc2ࠝČT cI5Unv !ҏVBLZAm\h}pWL5sv`O<M$]HY3uQ+O."L^.ȁXĀ3O,Åg7I*>/"^':Rw1ӝ>hDK <t3T1%;C)F@U(v\[xG14RtG7 1ɤ*=m‹y"ǖ41]ox.6j)n#}/:#sƊ𾝙Z,aSؿ?g]s$!],2lPqc+Q@}RӄͰQLh[SOTS-Vɓ c&v3Ws!솒=ҏ}ϛF)R N{/2Fen%\-a@׸iEhֈSM_RO*y>/A-k[%xbL;6U5؎|d9J'JBن%_爤l^-Lgއ=2%g}+<5.Ra,RDag~U|DۓN`T4͊dQq%+ A|Q\n1 u٘y[/JL3 p6(lG%%WkѡLv$Ӱ7ghDL0Yh*w ;'XӨ5Llvb9|u};Q+gAY)T&źa"rB[C)$hn-d3/})GphE=HɃRUHn(\nl#"*mq?Maef/DۘXx_Lh*ɸC%SQ ,(O]a&G,9 ]{@dRex7P2!a[AQlgٲ̋k__|md-aG Vh$9٘ކ%j,jt:9)^\kT #No̕T6O4KfX&"a" .ӑo|ׇf*yh譄z|*{湔7wٝ~m4iabu2J B07ث8_JHWÔSDzlg|ЂXW*e(줡qw5X#Z1Dq Cwb6?,aL&=T j~RJrJkG,guݨ U Nɋɍp+A:Nƌ\D2K Z,6-[W34 ʺ|D#%t!~bJ`hX8|;_g%)N8#)\)4orACy9AoA|M 8\ƫn+)]JҋtEO0YzWqY:psЊE#ٻT7ү FNڲJp=gÞLuϑ¸d\;n\~NȞi[,mڬG"L71)Ù$_;pӋcP ԪZF{{͹e{g~ϙƙlrAX] ⥛ck~ұK*֝U[ϒkL}j`~*qa֞ |PniDf=e 4w!U' ZKQ"\$$8m V. >@C)[m9ӴftC mWЕsCg3ã;AxT8gq,1» \ EKx{3DJ6Uf oacJ[9PJSH ahWa-mx8_iO _!Xx'pq@ NyIWKe /1VF?NcZ۰ &\8<,C-Gnl LlQ`2.PO[x#)Hڎ$ja!i'N d3؇YE {ݗ2R>L/Z=AD61{&5`>U6oJH11.I//3"@nۯk-PxL[Q 0O-)30wkOދMA' kq +$L6pxi'{; [PeǠMS.ŏһʘ2M~]UD2x00XkhG2)@'!,}m |E?{PP0L{~\*[|U|L2}KfRPnhk +֏p?g oj̱b#u'Ρ(#C\*^#dowVIe*O gW.m{"C4=XxN0' _ t]9(w!P?Q=O>Cxܩi@W! 2Gh n%XcǑcɂ:Є`r6~(RƩ)ILL6Uޞ|~;僛6C`WNt^}"GY1?P=QX̨Ńd`[B3E}q uWFR(VųWJNes1F N{~w]u+^]C:tsK~\vV;-h5o$R p^T\q* Bbz' J\=ojk>V$^~R~;{6}%)5IQgXh+6o;(G&nEŲ?@ VGع7*wvuHC<@$aWtlpaMds0{$omd:(%yf@aч h))&9uZM/ COFAwNëm X@jxLQ=X> 4f'/1(H00YZ-P`UG'_[rO~M>BS,TEH̠o +ܳ &f{0ƴ|I+y_"fW&nEf-.SrHaawޝPdQמ>;2RM4Ez(dc}׳,8Cǡ@kqq[J~wGWEy+D<}?DRifNt gCm!p4g̖;VIfp0hȊi ( ?VՈmF44Ԃbܟip''n_xě]Fs FCĵ ;&0`OF^*qYYS'f8ciM?u_({_':JP!|k֮PJV^OH{̱l/;̻>ُ5B^8렼W0=<|ӺVN~-c;Ԗ]qIC)2 fGM9 __)J?- "H]vmoW@@" ~fܻΗ$GQP&Sb0: "FG_n4t?|J_U|}cϦY%r)cBJ~u|Ř>+'/ryRVi'~QNW@%~`/sֿ*lYQ5LEx'9@gjcpܖ# _ w'IGM\xWN%tޏs(We\&A8sqna Vp eǵV8dJ#̼STHm80*fkL̄bQ8,͵"u6 oQAm&hUcce..n$@q=Z ;!D`P: \>RtΠji&wopzNa\o"Q0 @:n@_㨹TYá5 TeT 6k2}ulU,dm(Yu7O]'ɩYGCWq肷NBWK B'(جAЕRv{P 1ґRoUd% ɈT8!NmY8O_ ($ڬCLpxCITށ6,pچ%Tr@lZ0 i?aRs6=o7~MS^T7DĩŘQ=4m 0r~PiRG"}cСS_B`@nkD7RTf-пeވDUDzA,ATأ+wŒ^*qjh#"7*v=:Fe({-%/5B$EV@y8QǧC+S.iRonB |@ʸbvӮ$a[PPesl2 kUOU:^`\s}{ѱ:K?qǐ_1}GAL> qR[[Gȼ f-Yz0S;9DpucMp Qn,5V%^P9Ϸq$(5kz-ܥ65 YǾ[\쀸 #rPohvb ܌;2{Dz:U*y CkMu!%6ؚ+x\K޲.g$E>2ҥdw|,k?j!~4k@2*n(6Ӭ9s+ -Gz䓄dʜNw{ErU9ogE=nI΁KxsүO<3\iKM:dZdi5o@GSCHȴ1?8+dm\?\eL٢|e5?Kj0p2^gowB.ód¹4+2(~p,ōݮ8yAӌ\hj֋[o%e:Zʊ6wߞ9s1x"tbzK=3a]kZf$p[VU(AT9{'~yUybA}{X]2XpN7>з]2T{xjtV+}Eȍ9;]PSX%'5|fCtP"kaɗV

YQEcG+w:1h# @3C΁CG p!ޤC UY3k> 'qUʠ>ha)PW+ Kj>"rPo9h+)4zvhvuMM۹v&^99G}4@X,]eQdeTI7~j"y:|Yr(2T]z+!>8a^jKpor<_"RHl{P^cV`U ]F(bY_IɆG Y"rejXK\pvfb]wR@@n* C>}ټY_/s <ŬxvXWѰ~Vʣ6Q` i&jXrXE9qma.,{oeW̍% K V1R@>A0!k xc@<`plTدb=wHWFq IG)Xѯxzl`{_8&ϑ\ç2j}v%0 p|:^~/E;Uڣ< %U|qbc!j"54Ere4ofYXE9N|`6_=3{O<D: N3 㠁x y_PRdH̉SW.4~c$e,%&8 '8pߍ [M[D-GpNwK 7k`#$ P#N _tʹxs墬n2vyG2KRC2kΟOg듇bE)bGKs2]\W50 Za M;ic}_ 6|G}Hl u\ׁ~2SD'3wEmxz~=ׅh9hQ,wC~X5{ֵ4U_ bE3JVGlwkl4s3/Z+W4s/5}+fr~xX1t:6`p_ekN+f]O|/I9Qr!!J].> \=ӎwEeI j%Y+RnM$AERo31LKh^q! ;Y*6?.kH(䓙16LQ799<dNF.+&kWXԍGp-Ygql2H2x*4Ystjq- eok +^/GO:̯tZg}n|-jNkL.܉=muiԛoq% ,aD:R9b'Ls,` [EA7`E c~Sӂ5B0#GgGe;PQjdݟk#\dhᨯ=(_4w1ݔzuHM]6u72Ct-$^ ?GŌ= I(u_r6*<rf`[?nr]뙴Ayή&vaVv lsDuyn)v_+zni ɕTFa5Xg- &q̈ E ,EcPLQ973) .lomZѣ'HjV}H3\e%,tu ưsK 6kY%ez:cdR bNPTRh*{T> vJq}q TTs}lI^ q~~~&xu/RQb;0B߽Pij Vnqefb͠İRb!5 .p ֫0j=Kv'镽Oy(vOo/˟[bGxi3gp;<%LNp/Z.;{NU[ s_ˌOLIvl+#Ѳ šgѻ+!.eGgOíTda"/+FB]-B ~r{% ߑQ vS]hO=xz`@X hCov `d]s3 u1 ^x {߶wd,K9eAZZdR]9BN6e1Br7/Cx/%M\7<׏v%é/cjzX3n;q-X'9j q}:Ӧ d)txxyFTKXH-4avm-oF и=Tº D46ٙVO_J#TrXi~ ?@B[S<^yk\" :~Z ƻJyP]xy.li@&[tłU)Vh8c!{'3xp7v8`kU8"gn( 6Bʯ}j+ܛx kI}|G.Md!߯Dij"S4[cQ|*_d4S^eڴ=<}p`ƌ޵Ej_is$4B侢ӏ.ޓ}>3 ( }j4hi׮C K@6}Lt}=JU4!afr <E_/p`Sxh"J8H:, "çtU6סyY?I`;ٍG7.>ugH- 04N MۼCoYS9w+ԸOe124!N4? ^?o?ۖ<,ti\N❏J@h>'u>A: BU/wjz~-2C/BC1x~pZ[~8 iiPKyT7wK]/t+i]ֵQaO\, &Ćb.mwm"g [ol:}L89f0m&]xpE|Y5r^ևH rfN #Ōŧ.#/&Ԋ109l@ 66Jp ws!r" |\MD( (SXgPvAޗy! VʈRC L+^pe +nڵTBn7'T e:匼R bŃU@yʝ`S1AwZ:w\g494*vкbms@Jd,;2/mM= H,e@~S)ACOm;2iѭr2A1qY2g 'D&$DM`lhX83X{uM,쒳#޵a7=*A.{5e.ė&Ľ^ꐐ2Y"ud^烄weu/THјLSOK q&Vt+IdaRdEY r||l+GKo{?7( (,e֩RM'iv Q&"Ar6wRNxp]~1Sװ)| ᭈƌJ4݀-[h#}ƋfTD Jp m< 'q'Cи;YG; Ƃ*:Э7z}ɗ5DN)(qx'L3F}}uJI.̜n?M QGP5I9Vh;Er YthxtC-4ԙ٤\JtQRKZ7ǑRxK#\[0٪`e\`HÁL0XCWnO;Ԙ5e\mNte֣iaE:D4b8?:f+gT&tE|5ꣀVH]{+>ӝvp3$x b2 N97asmU 8}x$q'8n&QtD\+H q.0'0j2^p'{"kcT/bɲ\܆5StGӗY/Kf 3YC).Q\eG^nd=bHI}S<Pۯ?ejNjY Ǹ0YPPg3  9m,#cC|Dc^}a,~gM'0U@ˉS%Lvpi{Ul]L<3ؿU mʫ:>j>`7RJNeQQ.'K?#]cۡ@Bp'tOȪ J*vRVH_ Rlo_; DZ6YE?ga֤e?$.XIql@^Q 5j`vM ya__jz㰣P6 bZc>t/ڜv+`MhcA "_uhs\ٔ(L_7WUqÃZ'F\C#HQ{\]1te`cND 5z/*\7.Z8^yaۤTB06)ߜvl.3IQs e62tu !:7&jءȾJ}~Spy\oavB2zd:yrT9EaF cދ*Wׂ<?F15: 0(jW ^:Ba Q+s5+AsKwޣԷwu$(e )aTI_3j~gkRS (QO@B~|Zڂ,8{$44W:5@WejLʕbq"T\ܜ`q |nOkwh]z:ٙ |iaT;8H@^#74^` 64tsZ?U)$w>q!)dL0f+ <] k[ T:2GבątLTAvYSI$,*J{5 %u G737y >8XO!X<ձ #bؿR?m@qcNd,~vShaX (+wZl􏉞Aܯ+>Zq{v>>wy!gʬ@:Tc3(W&lKaCf+lsD9f"eWP J ۍuNW,A{=](%e6D}oRG-}M%SkXjQ2ۮc.f/xBCu5?HAZp6H(bڢ"tkLwkUp5 8τE>(Muׅ AYZŸz9H=+[V o(ey$4!bBjmY6fgʋ⒌֖O{P/7AgO)H5d]sSX@[cp ~lr:WmDOxckS1O^S' тrƳX%E B!-v)/}MY&WMZ4c '<$c14*J^IG@Aqt+l)Nun{IJj.A+PL՞~mfr~bŸ VE8CdOi`Wɷo(ZeAc]eCA]mc&f m{.iV` T5<2*Ia;R,tj<f?-g t5ś EV"#.y|5kCaJKYGe'gmEԼ>Ѻ|$Dy-r[4FY&pli:SXp sЎQ$(Nfir.b~IP 1EyQ;CHV T<=T11Zᕸ;sS6yCwP̜At:x[˗ GVm<0ZM;D^n_V)t>+%._c(Ѳ_LI1t.?cxyazũJ~Q{=*`#Ԑa!5g1NZ g1 S"Y+ƌ8e}bJsj )B'fp*WNY>9L o,{ΛުGӛ${1B /l*aX+cH1r({W菵#?qDk-FvV;ҥŜGsG<}k @7bü }]|*k{t{TOp$̏lvU_u,Hm xj˪•+&# ~S'0Аר]gܲ$BQ[ ifXJ!]ĸYf1Y(1,({x-L}by 9ƹwξN~_^akA-Ϫ|Cc^jp+ifX\\3aҌHXv{pIBXZ[V-ū:įOgIcn+ ;h՛SiJzN*t,Κ4 tnu궐*'t 5|=KEH+Xjt2+ n[y-x4ݐ+6JS3B0϶&DN-@NW :*~. D:cko֕'=ho%#}R!Sέr#IIڅ:׭x|<Z1KO&R3~_zOxB 'ke¬@>Lx+HFfWWp HB?a=eY[4׏=Khx˒p`@\C CLYS?[`W}2OTSۼStC Ve)2 `Zu V:oաvjѮ.ٰn_s y+nG<Vf#kG"*'d$wm) KsvLir%X|%zѶAHBt|v-(\6>2po+&b'0@Ԏ7HS?r=DLWUk- 2<@qp"icuhP)pIp빓}k{Vgx#jGUKgEb SI©W@2D Q aLke)*j@$,kTgoW;G>M`bA6}|w k>L.+ś픈\q׸4LCN_#(*7BC;;g&AαytBy3l]iʦ"wP6Ā=MO?k4/JS>(lGgЬV R','}u%Y@/ϔ"=I֌`&U60J9{>aO H%{v]d,6ۜPq?8$NEᏏ-79u8Fp.+q4F26ܗ&S, sd!!c-R[}%A;}:ځ.%81l?JX,OxdpȀW $R3f^67!2MaA^} `X a#i|]w|e=v8FrVIcymUa.6n^ :T\w&zڅ }I."o2t]uw0N0" 7tU`2GكD=Q_tm? D?eֆ5-t1ܿ@8XX >M0\8au&ѣr|~f W-}(XL,,-~S R0wlh\3`l& =؛[/ 9 0LNpKg/x@i5m-lTQNSVHV d .}X P+va6tdᆻr^ǜׇrMD5*崫eƌ^ \mxވ\+׍U(%b6.w-xk__SX!f{ A[`NӼ+&Xc%&S܌2$vgev.S)SU\{.QdToA x?.M") ^'֯ _g /AQfD!Ȅ'yhh@n[3X#*iJ|K팈O0rh œwoo.$ofWe%݅xk`EKED@ Ӧ=^F$mCZ,éń5F C\U;HԮˆtޱa6T2sh JIDpɝr'S}k'G>V\P 2#rY04K$>,97Saډs4ihv<_qTbb́Fhm?!iڍ0#? @ + żbHߥlt8RIr$ |p FGG1Ƽ4R1޲ćpj% Nuy8J.!;jQg?41H,.!;#͕hs`Ԛ%Xs6?,9XmCq8ƣz?Dl7f3}X i=d_HO `BˏnZRPh~P.wU X96K{]]*C IŖeU 1.|,H"Nd|k&*dx[./ '5!(xhkmOdh7r+:Dwa5Ct|VL&bCݒwLA *=㐒mNB C8EBs7Qn<]e'\x/ Vix\ p:E0'DO=4mf.,YN5jG3toD9JD.oqT V$M~0 ޷>P,K:ޭ>fZ}C$195!y `/_eF -R6O {VJįh Nf8l,U2?+N-,w4}~xAZ< 0Ckfa58a>y#2xd4``/+5%jawj޻7 ߖf ڣPt׌\Чzl] չcBY >K\=cS$GS@!r(M]HAK叴Ks?.ZrP[s}[`Ơ7ka$tMę[ޡgZdܫ//N h8K!M5ŌտYlR'`m;n:{v`xaq$YO."V^(1n^h}MlHW9ıט^5:?r'9܏j_TI4CigE+}> eQmgQ '[{5&u1-b #ŪYeSC!4ʢ?maY%Id6k[ؠ1> r빙uo|- b7p) @U)jI8n [0:. &#C*0~Ǜ2|Ry^EƷhl jbhz'KEFFߏP}aAW~vd. 6w1FxNӐ2ѓSHAb3K- UN\2,IoLDIɍ&^v D)uՅ̮TF ]D j3EāzlܱJKQ 7j72 = wxuzpAX|p6Ce5,ʿI)=ppLrG9hU9J/= mf<QL rb;^ZC,TW ?N7?s½fI(gA] \ 8*.+vlے|&O6Fc8ɏ3 L2r׎Hιӎe, Dyf!Lh/P 2VWϣmjcJsmrm 0;bE\\W:y+D"mxǎ[&CF W >Nl[[=G~LӤ2=$'YI^ #8=}ږ3f?Y4 Xa_bSYVH;y\p6c"qPb <~%AJKx3RwاtZwl`YF٫%Z$[|nL.ek@&㩏ŚH> Ɇdx:U^Ks}'lYsS%9# sߟfMͤs/)nu~\J.CN\坣)\[y3B"\[d+QJWS:Y_ϣ1iAQw-'K@?^'}P3[PaXQ62B5uyo*p+>$P-׊F>82H!b.6, '#1 ,}E"k5Bfǎ}Qֵ;*/0 O+ ׁba$Sa2YKD~X4||f8t?#ĨOkudu\HՄHXΧP)E{7/U(&d7tQm;/@,gT~28 m_| ^sLaG)U.GE0G3~R.oCԮlQ W#TA7U0:r,)sN%9Rx "A=nF5I =G!E*c 4ZksSYIpTtu5@$( j۱[Ys:}]'/:sQHCqlU"q ʎp.u\Ӻ*=\H[T+ǾUUFD_6ejdǂ3گ !0G_D.,V=*G7O3cFd˰%м05ےӿe''Y&bDzfu%ϊuԅ|96mQޘ)%a Ck2bt~LjcBJie|3卤D3v̙w8[뼎~j4>=,G9IrܦTNqdչ`B4U :Zd'sȡ^$~d q.F8nl*kK8mofb4/IݻM<:9RIH nt)5c+e2'_FMsAYt "z WҳS?sh&HWWȀ5߁"j"Y8^ Heۻ""æ&'`{lLڄ\Xމ('Syigd&XǹožKMZq_qcIb]o Ъn26 \OԫHOW'իF@_Ds [ŲѿbC͈jTG @@3N\~UzZn׶Q%nkvt#g}KXRDh,PQ嬔T~x}cCyP>"LFQFp G:cE%s;!!YDQFQ=n|t<lOb9~)2 <{"0VԲ8`GXϖɣ|n8+CbO@v'Zj 眘w##ӦEBӆ'6]C2cLIUl|[e[juΕ{QL4\ET&Wث"Bji-C}Ba3ž \j.s^}dFpyV!{nzI%:z+C≼0u^"9Ϣ -ly/ReCF 57~}/O* ;ؼ! N^ό0La:0Qy*#Yn7+hڅ|TmNA"O%`L1b0n?/盅,L{x+$|-ub~Fo@eڡB>.KjB<,`F Lvcw<h ]'o12wy~a7?*`.1(Utvڍ屲6/Ug eV(h xjVHRtTr|xcs؏ka~Q x& B2w-MF.p"Z4H^:yS*mhm, QULvNa1\rAr}}qAC9X9*$!,"Ջ3 ^Ln*2h 򂥊E7tX\vxt[.ӛ#v:L8 &$[Z\ڎ3օíǤ什RBbvc+y)>Ɖix8Ǡ߇վj$ "_w^\E=CaV.mj &[7@D5u}G@$˙z tMd^h7qG+tkk Lp*˾8\ͅ;o5l0 ?7X ErE6D!F~N%k'n9̡Y}!4jLEj[HEpFGul`Ȼ ¶ Oĉ Ǩh%C~ rԍ*L7,'hMP+[M~u&7<ڀI0r&Ά7F}GkDU5$A)kҰbLƼ u䣀W% خ|uW0{Q֫3!Es ZLx잉prw5s_ŤE7A;V x6GT{.^9bqC~4[򖉜jg 1֧juuv]!Za%4 ]>.BJwj&Vq5 A&c0C& iEeNfTQǀ. ݗq&7'r]\K{5|ȀhL&9B5~\"X~.Վ@Fx: fwX<.77Lˋnc[U(zBaH\&ŰWZ'xY„UVCBJwOp"g,>!MC3)pt"g?4(SqOtf}[?Z-B+m_L!bcX;؆TQC[|0:.?|'y"9]~1|=;8[c9-T_A5|5y\ԛ#, 6xmb.aH4} +h$4 MFBF^DC^PIf]ZɨHAW4+E_:tXYBk}]ٸt=RtVz6U@ԕǠ L!Ϛ%L$V0K 1qK+>VXM粏7 mr .>EyxFlJE{t .7+wֿ0)Y@q K|I6.-KP?_NP KH!j0\tIjBҒltv"9H(a͐?k 毫b*m2q&/-_LlI%8b.J Q',<LT"ԏ4ft(0Tofj: 5Qʹ?6DI?ߙʡ#Ѧ\ $QLAi@Vf\wE5$NBcyg ݤrEY9H)+$_͍ "j Ïmu*!f?w nsE |4_J)_/iKM^Ynmgsٮ4*9/]R;H&Jӧw7axS Uu{!>e R3H>xe] j@Q$L &f <|{WO;MN% =a քS7J?>K>lge%D QGw*_,[B${<YFוʆi)8_OӞO!k һ; e]AJj@bK>JO~z{Ϸ=Զ vݑ`$ KbH)f~Tݾ=Z=ܧ":UC\H7P:*(eI["02damA Ӑ{6ӈ:NJlAP$lHP:2eS֎Z3ɞ;Us1v~]xd۟Cjy8؂'aX4j> lB >ݍF-!-QMt r"-e:i{At9ׂtCDYX5q?Ұ˳/xU0ʢ̨ޛLG%h=pu'9/<8!@:C\v[c JP? icy0MМ`Q UIt:B(an, GV"h 5y+|Sv^mJ:^?74"}W@;^E-#UT,?ek M׾GA57q9XmN\wF` S ۿ/Ҿ ґP@ Abf{h&hb:skf|mI\ Gb{y7pbvAဿjfON͢ȖK+DC! )Vţ*|+(Mt~ʟGvT[nS Hs49_`7\"zXI;KZެeJUmV^FpTJrvL9vc3q^;0| #hHbGrY^ AD"qI?'ŒbppCb`iξQƼHvѮx$q*u *,*13ŠCY#vXY߰y$񩠺comF6^B@ |6i Oe'ɱuE ؋9jd;GWsiYؐ[K;cpἐBr-^*;l=o`循M*.D^@o$}>_$zc75х%YT>y )nX[W}Gx|iYɍ2. j46ٷ0Ǔ?V]V#D{mh8KdݙQwůTnjɂS}6Up<6uP*R7] 0-8é1pTdC wՕlmEI7yIsc;W+A5>!J!Y2b<YÐS<*RಢD ƍpi1_o 2;MKvK<+!]ATXC Z#116`8-k >osi)`UMHl7̧>vO?u_zxik[.\m }Q%{.Oæ ֈ:vJa%Hߗz=7ڈI+zdde9|ԝ\;7cZxq>RȽuH%p^N26sWL6Tu`\` )>KCyG#(@yͨVK(k?Q."t\Q2w݆ f`Q5px⟆ƇE(RBi }' :dԣ& #`8d`SZyRx9MthKgKjS^{*!Nfjf *\N?+nӦPmEز\پY\1N{pXlH8/W*tlkK$ /B}@i^I{+ʏcs̛pbDc)Yurz7|R=QXMPm0zMl}8ێԺCu ! AhR z.4̔R蠻Lwnq\YvX>^wQ= Xo$ro;1A/>]HxEdIƛ({^3O͡-o#'rٟ>[2Bv3MAU gȁO*Q6Y8R{*W5TfM򜆩|cqd F-+D4DZ?4F+cyNRY/8a$܊0n ]7-lsٺ9Q-d<腩rQZ]8Z;ʍff?Gt k=| A]xJjDs]kd_Y[z4)yv0LSK8T5`E[L_1t >6!©/=ot;b>M|=Q \c;|HrSۤu?vATB,үvYiAWymq0hXPG|bSyWw-H<3K5%dQf>1?-r[.^Thjlr<[l #4ȩ-7麯F("5-*F)o16mٽ'QC3 M.1upJzi ]S2|G~1G3pуx7p_b1TJ?@MvN_L[Q Bwu^I^צ aZ TxϘlgu-nZΓ d}\6K;LQAΗ(8GFX- b^(,3z\v1d]򵪋}[UB8KoZ읜+\ l:w$kU\4mxE1'?K{kXN4~=l}B9dUVSUp}Lt>"C&P [j-F,Dy%ى)FG)CFxq#܈6;UKhdqE}2i|VaW1q*'%@Yک2[]!}TH$&PRHpX!BxW6][=}pAKqEpJ~-8 f.^yKzڹ!6/[LUX/tS&|FVD\fJ=(1EW}i$C.1E+%dc96} bN3AzFea՟vp%Au7YHήhcS|/F0V>o*_ʳLiK`Bu0^\*(΂FHYLbFܸ}MXA}ͣ2XHۂW@nW=X ՝tv} K9 "ydBXOQ2AjڨM1m+܈.YmY߆xӀ@G&91.I/zxE\{x&WkaykWEİ&{VIh ԟ#5f x,o)..c)EB,3dM'/-\E*> pBŌ5h05[60Fw+)v!e8&% YEAs^eTAH[0 ?#t IELK#7a"Щy%  (oB(cr}.L"CAҕCw??X|hr8YY<mELC}ۘ唱9ΕmbànYf׎ R,demC7x+֠^ k4Xc氯*P]pm҇VR`Am*j_iۀ5l 7M\}gIT:(en?pdpdD(^Q~s 7qMOprC-U8Y$*0B쨷ʾ`?626ʑ3:2R!|TA3]GKY )ӎFp,ddjE8Eېΐafΰ ?G`]Br+dꤩ&0iuj2dʪU -UD$ T솞QQd@ 5teu&K\,;ާ]<ډ5ZK&_5e-Lz(= RD. Xkiٺ7z(V*9(0yѩ)$!,f.b.RXMO m6P4hN[$Es3H\dn6׍EO\9 L8x]'Sѧq|0LNJ*Ow!g$7ɂ1`??,f?hM$/ &c/딦n;J% Eg0nz",ASތ\'?6tw ۱,xǎT0&.ĹLt!xϬhyِuO@XL3#~ !SatFa{Lב MF0}pPg*)ⲚYAˬI?|G:}%h9y Tea$F(/ffn #.I{ۈfXe ӳECخD#|@n 9kaoeՕ#iqC6 <ˋ0fJ @ibze5;{<'8-/J6ҏ\Po>f?{1^ueC D:$G MiȞn*+%#~5G=c҇(wK6mpm5̚ :6{,m{&̂gFppbM#bW;0Wͼlhɣ~i Q@Z zɉJqg#H5FD//#o8)ݥ ,q(rii&r 8JJmZqiT w,3OOZ&-x$SP6WY{A=K~*؏}3:S#vFHDOc"';oӟZ?b8ِՠa$__m:dQO/.W%WAjҹ\TSKx3|s͏Hh˒j ֞(x-oTJ ǩו_eV{챈c]ڍuWxp!U$aUll&T!iDAmx^+rG+尊2H9J8PjeP^WZz-UӤyFG7 ӹV ߧi*/fH/zl1:xQ1EFNW*.L=!f5h,f-eAi-3h3>bɔT2gv Wb94UG"[LRsvi |ԏA7]VǬ(;Ȅg:8aC%_8y釩l8s&UYU4yRf`MǘpqdOv?Vx. ۬[Af='ڧѳGS0D KE^rNK3Z~, B7\[Lz9 YfN)ۡ%tV62ݚ^'XoUZGp%'gcdb8'865|C=| 7hjcߣ6Io}DzV)(keyƥ^gM}oLwQA|  cAɪEa C5)0lOKwc#s)C>9:@\*Rea'CTNcsX凾詡ӓ w'\GOȸP.rGofQ@cŎL.+UG4i]t󞝙SH&ND-XudnYmr _-7Y6'/#k:f/kDiNqSTmj8$c]A;@!Bkj QQn)I3R/tUMp\#U{ץcl,ǽlJ>C#lKh Á^i3Wx?7ٔ ȉG YۗEQQ~L?W9K. '"`7Mz,'B)̞UW:苁뚫[8&ЭAGD:%_cH9杻FGN? $5_N;ٲ0l BՉh O{W/x pV_x K؆AA_%1󔛓ZGh+6J ޼=beF:>ՍvBi`LSHhš@QH^GWCQnO4h9Ű T6&bR#'+0QaȊϵRa]:YG3_dOskv[X(wG5`U0-ʏ7`Nj I| ,F OHLnp6 פ L|!h!Olq)VZiH9/k62"Tˈ> ǽi[WDkA^XGAT Rzq#B+C svrl̻pQ*l]lb9:Sv0d0eɨcNog@2ҔC0Y/g1j[i"U'vY3'GdR0hJ`܆^ X'o"/Qe&K3~zvGT cBy7FBl^JC`*2Ӫnx&?GQ^Z21Ts,tXNb04y$`X0Mj:&CG6U`]z= v14.m\ޡG*a4)Zt&g  0+[t[ |%vJ֝>ر/jEx&Pj.fmjWuIP)IX(6RE-j:)o6οD4LtCbm{4#7;L2|/sl tSwyfPZWL/ߴܼ l^`J,q8 Q5J ޏ臚Zq7=]+CR" i`- ۘ ,46Ɓ ~Ld ND ~C)60ޒ|z|i%+cr6i0 z?BBdi?~D5!?/"yPeH:8۝k { zT6vM6:pƒQ/x@o OUνAܞ/RW׭sJ~;(o13 8$ḪGCJ$" _b%bßXxyILژXwױtkhX%"Q{1L/}ّۧFxVN~`~^p[+Q@m%s"}?z}8:]AD!_rc%%k)t>N+0tکrI8bCJ/s!:N̝jlyds+"+NuwH^vߦ" \_{3ExG?/hB gwJT쏁䁰 3_+~CU4ɔABM.a]EI;ʔg~.L´#j*kQig{,^l>Ѷj&GF̙Imm<\T=TUbx?_Ҍu [|?3ţ1O^1 4tV}d'\m_/Lznx[,Fq/gۖ ImZ#F'ЭU!fI_oIWy^MU@Fx*6.+ (4H |-֢B>"%y)Я oO,`9יtm[x9y6p\%KU HlG÷Jw5ZW9v {T F7b\(Yow83ql*!ܱ5 EF$5W?^><ϩ[tS( !) t$MLg^Aĉh; 0.̷~Mq"*i}Xe~l4p:Ÿ]CQf?`:׵u.ـwHL/:1G;, e x (yQ].JI 2"K$@ڢVyK'95-csx;V%}mb7NIBmF &ト5nÙ_Oe#K~>9Kz:% -5,= cߤBأF;bOwմmcL,)EMV,5a'¼xoה7΁=6t d dDdb5a"|gpvdHͦLRr#j N_=?{FFŚ S4U$ 8PAD4ۼY9J-a3>ai?(鱠f<2}cCǎ~#ƒ`g``F4нlchT)P?Zj (=wa 3:r~UA| QD{WzuȊ*f)ӎ.ͧhu|T bcsAmBH6[ܸBHuILJ*WțG3yE\L+h1-xdeiEm `ŨguXỬO6+2Q9߃v&u,G(khSnSqН2+3fWwF9<\v9{`O{GLM[~ozQ?S'3vA}[7?z#`M[PDv(" )LgA&[ }07"S*O;V_~i  Uv5}=&WdT$s ]Y.lZpq4Xˎ'!i`Yך6_:ƕNKlW@_4Qu< )XrBrerх$'eߡXwo{)TلpNcd<&g;(a#%VsCQ1u,U 4³L:eezu$gDU9|G߼k&:JwH{GND45{;F)xDjLU#Mqu0ܺ)!G\ˢs܁\ڴn(y՗):sP!ԭu4϶0J;6Jp}Sjďf}8U dnz%seCXt{-s~Y5ΐxv+YvF4Py$hJ+&yg d [YfkȫbdϿf`t \Yƚ zY;AczwjՑʌJ !pn:([˂+gFts: RTRrTҖ[+,a#7͇ohۂ}\ONL< U}R_(g"5C2]Lp'=G嚚m]1) q!K2mu `2vg9{GYqH1K,,](SwTRUH1g()JS'?r;^ʰmWMJ27zƖ?G) ͚v0{Y`-@OO@llX2|K3ՎYCː Joç "@KDgEδT}*gTlԽ51i\n&tI0ΉɁsUglşHרj"]أuY@C d4ۡ[/ekiYqZͧ5c75鐒_!A٭,O=v'+b'?D9Q@3!dH`k33UafA% <ɲjDrVM_7#dY`?G{w7ftv k#4ç۞qĨt^%nY}N~{LcDsf29 LAiš&lEF \:m&,T1\ͱjA0j; y5WK-39j SGK.KF,?ī R1' Mr#t)wΊ(eN%x"yVb#ޑ8'ӻi-},쵹7_μuQcOO\K=ڭ4.[('w8.~:^_ZVبn={-HFp4up:z$W/k0$ -IyI~$/bEژ l?UfR- Wgu&>e%d.iUS˪+5>:bZ^6ÿJZ.QrK)G{F}jJ}5qf{Yz6N4iFS썿;W z:|cW#e#6Ppb1|`׬b.5f: (ͤo<,_?XK>m4-3]4dԤG Ў& UJF@3GXZ=ݝ6M^7O^.cCS'#$ڄz J< o;QBfcSHIS%ʼ{l"PydT#OznJW7]91k`7 Q8>J,?\=+o'ҥ򡿋 |%W"2!^eJn ggc=Fe,;6%̌ yR2~W.vL-~ntˉ){4YӍ?xmU#֬ýwɌU#&lTl˲~ _5؏U \L ,`U oBĿ?qŷtw Op(tW))"hjh]-Iw,s޼tES-\ B.noax]Ղ:8o?뢵Foc rS= 3yWyo_ךvq܍U$U룢0掫ܸlꀒ̽(3G.Nu`<'CɻVk51\<@,:BΙ3D}E,mݱ1*al*?ZD":}k2Ë]UnXVm= ėY=H|=12Yz1V6޼%!y[Qྎ/qW :eFy!K? *3$]~AA.v%1J&ۭs7TaQ2óp]VH:h! jO~.CtgVmjDp&D vkxp|ځ N7Zx%szB㋝8#(/9"*\O350YޥMM^+GKo|^ .! k1pW+CD…y #ttw[Wfac >@䞫R:rDGL2gV)Fpِ/Rk< c-`د{&X1&\ŠFc8:XcPpn8H)+34 1qe1t>H;@ oKӝo_umԑt VoPQ2Q0BVNhr!ڈ]>tyX zʞu*itsNObЈ:z3x=1Yu͇]BP-k86m+}Sxޛa`EwpJ:e`x b8:72<(<"Ȁgf֋^adr7[$D8Ӥf?T +c i%6ē)|}^&a0 TK|RnRTfL?`Eܨl=bٜsGK~ngJ0*27vwkLRpP-ue/mWgQ|?I6=ArQ)/-T2N4k)ߋHsm3VV٧ -^V.D 0o4+lGxNzu*={@)[4Yp$>`$d?P0E2&A9%tD>B$$*/ MU:*"~FurX&`S5rHㅁώ`ӊ@n*^12cT14TZ(8z2Я_((-i8h3dfgٓO&ƼfJፌ͢c ȗqHq&hM16K `xVazMX{Տxl^uo 5X탴.upn_ݰzLw`+$]w&&  ?mp/K)|@06K ܰȣ萩zω꼊$@T,]+VG^xpYdI_#Ѧ;P(h .-c[PFVPn<8;%JǬu. 9u[STTeh)]N8#Tt@_hOU1{vd 1pݓw'LNK٭?=sel{4C{[8(+%++@@Pc4|dirk*GALH}K]UWtڀLO/< 8 j?]X&X :&Gay T ri;2Nc:Je@5L3gd)ml+ Nɣ̞>Xg6cV 駞蟅0LP9V7FGCt40tePT N\?<@31X: {bcR$y[94ze9zīc!&j}^}'$hO 2aa2Q|v_9U'ړAbl)M1R@,K3)?x9IH8#yK+L#h&&c"/jV K%%볆RnŸ+) 6WJ'.'a (z;X"IJlz]/PB{+Hr2K}5>uhN.RYhr8_KBbV=3m& vH^ KwzӔ |f,L;R5  փaec +[y׈O:9CK%B5h&otF _@1eMg +~Qw;Rε G!Eٕps9ɣ}e\X ciiîGS359r_k8ʆCv `Q VI*cHUr15ŤR4 xb)Z x"k}, |=oCNIY%mp֜ۂnurTf'˺mR^z%hp ȡȔr N%LZưOY޾:$hTIh|8$4N G\/a&Ĩ\g>3e={ /26GFMKKi?)#C2Q,eCcƔb3|kdL9m~1: >e>-,:D6,kũ1-Gmj1-3EJ$Eq@JcA͙7dtg51Ro@//n(UWYA\TEycZ^iA߿gSJ~6/[*ZKGci-@ԇpoyF!.PTwN^DqSAIuCguRȊS;)[^ѹ!g-3GHJ:Nqx!EEpofxbC,^ٕ[,oĉz:AS룓gC3q{4ZX hz<q&NQ980VxY+ؔbַGY ;jjKId&ar ⷴ3)(,Y|n|esO_ww|$lz%d{΁c:gSΤ bWǕo6܏0hZrV? 2X_y$uEؿiɖ#E3So/җBбa&OVd«xF,Geb۞F&jwUg]8ޫz76Zҩ.XQe!zĞI~c*ކ&@D =NʻM 7lGcIf?p<$C$yN=2e'D̻# ·pNMoJF,B%Hzq<<_)I <R#k狞nTbESoMiߎ`$?~$K{1Fs| fWdwCQߕ6Ȉm{e x9xuAy;&+u( f/ n^sof_ \g@{))Ok#=sbwZfޭyje Kz`zpGk:0eC,T.0X+'+(jI79R@ӏ3/A[YemkW.Q|7uC|b n(I>*y{%|:+ WGv0_i9Mc6xk8@p'c{W~GlP[hq:- y}PO! (rQI>>雊{>SzG35cG6I&qz2 [xQN=Ab:fAVa?\9^LHf~uGBߵ89oLu>Fdq:{u 9ILʺ#l^2Rtk)FE]y<{l=:ݓ.A.-|} tYL%i7(RxdB̊m$~6GvwfHzΒKU& \x Ltzm ;Rlux K \Ѵ0/h4?##Jl_[bWSj)5Jb&;@G0[,W? 755fy 0m…nm|{ͳIWR:hlh& H ț?wl7BEָ`|^AЉ8] zm@.`F8Mj~߶ &,o;7]>FVir7H]Y۴"7E`W{$I ̷=.ǿOe23N8тޘ~R5!f\!~~gwD^| (^E80RN'sFpH99Z_ݱ? xu|iVbGBrb`Q(>ݕl&Tw\gR5;-)}DM\=ÕS|3!5& mH&kaf!z,v/!5 Xƍ:_}li]Uۏ Of౐J5$9]+2tJ۵iԭJw#e~+\`t2P]x%j|\4} \}qtnRb2n^w[hZ-vV5|Fq{u'R,|2_V>*l{д.AZ{^/{|ؗE%f׸sPs$7G3I4E]d2.@ǁU~<(x[v_o'$v?k,_OT T9wf >~"7)""tS܆5W*kSX(^)c2hK1= ͛-UYݝ K >\1澗rr+ч%[Ϝw 0~|[8s@GseVVrfךhw B'Xj\=iDSK+j#$ʾZr<ҲK ^ J2mIoHR~.r3IS_vd[ir)H͇р#k_95h507`ށlGSjL-tGFQZbH n]E+B]ع$ɽ Ɓ ϖp IN4e_S4 ߂ݕa~]xm2!ڵU*R@R3El_1v#3mé=ɽTVVo sv/$lfuvn=f0s FMo^,( ĉqvOJIlwq{xX q.}!;L T!5:ã"OzTG(*o? bL/\LoW Ŭ5\lH4Yزn!/Y2e:z,)Iױٸ`u0\r)—RM59>)ͫ)t)2 TLgO8wY>gj[\I#=#k`_И$iYa"g!A~gc)ŽGReQ~MH tNz C4̗6o`nxvԛ_ՑzTPCU(cylp$tL7$̓i2MhVY\vE3*MeVZȵ} o)']Iw55:qH-x1i rD:4=Q͓>Wh )=tX ^wCv Er- =tb]{dp܃2O(B16c2>_ZoݪUI%И>-B9/Ӹf\ߙ,,PW?B>0_Q%iN7v%ݫ2i΢@)wG8צXmoi3@9>TtMw em^W,9wRSJdgiTغsSR,JfI_ljVuwM2{ Bފüud?L.(5Zəʛ"U.ly&OxU P[_%?opFYÏ0 w0Uڣȡxl?#q/3*ϋu ED *AFEtT++!N <8r#7aF<̐!gif͐ӥd@duL1he 茇FOjIS} Bh*8e__q3VɀCy͜)9A9.+[(pqgHN5{90j Vz`M+r+4Bm+3T츃F! oʛGd-ՇhsYC"5QDK8}`ehE䦂<+<=<㓀C$FiUU W1yH:LZ}{]^SҜ.Ol '0(?Ȳ/q]U<)P *!^@-&_@6o K- őP?^ ׊M/s Рb0c 1;_Ļ*ͷCeSER ! 啡MDhm_+2J{ҎV:1*ٙ[@Iz4V}-5GUxg*.>QLf./-V=}ñ/^|\$ S|ЀU@tzc6=8h+$|NEiJYE,Vn%1q5M0hW&{#J|>05V(wf,d#lh/q`MyϊʷBaDAq۪tb0I*⢈g'0ia/O /jOj  KTQsEؗ=7Q*e[BDyO(,w)(5yYHJMty-e3)*MZX1Unϙ+}VW+Q_<*+RեB7\iA]g)?FD]iNJH=^1)+Sf3~g4l%9 K]f4Ao =zUS]C+Oq'{k"k6v# O4" ]SKi6P6 f;LsxS^)񬩕jvI;A„9]Ek[Czx" 쀐jlfP6mm'JHa1{M>1W^*K*&'Ժ̂$K˧tT3)]hBE $'Ϳbx{Ҥ1B;0Q#cx/r=V b'_dNJv1AҶ.-. (NF/iˉ9QGqp20/OjcaJE(NE lc l怴`3U":v-zAG/ T5G^:;[X7G n;ZJ%x!H:k032Sq5e[C;T nX3̽:kڸ[f~&s{&HlKB6t9>ß&+63i&kv~30ၲv+ߜ3MU]7:4H8ޯ6'PlW2(yG#rqQҲ!%GJᇶ˖aeN{ō:Y:\!(v(}w@O#h½pެ HZz2AU߲mX47Ph0T<+[EExQ |nE~X FfERL5」-@@>#izBytF;C%8۟8\My\{cbO-zZEhN[ƂywMTJZĀ{eQɡC>!HC.iuW84KKp301Bfȇ!x2z!\y!Zm,piqҵ3l2-7p1'H*:KP+݀ SqaEA/~9 ݆uByiǔTX =%ؖKvMV%~^!&}QbH9^XCV󣃖ھH*dXT뷤/osqhmeUځX qqb)oW=ry,NZ!eKI֗Zw(2Atj??2$8ek헎=: vZ^$b^:q4XKd!>F>U"- Ҁqcte ~6qT6T]VJގ Ox7H?N͢ c`<9!n.JROFo,e^ ..vGY OxbNM`<|H>V>ucl\YQOaDz4Zr!gUKf8dKcH ^̌e4N\L!,F=܂b6D=;Z&+bd E6a^c5Fڇ꫍%W* 6S>BG6INF҅2w6J4|`0o > lC d{Ӻ! \b8⫿\zHz.Ÿ"{4 Vk"m'޶ ͐Fc/%0ô |ǂE, >EUF[8z$QOrqHqu!+\f><䀵U~-O,HlFJZaV^4Z`TV%d[rId›9WKOG<_((opUEeҋlE TΕ3h6KJF\$n #<";ZU,6G7QI"p9{\7He4IQx}L6ܐ$y%yS{t>\"9!eI*gagax}/6l'=) hxA49ozg Wg>2`N\P(L[ Er64v8t~ C0?71~õ]eF3 ItNxT aY;g {)͏JIF>9kI ^ýZ XKQi^v]@txڄ_VUC_hbCYe9\X~( LH6ЈB -wmlCܾP ILMaBm[0N&5#X-g2ɤq'=k5o}k߫dic$87bAݏ<_JąkUGл1İw3rҲ[>p R\{< H>TPZgvK+L/?5sz 6cV_\?caoGvxab}Y*o}z0<0&@Új|D׫(SEQ:\Z]Μ+3CL)̣(ЇQ?>9K=Ѥ,\WW>z#Yƾᖤ"&uٔ0@s?"R^ 0jr 8 [t=Qĵ+:蟢CVL *]եe왺X\{KH7 uJǝn P>`fvHQ xڲu`jjJTW MX :.ka6eҤL ; N.wx UjFRyG?Rkɤb5ϣ_Y\fnN'ES9E.m$QFLSlϑg#-"̪PӍ96hX!9l8 R=^0.A}=#A5Y*CAd//7oJҏ>0`gZ֓KM̷u ;Y8i#o |*?"͕)LgUBңЂlRp'~K^Րsk1_=+%OQk.]0|`m)KLxE?@ F&eW6|Z:qC. ӌ 8L܈_PS|U^aϺQ L~Cs,-ج7b{,G`M2S JlD)pL & MZ.Hs9'!߇ٷC/2z7XڞAe~HY֚ۙT,}AQaC@bdS>P7Igb_<|UBv]E"Alx Оа>hALAH![٠vSx91ʡ5[ĭ f( W&{ŋ4]A̠wX?ଢ଼.ZQ",r"iPFN}t^;-Z:M3Av T9laZOuy>߱3J^1~`Y,N lZu7 /zr$_s6*d~p",t;w+d0 ?IDjg$_REСt .8=#0K ,آFn*P ]H<Ӂ#~wX5&-B\w0YLXFhW%O`d,P|JC!@g6ϊ> f_RK];MܮW[NdB|YfÙƴΒi16 up]h"xs{=+֩.q^v*;i N8e<}7sQMuS @Q~N>([1ZXD[%U`6x8<VTiܗRP[G(1 !<'()+|GN;壚vԤ Kx^7Zdܸghw:kqT$ۙtsM?DNNt)ٚŘ(ZM` g7ULKHl^\(V\D,Ɣ2eԐ v~ϓt.M@T]!DI~$cĉ|8}߷SSa'a6aYS&޽)jė z4Zz/5X+Z\U٬5F":JnΒWcӛ҈Wsy|+&29W9àI &JB{_YTrH6F=duCQ<7 F\Pun4wѣdx]Oɋpݙ LgA2S".{#m-R+< 2IՎME:B q{ՎFQudm'a+9dgm dRL*B"mU3u`\m${g}(}nɲ2"̸93(S-Ro[pPfͥ3d/ܘyY,m)|}w9wΆ(FHxڏ/ I"yef `fDɨG/~b؂愚-ѹ v3S/8a>gвʫu+%;Y4)TC++)wԟ03^+Fϓ7N6Y@q鐃n UֿLb& = :-v(Ip=~~E `.@{"9lBpͲ"AqqLY&yiMg4qWV5joNhc Ϝd/7J1(K *-s}^^M]Pm8nG4w`}3,`ق?Lh<Dٓ Q ;&fk:AJ { < @}T-?^+ )9Nfx8hepȠk7MG8rM\M>WyNJGL6Ҩ+;jUmvn+31-⠮4ytEP<[~YSeg%m'E.ۓ;sVd&2(Rl)6 @PD܇7 Kz('v:6WmsSߎ0=]Zg0ׂ.f,1Y ^gD@ҼxRb_kHki<6I.5G&- A+ׁf|&d'Ɲo hOvwe{RYIh(;/FCiRP%0]"|Jas['4T%^k y}fFqK=^4( - J&W ,\H+hUӔ|f@}fȫJelB0|7z3'!f~}4- [{cЉ5da ^%+)M2 ˆT;^dFFrMxѧĤ XP["A[R!- eU$0#bqifDV:| CmŶfl\߰0 5ћ\Y[oD7'"CxwӓtepAMΛ@hTo$Cu/6S,2tݼY1[Vg[SJM-&LRj[b!9$ Mǔ]vkIjNg?wՄ=r,m_/ts R=6?Z $ P1Xu(v/G|s,_C՛̟+0g/@\bjiR!>I-`e;i`֫DDdwFJCԵC$Uw&N͆Xz|o 2㉢_ҹ=_SnE zeKK0z\=odb݃VvVώYq-Z;i[,uŧPR)ߎR&r+DKZLd~=Cm(.p6"Ii yʅ\z)JV,{ip|bC,|,xE-%; $HU]V8 s!Wqf+9v/p.lv4ʋ|w^Nә S-WU(ʋX_U1y6ۭm$P8s,>e|@tAo,Ma #+'5U˷m4쑂kjadj2։!/v˒` T1hS'us3$d$)P5۾e6EOҾzٮe7k\Yã]52"Y[Zdͭ&g#V] @C؃n B>5I3yMZSqOo4(NWS k ,a6zDE;K8&=/kC+]AqTG1~4%A}T9T_O P`[eYH.qA8,K}qNe['.];SM2➿nԌg;T v~ʜ>HRr>( Vku|!q'/2R0"UQd7CжKEg v*VsnĞ<|f7 ؆֖V<]FkقlL4A:؜|BX҉?ȥ=Io٩oGLpZ`廕vyf?9__ WJ0-?M<rr+( d EXmBRH:o 1Mȏ7&lhЃQY7mɟFlWgC8/4QrLژm_^O0xg2nBh | W]q=\?AO ҫI(=xBFdamCjmm *0:Ib[ul)tF1TŸsG; 2{qt8dz0^־ԃ=<@|[/"3b]8~P(ߟAI7>KC_q׊-|sعpu kv5^4Uv`hD3)Y̓M&YRh2~ll> 1/#`bƇS΢pQ7+х~:|u&s$ 8%6uǥAsqi>0?QoBM elj8˸l%k87@ k|1Fz4MH>Lp*bMclR)$:* (g^oU̙UtTřw? ,&JfD#h4Ws\2}c3\5}t ݅GO&BeZrL[`I;%ȁry;iUNHzPshIVwu-șR#k ui5uODCC,V$1;w(=Z2'ҍN0s>`ح!=p绌dQ^>ob_uOtX%#DRk>,XǶoJodUY|] TY^ȱU55nRbt$̞ ȐDMw?4VA Uj F&H3eЂ= YC3ړ_EBUqs^ٻ IIU* p8zVqU1l'F3 œKȱ2Lν}+ԢgA} 2B3P9.YۯVꗠBZHp]%fūW3$:,ʛ/]Q:^Z7_mL#Sj`X5ٹj7(Uo ._@¤g6B;nKթM~i2E/~LxVn|vP<^(IE΀ТڧPH"'l? vFb3dѺ),\v@1Azi-ō]"aҠLrp7!]pmD^x?zkxJ@~9G,C[CFJ~' F:.ϰV0YmW,SA*/> !^\Y W1CPmϫ$AxQN'(UF'9\Lk!Ǜ ע5426m oX9f0:?䴄h@B`ro,wi2m[^aҍm1*s>?FMǵ¦c]XvyӦlAa13rRd) ͊LR~S_feb!Yf1]l9AsJ5\p6aWI{nS`)N= k=̱\Zn g *9 .݁yKLH.$8*{LлdB#|NYS[ݨW:4T [EJ"} pڱ ɒm_ N*Qp5b:ǻ5w6>ۅC!CWzXa#5p;%9ǃ 8WZ$Z=Xy8.^gfdSSf=ؚx̟֡Hf0{fJm%qmv#tVf7<6A-wH­BEI\ɝ!E{zS[%OeP7+Y*=S |7?>lq3(HuS r5Q4of嚺ZDvh# X u-*p wQkAY}i/Ģ 5vټʵ,4̇=\`@ȽT\HZ[7m9W)Eӻg݃:tg1O4EMx2,[xіApD T |VsLMvE 4 v ^CCmP.qMy5S?sM}BT -lrB=31 B5KU{q~; GDپWmS~+!쏧CJXM=-لaQP9zSU=j;mCv RxyK>ɚogq@!mt$<YH'Q!bjA:hK5?B:OGn0@mX&_-*Q־K..x )\òg,OH%KEGzYD<ݟΥHͽы>7S\Xfqu;h" JG'9v0e?`}Q–(͋j x&jӬ,oZl̤e-r&5kT_]IoN9E:$XNqh#n\D)bznD.N>qx771˹;wK䧕[y? ыI~xęɍJz9m\aݰkNzJd[Z6pmP[hR{Ր]dȗ#΄ a34?BQ9hq>7{IN$>Ґ/,ltq߁nR+[A2w;ɨ)ݦٵO2ީdI>~>r cf W8!E\ih HX{i6WE3_S7 yχ/3g\PJ aF-e}pl#/rX{ A<7=MsH*č+4щkB[Й|p^%5qR%!gkWd X,ebPԄ,1Ja9&Uҟ1Y{B;8jøHY4oGi>Lnlznb]S>'AĮԐ$D+R51ST#`>b^2k9^L]Wډ6ŵ*wˌә]Z͝_FEmm?#9sxl*~kZ%RP !WGy!Q2s̚Zv&vNrk&|INՑE?o#SGf:nw+-QqLV>K֠sޟ-3+"Ypbmp˅-ԦErJoXc\܏ ?gU#X(Q{ :5.jGnu?Z{P*;gt@6B2L8UqAAVcw)ߏl4銾ffKYh,sȘ@ q\qŴ~'kk׬BW?m=mO6y@؊Nğsb/BÈ0($ dUu3 k\#U{PtM֌ {*= 7: `_*t|bJwHR%l}v(?%u?'SS@kM);rl6>zSi̙-< zorOgщ6W:xWL#uO)A= *UX}YwjQq :zEֱ_R,DCF1h:(Q75R-: mN(81+NjGZJOe êofX@%l!m".i޷K*,Lqw\V*.q_Q[iiO/k dQ: Bl0xOUJT+[0Ŋ@oUf 1&|%]Yν]8 dlH{+Ŭk}ذ~6?}bR ңSZNՄ)!CbdMd*L5% T8yv9b۞߫ce[IÅƎh% ̲rA"Q6gصIp_@:sP$3Y=Vrr&oaTOR !_H6^W )W\6LQ.z5̫D[L"s~7pX+1?08J?ڋ!UMhai)/(쭟a1c^}:f6/r,Ѹz:(JpQq6kGZzTm4 C5?et :P1p\8;gμ> Fd7bxhB5m-x"\=Bb7Rk\ﰑ 6STnt[eMv:'YI,,>qo5e"Ja^׎.:;5)=7E28[~%?BQ.%+7)$如4}ltT*:֖ 1Z&IImw{-M %'JJv -L~,K\DžJEzUc9| ^ Y\^"M:C*Lt{ "K7ZE 7rҝGļs2}1T=$r׿9R2^; p.Jp7[AI$(Sblw?LrV*h&z[ZcRZ>r Y&B*(bŊoㆍ΀ꋟp!z-ϦBsęl5Yzy1P6]iH 729~8wB1,coʵS6''PcyB&%Tslzے#MLL]}OGg6ӌAIS XK%9=ATѥ`4n$x1Y=&qBa ܡ˥*gi7+U2jAVN(f%5T/meIJa7Hş`+7[S +bokDq71no+B6{uKv\`/"&|A0oSݸTHl6 :>SH y ZP eo.MKk[1V6:1pv4O#QY6r )'ΓkMRwO~8sp8׃(,oS{"~ YvC5cӲة ^9uB> 3 CAdzHL)d_ Q;a  wq~w'!o ZZ>N?b\rGe~UM .D@x+\ʏOOR7.(\Ǧ}E9ۗRk~d b2ƽ~gBa"2͙}/Q}oYIR ;Bb=SA~\yC9@F170&7oHJk_i/rqB־UiDZiZg1]>MqMf"Dl/eU@oQn)|dKUDY e>:&]v\@@{in`!<bY 3Zp# .1Ъ_hxLLʂUJ)/9_)Ar=,.GI_ IBf 5 !fV{!Xv{}6b8Y] /cNG"P%rW5UqRͅvRї7;O0\8&F|$ *%Nv;!ch!"rĎ0kL_cj;ZA( g+OI bWSL뻻}<xMu^\T"}''#IuuINą 2N?c>n +pȉ&Hx?p 8.8. Iq bN:|l$S@,gؼ1yK%e!!0 <ޟCpP>Ȕ_B&Kcx4Pڼ ftwґ#׶=pk6jOf)j1"D1~LTF&J3UY1ԆCi2dmI`Pk972#qL0Iw NlۗEB(VxGNOQYĉU W0fe 35x jz\lHneoէȂ>;#ЅCR y`pd?[;4:I,>`3D,6?~P{ջ޲LCx>6F8"ZbG1 ;SbsF?2Bg׿Iǘ_bD+z`6c_٦+|aa!DW\)O{^G(j93(SO ҌõMt]R6|n0;}A7;:.DxOOc&ZTiT6Ǫi6^Z\fgyOǟSf,xa 9Má;MPli~O64ť\(vefӼy8Ec}جSћxXG{_Gl{A,(9(s\eCX*'h\0מwDY=',pXȃLY!ETTI^zdϱgE.ws L4Σ>*e,x&#I 4WRyڜHq8 D䊪XSXy|Fl#*u܅Vn@Bsˌ-{@rQ`g8!i`Q\7n` 06.Bdm b[ ':R軠a;<)ϗٵeQXLB&H6BR+ вpeGofPZi3RTxj՘vYh` M-% ó  _Glf٫C o5[H!KY.ZZ_ɂ ZŁc_;yri1h$OUBRFhsHjNIS3 q?#6_6<l Kg( řo:e8PFV渓<]Ǟ|BdlpXYIm1f>~c)pW;.k7gٲ{]sFCT)[]nr ܥtʡ\,H"ϰWe2 %iP[|SՁ{X!wn}ovgUqaW]p #5\XEm np$%Lo5 5L# QvT"X0-kB.i[LZF H4ţ s\E/z*p_CKӘ.-tp5Qqbg;MuA\ ɳ\ БfP#'b@>M/!b aSʘ7+M&%dmݤ*pi6VEw7| 9n>!W01EQ$Ih"|穉aE8;7䪲dX~bo'_ȟiY&_yTʦe#5z{E9kU)CA,4ЀJڃX`:6fguĢ,_q^ -r?6y25z ώY۴Á8&ʫLևO]&s_AXwnP ז-{W8x3RE|] i-&ʎ髝ζfI6Jj*'@#fZ ~>윮>|mx6ܦ':0xa՜M\D;d-!R]qwOomG t`WM-kcWV}Vjob[zzhBlzr=}43rVbZ=r89T{A][8[oO_slPT"9B%(5PA!ѽjXoJ@;q+iNg%,vS~~"D~bVRFy.ϋ5j5ˑ؟;Cpw }sgLAKXJ/-qoF|$ HkǥY=l(vd`2ssA]%8t&QU*te7/]cZ]TG)pBw'+cD$9ͪ=/7n"rZ6J\IO'9Uqj)%,*4sձ?G<>v*1k,AdAiwa@tOYQ]1uUOew2@F ~@e3y=N8%558HX+:ݓo7fCW_e`ǙYr[X_-J)LZi2tOeƂTFR@L&ߐT+e\Kn!u-ɂvi6{RjHE Hӱq2D\CZ/wDy挾J"Gm> b $s+"OMkמ; ,VneK?skF7‘)O? ?ZH8'Uf tЅs8o94J7\t2nH_iSh\^_6d֢3Hq`J^]xF󣎔ݨhGQ5*ܴ-c(oN,ą5{`1cE+UZ h(4As|5i (}*+E=p`,SϬc,!ُ$O !G_c0m(es I˞WԶ_ !a[!p^y߈GO$Wަ9sPV֫8C7|D]R;}{x{[6_ӂmE/[9Z\0 >XF^JB -@rI.Q=h) ϣow&V,JwǴO\.TANa-D'94I\,,13VIUeH Z( ɉG^t{+9u;LVǠCcy pǔoP )A%}7c):% , L'g P|manXXJm!3{wvsfCIK$ g XcF?e({LnKl`dׁ(zuǘa*Zo¼L;[˫pdE^kejC T3I<)v!c)Ȝmʲ޷g<~"Dy97TDBLXu-3=H@GH.u)(17,ṯr"F1^]Bb3M#@^5+4n:*}oڐRowf#ION%y_]ZM)$n e3~u{;4cy&TQG[[3sp7:؅sboZeBYTX=1h1.i $N$.LN1vsA7~쌮AN|$ajR:4{gR ?!DgCk-pIBgZ. ;1, h s/1Vvk>sSsR UH2Ur;yTA.:Mu#*/VbnQH]>kGg:` Ӕ\N]՝DR2Pqq?^ݻ'"!ƚu'O}i굞Fn58e*d>D#"+w}|03M>юI}P Ak KNJWȿg"ZiFόG|e 9{1hltU)A P㘶/$SQgB62E`TA LVΣNvT}(u%D^݌a9?l3DpK܉?:S@V+kZԾHe\ǕXQ\}<k=3%6ӳzd4HD*ne^ 15>Ȋh)"TT'EoߐS= #׉ 1^ ƼKByuʜ?ǭS0NY;{N3i8@гn]HV v[ݗEvIM5+fGYQ3dƇ:7_}P=ڷ3 7XeVC%Hx|x}ᯛ*HDӊ/η۳4lNZ8f=-et'lxLc{.T[\g^U F 24z]a>v2'խJE%)\Cl[ai+q⚓<!0&uTؗ쿯W"/l$Lis VHj :^^ę0 &nv}ӊH3 ^*=ŸÎ8 r/lr^ #Rcr`c0k-XMa %qz,К/Y&-ԏtyھw1z }YaC0TR^x#LdoeG>dD-G]4whrƗJzjё7dž1 Amfɏ(9`@goAsʇqY/գ_o.L\`a';bX͵KEa}\TX.~/&RRO7m2 AO~5YREͽ?ӶvDS<O\LU#,Sۚ;e2i:y߬><"%ChŸ_+`p!GH,U^1{1-,EtN8 9C9Ɨ::?eˬE?t#pݿ2z1^+אr^Bo`%%; h+7V5^=k^5/1ͻ$6T~F{bΑąrܶHEZ3bBր8Peo|~+xjk4yS.Fx,:N&-nCf}%o N:*O5ksQ4U` b`ck(|KPw⥸:E_)Ė_n4N˜3MwsWUC+H|?s?M ,6|RԀARZfBFЖ4 08任DNͪ+]p:0W.Bsxk:ĉF(v ,V?+!^5w|O7g _Jo f; `w]HNxL5Q%]s~d\2.X43y+L~P]T%;CZ%ۺSG6Vո)'l?pbaL"t崶rAm42ᤃ]R&2spemݨDw*zb`3-kp|$ejw(~Τ5ZEc|+Ë98 5 =. `7IcWbBu|q?0|/n CPs3[11@0j?&^F.NYRإv8oO-"ƈD'yqDC<םeTc>%C2 YYˣה|S[r0Ѩ9Pr8Ut\uU~[0'6d(~7pKrOCiuV@l*LE3z;};Fh|w0C_r| D=T-њPn>ۨE&j봿{0 !8YS'MBuV%OݗD8mPlt樦#!xUmp+{Dypᛋv fNIOa)/_HS7,1DOܢbRy?uڔS ʩl7$GYqBS JLm˲Q/(WP,WuC/j:>?BYJS1HBcArJڄ\^"龞̲Gp]XXvzKмdWxjj5ކ[ä3rv{ \gOg4_ Xy! R*:ئWfC"0\&>5_7;-1ut1RA ]Zf5j_yK1'`Ażx+}@#O p\fG3 T&~6lBqw!9LݏaU㩱: Y-4z~rޖ!2^g41lzV)^FNi 84& mѧׂ):FAHw'?-|*>w(pK[BUӔSk+[_ qoMOBGshD0@ЄY)) m&3pUEbiQjc\5ou8ұ_M$*kન@yԒnb3n=֜U\P)'E$`6d [TL`юVJ"c}mԝ[Ôtʉ̫]o3ڑ }KA-FN/TskZ~PFwavz5[CKW+B}*=`5llh!QIgX#|-Z~SRLkeBхYbE.'ז#AR" `FLC(iHWymψ;zސi=7@ ?)g2zdrl/v#PmŞ7_@$ Y sޯ>8>XǿӠՒ@dBccUmܯ;]- rNF.35Q ej՞| ˙DԖOf>9P#,$ϢO1N=2yk{VJ;aӴtTWp+msrT4 k¾jLu:*N1]@I.XL7 p@vv c>YKbo(EIw5|/ף8>J2WiE Y1@1kg?lNЏHϛGfViuG8 <ޡ(鹽I:Pelۉ1 4 r&0^A0& la󍠾l Fxbu0z~A7ά]tgYn695ZUG0-n w% xveҴW\8 @@VO>|ؒ"h +c?h|}i>6eu :iXVx,I<UL57cgr 2;0P7/gm<76Z> wV:y GwsP V4<1J/7ۂk9Q&z˔b/{S)lz]WǒM;~ RE;yPQ 18>+fGJOIY7a,o܉XLa{y4C n|Mif<8yaӝ;?VZmI˔b|dJ(UkI U&PJSO:^#ѿl,2t"0 , W"X$Du =Ew񴙥֔#2 PS$}j[_Ħ8+{6&ҀM-Yٷ(E ]A{. UH1#0H7QPlm2%<+o1 *J4TԸ@Q)+sC!E,èU_Sm\X=},끶˫ӓ ?$̐FªrJv)ey0-RʃK <>-y?gA՟QdD52R)NzuQp*>_n3W:ԧtWuMřY 2SRd6Gџb`I9 AA83Ň:9!SKΔv-S*J~SLB+P+%L)!31Nݢ9:0+񰸋Αrvr =aoC,= ϐp㛍pw{S ٲ,Q bVCW[w }wk-Z=y2xp?=55Hb)3,ZbIB"}:#|umLJ_/gJSXW1чV;5SH܏%|a#oqzJ_NATqllg(eC~,AG֩2{CqsfU= <:MKe`u;]Z~~R,fՉ[(Œ{e3 偛hG eIgi"$rˎyHzc1ѿdxjޔ.S%ը"5%R!u}Y吞vqfVƈN$n'{}p$ r5a@ɔ"S&'rX=KW{-~g`4.z [~_1>y;ˇZ-0eS Κ%e,77Xu@{1v<H߲_kw~38%Zky(ΕSrf5S-(ݩ/55(̣ fk@:c;}H)]u\^ -Q^;ͩT/f|sK\ &S]ɍ O[U7(*m,ퟀoqPkSW;+8md%InF]*kDiwKwuN|,cjĖ;VĜchDI۩QOv{'ˀ4 H5sΏfE,~yh%&d~j`M_UrmZ @v%)1F ж@[6Y(2L'"\:'܇㰟c ,%jJz(KGwpCIl!$ Q$өV0ct8*8D:J!hu]7*a? tsy], '7hP;`qnOC(0SYrAS/ObA]w2CR&LXPY5Z~;Z't} ,I_ ܭ|v;A"K~\ }4(&դ^ ? 7SOj=[쐲ȥTA T!>V/6yicTS\bjwGtʕnJ83e%Um_|As5oGGG\]Jt1h\>Ԅ5 J!x^Q~݅?-U ^ջY7bmOB Zb;5A  6w^ 5Qomdא$ʋ̨~'o2?qԲ/gug4wAET cbi8{K˽N8"$Qf-\~Imn-_99 ˵Wxst$Jn9h!t9q'm1,k d5GmҷP^i-\WT`B,΢R1^6ԃ_v²ya(?B  -/d+()tMJČU{ :S u.ˇ_KVP;쾈ojoP¤@^X{k (!#fĖ8\߿(pyd܍c%A5d֧zpUt}uHR_Ez,Ê!JyTzyh#Ǿ|N@ 6:&/d 1hԤljSR1;_ Tr+P"afXHnXh'ך,&p7*./xp$ 45f6Zgj|SKQUH:j2sxd NǸL }]wGB'H;%^aM,ywFS `HO)IÄ@%NT7QcHpg"@s.%"@餂KHh.d-a_ ?wb';<.45u9& &>z #89$.rMV^s5he3ƣ$|sq)UUsBgtũk gqSe7+6 ͱWdLCfC(|־M醝-,i_[?6ys&]^WOF_xOHg2~"͝( "3Yv!wk'3~#אʥC'^!u-ʕ\(a ?Tq{bBzl#3Cet UuTSxp `XC% x#HURq|<.L|FN'jV^ÓX!y.bso'GXy)Lܭ Rw&+@dԐzZi$d>п?o TGwD%z4J{xH$ޡ3>di@%#Ҧ,l&L"=䑬9 &&I0d񱋻X1[ו^> r?#:뚤ܒ#j)'`]'@G&6),*<'}"FzY @l罥ÿp^ @+PjL7jH".r[En%v}Z>z4XLm!4~d m`Ysq!s>>|RZdcNSDMd⚛&`zS?)ō~5{~2duA&vqlN⑳`+Ū3l/,!v;53lz $I S.E5 M.5ح`w$-_pW$4גQPY$u=xB^=膇Tͥ4z3zClx l{DL+8(FlDIPleڐg,]oCfXˆ[TQd Ôl!Oa) Ke0¤^%e3"7D]oLؔu 呴I& ^] Q ?L0¤L;lcLx2(QG<-n};Kpv!r ߸1:/П"`_p0JӮ;,FG`ƞB \.2LJa}>U߳D/򒆖T.~ge! CW.]/> 5f온4{x}Su)#g&RdH chNr Jpk ҟGhU/f37 ǃŔjuS3AܢS8SϕDj7'A%Fc/EөTu*i.WYAJ!8?;o1UK6Mx _D^,x9Bf\ Pgi%7 u&>Ʊ?3~ptX%A@ ߥO;)~T=Q+ BE_QKԦjZ&9WO3 J[X.$.*o"p(ۓY =FZo1s./co<¾~1L iul! l2Z)TO_0MD\8 ^DcOXdTr\p5KNI)NWrQ1y-$ V#+a;风hty4LmER `ErUGij`|lC.%N !7P9I6b$)`qX]d۟\~`O+lW5chLA=b,M6WMUOb&ZO$00^Vz;u:|^i@FZf~xrPtH}eaVɑTh٦! Rp_.$u03۽6:%i _Ys1;? ~51_bx:[\t#܋W%v<50cQ$cdgǡlR&K3L]:wѵY]h^a6IaEsdk`6< Ɛeop<2YrB;`aƽ<2j* Iv[e `CHIei +A_Q&,郂6z`a!s|8xBx+=fzj{1h $$߉gC`b9EO*:4 R,%/|:YBIBGM8G.D=ZKyvB\-o2shUq K3pFkPhfin&?ZuP|gu lKɆD}I2͙b˫\cZ(;Uv>}CsHL\ ܚ;!6XyhIՎ,c0KM%^pԅ uwK+2Ux$L΂ɖB$2ŝ;H1O0N(lxb jUP6mYוu}]ɡ7 [i d|B;% $c άtFlȀ) \ ȶSW?mqv<*M̖~9mBJW w924% eF20kZ|A.rW5 "wçXYd"y1!nH}&ݞq8;5iĔ"Y.۠([x##4i6X-oȂjٖ.pWg֣' .OOo+Ts%ILY(kXX(>R+WWFcWˌ)'N[j0qNCJ D$` ^|Q_e,lJNz(|evrZ*h)\S_9~ٕ>KUGF)W5bi؎T|')N)ӳ?%:r ȓd΁3C Q&*R ާZc e~uZQqܸ|DA\2܎dB-/& cv("VHiC oSI>XN(mxR< &zrnhvsJ< ̴j g^Q-ę0R=K°Dֿ `dMC` N*Kq1v-,`O"'б;G68ycСs(d;hPnDKzATGUaV1.ꚃɊX 4=(@1"S A!q)-*k" bR1`Oz^ԂٜFFܙu2 P% XBjTWM/xd yދgW|(䝢JT1YZݨv"KR>N]W OI#6GoCJeW~RVɓvJ;A"wN>Y5IXvE&(yLe50il 5j˴-WADUJ HsLǃb>u1Ǯ^Lxo }` x^ƚ'lv&ۨ#L؈KKHx۰f&{:m`,7s] ƥAa@ >z}[k5Y>_M=14X*:9f?(P~;56QsDHG;Hֱ茣cX?3`23[!)[U=vh2 4#'3%nYSkPOKQz&mBM<>d bp2RJr#TyL`(3L Vpgy\u'x.sÇ법W:,&W 3X$_$ydBu *9$3iR7e1ĉI~Z_bWMRea/`m"`mep5x2b(@{b:p&H/~@10fqe 5w6\K*Jgb7+ryC{A-2?+J}(f,/O>|?6/ }"\ݻo\[sWoBm -V:Hfok2Gn ogb+1s4w*\k>M/7ŋL݂יsjӬN0Y_:'0,Y_Y g{p 9=Ix8:V>҇u=oZ \`3,`*9~;.]Cta|ď+C?V ~}M%  &u)%.є%zb "͂%X1pl͏%h(Փ!6~Ґa.E3_H 3k;X-B@whL{᛾(n֒L7,q8(Ȗ⎘dL)KSr;\XFw m2P5F[Pۃ3l$Fr%+V 7LZ:={rn½P> ? v3 }zLh?]vvtN֯[l[ў?$C,O.M.;9 h| wB8bo cթ'`vuCV$d)W~̊bgG&>\esitЏ?l۽Ck:7|?kywwk $.) A kpayo,v:FAK"Vv2 E7.2[ S/SWs)talԊǷ(IJk<&N?&GTmيv5J+oj*4AER׈Y]6RSхtb<y5 W`a٪qż  p+%0th& y暕x"nl#6pjhe!)RA=f(Pp4 |dhpF $f㿹{;;1{;~2~#nrQ^ g8"Dy g̖ tX]S?o6^X[,ie"@@dw7 UYfض3+ 4N'M<%?0bb 3= R PNіazSw3u n#x!7FFtf)ۀTVrWMxCmu'Qm xjd2*ٻEvN-ʏVZȥ19ʔ^(@9(߰d׳=q6%(F̲Ň(ld4myggt f8倞,d\gYȞ˗r25M.*5ULq !︬%8b=]CAr3!`sݱ+:fk…;E[6%$f,_⻒#Arz^9 ~aN&V@4BZ!kv0YmE$ڛ}|Bi6譡u:vP-r VE}DpΙ+X-52~9>t)&g#4zǟ$j"IJ9C.$‚PLQ;Ƕ*um '|֧PDx(/-DB&A~X/J'J9~*+WtZ{ fI\<95&Vf5i[ @] SP=v" hb?\<ރ@M8zq !@Vf ơ|Y y m% ҚB,%=P{dz~&HpDif ^$U+Lq`6Y۴DP"[e/]A㼼#$ RF>N&#RPʼn#{,izWg˷_pFiRu#7%̳{ܠ7!a8+݇5+YMjR<2Ghr CZ [8sy3u62D^ bHښ^|&;nq=d~aۺ o;#L<0E=ӎm5o->U-|z#@.}}=_+=1l* p[ lc|[pRӫ`=?|6I瓉t2& \Tw4]P0M^w\S5Z!;+b+7ԟ?^`w)db(p7jMv1ZF[PX.|f:SKl t*[٤3>ƲRKhā6isX=w`FJHƹXǃ:2͐mwĸ)uPLk~\h5oƒ-mW%ŀd\430TN_WHmQ^hg{tn"{Z|qgܬ1=uqjҧ_uuw yhvg cON 7HɳFgRMŅ&My~+A(A0_Mza#"vFkCIgzJ#iJhگ)[iԊuG{B; Qb9^F*l?DiOd)lݣEb0ޓ^ [-䧞c{zP+nXUܮw+0ر- Il_#I,. yzgcs|tFYQҕ<3FɓKSOoNqQcɯza~WW BVQt7XJqǤ`ʷid rXtśCmw;8F"9&edb΁F6gxXBP-UK$#B1o\;vܬ!v CW4zYPKMoE-l;|Y5X)GE=!)<@2jcmPf;>MFr 4 ل ZLvۛ\'*5_420'hF.,B[ ѡ-N5m[Yd`K3 Pf9nR"L3, V:+11SuRkC-08sI/{PV*}0O:Ka*t\M_H3i|:Z Y (XDy* 铡od\yZ: ?W Z;WL.nDÝ+'l༰mԿNK!qt4FQt5\UoIS, B^i ^wo]q-Z3f J1Tod.Ssa4k{%&,NS9=\-Qk;fz]{yT_|e?Fr eڏ^0#F|K%Mȕ A=aݭj=qv\jf)NqҘeL3^>8MnhkwDA}>d4 q дd"‡r0Ίd0*|UCћ6"S{vG x``J8S17#;Y'f^#Rh46lO:lAS|dW1z쯱VX$ޓlcTUlG)Kgxo=7KkM1yi[eY4Tmaؠ4ˆݹ6]̚ auίJFaCY d!_=@jN*4%u!=Dq΄4F_sVvCIE? NfhE9lu0n&`|!sH󖳘b3/JV7H.GϽҚ$*݂_(C SP;2g)SPK09KϦHWNM 1\ZjbdxX&5=ߢuX8B^=|Bt= "Hs)~$52u`ө='3R+'wbMy[1Y)RLy&$޲BR]|wfcu\mą0ǏФTiq}>_g]YnM5/ i"%!RhBz')1}*GkL"ytX;0+xvv@S0sAPzr"4Zkى6dFObJ' AӈݑmGKq]lE-ڶzRХk2nx;_fblpd"cYUgrAG\Vל=t L*':CjŒken& @5%) %n|ۆjKg[ YfO5~̂{\v)2`\WqgY:'kg<xDnڡ۞@CdOwBU zj$ Q~n^Q_]r !6}H'^v3 鄹{e{7%Vs ƌ6-߈Z)aHSc"VB%wR7'4)ElgҊꮞ0ƌ^ʖG!CZYШS#ʼW |1;bEA_ŰD\H*;WYo7mF.k/#庀x=kyPqMOPRצnP@WG]{K\]aJ)\46n B榶N2{WG?/t{_*NZ7X6jI@YH,*A E­Z!D /q1;v$}zAPViَƼװX/9 go!xtEyÍY|P4t+Kc ߯U= sPw4tEgvK N[>E1Is6a !!!SW%WP6RBE;GJY&d !&zAz*s8-O{~Aޱ݇Tdϓ s{gs(] o޹PDJD Mkٳx9S*yJ8(&p )YN O _e 2?A78UŁni+ -\fËzK 9y *{ ]T> iL2m ~#y\7kRܢf 1mjB0qG@lN[t?ikpEn\)~ꯅx맯Yh;גmL[O}x eEbiɛwσ!/ݞڄ ~o _EN| 6ؤy-o4)H06V"!񁶡9yX#R&tFΗjM=ָ>N (C@fo33>+kZHꆪVdhB-%_L o#bei'pވ\{U$ז {>hBVm&{}7CFiݿ 5iŰK A; .dɼb{Z*,&d|`.y.HQq {U,4`S ֕'z߸Q \˵-~jY0exHԏa78):N@KqPӷ;m4=7 * FBG0 ;zUF3p7gɇ*z{Uƀ:urޢXOGGMS`Jb px6A!]+,"!Pۚ@%K/>@ w{k5.cl^Q^B)L`lSc,ȊSACz[vCp}%lZ@=)h>J$nNMѵ (5Ь5j'^N^XD59"3Sp9+!РW]ޘ`veĨ*vJ` (]$x#AIx6P-HV/ժ͡M e${>P! ^9p$(W^ETyN .sLB3kN72r%!j=9efP.TGK;F6#v9[8˦dlKaSlY&\bK􈽑-E J(LR/$}F,帾c@g|Ӈ'um2`g=ĕwI"%[R>O#WNnt:D NKU|@%"WAPw $@Nb Z. Z s<$O(Gz-Z <).z"%W*I%<aݸ(頚zd 'le&U;KY[.qyhJQaIq Ͽٗ!wC0ruL.}5%]`+vJ22.g^C7,@KޜR"zKhkF[|i:  8noNe79qRҼmS$)*m+ ֓-s:,?]c/}~`-tEvgWj/|[yq순ӳ ]|{!R6t^<0e22.BjwѧגBܬi|UےVjѺ'CJYOn'%θaZpߪlx-.z~6u/nq(Y-c5^B$I겾(Ly(@/ͮ`}-Kє}.~Ѱ9[/hWZs%5lKmy8ᅠEI QS@A6*k3SGꦧ֡nT D93h$_rxpͣSg(c]6Iq7b8=ex6x$0#M}7x9FZ͡ɻOŒCQ␰\<Kبa)̤aLk7gJ\ˠ Y~?;*LcHuNQupIoq&e1o:b8G-P<}%pYc/?%~?I8`lw"q3j{}]Zobb;Ġ~`il%i7HHӜ65!2S!lzLiq}OS7!VϚ8E3 u3&Mpwz hm08?JאLhF"D<s_h j\qM*{@od>^~Pe+'`;T;~*y:]1$oئtQ6}3zĪ3bAGQ{^V5P@OTL39RGQI鲣[ٔ ?{DŽ+gO;Y?1nS2"~kEd Y]/`gWM!~&u6#[ 8qdH4nH Qewͼ2x60n|(N/ף_R; '#Ikՠ{ue?L| =Z,ˁc`)so(qH/q*s2)zj+ cu}-=u#\ƈ>$#JyDv2M~NsPS⟉q:Y9cIJW8z<>W,,^e12UyL`~řLnVJ>ad'7}uskƁNHBZ kQ#/mx`#2?-If,BjRXBvST +dWV(g1mmKK lhO C  2C/ESZDxLnȚ5EW&)N9V7x, Au%]b&:_`S*w|CA0=" 2K巣 cZ%|1cLʐ}٧4}ZPJ }OB Fԙ2 Vp]sɅRSgs(_砰`"]ȄyH0L=i3 ѝMw@a- +hg˥Xo̘2@m$W8b+izfC;\Jte rh{[a۶tBx+z vM]oXAa[bO%Q%Bê#+Ow 0bA|g5}X%q&p~??N$U8+o{CY (0$QiU9:p\ߏ ڻ}J#0\GF C5E 2 D$T)~<sXˡ C-%H#'Hc 0+S-d&BNӱ8IؓJ>T#XD]PZ+(ܵz{TKx`(oX{/UmĄ0S1?z镛M'M)@6G_Uk7-?&O'zdޫ!yڸ3N5|` kv1is:,;eNJs-aԧ`.$X*usd!ԭyvv|WH}ɯW fz/ 矄IU@2cJ=%,W6kOȲ +UlI89YEˁ*f=I-b .vkC=Ɉ)_ZpijgHҐa( Xc,2v~j(,u7qO҉J(Co i6c28,Nf &^C SԌhEn:|U ;d84d\0g#FV#R.x lW#)A* ]CXyEQRKxMNAʷ \L$u69l_lmm{ڦGkvNkh(>dW=ნbL*UmF2-8QTO$ܿ_C:"5}'J7+Ѿ&JNƋ_*mvBixPγXT?3/kW1ϫB`0Osb$: d'7fkаQJ"k0)K?R9dWѨW+|[{ >}.8&O/djǍ]{y'y u [TOnUbhU@1q  )I̮M˗)+.h~O"h6 Y uxF-hfu "I3$;|v]w(8\V>iN=3v0 W()J D3Y);^gr(Hdۮ8hfטn{x6[zgkP4~vv|)&$nRZiL Oح;K{4NEVK#f<X <{>hX=v#Zia E<k,6i3-5p=.Rs&7ēԂt!*?oHD o^Z6K z`*z(yHcRA' U7`o/캟˚~A+E}V֩xH!Xe'-2돔\In?h" Z!+X, sVXU㜍]GnKh= ۀ Ԉ&`>*Yjah3"xR u"P\v#_pgefUv:^TaA=.33|}+f8h2C8V7} !D7p*9xg{D>sď*:'C`lS=É/er <|]?q Laz\?mNMUkH'+gƻ=kJ00{of,*r&mt{ *cOlu~Rh}DSZ1a*o}dečdg2pdWE-JF<<:?#0YܴmCTk|¬_V5z xXғQ썗';5N.6hjSQCcEw=`mB0kߏYMr' yBgl6KC&{r4܄aK{xy;tԵtx|.IdCujjvRd1"I^)z?߯VA$\? 7K2TwL̲'u; htL؇k}]qw)L[ܙJ`uH8qK+DxQt?̫u|ǒrcIc;D$׵^_Jfpj~ \w6LYD4!A'+74ev)$onpl+GUFn_B~Ć}4j^j^2hӃ 2UX@ XWi07./zRz3ydz|v $è ST ja]5M#rq㻵.Q6rlRE}(xt~x59ܵѿ+)cKXC\mUMAb7Pbĉ |-Z؜ji٦`yUJW;d\;J{_mYz DӰ9T}-w#ơnEݔG 5X`!D=uv߅Kh;p< (Jsnx+9z8鍉@0lTEHp[ϕ~>riYȜ/Q1KCHO-FkUY9v͢:C4(2KC2]E-+2ʶhgq2%O"-yh6Zˠ9DD,&q ee!ZgыY0+X-$mݗV;/6 &"}xՙXt8=Z)̆鹦40nZ"zci|m)z/طS fưuO Z4K5E:=EaA$0ɜG4yEN>6wCgvo\c)?/2JPdym*pkgڌ"}&$Pan@Vx?kOei/*K2i`cW[?IuY tza# yȐL߼3t+uɍ%Q_Si+90U8~ ߛ,n8(Cљ@ߦo0Zޔ^U`JܞOf0 k҇WDSWh@ pNYEm[^@޶|BOW]>9L>+ <EB~GD}BV6{qUy0FE YO0;LT9PH|)w`%R-ݭE%Pt*$,TSbH-O{~wxo%A˰^?1U/Rbhqj]fqtIT 臃V$8;%h9LPl>+x קޞ5] l7(B2$oG|V!<[^ꇙ(=On!=-n:  8 e!)oZ>ӑv.@kkRPq뜘 ^FU6s jr 3 ll*f=#/0S +}V ́=.ꏦWxA(;D/W Eů\e&ŀ*FV\󯧺 ԗZ&[V˶-㬈wqKw!=hs׷?+Pf &:[M=۾td -qr^3O>r "Li[lf ·S|'Jģ_b}᝞o I).z!ëBM]>L@{ǀ U8`warp&ۆ>;%5R| 1T؉Sj:$%,h=4F7zto :.%(!OqLV0B UW)mjFV2oR*/Le{`U ܛVdYȊo 4 f]^*$.%c9-N/{֜S|՟fru(ߵ}Xoay18&hW݋N^ D]4f[}l'YN ޘ9MbZݩx{~WSVb%z^xgٺnT9Q׊ /TJz&m\>9flK)$M5tUu:ؐ BtcuBL{B/Ît3ã&;>%"}>S:_;O9qɃ-K%dhVQLqWxg]>i>Gy/ Cnwƙ\ )8KQdtd/yq;:lNF~X4 ,ʢIdmp,:o7; $}"Z}\cLވ=+szLgs쉭39҄;,ٍc0it\J}>'%1|Y]ZkZ+I3}+btecgْʈB>aU"@^sb)*f#T7.N3;􁂛ewMQoO UfZx1?MKWl34Z?R| _pxuydx@.{U?Dw;|oWo~GCXոoٯ_ˍC7DnWO*k͑l8J&sܻmEhHj)e}MmD; L5 VpkG x(+|6S^t/ kf< V OCLÓ`6ņb!(3;drqƬ z*?h;6mIiUw+X[D?]~uY_.nAK&}CNҝN&QD ׼^YW }ٯ'{OeUYcGlU@ylb[[L%(bJeQhWATq0z5^.*%:= |cJE/OX{2 T_d*V_{`ݠH)aQ(OEBڲ5UF n[|wu5'`f:Bh @fw*^CVbK:ޙi:? dQAϛXuE2f-Wvo3I fS CclJV wyi Ӟ"`S"ɱ&&+)1U/0^tmzfͣʹ4q PsZhcfϿRiUu[0p[Af=E}7QZ%[TYύh\z#StKCz%n8ƗT# Ea]>iSRv@ fD#S}=6c.{4{7sJ[%ϭZ9<3.SzφׅXr1˝}ǺAaBq5H| 8|6< \D gk@k:\MwlH0VlI (30mBڨ(-_z·xUٞoTO^AC +V.ؑq_tjā`'`1`s.v$(UySmCEmĦ55bbBAa7ez94ƘVj6w:!(x2wVBZIyp#xqG7=\j%4u'Y-dREU i)nF`U <@7 #05GQd/W7h 94x26{K vi{YuX[8lF赴q 3A-;m)fŜ"Ifrqg@W%Ӟ ܴo63Jh\iV,@0V86 (Jq54M̖^Mrn13юѰ;SNjBDٰ`-H6)"BM̀|~=XIpvHQ1mq4|3!g D%X"BFnGk -ra1{=ڹvGŦ iLzB1UW!- ,`t&o)AX[ِӳ,}`"E'gFvS_dSB=\V yA0hi˂|j)0vºxR  ٗDc 7 :Tk yH}>*o 2*պ9tcMy.-K7ϵ%^*ZM{,cFD"j$zj Ɯg(?(,79;NM3<5a 0) *K, m]jkzg:Wn 8 yq߳>޵P%x)ѠЩ3lfNd_s57R98Aq$QUaXdޟ& *ٓ (?3'\l_-Pѕt/~ҙ(#"t-ޚj~W%Fd˾B?Yl&tƆb-*ᇨFNV>v7+ UD* ?h9t 1 aNvUAEcBΎi_'GfeAc~QsUdա\T&%EOثKϙ,s3b/*+=l3 >zSbkM6b16~roqEx9\A:~)oߡ\RJ7*s&EM_0h ]{tn7JzidƕQ:^Lsrd <*Gj)21ʒwtF8&ez{ BXnW춝, k@n2)k1O*eSc)ƆX!TfÔM_[F J,pz "h$S0Pt@S=6]jwćiR(G L\IJDBz,%KCl4\uTM k!Ҁ_sn : =2m#3],6>tXק25g үzlKX-EwVZhڱ3ҁz"54>Iavtw*xʱ@{Hl7 aC$ČiTq$l:?Q`A4Uid4 n;^] $bh¯ff_NņN,O(5نizn#YiP),-H<ĕx˿KӮIcHwkϪBPFz!mv-iv2?#Ic1M]7n!]Z7Ȗ0<9h {u%skY, 2RRpƜNuzXᚶ9>*/F v= Ӌ/ &Vz S|tx- 3Q{;_";d&E5 q&"PD{{(fPXR8GİKfp,#)86Oןm BX5#$*KD }m<5Wu+??F۪̀$f1YcPyCb|O}1$xPԜTgM1k22i7c[_l9MtTSqYajlFۺ[X|f&tj+߅Q5(2 Ň/(X佯;A;+V3EIjPW O 61Эh$0>;h+ *~BVZsi;1(i(<~ #CմX3羛X/l\lPlF<51ho^##CUЉ{hW[H%("r(8*K +y-?KQbLAWolLŌ9#>HQA LwyqA4X(LMǔYN5QҙҰ41ƪU[/iu-b>z}R||o?0A&2D^N$˚ A0e@Cht'$FyR4Zj2mLN TEYHC Uϖ=. YKHdcǨ_ou_%RYRXyTzc0%C塲 tsR ;HRš.΃ P_(sn$Tr9t5ӛ/4g_=>Ԗֿ56 $$1P,߈'!r%m⺝L9GkU_u b;!r"6NSF mvho7M_s BG% 7p VA iVMk х?fl+^{%G! ]J M`ā0dYb;~ L'k[ڰg+_3v^?x8>Kre`r9UhH8f ҫ̯{01KuF %%M_NJ|[tE$[}@*ͥ8L%'hST<{¤PH^Dw39V KaR׵ϤM@Hw}<9q4p\EAquC(z.JJX٢cކ1qZa˘D$Shd3'h-lheVqe+C2Xz{ܭ1uEfqDC{mB#ז-%3H@j̀7W(q5Ȳo=-7"Z6F Zjv5#\-{0c)jK1뚟c a=ؕt Uv97gy_JYK߇ 5 -sNV~e *v'֊!OSg[w]=4WԴ Acb0VvO]86@4DE/+#53|lU|bX7Wt`{~OYx+>=zhFp}^I~jy8ۈw:ܑGcDuxIHCaus=%A;aM|eI^[qj)\oŜ&[FSI'_vҌt٢`(J6a%aO_V,3.&>֥:JBkbG }d+R3xO @t; :f3 ޲~ʒqHƝC`K.CBQ725Eڶ4ioF$k@ = e?sӾ~Ý_ZhsRN(/uxHS;˂ted t% RFܸ<2w&e SCcG$,i18i}i HW'oV.(fcYJn%q 'NqʀDB `/7xe]6tjIPᥳJNd˸a]*:5L@2n49˥}$A\CT,x`4sp *Ga^?kLRe=ֹn}&hgivp<fp{7Y>Y\Z`5|:9llUJLJ IH-4oII{8|;[ ߹V(c)zja~k-{1܂蓮xVAc宖'.erLw x2 F/e^^}L#ٽs8v(5pKӝ4m,gl%Bmpi  pɨ9A4`\kGs`]G o0؄&+q>i&*H#kenrti;9iԋ {‡/ )ju'd(x!c=KCٛ%Z*^jwcm>e~s@2VE۹I1,Lbvs Dur7l]HZ@])φ3/ʑ3 G`dvzŻ*bzKi aPp@`&1: . J>`.M3B7MM3USڛͷi:'G }n(>G Hj0$#d烇G0$Wm0_GƏϚR1~‚iWE%4ve[Zʈp RYJוQ^:c%@aluMRF'E:kt^z&h5Si)7ܝN#L ( y, ={8ҽ;LL +]vٗ~#bh΁x1͖/W(*K]; n%:$KԯC/"ә>K}CWF vh"69q_&^CsXxd)QS_v>=9`oV;m S ڬ0)lꙎLЍ>t1{dT~ˆӤ1qA"3$9&Ӓ *8.^TXxjplj z=WE[aҀh0Fl|`{sP kA@pUd- 9P!đE ^k/~48aY˭t3kMv@eeY!.I(u1OTl.qv&5θ1ZgmJwQ8k{8e_ȊI.Hϥm}mC-b"娩~ieK^c;Q Us[%e(SaPmm8Ɏh(l80!hPtʦ*};/<2ة3׋]Rrө̗L/6h<|wBKI 8bRd '+G`կg²/s ֪p^#)tЯ W5A\>Wm b?WXEr > gC~I/[ݩGˆ$kq(t>p-٪[\K ^E09UNQ| #;Ow<-h4tt_3{< M`Vd\Nn@IJ.ºisĢADN`ý%SB;P-h}3܂`#H囕PɉĢMS W=3e.T#y[Q]! 5rCs@8q&?A- 0'+Ո86l+8m>ٸ!hU2ti{rӆ&rJx^C'!Opٚ8b Bx#~ȴf3=4zÖ0"̒Y]E/ҁTjQRFD`U}~Αl[Cθ-ndթ(nab~9N݌v5~(-[m~uhNsZIlo ]mgGW 蛳V!e"#+rQ,BAv/PR.ryAG*pd7 %Ud#aȮX.Ss^GbX0/.LQn4R**`f-!Op/6ɹMOU"e)eBeb6Jv{l8'[u~yOG R&M ӧ s̤t E%9W&^ 5rۛ5v|8s=|61lx8 ;1JnJMpB(բ >]lZɠ-Fw!w{&W,%^%?ӄCaB@ݮB *!9j7{]mnkiYι*SHg g$5QRȼm*zd`E@K/ltDkajVա$22`  = "2Y)WS0 $rp= g=&"Gx,hˑt_jȤ'\s]wKȱXk|`*-V1Rᦺ$}%-)pd g}!\PؑtBQF9V%˫E*H;V[I" "*F^ ^APOS8oxKZ8a;Z@v;ޤΒ_MVk{i g^`@Q/hk+$3x $:u|'jݚS߿h@Zn$l7IqYxtKdOp*U6$͏N"Ƭ͓|o萘iIV'aZm!'?+PoU }uit&c ЖQ5&0w/9v߅2^XOhj>uAgD],yAxj[ Y` Nt:u8U[7_j9OXo>jTאRˑAay^}9P\\0`D7iJTF*N%zp0Ev8E[x :Y^ƕqh >e͈8'(k o! ?[oJWazP *cV@Р@MX޹QO`75@8x1se(^Z>Re ?5m_^1GT8kELt~W zv}s:GO-9z#֙"S_>9=p^$d}ΥB-嵛_}LlfSJ3uޣigpJ{T y۬':N Һ; `Ӻ6v5oiڗ+}FC\s2>Y(idoF7̚١I 8VNV6?C3`uqST9]aSWOQ/qZ[tH /LDDՇ,cK'жG[i$9:(Ow=9iN=ZC g _6@Me?C_+5s/GGeQ#-Z$K'i(ja( =sjV7ԫFG0U pBoCk*DHwWG MB3yFOY!Y30Cn[蠇gkI)@]Es P 0^om0#*!Y"sƮt"XsdȪDnKDIO6v,JcrKH1YADʽB ߏ\rK3;ZBn^P)[Я#SBI!8VXѼx;dd,yAhuqL+E)@){>+i@xIf[1,`38`ςCn5bwGYX4fʯB di< !?=pE0)]+b| W=쎝,yhz|ߨ2. Xhdaq#8~vf/#^fEEhx~4ykI\攸UX4q?:,iV;꧗b&.!7 !myTpnr7pk?qf҉M⬔=!,5;_ 9N\J(GfXЫp1V{J1r:]S'IV=EáB.,5}#;Di<zf$s*SO'2G|#Jh`J^{dh;gfFf4=H*;q};2! <8m>LE<ܓ"𚝄6>p\b*ĥ&#x txٟ{& H+D)H/FMG'*X"Іxn<a,C,ɜZn2O&L[j_ךc΂8"$~'w&KVF8ï"%r_LD( S!`:b.y ;G,ϱ\@˪A#-w}~]: s=I&Gt[ ^m4(CkByG2pX{Ƽ ;3I*H)"""2f?VZ0d%a /(0{dcCrE"AM[)*jjhVN=?) #DGVLBblz[ȳ&FS!ȼc; /s[N/SMD>,\O<:Rؑ(0Grop]P&؁46Ȋ%5Gs% W@Y{/ h5Y|6&yu)i6DZLb RFh۴1ֻY tҟi|zoa b[)!i*J++ )>Lh⫯2ZE=.1k/Uh/^ i|q6bd{a_ w8Si7᫃M?!7ɍtcErarBsJ蕺_|٧WG=U -rskZF^JΓImi |13 Nn//g%Y&tfa QߚV!uxUΈ;wR @Z!ߋf[HAt&n &;䷺ kiu3n\tщzf.V`ޣXew,lF[j]sD{ ZofSF`2RW7+>h$wz*o&!D9~W;ӊ "ߺ{~x\ֲ(tVk;1 `pӞDp6gGrS6XdP+y>{dfu˧M\&aEK1QމӶS,e'uK9Od*E 5uy{@c%b@/1ERrIb?3v$FL+QJo>Zs腛Iǖ72O&V<ď][OcQ .x3fn>0m#N9~6-"k~^j_N2vfF7 K^7!ӻY3YyF>^ ^)n..,0Xot_ .Ƒw\`X ڎ96%ңNJ!ZH)(̬'Qy, ف$< />qܧ,;<}$)^_؝5]vQ,ϥ& [;t<{I6g\(!@ɾs۴!WӀ>З;@^а#>q!/FMeę(Z$&@s|<3A?0kTjctkgj\B;r6GKc{K.i0WYC&{pMNw p{yCikyed*}39zoXI.d3\-23rd@\6x]oUONWO i[Xr*_ϘXS\dV+1%*DՇ( N봢} ~J}/kc,Ǫ}Ev|Ȯo#+(a 7 =Mhifbkrh@$hq΄w&yOF*ޚS'GXЈp}\Rpeu QFF6Nֈ .P< g դԫ.PC`;cgUT7LX.Gc0tQu,7? U9@ 3b|sp~˥V!o+nLP6$"yyA}{Z%%QM 󂏮 HNh)Cޕ+Esta}ki69 9.롦F=! | W`>W$Vnlzd(HvaDb9SHuE\xǃR[N[Ҡ/(XscQ53lk(9Ͱ*W8]y~V?FH_x$ QTsd0,è()O*]p0ih=kKc? Ii"$9m狯,~vuq\Z1M1k$>*FOp_)N(h38Y^g `\c}sh/+pf'vBD'I{G#y ݫ3@^SwfgLky\ϗ' 9$A5)~{d8k+ܫ 9{q&o-hF{yN@H;ZZb*,K%` q;X$j!雄~FZ_ <.*N3sp̣Wm>9b(+ZHCʠ[^5ܡ3LԈ^q],z"ʢwɕWłPhPF zmHPȈk帜Ғ(J}(جCZU$ӼiS%Kr!>+HS6'epr]يJpz\;AFl:~sPGy0 90hrvybĸIxZ =DŬ/0bӽ6x}u}:YZ<™x nmt0 409S ; ImAʛ |%zh3"e1@h~ t"&XlL\R<^߈D>f˖^.͍|Q]Ƀ<˨Wo*ծ _f]P 12]Y̕pVx/ [8{Ar' zeo^EM?٩1 kV`,uo;1eCֲ-:Wq9A{ҵW.񾊂^󵎧q8,7E`V)'\$ Q:s?J!_C_o.Cb׃XF8f-=Zg oBNvA6WvM8WueGo41GI}Z\ScC"I]lP{Y3v4k)*ٜ^9˥&:tIYck<{gMn֯8Yr:b{ĸ%z\=/~ Nkw85ix|ja>U=< 6m,>¦<+ _xΏ{is;nzh2$|+9[PK>gaҍsV9,xv R@v!eݙ U&n؛&m5L)?q:= yiAD,O$|,?@,: Լ#QyKg>ʂSs@C_|Y<:$Mm_۱!,sI6铽|>v_^Nh=tWqmG)#%|gA~4"d2ӻRg MrfȰ0Tl.2 MuֳupB.3WUPKs{pPpfۉ ~Bt([h ͙ڭCi6`ugrBh$& _M 3^Ԙ=VW1P%Q7z(=>rwnStq*C֩՗a7"#a^䜈#SC%d`Euë jZǃ;8 kBv6\,(]toOz3X׫`lOY6{u?dF[`'s¹ ퟱ@ZH\&jQތxB nKXFyM+bUqɫ3Ũ'IVP^`FҪ)MG{ .MM`2Sݼ0j䯑NQpںp-(XR ΋t}6Oϻ,2-ii[j>RzWY.v+G]z$^(9K8TIl64xP[J1\XdFVGP| 6 $#Ɍ5[ YNG <Y(M's2K}XkaZQ #S~ۜ1"fs1g#Q<:w/CUk#3e &}#of4sm;-u]1ѣR#w)Y1t"_:TF/f ( ]ij5/5?%Fl.d"I'@[J)|sz @z;2"czz:( icDH'2I)K)M?#G+$Ϫͽ ~EA[${+Ŗ~F1ЈDxkxFUM"@8ܯDg`ц,#)N $lC= f jxL\]EB8\ƄwgnssSdHG;ls17P(PԺy\XHͲ| 1~3z6$C Z1lJ]¤8Y(;;`U-B/ 'ZS'ыQ aEy9li\u,n)i)\Kލaлvc|L|e*,zڽ 8l9ΡVcswck@``Oe & jL2d )ouEuͧwلćӹ7/㱦֭ei?TgUR%L~Fr^h|3luM3غa1rYGw` d_R1 /] =Ħ݉_DcCXt]O7i>oovoR" #Gγo @VtM (z .id a|$\ 2#3Z8wXJΗ96k"(\-MY.@fm**f鬴/F(6kE:Ppx|hܗ\-?B`+z@NEnP#8~ A_'IZ F@!T9a\a1B*ek=( 'k WDUK.Cp-Dhӽ\ -tVz}S GG¦3'޶)wJnv ]C4=~{G޴_ Ut~F:+c[CeE_F){rtHWY (ؾN d9??_<6 ޽'w pdpe{|)̡#BY[ 1O,5qTi_seDZe ʲ$VÃ%jV hN5莐#+v29.wں إ. ;}`45!z p'!z g0U[77s/®|(cvsӈ%_wzzc8rP нpL/:$)-rӕ"9sT='&C'.1cAMx,eux=nFy27<8u}TqQ!rXq sQKx+$C+;"k0-79Cn $h<'7٢Hu.L^*_= %pRA]&vS, zԓkћJhvIt r~2݄˹_?*ȍS]׳aͣ)*y$Y]~E&<%s4mL@?ґ[c4G1.8i++и"U7LϾEЀKfUH;$دX2E][:ZGGQc/@Վ!v?k ׉+k qBX\Kj5e|.`6B EYiÎQ+G8 ,:l@hu16T4CBDɌ˖.ЁTViak"5Dn ?s;բ&Qa+h9%7+pq65qC{$ ]&wAHe_*׉Ʀ#DJC$~}l;fi"g"|$՗Ud/̀ T_Yԃu)D΁B;䏳($?ߠ&LygLrY|>> duLQf=`YLuQ}TSbwK3mdz?Ҋu AuRQ]" sp Βq~:x/~vTTsӚ7xtbD~.gʏ0'qaCm=J|m(䚷'?Nߏ=/ ʧ|LFœ?RQ#YZ!۞ UTc (U#.13S , " vE&.Uvo[GۈdҐGiE ƓêrgJyʧ*%>$;eBU@H%PpM`˻ƭmy'N~&ȤT;MjG8ߛFX9,WuGv/=BV?/-O ) ]\!xW;EGTpfmӶk~Vgqpm/q2SD}d{O[ 6B\dZ =wƜ42G a!~3$5ӥ09jnw>- lp&6g)3,[ ˌ`bK"x}DZ$ݟbйo/I$#bKaxb+|"Y;%>Ršsi?sRƧ^:ם[yjj_ӍB8DbۆG(NRzzRU3q٥6ڨtZ󒨑1K?y"f@m2 JEP&A.}+H9p O᫚0r)Ns@`, RK}ɱ /Ra"WnHS|~u}+˿~M /iWL [VيeKGQ{J0fAYO4d~.Ƞ\0P޲ڬF8]n91SOSbr:HR 󞟭T+˅dWq?μo n3ſxc`nٺ!`ҐhNWfw"Yl, 3{9C53˔yKt \¿k[8n+6TP1cfʲ0>] 1gpK@v$hSj KSeQerjgBBU\[.U$!49t 뤀W`׼4 XX??NsR"y@\) m*@ FX -#xBk>芅 &q2u]PzWA<3xj O k`.S6ee~2=E p+'nDP5~,d@Ig@-V H|Y&l!j|s:!k=\/nr75.D6gY9MKn?`2k(}f0_1/dR_wcŴ옛I"^DP<vVX>=S@BHshg(uiq)r|3;~';@҈R#&fJ :μ/]vgGF8X>`H9 4!B%,)PV`nE&M$XSl/ fx7Vdhc4@s ۙ>qPu:  G7[˒/ݝiJ`xf#NI)>o8{SIV eԍ3^ 2Ο;u] LX#+yrRUfuwi :pThR .xI2r4{Eխ)M >j 󖃺}qeVVS$-W?RQAKpP8ux?ߊ H|%ߟ{ b6V;`:rYF7wۜX}jsH;_P'7̜Lt:_HWVWڏög7>67l E6W9j/"z{od (SvK ہP秅"̕B{<-مV4b<0y AXI5Ӡ+0Xe_D?Q3'rSl,>ڙʽȊOA™G&kR o>ٮ1Owvl 値S6>xs'U54/d gPlV.Oq;bRSiR/@#Cq]$GixX+3^gPjP5Xch#os{җv>&܍ ~:VFg/f`> VLptQ]jZ`<q>l|;h(l8īFEIۨƲor{ƙ &_l_ ʆ Hd)ߤDݬy27!]: ,ʇ$NJk,X*{#~(#+jFoe[p3E]ޑI.p( So+XK ZWG1 **nܫ[w9|F/g!xe*ԟ7M@~ +զ=e:`(O'Y2jvamKאP]C1rl<5'ɱ`u5vO %FohHqAJpCD%x^ GO30j\2p<\1-<E܇HC8炖O6zwHK~{붃l[B_0 _x1&ߎvϻث$v8mdA=K~xV3|֑J4d -ڻE:]wp$bd `+W4a/ o >"BppO@ٙ=/'oC񋯠EkA ! zG.M1yӋKhH(`f&3p7+"f'XnҿK"h*\\kR.mu.rFL{!ry~. 5;'IK@l`ӽR.! YA<0zM_B=x0s:^CaΌu!‘]4sKOW<w.UO>/rw鼕O Αrr_61| 1asN9"xOaPjв&X_͞0E,oO/(%sPGw.cCe8}Q\}K٧bEa%YV8X;$e3-7Va)ʩ)_qB#K$Lngc51PS֨}s8G18~ p:0ƪLS~w"^C.N[ >l3DmNr28#`x㭲& +A账إ< Cv颌"Ix=X#$W闐nL-R+h`QkVpFS(oB9$/`Z }`z|<`0ƪ  c&PM/gJD~t[O-3bfԑ q?QbK2pf- 2UgbL*?= =&cݵ/3`t@e) )AF8f/J >?TNvUrLztڨՑ<^O[z^Gai+<à^n~40FlyRRVt AǕ h/V'⣞vEF`Ut~te,[>0ΒJZ Ea =A8mԻ<(퍼?6yw[٬1p Tl06' ldIIY=v?SlȲNs-ʴ,ݛC\z+4N1l=x~bI sL&K3n.rlKNsOðc`۱Ay{Iq\CCq2z]';ϽHMu YLe B2ygpeWgrm(6Rݻx=ewr5-W i+)0g.x)%kOn+j=i.j5s~\gF: G _Ȣ G'2^쁯ټy@rRuM}905rK!K3LdAVY{DңejpR^@ѻ9Ն>d#gL^W}*X|F5h5gêa^Ag% M0ܱ,]J!Z`9]#&|R#J-Ol طI /a􄡨sSyoGe'40`Snڲo/(W!őHkl̜mˑiߡ-3q2䬑'YRJL3\ pnseLMsͰ.C@-yJ,Ч&wcetNH|kf\ BZy5:VV#p2${.jE"1H-u[O1SB+=|m#Yu^՞{=&AE;B<:?{p2VX4P@q \1)/eL@ƱCxso.R!JO),ftWkȀl5Z^KW᎗'v6\J5`*=GBe1ذs> YI! <#)OP.,$nAi[Rd\M,OQ8;oF3#KiH,W.!]LU7 ?.ȱ'BSz? Q6Oe쫳{<iϠWZIL~iHG0mT@ `Yݱ!Β4Zjj^$5 c1Y5׿"%L;ի#%)w_Z*v @4ĩ@={=_f; tLE;1$:RV*(=a ?PRevo0D3 3淖D(?pŵg(gi|hxZY%`i6Z+= X&|"9k跛IPvaV3: hM5h%cYmjo2Np Q։KpY~Ƀ 'v2{- L\{L7V6@}ڛ~?#JtC|gEpk&ۊ!*IYWvrh-T|Q+f9%Om{D)\CY5u5%(sݷ"] ;u%;E *y>ܙY^g8.w#Jj=)2N~ [.QxsI/+9R0q: `goZ@|#3]B5!5 oD&9e!|$LlAh t5PѓVyEĦ[j o?Aycy7A1lu+5ʖxgea@sOmix\zI|EPM 8\(<mϲO*A߈Le"}hdzg]6Ӝ%9Nxr'>ߚTOU5ثѪ{ ģ%@G Rc4qer#KDߐa^ veԧ}Cxa+jAE&$7] SFu͇ظnirLn)g0pj2RӅtUKpK<ʪ8'~6]¼i >W7:Eyw}%j*yͤLIL2oܧhߠ@x;1?8΀fVB @z̀T՟K|C bɝd/`wŴ/'\5eXE&:ޕeYؼ:>0饶c|~~; .)(hG=0XuB~چ^Vn hD4΋'S8WYv3Vh->Rkz]8 K+iX2YxqrL!󂁋S=-#5b/'):([^U16?MK^^ }tyN & T@(oh޸~߬yӻE,ƂK#_  gTrqM?_}EryOC+Q%:)w {%o|$R C2ۚj92n<->';/Úy +@c n1_;)>BŵŠ-¢fMgoP6JQ ?%7pi! *3l&8ͻ1cDvn75c1yԇ mjehUCP[tY'/Z%Gc#fڢ OKSUOTT'-Puu< d<^vFM-*X-: |jd׭KSW '^bظGi<$@#Z;~w`Zmߓ_hAP˂s,}dA?WVwކyK(FWi﫻mOWBʈ[iר^?aX9Z2A FBĝ%x ^ U7-ɋVY٬)6jř~<oscCb`}J[E0ګ@xp0y8 S[Sc0!_"jZf7R-4]-z} xc_<#/z͟Թn ~yr;AcF~Ԃi}?[[xƙ'+!Q~4' _c 3Vh Q#@2Q( 3֟U'h;i[E$0mNGwDӍXFMR2[l>71Eː]p/dv1={\vMG7E:(5 >P{ʹST*e-j <'c))c iwsML䨣ЪŞ.:XΌ$-Doݼo׹~W.03OdndKKaێ4fNB~F/EMWBS= /}& )T IyvP&{e!"P\ תituXjkS^_ >c,C*ru2uŁf8W=qZMBp&{L/F^ꅮvy}g<uV g(>|q]Vsu*RF~~`1)[X}I}Vz)9s*&`s%ik8]*!r6X2F`uy!]!7(;r COvDN*?0U+,OkyXZZ pK9logMhCSq6dI8 64Mn.;&CQEd0TΚk_o?\$ v V6OEew3_ ޏ5Q5e#wA2+s~nmajKᓟogť&9C Яby@ %$:M_PtRdZG[jcLN=J*\12$A5V@B^P%+p.sO~4'0͌~qsxz Dk f,HWHKJѝS +wImH.0i6<.%1f[?o0R7'YJ[uK?;/^bR52mx&4S::n~Rvzegap!PdH5@tBy&h횵 nYkH(ѵTqm[U5s[3铕N"#TvDGKq6<,3lrcwg'K nwӞuĈ*진pokU4Ew0TV=+Ѩ-+?(2^Va;m|Q14_@QXTcx|a[+L3Bݺtf&fE>*NXh-^4&UGw1&m#| #Bw'_t `c Ht06qjtл0P&KU.{did:AGE;5Em0O]'OAaar ZkCz uv.(bMtDisUOKkF4:rUxpF\07`Ǎ{n#$cZfE )N\E f huX?um2띁v6Bp)oD'j1(HG&*͉NAL[pK@ ]0 -Du!g0e:0rfωA`W[u?ؠ`װ6Ezhlme >v*}2T@'\I1ŐӾCp6Gmck tDUIz%ˈ͑>UߞQ?shɃ5E*4ԅl25>=ω4y  C 殻17pfnSBrT<^eƄ+H=ҁulˉEd.ʁ&.D-'[t]xpP_#k>Wtz_`ALy3Z9s=l?z 47`y|1#1HV )x+2/@Pm(,ߜb/Ocs :uFip3!R=p+iJZr~ƯCЕ2*pf  (6{6LZ5z,夜J;Xfn#۴J)QɛWh_$f@2,Jz=; T\}3l3rT1Qr,lN?@P(z=4BCf2H!뿲5C3P"Yv+POg9^n%=Ko@% 6c+=Gj5w@@ŗBRAE"v>p*/lM \snOM(+qx;͕ԓ"zFczX)1#,fe-udo`is_Z'*D<؟*!af u/k!okl>㘝$Ǻ8F ~, <2}K^)@Q*l~4C9»3Enye<ߓ0=vwR{dEU;5v5gu[AI4K\qKF^^4KH<]t m3p%6ےb!^QǂӀ2z5" &|Gh֯_3σ,YPCw@TmZD72M SQ%.P2<ٻk)Ro VH*egiST^Dc xxNrt@碴:p%k9OLMb9Rv r9q&*a^ӀROpy$Z~>Ïk4tSKX1 qhSMBy?D 4HrDy;# 2dSx tc"R ݔHl2sjZk&(`xAhPҘ¥A~BQ96t F m1Д\T#ކ ji^j +sR#3 &&NK kt-9 SQN۟})IƧPg,Lկi6Q/.S^4R4*'^-!]ɎHǂ>84 cJaq %jٮZ:ֲwrF(tXA)&Wg$9"@n63V^!2.!4_V '_lk/gZ7lP&X:W A'5}]7tU踅 l_C'~xHy~ݫ' ^n=/y;MH&1.$|ўuEm4ncalR "CjԱwΞ YjcuJ o[j,)e@BЦ 56YX<S# u! "$R46eAmdH~edفm#dJB WZuDSw"gLj/T:Me( 2 ? δw;lN&2Ӻ k؆H_3 j{B4>K.xʱWf^)aGdBh6j,8"pxqK7r#WODKH6hzB}ѺH]_I^3_2GX-A\ vkuG-Kvf튄o|AXh u"AB܎xfxI(s vW5' P{8]ܱeobܽ|VΫW:|98Öhq8ցܥRTYVHsBBEDN#UF̰4(iUUBM;HkiՈ,Ad@֖`ӏ9 Xr@xyBt01#8RLq9h=u Rـ;K]< Nσl ZJG5<20Cj'DqFܯ:#QTT␾/f:"I -DL'+%&fBώ!;/'$OYࡾMȥسwL}驁8)?:ézlYGV!K<[q#8]MwL+ݩ/UCD.kx#/;>IN=UB|#:#b16 lhb',tTom}\D:M&)ظzT0Jf"%NM aIp+OYPO쪯`ٞJc(IGu}~fM#`W5S~'0+_]5{meg`'^Lj1yq%ƥpf<ٽ0zFy]kС?h\^Vy.r$?0s֩+2KI֜w>؁'f2^*VDݒ$N0b H@EarXI*{n %|[<,_gTn9kmH~W-V`QGb#O}NUE1lp߬:B!.dV^5ci_ Û& ӱ>o`?n OhR <+cIHG.^;YxRatKoŢPncl Fb'*fiQPۚ("E`Ҡ֊3~*A]03HHm4Gsi Kٰ6J]RA)bTFv#&hdȧ}+8//a9jq(1bR(ʪ))G-jwIHĴC`!oz>2AtGvs9-JcU@`f{󤤣3EQ+[:"v ܈[ Ak?lGdc墪vYz!g \yy[DM{\uVZ@PrڬW}gVNEW#d]>R#G,mfQpV⡧^:C3w_YR9($R;6 O[9A:v)%ah*GxvnIb 55!sUO"(wBn3wCߥWa#ϴY2ƕiV̸FÔ@"[`E]I5ےYnm^֟A,ՊMQT˟UcU&'j(:v&)lnnqXX=UayͨmQ5bHU:)nc,xԟ>4w&eXŋBg`?oi7̅eLޤ]#9D[Dc{|l/g{C59:Wͧ1o7˼DJgv2nైm.s/yo)fSCS68i6(f Gzw҈L`w)j+Mmj/ Vo獿'*DFoOrD >_x[ʽ9( yCWd9g]Fri>CBԲKƆ -0$z$?! #4{~~"tO܅l(tekq5bV^+@UjOӳ{]DqT$c0R":8?7Se(p4IQÚ"|.ݧo jK儻 J2|]IpU}-;ܔ.F.*DEȪ];%@\:*J*s>ObxF<63~!;H6 J=*J #@X̴ =Mȗꢗ:ݝ,ndnQ| +!¦dbEF#{hűxnA,>an@6~"vt炭#H8ӓS2hXLS ^ܐd|/8mSK5eNr[N7XYcv`]!?e Lc1-&/aڸPӼʐO2 ̝*xcYoXr$$RsϨV^=e.;+Q=*ϝc]D,WW74s:7Mn9x\ &Y[ 5n7ϵS[ CY}DFx(.qtxrH:.̧' lqF $"պ]gR'@a%X38lY8DSŢ9˦q+ L"׀9 y=I2r:L;bA}k@oD/ AlG>٩;\(_6Hoy 'YKW2wO /{ 2y-JRp9"^ʋRTxLp92fZN!=~dN*Fߛ0zJc#wљLg'ݨ:=)zZBZS%sz9 :$)r9ER#E6 +98s6|w"E[С׉p~F\lo w7ӡy} ڤ0%`43[&:z֔`Srd@a 3׫o։[.rDmB$븠a3A]&G .6oprVZih<pdwWѾ,h͇CGR ^\I7e3U#F[.JF-z 'Wi1[2*MpfTu5nK~gOYDY!*'ZE~KUݸPN4V5+uFa +oZ{_g$u'2~EjĝeUT)]'K.;A $ XQo:1='..x3BKޱ *8> L,ELqa8sgqtħHM\0_Ay,g2sQn'eq7uádR 再;LЙX; W61N1,-vS.<1 ,ֈro+oPux辳%yF'n/.AیGi6Ft*B .>~6B3T(.nةfN֝Jk6 aOUqAM2(Pl]_JyF`DebzNmW.j6OL}ɣeqIjV1mKƣvT: 2z5bUT̫SlѦm_A5r077(7c23o|1X\9xxdƂ0Wr=,Aɂ.MRBX9a)W"D"UzڷڥC|Ħ#g hIJ {;65 τyGEԠcM$%^ $K?hg)ufvIẉO"<w\ frT䕆-~9'Zݠq$aIviS[J%1/~,c IM[/OVOM$ HYO TdK9+Ԇա.O(FCdOgLKCƴ<[Yf08]1?%p1_;]0c}QPRaNdTyN58fo&6ؕG!0\ Q:MaM$v6Z8UzNHK1ꝖNGLcB ֵFoPnzmJ'LX8]6buM+;MY|́aKEr^Ut| GFv3*wjgnPgN:F1YeļȑAIa,̼㍔ZXIu 2J1{_")cS1`MqFG!HnH2 z$iPG5gBTc0jI?m(fm Aq̀lϙ0?lv`b8&1¼GiG! 7bh^/UDB?ek0 c%`" z-TM[s(by`=b,UB=U5oq ,0inqr&g=m:XTH3Q9!\K Q"*R(KIDkSklwvb{ȼ{vv5ѵV*l֊ƄvpYVpϗ~d@NVd0d.5O%”?tu,OYU/!9֧h8'Ӡpq~ڟntsȒke"~.D o<V ;t@KԻ6]Uz R\t] ق)~嗃: I?b3aUψo p۪pP"9b뀞gzd>+E-U'/A=: 2P`k2U*H=畯YE1漲x  #zlj_#a?+@E{SBm1=VNIȘWpnWő$ϪCX2o"iSp P|<ӰP_Ô@i w=BKL*MtVJ`fS{8F1(F%"ob[ϔZM%j^׏(&F@0,z2\FM*ԧe I]Ecevm]t՘2iR%4[SO<cc9 >Z/wHoƧq>>dX~y=}ВX_GDj ]"!Dc&܊ݹ{5BۨB]ܔŒ(󸟍U0O'Pr_\4J.±Pv6dV>URvJWT+)8$*QS 9L{7 -53xJJ Urc-\rbK^~jcr\0sa'L3MŹ}2"?Q\׼K|@5?F#*aY䑥<"b%pSg4[3)i9Kd]'pwҒ.La'sJFQʇF ~WȰ^wXD".~-Bw\b%m2@q5u94xs&R؉i##O=@v؉=r=Lf:UN!JLΖ8ץҭO &J_+PJ(WPU2!1QRz=ϟgr Sv5e0zԺ @g[3ӏSu)u20._?d# 6~M,;mu[^M<]63ʴd(dxDY)d3 mQ\dBaEEN(XdḛfyPWd : sX7Lb vMJv';g|1@'Sdx^+١ޏAW] G|~8M G6FZa#y"cq$RF4F+pۮxF\yGI|B Xɛ:ݪoMl]B"(xYE{g;Wdڀg}q^ * >in)pɍ!Q~N rdX,Z 0FM:堸.gS7Rq۞D0`>AEnz<$>&:r-eJ3hڳZCj۰E`1"$X^{_XXd"H5NL0De%Mx!KD1R M 4{bz4jgEi{KKa(y{°ɾWӣRX smHzHFa¡K?~/F.-vQ v, s;ȚQ X*كr=2s=Ɋ_6ې,YZ>-:WyH.;`%Jx$f4Ďz(W/:;ȲzCA뽣$nu8޽Ks AvRM2`(E|EELdLr920PUUvSc4{Ԣ#IY+nR0 2<ޒ0ozp>hzf0RA>삀 [ m#UjR޵Uq]Ƒ܊@&=%"Iv*<:Cͮ#Ԃ8 g΂F56o`ܴsn9w?$-Tcq{DCbT}h!re暶G<7 ~H^c!l< (,d)1֖v rb=_&{ Dž^t(6?݂}׬#QL˙?r¯lvO8Oݣ9l ݣP`^ES`G:#ff i%a(U=o{yF:-j6hO+i.Am<5KjUnp\^_NtX/&qiu}䶠CC'l0&\X{Q>C"v)ȄTʹ6d-'C[芧!<"|%^U.VaJ$,(U/6l T?c}gE׏5ڔ,*Mh~+P=ܖ'-;X-Z$=}4*ȥxGvwJi Q̕:XV x0bvC4eeZ*sVX}MtdX.؎g 2jG9 G3klSUwVd kݠn)x"u럇g0*,HKaS9M6"OHת$3|a=zc;=k ON>|ȗ _05G\YriPi;ogoe!H0>P*. 7*7cqHnڕ+>_yszNT[{LNvTܣM{AC֒`\K^ *,lcFͻ~{5ߓSN%ZM23\tgkIϳԱžbpcx{̓ "XS˓vl<?A |ObjJhbQҀ+ RRԱA="_i+?iWkU;},]G- ee*K'rIeRHq>VT%^sז(IDl'i3女]zuWOS,p؋,Bas`6# uT굝gK6W9?.?63O 0șdnڄ)T;Pry&pgrrNM|T߻x׼8P\W\IQs,DN*.mYr֙Mٖ$O/(䐒 6ջtE%4Ϩ"+Gq(QT@(lwzWf\\:C?X !YOYy5]zsyI_Q'RaX^Iaing@y5<:6fgyk9o_Bx4H1Ƴ (Xk9i/U,Oz-3?񸍬{jOyЙ"wgNԊ[R@D aYqbXRlʫt ;tɨ^Z>|ܵ`Qi1[$YbKi|A:SU$̈1bCa>I%a<švytKC)kG=y,Qqkfo#7>+vEˡn JLreCc#qP caEjভPn(T籚kzՈg= IjcK |@mUb䰩V/t :T3eN=g35 r[u#:?PEr NX!2ΙKrt!R=j@#s@Z6[MkSu4Ъsg(gz2z{-krRCF,{HɍyX"#?ORoV*Hm;GbCGY#b)#_VhT:7TZf8VFLvH4$AP\܅(l{Ӷ7/|C³ +޻%AFQf,dïL-8\,!O ݞUɎ=̗mb9KrQg8U톛k(Z2E{ 9Rl_@).:!; *@K9KP+a<5 {YWޜh.6!F-W`,$0 .ݲֵ#cΙ^]YdxcYC"[*o 2˽uB/lF(d-LEiŀ S\W<0^=lE.q ^$Rin—^ĄC.,ka)8i tF&yM'&3d &d !rWH[%F1+#@~A$Y۪:p0VҢ4\[ύFu=e[Λ*d'oa2ҿwGٹct;0f4 m~z F*UkY)86n+5՝>vcd̳.`Ɲ:}!Y+Bj=bm{"Յzj^PIak `ʹQa; l9rX+rCgVǵKpH= Xt$rpRGB*|' `'7|)=l|:lǪZ| Ob#\:ʚLb4h TL#. hA4`\4 HP=!(U:$W*fHF|p6Pud j'(E,b## g ^HO v*5΁8ya7y/j3HaEGRV`%+ii;ǦdU;znA qN!~qD:n(?Ǖ:5WdM[+к͌opNٹO{(?6e+g,g~I憑󣹢=Udc$i$7ޘS RaR|7[wp=פʪg^~(h𕹃ǝ^G<3Eӛ9ΔA0۠kRD$/6xxz4:_s1J=0QZ d{_?9ƈd-E`~_//(y(5 MV!^һE2Yimg{Zrl | b91pBl,'art"p\_r&4f3]gܟp O$kўpan'#L*є&qJZ:teD+aS0!q\!]сSӓU4;ׅtmOM8HR!@Α[)Du!c)Axx.GysPftwn!ÞEUMMH=9#J@]X1ڻ5ewߗ"qQo&8U"AsJ:o/2ڨ{B#`E@F*ih3td= #Ul$ ]S42ZIEkܢh z]b(=/.paԵPks,nI58>m?y4+gۅYHfL~NɳBHo /1^T7xN">+GITLu\9Fys֭)@K Fw9JWr +ƕ>æ'|0 tڎk-ڛBߍQt4h̆U_y3Mּ5*8~!z4]K?5Z`c`9{ 0$w'eӒa|-MpYA\Nbrf(^#DO&v1L"$V2Gatub=6 YT˒=q+9bq$fWfP4ҁp3 حS؂k7;xnmX5V@Y  R#:=Rl՝4ؒ7h% ޻^؍cC':F3So޳F)-1c.I PGZ 2Vt2,%hUUyaU66w&X)X 6#Z- YZI3\g0 34.KtT+ЛgujfERZrTY bۧ#5FgU=V$AxͬbH0Ԏq";DXV(A%L^"s'X-)I z!;"rhf-@ǸGEC5G #JZ XYӵ[FsBf/f`=m"i[M`e1KR<HB{]C}{gzT[XvT\,rQ s.gakSZq{W9ii\Ƣx;Ӹ#̇H ~f)=LtCNVFWw=Ii+XJ,; G|E}'SlEpH2 :' T-C՜kBH=j39-Y)qYSp7M0zGAoQuEt@]Y.'fU!.v:;) !zA#%cEXmr-<߯lAvalwu?%FU+/Z;ve{4.AˁzLAz*mB oA31p%PW2p g%n駘>{7V`6┣Ua+! fD~g z፝O7G:"O,@@窪UÓ3mR8CWxx} ACA6g2?5ڼ2؋\JT̽ RC!rӃCR,e7-+wD8%Zd6TίrM47 3ȞR$tu%O @."6'H?*kKTrf8`ZsǬh'+u|Dh w'a{ObcQ:kWAH,0tRe #kԝr7Ο4jσ9d^?opxߌN@9[E[9&&Kΐ(د$՚.o.EObKDIxG7oGZ6wYTҬa+ya|=_ҡ[[HRTN!WVhf&dgӱmH3;j?N9O.Sc#AB݁_x֞~$Q{݌{XQ /A'pύ,MK.44=yoDTX SoG뮴) NC9Yy`J겦~eӉj%JXKYQ4o? )+Yn Ujb?SjIshB$ ƍZC5;g5C˩ToNs?,k7d㖝_уCg[Fc`B(gǣt73x#w`%8 aԄ3J_O&et SHbnn,+HڌI8Et"T^F %Ln,%Տ->8Dk^:.ZΉN7D.Y S;0Y|i7fgi{ls|~5Q)F_!ϑJQ0wl+JKq ՌG }Kja~_F _t/cHPW]ӈQ\~%+bXx+TxrmiZ /1&Bco5s9?呱d*3]̭ʤp=`~_q|pD Cus8Lۢ:ںn>i? yؾn[5=c76\I6G~5o,@~\Jjott;U Fn;ѦpO]z@)bl*rܳNBnVJ(o3 K zd|,?sL>V.4QW\6v$B(=o wOV[oS)D.Sxv&܃`߹/d3gt;Gpt7G79*%ni*},?SƉ:93PmRN٭mPnM!SJcLTb\&:9?Dᠫ@1|l02C hĤjÖRr.]ު^kY6=NC$+Ib@ط(D~X$D& 1p,xgjX fL| B5`xw qDFDL+h96w|j>muR庌3)d“zZIp/Nfqu<\-rmq~:gqϏiVim]<}kg?,Z$Rcd,w~-Î> ;VO|cY%!aiLsuj_p"B { C[rI x@~'D髑8ֻB1C p hjjiPyMB&$#쨉xrec^'DQ`2c葝SBlڴ?[8W lTQPtZxF­.coiܒodtVB(}(eYnjcGwDtR[E*M]oم"'׸YWpTfT8UϸV(; [)1irÔUf6Ohz SޝT%\iBq8lɦ[N=j`jO3L0lؒyJ "VYK0ݣNjw7C'zw0/g)œû!8_W DfDžkm[mfd[erW%]p^ԶFQ0U @1HBQ h, 9_dM4M3Qv"J{Z)K$k) ^_ 06;,ڠ c ՑU| o򥱜_¾E(th_bq/ 'zMFp=۪dS$f|~&Cq5Ø9\搕B7 n|?vUa1LbVMv i޾wNx4t4owt5gJL|@HkYh<3yO%T#΀yɃZ}ɛI&26{t=N$XvZ۬kFH}*B]5")lcDQax0~ `!,*ˣ\ uq~B AW tyfn Vdȋ\>;X!r=ir &3-eb %p(M"«=(~`$@KX5Nv/^1`>NgW!}ZŰK;A}`kJu*3MȎ D&k+I;ܔb^4ΤD~x[*IZ/Wʻ3lNf5+7R1☎nU-Dt#rw0"·"a dFkXlb\ OU `-sLǫuu8yd)ql0a,CTx{1N@LؑϘ(VO95y,I]#vr7%.J<}3:%ht(~ޘ f[L;pJ'ڠ]WEԿXr(yvq2¶Wl8(+9#]zbgiW:x*/9= >UHGkC .fLq>K-՛FۺVyeA1q07уС/ž9m贜GBևun!B~Rf1I.8g OЋ /Z s~NE*c?'b7{)p֚+iJҵOz NI9AR5쮆W}5 <:ω(ʉ55rcg\!I`^E%r1eB iP}tlF4sI[L)h~I$_VkDֻ%iXwY0ؔTrUʵ4!cDZCY ܘk Z/g!~d)pT ;`> FUՔ$`(}Pi tq#0MISIU"!Α h=7;l;X=sÖэؙquTd5Zosj^HH=kPC嘝RcNK%ǚwIxKC.UI%S/f^Ku53Y7xMݐ=fp08Hl9zI(5;5kb@-jљЭ+K&*$S cL-K pX6aIS82VEssX/-"UFhk3));ڑfm>שIhɞS'*m@D^[KE9U*VNbfGYӄ*C\_@*2n:'v0C;#HXyk0(۾8.kmq88&/5r˥vk"R.eڊjUۢKXС]YOT.UT ބF0*݌R|d~ yUQNw#f%!!-3_-ʚ8)Ploȧoj!?7Kפjl;]45VȄ/Rbb'Fs]8k>FngouclmR7_` QyyܡKl/i,-Z:h` CeSOr<(~ 8uvX\ER i|eKXC}!P8/T_qflb18.>"T"[ 'Rt?Wxlg/' eFVlW >lytsPPCf砡CG5jcNf>aČRt`pIW/\͂atn3+i~fbX -r[b'oꑈm.'csg{(2K$kjLP׾Rcd>I!: JȚ 3)l Px}47 S8CCm)ȷS4AqvMDw8w,lZ}M3T#I 'E"LuW{O7'~hI$'kv\dZi9(EI F&7 w$ [D L;& x{Y7[~=m_xRǍ1.%?r x#e˭nIip㎹oÖua_xG鮄=к^7¥N<\ Y]~DE0Ү0IEt0`MX uA 1Weyi.a|߸!7m܅}V ]e U9 &q^ sS#?>ʫ=`KkstEkFy2pHmQUE_x\r&h.]w\'3sZy{"w4uXIK%qPiD14½TjKlJP2sЖ0 sC\yʕv7g7œ]4kl,pM0g}  1ٰ԰}bd-6%CވXcUm&<ٞ!g a=\%/h99I=  YQSN} :#g]TnLM[U k^dx 11! A^94jm8=QNSd[&lI\e?QŖޱbD'6G](.UJ")r?B$I_Ŕle3c"*SZE*N!kK,; ]e`JnCr&@!24% 7q[AQ*QuwR衯&W}+OO)Jk;IϤfz~;HO4X/hr䕃0'9&Ci[# Y94)^H'*F_DVD忆kϰ>aV:3so]N@7XNքŖhҹA'ienɶL0&@(P(jɬjDت*SփR3̨oUCűkR,fFe?u4gDH:r@ VClqLp^rywӔqm $UaH)dsCoCXttD@`(0˙`{KJ]׌B !&܁"r@OHys L@>b+0|Ti5Boy'tIo 1ߤ#o&%o ~tnbA _'ʍ3 }@ΐk[j/&|ުqiļꗵSƻ o5 k]RdDZM3]x5jtWasɦ[hwvpÚ[ Q$R#R OYCTx"R ̡C)i +nFk][W07K/m4G/Zw,gj]c _G%&`gRð0G}s*vV4:dF˖`UP!ؖe%+\eb'tdjHh8et RbaZ>N8.GSV516aA{dYd4:~KU{)t8 $#Շ [F\8ϹD`%c z}Au/~o.]?У\k Uё F{bSb+ȉYj7hqk 4yhAGk# ߚR= ^r 30uOZ+%ˍ6s5C.(Gw,]DǥP3XqVchF%Jl$-oj4b_=f XL"NO;nv5e"83mp6l֤Ց&8FVxBb ǢboJӢ8c" !Ć[~v~5iV1~qKHzUNO \ $$fMf.@ALZ&oI0{`u, O~VF;3?BvkJ8 F4_i^䄩BelANXJ&~ቜXS`8/Z(@i@h[,w$7PxjҞtSp]:V6QHmRfs+<܋u׊ù$ޖsU?0GyOPk2(`\S$)mFBy( ndV_<}Uǭ^C04^BaIk}(4Zj\>tYz/ %\pI:Z@TyzA8}JO3 ҅M,lfu"Οj5nM##JO#x>7%\͙NɅ%+㺓#1:3HK;4[tόcwPKm=iidIWZK@9;+ P.~n{y,x\$$6Tid$1 O, ?8"SKYQtPy!'M(|G}KcatOMc\FltЩ$qϓfcd ;T@ u@SuQ'YI|h#]:Yønʹ[)Aewwm\Ƈh(U!K*Z*ϊfp (^:>z?>wo'Em+V6θw8x ߠ¥H-.$_01vdF\Y"flq I.𠴙A&4yOBq):宂ig)'K$olZ*۰xX_L74n_Ejd%3<&P ,lF ڛ׉^Da7yj@L4c޺4V nLX؜GN7ԳE^FI$;2-/W;KJ;`0j2YN*˵ڮm'4 7%nZ pTӝ%>b˶Xg_*5 xpLe󡨠¦Du Y}d|CC [} G0 Yb$ 0xDK$(j0}Z ]oȇfX=d:QH?0^hRR*0«=|ć1o \]5z tIcPqX\`W8LXߘXVp!&M0cٳ{ؕS鴱mfS'x~^>N7A־K Co eHzg!BN~L?Z|@f-MԜam˲yՋ?%w )4lGۇ|=~,TMIE7,ņ"k|?P֓7XlĪznUn9^VB0G!8~R< ȕm[O/}GAVw'6 F  3wȮ#vH_V -Ct˗{=hƢ8Pbdyak׎7q"i6bGtݮQ_׶Вa0APQoO| Q22jΎ z;gr+W j.0b`o[2\X{c%e(]g\A=S3,[p{>>=VD=5$3Owt8X~=r(xmU^S`o4| o5鲷.U[p2!%87`PY ?/:%׼dsӡj >iXf0&{A`3c=+Da @C8͹5Ο|jG2̉B줈iFe[%yl|/mit؀N(q=h?^/  YzzC4J!\c'Ͼrk^C;J%-cHhRc$jVQlY7f[^Oh1y'uwf$8(#y]#WKO{X]tڨi6gQ[3XbIMԢ4ڪtI>M/;I_ޖ;wk u`% o?G:,}c,#Vz2gB+/5AHUF5-jOD*%l=l~a";1<0ʛcdG<3giC,zl3@UxԖ|k F)x l/Rn>ƚ܁"]5\;Hh!bQ r(u(=bkQn/ɭmWh/wZwH=H28bX!MW9X .:)h; vh]UӠi0QJAd϶ Ni_GR1dc+CԢO lSB.tq۩4O^lUS^;bo%K<+_DEx%.G~c;άBkH-2<|=l)/#'-Q[ mյ~ ͤ\5=]Hqq7u=փ3*j;y=)DS v" E 3[S.dR,KJ ]n+(ƇOgUGvlx!5\I<7~h; !`W!ɔ`!}5$Bc?^8Bu"r1CG䠂ω.z o}(zs*iLxj# (()EMy$.[lc݇ ~gD9K¼:|`.>Uu< i |:>0F]kf:_$:Jl32QYM蓉:iNo#200"T"ԇPq(n1Y>y}<inI\87I8Sqߋ;Zg~3|яЍEvɓ`,\eR;rTÉbHzW҅6&/u4z?{8aҶ @ 69 K84pI$8P~~m0~ xS پQn'4K H1S7o[y^ wJ^Qti{пAZM ǭX >#aG=0^bb6=!eU?*l=@Rq9w |*<_q"i 1:nCVog㯑 m.Bb '#w=;!cDz1|*\5ޥ-GRoJ ~'i|ܾ ;fdM|j"{=j-9so CXA>ˎpJy$Ae$LMc* xP~K fZ kX(SK1Lp.ٶza z}rEƞD`tPMHI ;x"z ͷXqhE_e"[.& {,o@~q!`(@q@ Ʃ̓xDPdoa TArI֘qՖ Y̋.)6+_ED,3D(?, &$dN׫??($N/;. ue?JC|C!o2t`sʺ;mVjV(ܾ0NDOexPֆ`(q4 {@ƻ^ GQwxt`b>,l6\b+<1U тpt_⏨ߣ/$PbEGW iN.yO!\{/F&yۚcgHe$WE6%TbA=R G/-\;# Ǟ`$S UygOBiXh(, d̎"?JjTeZbP(͑6T=$VHl~!2YJ %Ui" Yq'A)Y4Ҏ5Ѧ(odf`~F5~a,}nKPH-L97lqlv xZY%UM`ǽ> `!=}2#6ڲ60;ӈFV}a/.jJ*A&aMB®+>q8c'񋝇R[?tn}\oNJ1_3I6R+eek, *WH1u>fp AY-!)>%peuhFj!cЫ gqS._RS ǝtB&PK:}\T8WE ?%V1Xq>`:iQ0blIGo vQ [%o5 o>g8E}.bFnzg#zWRZջ{RY%=wx_Ӗ !3Z{R&!TWc/5;|hӲ|~*;ЌDz@=tEԇ!1P|WFf>P j-W\^mŌJĈ粦iOī O^ YϻW6}/¨sB +{aSE^dyزaKXthS(H1lZmVqkCj-7cdkKszXJ|Q+EZ$zl=7WsG,dD}_ASR:Vtʺ3Imi>jn&ؼ`7z&%~$D*78tB\뵍\5}w2*Sa31 ^ ɘKG:<@g5sn9'eQ}Ryۃb3"OeWolRj46qa7Lǒx/ZP%#% Y:2 ]P8qW,y)İ!~&w,V '`2 #%&YNJ)fN>pm0n/'SbnT$@ x0)G ,ilиǷHE6r_)qf/rPU dɔWWOKsGiflFgxu\wE^..@Ūw+]XrTk~'E' j2>#FQkՃ%kȌɺjB6:w,B7"t095c^2~ erDkUD>h1&kKJlnB&V+pwJl n'$\m$Z4W8q.nd<]:^*[$vCRӞ02q*hb7^yxAO/əʮ53κy. YߏPґ BB*EhHr"YC:ۜs]t9|L e od[sEcF\Z~w9=&!- 5D ]z9-a87O5'דD&0(]c aeJSw_j7' eo=IֻH& q:D/;AϫB[)^G 0 P3!d4Qߩpy&&JR9Qq O2م e5{gUҕQY L7&l p)̧9> XhtofrL5+kwūO5©GkVD*iy*TƃԺY4Ke>$ULǩKƓ+`Y_}⋘ 0׹.ݟ5HOygjqFR4VuxkPyg'n$ B*Ѣ" %Qf\}.e!R ڑ{\~Ha =AA.CEtܜ`d޶Ѩ%UP\><--:Oq73bYR6_·+^ ϧLa Ttp `WV6P4s6i?ZuQŊ \Kɞw)4j4{gLR;[:ҹ`RYMs| l1}`t!@ wb3oGP4ֹzJV&qo "fZ,bE&'k Ld%Jd 7ZUߠw5IZ.yͦEh.8OMU#\:#z0FW bd;9`H$@'mnCaoD.alv2}W TZl(1H$Dz@ s[3)*tF.}!?| .C/fRNj$EKbi =6b\9^ԥP@:VHJȦAg m?̮q|E?{>Py*b,G#kg_!767 @\zi )0q,15'TCHu&ҚN1.T>ޏLE~v.K @@o_VgA-i U[=YN, V!!6ϖvl0r+>oZPaŬ}Os֎| pN j 0v˳ 4Dس~47ikCĹD x5 G9:u_Q`(G B[kE"l&c@AUT~ sq!A TRQ}̖8 LVճ ܊PLk LDjR'gj4FNP^A!Er&W[$t T>3aQ#6(Cɣ Ę; Ҁ.򋰖&=Vg{{4 G g~;JYV4ei:^]"L+\L^ml VDXȗ k%ٕܝJf\'WۊQL(9/d"'&+y5jAh5W=sDifDY(`%'Ӣ2) [uX-bIÔNio$OuX=e" j 92#S= CpSkǤ/[MoX!D'%YX |Hk@0MKdGV8'&t;ru\M΃v.}6LEu?Fλ$D5D[`څ:" ;ݹO?p>b~ *A[?u ۞Ә Ct21=V/BI6(0K,[ڟ~]wP:鹌s#5P{Ӝk*j=GI8JSQ쎣ˏJJTyK4 t 鐼.&ђV཰$qf#zAF^b[qEUOhL-h\ q}\y/^pŇy)Ǣ. T.GmQ`/X~"d# d 0!DȠ-W$asA[$YЕuq[ 藏׻A˭F 2PHjZ','b74eU9ݬ=k,agj! w,+TK|l"?s/Fp=bGw=cֱ)˱|m" It2nxT2 x{Qs>*[ʉ6}y/282wŋ'$ tξ)ȃNZvz~g n :Xה o _ح(],Sg,GDQ)+V8&nIpJ`2D[ZP.ttN Vt~\8ֆn!(5 ]STuU0X8mB {\jBUTK:vfң%gR~h`pvU~фaDL@ 4Ӷ+ TYx_jZUr*0$'[?(í(a`(5IIX9۹Hs Tx Z-nax=UZu'\ah3x[KQ}MgMq[͆VuJ#ryVoF.S)6ymZAi_WgNn3}uR&{?IÂBR҅Or0XlY_rsDj~I:5̝/{o/ ZҤK!kYsu6b.(1YnN13107WYub/'4 +ir onQ@#/PJyc֌8E;P4[mmB!/)8RK7x-SR"Rnl!(^œusVws 3,lSvSHuuW>l"r*Ei+PubNz\l98V!.\ x#nvvqUSŻ*mA |D S FD,_pϕl瞨R؅x}h *[z讲(V.GS,柋ZМ :+e;vlcSGW;F˯U'TiGlb̾ǣ$-ŵSA-C62,ŀuWhpT⠴іF5q'kr`|v1JH\ǃ ̂:?v9gh^Yum9}yUV}|SR :ٌ36hN2ʂR:R tZcc!!"I:{F ؂ݝEE{GL|jƴʻqد߸ZQIc:|)n]Ŀe:ɪ;1 +0 ÝA5Jk'Ʉ=wdޅ瓋([c\U^ wi+$$1#EӖYa:\wv6%h"LҔs4a\nܶIos#/ڏduh39eVUA,(wj#U fאTL0gF]{?DȭX Cdk=tCZZ6Yj0k3f4.@,m]}'[Wf,'>|o[Yiq0K;$ uo)6C}nRaZ“bByF&oQΞMFwdm0`< KQKƕ}})Q)٭,z+]H90'O'zǵ2 U4Q{# u>Cp 04[JV{ElhY(bBaS7hE*6(eYdE*Uf32\\p-xRߌRf4yh?'? `ꞛݪgbGÈwy$׭N굄薻|H’̠ia.H6њJouj奥ͫeJA5IܳA,p1J +|K"'':$4{!_dw:etH7XTt=p*`Up:RCZPL1{zow+o3S1r߀f%ye$נ=L7b7JDb5+m9Bҭ]L W U]Thn(THb< 17j5fi߄^E{B rH#?A"Lke=+uULJ* ڍ%6wJ)$ӂY>t~,ĸK X2Ltu-ijm~TdrMwjdi82e\\f_elXu%c%<X~VG>|#$e,GZSJ「6q4B7фNFTMp yifQ/ &C1/R26@x*N$.? 0tiuן!lb{ kj%TuO>4P#X9AK=,X|]RiGyv?,l7Ş !;uD< f;7Q5 QN& cOC]’b.46ؚQf.'VpV4J޾"PD"! fbK{0NȠ0{'If(2kƯ1TBqBv{Ǝ`!jS;XW&lb]JQ;HƇ0Z#|Tp{gt*vAqm5܍\-AfK+I:1k6.GD!@x{l4f^l'm:e(] Q{B]dqZWxjc2SϓnDTDܔqw,*3 5}+&{#I9pN/!%0MwP<\2 !!%'i*X-95a@6x4~ʘ[(WaM5Hd7;t"Nk|A]y42.ΥaC;:]T*ww ,|YPoj>@{R}խy[N_?BC0č;-ᐲ^Q8턩  |&,:7ǠBrWO~&Xrsmoް5i :㭍˨;^I%ljhߘ&8PNJv>B>s6/ծQ&O^ر–,8bvكU!\,5t^WJ k]^3 kTCߐZ2=ƻDz DisollNu kX]od\1?N .n8> p}XRPڨIxvbƤ5n ;li"e|0S^Tz*v]Z=Zb2c0x\pTn0EḣSmtyV'{p:kQ oПqc { _i^[icM~֣VՂFWxBV_QW\rБp(&*4K"|t`jM 7f%?H̟Tj9\c0m;C?1CF/ǁ%``G|&V݆4rd]j:_I~9 fmYj&~N 'GׯPB-urFcUS{ %q,HGBWk }#iWxL|7~'- G~x*@c6%۱D` 3-0eC-xGW/Yu~E)UGnVK;H:٭2`nS5 *#~P1SZ- s_'o J M;꣞Hq|ڡpu}>(5B@)6QLCxү>Kur+XXĔ8GixaůR*WvDhaeuۋYl+Lf]t+dZ`6M6KXh f/-`952p5Y^9\P&9Q=˕*WɌ]y6g],k.Ǵ u1-"MfF.3͛j^$Uz"(#5?Umڬ/ۄ(kmݣ/U6V>|`/)x%5ԷTU;P`(Rh][jފI{8(kM %HwT[3E!:{qIlׂxhgcJ,SDUt.QVXbMBPbhY m?*@鏻_6{eP”rďU5d u B $CJMߔzQ9IvWDwt5q;cq{ 4(tOi1VLrX2 &6_z{nmmVJX?͢1z/h1)O@aE((KUIay _ h9cyO &voCȦL; Q] p*z2Yu/o 5bԸHeB=YtZ2}Nӑ3-Vij1~fέWvv ?03~.2 t8G EvZin9`^ ЗIDLbo:rq/v#,#7~TjSB\6YsCa|h3Ŀhc.SƳ|M 汊H[_'k4 ?@r}gU:k3OgMdnNyߤS0ӹŕ8hWip YSҞ اlށ+ PD0V=cvGo,r3d=oAh9\/Pd<|@؍r8zgm83l `Pݦ4u4$VA4@Tt'_(݂E?FZmg@C`䓜+$+ao&[$\uɉ"(d]'. hȵ?rkރXdzAe @  !FԻj{mQ~!wʑkR|q" oRkKԊbY2_dvY3m/nopjMt2sQG _S P8 rDR˝Ł<^{ia @-67-(,)6vh tJ^DeTe.PL[w`1E a!|ӞYMfa#%sy ϒbEcdȾg%( eZAo--!54>uBx'0J^Ի\u6Л4FyIBf_]rZ 3 swqf^AtEuGg?6_Ց5;U ]L:DaOX3YG2u`MӒMuH=+T\/-%rBAވ,u< C'gJ:C9ŨQnf}{wCZ䛈ϼ&G%ZKd`h G%I%lP_ǐ%$8 J霥.:jvP™)} w5R]qYiCt#/(Rm HUԔ̦@M:%ӡPJmQW.[޹Oc /$w팤|jܑzȶ6?(R,8iF2@ mRWFՐa邕0ownY2spXf0*F9T""x wH 5-FJv:䰥zRJcCu-KD,d&ϕ0Foa~AyܨL'g2isoTo"uW""9ms!`DVO4IJ6~krr0ݺ͕:&LF1ڍ12O$.&T-IK)C]sf.1ԮWt@IZ&ci0JȂFe/kP~YZQkPn@ ݤBm'^r7PA\L:#0I_$ 3hf a[I&F[0}I|¬:BpLs?O`Hr`bZZ;!<=0~Y`PߡI jӽ!ܐ N/OZ2tgX D \SlUTUvFb鸅 q4ܓ"mPvD<\>g*wmŮA7` V<03'sbV[U"މ t*!ŏ3ܬ.p]m ͌qSc-1,]$UWɉGl ޼T}_= ٕ8[e &QmXDBsowFY OFu,ui~B|ג0Gjx== 0.=5e&H&Ujx[v>/ڱe5[rkw-5,!7#RDt\\!!15]Ne.W<_ LD*7 c8+ U@-w:`C&촆 "Ŕ]uNf ؙר$Y+p;b,TIW'RBDAw"3+}$@K,xO9*}PM,#m>=F(ҮU S*[PD'z;4)a(xCoy}J6#O!ĐǪoގ"\EcO?Pg@RbjvkJ$2ltT@j;!} = x [_.F펕d6Ōw:ُbʉ[̃-E-SP'(NwUwPfIq% AK&8I3>-i>Ymt;~Oʰ/032Jp%DwM @Q6 돬%踃]0wՕSM:M$ȧ7 x>,ILuo{HcRsft!\+?4RB2_ 0@YR Ot(4XxߑcLn*Xj=WF*{CG\y+ v=sݸ!xwvv?xع a <8 /?SQ"7[|0+;(׶߫Cd:^nqjY4~NuYi[6OBM.q~ο\&j'hBiXAޥзnDu>4YM+jx,U i렙?M`)[]dع-p=ј˨(w,7\D] ;z1Ɍ5W /RIQıtgv҇ENk^C8 zPK_Bc07 Ƿ#ad *"i~,e29l‰0'ӓ" !{;F VDێ=>[Wʦ Wm֏Y:n- (n=&c{2W k &Ag_vbE0 KQ_R.Q* l㶶%UdBһ g)VXL@nP(`T>KBLֆ|%B3fk1+`  EC;U\}tH_T_mQ8"h AW-ϥ]mhKA;S-f _CU?[&L%_ho}Zw4Φҍ;`4=ꊾUn1#!-InM`"' ;V;.%:,Wu>Ǚ~Kj*6h?\Kl_4ẽPE/>{ᕚvwKAK{:E\iS~i@Z|1RK}'TXLu8='!NJ g`|.*]Cƶ{?K2{Zu7I^eW>D  T 5^O'^VʌǧJY3$[Y = $϶l_r e~ 9SsC#QͶU#g3e%Pi@]領lr|a/ u`V8pM[I=ks0dU`= V^|瓦S8Q@kx@>;$Ji%=wi pPx Y3R16S;c)3FGE2Y\c\R_M"2v[9ت.&\ LJYHxE(?hƔ] iLQi++i&'W^r<[7":ؙLL7BmKvVnN_\7)NqE?EyDTK xw$l!` Š=K#Fs 9Č(?u4+zp=).?ƫdNZb87Gn) |}luwkkfGTHpT7Qݽk - <)b4G;;ʴ)X~':Gkie?ni%I #5J5 T6[ߑֱ0'F'J fFlƧ#"?cUȲ\TfߨMtQL${oBQwSٛK?z^i0W ~Y}]ԊB0trLtn䬯JU^*i==&&|oP$k&Nڣ.P:,v7I}]@lCW8'mgROICWWon q8̝t"s2_Pfյ`|DB`$XMwטl_L fi 9_ڐ<{>e>]&%J2f8[| [UKy/'[*͒t#dtxZ"e 9PiV؝J4{ %GµSҳW_X(mj-)$>({nB<{OPݡ&;i5KubF;d7Csm͆ L3B)swѦ1;9nxr¯E$:9guL3*wjۤc^G#gk f(ٵcqf="ÅHD</o飕W:wͪy~ABI*$II:]PS;n[]0T+cm)`Є/ *!#{&שtL4/D2JKli<a%nYPtY[Tov)1ցR:8J_p-&lUr(J%MjɼiI 7ǯus*ʝR_ u:])$y?ʙ9yfz/Hoo>.!~htb)R@G텚Imsj/"nVü\MO ykfPD?}ِS->aiĵLFvXɐ\ª;ƑsPY\IJ8\o\(D4|+1hZL(+C&vWζ儷VH{ˣ s Ir8%F+2*l*S|`dƛOp-zUaf%q"s+gC^9+MkF"g;Ξ3a^B+Sx `Dl{ʶig"$ jLAS<^5jć.\SvsU_'= ]%(zK][sDb:W)rA03"gr2P{q铐^`d)GRA$w2 j7fDм,l/Ofē.f^0*VPA" ʏ /{˜ e|LfkwiSUi9ڠt欏'qxh m@EO5"}UGH鑰TZ\k`^wnyAOg}=ltcxf;smӡh5 # $A vؐz$L)@a啬FkAšaVzMEO幯ZA9}khwZ'[[,y)mIW >Z܋ F@Rшdl`h0gťJjbAaETq˟*f˸hݿ>td&hˠ%D S" 3YjݑY V'4B%dK0ܨ0`+=n7kKFKA༐)e,fvE2K=x@."J ϢQ|q_n?mgnL(Ra,б':E{"/ <2'/߀^31ft!u{NGyAEn peUոlep sN!DexTkRQ$n˪ʓ\Yς 7eD2ԉ?|wD~S{qC پ#~$~)3&? Ûz=X-SUXz,CeC@3Wu[\9!,Dhm t82 /9c2_*"AW )FpJr֫G _>YN,? c@2 ޮ_D !\ꆿpHLISu&wݯsS >}Sd^+n<O"v|O|Yc7X*"]I_@_*K9Wax!o.XD_ kƚaM8b i@?EYl X2ZIu8,;3ƛ1pŕrcIP9 tVggf(H#9HGH7{syՉۦlOhG8y5WeO:OgcݷzZ$;К\f*tx5+(B?O:@%xcqq "UóٿfA#V + Raa]-4:uW qoS}H;yR6Ɏ(w$J9JE3چgKvpͥ$S3p<7׾03e̊M+zB/D±.m7aJ|@q 3zjg120E병#P砲LShMY.泂b9_"x O1 "H*62kd߰0A瘕{;nŽx r%|OA?ãrD&D_60Y- ೆ6.heGU]xsB,<5p)s@N'I>cѻ(Wh/ ӝ(߼7ީچ&[}Э{Ekт斳v'K1&H1b"g%b]UMXV0?C k7#n)Z/#xVdujM6u(|+J~pxhh]o{R.U6a2wxZǎSm v{McGƎ9=2UQ"9OZF*`tv '\j qɔWUfje- 8M2%IU:LXY+MO Ą9o;fvys܄;c|uqn1WaѬyY$1tAj]PD)H%6 ãq{„xƥhl [Q`UYiՌ c{+ؠuYk>i5?[*q .q- 2$T"or XT6YbT\RPmo֩v)&X\-&v#$_Ea14$bԒ6iA1Vm]!hJY˗05a7Hz<{/6v{GjUW/n\QC"gT*KfDi^Bmo j^]jFW3(Bpwb4L#"L0o<@$,BnpIDRU++cпX=}J)0p6\OD-zLцa^쪹CCy1[1e 3TY9 $Rd(ɭ_Qλ`sl,QB9DlJOB bU,c<N.[VM\?OୠKd  *wB/棣6EHq'{sםW%ٔzT7+[w@b"vF!,Vt{lZ,vs8PlN/Yo~7nч[]2#? fi~ꅧcv%/I5|rFvJ{:ޗI@8UAw6l`z\$Y)(QX;T߅l?U^ "C+H8u5jz#ɮQӯhMz|>CzL&;irE~#tDTUk{ tdmh\(n}:QRRg()˰eX~aS=\9Oc*>*=A`9aTE/(7:ԛFp > 6ln $H[^5}#X^Zl 3L:1b Mj"!޹a`: Nē.qn)BA\mi.FPn2MoI>┉MaN_=8qw㮋UEuA=iC >J!TpD^.VY9noh H4L=ƚRJq&۫n18ܐqnp5% ߈j*_cN$q+V\e!PU5XefҌo3r:ҕtGj^VJ6bLLO%!HIJ^B.TV@P;0Oߖtu%1t->|p©B?QQWbÓ=N%&lǃ ˻%NeKADQ4?7VR\sgVZ6\_ s_To{qWupGbom'FeҶҕеd>H\Ɍ/W𺧱HrŸT%$[%kwee*C-Gސs swZpM ]175?t+HWѴ9>;+`Q!kG wEՏ -Oζw̳?"yU1G|'20WlW1 [Ъ<󙗼LQ}C ^a4si|{FM >\ݥʪr -Z-d͵/I򄋤 iOf f29 帓kALӝ~6MLm)(e\4ꖞjWonU8w~Qсv.<1mؓ䎼Cs( oG4Bveۮ%0*OQz62c.ZbV-hO(nMƙUWw }bCl1$Jf/\wƂ$e("JrGq*x+r\þVuͅ]A UL " c*nΟH5PA0oW>8zh׉A7 18Yܰe.~^As8Gf l^lmi%. 6]5cJ1F) [֫;[s!4'r}jP cu'r ch'8#_+ b𧯃k&b0zfB> 5h8 wW:W;9ᗄOcY_I~Ԕ {"*Mˮdle ,yG!xd/'(oIyTl&kr/ClkFq֣Ջ$ 2?m 0._|ŕ+~O'Я18~Q*Ki TSpe6SʟåXhLQn$U<T&N'J@E>zW_$(j1'M }v$'0,D9J{_ CXݗLߦ W!)w]ۭ;[CrD7yJrKX6k,K#gH`El++ / ZdpId{:),A_̔C"$7#Ey>1ޥt4P#?0bhf=WT!{,*ao932c󧮆 I)[zS?#<DeP_ $NAla q?1K6lQ;Lgs\2EΔ 1fi@lK+̵)@ŽD>wYd# ]im)lt~3GS4QLW]ap)RЙAxP :x$nvس K0ǯ $R(f2x޴aUе^Jǻ>x#s6iXSB-?w"5βqtRN&EΔB $}$|%1̿չ6.Y;rdɇ[ʮc2,Cv%d ITj=^TRg-*x\A'`I6=rE4.=ErQ$ʍ&R V[ n UCůf$|a~(ÀJʘYH0WJ@u@pq> @zQwn?ܨGϵ/-Lbn׻Cڪ7s韕 _TT- /gVd*j1A.i|5/q?=!`|1'5rOh憏Mx~nBsA?PfA0shD˓(X: @LζW ^V=~P^# %}NxwF1t@\L\0_ *ӫrp($ޚG&e^i\s[:-7`? )`~[arHK72<3d{K0' 'W)H[P YUym?Sjɢh9b/fE zd\{RV_9qxQoktO[,h_Nr4tmIp(}b҆D7DLY*+v[[2V4|y6IeY(!Ń''D)"εܞ̄^/N2haè;^6ˉB NF !dxRد"]5SUmR"[44=Umqtkq#IN=%{}vG^JZ$Qwo!$W$˼߾RfiKz"Fm^22bsOQi>ڠ1wj}ܣJDmā"{a)oZ=Jgy/ 6%-kzc[cJ(~'g#(2[h;;LyyAN5 $[ );?V1IҿΟ{-1&&Hi PEyv[9~XN\xNܮw-&J&#mlՉz-!^*K" )0ƌ&WiKs]^ JqMWnjuܙ 5VC%ul]?ӟ:LX;:)!h]q!<Ī R}wKpS6ŸP;Mu<}ˆDL#V {={<izk= acB'X}?.Rݻ@2[/~uxl5i"Amnڄμ3Tz>eS#žQUd N9+9 'cּzfڦ#sUR{o$n/g;twAH%7?R-aWjZTr xMjYx|)_l@1D *EQ9۬slvi!]núh#%d߷` eA-8?Uf-atBQshm7<<J\jƩ"@2?nkqU\^8wvJQ$?ЫВ0x4$Lv d0Vcrae;jAxqƵbvܢ9J k,7P_O-NFl}qKÊXI/Q{;stodWB;qq6uH[/dCBe|X6H5w3™*?2MN;50_ k]vMSGq]ޮs5 tު96mMp]2РiA/鸔ZL̡*b56gf2%Ku6Ҡ)tb8kW:wcA7xId/g L=DžKӒl[&/_| 8udZ_aF}*>b!\ {}L; VfҼwaKeE|`r Rg-DŌi";ϪN3 %(,>kݸ=^ ?bljmc9zY)UfZhvS78 l N[Js ל1&KF`墨0}wth6W[,@ٴ Տ_FHӑEuC v[FH .TGmu4"ȯLpI`ć-Џ1,H0u<\ yd5+kO0w,Fօ߉G 'i%2wC/;hE7oź3 ÊM8ꑜ%&>>ipzZ |)em}:^m?I pjFKW׺uռNGˁfӾfOQ૗1/=z# ):#,)x`kA>h!B˂.,FCd95\^~vQ.mwg.z|}e@ٵ7h%h hṊT7# !1Qg_S)V;0c$k71fK EZyҎxձX*ثt2` Ǵq-F1B2w0H~/hӃQ;%$h*`SkD12y{SRU7%.Q GpG-M† pn&2ιI$$Itjszeߔ0}\B:-Cf\`z` P; ՗zRP<`i%|8"ai{Q44ZL ̈SrACG#w2zLޟ'xuwRQ^n4 q#AʻM~*3Ϧ$2=lVNo;l4|9:${SVcՈ)RF#W?Q:#RGSq0K(8XD;fYzY[00}Mku)#V+4*Qq38tH)U e쫿`YLSgկ.|t09!YUs6˗ 3  cU1 gÜMpiΤa82Z`HSwz+ͯð`ǂAD-5 _9s ,b3-2G ZŴOzjY )i*mE `ݮh@Y 6!lʃPzfȔUS βƺ<&Hd1wHχP\T|ku湥&5pp!n0|[et~j)겂sc <5q`'aJ3KfUۣZ8>qnhu}Qpa~L[ʡK^ KX7T(]?dσ։"Y6 ?M$8_MpuX]TRAp=dMժdmÊ^vf6ogtkO+'XKpۿ/MmԔ$>\sCIbG:H 3 Ҥ$,F.0gUј*aҴKhAp85+RB͒Ǩ7WoJUe7>sHeK.4,ZD􉺛HqY'ЛE\"aSP2 9Ӣ:NMfנ67`UQ--K܊I7_75pzEBNp;=╢/ n"NAsyqhH6sZ1оh =+ 6>"PAjQAP8t#9ۼ c=èy>nzB%>5Kz\CoR!> [Ǣ M%nr>yW^%)Fԝ>$@& @ȿ_"_rF2G*Sϝju]q'INeOSEWgUeV}髨VfzzۂXt 47.O ѓ<GZgMf6ntQΟ .B+8E.odIlf 0;K~|2y)L!-J@na7]uP{SI1t QϛD,%?k/#>b!ڪss"M?>:8YVSC@ec"J,SvoSN՛"sW Bү<=S]ƅ[S_"QYfǣ_s >igXd+C}:ͪ(8ͶY&t5NyCoQAm#7c<|d.zʮ dmP"KNExS8(s /Csuɬ"RX{~>Fea/> $g;7d;hhPOP(yuS9rՅM*M/Ė$f7^ݾ3vr8^Ǧ[дr(*Ou@Vq;*tY D6A;v&3M/ÑË8ohB^XӢ~n< 8G)/Ѵd -%lG.YgýIFP J@ieKX Rqwy4( C'%j&^m kTwfrDgdוM_q XvZWnF'hI!^uo f\T{nۄ~$[%6+ى@)pk-Y`i[wwK#: Ht @"h8~1G<'h=# {`rl{5. Bx(@Kjj,Qqr/͑;>qpҒJx.) >AA֋YƱA=}Ca۟qAivphatܪkqvrȨh/pn](pKO#Y{ bDh'.rkvkhl7b5zdl;Hs1qKnYs`#~SŴpwt*5ud-I,'M՟Wz)Ik5(E xUjM˄QH<*h<Fbr 0MH'ϻߕ7B={ X+^jɴy Pa C`dj? 0EhkR}ɹC}Vfs;5% 1;ee`XA[x{^"?W5^~_C U^Ysr!dQڱ-27[[ _ -u]?bx-6w$?Uo-cg`O$RϚ'w~nߕMv% }A9'ulư8(z"^J3F"8Y9se ulj@0|آ+J#nXע3F{mtjhƨ"@xnNøDJp G'2L%l˱)ݤyK za9Ch9 4w\ɩorڢP G9PgGv0b$Ih.8 ~X͎H7[FnޟQ+P>>жoQ$aab`sˆy]7Ze}: ~jJ5K4<jN߅;n^en> DҪ0;g#xjVCT(=; h^&gTZ]ϪG`hyE)=޳pV{q1\@b%Brp˗u?ii!BI6ߤUD0z^"ds/Z S,~!бe+Q(@ nLvq{`GT+Ҟ6H_;ۜ>n1wLcy)єET$a*83ڜ݇g*}onwV=ľՁޢp=6۲CzW?{"GI,S)TWtgp"._,^mB٤ā, lK^\m~#@쑓0kJzt{0!KYZ,t±%1ƠmN**DK 1` ɵ7Z_(vnIww*>Hn,uajRш[>xY@!ϊfwtj7X2{ۤM,>I1i!Fp[NvhR`@N>z| ,9ý7R0V+] L79A,VO Ky5LZi1i=)] Vf#lVN>9"Pap/z40qvn;(gs@Ep`^_j㸸"GiCئ\ n:\L; }ExԿ3XM @ZCGXp<0D1$ش=6+=%gJT1Z7~mՇ֨! T"Ĺ=Fs)$'7 &g}el/T637;3{EɽyK{ w/u.wdV;f'fQ;;}yp$/KS՝K " 8m&8K-C4>ƇAZr1N}PWú3InZ.[FSKbL3 W> .5]$قҺ񒒶}M@PI'h9LqB7Z؈,QZ0p?z=+tp)2 zC{4-^}wV`Fc_5pBrr*pB$IaQO(^V8Z&(866dNAR]zzT"S|AB##܋hǕx.C \v=+^BsPL5i˪9J/=dݮWoWa:7~&wa]z& )M"Ζ_/\.z'lRb݃1[{S|C34Ic95{igx :L‰%>jpt-' aery%A o41_ `3qu߰ݼgIvF4mYХBHk-kZ AYwE${jxXt~SW!11r^υm&HggIvN,gٴ -jY,"R/OW\K!q!ͻ T J(Vq8f gҥl445K~$ToΞV3ǬX-)lT9bo=H%w9p=Uz@#X ǟ\rYLijij-zRī"Ŗԟj624 gy?tQ|wY$&)#gJjupkt+ Cj_+Ewr=(O$ke^qPFٰC==~ MhyaI4+Ґb0U$1,, W̞V*AdaہHFH_C$G/  |Sod2)O %6J-$u Ct\,fkGisN8D;ۓ+l(14v_LVbPcML5}${7OZϙ.RK{@-)hԡ ]]N3HW䮷T{l\|i3c{5ɏw)pk2GJT=b7,U8."<{?aQ-ϦxZ Õd\GvE$d'T QmQsxpْ_)ȈV&g !u-*q|lWub9-rSӐ='KJ[_X.? `20X#g!ogeŽl=$E=cZ9d*˓L|a"ex JXI6np1AN92%DQσIƔok?JZ R#W\ ΔQY>yV?[4%Vht' |X6i)( i,gƴ@U| 9 հØm1çLn|D@Mq-+n U13g΍H /Zv"ݬMy0(ׂ*D_b]s mQFoi%zXg%ס)T%Z \Q|"j۳5JGpgSf1XAs3"f?0C(Sa$JŧE[{̇a ]&G~ь9Y1uZz6||J}b-HC00gT(\C7ш56I!u}]m.$ap~@kMUeL 1F'%7ۼi4HbU~i7Fi6s PǨ]:\y`9D /M%񛉼w3~k/(Apڎ7ghMY8DΊmA셍ƻ#ͥa9z>n=lXZ5"ezHsajnUTFZWa{w#;rnD^Oi !{8F|!lgZZCL<x~_>tqmMF@|?N0OrEZ&ϼ+ppMQWq*Y=?%9DXÇŐEUjpï{덆x~ %~ToMf8P'ª`qb p)=!,ڬh "P<Hr`G ~E-R)tR&8$HP]:M(Å;@d;q-rJ+ /k1d=BV+rH=pWk?[F`dWǝ(Q N3 ~^8mR [6%&S立H| ʋA\1$z".\1߇jVQJumF]]y&4 +7;g ŃG8tpi焊E-Av޳uKʁUXuPVPtv<>$Ox*θ>zȱV׈QE KŇIEKD L ~9{g$g!W(XVHN6rp Asc Z㈥*8*CrۚyGᥠ%2j}R!'v7)Lb/2NR(Hti9ik41Yl1w |y1w9K5؊ۘSBB(Rf06G(Fܶ>*v ns` 5)6˨loRo:IS.Wޛ U8PؚJ5Sb2\XRp\wC888 ;=VX)3Yi&VxÄ]NTzQLJSizA<1/z6i?eY07|0Ƣ60#b^1c`JDž bGDׁYbg3Nj9;pXh|]W<'|KrĖpHDbwC¸ӆ$IߞO :.^f2x"n0ϏEEd"wޚ0QPg_R@WU)2(0dɁu(Uh[a3zYrfB[\CnR04,TOm6oK~ۺ:L`az@ WX)W0Ov10˧7ib< G՝B k/4m߂:9&p7uTUZ019ңڬbm@Wj9!(!F66g`G N T͡<l3Xz|eyY"svFO"QqIaGlaêI Q} ,*DszI8T^N5ՠȵcΣo]Cmg{R׭}3:lpIΜ0AbGK`CgWE/jOfvBW bKP^jw^J0eR#+`-/.O$˜QԷXpV7SAhK"ػ¯Ucji\ҕI c"%5kz~/bVf~jZ"4;;ez< 24l+åU6d0ىoV}Rna8q8"b@3+ pd60mDYI%q{:ùABpqHAGOТU!5{c Y+L7. + =bP(W0d`3n#%wLya,MۼtN\ *GnG`tYǂՕRSWkis%jWϒvTLY@UPJ4V] S@_.{HHSƉ8Jg3aCE܂'5O3$)0~.8FoΌ4DƋ8nlY}j4' Ʈ3R+)(#vѨ)/\ס-ZSSa#D&=q=,[\7jn'd+10U/$L!2_!x^KxRrofbo#6wot.Jb.,(h[;R6 0_-ơ/";rFؔ4%(c;g+LAwAlǥ(Z˜(ʆ".7.UZz20FgDzù'9twR󥗚QP'A =#;"۟{q-┳-P.bA^9 S̖n$[R_tf!~u곶`u`(#;H4ȶ$w=oTw+w/+#2.@+,w碚`-Kb<(]x%_X|.l2˽fk!ogU\͛AcXOQ/Šn1I9UճےFƍ-lRwekȠ9=~zwپU9.eZFYIU9y%ܟ>ڠ0{X6ޑ!]͂]"]yRM t*r +?ۙ \pO_Gрxr \m ebW}qvMgQ]RubX7 74Z\0rt <:K*r̖,&nOY\3q%{KӚTęԫ02, S%x_]/@~ĝϓ'{DL#0A8i; !,.@`l*h%?Vn Qd'܀14M&h0D$^ t7 A~:P{ϹTkI\,'4%`IX)]%1) |Nr/%v9VV}9!Q:q\4{Cq.6v5Gc=gO# lz^\deߕ2Kq4V ӣ֍WCíݜD]v '(NJa#_cŇ3~ܶnB ;{Wa?.6n=oa_II2l,m k+Vw$>1کV#.C,D˫[b=k<5p@BtAnLN>ss;De헣Ph8, 6z2Wg%Ltl4d3kHEW5@Շ=ǖo{up4?'  ~7ow_5eB%GF d9F {.a0wp)FZ\'U\smsstN{CdT;eU[]|wϴ3(ȧ$;\߁y[!9/LڪVDtI`d#B@7h/t֧u?9젨) [k#-p9c TYRڵ{q-[LW;}Y e_"3[WKv 0B?$eM*2P?Qa&IGS5=v GZ2lfK @j4uYYS uF2Br$|S4Csv($,l>< tZ] ph]\/_Q#h3irKU5pW? NDڇJ d(x;{{ G_Zf/얎FO*3;6] NLl2\O\#3 -dnD݋R[p%&ݗv^d,}yjR} )ʯRw\r~ݛB9ߞ=)GѢYcWBaaB<jeOIµJU J犓-T!ij݀|kA-lf#;;}kɱ[^e{3(0onČBӀ2"~`U "x3 ætʟBaze-w/Z}p&Vy譌:3]@W$!DeF*} lSlG"c04ĕ4a\uwK~9Iuo )j#Q#;,}?D?1 A0xE m+z Gh56e(Q+Q+}x:0ɻ&3u?\ vCO)X~lU@y!U"ŽiLZ15>~2(1 ݳ뭲)uj'2wS"NA<6=0"B)9iw?O5Α1¶\"Enb.r\hI)DGbWmigKGW:Zek֮HGR71 V{wڂ]SÜ1{D`>ć\ps.KeD @ΟLxs5MvH'IQWAX^%Ī9o}8j~"9aƮQm!qP^8=Ҏk;lvi'6JӁ_$S(D,l!`[ } [^]u2zS/g\ji3Q _H0Y g##q-OϕC\$I!Ƽz h)tMfmi봠@#%+\LHТKD@>C:蕩(nytMG/I9ymw6 /ܙsO3Wſ_nH=k۵/ RYwY%KaŌQK55:-4SFyF$gGjh]rP:ʸ6y7'~^aI7aF`1 ˧ T{u硜 A*ιV1,廨nO(ii_X9"|Qq~3@Ж̅o%NxzDshʉo2ΊYi+pCc%}w Fc0ܳ\Qz`bÒR C2>ǻr- 2N1u)*_4/N% >.ț9W9Z*$>XHoYF/O.uEbaijj?΢r*1lX"н9'UmtO{-&RWä+5ɪ4ْ# /#O%Q܈o oA($ oQ:)Dsl?v=χ`ʼn /!wsl+UA$X;D*Ʊk7Ŭ}/!P<}Cz7s$1"8 /+p! 4fMKKSJ[I |UJ:<` 폥$)AMc\r"-9%Ib%H 9R |cq#{R7 MR#vWF) O62\t%F:.hl]c!Je9xi "XΝꀱ(M[^{NPcHQg`YOԑW^K@R&Ӂ?iYqp:Tر4ˈb{O٢t߇kz6dV Ϥd:Lr";3ϤW2"afl$NMr ?vw"WѷĭB]86wMJ @|ׯfXھeb0_Cn4C['ݟ:_ 4_ &]1;IZ;K>@(w2;?;qٻ(vNEy6~E&~9b bѩgrϻ4ʱ`X+~iew?鍦] q01;F5lp(~Ρ!Eti%7?PKAmԋ\sVoXǠ3 d\RuR:C$N!PsI,Ni<s11}9t4ź[*7쨤zd6MjAS$&c  C(<ڛl L2JGט~sAF@^Ͷ<<^5iZ^v2T{DGzKδl)si0Y<- آIc 2=azU7&|GKižpC veI,T ^:g#ٱOƖy- ~-u_|֟;(L(1fDc[C3עTq N^{5j5[î0W'Ifxsӂ4'VNsLduK{xa0ύ# K?l %2_IP!\)-kKc+cZG{/T#cRx2lY <^KMH&ULjd &}ً&ZȌ`pj'8wpZCQw>ym!eZPǽVޅG{kb~V n憫_-PAHL!?aDX H wUwQ-#r45jBAbS}\c4"9pl0_k߲;pxmP4eՈ; PTzRW|'`"9py!jz,d$/޿-.&aʴkEZ! / =Duu9%EP CD˥g%]p~[old~|8w-I H3}MHE_arŽ1sM #*o;v%$! J%]F53l~T njwou d/r@a09dT牀N)`" dPVؘLvjU1A\?,qx2?_H T$гuPeOUn| CmH:ĺZ7 si ]1)T7f;OǶC R%0\%oЄfpg/ X*`HQR53$\*bve [+.&PjA&`~}AlipoaY&Ͼzztn"Sri? )p *? ԁ_cItgZIM`$r٦"bRY58ךvQHW'Sڒ>΀bqZJ;He߫ӵdT ]#_:payox3S_|H+аRal.y^!Zθ>]J4r/N)KXXmNC>a sLaU1᧐sU2g_+}o^j7~ޑl@^hw]/}\y"@nzkR3qRuDUExO%8檦n`5_E&pW  BMY'z+-ltK3{ZB, B7v܊v:B>h^;-%/PJξ`j ak}ϡZjb8T,ȾW3KKk>ˎb}ZXSVn1N>Ɏy(ҭۇnDmSДxW-[ - W88W->V.>%PٟfJ ٠1oh|$m-:^VkD~(*0th\.Iq;~g^\ fMBYpvW;ZiǗַi(zj'`IvdJpKAz *JA>݅qK䵳Z?U06RBt#y˅JW?}ɒ>]J[Y͍ j4ׄ<(;>ct=f_[zN%xAͅc^<1(÷qAHB9a(҈9~ fhL^{dwģgł=MzCc8y^+ L4cN{YVRQL6 \L{xB s;-~5o{Jo ѝ9H Ayd&7?,T(t'%.|J1QVC.CD ŷh?9_v!䫎9I ܝD/nIutܕX}t-x-lg0kc4VJO/&l0tXYp#a+㽸z_趎"vV?̾|-5穐̏2͐([F#h\]ȫzmipCY.rv5wGj e)@]qTbr EQ+De+'*{45H/J 0Jg/ Yt!hi&C ?Zy؃9&{//IV\c8wgJoKd.7Rxr;!,[$hsW/ Qx=LZ΂<)qZ dk(" «Eh:л;L?_yo_kskS#BY,hdCGN\vTg4oj=ʔ'ߊ6c:_:uviRe \BF*@'-[#ã1˦MT "@>ËC~eiza=RCoz,}E6O* Ӡ3ETT'lmصN!dcݐ^i=3,WunK=  *P>8(T7Em~a.tR[OĮwAa'sc ?^]?S$ĔzsdYӬOO f#xT۽ >q @ϗTOWo6:꾀* =xr6ߌ%ywF&>!~`gAnp%v0O9( x٘oQԚpidF?SL{kp,v#4A%Mʹ<cOΈ[ S]3 wQP_k}1#8 qs:qh%$6?>Q1dqE {(IKoRm. I"̂(B';4=IL|̎ҹ$¨N"v<mC(+= "@Mf%Rnl{3-OHp2b#Ybr6d 4-ǫY/r*rg:, DZ^9xk./l5Ҷ"*? ep~a982SOC5D؛\: M&xd.Uff@:/2/?~e;6H0m'F[J]-ő!Qk{%[[͌N3XI58 H+=Iۆԃ!OQԿGgک-gDp^WSB^2%RNz?aoוwƇ*4G5,N qCgg]ʪ*rXɤ1Jr=9?ɌBċjݼ̦bu_p>"Mcx)qsxYzG2wXCwd'['c(8VOx.unaVcS/#K Dxm[`\km{U[W.{M;)gqk($lS@FK~VX=2gݦo>"r#n;C jw|7=. $&A=gv3gjRW[gqŠOT6nAX~:P3 {ْ,{3i #uy5T+S; dW%靌@T +F$]UW AR)& Jo*I7l$׽=7AtFa#)W\a`5zTx 8BØBCٵ*gdLٯ.博G3_prW9YSw h"^tx.P3~~bfϦ{pMd,hEWpS #,i#"rq|q!kE*ȝ~uv!>6989?),PMp /Kk^.ڳY'BV9 |IAQNŇuǏ筵g:bYC02<0%;U[r)}­4ōU5"BJBWq#d8rI^`3ؑڇg1xS3R4NսH/eK3TgB$o2М\pr;2#7܋jkDy[.H8ᛢ C^D"y]̝T4T\6E`=RϪpIM?O2?f xN쓹濼#۬P5O73KK(r4˶?ĀX ܋.׭#S{nnJz{Qc>f[NAE93FN/ێ@M|(e/gVa ̌=֔Tj2QtOPpmZ14m^H7 'jE8%9AD'tȧI@M{$ʴY+#bBٮ@irpTT [qXm gf;Oô %^/nBz{ܥW$;GñU?^Ih%pN.|:3+54 <)QgG$JvO *zD`rN*Mvd&b~k=%R;R '4 nLIndD)J)[jpəyBiI\)h:u-!_#^EJyxФ/ޥ5 "7ס瓰=z)x9PP$'5 TS1)#OgkTWm}:9ń+pqǷ }PqZ73.YgLttɯ,E{J#g2{2\8vgŮ| tO~ld$(ʺε;҃@z,IkHu|ln_% njg~^9#|ъ 8i]< 4oLE;CR8 89JԜ;3}E( *8Txٮn⭜4FZ^ƱHhw+쒚5D V0|-t&;W/CK(!̢Zʰ'vߗv4'N3>>g&|f*_q H[\6a6X-KI쓤b2U8C2uN .f5ܚ{7/}H3|ba\lǷ尲?@6r&a9UBdf]r>c1zplѨNou$̣N)j ՚Wc\u@N/u~[=w̞ )0J % BN\exkŪK}Sm)<`ڋ/L}vp^{'H}=2\^Ø xP#zῢd6ƺ Ʊ?gFz=mzŢ ]l5 []њ貭jG-L:-6U8h 2{ڱoAj!Jq ,# vA<Y}$ =rZn43rsWd'}%HGþK1 oD- o 8n HWگHoaªCZ;Z߅y Њ`@Q ~-1=5hy R;ԽP^P X=$[bJ_Y"^W!Pncs"@e!(gqT=/C[lJYTQbLvlB'1 >8*QW$cUv> RH?oؑs#!n6Όz'J-!A+J&\!'ׇ?r>~+L;K4.\pCxEDo<͙2Ez EZ=KZQra_jv@▸/IHQoXpbߢB$``QҲȿjjQTB\&:E]vॡ &a': / ?ʂ#^jJE̼Y%Ptx;uC"BwW5o3Y=j˯ ݙeлe.%ґrS,mG:{;*pZ1t'Noߦ#30=/CqJx\熄`).~b+Xgʒ7[ 16(eMےJuN&E/? *~;'7P)Gԃ}pmȠw+'72 W@`fBICXGO6Pm.=J~ފѰ`e0lYZD qaLy=rW-hk_׸'6wCעČ2+; 5Dݦ)vj8E9E)Z ݎ.}G|2ZH3Im~&l vCQc k8ޚnx/tyڲ(֛*j$mfҋV":MlK)DTLl~lߡ_~o J|Yf8Z0 ̺+n/;ϬFߧeި}E~^9IXm#@« FA"(ppH+ O3[иȘJreYI$3S\zFར5ؔduWB ""V!+ƙm[NԲ{Ӡg%+ƇmBo/-[NXhUkX{ZeS1ҠTMif5 ZR}s܂(n׸x ]nh@X(k\`peH981A^Ś @utT<F&rRى|.9TZgNcxYb63W"cu9gd'c@!vHc5rj^UP^hpo+`/|0~l ׈0몣bTJPJ՘ot 2BӻrA= / x+r/ ??w0s=X~1by[](R{>p0prK4GoU+h-.VLGՌͷ¼ږ]L(O `k1b*Oã3?&%B:.-Be{IY+L5pL"^Pݡu]`>LqH?&rikY6+`w 7F3\v)r\I JHE`/[dl;^|=Ƭ]yoSJM{;2!fO+R=9㝵<}Ā0v7FwƏX q2w=Rd;%?j,xZHj"#MxqߥV<&]!>ZG"g]X1”"Nø{7e9˹37w ] "<5Z2T_eHM+J 'MMpǣEO~xAXnK 6UIm usXLeEwvwrv MdiA4%#sZ͑5e!(}jܾUPm@%,-ˀ*UC| [&$$i6J)re Es~&PZC 14'Db<('2ވi@Yf^b^+![5ޮ! 2Vt0%Ywz9K[]pYŖ8S?a`zvl秃·;6ļԻ)$^RrVO1S~y5 0sIfM7u֝zRrZAu A6?,4.f|7٬1>MSŴOQ bSi=υ;^} St Bs 2@A'ǪsgT{ /:,~hGc4ӯJW !7[nItQw%+p|h{hmY}ōKa̯Oz~^A=+g+uH~%Yg;SL@ 8^_Ed=d/SI„leX!4me0l8!i5QUfC7"˗ztAd-.8]dCn6t#EICCT}"͹%F, |QHɄ=3{GRt@s`+E!'AU63OJMTPG.킿+Մԥ;e~BufV5K<@>60f<9uLw27>!Zt@RϚ&ځjJ,*OQ,,&Y+c꠩@ΧXOap`;DtL{Ζl`Z<9Hx^W6kO8Ѕ2+]I BMmX'8Il/ߏh݂̦?T'H&83IdgjϭTgg9Zsޤl%Vyokky kVB%HHXo&6CB( 0nW3ϷC"'{fOŷLIA7ŀap$lD;CZemf2c짎k(cd['_Eݑen+S5|f yv!5n(< 9eZBp>H7lSilT!м4碌 *cJ+BmQ qgaRN^&o (k e(^sf? ^CiМlХ3{v"z9EJ[KQa#rWB.dd>)3Q)iiM;Om.{~>'ka_'R BQ~h7U"):nNc+?;։\_*y8&z<0d2tN[cWLjz%TY_zv:Ѷܾ#ø|>5j:f]paN)XOm$Pl l+7CS6}h3JAXy|{HN E@W#}+?|Ss>tZ"={wa ̍%\fV`aQ("-A>dVxk4Ҡ6 sDB/9>}xj1H|v8_Mqoh k j͖U3 V4f>ԏ|U}4֘Y vwGNf7lnQi7|WT?Ub{-= /kW%<"p3$Qƶ`>:x "@.urϗd ! Q8*W,HRZ@E0nZ<]b$tw\_ѧ8Zzo R;AZFst'6D>Կމص̝q+ yTb@g;v/-]tTKq>; ^هȳ8#Jg`vTfnHywt@H:4Alȫ$n7f~z||Z^A{J-GCB$X0iaQ'/o^kP 6{r~%`wo_bB[r5ҵTu&*%9-f֯Ti:!m&63Zfq5s劼ɹ6~@9Qpry]9(rġD_1GFu}af%L.CZ?Sgs*L<% 5DhF(зX҈S?".+kiVCBB QEOA 6l5؆J UZZ*xr'#kv..7z0ӏ I(Ktɸd='wKLa^m-u/fY-iR~mSV\~ sٷv;C10sQӬ[@}xgf"Z,%HCخD謩 R!"]aq_j\LOAv9%zTC%+_FA?8kRڲi-q*W̞S1'q4\ERd=4wv2P[G6/D2ιbx{VhO> t=X+\)]2JGX-M &ߥo ~8aS% %,b:4lyY`H"ONnݼg 7X V~aLʼn:;AFvH] (KxA&ma"5w > #]\C㐙 =yLMLhz^40{yBPv[|x~C>ڔoE]zy|Vo`,գH  }`GB$~g܌TY0z lv:L 4k4Z}((=ez.W~}u.f2.SdڒIpXb,ktR<>^5oӗ XNIo6,UCyXdm6:+垣< }}mӓ h-zA4%?Zsm֢bU VZL4tF-SGಉۻ߁pOӒDL 1;#`ohԗYfk:&VyL;TPq1eQǎPh}Bܰ+ZvjkmaqZnȼ4Mj{üP$?7zYU5|[@;{F7\|Bqs?(#j/POr}oRR8$\vb󠷼Cs8ƿۅ[U'CR9*y |{vke"V4lVKily()IF dd"ve1h5Mӫ"ԛp,|6R>$ܦoqB!1| *T+^ڥv2Xmi/'e"᫅=;O-[]In@5Xr[=ZxߚF0N!̈O~zQ\'ႷlgA Q^+q] "Bl]H%4tT٩ ÐnR;Žp:Q}rOQ=&2²V|Z1%&)ak1͋FrpŢ9휉c?qY#"IekbIHQ^|pWp a]˅X|JU#7G~u9&1NX%u Ztt4]w\ӨÄ'7ūlZZu7ybR˭>9WӐ;kz!B$%k_ +fbx _x^cNlyoTr#.jYg҈sQW cx]T'K-Kw㺬д\(gqW}.U[j*'x?[sVFZ}073`*pi~ E655 ;a=m⏆YʴXa(jBpЀv-hGvb@eڹ[#<;$ǃXӏ̕<U/R7>x,b\ZE߻DYP0ԡB{ZƤ瀝hy_yDJ!D-0ݙFQ_XJ,3E1u'{=ON+NÐ#Yp2 ʪ|LAj G&\D݊koK6OkC:ylE=7O˽WѦt'ʬV,Y@EOt3nwMPUL+%\ߧ*O4.1dŰZtn˖#6'12 hO۩gUqyͲ ZB1"׼D=##}϶1x+vrJUE'CGTޤR Yq*)=kl2ʘ'|oZYx$^:2ŌvkY[E186;_eJ|* t8@Y$B4|rD󬣏ui- |)&9 Y{y9BIX`fefM &T9EG`"+vv!S}Ա` 1#0:._Mw DCL {.Ae2B1rgUG S#˗ě&4ԥ4ϫy0_q~N# i5"b+o=vVxG KY+EaJWȲrA gc̢$O< ,fdM@q,XK!ӝ=G-#CY/dzϚL;3R ?_xGkY5 zZM&@p"v_Czv* e?" ; / VZb+?R0XuBQS,ӬV)2p}FX 7"r93S-0] 4/8&lޜ}liؚĦ$pY!K5rS7Gz3uY**?<¹=ѩ,ZR5 &6 S›CqXF.;DWzSXx%uR4vlBmqMgҵ&VPDd'Z C =n:V@ k m0u&;ջ͠gؕ?^ϕk9Q]T{~KkBҶ~үCc >UdMiO+>d~WCi ]&~fVܿ ѯJ  [ VlNŻ4u~ 9"مv|4t/Aw=aDfA=xփwG `&6V7+ pd{a!S2NG"98E\ķyT@G9l%nJh&qsA+,ƱT*8(;#Sn:I'3ma8ЋΑh-a7S4EqI.T$ K_8TׅO%LS/_9|tdb ue;F'M0j #ؒ? 5q~`Kc0x J9%">FS@@ 2ea*Eh9V<8=";˦IMvj;~.EP v^Y+h*.PDXhC뽛vHA=_ъlc4Ka@*LjCHhlܓdr=nb/]9m``v Ȑn2Mpk2wQI[Rw>ZGVtH*+?D]6h,Q8o #t(De*IV1`U:MFY,yt6Q73s\8FC 4, mL7o확nN $dV1mD&tȅNC]pxEm(nX$˭鴏2^\ 5mÝڢfl(*|]NfU*5^jNXQ!4u):ml7s27Ov]t1w_hj0XŜ΀vS3:3ۧ+לF4c2_~s 6)bXD袄ԟ5|7 MԄhP`@ rϭ i!xqw.<aL*2,vb18p2$˛n_2>՝qңYNᣍ@H8{VV&_8Ӆsl\ |ps` Lo!Źj1SL,p!0]}jRĈYq/ ޘB/vC]#_{e_K3ݝ'*]ؙ7 4YЅs;gOtɔwS5G}hϞ#E[$տ:tqqgKM䖫`QH{KH#Q* ͝aԹ(JF߁Z#өDOM8O*W)݃ 6D5p!u]Uu )z;-$-U> 0W\b _&SAnJ -(D dcqA"̾V,Q8간tSi脹0;`ft%Acd'}2q̀BJ]$ 3T h9/S4e"zfq{w0k9BW*oKhi]ʝYӃ'EG^_MdHwxmy-ݛu'~΀SuϛA[6[vmcf΋ `lPtvUN# 7_;z@'ww@YWK V#aT<dZKFW.'cG7dH,?-Pl?ڧ1t|F$_l-LW-IƄ l0k1/n Y&"Z ٙD< iHR("9o(F\Uo&Y(L0*)Gy҃9,v!1%"߬ \Mp`IVG|8f`Ib=V&䖽]܀c&\uZKM_pxL/|fyQEJ~`5݉#)"Ih";VkF9LSi]tx] ./iMkR>'%|K ')#KM"z̖@ЈHXYΪJdIC/5؞-ȨߟCC9ҭGRNSm z`ѥ1Đ異'dKxMhޅeQ(>1?xGRB̢svPl԰ap#A qR7pm&l34GoMṂM4EgzTK2J@r17QjaqGBEx]eEwǎkGlaO֕j!û2_[T{Gdm@ec"+NhL#ĞcO <#_̡ |Mi=XrkTCs3D90R$,hew{{{QRmy0ŽMw 7?)*A6@ZF{:cC?nLg>@iRkN 5*G ^[W6(ԉ2J̚ۿᐓ"5$=/}®6+;dKY̼nD^f#H[ަٯK-_VBI֣aNo%24z2p> -TAGܷ6`==NrЋgc|Pܶ!)S9.^},\^a]V{]ˑTvur|Ni|p@b6ζfbkD}5珅 ?Jt<oز2`?9x5>M4 tږY(GWA76*zӇ"VF)~8=^x]{J*C+V(u‡S7{ݯ?r(TIw/]s}H;Z4c '^?w9S#;qRS]<ƸAQW+tt9и<.Χ?yG 5n:P@&{s6Ȣ1q|Y勷ʸ[R**6hpťd;/21a;ϱXL ~ ?Ҍ.41MB9,-T!WJzn*搌d{_;/O֜mPa/ 7m!/f1B^5o=SOD^ҳ0cZ͈cr - dhsDXGEd#8ͺ`5:X'd7{? KqbfD/}2*b j]]row*a(A$"g9hE`Bi\ ['[_{sm6q]G`@_Me<6pMӪ'JYWg|cY_ B$ܒ}o&"}[hrFT~XhHOz {$ÛGt8({ Tˑ} &? @)5ۋuvuB%|/ݟcUmh6b0d|p3ОWFǦxt~yS8t.+`e6ܻ̓d,ܦ39|ڳNWi;ZQ(Mj4 t>鉺0câKg> 2!R ή6)$ $uKyh lRް Hݱ]ڝ͐4SlxK|qͤ 49=س9nW7-q~d(ER&`U r-nxh n)V$?4ޭ.ޣnG7%M<|,\gC\:Y8k9f2 _mveG-1rrW(ssu{yjjB :h +09:X)%O/ךa']ܜѯ,ʌbJ8_ hAAU4Sh{_)ߡ *Eڐפyu@ci7A@:>9k|*;w{4gz'Sso@/_F4lF4:R 0aHi{/oi|ɇe`Bq߇+^)Rg.#yDݸѪy p1zdR"-(6T:׀͔T8H`YaY#iDsp zZBICH^`:Q{O9CVi=lp¹J!2ڙ4t sg* xL`@ [pFwuj$W%AgEU3@-*Ќ t٢=\/g|rr[@Ps}Bo;ۜ_a nܤ !NbLD[v떈``1SrqwJīO`lV(J "0ag *Z14?:0eա')L}mӏMJ}m5߈35֏ʇigFP|vzYAPw|i٘/GVL-MA\Ƈ:`O#uj0:jmLyf^RAAJSމSa Rt%G [u5_R*sP0a%daxHK [vkKz'<9p4#\e(CJ]3Uy1˂ML=m2?@{5u;>kIo%L(dLXqַv4 XT$m_t#@z|Ø78h;$t|a7ƉQY|$ڨ0OIZzI8|s ifc{DlzaO5c-ݘSFx Bԓ+d,~㙩-Q>T!< M5jB7v/?7]0oZv"4XJUL,[@#UF^APJ0Vi%|6 K *7HbΨ#l%K\]7?T85yС3B֯.h(x永/(R щXU6Az#y35'SPVt$]G9fL;3B<KҀxի Xnq4l}13rP&Dz]fY2;84.rd9䆨CBiv%bC:nj6Nq;Ru`Su`F @q區:/3ruߪ첗хT:?.N6CSf[soǘv~!sKfPSy|~5[w蹄D),./+1R~~_>PmZ3"5l9:G~zR|`GםeJ&Hg@FU8dMB4!8tB2M<ŷDG+*xD>qpb4DAdJ *V=DfImVM>{P-"UXdԻILY{w[xC*Xħ(mP4̳-Vk!2#iP@lE 贗DdI%?_#} ǬRm[v`ۯn8 &jXwnqcʟLWB+8ßṢ]^2ia Z)84h{tz }Q >G0]Btȍ)<" =tD$OΛPUn]1ft\OeOd'YjuOLUN&pbwOjO`u?BesqjdNq>  k(,a|0`*7TU~ -o! .JD&AoUZ5 c_Y̑z7 )yd7F5P7IɯE*L pJΌ|}L?Y Ȍ:5C3]>Ӻ'$b S⎲@|$07^TsmmpDyc8ҏ5 .Wuu=7w?A"|HgH=|Ƶ~LEyFj_E(e]*f*U{g,sI=6S i Ӻ(Vx? +s -fB1 ^k,->XiC?:'{GniM䨶*Ysshous!#fh8æKn3v \xqo~pJWBp'} 'd72J_)1Ḑx>{<5;,ij^2=kTQ KdDD/فq36[n } ӫ;;<LJqBpjqq5ldui(vqZY$yg\=NG^nzV2CIw7ӇGԆ%|.1y6ت@8\M@B!D/u|H!h!( 8*cjXm pQҝP0w^bJ?)*tK%Թ LH8ùEYZYR'c\/f;4p:ý9iT6ۣ}Er 3ªqۖYt`cáY:7Io@G)Nz9bV~A{#֙Y_&% Wv"jI355#ߦL4 JS[Cl$p, YbR&D9o]+N?E},R2{1$)23Pzg73:}.s+,SCP!r,~5`CbFa6S tY07u$4lO_hFjPcofČ;^ဃ8ahq^^3.ގQgt . *h 쥴Sn_2(ȀwHw۔iEO@pHۻA<"`r@X`Nr9]tQq<lN Ricz;Ӊ8\{aOsɧ&buH_7qȢo ͏wyLoo=37 i}I,\Ʈ&'lX5Jx\lw̦$p)idiTJ\ Wv|!pg}9$ϱBI:YIYԆUZ}ؑ]5 yqw7"^rS; ~֜)hl(RH̒E:9+r p;9Pi6BRrVa^N5w:ָ3VhB[p}?ĭoF7L e/."rg%⼄§-cG@9~8;Ô7%?.EάG&.!O3n"H5ݟwͦ5EJ~4`q"/6%L8⺀el^.zwFe.<Gr$GEw34ra\)K¼˺Osl=8њ$!_V&lRo~bf[<);&T@iG_dymATtmȠN;wX\WdKrPNj5gjvʷubU+g1UbyOviT:PQ%돊pdrTk^ymu, YPO`@ U "`PrǶr0\ ǜ p"`O;,iDf I#bڋ5]NleT1˞\4Jcl7M.D@~j0Jl{D]H a7OHnwgG AB0i5dO.E/[:DD(W0Plm;O/jv#s\5 `nFuZ * V*-Je'A dYiJtjv؎f9ߛ4KDp6O=0A#x=j7·}Sv l3S 8ڞxEHd6i _*!l`rEɲQDZ:4t!v`f O'QeU6֡d3Fd-@TJzPzee!?Qj"皗"ɪHkr5jTA9{X{MC@L—ӏ5>;rwD`\Q >$qcYo'Rj9 1k-OȮ+Zw"2V6O{&m#]%^>v7% s?zk. ^ nTsՒP(n ɔ#EFG|W9Ŕ.y&𘄤%[6iԔL%2L:jo3;R&tRJ գ _Ȣ"^E/_7~ m20:"~V̦^E;SZDŽEk0$bWKo8۰~˾ 8?`sl~P.Ԑ6QC">qI7Q,DZYȶ]@.]KZTY(7jZLع̝SqQ^x/A]WɇQGtmH:8Gw+@TӤ|Ҩ}7T ',W p ٷcNM}­RֵԺKA|;BX0d)/Qx`5Bi#$b3kCŷifͦM|01ҁCŏ';} ^:4ζp]10di8W|yÄ`KzNRZ{X`(9uɻ )KLb"^5K6wm@FJk mnuW"&/쑞1}F55h882b/T* ޭ vgo4{ݑsjw7Njܙ$ [Ua&eO%_B5~>>)C1q eUma\:IkmD$)rw$z>WBTY6Z_~Q.C>\g4_9 oX9QԳ9$Ƹ@>%% g~l!W#BZ+{=&BS:Is2K6xҠ,`%u*:jEW92u@Q0fH|d,ݺ<#ol6 k y$dƊMlKEϰyn?S} )Lb֔Y{ϱ>0ḼQ9x."'S`gJȜ1/#!\a!r;2nhj\]Uә5G.D~8@Fjʸ^X)5/U̒N诚 `{tшs!/p) ʫ$f Hhw=%a+riz &q(|vhS>gK';BMb bGhը!j`9fzM8j{,6M1E߷أx2 ?U)0jSu.@b$c٤wL*[hRg%ON0O^*bJW8Ef%&NDD#Q5%MKLVI-ĝdjqQ8`NnU4'䯣~L7x';f#8q( 5 p v^Uۊrm}b8&ۋTYWrOvD*nc*Z 6|ښ ݄uk/Bi:г)9B40)nT#VEYٲ8TBݢ]m hA4?o`qb;c]VTuLz} }3<^%>wBO"[ $swq41Z%+ 5bR'S$ّUKFdSJr v4 (R8z*!hb͂SEG[LmApвN}@NLJ{wQ 9y8T#tY[mٸ,0U+Gc0=Rr1f`/svxnT o&O _.|S1?\僷 jBI!0EWȍ*O> WL=9;7 1x <4eށ 72کY2MPot JxҸ}Sz̆J\ˤ+*ڜŕ1 1H &]τoO܎j/G?Q>:upO)OI%\4lBC2oRwSb:tC/ۇR{Ilމi ^אK;Яa!ӯ֢iV"Nvkѩӕwa6T6O@~\CIT:ڰ5V;;h~WB3C Gva л_8Brd+5߯>HmS2`_.H~:bܑD2zo[D&g[y.?` G}=õkno !%CO+O#/)\r9<9lZȣ8Ȉ}f3R'z{*];?'T"ETB1Yx;a:tzqd*MKO)dr*`})aI֩<^ĴY˫hX9bN4 aL.m#ءrN+b{=rONZ7&qzkw,Z[b#V}SJ M X*065\r*+;b?UO%I}?5eiyçmb ZQWx[k`["B~p r-C {p㏳jLΥp0z1|Me,H/g?yb4\v 9`i)JT@!@]v@dc[5]jnK$nvڝ90YlՀ?9/\i_xDIj `%67Z7ԛ4쉃<㘓WlB!/5=FScw:ިq@rA-Ft ?_p@|Ns ]68aaO$?˞ 4pZH٘GEpVxvܵ~M!ƮǏb^#z&U }GLՇ!/!وUO ]rf^5'+}"`"xEWvC'A( KI ɂ,?i0NrlOx3!xoN"377}?LBt N!%D~faBLѸÃuP]\)v綾?ѽ8or~(+UۀNFp3RUX%Xr4mLD,czƠȲ˻d4Oxmn BnHWn6 7+$ຆ׻p偄mnopx5{yȨcqg+b]m:n f&\ ''˄d-(:˨u)@@vi ifadQsF)Hˮ"TWTן?Rax ;M F6Ge Fo !frm?ϋH|/"6J` MeIgC`IA[-"n?GuH+;g<ú%l?U( {P6`z0/& a';7K)*ǣ2ĀhO.c lr/+qI )*`kûQ``z1+L1H5_fT]$>͛n}[FL`S2Pk$ ~$Z/^_:^@b~N)_\CLqޱ  VSِdEAn)M.#KwkO q7MLq ǭѮQI -Fh3_t(KB+^\,D5 ܧљ"^?EƮMO8]!|&>^<m`Ui3#$`]'TYS.*a:x(Ѹkx[7ODOb8MG\.n:\c(pi_b ]@#ņe[/#lXpyW玼ztZ+;]-,.y܁_ m1H}A'Tzd$?:pB[\IbyPX ;ے7TBOcJ%!a|NMS+x]q!9'V6vqO'-d B8:8~CWNd g/քքvQv\o0DV5=M{ ! scMv<>KE$Sf dY5YPKW/@Zzz#e]raFf_kM 9V骉Ń@W7~;RS=f#f?XbPE-x-͔JBMƔ-~Py%b?t#t:wy~C>M(J@}Ue8Ȃyߧt#AR}gͅ.L|'I2cM-Br~'!malcP-r=Cx3eP=g' ZKiT{%/e_%W$:_b\կfu|/(FI׺jd->кQ;  VĵPg$"[+8Ķ< 8|^L79mD=ljE1s#Kl`tœ7$yW3}xtIW"O^tK|ipw"ZyqزF+!ϒYpc^r;q̀K HSh>2Dm1duνSd]DLw1N Vc4!m7 8G/,lڞx[{ļsb?ScCV_&;SOW5q @3j8kL)pclH,``>m`ƞpsL`޳Ywz^U2لy1DQ6Y:(!!k_n ɝ qo'sK%5!1LG 96~sevnu:IBIza1')!UT']|2 c\umCJ8e vm9̀.4{ ?]*% ‡aS@TM͎~3h%΅]Ͼg;[Z4O1~#? dIv֢-\(]yŧ6ő|*܄vKf\Jټ׹=زH)k x\Sy}ln2sŗ WxWh$ rq*Mgx_bbL(Lzݩ X  ),&֘($j)OY{>vmS۷K۫8qCt-tckn_yMw:bbNx)hmtTp%GWJf87WIp}>bYN*:x@__3k!t&PR0on#m@Z4l4%MJJ%1Wo;\RZbm/8eM/@+"7SGd+ͱ bCB2TlIzbW&Y9vqD e',?VoMHO Pa2 k~ldp;Pq4i2s˗\&h җn9+"`,v@covŮT @:M$];ݳUe v2(U p*4,K#[ \I91uˮuB=: oL;wCKI72T 88;*n. JsPnKE*Qꁛ7DP6l@]UMU& i.YUC^\gʎ޼(or7Z\ۂTY fdO$SvJf yn^93J>>Y*ؚ.*`@CzAصs7N1sXA'wkTP'LZhXKޚ,fA"YH$^\xl3};#[bʄ'jP@]1@-qij~ƻ*Oe ( u9P`,NJ5s-N(kk%B DX~og_PHp#6Zk 8Z!qe>$yl\Q`]v`ץ6y)'!m< IHgЉ]Y:Zue[-+s6ZDfG"zAP&4~{3'{H0*VcJ͋=k1KC ù'nEwhL(z)*K[iUsp<9Yag,vg,^YXB0_njTl/n݇Ech5 6@y9o،$=e=NT(d䂔1|U"].R͕N &pE_5owt qÛ/E<5I1-MaԨgĸDLVDIqZOu9UVDZ.mM!<2p6a ΐYazMAoUO乵8/Gfe&o۳e=H+ #R5Tly3j((Ë4b8=տv &B}7sJN_&E?|.7{' )k&Z[89rp+ۙStTN?ZK_ D񤇡C+P,u_q4L\ PK sYRG^Wj|Xf +ˢGU|gS0*l@:{n!֬dXqH_h`'P%չ5(~RVtkR\+zB*֘ےTЇ_=ξr;qYt3(41L8kP X0=)x]z7 pD;a' [̙HXZs-B\k/TTF8 L<%KK5WJ.zH3Ux+ 4vz H K)r$wZI[^[_E1COD7p5F¢ me_<2e/ J3I"VeRգcV EQ\hr'ܷ>.pv7=GgLMFo^r t3k o=YyoĜkJQfi ;3k r%c찙|]` XW+N-!ƈ ʬa6pgЅC&cs'kvj~ $Eo>=ј7ix72(ܕ|1.4pt}]{y,,S{RVM_(~NYvXƌ XZ|?F":--N.@#Mq6X:x|{/rQ߉ HM"E '_hUΎ;Kɱp?xCwOi\ӏ6YX#H#p?Tm';}Z<N-H$L̟.#V> :L7-!x"ّEF<̯ZkquN*%1%볁۪kHL)$yQj/~v_;o/- °GOwU SJ`Z;OzNiVM"E4>U2"ĎS&*H[Qw9~ Z#{~aym2ƪ\|sbe^f#L$͕3:#v/#zrN;c֟vZﴣ"?hh&*Φ-la\pS1"K9MT}{*4wٲX/!Vڢ5;;#?o6g(6m\C;#h ٭65b[? Hy!:kx)hG٨/*A $܎˔Se9_%&w &Zwk'?ٍ:}+3m~{󘄄u>V"(#%R: I&MW!2觀{%"L-%V˄{]lϵG#  Vk12YJcW4#:$H 20"4}ZQ ҉C5乕5PfU\ˢ5nVyJ7^ZOy&iB}>XAu?kR|P/],yXAl P)Ti|GꬪECa!0?@YEs g7|Gp [af\@.]M7#1Ddtg* !BQm@USդV38V@bZhNYb|ˋ>O[+d?H7j 6ox_HOl/6Qƞ_acL0{de1L%5 *kA&ia9Uv~yR =' dNm+(,ѯ}L K82?f}dFsA tTR,f^>Di'COZD'`oQd4N'aObk7czcKfϼ֩U>g:s FZ55z_ T*dh m<@yx;9~[]I p_ct2#:\Y$7VDHBmc6tr.laF,**WBMam0 ^IgF-ϽF5;}JG`D!y֐nyIg`X)bBM VK`ew֕'KT2z)vI!:Dt쏚h7e$A M&>SUU0l$KXtcC|mCX.146q0G5>wqr)N z]*KH|":3NVQ$^d/ 2= `Y_W:,uz8a_N ~o6IV{"ԡ*.?)uR} }S=Of~84H(8o(' ֗Z5FMf}]PO[j@̸8`TegWtLWqY&b} uʱyohsT>q(f#+؞R\Mš)'sG KO81A9/zco 9cRG?V<(A)(=r똍ֆ8lrO%ޤτ`3~0tzH_j; ;Ɔy/= F{݆7~b}G b%}]-3hRݝTlJfM ^XW>7X0eP켠~}{ xwyl+nq;qDҪCC &"/[[$7$5r4ؙv6HKsڎZw `Ic6Z@ʌݢLA?bgl&|Wm[Yc6Q^ ڙBl_".ҺjIb :6@2A T3vIҀ\^: +LbF5;`(SzRSV\I|)"AKUQ(eCò\l 00"Lks"@;\MP )( GkU4'6 Ί_[㵜&sVb`!d}!34i^;K܁pCLĖ%{vc厒-l$kJK{|gp?̊\n%̠?^[P+(7 (΅ ae"a@{F/)7PTփ7rDVQG<2J 钭{6sPt$C>H|0w?^SDNja>/ʜs57n'-ў?A%>ӵkU _ֳf|C=2 7:x |7`멆[i3Ys6_Λu'4r^8MO}} a[<(:RHBciQMFl iL2Ʋ0k k&D?bjr*~ˈuY ^y_k)VwN_ڣҚC2"Jq"SAzV|<. PMk <.uR?e${^*n.ut0M} & m2 i1۱k=;$ F` +![{cI'<; C\Yi|S- n|f//'q4a=-݋5x VEWyn fZfz҄Mx J x*òzMɆE\)q]N߳JmGy#9Y腟j(;ഉ_ďTd0D<_4FqL5d.3KUR QdHvI EޔB/8ÉKp+uBX[ 9l  PoWr΅LAeQMZtY-9mvy O>Tx#me4]@.{1je#>B~ ޔ:s=+-V$& |#rӛĒ(S^?\\VqMvp#"QKS1-00+k+YCpL{_$d&_C#t>Gx_P6t,'+W*шz*V˰,sC7?ӞmHbp28Ƶϙꎮ^V"9;B^ W{^kwjp>R*u'(d% $m'QwAlօu|pTb':6t~߳s.~1,z"_:z" KM!`2 ɰєU#W5RC\/MT7K6uÌPj!}JagODMjS7oTnpb5QsO=ω @lv3䩢Ge''[tcuZw r{D ЎuM$M0im!0rRGW;Dss,*;S1fVruFZ9\^ HOPHBlTQ7- @1W7@-.`}6oCX\"hL]a s&$sឌ7G a"Kܕ,شTT4DA:)y^u|?'Ҩ" )C8ϔ{"9&ysGyukxzlU_Մd5 ۜǥ)>/wɒ;I$KU EIYQQLxØ%Wb3 a!>фZ0S>##sݳsY& |v fNjd!k- {ocY@:MYXF<,>hȏ#=`j/ 1_JVs+)Յ:k *XF*^OD'Κ#%}_NʗH~mq)׊VհD%>t#旀z"Q+%YW#d.NK ~sM.YT+g3zy^@ʫ(j 7-4ؖi[&CmG-$'QWLhP K&ElbJTC,DžJc}jq]Zmw$Cϗf58 -*p'=E,ujE-s0pQA D M~ȓR')wZIs{eѢwv kX`P4ʇ[xo3kVgAѬI"?ś[cYU0V;CyDKFi~R 9_pDcJ0L^r ܵGS{]Rm)oRO߸S` ȂJ7Sqǁ$P$ð&uv)z͂_ f¯I^RZn.tUŎR.{ UQM Kzhž ϩdž޹caf{/;꥓X鱿Yu`p9i[^o֘lȕ\39 i|'I u Gw}]}a!F.='VBNWH: 8$*ޟEAj)?/r:u9ykKph9}&6~C r$p;!TzGޓ9)c ^Gv~n̥g~Ã(<΄slT?Fg lG=)4gnPKG*H+wHˎ"ضa%cwgRsP:?|?E譆=EZQ>O y\l[-ؤ `ەζbIe盆TzY2e>]@pͪ19+rVLu7 = -Ni3t4!L_o(w~3H-1:Qwa1 v7DR<$H oi}370hb#G!P{h[mXV")hT|ob3{n>![-Qߗx'1 .`i8r~^`sX>Y*|"MdqcJjW%ϱrz#W;'׶?!vIC Axm@۲ia >TȿRM`6:F04NLzT$ax'j'=n<[C%Փk1xl&wG)G"{(jĢD"/*G+;( fj/QQ 5l'7HB)?/>)z?6kb.NSeNXYR6i拔9 c,Kv݊ϡ<:I5N'jǍu j/{]B"Rԟ׸~_J|_bbq}c6|nJr9:d{>Lak/S**bPax=_c&G?S`䭹C6FaeGzD n˚T޳4)v R{]:TyD]*+P!lSzr>0 H0EIP8޻Ip}2ߪJ#w_N]T \hҳO:/A/ezυi |fSxӰEݬ`UK3GX|~d#$ʲ1yRiUYp2SmAԲ5)BsKĎ9EGc^>A׭9;Li^bU[7jV5Vk}2`ԟcOtᄁ8ws{-+[F &GƔ.ɘl0^Xu.9c 0 #̝Ej{Ptt_~䟹߹on cqWo$k -l=( )g[䭅RK}ڂV?ĘMix-9⤴h(nӒlQ$GIel )"/0>/Cȑn$,K}L݌8AL>_xހV |Z5EAk|cM&ŐfTw6PPSDn'} ץNy']ꪺjCP[ 1B7OGHa{|GA5lWٯ>g$q>K٧YH2E #h Z]q5xǶr2uD˖W;^tȶFj%"en-$x!%HXWoDp9(BZ+Q o#=K&v#v%ޤh=r X1*| =*,stܠsk,EdSȺr272mFɋoCQXSB`7yLDrF SBY3!Tg$L-gE 8fEJT-Gom\}V*ռ i1 NC*,˷{|NsGYxiJcT7Ư*ĖXB2AC9W]w(OO^#̇"3t`/tK2LbV;'o5c.jo?09+顑JsW7ⶊ (!xcetD4*oSYu+rz}0ɜ(UjôaFy*q m9H׋CxeJ}yFLǩ@V0n$-4'ZCJtOBľH5"x͢ټ;Jw N\qz>A@1܃c$GՖPp㙜k5i(rpLfCZ"nW[Y8V33b!_0]Mqh`mh WP5$z{] C6<bwf |#{;ŵ~B[T C㔜 Ƣ% ] ͹ Ұ?-.F}!ѦUchQr,c 'AN`k~8/F;P|@y^`j<q3ċ@]鷋Yb[I4q6vI>S|Y= {%fope$C o+@vL I;E Y+-ÏV*:L/1AAe04&H=MɣFuJgm1,ó@;:%?B> I=)`Kټ+qoEY(mnX+p x9Nտ+]KXS!pe)+yw!N)~H-|BbUbhF;60 yxWy乃tܼDe &@5GrTg_pt[> =@WDzybh肩+7HIܤɯqrӓxQ&Vvle9sA6*G.@}BE!\5T0.4f7|{?#b v4{ڵ*tOӺBq=W6#;a+L$Rb!y${MZ8ۈ57EgԐ)$ ,a 8AGh1Gp䧧0 HE*6jnG,\:Тa5{/S0g.ה՞Eu o'ύp1ct&6fqimzBq![ ؾi^Eat~ ?YIcgF0smb=;7mcД{#\r$%P7 É>O/2o|>PJ}I$oqSv,D0p<=+g rs  5?gk==^3#QGvwaP^B$dl(5;LVI-Z ALt:_aEs=,sY9l2/T=pw=U" j+"{y#ޖjߞ8\vu,)iAbaqlV7js- h|W6]v2o )O\TZ4JZ}ˤĺ0ac&&m /Si0&͟K[yk$T,`ْ2U_~tFeb*$ͦŸ,Om(&5Jԯp[ #e{M)j0Iƨ VCRJk 9 PvG5HYRV9vP%6?]'=YAq8,td? Cs m`d橷BfhP`[, p4kOP#b0/[exW&g35z$]4Bƚc .-9 Me0_ lasO^m"axKQ"{Ql<MS?OzPlѩl 13)hȟ9K0í.b>сFBdd@c{`TI7ރ.zn}>IsSJ0uH [Jdt% lDc}|x9jj3-U_xή/t^G枰v&k雩s:%"[k{< V6bt0YV?TA3Tq\栩uԡ,h5o35]GAʿ@үL4J7#R@#`}L++Xlk,H^]t&dѷN墷;{hqp |SrLoM=b23fTaqT]>&+(D`b^va9XI(jArVm+< g.;op#8` 4#NY:g8zfԨlNWvduR`GL)/\Jz…Sgy<ŏS_ed)YJX侼a]CG^4t^8{RrO5xpJhb6u)$HS!3%g`qJ}jJUt@AYsMӘ*HfI$v6snHmY-cXI@'mMM;]|Ρ#ﱞvGWv$W[%>Ҏ'{bnRʬD^QvX\ag "8TMᖥY)b0+fF%[4HVPe~b]^&?E/!ԧ 1/E' 1 Aթ cϲNxюq0 @trR W4}>)෍I*O#ĝRI)IBRx?5Ow]B^~ڄӔ>zl$Հa.ryToY;dqKY }S e]/o;.`%,txتҜØK0CB1j1ķ=üWz\{  k T=ҚUŎ*e0n!s$ʦ `5ym>^k4t~Qɀ0ot;aX\v)b@~+.;Ktj&'Z.UVڴFyLR9xZE69eGߕNHM}oZ$FxqhnǸL*tBMm>FV,G]iaJѳolUᖋ@  ΉS|JbըG&+.ed)'j=5Ç KGCg@]A"`*&=ׅL\uI4!~A4w+GQYB$|. O O,n]#(sVeyzqZ죑Ğ˓v l)3C ɗ1WE'͍Kq'ҧ䫏әU\1Ȓ~h)()ƞ<U ސ%քxcB$ȯE2!R] €^RI~V3OozJN 8ǫ yjHa.8sek^FI]7ZEZ/{0 5d%ր")b۵p鵊$U1 :\ &cH$0<'A2cg6m`[\J[cP.vW6&Q ^-Ѿ9GM +f"G8^;F)9FQ- b'wWĘDJ$2 91XI<z*TiwK, H|dZB7C /i!08MGTvbZ,"M 5qhUgv6;Ę-%6VWu ڨLOčmbȡ{7٤*:^X}\US.-]}8xkcx]O BD- I1v;q*׭M^i:NYcQ6 mT+wq{==H|#bhio7 ] } s7Nw=D-CfEpwEpi~TH^ 2`O"ˎe|춽&H\ygsӲ-Pn^xa\-c?#<̢"B[z{6͚6niN͒hTށ_ ' 4u^2hO!5"c^3KyKR.7vعь;~5 U*Np'tLpRA}/W]4kvG{kч>2ptP-LWWmB˴1L_"3l5M[4k ~N %)R ztJZD[nEwְ*8ot; l2E.8^&uL=tBum?IiXq*5iND(~aqIP|*׊~/w.n Bdư)2 K3 5>GI$ \O]G~(ss]M;i *(IY(Wj<.%b2c9 :6 PWZ6/\^(6K毁A;5"dI]TP(z5~|&9S0H>l}yjňAf() ʺ"fbxƾz街!Φp>3+=T@ąP)>T0y=Ԁ2HnZ7PүaL6{p45W(-:0Yr\y9;r`X@ӈ(3ʺs?!õ!)Atv\l .@ZP$zWKJg +C9=~':pnNhR%.eu>0N32c%9m|!nwRɻEo6$Vb+qyj ^4V0gu{36Y2}"An~Ij6XQ%wd|~ Q^0Ɇ>&Becf٦Ta8¹HL|o}cqC*z5!/Mks`%!oj## i|=_$<5`ԎhЅ{TR:nYA)7Lf!B :* H$٘Xv0Vd~pĎg@1kBaŷ"50q1ǥ ^b)l'"Հcv*b:s_ڞ.;FFX>*V#'V2#̭aj 0:_jS(~Ԕq NBW}?FީaO  IZ@)'?K: 8"5 lu:`Yr6ia)u5lqFVү/s?hC_ON:}1H|oYNJ;Y5^fbBn R(:d`Y嚭ouZ'jeÕ_}D~E̙7i(3I Yy|i[`4軝}?u9Iz| pEmvJ 0mo ʞ˓LYm}HS:;[OzZ@-?ǂoSHu\yyܢT1oW $NRn9sI}hܳ:1/iFqUD"id$- uCzHǷڼ4U w$ayrՍx_` چam ?q 0w{X3_kJʭ R` CFggv&)xjQE _0C$Q놮/λCi 4 RĦ%HI\f:M^(v5^|YVtrvDAQXFr)ڶTCdߍ;T}9z|%"C㽧tÎ I@`esl! L 3#:GTg`t9_^\x7,!ܮES H~xt1(("IP]|3VEB\7 m&oejûά˼ DҘHj)4A ,`ly[`%:@؄K:wi˷^N#+Mtdr]]4KHW#4#-dknSlcC6a-/kgSg>Ydא}x_u/w%2Y,o:ZŌ./O VcڒM! C)%qQ^b)ՅMVZC4B"w-KD46E)[cMnoZGU duq7ߨqLT%uwQD tBs_`4\F> EKtg:7.b`Iڅ+ygq5ydTDQ{ : ˏ 3ZSw`se 쇢j{&?em nk[kM0'KXIKEt_ 0z `ވ̎ d #yh`u>w-Ԓz%u_4N9r8v8{qi1A1eqwz/7sϪ+1/&iQ,+02 ջ5%ުu#Tom=nClz?ln j@o`;u&x+/Z]y 7;SzC5Ĺxi*SA9j λ/"$,tN}tX_\RYoYSWoEڕ@z̊C2xK\<4Y<)~L+q7uY_yl;=hlksؓUu^كWo@V+M\v BM6>r'CcUc>u9mR\du4*Y @T Y4, 4o<"$Im)"ʸrvEHdLad|FnUNEH GwXnCԠq.w ج´N>Ve($"/shQ83n[(jdY{m4R/}O#hJj=9^ΞudqSG)V_Bw `h+ZrabT sقj7W]1d]$Ӆ.,s Ov4}˹C̾+MӘCH!džr .;1"I0#z8;E_E-ybq{2ïvkAA~38ޕֻ.DlC&Xju1Ia:"uHA'w~eIRD,z4fm֏L8[Obw"$\ (C.kผu z}M{=i'!wzt''>p=]oXqaħoG3q0APYk@k2} V6ӱlSS ݁aLN%-&)T`=z:=Ko52$_LyH-#x٥ρ#yY߭X`}ሏBCˑոI!}!cٻt]E^@X./&: J^m%GꯧCM0+vRDH0Bvsn7i dctyX+f&h<)\=NNrpVfeS&Dh:%YFNMJܣ^rTd3_6s+u4d/ ̾.](>Mitf)Ak%Sg-EO}gB.(2RJ:]LCw[}Cܶ!DV)!=qZ^q~ČM,tR۲a6vn01&nL@~qK>6(gо=f~[ m{U隈}8, H @n$YWKtn-ҩچdDP5\˳{s&۝Dš%k6{*V /,P̮2$ʴ|>=l [Um0,-畚[V"cl6QҊbiKp/0NL_!C3pNAk<7/)&عe8+B+@(6]s=I. h 1jB2S'hp09`&WgOtl18%4E= 7 c!gOg1+^z+:?ٟ;*;`vZ+ɵWqRΉVJ%KqK@w@8)[[F#e XwGdscsmi$3a8SUm,S6s6 ws̈~C_H{9j=Sl Z}ꬖ>u8g e9U:ӯ޵Oa{Z4!:oM%P*Gpfr9}pE,r@INB3aK#f/tBZ$8 ܦiU}cp.MJedzܜwoS>lmHO282}umxy\)7n:h+ :Yv[}疋&EG57A |Ud&Аl=&];I`rRtNS< h2 wg+L-3DP8ߴeq0(%hsZh:+(I1(ӫ6UmL|˸cHDjYYcO$L|#,V#l*~|3>fTfgS$)_LnP16[Ԍ=oD$ f?"^2 *D{pn5s9g{á6ݥg~l +Ѯ3os=DGQYFX%nG5Rsv֖ nD\TØ)pl?2² QTR {ڕ~سc(snjg6Ԋ;ߓraw="K.oZR(mR!VUPϞ I b %Peu"!fnEZ@(9y0%0 ?~yt”j֑Dm.3B.(4 UJ`Ch&$j!mSc-s$i <];ԩ z,[B>;&5; @=ļ0b?7be=jcatWwJxkD89hxPZ]HuP")f}0$-i ,ˢ;y(Q7c.m"l넘!tF\v)9~uʬ -a_)$N[8Ѫ~T51MzGnƢMhcgˌ2=涯 Ǡݚ_n(Xnm+?-o_ 8wm 袶6ojڌ1w;$KTGX5h>Bۣ:!Eí>X򥤋ov~d"`to>\.O> UAa[w1EV2WVod3GbT~zgGdsPg^k8)4r2 wCfVd"h,+[GG hɚ֑)HBOe~$!9ɤE3lںj(JpSĜ4|r9uF?w3R6ۃ8_YzU)g=mm d[Y\2%^er}yowbS};n { A"0f,')U]тAee\ Ju Q~!`|@Y $3j[ >[#ֽ$VaT~w6>)=z0LjANghw#᎜סgS:R}(1)}?q|9rȫ]\(F R'{b,fwG@|M,!w1.D¤6LH$ KBVIu"\E@u6_RŰ@s(jo ̡2\\U۟3hh8K3 #Zs_zeʋHfnci=2s`J j VS&+}/ߩ7 j% (XJL r+Ub9\90`I*O)at`+N\R m7כe_Sf+8f(d/4|vRbfΌ 8QᐻwvM"n< )w;l0uQMDùN58gP3|xb=@f3I-E.r?c\CwrwoqC#Cc:>ILJזǴLO-v3k*S[¤N 续=IF6:Ydj5!rTc IǓ98nU]*\mzL/4DWP,~ޭd҉Hu9 I' `Zи_tYTՍ X`i_-쑅d٩$|brrsE!`todty+GgJ~%>7nAصlipwHj9jC+gYoskX53e zo?7C&.JZQ+wg/gVgT>KufhlS LLl ʀB+9$B8ܠ9Ph~i>W?Sp֧vj1t8JǦNk i cY?§~~?+E GwHyS/uA^LQ"xcٯTO F B n #l4>lWYjf-uUUkGEݴZl#Ɍ0(+ Iߏ]4z6>K[ZA\M ~|b8s/B9 s $gѳqX ExNSO;׸5h7@s9 _u0ykͯ'(Wf#=(,RE򵼱,U2{ZҸ6!(H;Ds~'RzGK Dz0=BlSfQ}iY [:Z(ZP7GUH9{7 d DGF_nV58 |\r-a^"8{>z6B &JǷ_>dڪޚSq56c 39aGWdFB}M B@rk ؘbV!$ }'Mf!:LMĀ:ټr؏-%]vc`% 2ʼ8"8OUfcL\5ZO틖?:sVի7W[ަ'|t@u$J0nK\w@q{捣Q믈`GP;EL]+عzۘcʆcשb2dW,Ƌ`hp:&/p n51 Ыz?\ X]!D.W>GբK{^3j@S^Y*y!go, ~ye=VvHʬZij?g<#wr{Lc))8uG/u~1&eu)IS;uC.~{T060p{p繽݀#mG'K$ dMG6|舴i?ݭFay_S·+5D)ǧs AeM!"[rk6V æ$>㗯{ ]2AO1/?>P0OJۣ;:HSo֎wK%WC?PC_L:ŢLW. 70>ɑ;6OBx$PC"iXx4&(\CjF{${U#eq?2bdUZ^U9갹]VLsT4y:s`u~-MDz >@|l*+[nUɮ瑱3e#xqkGO&l=Gń2,fuZY D ]b*,0pM"I~OٔY`[YW{j~dS L`Dg@|;|ɟ,LPUcu*5s XxJQ( 6W}-PqI7$^0R<woݡ7~nA+Ql乒~ [bim]Sf| \N=`x쵐71߹ȞKIaLjFtG߰oCkK-K 3t\}21 *&VEЖjrQlVë388ҰguK6oPZwUW W.ywc09tL8^u[ړ=>5xiW"N >%) 34捑&#\s-q~YBfUn[6?IPյz8{BMHPRO7bwP|,[|A^d~XIcQQЌ\Ҹ|hdppy;Onvr}W232i7whN@NA]KdWi:JnPjDjlhFFea^U5)NFy+ ըt<6!xȑm5m_ȗ;R6<.2D1m(yn Ѹw‰7f%R;6ei-E@ 3 ۂ4O|'Xhu}W0淰*aAYzQeE&$o Yjl22 |KLJBBUdc Gp5k=!jܱպxIBj1QQdj{GR%Q#"PG<cCQZCMC LRh@9Ρ X;邇v.>DMdbii,U |f 9]_; ۪Ⱥmn5?YO>R;3$I:nÉ V3z" ?.ɉ=9%oA1)ֈ:~_P+35m^ 敄MI#)-ƾcŒ ѝ\P<<:!7vԑE_^V { f  ǭZ tcko4U+FiAmHNLea.\KKNnYi76ɸ ktJ6}}14֐I|l(k?q!+ = Mh4򩲄m9GFCJUܡlAլ6=AS<]\[$Ga,8`ZKz5Zm$H[i,u+@E]^AgcTxlr%m9IVkډ延 FlPjk]߽O؜zD!*'Nz2˟UV$q@j:@TMD`16tV5< YG]hՃ`!n̻æF9q)ؓbB4* ֯!&p=(f#F~ P۾o>_0 /21rѻ84ճsicʱԱN#==م56[e!XEzTVRaH~wdJr#?e1ۧPա@#>S(} &(cq"iIOU^ C~Əqْa` _rJ@ j^aV4Q\y] BkP_!#Ĝl"]}vtdTۖZFVZ!Dk-TxȆgƆg^-'?1-ֆUrV;% `?)a|wumĽLo`α|t襬Ys 4w.`މC}J9y{KGGMamOUs.g˦d,a)ϓ&߂dhk4>}vBb(n'83y~d(d' $f@ݩkV6{{D;=rx6 eGrKP 34-Hl2-Z?A $a,~h[3лr%`;'>*)M5 w;n.-T.h?eB;N b |**B=rP)3$ ˺$)CQFeG}P}uSw{{y*|m5fPӳC,?CKLtT-y3h?t$ zUOdW,J5bp⟗T٘ Q Y))NBn;]  p i`>[&De c2.ROSCq|,X%Afw/oȞ8t 3͗vq<+?%| WƇniuĀ|-@}(l&-e,3~V*4-D+e7R[06=L=/yAfutǞC/cDŽY_u~ZMje Hlu$n߹Vy5Ѱ2SfvEFMj-qlj[Z9` ج\u,9*&.QXA/e:rKrao vW=x0!8Nwe[X<7f=LDrC\a1 jlWNȏ01e3ܥ3ѡ ;bUXAτ\6w- _Xӓy&/2ޓzH=QW2Liϡ$2K%2,99VU~*U&WJ ]N-SRMUF>J^Y tĠ#wՅ&4`PyKV說a(#ݡO4˒Jhf! auGj=8ʫeweX\Ki) ͈Ehf'=izuiLg6t>?)'꺩1=72i%d IAŐv{;vVzՑ񽶞l6x<­n\+aDp|QcVHr ٟ(U Aůt&t !>. )ed @pEj@WJ8h0BC_u%U_a^}c*_[n !U-_Sb#Ԁy> ^?u U[ qٹfIh730Pxq݆)89k!uttf&Ԙ!;!+#יP?H+G M rH/U$(C(oފ<HsV, [[gܫ̳\Კ(Ay)X'gS*Q{,v. o~ܫ'O/!{>xpѺuqs | oxlH=fse,<,z3M֢Ψ o6V`[u  a~PUMt;3\`CR.6:z)ireVm#J6Ppn@[r1>CSf&DEv}D|kk.Sky=??N]^^HPRH'=ky2i.M%h%vzpǩ!4ۇ[M5I$O 2_)4gKaV+9sd#DJ+C:\;d.qQAtNMr6w]G%2z)lעwb?a.s8蝊E=t"ڤ`}ܳT`ȹ̆^yU'#KA[ֈz)z w4x|$s5 z {ʽL~觅auSвüdb?ԝg蓥i\ ' fD beMl%n!)4@sx>{<nrudr~9ûKp2}Q(Cٚp?8Qyl/^ i$QZ!/.Lpsg8?~DzJj q* w `+w[ьb1)Uɰy(8x'tP5?8Z$΂c$~(<3M 3pG~!jvȁ';a@{y.p6GMݿ©s!fu;VV+@򨀰zϳ HaakPƱE-]2kzx;k=UaN`9WZLo]0嬎~6R4]ی *ZHcLrӁo~Hé]Zh\ZdCCs'qݑU"S^2z2ƱߖUC,܍[ {̰rq%$`p @X JQ%! et{. tVxM0s-EpޠMtïAT\`#"LՄ>Z'6DzJ,Y$rebJ*ܔV_c;,eI$VX)|39^6!#@ySA.1\T#3Yz#Mij&[$9Åmk?XN4BY4h3"K䋌l6ARzR&ZPLo2NuNJ+ɽo0(`{xRSf޹`I~%_B[6)6̼`Nǣ(4Y霪m"{l?}Ei7:+Ll4*NZzqD\?]\s}8-ĂQv&<[1-yAR^ #g/W ڍ!@dWY"H uz,[EtW:ZNJ{dk2|lS`=u/ Odofc߲),0p:ʪkOX򱹆VCК/*"ge[@}#9pWSdٲʽP~1:G'BT\PW<81}`n"7n~X !I8 ;iLUd9jw07-lri{W1PX<~Gcv^M<% fz$Rfx2R˺W橶4~WfXYrg}rɟcEȈC_+֐a)dm5Ћ|(z.MEoyuRF0V6ĻڍBGEuJF_] [յW)9ng˙$DO=( -]訆 ыo7`ÿ.Q\9w}1)tnc_6H,e|qĖ&bnkfb`wA^co ʷhFmAQ]C#+t!!yt)!M-=|A?v9Rf821:ĊuakKeWo!=Q.2!aZ474>JO"dE vy~x(7oMݍԱﺍȅ"pZwM\wv.H2WL)sVu/!(+Y5KUnT`u;WЩсHQ;ճd&=v54Sj8N"DMI?/)zMFK6T 7$-IP'7-UIdܐ/3{YXzޗ< Y oӨ+ N81:KZيzWSu6vu„us.7cD.cP ar9iڋ!l>>U͙`U sړ\ ˢȍ4櫯2S]7وo -UfPK4p`bΘNE$MS^Ed,n`;0 u_(cxYͣ2]"_AG~&ø ABVj=?;Sґ4?\bV5,=!}ha^~[zA}F42=y?M}G.J&MHPEEѡV JgX52Uh)< {u@+$ P**Js٬m3Eo䦰JڌڭkgO aƐQyX—̜'XEBCKlRU1q?: LɩҘVc's۾/0<{tn\o3b"_BR"lüv,|rҨ=uiMBx*SH0f>z`C`z15h[PN =#^LZ^z@6/+9lvVX`=ӭ,Yj0 k9(6q)b];bap,E{u_ek &`ZЫm9)cp[b"j"6[U2A67B8}\[ccS3B&Ї4o? v[5҃ g@X\6@=i)q^qqqf{}ӓtC;%U{:3I K(hl2TRh-MlIH u)쟱]7ʅ$G򉬒Yoli?xAbFU /ϸQYsLP7JNrC44)#RIzc] W,s6YV11@XBM!/{%q {v3&%/?{sKk^Q4Y0}S[ e-*z0O#xM9"Qfpی&h&Lj+s"==65RuT/&Γ-+F* ?N| EM3n%a+:4#$u "Aso ]p]ꈺ"X]`0,"VL!z hfOϿxt|'.{5I~&tCk|N*Kf**.}#L]>k9{'jkd۞N]}yKѽ ̺`;iA "lwYTcJu^\jwO N7ǘ1^`$;~?YQxq&n 6D>$NP?=R5Sz6 KrW}Tt cgE[eühl-&X0(ڃfR9OA[4L?#t<*eup-Ρw8uUw>^ >[.Ԗ:6W[t0x ~ 6fHiv:fqO']- &"DDA9 p*kv7J:)rGPQ>@i6\@_D')F5]hg0x Er;p@~q?evݿ2o}kĔYg}vhw,1m? _,I+c`(.9B;]|3P%F7:`xh FѧkoQZ}1B0TU-B&bu#$1f(1 Iwsߓ0bZ_G "%n 5JB &ekw{f[ؔQf 04r0 @*6tevA^Z9C+o᷾G* &EZ߂FQl$}SJΨfJ7u_g8zW?풜ĖD/[W?Պ~jq;ҫ̃5%:?BτoP>N<84`H_T7Isc,jBu n9v8.<rt2!t"ydDz؟q%\FF0Íbkꉽ}0~Xa'.?83(,g+}zj1490Ͱ;~9n峼M2:H2w͂`UGvÛNQƠie6W+Jg%Gͳ VLd.W9򵁫S;60<{>,EWJ;U;<]1ktޚc[CV0~`u6A\碨؇+&bUQ?m+(}9~B\,r.3sibN}Ȩ +tl 78xA! Y.vP5_-q]Q^h6(#@`+Q9ӕY$M,P꺃 qa&xD%QwkwU A8i.ɿk|q[nЌt{ֽGN%]W[p%h~?kԡSuXx3.OTN$DTl͊ïy}}Q[QHL<xt pZԑ8y:dmϠ:k40. I͚pf Ime[4R}A>"@\DdlK>J@GK޶aen]fzܳGĦάn$ʞ5\')@Bcr M4O:H?Fݼl}mPfC:@8a6T- c!LjjV:g1Eh&4R{`iJFzr2V/@B,yvWD7p;f$>"MT5Qۄ2W)j d/@Ti:{/+ (TM9SJmskJkyBH^ńW\kU/(Q4 ct11lk`fsItx}FTy;w(sC;';7XIOtU:ScߚYZ;ݺ\rt!_\G~o+W gx/ۦBOѶJrDh[M)Zu.:|?qboCvq^ vǾYEPQk2( ZMg~T +$Rd#*rspaBu/bwۜE/"wߟ Y1Iӄ"湩r afpz_o'&a{_h&`EXxo,A&Ɛ 3ʅɝiR2 [(1sai>Qϒ}ď&@yY-_[Gs&1,`|%<25*cZƚVr[<z9|u*lpPk"u0 ϏB")$q{JdsrYM,gĬ2~j9lZo~04qaUz@0%wg2q"Y{$FyCLC]Qţ:s8'.̉^IKW^H/a`jw_92)?,h^d}e60Aج#ҵbgfșQps4ʡ["Ŋ(^O5'^?*T,*̄4$@]V8!(gݤdHSFfRW&e8p\?Ķ3.V'}Lr6rw5Fӵ.OnmG{}N:ԍ(-U̘lJO̊ݪ0 5.cqؿ ~.nY>;lҒL/ao=ܴbN3@0QN &IɮSEAf<N?T#ę5z_ >RcQjȺnTuK}Cy=#_mdv^*(Bڟ½~RSkI Hj8 -4c’T>-y&&b+[ʘ*+dF$1.l6 -^8RLt4:Czi ֯#"q>^dZW3ߛb:iDlPMV+BLwnEa.=FnoȆUN9)w-H_-z'c:졷әbj\\8B ұdptGq&7K<\&Ӓ^*<%y4綈$S Cr5;% .S˞QBkMӻ:╇5N^ Hԅ#t=d"eE'R]=('L{JqaW1]b1Y-?B4q9ʝl<+T?ϸgCѷI@NVRo/1Jn0^؋`A|{|*H6Y-}jjk* EV+ 1J֠}nTg0zӣ K9 ~{ NV7&} :wv.Y;/aM܉jllи%d+vO`li1fbw>׏H%찏0st7]UOgwcfu}1zuQz8;1< S$R7m4nNI: &3U 6\#Pzm^kjٶ^gS=kk OIul%7n b^.I̺Eye,@y"~R3'qB”}H1-p0|0Y)\JLfBІ.<}W#vT羻B`1W|9mUQ4SyLvO{G:i].?ҵ2?PZ.}7hҧ_Cb`/VM8_QfwM}4 }ĮȈF5`%Pcv~k`⅂c?r ɊHRY_&96:X8>w}fI /@e+~3 VX%Vfus39n 4 *(`<-wY4K\*dL;7Q62 }7GVm{9:_r6Ԫ89tYvg(ǷP BZy<@δm+Z[6cX[֕f3Sw7٥PR;*^~u_"ؼѕ 'd|*HהOY剀gA[vʰ8 vњQ;Jg-q[dˇX*As $LNw)/ C+n ZhrNlb̷ZG-{T5} en>#eN" Oۂ=9bX;Kܡgo cxجNzTfzE!^5#MFa/] j$xVz1!虫Auplkx~Zz׻C nꄗm=_(nȬ,7 J4bc*ph- -0-YVrR v ؿ5DBƘa*l64=mdj.9$wf:** ̣A!9ڦM+D"el-hՇsp+7/<10uv4B}]/k-y\gG8QN fOv, |LU/O[\ A=uoFK`ƠCM YV葃&P[F$>U~ݑNb|{-:4/aUȁsT&+dbxb(W޶'t.H޺ک_N öx]T،|fzaW!aQQ{|ʫ;OǷL16w|(ysL=cs)0fk􆜱㨌^7"Ni卐x8zWA/udZa6#lEg[&yW.l[#C,g9%sWKx@hWk!̜VI@yn|Lvl%NIZ4]9J(dNʌ\w@*u`z7aZGЮ5uyCԜ2U4ln0_O N3'LLfrJF1{t5αG +ƅjH&򋆟 ZG76.O"fxqhH7e@c6BԬ+3lfQ| vd*KW 0A6AI638[Xo-,7>APlbiL 3/`[DKܸ%{@V%U.x`MY  5pX~q 8S!󵩑7gtJ+7+ wЍO+,gŌmi}#࣒JG@9;pQϒ.`E*Jz}n @Ʈa#):Q(" /#3 چGh,ȸzjwpyrMkEČ9PL|7 oY?҉%iL 'U~97gFD5ٙru|7THs!+!ѳZ2o*3RŢw-ZЎQT$Z`/+`$k [8jaǃDs,^|s E\^*~@ZlJ80/54-&=4BwZ—y0g9y*#~/*ǫ8ap]Yz|tiN/$<" P |bP uV qʿGF }ylZK(}mBo[La:py|Yh}48.3-$hQz Bkؿ~n5n)inS8:(P!FnU`h"3LaZxg\Ը3;KD𠓎{kƊ=\n]iR -u W>^\y>@%W/ϠuU|{dCW'gz:[úztp0'17y/{v,>(~'p^@)WtacN-Ol@+]uH.zEB ͲJXLLW JrȤSMt3''Zڅ j]j$ad,U/I~$+/x^ vnqd!]n*I:dj#zY{MuWB-ur<86U'e~0ٯ?+oC݁^:S(웿1Ev02~yQj2$+Ze*߯:^anmIex4c.HhzU w*,GȗWeXEOe2a$ u>U= ʉlF!N<<)u(3h7(&aLuL:(,3$qlbF07$ xlmq]<&l(Y'4.o[?r )=rD6iAR J Զ!.̄6v ؑQxUnBOOćJUA7N5*LNW2^~|}wyՁ);q\a^ q[Z)oq3|ճ"Fhep]s*Y]^dp_MlJ%9rY 6Qp',Ϝ"E )K0L\$jS}eڬ$qGUYcÏ2^ Do~Cf{i>b`9˯S85&Hw8B6X.0": .,g[Y>cYv/aiؔ'N{.N@_Ms /i.~:N6?aCIۭ)nDL0ͻRo7! JUf(ڱ~XD_5TP:ӿTi>9`ztIƈ^'\gf(l]Ѡ$4 ϋe<8Y$e3H>]@qOC7.:ɲ>|׍j4${Ŝ0+o7o1+au.AI -敘]#C&z.׸]eSP02~Z>>'6dkmJdnJUG=5@Zp-V!{7A6oLZPW38\C'9mWẋPo gS̻oΛ#``5k3W״@M |aX j}w_{6K-7YAHW>>uM KX2 x*MwG)MBOTF ,XZ8Kr蠑xMGY5jPe݄NGqp]4,b vp(oHCTݶݷW }6uMO)r S{<47햀INE/'^1)\ 1|gf<r [owX]Q<أ@&;6LV)Yi &uIx ٫iaϘ1Z Cs$b҆Ax5Y`v(\CP,v<5DNGKG 5,åk`Rg#j<>6VCF ycuڶL;}0#I>Lpi>}-t/AiĕQO_&~2^MH}/ZBA"umU(Gn98GAy9 'F+MNbaQ6;O{2#5g5Iz~.16P_&[? CBydn0Ђ=Qٿ6C)_c|hAb wS]$yVD9e{SsLj<噇ORC]Uh2J#ѱon1Z9߰IG8_\u%v(W _o]]\B#ֆdP6:2!Z#mR3(1^7*MƢ Ԛ[( 6A_')mMsE Hq9hTo2^6O67s@?۶A\K$sRxߑ ͌Q=/.VUۈr /52V oNNuo1N^ߘ&^\ezV*D&3"Zd0 仯ţq4OS%4c68!9 1vC("bbJI7؊TVe2Vc0n)C*JC<(g 85F5+29{KK!<5"DQdr(t0#YR:''|R|@Ni!9nۍ< B^݌:`ʍl/05{ A-% rjuph!ՔřeD*uxuh9ԙiX-{;eʨ y֐k5[Fda$ֳ1ݭC ៪^MH6;Gz\';<Օ7A,SymV4/Sqv'F-ĀBv fzͤBC<>{ȪU줥"6+U%#R\T{ <, _}&8= OuRJiV m w4Wh}w1):R2CIU#jÊs8M['0n:[keV5+ pHoS"JtљGch1ne"gG݊cmhtb2>~MrLLᤊ7 AҿE ?̎(0gՠ{~5Z<ْyZaWX&7+vA~LJ.u543!vΙMIawlK>-bTe'g ݨ세@1v_4qƊ7(9ze BRse:1TCBC!ohH>Jd~2:N@lw:ʫ AV'"S[r] j{r W}vz+$_:Qc~Ƃ,FFnvXPцZCƪy2 ^?b2>˂>pB> o쭫]3NN|y'|_<dx 8S?Lsazּ= MXS`gVnwqa9oGUx*dx$Q@YN%Apt\Flo]!G+'9`AABg`7='hq#cdYFVL 3n1`gGb5skJ*7&@Tʀf5.HXP {+6ɳz\kS] '[M!0N'Yq'DE)|͏SlSKR:b"D}.5زMlfe;Y3A+B}.!}{0̻`W1[bȇzFuu*g54`gTQѵILDM@6W}Ν^U?lUi-nUai#D H(y/ʦ-*7z}ήkʼx`cdAp?X#ޯlI)?,n}\ɞ0re(:/! ! n,*BԻZ9=i(M'7;{bp#H:oEX(G;~>p Nfe hH"0V12\ٷȥz7+ظMiSv&ju`J۹On~K}RI53 Q][ns { xʣ}FN$ҾA6R.%=3>Թ1bwxl,t2r#Ws_x(@Da3&!Lk)ÂMa5!ܔםH29.=@&,[paQpKYIQE Y-+P, _pOWQXa-\^No֕Q, 2]\b+AӞbYkv:'UkbGB!y.qvsFVn[A#,!eD }bi}9%H 5 "IȎgwFaTqLL_ 7Q2 5W~'W_ WY"DqkJ6lѷspuB:QUI#lH8D5@g,nhd?]k4JՋ3_&m]4G͙;;+kY.faILV%cěX~9:boTb'-YH7ȮEQlbtwǻLLp͌@.wk홃1wM"+ !hq-fwuJR8& qBl3fe#pNN\w$L8A9Jk1سO2f_J4$hd %0r B1hYmsmZRZߡQf6sf+b{rz=ֹ2@_԰v%GC-=yuhM l|Wu84% WL+`wAb_B|J{.pK)U!<& Ʃb_v_mk|?>]`$ax8“c3\X$$K=N7]~ՆxIp)TB$5/WL@|gR#;+dNT*`5dЭ_\jJݧ84%4aˮ()Ld=τ&E6{a6`hǫ3h!X O;~)rwtn?r\;j/hjDIȣ?n =*0'W)hl]H?#r_!RJed(iw5r>6̭k60sH3pu7` tPRGLv$R`pθ-(3E]c~>Li7\Gz"laߊц4+0t'f'{݇)4oiٺ8:ةOW<8/e,Nd4{]z?]1tT ҄@3%!Z*p"6c{b86ȕgθQwpJ2E  )f>X$F²vO.%lE#[g #tRQ sF4&9"i!t^Td4#t}5KVCDn1H,b.Z0^R6Jı/e#^l92?I͒Sp1#fG3=O"P%Ӡ#|(R?w[:CKIq|8_Vsp…)XLc@D^\C?ma4Έ'~2ז3<D7RyXHDr-/ȣCCx|7@gy }%%_,d0vHS<,9銴HGh)LDžgR͏p<"vL#}oڼ\B/T5pa/M?.q@+NSݸq<*moeN4*gw\`jKJ ZEQl-r7Vf{hU60ОxC3*l ^Ŗi%|8ęM#qaJ&;)!H2i0(S H8]U5dF?Z>זV{u6< Zw}Vb=pjR B'֢Va˭QWΣ;2@H ~K:L6#~A7@9Y鴥5Rnx*A2ɾU'aFG}NLXcfVnu"5sf]SU!>]A>ݯ_T|w Ɵ;~%$p`v< i6ˉkZ֪L$O.~qXT>6G`2w*W`\y;b#6#o[uz"(y Y0ً>S,$栓Hތ0p>i31?/MzNt'k j :r%e ,3o!^`;;Pxzv15O\32XfL-9~<8Rla3ZS3=^cEڳFբl?WJt\z>(Zg;Z1( mv3|o#Ӱ]8-gOO vdJㆺHr!_lc]pa4>Ha,2Uri4':/he@N$4cWFtz4_6wW%Aa;cbe7<:-#6@Q@K|˝%T *LyElPhbUض6jbLt#>i54d, qKzP'NTuvY%ZLY0Kf%*J-c[q+ܑ`n4v96Eu }rUshHT,hT4mIu?Nltx[ "C\}[;T:p` \61fWMU(U K@wj*fB#+[@Vw;~8 $Ajl {^_p-^4#:;kSEDrCGR?Ɣ?P:Jh) a;j: gp&[Y|GͱP1CF`ݚ΃X;tbS Gɓ0,(QF|UmM}JT-`& -A2ϼ5*겛t ݬ(e%Z]?#d)uZYk>AH˟i26& (O9X9@kuRP{VCN]}ا@ew\'>5a*#TrݻDf5åyv81j4O9)4a*A [F LGnFł<+TQⱲB5 p%b{ (Wot5N2_υw`~ @ $w<+&07`mPu8{сR/x 8 'd>4Mxb;춐M>}=ZtXqUUp-˛ugX8TW2[u&"a.2)C$qC/." t}S<1K%KeYĩl+k8&so;XO,bTyNSWKBUJqS\O {ǻv %Ow!sy/nYa`ˢ -{M#TzXb:=vG=8ywfxpMzJ)EmY=9JS荢vAֻt㷫{Y!(Xujc>/cruӈX.\SS--Rjvi ΍R/^W`**p-MEZI%2.c\./{%JzK;Z~R!.ڪxR?S+_zO}Uj:wj.g1 &v %ݸJL PkdcwуB"Yrq$pqv9=>3ٙJ.~ًDWN'Io3x~]Cl}6Sq#$I;, H:* p߃F{<6IXE ] \n7vC{5zlHs J^+&]*/ D )?DzrbiIG5`%Ky3jIH 8` Di׆2-#o:PBF*YFvMZ#lz&䔫A6aN;?z|g rehһQV'Sts&4Έszt {#,akTӝw<& AXlO:R%Fn@gJ[(?+k:J Y6b d_굝_-EL̤ dBY61tDmJwQ*"^4# Ԛ|iEMLQ6w3֩Ω}յ*Bocm \[ClFZ,35kÓMEfwG@\_ c_9o70*?Vk{c DhGKu uT[:#[8xu oW>\h=وN>Ҙp3.dZRjN#Ic&8Gh`#eܔ}H DUI?[{-xaRѣ{dwZ^d |JeB+)vc&?M$!/)@^vVuW7}k 7GkEtL^q#Q@P;2qo v}r,b#Xƾ[E}Lт3T\%31Q3 `q@䇸rbI_a{s"9%mLSoGgd^-ut%hwK;FO)'Z\V,nTDs=/L.l2.*3%vdbD^?_DR&}2 tRt}i7$*燦HCucync1bjB.a mHEXTzYV&Fz<\mԊ'Jm/YhɎ.fYmkbRH_U~댔{}8Xp^ W|a2S萶P]IhH5cGjg5l-{¤eeLjh{ }{#ϙ3oj ؞I:N3cLLFi CXIZ\@(&ru vWTw 8K45in'wz6"m'z ,5Yү+R1tR"B6w9dA0􆒈LG>q7b7^h"cztU?)Rd8G ˰DyM bF>籍V x t.2Tb2PE^>BpSrGZb  ys!ȅ)}ń^b&?Lm]|\8zIaK{}H&ȯ"s^Ic6vVl,\ ﯬLuIT=nfdY0*3w)$vW.<=NB3fm׽|\za ( CzY9hWK^UCbq&U S ,'~hl\Oɣ3ͣ+/5)ANR͕++ERxA IŰ)%;;fhUXIqq7D[EZcEEp$[53RcM. %a>RҳI'Y#.3qEO*$cj!8ˉ QPJ ۉVxAAk4*J9]GeXpa+!f(Z0T^MnU6nbݬW/ Ն69d)Jd6UgAH;TC=p,\6ع_vaR:O``b9hnφ\?OKF}L/W j;jT\hTHJs&݋vȳї =7yE|\ NAɶQW{kU`Â^d;7͸YHܖk?=@Ŵdkq?oҖtpT|IӃ׆ /+ Cw~^}H}/#6m@.3:ԣ :< ]2P d3ʹ6/S崋Q!e$ڤ7"⥙m!|l塚姆Bdc lA|*`3Х"LfC䌁QA}LINlj=hŢW;p>e'oxMўV^I#,5 ϫ6G >l$~R)G=r8PkdA~~a@LX͓ŁBE\Y!L뗤_>"8CJZgj5(m` 1k?Ed"#̈>@ŕN+\4 _+Vre\m<=y4;h)*G縇˨υJSnori*]2 1'F""9Q, ؓ[Y bk܉0+$@x`8e{NlS!AGztKA_=h2,.= {`~ZĵW6D@/d@4g-j,!v۳^Z*s#p}uWK|J:|80YjCv!a#rk+wMIWe   O\rId@[l?5'`$*A8Z&W& H/o!̟ĞC719m=!q ]x u~{eK;26/_wwq1.HI*r^Řt{8d,iN׋D/p&樄a a0F3)?Lߐ=mNUX|Q3ij!B3bp^F3ѫN-D$ǙxUS`Dr}*'8{pMՐ Ƅffr'N4"f&&pwUR-Vp]Ejv \ݰPw&LJ" yL5Ll0D)}A8n "4·GC]I#'4m"F=i˹;?) mjq[* @d(9O댓K5yŤGؑ ( ڪgODM`.wXGƜEۍ]Rqc_P~QɧxmK|#Ά6f*9ҭq2{Nk"6A)MMAd A lh <421WnX,$EVB^)6![@ {qn B%t~1[cN \ŰL3*ןDK[y խ沜.H1e}t`'{ G[v)AO>N(oūίQ.ҕD{:+{z*}o[o=EKs%Ǭ ɽVGkTt9J0wN3Yq""X_;4xDPU]f)f@-nիfox2CwѨʼn U[ʀh43 iAcI8aæpuİmxw_|Eȏ%}"SXw E6,y\DTٮp|Jϻ0תD :(RVfCF I#dCJD?k⏩ZPKk-xݡygU_1*.{S1dBg1\N8љ%^Xz^|Hv,R!IhnR\6_"i'P6T+;L󦊖(_A}#xDF6Jr㏞J} +F;ȩ9滊+#k"XҕԶo7䟊L+ԁEix%mEKr W`FhIHڻ]-d l!s MbM񎭽5gt]fDǻj)KU0)-$5qY Gc$ڌ rR.I>@ ݘ|sC!h3f]aB'Flw%5 ) l;0Į"Q3UrA$+2{iS1lƌ#FM'%$f)U"sbA_ޡEЎMCn˂VJnޫr45YY]^+*3"@Bl*GP#T: 5o\}=#&)N\[r&*Yp]#Rpc&lXP f*8o3\W#7  R[nIM5uIĈNs>NH˜cТy}"͏֟E<&'*K 0DCq,hoڰIu="m2=fM}2/W]Nj]n~ċ tuFcR^ -R{uXk'A;!$^ ^fbIď/h: '[;Ι#AET[[Rc.SCH`ۂn!q`:t3paѵi:2mCQEer}/zۑÑ1xv1 婶N9Ab0ğ+Poj&pd^xM(;p5TH c҂ in(6%!qhn v5%Rz>d߭U=Oͻ?Ez "&ڠU, ;rpha =1z^v~O盦<3ǘgVZ<w8a$΍iP4 5);fZ*ǭG 8/!DI qJЉEyL1D8 Iz2TW'0+v/FWg7(mYKڧJu&d>U> #߶D@G-{uG>xI>Ki2.pIktc2Ey<-ԋ&"RtBJH"㦦G(_ro4G_+KAT3$B*Pc F4CٞNM, `cs?{qGg'@"+BcIE$NΩWC(x+(>Sdl)Q~-m9E.wlm. bft {sx>$#B\0&4ӀSUO=5髜Kutϗ®uJa$2Uo_$MVSfD=٬$Ֆd*Myas.#$r ?rl:áTɂ1HXtzS N#Y; q4kr[3ڐ'wإoZF2GEv+UpQcPRc_?VI^&۳y]Xj Xn8*A{6geHOQK8+׮ZM^AzGr G ߵkmRiȽ5|tNҝ8X ӾPt!aX;O Sr?<%@f,cDl:(LA2a]޵О:cmcmRqq:ARycG{"hV|A@G9B$ mɉ?΄ie9=UWp;MۛEbO(FдcA ^_~htv)X _qɓiYAPړӢį!O%]>7aJloƴZi:mkcy =%c, n|Sr,z*%=y6ىû# 7h;"c5L>WcjMZƋʗVcBD)ұ,8`|A#p:t1<]֖~u6.m,x[i#F#XIW}3h{+wDw+eQlvdQIiwScgut_ǃ?jp6Lg=[B5~4g2-|()m={9gf#ݼª5I//Z5 Pvg ND&Q$ m1ouޔ`E'"N-E7 js$s)Uf1>&;,yh1zCo?aӄ]~}un*;6-$՗XWC K$KT;SGNKu=ZP~% k5r҈胡prʖ–R9.ɯW^E)e?T>P(oא{K_b=k_2f נcQ`J]ԩ>T:2G69jUY7"%ƼN|75#H8XRJ5hOjuxll>3imݤIt[bиԼ0-CMHK6ҋ;Qt1~^riҞ$\r(vAk-6#^!3y+MUV7Fǧлye@n—[-aPu 6Ԛ c/ Ll''6p0鋉Vy (gpXQ!(2E00@o,ÍHK"4ZZߖi( .OCE5ȫ~襀S0<9?àٯlՠ BU K:սy'{Љ\Nkȧ%{`so= !0OS`E-x,W[Dp.N)J/f="غ!4//ؘJGYxrQ,þZoQiK3r^O(w 41&@3V4-2NB\%|JBQ)K ^cJ7G֞'%M&d=&!^ #ST eУ \xL*yf)=kxG)母i+ Ĝ_=L۩fsX U}[TE# %_k vj~T*N>EgWS2JΓ [g ,iƌHyӋ_idF; xpUmNJj,aFUiOg&Emrn(bP5p|"͖t)wl]^()[@2SOḨa2r ]n6w؀EÉ$  + LC|I~Fp!@4#لx):$T~J0, }[M߾wfBY-vSl FZ ,ލt}^ߩ $Z>GY1)c-iwDR0)Nr5n̋t1:Ƨ9L4IHlh+03>iW\ӄݴxZ~Wl+ `hGj{<2zgŎNqT $ՓrUܦ2?n#s[hrm+ˊ."q8[?a~<0/Oޱ΂Ehv'He0kM,\gŲc2t?]XB'Tz7G eLƚ:s=M 3hZ4z1DY|4j۰U-@b]BEY" _R[v9~>u159gu0xJCUJ9uJfuKKDR$L/gh]6zQcۛ9/\ك!>έc?393;|kl8[VAaM&H״r/6SR [XXϭ( s ڗ|)nR9KF&'!R/pg4"D !SԺ`,}u!2?f6/EK3\iuS5v/ [3'V/Dp:K"~TBJ]R*DžZ,XwW㕀˯IsnEK,Y떒u{`/J"mȞ4W jTHXӫ4->iE>5*aUJÏVN(]!1Ajq٢Y hQ `~ ҍ17%q+I$ *w}s.`eRa7^*z5!Pި;+M`Dȕgv˛|G5?Y(wvzk[]2f%yt@YCK]O$D逯o)  Snn]_Q 6/>bE$Q>[u!D>.n5[H kP*XE5";?:$'l_Døw!5v7(Sf_:R%#Yq;]GDgjEQ\+Pj"CֹOpg&u-K^.'>jQ21^F~ֳR iڎB2Z-}Q́|޸|u%JKVv+Ww|\#4x6aU\Nbg\(LB$:~lU$zN5-pb yrF;NM1$jMDpK nms\v["R-y c &*?] ==5C[Lu~yҩH5Th0=s>Cp4˲~g<2{;sk#Rng) =o#3;U1l0^w1T[һ=yD ytxh6H@RK<`+qp:.pzѷx짣‡5`x?d# ƿ1PN>OfrFTtZ@4?q!x~7se7H4 %xO?Hʢ3:ܖsnrP{tyfa4;e*Ks\:=+E#17id"pӁ|9qA!;3%I +TG v-, W{H8jc̙m$%Mi0RW! ]ٞ ޖ6<.sO4d#՞tChR6E> ӎW_nszQxՁ:5HL;Z,…&rI x%7V!S~&vU q-[7d%^[}VN<~v5%Ꚓt.m3 MJBxRثDÊ]oT0Vy \̣y%ljF/ *",٭7^AmNi>? M~z;`,h t|l ? ܙO`}@-Zor^c#neZv_/${#+C8%ẽ_7#ˎ^g d&A7 W' C;:6qၟ.H.xmmFE] x>A&BndIᩢyfEB`P]Fh"} t[Mn+u%JHorKvsPv`sFaxhEq\f\bXoibo:$(M`dK|"iRJWA:.KISu۾FJWf@4[y|&ДG~A-q]*e>mp\wOs!@qCC~tvi|N?5S;zS|m) *,'}7,B =ߎ؟7 3~0uPZb~"m갓KSj˟"P֑h7, SDƭ ,ɠfTJ[rO= r'gqSZY;2h./{.!P>%\B;0@86ELmKNEdA,ZXM댷wHkWfb5Cw( hwF(o"#86άZhEe>;֨k 4. Q ʗ+eNHNl~:DžJ>L-fkxD+CEݪ_4KpB<$Sq6 Y;|@ǴJ)*y32$/UQVfOy]]ūrϰ-Q/d2V"F,YEHaH„4)FAW_FuC9\DIgPlʧϛuȘ;t:ref*] M"y//nXul_A9k ^oʾ~yIjg&nG ?YB$jr5G,؍E~ӷ[2U#j4! 8f 2R^= ki\/?VO!$ʕ-XP=ȟ^ϾRlaDt 9"(A |8X~Wipʳjtx}̪y}@OaOon pMQxOUGF#ǖC(mycq(c2h%WB'aC8(c(@= )"76*[|=gLv_Dg_GoC(2Lʹ"~N\efݲH 1 3@93@gS鈯waHU{L$k=o-} ;הւzhP2Gb!nS.zO`e>Oj=qW7,CO.tr\ٺBRS5Hb4qrԼ֋/da5]NHm)MM)jFM-!!%o/4`# *CDV4Gci=M)|HLF"u Vdx% wo)̚sB'(+! &` t5GmQv@[z1K!zq'P)O"WQ;+)Ko_,&mAǙWɷ 1Ϡ{,NeD>鷰%xK9,h!m0qO;S^kHWw4:O, SA\E LN7;ZQOoK-^ij&s?1G5pIKЦ U>퇵IڡevX|i|DCK`LӢ V|l`O}c'U,+}c*%.R\<+<.PXJgpX (q]ހS,PiF"Od{Nw\-U{A~4Qap’ݧKA͝:<.nhn# g䇮>+[V$" ToVLƊ@W X-&p3Ӝņ60NXSUJY7,UV60V.4!r,sH8?IhMpC`Wm\'b }q#Mv1?&m? vbs4Iۻu&Zs3䮦bnWF(i{@ܢةyI&HY9-^{7te`1?qrE[\"ChE`/(~}Nn#C5̳v 9M4U)}/c:knBkG]w#3ͧ`1׮>s T0i4[Fڦ!Z>q}֤R3[ز45"}s\]M"˔}nȷkt5SLh}`p90 n]Y̔S駽Od#bB <*<>!T 8gԁK?{-gAJ oK`,^ :L60߲ BV~XZ$uɕXȎG鴊&]Ƣq\tܳv- s0X- =ǿKFY _ 7Ɔ%zxR#eSȾ&~C͓ٻU@R Qd$=?Jrod@-!#UN,ƽ1rk,Йuʃ=كbWd9td\p彊YoUVfCl9$GdZ]8Ș)!S'.9K!A՞MY_q!Gti TK7}X7s:F- 9]h0֑fZuEt y ͷ1m.<1p8e؀}g=2LeSZ d>`#l7IsR0%6 ߒMal|~vIS1?\턀s![A5Z9' >GH5gCz_DfYY'rp6 3ƥPeKaj__QЦ. >ъ^s Q\GEBޭѓ튕U=G°7$N޴ꢞ6&QϔAtFDrm6 Չ/)뾲%lQ{)pQz0U~tq1 ։,Nh*˂ P}9c׬PG"I7+S54&pA8 9[3qNC+qqm(y0JIEN˪G \ŮbeK۷DaE3`IV%Ssuog6"*W^/[ ! @1YPb|WƧ9ҷ}"e5-3}t"#FQ?D"jc>˕(K_]`9f.Obo1L-1o<ڗ܅Q}KԂpnEXpDI@!H∽w*-og 1FJKDRS`,o dl}ٟ:o娻vg6\lhdzHØJBM5.&; f]8:Sw( QQ0|ƧY$7]Ի*qτKý!4D]F^K8ɢtP6H@"sgEvȜa~Y)D3_Fz5$jx?]C}e&cX¢Xe輊%/&ЋC AeM'D̩yƙ]c=g^>[jҹ "A#rRɏfTU :~ &l8!7yh v3xOB߸qShN r3/=^% їK)7;TѮe0=9Hj9X\a+E)|3z9%P8Wug.Ԛn'ՓDN2H: ,mPO: 8;0/b`%YӫAJ ;Hit,g|i!D7?v {}# 1rU<d4uVGCg3w2sGgv=}>dAڱL^q Q헿Es(FcsmEsͅE HӏviuJ&K%H/|C՘ho5#}G,f+;?5O]Qh9TdG0<9b '3fLaţ*H wƛYe{I-`E=u kϬuoo2AqׄR[ EegYy2nb:d]^ᕯFY,OkˆCR {@U Iu2 j5pzLؤkY#-jId%zVuN=Wd։©fZ/H =s_Ik𚛽a%Gޮ3?ÃۚaNd5ӌXLo*ՒqOM)sydorQ].$GGIQɻbǓM$}P2djOHzͪ,\чGҾ^y;xẸJIF"!cpx/.aQ:Ss?NhhNT;si`TD I e,ś6|=Ք+oYϕ'0IXo.099o(E耐J/\;uh~" t9Tb)yE*Mx&:kQa,.]@n;fr GMR f}Z}Qk^ `-Bno+>OwLeSjw|5~>ye}S('H1З{nΧᷧ]ɻMv 5km2$ Ĭfwǡ[?W8za.jj9=\2}e#$$NB}Zib #wc ԟW+3ꆿ<.Lt#pށ ض \>p&¡1ץf l9.2]KˆYRETn3F+ ׼ ]{gXR^qR_U:OvR>?V\Fdz䕕,V' SDh2p͸>|-Px[Z7)ɛF,]&U7 hNgumݥ[r Y.k V7( qŨf/|$>{fjΣ.'@)d|WE`K/ÚO8V-0S3Js$%|?w;]5uDb#.Pm+~x!s^Qjg5,1MˈS<+?8ͽ G P 8R|YvdtIΦ{֗`жb=g`ZBw 5j& GDv;>* 2|p>+lM+`jF)Dyg=K^<$PQ%q E}gxIa d́:ui떺ǻ[5Qo_s fuW\&6|l=Z#@ >J'  \y$'˥ˍ2s dsD8FBONAȁbL^{ C|/K#} N'NKjN{Pp/Jp&x*7 Z7;]s"|@xTS!p!@gm-.7uA CEW_M1GY|G|s4mrhz xYW ~d̽w;Y_Ez-8< a9U ST_2>lA1"2&|RϷEUޔeA:;;.o:@WeqmV0R֣ޔ.#^{&uW%{nafI.m2Q;u~h9+c#l6GiМhO nh`BM 5Q9G'ͬv:)a<Mpwܵ_^$!e'U'G-uy"Ub1DZ4?ܩ/! Gu W;3vQɖIn\;'{ٺ'1PsϪY+*#錅#i+_*jXSj_fy}E2D8f_Jrsf ^YwTJ+jέNLBU;:[9Pˋ0WlGP+#B4>)dhܱUK0hfo 3δ![[{pGR%0- LK- lQVK]iJs $7L;ʇ;WeVuo((U#m{5Nܹ$;]:1tn|DǶae bPm39Ӟ6(+9&@u8L.9:;lh50Gc9rzܚn~mG„;E"pt%B=M‰k1e#<HR-Z^Fy˗ q]}md?4iuuH\hQP| DѬDlat}9ZQL{:|PWh4$K4BaftJkd^#NX) Qd]H glooz8j~,K? Q".HaˠD B! Ȫƙ[%O!VX.T+X)10 Bcv6Q "GkGirz{RK?]U WmJ Z}05:m?E{gBE=:7K@_E-?m~FC7G q}Z{_Ӵɣ2%Dj'J5K[-+m`4 .DcnH ʠg $'}@3&fg f;X邀KN>[-I~ tW=Z$cW#LU(ܞm@?$Q<`!,o淵n=r!W5(:du<3/Gx F*2rr/q\=pD> c$X'fV@3ѥYQq/5@źboR?˱wSg˧-)Ơ"6!@;)nM0y_pxUm/!]qmqoυQw#nЙ_ut*R̷|,!<ouK.|XC-M ]BG O (OΨ5J/Fӫ+/V'k IF PXLf `<|W#`4b&ǞɨˆH:r!V4eCZL*fs'E^hbp."q_4,*|}ɐّ^ae2Me4ج>a=_t$QC;QLpSJD{On!Ũo} avJ,էWf.pG&z{d!mM J' gJJ0 z\ӎ%2kiFiFN릒D\Ç9[>lv;C#Myƽ"FrEmqcvV)tK8yU۵:Wlfё:)}.6INyZ Nl7Az3vqySOi_#,Ѕ~ZDk^mXFQ ė~7y*mƫdI=Ɖ{ yM/(A03+%v0@o*vGѷa*HZHb}lXX ` =ݮˣBz6Je"YkHF%p +v@ jdB8ʴ" g޼fms[Ms}.AvűEl6LXMlح9Тߦ:"eFK -E/>w KAS?IHʋ4r6T*ql'B飍 PFֺzd8G^J3a[YTrrO?Uխ\VD_s L7}`rutd+x}/ ڱYɶNɟ (5;2mv g #n#sxW;͒r4֐o _.ܤxaL8]^ ˾i@]SHhPŏNxx@H4<%P[/9 p|Sq4HM<('NEtG2wu0ZH J3%M`U?@l pDzurpP2)cp}c!V" ,3v:J2VfN mUױnzBT?8V#b _<IJ=xWW:bvV b7u-ҥ<`tXt]J!ͳIR= ݋׷/MV<&עLiq$Q 9 7[!é& c=:J ٛL)/g8u=u bz|0~Ee<=J )71w#&yWݩFvTdT ZRLx>0?u`H%89t{jv.*.28y6qZ&@g&]A%Հ0G#:UƤL@#FRElzHSV (hpķϾCDA]_֥f =8"c*$&{DY8 5\QӅ3t\htuJB =,E \4T+WEH}z _161ׅ@+1 w1M6_*1[ ʊL[Dԩmae9Ύ`P*paʏS A-B?GN];jDL5G8-'yHl2-B_ !uQ> ^ڝ9HҠW7^C$@(Uɤ^<gAD [[E%쓻|!Jgv >Kp 5wlt{l$Li'>,2hCh:I0ȸK1It[ힴCB lB+T['43U/iiM=Wk6L cȁ:ƻ~D<!Ի)9ǂHNЇHY'F`P- @iq#hpLjbJsB2 .e(D(Q!1Qj7OMEy'9Pܿk ~Ǭ22 TV(lDgl}fUEZqWF2_G5i梌nRb+(06W ʂzKL@Eb3$5Ťl.o iPrylw.,bGϏ[4-ϙN'/᎜bhOFN gʛs+M]آF,mFZrgj%CB&'PF|_LPK܏\_G+9,%]f~3q Q1b03 Z"vWlI}K{ƾ~'箨Cӹ#/M 5fo&aEOc;?_\Fa\Y:D~gw)ɸ_PJ4oɥP2R.Dș@fh{ֵԜBݜk5x$}$qkҞ)oIa0S{?墤Y94$ %REwmKYf\,C rF,=ˊ('_/X7/qo"54ؼ2:?.CcFYaW~oP7O 60lC { `'T];@N9 Ƌj0Qb{b-mE9h,r!LF O1 0=/_lr#t#`7t; sM11#|vIt6a|N5S]}ҼjDmjr&%Gu9 z'9Xw̭XnԕA+*jئS%tW Jʧ=10z%& !"8}@ ![ouUCޯߏo$jdeԮn`#yEtx6:5$k5Szz~b(⧉ I,%C7HEM@zP-q׽Ogv/ 5E]^;ٌ$+H߈ zIgwy-Qa^:R=B2;rU)o*kWUZƬ#\O]sE܈Zȳ}Q=rƅUM.Xxh+]PZ=Z+$ 7@/ y]{ܔNkUxvB+ \cX8{):>1#jp6$)w1h(RmeplOy5g Lk@n.R: ć I"1T }Eޗtvz&[دLz ւ J.".j2&YA9=_Y oOAtYS+>FxB )wxaelQpiNcOy%*tnE;2D븘m2s-du8FZcelQ>M.@s5lwmP{R͇B\ڔu'xMsnR'-;9Y 3j?B0҂^ q/l@ΔZ̙bɷN5O=VghYFLgv~wc1I=Dc7WO8YfEŀSD^]&ŬbP褓=7Cb4߂8E;N8{))Ec@AqTɝ}gpޝ;~jVS^o6#۾7u}?+x&.Q+&t͙Y({ 2dٖ8d c)!j 5fX/p(ɨ +ZiH& ֑{\PɝO!_ˏT]Dt1ॐMKkJ IR 6usd*j 2&%Ľ" =+"4y,l9&07 jya,$CIϪ컕v' އ: C]3ksk_.YZNip\g#E]`GKNƓʐgZ$,|= -iXp (R`+^2z|$qA)2RL?X H׫˕jbBCҙnEێ M0bo@Taɿ#8M ؍l(Lmvk>AFNY9RüJƪ}D6ĩ]k㟧iS]М^׹DS"*zLb8bQaW[K rϥLx(6Tqu]1w7{s@ ƼMl+~[] SS'XT^kaIC'@ y-5ǰf 6V[’KcwsƺDiX۩gr LRށC6J^@GZ_דEQ! :IajHtQJQGk#`zgFlxbozdR=q, Y%3@DS})b>ڥ.V#IT*5&Tk[YMIp ɧ.EPu`(a*@p/C8ؼI6O|&DyVDpPT>їԁd*C@W|}6Zgi|reTd6dx}Yu"V\}?%t ͙g̫n""0/`ä#sō#zs48{\&>?o_a^m!h匨L+pPǚr`s"G\ۃD@G4IC^>@?V8{r?f8Ngh|p{`c!)|#$3mg(3?@"i玴y xLT jJQ"g/)fVVꤕ! & Yf$[yJ+a_="?B8]zrr/nݟPX ö;.!ϻl^e|;7nhژBJyJr4lkDg슟ߊX2=o87#_XGջLQLО`ck7iN7SƹS,}ۥk<]冁[F$c{R_%g1Ќ%ɠ#,pFoJA/\F#-  ͂JJSdI~ͳ1kP sVD}3; 4SL<rW<E Rn{(0B& L2,^dZ3ʯ&ʶe˷h]rr7LGc)\+lYUs78#| \8"_n"k+8ؾĴUWyBH?D __b]?yL?v>c[VTtD1'(2l2'VKwHqxmHusJb X=>@PH5NlxϸamPZE`5mð|VC[3AfÓu?oS0HS>n*o?p E8jfT̖|rB4 "'ή>}p^Luѕp$[x;i\Q=ĮÕ:oz_>6V #RAvr!C;qiX^P߻^SƦz+bTTJA=aOHZ:"D{VERphkݿ!jXgj#@7[F8|c 4T4Cn6aڹVqՊj?,K/JiDkΖ=oeOwH\iW'h eU.=L'ff0՝1\3#5'_OJȦ2KQJJPI;ɦ_A`*/ mz ¸lXI1'`e'GQE9yHlv”QZ`8nwCrjD< s\J:R S8Jc7q16buy*V7mTrHhS.Pj;/eN"ξp#7Oq Iإ hÚ8gLcplm]:n1Yjd۳tTRzbsѦn]&A'_kي#>: -uvNKs #uoJ#_dnʌjg~af"?r$ uY15EpĿ'ol0IL UjӹW`M:9}|OANJv_L] ށwa"Q*uh R Xeĩ1_ 'm{\R[Shs-N_Nt|31ZIKnx|!/=A2j?c^TʦZlG-vg4χC4L: CxaAiW^R *" pՅ3uV^K-KK\y(bǪisSihYqpWˑ~c;g},/YT3gҦ *uT&inxr?=:ܳ1FOobD.9f!\w23QfjNKSB+zm>l i̲fVGFaepM wpu6pwk_0E@Y|ݟpTѐTS?_cHZu>}.8Pg y?T 1W%Oo{kg^ j)7K@dA]@K\Š6ezsra5#*MK-ځL._"g^uj3\eoN 䘞xM1-cMw۷Ue`+/D5y  Q [8lEc{ʩUt9?\5ĸWY,o9c7BKOעE 6z_W~+HL(XǶ3LIT_ҙ@L1ڙsT4ehAq_3PL+ǐlf `mrƾI@nEbN2D%z˖=s4wLPXu?&=2\ÇЏa@61TۍE;Sf5^E,h(=p;C Q guܯ5HQ;}ntkcc入%7;i(Dl g[pB!fd2XT,H:Ebn4]`0~]]ւMS(ES|%n~j?rBv~e:R`)#Q,S) ch/+Fԏ'"%7)cdw|I ER+юn>B%g0fQ׸}Ru霣RUXZZ{~EG /@l)&=W;+ &B.!#W E:ZjR0Oً@{. Y;Yv ޒ9Qt@{Dӿ тS\>Ay {w%5;ϒy&QAQv죝8f 9nK *)a#oʧU!auO`B@&>Z8NlP&F6Ynnl}< N%?ýY ,IIy*MC S ;ptD#03/2CQ+p0?Hb>+@ew0 -Zk6-jHԝv3iKv-Rں/ՠal *H:0KSԫD?|_Cs*%'2Y<`KI@#S8܃˜ceK =>42E .n=hVeA!mn $Dwwv٣(dKB^ FF<b7~mXёNMLvpTLɱ-KN,I,Lwok'xxRMS1c7Pr-]KWt1 ē>/M#V"L( <@hkUoR/"=W<@hѝ\[Smi}&z. c6&8jȂWtdluNeEV3j7ϥb&NUw {zm``J怳O.P*WMqI#&Ӓ Oi_.iHubv_a<W~}Z0(L^] MW@Ѽ;.AY~Gᫌh܎h ,qz2])fԚ2JP!1i J lus)f-{; H@ՠ?<;IƋCt7^aIZ%IhA>US|rE@6n|۬Zm 21A̜Fkɾa|4IOCUr s8dpĭHȪpeǓ/<5nfߩo-J:w$8I{{D uwf85zP*G2@`j6NK_M e PvS ;CW cRUp5jPs9&*rkݏ VAKb]d_cRկlλ7At7~'K#u(?H,=(NKUr`>n.a ˝2z#5T-qAOq_{MU.L%ܳ^POr?U+[v_?㴂WSV\*bi\&H׋'T.C T GZfR W^@1!cWN)jE&Iҧdm#6\ "*>CrMy6MV]^|b'p.Z~k \8,1kNL.Ƹ rA}tdUqVzoz0Ƣs0E qH|J'wrGFJgS>& r5y,IBf?*EqvJsD/L6qnuw0bZcͅd+OX4Xb9 j&S=ܻxʅ"'R.SUN|=2;b"U2 Q&˟]ҝ]!7FE X~BUm>؉+3^wA;kZҔ5@䤙"a[Լ 5JśTaFS.]xCiieW%L*>x*)>eV6ʕ8MaWQUK'Ez'vaX%,H-:fHyR07ߔHM":#S7P2MųַƤ9x?K<7{6S 2ϝˀƼrT<ʗn@ӭGq3P e 0kzAMl PF2.2T>tµSOp%İ]&sŪk+〵e'mFo"]4\Em$;X`0%Y!SquzkaTS}nO\: 0f=+]awDa&|Q6 n6V}Ό[e׻r -ssr ǘdqi{h|ǁqk)3R`1@əgxDj{YD//#G5qFtzY&IbE8LHRsϣc=KmWtQN ogv8i/jH2:p? ndk#; !S*o0D/x )W!6_+gE6 A)"!<, SdGn\)@)S/ ؚqpӵ=$9GFxw*)['4N(őx_ҎG<7EU(ģqݜln 6@[XoE$=(zf<38B\$w!և\ie}wo t^|4p!sl{X rQjkNpȞ9,k2]H4K, z1?A WKv98K[r-X'h?i!֢O˭֓.d6j6Px.{f)ph;5c|N N痀s^CձgDH__;[4V'^9hdZsۧHo?~ uX;l}"wASPybJ <ILn#0Ol %&Z{ݎgU(RU`tZtTk MN:sB(E]Vp¸ k\X2T8\A}W NKٍHMԸ[i,Tۗonj!m0w\R+'_ޛ٣pN粎@#R-̽lO2>5kX\ƠOH8R=2!@ֽJAi)'` .<8aJ1TgR='K rxǼfA ] xשׁVo!n 쩷*^: -*5視0\#,X.}0|Ʈz_'˝%qy.7ԛ"#0koP<ʝ]B[E%LD_RZE8(>+Qx/% H*~akH-I&(M6~mOf쉨k2Ѥn~3:T5ɉTGʹ˯:1@<wi%F™x{z<ƃ,S׼&t}< [OBM,}lΐ)K>D1"x)>#~o'mbS>4]>OU$#γZO$K?2ouQ8M+z4e!]P6/X`Xdkn2ʥ~߀0 !TCAfzeRt-Mg+GzG[^'w\J aBHd1"(4hik4Zs^ݒܣX׫ڀT?w{.=! |Sܺw":0&kGR«_ @s 6~Z(2 Z6?801ZWW7{!q=aҘt- PTR8D$:Ydh-NM0|N۽T5 c}by0n[ ϭ ckV1*F4Ηe)nމiREiXx`*Rg>`5X lK4{^Pp" "p;}`4k'5Β ]4р!htKXrMh\ZZlaի l$ dq/?cLP}7 ]_.(2 Mu}5Gi崄gq`q'#:sR }3 ,["V %A}KįSjHsᆟ8[*XxfbQ:煳Rh> Z[9wP|+~5Kl=J.]v_y{js2k޹;ZeɷcȻSpCPI:cܖan'!}AMtS4*,$ŝxS-OpWU,w%fw ?ZasR]%as}IYrFrdZst/Pow>1R"^M.Gu9Iq$\4IZtMo]_F/ Bq[;uvt:̥yԌ =ecl9<]PK(S3"" V#?$8QߡtugwqU:񤇭QiK7[\B MC  mBw+"OMs|vH cQy, L.KPcmАtqVcucraS\WٽkԷxȬF^&9- :ƋfV㭺 \WRbpe Ow(Ԡ((Om v683F90[;)ەJQqnʏttq@( {B0wy,H${?=~&~Հ: t Z{U$a)Syr>o0zS\ؽ5QئW>7n7N([pxjDq/ϘJDLnU>۪7zqfTVǪӲ92J%*}]0LS8GJb^!jX}2=،(Aݔ,F3ik=%u,rj>U'c;Mr<}:!l8<|*;J\jdKPr7`0DrqGNBk<ǷQB}Y1(7 &%t l/;Zzl}x"8ndUN)] GapS4HLQP7ZZ煋⫵5ZF'rM-,+tOnKN:* e *{ҝ p2-+q4cx6q7r]@T0h⪇U聚MG#s I)X! yܭ\gqnIi0뛥0[9Kv|BԩF+!*zuF*"{rqW5,س6LJϥBw];b+-RgJP(/BSC!|1Y$>="^9 $aU@d,lK4 ŋհoU[~( 3=U{hN]?<3i޳C'͕-rBȫk>Qĥ֠~ Zy_6 wX'r8'lM+Q2h ǽȜ2z1P?v?7`ud~"(4>LZ@ԹgN4؍ \yzt+b$TZ{hSDE^WO2,cΣ\~ұԑ?. 5<ǝEGڵhE~@*"pTǐQx.NTS:떞.S$ݕ6(V̾,_+gE|ivr-P[sM}%/UMS>f $UHBLqaDϒTIƧӤBU0kb8Өyj 7;O8Vf:Bw;n͗!H]+ǢeǵIbJq]O7t2)Ft1pj1a1SnY (PK:TF <(=8X<xĺj`DCl*zL`r$TLI&[I`6Ǻ )?^@0bTq"G ^mKKih|gkA4KˊJ[u,`@֚>+Cϭ 1(\k |\NF.TM0(֒WOjy$HF"Ot8 S6ɿ-io7IjZLF-.XܶX'_曻LϰCvV9ZOM}XF "#`,AJQ(nɃ%rE<沷CWLpRa ٽ8]QGc{ 85P߬z^yklk_FND`\ iC-f:,z*ݨP,sdW,ԧh` "s(,ϟ=7zWGh$. @:-FjSM:-z,:KvW:mPba5t" p z}#/ɔ ^Eʷ/hAPwj@V䩠Fr0ԃ(!fRZ`Hɖ+ʑ]3>AUl h"rHcQ nM{"Pc㌬Zy ֻבH$2=ë!H{Cc7;?,ΎIF5YԷӡgKRz?*ey0,cEFT+Ȥz~ws¶%7c8Ͽ؇Y*Xz uSmoH Ŝm{n(;(V\[R9s@^^.A6t,̡P]OrJ.W<دã6.lMYSb;s"CKXkY_'hqƭoL :)PkRbɖIS`n;I~*;Nѕ xw@,>ž5]`0uOz:ԼXREcV;T{K]"#EM_M8? 5|]%7?wW ^·0-f apWqkeIb>mxR} |zIyXTY4+XUKvӲsQicVq 8)ke51+ q v0$FșO+KxN!ӝJf6K_\t$QE.uw'W^"9X)~R$9M>Wtv2[6=sp9ޭ<7qJa"Ռ h|xz#|Jb6]O~җI] M l$P)B$z%@J]6_{`k߽+ PO9$rc@R*R{_0&TOۧȝ}:E܁=j݉"T LKBg#4Y4ZCy-WD $.﨏w YV+t[ArTI3>gl*ʆA]"Նz5MLR 2B7䘬ꝡ2;P vU5d<㊘}0Hn)AiM,Ԡ*QgQ \a+%0 m$J̈ąy* .LcҁR񫴂Tgj|Y(ߺ9z QD)Thd?螣f{c; CpCwofgBE!W\Y εbs0~}hzLDJ CѾ֎!^;T¢[ˉk-|H ?<XLCmEvz؈PzZ7&%R0sx=WUy,nVGd7eW^C+k(-E,,廋dG!ZdD-0b2Y΢0[WqM}p쒣w-K pgARi/x4u ky4vx[ 0f ̿W5~Djs D%^]ua.-{+q4f9QT$ ٷ +d bVCb !%̫ W B9߼8++Ek ( M qߘ^ԩc.SC{U,~cFfiq3-nxvWZ{ qiI" 5]0On+`z~q%<A[Ns"aDO(ח~.Aͳ-H:s H#x+/3J=I8ٖ-u@Uԇ Jg" @r~q-,̧7Ge˟e+gl8k%bIREҡב*62Z 5!PUs70JM~ջ?p?EuX9) t9|,Y ݈|?=ꄞ-tSHJ?8*52]ٲd"o\$Ka3o%]qM#Te2S_(mBnrY©vgLa[:c~*c`+ԩ|y+cTcs*, n5 x8o@?]\A`uЯr JTjwԯ*P|S߈n#j{1?}+ӵ9sJΟ̃)₆I5-t:B&)1/pGzfH.u`QP/h$.Or!e9vn"=їAMƛbD^lgǨ~Qj n{iU^.jKlxK 8U㇁5,TT%5Ėh08J#~J0`s 컪d_ ih[|>ěKP~"6nJȰ&.R€ aZ;WhR$e3.,#hՎ0_D.aiB) \:ʫ":ڀ=$HԇYK ?wPm>Yen ]E't }(]UξEI NI=7mA5ծݨu'Ā?f|L"d99Orɺ&m㚏d]MFX ؼ;T޼&( 2C gfWt'7`=[[>37˰-rs A]O7"RaO`lt8 |$yzݏe@xX&Vb:(qBZs%l!R1B*}WBmUB>&]J]wA4MuΏW3ek"(y$t^oZMnLPR^#cq[q`"oVQUOn U^#㥺6G)ɤ+⁡sITZǩ16mIL>./jfr]) R6'nzn<ʬJ?j4xTc(Kۨv(KR㓼5?9 *>xqo0 1_ ,;e5U%0?AA r]'7ߴ%Frf5OG!m=7?[#(`76Gf1<7B^%W+l1W88U4Ղlꚨ8# BNrrYHY^ysZTfB,eSr;Uzo= 1we ơv:Q@;I熡ftaIgJDY"8ڷ2T}1Svl\) phHf"n|m` w!9FAvf:/`X,gG5*B试®ބk6&c7wcX#Tm^NF?czfFDfoSԆvIiWA"D-:i5vjX->#49;s68a ԞB5{2Z^W_)pKkwD!w<܎^缄1yqXݸmmC1[Npu瑹 G%ʬHv5i( M\`Y@LXIO颹κI$=BE{Iy2?놢nlXtZֹ vI?jdH&l֢~M}|ddYNǚ~}|ۋ oԏb7⠧_Bkw$Ct6J-aMAT%}Ӄ{-Mu,7 &ZI \8%x6ACՍ}$ThmekSp XS%7 DԬYR+%FCz@5;g|wFq>i "?E{ \W򝜔Vܘ B7@]ݦ5 2)tQ+lj\[M6lLcCD{hW 00J ]6\6FZy"PC6z$F+\BjFڇܑe2`1aޕ;p' UT"# R)}9*UyqsC -!oGe+rd\)7HU#Sjt_3V )Pv}ve6Hӑl/x\FM<8fI% n)"r?[?%7l/uJ#`'KfЖ< F ߼!a_ t7 f罛p*x@ p,lx"mꂠyߟ֋-ue_")#wٍ]C5CۉiTq!m̏3L!G ZkP i_(#^,xmCٷfL m͉'(q>Jc~WSw9V 3otzdߙlVt84Z_sպF @E6שEՊ-´뇪Y+-C܍ʹO+ |iK- Q+GmiL &J M LO))D55A`6oӝ/@ 051$fPn?HbqYN9?C.ExRlgӶzA/&``5_ pimXbz_8;.s,YeR2EaĸqOxͧ' dVCz6r2 Y]/X%eN{JJ0G$iß])v56N[f%Ƈ8=2Jn`Rq!6~:^p 6)m &P& 'Mɪ#'_ȴ4D,+ a }]ugX県<2}@42+`nnpp>W|sh-#x[=4O4gu*aH$ :P=*%ӈƩn^H$fqVD`zLTNQSwb,c`0xN1jM4E!7\RE{Nt3sw5=ب701f6_ 9hW"43H NٝC& >U L,#I2=U/OriL/A]w^=1<+;j-_)qLѳ+2E^D^~^]nW6lL;94 }lw}jQ캯i5{Ddelk²qjWz@F{1 y囚|A7f@<4Pkfަؾ%AY% 8zOʵd-&Àާ)VސxO~J|MuzQLATM_p×͔{6#H%W:뚡ԢrȿաN17V[dD@qrEyfr]Tn4EI"4K3p?8(ߔV[c h!qv+(Xzddl5$wEfq|3hSKAg(Cq+T3.:h[eD?tPk+ 5S $xKG+ @zR f N7a ց>݃I7E~<Si $cٹ*`,%< ƾ՚<"h\}=}v`N}h*?ָk:Xe'2KsdlxEX|VqZ3S$0W+(\[WYy[# 'ӎC8U+%1s4_XcsKAX/n280(E?HX}uz5Y;Zs$IMTIKiVȠR/w-+‘|nh"BJO.^KpZ8^qٞ 7ez3VΩ.Ŧ8YR.W;GPGar7K |ÕgRȄpeM ­jF^AW BC>j;ӗ0h|^~s )2"8^up $L׀\Z6j *uhoeȓ?i%:cm%@4 |>(MCJ.'jmJ-k(gB݊nSA\Z,&T1 (۲bf xi cMz/Msf`-=62%o93k~,kGGS_ir[*6]=ۋpjmXԨ"$A,14n|AȦS˾cDC{'G}O-_ } ei5mWz7QV%Hem mv햻SL?NcbDEwIS0Ac05 `Į E@w==wO156m,~A;ۗX;.pgP>ufB"3ԢbXT*o>Lś{g 8Y@ڭWܑhPDiQ"\Ѝ9x3He,,6 XRR),$lyMǽ1d,R*kU;-{j?hOW܊1YbN-Nu#櫧+ 4hAɌly8΋?>>CڅͶ!Q'F[c (EDc_V xy aPg@]_(]ư"T'+,x$#^42.$=`%eT{,Թ@PUV~N.*(ɹ1%y״k̴=h \Wnك+&+*&YףN~ԂFn'K7*BqVrxm^yg=)MjU(S)ى\P8x rˡ@Jз͞U0k*uB*zqFsT<mo)[!" z)kn_[q4ՈBP=;UyG3x"H_Sآ/;n!>7i,xo ڌaR@0Ϗvhvh]E=hT7b\%CngKkq`jCp 6O}R>MKU%|l= THiROA2?ފM`hm<9A'hSfq[MP8`h#8 cg*-cv[p. *yydvm[J^v?إa?m"/Zc(# NsyzJn6S2Ma$GTijPag,GɨXBL.=?jQN˪ 18#E7CEYJ~ uRFƮ6(Je )(5+ZЋb~ Ro Cox }瞛Q%FGuц%ـ;6*jWE8(-iݠizhfi^$ YaB?lL4ݾXDGV_NIl,"ἓi _+%/^m-WxC@d 򈡩BHZ%c[5s,T&7 V3ʙ;`_[,(4-4 ѽ{h4i߰:jpT'\CM\bHi#Ik,`Y%Xʕhi+{L wĊ K_G,B{Gb釼9#wF6 )Ss'b~xOq;{t=h~^NcTIF]&9(D[s>%Uh ť>ƒ#EݚFZnjlZEPFzd-M":+9#)b\~ovn+OȂLڞپ L˫ϧyoLLaqc20` k:n!(wE$5 _Wŭi7um&gcp? )rS h0'SMh#W ~nV6g=3r6MB 14L{nW]{k Uf;u4xV # h_ibK~dAo\ьFs)Mq&I",#@lW@AMmk7f]Uc-X rY˦M: P?e$\6<'B%cl+T~`A'@6&{vC:"jȓO>og[՞B֮o76Jz, rv7r%f)vSVU]n7YVxQ07np.o;DئkH\A[h\jDΡ "ygah(nNrSڄ+-B#eEqYڙfܽ 6M%tJ0a%rG&Fu yJߏ:[¦cUT3!r]M2IVIH8"2"t.2m]84M z5:@v4 MpX3~G+``]K>ٹj|)<_[}lעT̥[p(JkғeDWS}fZBJ?xVHI01$rS9΅h;AG Lia1OL$pI>/=MJN: w&My58[ue\ 4JQS潵iF4 sNVa{٨I\u0wW|筽-D^xO;xd,H%{5]:RD>t~F X4!u[RgMՊJsݕpM S<|U: mOQmĹx& A 2nCqZ~\VJ 60 - T^1mK2iS˙ TcB4ǮI!z9Ĩhǡ*Z鋯׋/搙gx[>/Ds%w^dEoL}%]ȭCGБ ;-j}hߺolg/ ֫ׄXq$ġQZ579K,He7.pN{#䤻q2[A76S f@tXG;szcub;'Pkz+%>=FWicE-pܮqB3z? 5ZtU FEnjG.`ؔ}JZx |\Nvl\xq6jRKH&$pڶ\]rmAX 0>}#ኴiϔ4Y͎hob,/ 3kXI;u4Xf[j}Rw"ٴw*=3z5#3* yh򧿜lJ Q/ϯ߀UbO[&]5L,fU|ʒ"X !-0iꞤ1D+tO_.g2$ZZiG=ޚKeSiP&CzriyޣNQ6(4mE0-_xGTt}e8ѻ0ו}xpjNK;S5b4JROlgVɐp1gbԶ&ӣNj#炚CfF*2,r8֖Yj={JYO9q16wa}(%S:qas߀ƖgG-!rIf\rKL* G_.AMDKﲧMzKѺn%!bCfXiԣ1u'ӛ)?$=,@!ܗoBvD֔P#?gw~?_ ƖO3:v⑥Klem@ͩ7U 6 [*uubZݗQ+IL^)Y{R"H8/X^l/Qb^=+V(f8Z=ߝ /60hq-ņTP2nQ*iޅ:?ozNgW>?CH!SG ,঱"5y?l,AaA [̈́}>EDݙhU"LJVDdi'd=1A]r1O y϶@iW¾nk*yc+<H \TEyq ,U+! p8oqX;n-NfD!(L-`Gʺyw^T#Xuy3v@:>GE(FrvZi9Εjؠ$ ز[]ܣ͈gT?8vz/4*RFĒh(sIxqÕ_yLНAdJB̏:^ޯS0AO #~EW=k%HL-}zTH oÝ^oP ikAe6e1>iYP6k\z(}ܺGcF%o3c h*Iaj\et?ܖT.\⤀Uk9ӯXgwHuUqBb*TǾ ` `RXƓ_wˍx;4#BVZkFaW3RߧwZ4|n2`-Gd犅PR;踷<6H9r6 \M#~-yIMULkf\ 15'iWJ[P-ho^m#ԨrD`y=6ḿnMyЊ٣4]Ӎ 3JU/Yܷ)eUk@ļS;04[f/ =:=VG  ngBOa0z&Ejd%gS\ERm\rBϻ3ri~ 52*-8m9,Sfn'g̑ } D4U͐S /x/x-;\oz|قղqb{03h "fwߋg"&)Q=E82l'#EqjGDlRNet[nfbL__q=/fl_<*0JykrQ5Z]j<<$e LzF~),gx"y┐7ȧ`cout.Y»3`BϦaE{2\0랚oHNĨsoO\$ 1?%c QjD[YXoTӥBއ,g10}@/K]ĪJ|?yZpu+tq"q=]Q6ʊ?1 $1{=[-A׽.̄ty.95[9c倘sw;(pi~,B\܃?"Q9hjاwK9LJ,#pzGqxԣ x$P!:VM0}=YnOCW  bӀhsscT|?kez>s$-Zf|~$zoJՖC:.M;$u^ű_;KHIMIއ5Mfar}cz5 wE<G\5]A븀TzvONKRvV+9 ρPˀ}USqYk!D\HY-[3ݿdMU`n&G{`fvFͪnJ|ߟQ|r=&arf5*kIr^6r 쥫y!k* bqXإRՏ-bd)Y<"@ PR\=Q_E3YXᛊ0? V|lkF+5s lj6;{'z)#]WÇhU&XY-͖%@ʡI&_mٙˁn_Wf-va{b>{{-?zERp]Yq}M:~i\pCK;C> ̧"[-OPemEluΉLtH+tV|[>z:IۥSX' ?QAץ1jWȈX|NT#vaC K"s!_(SN9Ξ>@y oK=ipKDrڰi3]xXc|=xzhGvqDO.;=a~.(}*#{4Qld˗޺m*l l/Kr#JE!\&T̖sEҪA  }vkz$!A1dICw:u"! ֶM{U $pѡk.8rshPv#'zBz0os:~`axZ IVq]ހ+~wJi g_cFh0,Sk$1fHp7V4xJ Cr 2rvSqr3 Qcl5+ti{t UҔO:paI׈>gF@ zq1;uh-~oqS6ЊrjK' YvᗴԧH벹, ҧvLuT_Uܰh$4 A"=j'{9 kL"W.W 4ȯ:"WZïw;Tk;:^Η7 ٸ.P\H (dWYftM%TM<H$/~ɉQeL98K/\4K?'· :.C戜N^yhj:ZfWU*,jDT".4A^8MnQ+ZhFqYV*)l{N6BB.H8l_S C{0U| &[Y& ^E) Aos"*Nk hĥ/J/ hlAucꌟ='o,ƚdװ)]F =8i;?ibtE>a9u}pZq=4e: JUAnﱵzlNVWNsay!UH0G<:6keݞVKyB]`sا \.IoNӑc:V5?W aؘ]BR#$]¼TfHKv[SqJrzUTwb:@4Ii&0%L_54h~VrH1f^埛ئPb5JL쬒O 8C,iP+8] P5!gY)qmpË ̵{ZH6?2>>}%A. Е9Zw2bxZ'Sspd QU N[ _ X=y{ѹdLOy#s2ǁ` n0xY7}.7<% M1򠻆OWL2BhdR#ӕ6ZfǷ©>Oi ` }fԾ,aWAg,xdҩ ^~f{pe](5g e%9W)}V!ggf;m43׽x{`I.+ I0{͵ӄÞGe.ʑCLxTĽ*P%kʚ0.JTh^q#'qKq&C.Q2x̾d/HT_+T?d屶^9hWKEFm"GL>ዬm%܃3sGNP!e"r"nrmDEלpH8>grk,4}9Y@sD1M\Y%PL2~U#nV;eGrD %?]Q Lw14xޒmb'{N CBno*\8?dIQ KCVBvCKVk5% bNA<Jg K"oJaB$u[Q< F+oW~zi_IomB<>HJcB4*<*7,9\;ֻ/>;b\-35V5 }_V\Md/N$Ǜ}7m_6D~a3Qlq1+@xn|-3Bym*ӏ=F*A>)Gg4 2 Tz ֘rBsl]2)G."l_(GiD$D;A;d\߭\ŭ[_ti?ʰZ)< p?ka)&|4v؋ t37Ylf.׫hșT!8U,8'Q{ho6!Mn{6ִBP˛USQ./PaxHeaders.X/src_test_resources_com_android_apksig_internal_util_random-data-525000-bytes0100644 0000000 0000000 00000000034 14763776540 031140 xustar000000000 0000000 28 mtime=1741684064.7220000 src/test/resources/com/android/apksig/internal/util/random-data-525000-bytes0100644 0000000 0000000 00002001310 14763776540 025641 0ustar000000000 0000000 @'[gZ $h4Y+,C#ТPml"16 cW ]RT9@>8M 'q 49a)(evJx4Tu*;;WlĎzWpvmIH*2$ 6jD!SHࠛySٜ>"C.c]O}:S@DM}&7-TQHMD+ak#q{D"'OV dw`iTMϟnrDdFt} !TDP9^L]g?…j-C5:7Fצ7~jH0FsbCO.(Ws9ϊ a9!D+c/D OTk":=97ܟiM:!.Hj8<םڢh>2YsxX?_02l=EҚ!bFiG '3Lkt7HX&$j>H*d/|1f)=6Sx&3e\thUuTqV6yZ s½:Ha*n_2se IiJ/U{W`кA鸛 B8)4 VaԕOkU/N@ƐQ8}t)jUΩ1&KwOb*:tf ̖0O ?ZrMbż`v, yX!A+7*qm[sY=CLO ׾Vvy%GP/^YE?'yb vz!?Li }1Fc}ü1}u~fl5&69KH8sn,t:P&n9ŭyqAgt3UEMكwqMP8LyیMF;gy,yPl {0TwVij(i̅Fwv&lR&(|??W7ߓ_AC:i+Fye W zT<3۪86T.)Ї4ߝnBj\F(\ RܜeAw]!MlZl: M0[Њ|Q*SRDw"< v}gj=÷ʫ.E!8ZPU[ gEQw%F%΋%mi -:bJZ_nSL7 {ZI~bmŒ~Q= ڞ{RX*i qzeأOE$OqОFfu>Uɮm PFyZ6<~L;aHj :derT'*{!$+$Q~B;B[XߣzVpVhmbr>[C3Ll`FPUꢚu SOIc5qs<FPGA.|_}qaA$=!Xs!̻1kEgK ԫ`0\z\մ̌P4{Ej0HՔH]|})nGj"9q2<E:yD  X|ny!QL2oи]]ʵ89D<'3yۺtw-O\dG-!cu%;CEO! { pUENs̬oԐYy*9OIǓu7 mV ~Ҋf. eV78j% ^NV:$' @V;407[{*N$^&B[MAv5{~ !cQ0.49"ZO 0)_!*=V4!}n."Ə)X 3!G-SˎK!lSYbPY$i2]߸NHQ[ib ?  !&44᳨꧅6Us 6I^>Ajʮ^2AucE>(su*j bH%d3L`d$,7f( SW͈cX>`Y_ۊ+g2I0S[4b)ZFǢJ>֚۵j3^CÚ\yTI8PVr^jNErNa(CUXҏTZuE교`kt3V7 ?Q5vcwقs-^ٜf|кӞv9mED<q2UM[*/:~PŔ(Z;-vC8Vo3őMX.[tKL+bƑP|y!]n-FܭAbJ @RRJK(> *#7ccHוo@9#y J̤/ 21 #@vefFڃU3. V Ɋ6RvY,5/YU)& efZlBk!дgP^՜ί(8@Ũ+hLMQ4}ȫnGUOAH-Ǎfu.-š8g~pAjgpP>v YFcuKSL$(?s=nAANM =7/m_/?x1=g!xka]9 6Z娧A] Aӻu P08e TN^ m1ӄҕuwHg @ـ/DOޗI-}E]KLݞ~aʘM@.} Ɂ^;/efjbϧĤֲ͋au3[qz9ye sJ I{x_ #r{ı=kA.~4IfcaS2O#פ¥&@b! Yпכg@Ǎ'vM֘0СSXĈ"@WICnĪ:bfo$P41M| Er97ܾGv| Њ6~l{WB|P>F4 /ޏ8NLG)OP,fgiFXɐ!k_9aί^kYo1HɨO\wΏ=t֧["cAҙgcpkh7tƏ3M8Uiyls_t,[l\K=>GԿYF@#1.r0ziy3bۤ1i;` 8NMݔ%RkXEu橿d)}?oF`|!qMq⌊x܋R Z߫w71Mo6t -R>#8i8_t! h,z#=% p^2TRLĪ 1,Wk }1r>se o8AdK~6q{ŵŊf!⠨>\o߈5Rҕ)ASUR3( ),iT^?軈ԅ9^obZ`wle"5Ajx2Eb [ J)ijk0m#VYTCKƜWUOkGo"K/5t\?|fKH.xk!с ob9AR8,Kn1눀8;__|Ji0~ڦ-F*MI@ЌM15>ʬb^T/Hc,>R׸| wx i5/AP?.buIE/,u;dm@4;z2rЦ9ln{CY-bEJN* Or"}3ǩ3 ϊZ6;ҩ4~+`-Mt\B%ъ% r! #oqz40$+raj2,@ftm(&:̥Є"1(h |/%fm%)n8RO=2  `XV."W7b1Gl.0YMӄK.v9.>E;sԿtgl2܁'y+ǹCY%"f8v0 r [l /gC|G 6sRJ?ݞ>͊vǤO')AD #v~&K̛.DRCkKCɞGv5ED& ˎJrߗkoPo2ܯA1'pXLn+< C- >E.P=pL~&Rrg5OúEJ>>_E(qR ECe|6ٜ@tqW!P΀u;`6vU8-e]i=qvd J+ABFKH}v0͍AND={!PfA鉅 {賄agͺ.oRs37i>{Hp@{e|{ы,+p2NCE! 7 ,1\k_ @w=:|r/`}BfL7I-|?`_ʺnnY]" I:Ѵ0,\ZE|zRe1AW[ h^lb1, W rۀ(c)dQE/Y^~B)2Va!;-[T1M%i1[8Y׶ntXFoA WȮ'b9C3 sGkT])8^زT`xL_uT:s%񭕿&$c7NϹ=mOUY)DT WIpB$,Da<K X{)[[O\m0Na'O)^!͠mc.(|mJlP%>|vȮ|p`\N]9'*4<njbk2cyqLBH4[YZC ՘Y+0 )6%&Bﰿ|'ujۺ]GeP 8s(Sj!K- [Y鲸k 5VŴs…6L"GbȖwC7Su5֋*~H@zJV=f E-m ^/(ҟhEݼCkAQ 7(75GzYM<;@Z.Q] EY{< y3IX`fz(^ Ig0p/! j$z=?**͖v(D 0|4?G>`lA{݅añH&.Foi }Ysk=:: m4t)Mf*PUFh1B_7^KElA|q)&)vkOPFȍ IBe6 Ʉ~4$M Զ5y(r9lYzHw\.Z)OK=W/{ Hyiڟc=qOoOb[fFITpiX¤o_p r0D\{WBk}wW̓3LgL%Vl~~hC` u1"<#-h;;L[jѦ/=7m3LR.X#eHhZё|l`x['q.7l5 >ζN:g_2nnQr)]#LgKkĢ\[`I110<CdhU ZeE])ۣL U AoVvS,L{S_E8U҆-T~wR o/ A٠*Ь+΅YS=8~OxhknBe5cz/!Rn T>\k)[.Lp9"|`#1IH'kNLd_# {T61eWt[?0p'4hb5w.7y!)Z\T7Ra&dr1sc?lpsL238bs2(cfI@+>tnӐɾ\x1c C7%\6[HN?p6P@0 о=JdGINCG'Cm"= s;t^(ݍZ|6mBr%J{MIFc`Dy3)ē|@H7M2 K{\^(w}Z~iu:—e6p^O]YJJΪ|tZV&bƛ%p^O!"TT '+qA} G*@Cśz4t{`-եJ bT= `xI5Em+ºba;fHMN`\hZ>:t(b{N1`b#؄K(^tʙKK Ǚ[?B ĀC(^m|wwڲ}vگxnA|Uzƽb޸Ѭ>жj;dʭ)ֈɹY)%5vֹd2 Zht8%L-'nbO>۲ Q !8'("$\tXO">__]w.]lFQ]6H$HCTΤ$9i[.ݝҝƂJ3R'˂k?Nz 6]/%pVs$*tm{1qEVkyd.B5 3ې,#*tgfniw;-޼@Wg8kHaC(&*tK-UD/"_6굝3=Y*/*H,V*׸o$.- $vWl%pxQ:HB@՞n,; *)zmCۡoH:G[Ri<0 {C̬߶PkEF(I/gn)+>Nq9hwN Dh1Li, *2R0 8Qq]bvGe:+3}(*ÆX亹62J`i5o~ݸiC@Yù "d#_VKaM 'sQ0^H#=4WTm 7Fm)6 Jv15mqHnL#K񟣳63!븚kQ "KaT|sX\Ѩ6,yI 9zξՐ&h 0jRe;aluzˡ*ϾjQ˟Uu2w;n.8Й'ehn`D~#>Tg?.pG96uPE?lu} $"+.Zc02>{ǁe lTDT ԢQ%T4Dr!A&fZВQv&o ҹ+h޾) D@ =5*ה{"zE,9ξc6ipg]ߖ>2-;<̥Jq.fV_Yq"F{,z./z#CGd9\@_ϟ2sOI5 IOGxg7w6*wF Vb,QgN0o}t6(@<A( R wcIV_b%%h_kPY &uENqM9|d#~y]jծcݔ &A2o"ȟ۷b8%)OOH5)a%oQD̩1Rkgr:w![ǶAkWڂ$ ó>"xz_yq9SsXh`xw d4y!Ύ잭l8 $'d 1ןj8&Xy; C}+<~YM!z)|#ϟXOؽ۴93`"=vMfsAc>YfeK |,YO]9z"9+3n+ E(ROQ̥f*aEiwx_1XћuK17⠆N'vwwBOr\ZSDX&@KX!4V|ܜ&;dzT,;iw;Z^%VŻKs-ħ'RPM|tͤ@NC $Bh+W[yD̶ӓ{ocTs 2Bˠ ف#ҝh֩y~:%RfiLX._ 1_$G@Lz2۩rqX7$v !kK׶7Qc{ 5|ZΎY(5=iE>ad8h-kl~^BLeG'q /A#K3v|"1|I_"ǻb>>4:Ď7_ \M'ЃJUp&tEy%~ {6(𼏠!:E<[,|߮j5\iQ: X4;d6] M.Zׇ[ǮUn~U!M[FؠV4 ]2W|C h;muF FxZ+ {= >jeꞔ?̟ǨG`PqYD/&ش8떣 h{M}|eqV { ]P&1"?Rrmƫx=/7#^}f݀^&$G*\yf2jĽ[>* 6gZۗ& ӌZլ7 cK.^ۛSTϩ~z+LHT:f[MF(ٵ {]"5WdI3짡[(`1hdKtaIcatMIs8-aIRzs"4Ĩs,%;An{lPqdll [ٱ}#[#g1 ϝs8j ߖAnXE1܅B-6KJvҟ)AJav9٬QՂDeב84BG+#EԁGx=F(`59]H(JWFp}8uh5~]Gơ6~|<ᰃVbԚLCdCi(R˞Gf|4VXȖcWo7Hč4u?įl,s?,\::)b B*ƮE) 𕪎5nweqw+2mnQ.2Bi&#FKp03)>!8vl]s%C~"1{/pEc֟PyHҍRbC AgTy0L|Ab#).;PH6i}rڼ.1>LTWP-^$bۣ㭟鈥^]VFHɰzw-2CpԊF9t J>2 ȷ0ߠK!ChP)mK"쐮!Dt=tNƽA" Q/[ֱ9s;cmALֱ# 1\*'z<(mAm#3یd8Kk( Ci.#UƟAU\{彇vHZzj7.f2X{e!;eE5^fv/,ECKz+m*H+|W}{֎3fq.wmڽD5nkxے9N??UdO/cRڮ&cFZj`t9erbzWm6niGDje9L Vv2hxs&#UTWCx5.܄r2}ɿ?Z @aM3@Pޔi|[w#nEa2mCn⹾}Ml_';~ic.P(B058>Qj){RaL|!~;CN6O!R,Y^L]JݍU3ڔhvź6ɖ3xl_:1zA"KX%is[̋}+@2-75 |r~Ĥ׏ XO.ڷ~C?yybE,L\9#YҖi- %+^3&n']z-‘>br|6Hy ԮʑMl[@z,0[pQOja@eQ `~gfe0@@qYnhq AcQV KpAHHrvPqy?0"V-%zF>Eg\Oo U۱6s&HAb- B hp~/X@7k10M 459mRcf^7+|k.o}<>0=<)V $6ƨQ* ?~~"'b{z]]\"%ծee۹Ĭ];VѲ9f!.ijf\ E*(5ĿT퇒,لԽF,]ͮLj99asUxpI$2~2+|d}ؕ/^Y&v'[k/Pl).1A}i*Bs |HJ] bxժFGO0I>9Aep $RλE`oS0$*i6'WiaJLB=!`XH+ۻPNJv\ yc=ص|ٴEsm}ۘ8mWD-9F)9eAG1Qz& ƭ?)x8k1HDLHymC7sυY{@S'YC b#}tVU(G맽e+r&28!ώz<o((`7l̸xXD?~m}b^=M̭ +ߡϩ'W3w!T!oѹZM@H(0;sSaBZ{v{ܪi>x|[ /KM'Fw[PHwNnڲ\$9^R?t8ˌ]q Ї}#=5O($JsW)Fl> Z{l>``63U̾m?RŘċlii:V,%mxW=*\fDX8шlA@lEDҐiYVč|̓bpO~huvL /ZPI|:#1Ӓsc-t!VMnAt]Yl#=?ܦ讘ƿ>@,+! ;6'(b+6Z_jUWuT&{G?q~ޑ.=$Kd MϜ9<޺5'LH݄/e5O^GR┭LmN զӨw t6>q yvM/y턳L6;WDAANT_2GRd`*iQ?L1_@ʯ]ey)0*خ}63t9 ԆqHJVۋ|T*A 6C#x;z֐Ǻ(_4Pg< B۸GX8km=Ã;1 RHZ-qS\% ;V(`W :MzMͅ|hE0N4.M'+tq"w,xXӶe^&aga063ҏjdoa/|? 7.4o7r o3 ¸2jϨ?-0bD,N,6$n?{Q2@a< ZNح~-gҴ)\I3n7ٷ<&±z66.+&Hix@ذeR|0t*qX9,&SpEc&2#`Ų5D "Q ʮCh瑀k#A 0&1Hs׃sp&W;lO_ѳ_ ~ә3! mûL!]g֟49̲XfZsAOTȁmr8pٙ>b#WPcz5upmemcA+} 3 ۴U]{@R-סOt+Uڜi/IiK0KHvTNϹ-|}6/~_࿵ ᙆ35$С=XgeI- z$E>yVsnqNgp%@Ra.R(V8PW"0,Ck3 O"NRb;Ͳc˞ߩhnF6ɕUlw^4rQ1]sl1ĝ-̼Q1WF{rL[a&%Oxڅol8x)EKm} r ZDOWLHҬ6I`7_ot _xQdٮ0.%3`LPG"G=XFF+}=}+YV@QbhTSҀ9O/FFYʪGã+M+=f?aL _X:+1h:tx-2oo03}2k gm࠸IZ+^y(W?5,ɖ9TIo8K54vj[ގwHgah4+ wzC@+?Kf /H6{.*D/;dNx'K$\ + |G⒙,|Ǎ [W%WWi帢tF'ˬ0 ܹ:ψ(Tӏ 0 2 S+5*'eRF4`\e\RIAŤdm lœ>QuV>o|Ҽ#gLNK\/hFB""E8Iq↏O1iN"5~}i;E=]!We3.HAf _@ReDTimJ{@k4>oG^lOp}`AfM3sP,h~,?Y[}]6r?Kg\-a@P\k{iW@]z,T* ~m}Yh9UUUEd=bT(+RgGCĄ*552L7 :ͯkVNnM DQ#>&pmZAEI]Љ.Wa"HH5N T?:.)MD5|Ci{afh뽥juT5>&\xh/ TFl ӦX{H҉8|dIʲ̖Ȉv)n*H͊(H` YdC{ GzQ4o 6}xs:&}@7׍sgh{:߁nI+E58IUfn%<,Y|Z{:Xvsh|=:GWuι$s^,r,:Z4R=kq,+I]+Z_~)`G 9f6t"DD|1s#ljû6FMx<$Z\VToaAGe>FAֆL RI]JQ[=J![Pr` )FYOo<'CQs@39CN2K͗@N;(pG@丝2&n{x9D+۱ !yNͯB7;+y{JkdYVΏS73L^6V7Ŵ7rh.+RhhWii''1&v{FMeI@sd_:t}CM#YN~q-{7Z9DrQ@2ŋ@bw%>;bNW.𮑀84$'8.S97*y\UBJIâPq%4 r_[H'm_;\1TdZ.ɐr@h=8Z yh8r3Pg>: $ʶvqA@zu8T!/p8&gHQ\g:`0\84O-dJ?YE|_Tq]*›[!5A0I!ilxc3;2oX-S# poU)M/r>Ky[ww[La3]Bh$rsS`94lBZŪw%aXaXٟ{5E.py> ơ3b6(>2'?Ou\a '+3seZS"z+' `P8h 6@cz,Ѕ:h#/(.7[}hsF.>Ԝ\w~a{"W?i|PA@Hk"i!w5_R}qK:@(m&oW3d6یfzl^0 W=d5_J;b נU'U R;8M +8޹uFTߪcj$d( fЃب:m)7ķWz*̡Ra&9@ާWXV"*@Zx BVU^-\~hĖ*ESnFuޢƷ+1Ȏ."_$28)*5GL1Lm|Z c{*·p 4|d|tU-G\ۗi)"^߱8ޏ+-G#hAGTЎ9XNqNvXQTs8EYVَaoy)-D`4 f@ dlqB%J}չ pInZ!+hi<ܼ] ^; SFTNv#Jƿ˫Lq f6T]Pk c+I@k㠘1Xm8{?G!%cU좜jg) hOK\7!Rؼ[(6CաxM1s灱27%w%gYfAkೇBB- n&ߌN)%Y^Iz_P%~X[XZOMk+Ji倡p6CL56rx{ťWm.!Ix$J;! {`Ylw%*O@V066ԩH|@u}H6# gM_,fH}Jes-Iy o b uEP/5K!hF:hN;Irw+Ѡ(J^DΤK4%s5kM`nV4SW1"vP4g^CJi"bƮ͏Ҟp=`Cʫ&h7٪xMb̓ȐKQD?Պ S=H"уx۪Ta>= n ;MAYG-VfJIbT@7{`J-$^aL5Jlc#+, w ʓeĆ Ģ] 56LrA]Y7V .9(W\FVի*Ji@Fzx+HңYvCNb%)NsKֆrB/yo$2FFw^*~fŁ\uc3_QBUš%%A|@sK>CgexGJkDz}4~,Ì4BtnmB<(pJx/zt]dnA4;=%ʰ2}b?xM4.PLoNг΢:[ g=&Kz{Z(ЩbtEd:K <]!HQ?ԫ֍*'ؚS*CTepK%l~ra9i+iztD]Пg*wsּA%PӋ6Yv'<ER q^uw 9,oDԖnd@g3 ?Jpi:; &cmv%~HKznckB_ 'Rhnk=QNu!NQ8`l)Z'<^*pI_R<38bf ܎(Xb:7 # K%bW%= (js_F35;=&ʫ mᴔ&ErPhhNҹ' -GAyG r7+Y`ͯE3>LFNo$vڢ&JnߥF4u!eAֈs,켭ѭdz{}UW-N&,7;ָRuc|wQJH #ia,}Qg\Pùu_H׌>+\{!K.釀m3QrbK+10xV{IYyouʽ/g@ ;9B}U,2ʴ-ې,W9:a;d-+EteC>wE#]~֨4$Ê-ΡS-٠ciY7\kܺ[wQd@"hHSAe$cҩ7qvK#͐Ůz̪(QULz:T H'f5AA/i\ 9)Q^s6'h25i"IǂϕD۷:7ߝd(l4:n-7ǕŇ,}Np(sOqj#ef#yN~@72^W"AKJ5AV`VB ayHD6W[1c`4+Kd31va͵^"}᝚f]ņj3{4ה~"\\^'l;.v$-]JU;'D̏y-Re6xhr6T}7VK|4~!Juݑog,۰m_ŖڏJiaKC"J6^'ñZօ(ƳFmYy>Ff{my墯+Hw<5ÌUpCkvD=~סdv²uq \vIh~"7|~Q&-6:̓r[Um2.Uo_X~!;ʶgDƙ'XrqaA^5'U6uWǩ$ )sv< rʊiCnݼҮBe?ʚe opqCJ Jwk`l]hE=fZ:uIdoyy"IK9'\."VlyU"omYVaY~gz<+OǦ16<3HR\ZGQHTyx+P,|%}D 㤒%jWP-J^q@~P R^:D`-!l@kG2C!@P4¥]6aVdϬ1Q5TE+9f ܤd$cXX.2Aqh-VqIn^&^5ꇁoFmAN! ѡ޸ei" 3ZP:ڷ/8 DsiY1?Wgn@>¹ Li>4!RS^3n!ZJ ^hLZJ+į>֩D]O|}JSm1 mvZs(!hpy"k%T'zC!Wa&rRÎuθ}4v|4]bSXfG ]]]E-y|,W1 ATlV<*} bb\b/.VSUSJn7vZĮ_yB+ D{ [sߜh)[F!R('(ڢ X:Xʎ1˂i;x!1?vUQȕuZlje=o<Ԡ"V\20 sZgi\]rEEGM,J)Ub?dL7j{ r5/ 29XێQURQLiLkY[,s=/1eQ~ȓǣfZH8f??5Ď󘉕qr}9S10U03/toAMyݢfyO/U,=|-^)Ҙ/'.L}Sv_,-=8vti^Cw6俄b*$H*}PK}E;]s (pO:^nʮA'wXPcB iAYUDMF 7Y`eI)\Ⱦ+лx8yb/VAGDlHGhͷ꥿1&DAn/΢\zB!R#X\כ4c;oBKQnmfO[:teiBֹ0-==4'ogEu?2ZSGe7F?H{FdΡh!a D^1 jx"ѺnzxWej<_ItR0MH=)¢ۦ͓8v3gR72@v7bSYlKb0o좲5MOCCU˪-Le4nג;#@JUURir|͎Bœ;W_gROVKX HVA"ebɒ<t ,p>jr6K^}dL >7+k̗K <.v?f@P#ߓ' Wna4d%NBe˧_h|s3eg@'(W,nTgN>+d-#NONv@b[AWqn,0 t k3)`x!*cfnD3ְ|nmPg9@ '-9+ە8<ِz;~dA|'?3@rXR86b1d= aofl2 6* 9eHQ&ɓ*,48acQtI1ֺump2qTH쾧2 Fg|Jޘ-atq9iD6l䜘"&DNH:?&1?0ڔFF@h:؎M}("uœ.|zB:>k9>vT^3<:q&!F;:`ɕ_|TC}žb&qU`vn,F/GɠQ2k TXNl+ĄzlICS ҅=E4O3X^? APc#`zӠS2ՌuC0᝭oY.b ԣr5 )X1E[ZHZбjpu214 U57Eѫw1<걇[q45mγY^'It]u!!k&jc3`)ړ*J0;^%ۋŋ\;8 ek$E ]7twZJ#\Mkq*s8;H8 ؤVk.xssZLЦ(:hq8⒖l CKrqsM[Jn=ltxX["nZ/Q@;zo qb/K ];Ujw8o(vVHh%\!r{ ) 7bz s+z;qkد})xڵij#}(*=KӅCZ7TVqWʈo ch3G5+y`e!ݩ7߃pOwG!,T=MtŚoT̖^RfhWu )CQD9>KFa^~r>TE7c$): 򚎃ezcCFBb.nsxjlsh]a"ePHmX~WLbZ]ko{//e|4()$WXerdLMq0-Vw8;ǟfOm ݀q)aƐ "5_MYN8U|U6vqνMV=o-G|O%R8mJ *sa8^s`H`;}B|HOk ڴpœXb[pYᄍ1+2 SLa%Z0^]۬> " ds* 56=bb+/ z0eLvl@+ÙM\TH<YYWflsT5s5HpMQ% Aаyncۤ{3# gn;i&3URnyQ"$? wtd p?c^%%Ewtݽ>3~w_Wwfb® A[ݡCԩ枦oA=Q'F ʁ,/Ŕ BxTpl- zVF[n尳DhPϤNWOp%aV2]~cXi*f)crۇ [ڕwmUQN2Ho~ae0ŜG'}P}gF:+Pxpa8 q}giFY$gͱݑU?^O^ ?z5Ot=>Æ6?@W;FGZyڭ>7RsL jh; Q`#, Gq_{"TDTaγ]s͑?did<L F$xg7eWk]5o$8nU.}[MaRUڧ_>4xv8ݳdL'/88[~_rbIW]xL9\I/$ 7w) Y]FN^VrGjt$7kziC(~V ej<1 gor=k6o*Q &w98/<6u>cXnB+D ^K*isXvC(Ps~gM3Hf.;%lX~3d|gBbNZZ45bT&u·q{r^Pd}f0h@keߩIޚZ(Z6 XpZ\z;69I\01b-yiqmMɠx՜eBM港2B|;\ <σ~,:^J7dK|GXpLI8/n!\ I' R)@THt`f*nFdžAv=lʋ#wQ[糽/#fKzH=)!^DKt)k)| Wl%:=s y"#dD<('?9A%JhXO=ᒎ I'L)^"j]]Pxcn j#$Y#z;atUrYOjc\LB©$SATMd}M8N1M9 Ք Vp΀8YJ-"&MYw_y2=?u4XL gUk lz$Co8`8Ԅ-A(tB?YPLk5G>h@=3to\Cd>U[ty%'F=)-8Z][4[ vv6YΚ^i@-]Ͽin,L~.n_6j-Yh' =N0+Wvk}:w@y8|~7?Kͩք9׋@ʢaHOMAȹ7 dXAaW|xoBh\ӡܱ~ǀc՚{ P~GGeT<'zץS1zn FPBm>h:42(zpipkZoJȺB([Dgbno :\,j" ֒U1uD7 `AfsZ?V].[D%?ٝw="-늀]rk!nRPi@iHDys5Tq,;k;iX.;Sǝk.޶Qx8]52Džk[믥`n;)f'p(-jfU,w5W ?=w~6*m]̌ #ܽȸS]̑pP7-^ͥ &8:CYw,uQ2+b T}XBQ'4'8@p6;hrAT\[vOOA<m鿒x D0i^d_.$R,WZHi[cRͻ)kxE ۹e"` '$َQʂ9t~@ͬ&z5CM xlu:RYR#h %^񔑀Yc)iL]o&媆 P>œK|fFв6]t .](r}9\B#XGJgWu^m`EiIHs ?ZV(F-^1OAY[8~o˾`e_w^PŖ7+LCiWyYf)ձc-f|3>USKݖ ?2jêl0'-X["]{jY3h€:}zaǎ̸uߑilod"{ H,z@xA7k\ %D7W.88HqC5qی6FwAXQ5tD#yj\\~:zMzb\!֨ۑ\4$%\j@̸F;2F g,zz^ZV3tIfb<8/ʴyM)lRoѾ]aΜ( m|pN 2zV&ԒCZ/CK ]V|ed^%`܅5!CWහ &n8؃h]n& W7ICB9VxOQ}A`.%[/Li#Φut%kksZ(3\̺J/|ʷ~)s&'ڢI4AxX_L8N~NHh&4M1`=WdgiQJKQ~(kU8!άgz|n/|uCȨ(6Z`̦cs)ҹCE"tuߔ~ dַ>.`] LRS#!`W֌q/y$<zϓ>?{g1izr ՇZ:m\SЊt)iBɜ+\m-JU%B\emDj6ך~hh巄!as}\1ݩsReEqԗYP@ڵ^'p˾*wj-;{E}X׃ )X"b84bCؠG{*x֝ΰtwyӂjj&&ߊ܋RWȪbaOB#$S؇plȳUm%n]ر$Ι<G̔^sYQu5:Q\Jy)Cxьj7~+=ڦ.u/iD@I֏G8Si,472FMx:Tne}*jEwfI~b8ió˛~*FiJJ"iJ/蓜l(Qbٻ<ʟar<]#/Aa"=6胹<РgQ 'rP(dI{f&Z3'5Y6Ď٥htJd߹ (T!hא2^L@HE-%;^{OQq5_/ٿ@\7di%z;8۾Ґ( [ncӒ,ڀy=w9ѷRgD *%x\@K"[%pK32 4ZmA(SxW )h*c]rEZ'6zv1C⧷RN3kIM4! [(w ̢$l E%?ip]HZ3a C%y/S5,X]B5Bj~wcÝ^X\J̺̹p{kfLG/oL ;4gyk'va1Uڽ9z}v_B|J7?V#MzU!Ht~82Tꊭ<˃D[js'%jTW{d<09E{UCrShOI]!{YCW6 "#~5YJz˨cs*!QX=4XM -.Ȯ6,{tk$4ISOc8TEp<" Uvމb2G&j*:9pvARf.8M4 o)Z4ۤ/+d=R^_d) e^&HB)p@L#f5VJ!&2m3%,GO!}_E_wD61!mrXGpd_s;m?Cԓq֮!u[-~'.;@X(ᆲvXƊ%TaujI OG$ӷOiv_A#'">'שͫ%8} Țlc6e.x*s\Yȕ_嵼!Ro8\1,Ђ]v J X _YVA)D¦C  ߚ1aS_ܐkPg^lp r3"6:?+^V*g6X̴ex!PH' {'*Zh=ק`kMY\ȿ!̎Mp/-%l3Α7i~o+h6W٪ac^x]{3{˝Ο9~, V.NόiV!b1|]BKVim'Y̚ [`H !66k-xY4l`Zu r-~\ 83d}@+ڑm1 ȌlZt.0Z|)mTt쑻 GH+ZdK OK9d͏WxW *===8?HƘZQW7ނyU٘T`iƹ0G!x9FX;ޟRE=!Xnr42OOYx #~AMcFbϮOf*7veTZY#\#>m_/ЁO`4'i=i]RѦ7wXB߀=ȒgiyPnI &#kpbXgzwxl: nѷ]r7~{P:%ʌ _F? &rmO$T Q@oǁ' >-F;v&"r'~6;OD?ˆZ˹ަӗQ( Ԥ1*Z-z#ovīYܔ3h/N;}cšy6þwEqɺxySz dM{m!w+ #W "-/A7U'Տ󬅔SeVʻ9]!x$e{M :ȉS&?3EZ)@;"/1(?PQ08R^ګt,~ փDJ٘CӟKU)T+揲ns9|m -$uE~83RSI5Rg 2= Z2E)1%FvdX҆^'GI\/LuVYugaWK2ck@Qfs \[kj=:d! !APH ;Oxo#w¾I~I{L{>֞#_ ~ =tBuݸFhDKAsu|HuKSI/ DGX˻u+%H,x^qO ͛W}ًgkZ<-dTv7/9ʣg942q :57<?VAH,a74 [w!"2&ZcQ.m9+6S,BET$2TuUa *W?lm=Pfn>D͑N{gciPVoտvt"CO֘onIj+Rߓ@IԉZq/qOtdS]LΦǁ!},vMy^T{sfH]T9*t; BT8>DI)QKVtPrcb}w 6B(OpaQ`~k}T>Fg}R;P;%w~ƸlFBv4بuC{'J|F 88#~|w; j9|VS(oJ.~}b)\b%c/k9yp譩7 0|b|v khH5В-b޵(2GGz,X* "Ͷ$8:i1k<_K@RJI ~^6^ԾYT! UIN Q.bV}jL_$}h=$JpU#Cl튑m)8t & 4 :ϩ,E h 'ScB.?Y8=8$Yђ=JۆZ.P厐۶_#G`k,TݟrMtmڑ[E&x-6&7%@o:Bfo}ebMO뒀f]NBy<0+CuRR3TҠyn{5SuI;is_PcE{ۂ* e;}DN 3~Keo)P&׿gӜgej+䞔5EFh}'X61x*(z;U:#2Ԭ>)_YPS^k1:Մ,.F^KܴA @N? x'W-L 6ٱ^x@ ŁٙM]oɒf`/$ P,|6i"1q*n6c*&^rE אbMqaMՌ4+2PG7gP!(iEJʩ"[^w,[ɼrŊz0%ѻ7+onnC+ fhv"Wrِ)8OU;j ѯtdsaSz}X/o߸~\^` QCםj,NK.:Ԭ>g=䓗:=dQNۉ%jJ3kĔX**!(9}Ϻ(&W~@ :ȱ7}:r>q )1{̇{3%,9`ia/y=ʀTR&Iiҧ˲qb1'xɅP-ͯ4 &72dAoAŗ>+o_Z[}Gl z.ML8Ҿ3@=pU_xtS?5[/`*2Ѭ!{S2QZ>1c_/rHE _yyqðjZo} BY>RoF?rXZ@"822+qLhKX8uӎXx9oy9f GsKz{}Л?b]1Ulڣ6PtO `Nϒf[g%KFPcíV=ltb͗{6XۡmQqd%J=YgU)n tj' 0T`jo/qme}^6V`(><}g4xeBQUwD*x ,5~s@(&.]௞wu۠mhST1 mdB׊YmVX.ΝugW=ץu1`cž/rߞ5P'[U5;n*GZsˁ;76ѡ} ΄<(+.jDs(,4IIe1=g| 8]{?clF̈ik MH{T@g S"8^ 7v+9Kz_P. g4Pv<Q!aM&? -yb~-&yWupP&-`NƬhT.bz˛Ai8l2Vی,a35qL#c!39qJY]IhɿWE6A?)Ni!1u&LJ2#ehC{XcMl'I[ &1؅Q,Vc)yYHSuO&h%AIubm5/!D^?fdibJoTꛜ u"akXA}6k^/)y8%?>z:Fn1zv[!{VE]vLOكʅ#p4J[BJ}uoQLAii3ߦ6'ú$[Q Bט|;w΍ڡ䣆JXIw )ÂF2~i -dsyD*wmq+ ˌ $L>e|IQ˻i<7aT+‡B x1{fuclͯ !S>ņ4DU$YiQ:~H[_9u!G~Nj\(Oxxq^4/`nc% =!x#Ǐ%#}Fp $%dZrUiž)tIBM7TzFߧ=4(/l oMnu_$=:)STR]ic 㠾"ҿOgtFo[xXw.kʎ?ⶋͅtOg˾{s}>J̀]/qo{]do j*A?>YYjAʕ1ۥʖ"-}$x- vmŒk{3(wU/@5iZkfTq"iߠMRdSdYnoOoGjOxѻc*U٠;#iYR:Nrbٰ樲)!Dc 0e0},'^|-7*K6^S]كNQ{A\e#EfcB߲FOb؁Yto.Z,o$Y;69SVV%"XbDP>c5)ʪRWwTxH0$'?\+M|Hg_8Цb4Fݰ>}VD~Cx8(9_-%Yx_nQىnYY WxTmkIQяtJ6r./NXdcyό ;TPp:n.(P5&"49e@JpT~g x35[oOvcF zQtϳw[-\QbDDTkP[2xp^ ZcVi?D:JP6 윈&r1* !dQOu\Vwʀ;U(Y9DAYzv36 S]VJF&X g2*5l,@ϥ(ԫ7bRɝ4Xax'zk>(A P)([a\`0ԅL#&Fqܹdxf)\DI u] Yb*IP r%,R.}bJ̰bX6=A4~ bZ*,H> x`:pIKYk: bε'׌\~&s!~/;IS'ZckíAwv_t^RI0m5ZNPTw7ػ(j:vAG'}K!x%e MɄJ^; w]h$'sc)Ue T=.,6w9p"XW-7Jd.ܩQdk>W}gO{NȲvaZ)uAkpˏ0S:%LB6€TEHF(lt-šH`f`.jlz9XxHi~:_iB%D)t^$`RS(jl<-V%(/L)]m2^-f]vSi-fa tࡔ4qļcm7J#2,oiҏq}T  {Ƒ hİ^W-9GAw S%?qЧ:ɏBD`HPGq,z^aTzZ/:҂E}Sl-;ǝZ F2KE-Gf:Xݨ ?o/ ) 04%VseZAq9QVÈ9 Y_C Y㢅+PLA@oJ'A[%bӯ腓̚&ب>K"lBF Ei.xEeZ|G-9ȁ(ȃckl$nWIɕB+3Pkˢ]FfyG~:f<>亏 }-?4׸mƹjf=rӔ_]t~FGm};f:-Y{n_ugPL]7@33j:`~ߢy ~.vt+WրU:#lTЧB"%8Rifyz{oNc^oniuD%2mwv J͟ceU4CO%ci*S"=3fY0_@bG܇[ /{/uH;cu۳:F*b,# " k*[Vs fYOJ'x֧d^AʪU;RMK#ZBBɅ{=9#aPFƯl~k:o%Mo?b0Bvz[K'ZbL 2 ގ?FrԈ!f _i{/8PzQXYċ͢~ >š#oϵQk2)ApR_(K!qVWV-`$NWL2$ <\t0pWOXVvWh!gyO AvJU,Y_*29^x:ή#壦_G]գB?VrT\]W|˥{;;ARUfQ/>G7µXm洪{%%^Vù?28ɞ)p是n#,:O{H]3)p10bA-ז+6|_R.V F ZdzSvKUg^B2HNFN^3 -ܴ{ՑRF޲=WBCRp荔zӰȎB\q tUHb^5}C)UPQ`]f8>,Q~_ w妣'+S 0QO blS<5;Ԕߨi(> cgDPis4^+4e*Z?{YJ%F:2GؙҏUႲ9~~XFW|Co@B}#4d̻n;&'uuc&j<:Sӧ (2G^T@ O?ՙ4׸JS_wc\9N=s(KKVFHZq|fWFt=\#D͕fGQ J[96<'vÝ[rMDzTRQĮ6x90w__Q/W? q2Fo@`?,ڣghƈ9zM9t?X`&ɿ lr3& '˺*ڤC +5띑2S ,%=KN)"w{벳FdpxCp>{EQ\ c,1^&%/)7Mӌ}T+/ė&SkQL;NEڲ4? gۗPBfޒ &)MbH wPr әw<>,*>qkK'HsOsI³`g]}."_gf,5YCTiu Q&5O| sߔ w~񻓄kߜ:e,ai[|39D yDen?X{6_s ѷ_}Ō$ AoԻ6 tsƚBK 1O!DǗ(6,P.I { 3FFi5B ǜ>RkkA:ۺ9z멒нLUV=%\tUtybx3Z}QJ, 4ӛ[.U~n W9QFVSB?lsHn\4<4ҖwKQ 8Qc ш%XpԞINX>l`|&@@S#ɾSM$Ǻ{hPX#%fw-/}@:zfz_vZwY,` j8ӈ5Bl("\ h8+ d(dZyjo*(y^ǹ:Һ 1j6l? G0 R:Awcs'-ٛ'Y`t"UuFOl&"U?d}D.SʎJ .`&1[ޤ4Cf1 %>o`(|8DyRĘ5Zo5;TQ脦E^~B/vl/ˊFCچ>vo Sv xN(UYڐa{])]b,*r2KTdqO#oW4^[3s=&xMQC%:0jaTN-0ֺ5#AwЅREo塊OBػ:GN^:FĻ:&V3wqL3+ҩ쟕*d6i]8zX{Ƈix RGG J:kHny1 VL$l%p""!boBoofoKp(yV_?x=B| 4Rwi$j _(r(ײ7V׬ϔ@g]r;X.BnF(,A7D8o<АtܝY;G*s(:jՉ @8.gB@q;#(|`njxr)ED画o<{ P w9lK[۰ql}{(fi,oJU^BFŚǻWa$Q59唀YUq )F ˆxWpw)dNπGE9v}^q|ßqer\v:\\YBߐ,lkSjiSC=bW%+yUs ~mEU֎U p uIk;)Pa>-AU1&n 2|3#.~+f8ʳ#  WSYekt|eqr7Sr g&jcf.{+|!]ЍcVDm%ihcD6Ԏw"H@_kd@Z>ўPNvAKOW8 K3"_{1gweTָIlji]p{kA`/oX+- _ǔBuX'2+1r®1GN׀-/Ćhٟ~|&]եʧ a,@ 6,E`@B܄R)Ev<ݐhaa썩ǥRY :|ps,?2,pgG lct-{-Hw-TiYomr15|zSGյ9<8"@(BsɄKe1nXx(mFor;SIx٫ .i0̶A$ݢ :@2ɌC砞`Ja:nUymo}Wfs^:^C)pcq5) +O|_);yߩ>Dv+' 1">pyEb3Sn޼_c1S{t{0Ħ.G[wȼÛМzggKͬy:1T7{K}<”!X(\\io-\\b-?2v1̾ȒC߰URU",ӫcE9}F-y%|cyX: /~>47%gwp\?_ˊ~dNuVhI38T^=O} 'SeYn MӻBd dƍ:-_C$/ӔWpwӳN\w'Sy#譫y5{6'hΛovزۙ!q%\P=Ir(KRc5Vʼ8S0ͬ#?+waf$ķE6\E jw la"2ؙ뫭܀\-'t'J41YXCQvDZC$y&Ba!T50Ћ|ӤH TݐyWCwݙ< Jb*l,6ntWSMB6Um{8~]Kk:S9&^*OEI|aSIۈH%*>As>Jg5ՠ7FG]s>dڻk6Rd9BY8~{acci܈g`GVԶ?ͻDC|IO7QvEv(/ߎ ! a%`r1a({~JǼ,nFeTe+c}qAc*˨u>8s88c./q|AK9 nWܩgfvÅ2 +o`/Fe;Y> w^>*BiV}IAJf:T,c:RaE{@#Lc)\hyVv4xC\#v< 5g.Q(2ze* 18x+UC$`!OJLwV/܊K<{=%yJթor&v3y.s笋B{EkQlٝR+*\@wfRIAA$ʦa:Zk_ #E,\>f')>O2|TY2ծJsUm }#B/)S8W $%(OI39P&VZgmrk*M'܆y[dZ˂/*D%nOb"q& 93;euN'U KSk>8uxBo+l?S*=oyl aq%T|&`8|[`]bXjV ~tTLzŊWw~}f*7Bc+ʫˏ,PkG|Lkǡ][n avlE7yWqF'(ߙx ,f k1T$+3tZe:a?SׁM%Nsk?2!9t!>и=G~1[;.kfߍGm }=NG߿crb{^q]zr"Xhɞo-Bʻޝ+eh? MbTnGh^zFM5xTk 䫳*8zX,9WQcwA5D9gʩI^,ڿR?lV*kuFOB գ%bA݂ԡ@0H찵@2?@%*p4S JɠynܤDDyTX+%+[W>.߱V"VUnWGu!b9:X;vm_ MdSԔ(F%w}ݡqxRM Z2ʦIm; !>O|.pI ܯ7a(uӬqٖU% |pqرђ d&` gc}%۩RALc#}}mIYhIbA5 ϋPN RDe=ܯ& y3y߼ t(br9nGmcBwըj6f}g F[ʿM(T/ofuepȉzf2!}a }x*x[g~N&s)g;%OWQq:B@5k:E;nD&SC=20bS%}o)쟥DgYP%3do <foMW(SޠQ UbCboCb7nTa#[%@tqdLHÀكiKp-mX0<ǵJ%Y} /|#%4 Ae"t"'պN cS @kCB)RCd%d$Y J7Pw'%EBm#~'q<8р[IՅ}cդO:uB25šc|q?4/%2UsdG^ވζ+ScD-4|-9XzCm }HOI?չSRV"HoܾFjcJř"LّiΜKYL̼5oK?$ajpXY(nY}f:p\o 'X+: Z;g gNg % $ʢXMDo&V c3#xTcO6O`vyPPC>o$g;e$&zߜ 7g*H6z _<*ª+IVsM[7fc]]Y׿en̆)L§~mn&"ͺ>mH$2ȥ[hAe!f)ꟆVxFYdY恑Z]k,4Ӿob$ijAĢFBGMOPߗw}bԱHR|Q8.cխvB(YBqD.:͠v"hL*kB"ՖJ AL7Eft[@5{v)NՇ;N D L!Z>Lq]ߏFB ,lT:ω Uy@Z[.3<<п&+sXtx[(C1BqH֤£ܘ܄c$אcގ~΀'W"Q=鸰?WGGmY6=tP w[&=QjeC9&c `тkĒbASiANTm[d̀ <3$ *-2(cq̊᪀SIhr+b`s6KYW6#0R h@JQQȁra7T]{Ʋ l'9v:\ղ"AzԬ0Թv<*p?VeeO.40_9o!v `аpVbX4vm"-c˶5gVjR1Q?s̍*I q KiЩ҈ GB&7hGSG(H^h 0\E }:!%;HAYd]O+4"b5i~ƒT8*x:!3g&N?unLCq1QMc_ڹ=shͮrE!Id:C~bB;yZ4Ý]^ JHLefRT4{.iol]Tp= dMiZnOOY,!¯!g4hhcjk\ 9ٳcIuHQ7OhE/!D(ꟶRq&ܪ8p85@+CNr,wW-YS=?GrM,G&ίl t\|OZ%3aXKǦ$Z.Ek2f_<ё#y3.M4^A'H[BiV΃!.o1=BMj.oꁳ6퐠䮙.LhtdjnzOr_8L,̞59=V?}F 9JgX -(m֎l}͉ʫ I#V+oxT6 N*ꏅ $)Kɖ.T1eLSߌHIIJ5ji{-ש4A}U,:r*+Qe/*3<~lyW.piI|aIn'abɗ&t@YTe%S1OZng6 NjR梵 (z„v&f)ňXSQ L/~U=sf!?= g’[x Wny^("Adn&m]/m:*9rQ3(E+<:CXhi 9x'%p/t!ٶ;Z _%V҉ZϪel .iRM9(hISHXʹW.<_m["0_tUU!c#8`^SsuۊQ}`*.% V ^hPEb "۪do×B77/'POזQ!C?oŠwK46>:[~Hn9(#,v;S_JQT_s4t[5UqőLթ,S~on-Sv8,&8ftƈ /eF嬑ޫ._/I 6HPsk)w ~ Yp;x!-T+" WFA:^1?V\$=R˟5M\Ѧ/.AUAtK1"sZ߻epŗŲٯ gf3nxƷ _s4=tNxyB!!0DE\wXAߧ ({%\p*))< P{H1(&η ?c ?~lfEj#̑^5i $]Sx16Idnm6NGn,6G6S_5 sCyqFeDo7W4*`0"(`l )!cBV糑6;FN;˶ 5LJs@'#)gͣՉX$|/ .SvjWIʓLr zP2}?q#Β#::1e3 ɕñ64n3śoӧ@f8Ӈ{*J ޔYtc|;tg)ڠUbm>n40/$Bdχa~*(d4h/\-*=w-F<@#%ʽa6yӷo=ɲsԮbe0*rGxōv)t$?X. )nu, f&˷ _`)H? :aл ^jʘZ R }jYcQTYFF"&qry}8 p qP%j ;~ d t"⤰[pjM2{uj ?x iBVNļ+<9A4/X#LG&C/\Gf g+{PBJ%z8C wKv<k72CCm0"En1t@!4*:!pUYS^x%8x@ ׿W6q. Q1ʴNߡ{4xlÈ|XH6雠_TR-P9fʥL 3} EYž˶5dfK*a~Zi2~c(}(b,2L@lDvgx:%Q6֖BuXj^L5\4_`7zl4zJ׹-yVA֒bSɐ;]eƹqrPoj߾ Ή˪G.EZ(lZ8$HHWy"Q,'ǀsS:ivGtq"E .adNܤh|,¥0e};R3O-r۰«|fRll1:pL*N;zl,1/#b <=ۏ[ׇ?](4Cn[U4NvZS0&lBUu3zRW0Əe @%mp#/!8+*X[]I K>bgjԀ}ޘWpC|a8m@ ]k%=dēHn) 0<:5s>XGn[wAaл ZTxe]+@ $o>9i84uu2'=PPu1—rhw`$c548Ap@N_woB͠l{>`*11.bx;YvVsR:Ͽi|07ifL_ vt?jťjQӽPW6ZP5&c9W1o˫ xᱳp ÅlzS~|HD׮2*в0XKצ#t D$xQP>|igV(W'x8?$ p@),}KΗ~=rON~`XWR&m/+f9*9O?1.1@;ԠmDJ ʈNS7:ZRE=&v`bۋӰ*8 }Mb>fKG{-&ԄE* 7V=Px6b`m?{y+JpO# S j嶍#1fz[^K1 ^g_舎?,܆< ;yk9^ !<u+&$Pg1Hcs] Fvo$t]}1fY] ]꫋bx0imjQC\ j$ Exd7} ЬB|anun&Id0<~6CCfZ_[0)̗5>69b.zP]Yc]"iVLUӮA*2eĻTͪ V H.s lb-M%6 v -rI=_/`=:<}f9ECK@CD[1BV7Tr}uxu(pH L=&$2drI.J@/`V̯{?Y{ ?vkOsSMa-t="v٬' k Px|Nc/=9Zk!' ¨5V.:SXC:f'%ګ94ԏ mH$ϓa{&yUhAd\kRO@iڐ0'q149[2 {$Fq,cSEH-:yphl7ظbrK'L2ODJBBT #(BGT]|USdwqzǝ\Q3E5K7HH@aOId6] F8+c: b|%NapJ5.n}k\)1uW2!jvѨk%'*(>,Wǣ7Pե> p}#.+$r٭1nW;2T4WQ7畹ÃJ"ЙbPd~#4-"GtOdSB'|-Æ<6蓌 gAoh|λ[uew]d6U8Gܑ-J\ A|yt,kBh[e0}=*+X_8vsɈZDUH𘛀 YzeVm!8D'45祑v4h:5q{7!TϘ&l":!K Ia]R ߢ)[(u D-|jOhpB6' R$v@IXvUZWZ 6NƇr w@д#*˯urɱ Kqɴv0=k+)Q ^҃EJQ)K֪Ǝ&$04?3, [ :PIOɘ( u+Kڅӽ >o>L5n?G`Pnx6*sWjEcv^Z<ƀ<Ee 0Hav5Z«ikQRrvSnO9m\-v0.!qk^=+!(tԆS\iYt7Iǝ;N!Yݨ Oiۘ3Sqq2@z5R2Xv^5 K90 #L<<Q'֗ g%E' :9cz;\ ɷ[6J֯lr/bhLZk33MM3x8,Or o*Rj%Z5_ eއZ"]s4o,ߨeG<LR Xs{%x)B$К]!Û7 M,,SZ,+Άhkc;?&ieu0 Bs1S.g(jqH12K`n<ҭ Zщ fUb!e/z* U4SᜃȜvakW?Sש[۔8Iuß,a-c=2݌XsC{ %WP̱ |h"{jGmZ!ȓS uaS!$08whYoxg1$JU?"r7a~s6;%RV˟ @~H{5J1|Cu1b24@d҂q2E6*E1!ą]-5Tpfh+*æ;6xS^p0$i^ވm$} L|<~ĵBDd Cs  aM}uKNJΫ};+8v䉢+;b5{ 挻i?]=8~ Pá ^!Dд C(K^\1Io?=lv4 ad4Ro#/>ca|*,Bk]-_jOOJl Bow; ^ 8sxz_1(;Ns"kךVMSKM M-_,.xإ.q4˜9-y(ODz!h˿bB湝ۻ#7c,tGBO#Y ÅSYkNGQNJ퇪9[4Q;G`KD9lW,1$'iw[ѱ0m)eR)mؼO cl*U*;R+".3WeRN(^#gtmݲ] G]&LjeEsDZh2Bly4Qwhv?s{p?VD)A8ӕ.Pҵ{⧕j~oLQOtG5P?^TG82%"KuZZϙ)X' p_-pts=5Lftahm^TExt n}4X0ʯq>Z~Dn *1)U=+`M'Nh9k)v#MR'Pl52f2''2 .*c{|Lp@YjtVfuTڦ]UeN];OOMw[޹a^9Iө<b(/V\Ǎ\()wA"[^ v$^JB\pF-ָoPnu;sᲺq=Q;\ Sqx$S5ńFz|1)-U!cJ. 6[y $3vs?K'" SLZo[pB!rHQlɨsoS!%7ޚL$ ׇqq-#=nl'_Xum%Ξx'k)MsJtR*yI9_dT>4d%\LgˊM/q{L%VCɄQTdp R֍%9ʞZ%mXڱKhTjش&C{ؠ2+cÉA%ёBSIma?]ш hvz7@rG-b':EJC@Yq\֘&%3b^<#cUW,`|_cnv-MGɟ~r0PRV:Խ] Ջ! XbziE`E3n-ĽÝm Be+Ea}]-8XIb?_J+9AS?;,:6]F&R['RuIn͉4.DH.x ,{+A`9̏~fMԚk$!| , =5}Z8~`:yqP`ɓs X vB!;$_+ÒvŒ:_'{tQzkI}һLo |_h,*f.4Y;ߝ s@@rtpy O`bWr jc1뗎9喇SC~e w7N;3Nc8IbB]B5x OR 铦8tg. 1%J~`=" GhT/t;`]?Xxk~2S넉I+?A^O:KS.ay0%Z<-Fq~δƞ8+SN(pg ,)F  JAKÔLQ4 o&C`fHiG ?]9?<2kb ~)*'gҩSЖ҉vL2Y'peQ>%A݀SnOժ7&`g(?&Ew;GJUN[6$Ds~܃}U<i~"mӤ=KaH&(6 NԄp~eĴ6W]Rб[s9 |(*VK*q;PR6ʪgxȷ/3) Xr,Y|+Gkػ-=,(p0")|`uKpmG#3HX7湩wO9?3zSRÂ7Bpr)몍]G(+\S%WeEu\;ejo.'PJuZ~׶ [ ]B: 7cWGRcA9Stn:s(v](]7j|3w>PC7%]bʨ݊I4h0z\ɓF;l٘\8ϏUǼY8eHw1H[en k%`)R;ۍ}|Mm~emS.ψϟsj#5 !/"cZva5^.Ų,+'6c-|HD/89Ax,WI*e[R@Fބ u} jʹ+-ԑ#dG|Z,pdOA?'3ExDa?x_Sı.=br1/7~&C';,KL22ԘoJAf X/D҆:@P<ػL.[ucJ69nIJ Cb?#˭ Xޞ*C6v 5lG7MXDxµRd10{CݚODCm_>y):-mEݩ!kPm񗶲V:ЮbnK{. mi.an r*L{x^ P3iQam1w-Oz7j9K備;ލjXWțP{Z EeT?si(k /[@gaKtsXwŶ1ͪR; N{\ %D[E!nJ`/_2ș1511'f0WԴUW;YqGMHqUQGB\ֵQVXcvEZk.jV<`y0 W%1Cxe-faaf=$__p@2zwqT6v)Pl{F1BYhiڥ.eMSph*+2Ţ=UsH5vk -чYQ}(jXhXMnMM̌`'Rj7H_;7)n <D~' 5&֦Uߎxd_`D% >=kn<*  kZKFO^^||krh`!q؅s UoE[{)40:e/5l 1!H5X7P?p=̖F %$ڟztSTBɥG9rۊV-Lӳmzl5dc֦":>J un~MhX?0e--QHyeo_!ߗ d$lė]nXC{J+]M0 Z^A-7z=,GߓM LM߳EI*ՎBG=(-F`NĺulKDQX[ª Г+Ge>' ?8L {㰶8r3Ý1D_K/ $lWoeB^ Sw yvbc/5oiY;OGBjY&4Gfs ߅9$E0ѺRۢנW.&*l(E@ah'q! IJJ` >zV'޿!NIiJY1R 0 1pM+3 SY6Fp](CZ\IDL3AFNR]mf5 Mj n}g4o>2̦ FѶS̜$3%Fy՘n"Jnjؾ{ "ʁc8a:,}Z.}',쏹1յyl'dd3"VwQ ﻯ E{ie`)ElsO*wAg+ t?v,$w&_eLl8;I!000en1Pϕh+EJrg"I9WxzF&n#Ae@3u%oZ6ѳ -ejޑ9,b̂%oeHԓWB5 ,)g,sv[?z8jc'wĔԏ0\vφh[ㅗMPtW͉^=I%pӵI.5^l.Qk)w(c&U"lwhs'Ea\?"T2^TX7N|2R["3ND&o&=e;Kx׬\&{lS tY_@]OX8(!d ~Q98$H'B;\Jl -+LJVKmjsE&C8H6!s/6O1q> V!FXtuw!ŵJٝG2wAbxgr"Yc$@ˑ[ d34-Y?de2wĘ/z:\;02^h@ӆ}UXդp#M/6b-] 6W9c%[sb9sSwHf(oqq4G!{KͻX"[K"B wڣZ*Fx> p`c-w%Ci'߹"K,UiﲚJl&KQD"2{ s @4?~IBwܯIuKI>X.>e{!P&,:SEۯ@pc`ڶq悳.NWApnDz2h7ݑ<9mB+7!Ue&һBB]?˽#L[ݠcy?6v[WhcgiXLw AّBl<|zb;wH?n0x9?"$R,iU#~‹[xveJ*H֏!c)=2PCPp Gc`H]$~/ IQ=G1.Yo 7h=dx哳j)eHk9N-\Kq9kf^El$5uߧi&߶u^ڌy%Gԃ_?Cٮn*oe8?bu$6ۛا' -\9Rφ]KR_{ε8}^^RBw!0VqaX,* ַ#Hw{Ũ4lyH#Ռ_C=޴՗.H)6SCH-w=Q:\!F"  P) |;=4YQø. =9AkMop۰CvNH[ /2=vaxd(7Grrq5|U uir&MkZi+r%I ^C4fZjq!zlHM3`m͍A'_S*n[Lk%ZOZk'TGL^0Er"벮ħ5$ l$2ֽUڌ)5xC–ʖA{y]>vRֹ6[  ."=Gǂ- jmXEUFvAOcA1?"x5pCZQeM+L/Jgx=I@A?꺷{UDja?{ѓ+UYgXg2Ud kC '",!yJhD~nZ5Aq TwG`ͧLGO+XbhBۏ&RYO ;Y sﬨ_3+9?.?Evjv qm,عU׳~0 5(ͨ:~G'2i }wI9:: K{FvsXX*^W}L*qm2߈/^ ɷh%~)R;sBP, n`g۵'U-SɪӘtFѳ׽xg!?HA{M JKjLw lŒI"Ρ㊎I|rϢtKGmĜ$T?W+ѽ{Y\zr&zTὢ].TH%S kvW  ߎbӟAKxB4bO*k<)vmEr՜ zp y|V| C| |L0rnt{]Үp̀V;A\Rq%k2 -RS}@0b+NN]߇ ga<&?5qP[^Mك~ދ|IJԸIs Dero"{Ӛ'YmىLA>HdʵWفW6|;h+MQig- ےVIzs:9}lpd ?{}Ungb_%2ϐ(x`s4$]-Jeii^Z:~fAVȅK056KS +r@h(oɤ6BS8H23f8uVAGjsIWKJaBR)=8} 0ِ>Dp0&ob^81jYZP_5j?GŁ>tOup(\&_l=:4 75X˃;db]rCe%| W)BJ IjF()>\h'·Ba.מڡ+<]@6#ϧk;8k=.N#RDˇL8Pz(3ljWءsJj@d!dP?L&R"œ,H1sֲ2`܃Չl~KE!0F EȂt%^IDt _hz|~ד8LyB b/gz·\}+ T]>%?%B)Wi0UrUG_})[0ɿ’>YKxP\ ן,`θI5%./ߒzыV<S0g[ K>- yBrb8&ㅬΤ`DhJ% v r1bчd ea1Wb}t[Jes),[?AKŠ]d.;~ֈׂOB|b m $pz9zpteEلn&i+wvn9=>1f)yY't?ClA /ǟ2A!Q\%Fy^_J'P9cICmP%{psc~jKƍCKXT׭9p'Sѐ~PFKt b4uI cUN4 cI^QD<;qK4gD1)7\RP~M h`6HttH[JuT @bMj%M:RcVbݜ9':4PO3xژ>$ lقR՛yF?^\08*h$b7F]@<<0 aǷ V]um]L毋pg`Sc1rC>eҹmgPWfoTXCmI k2NF8d^+'Ͻhlu2 & x;M %XiQO)\_x#9I'pkL9x^ RD TQ$GMl{Zz9Pt(jٵT@۷iBI:g9['wD;QEI."lynLxwu#.~v,Ŵa9Xk.;l '*h}ݏy]#gą+mg`>>x h.P EHTBjxV(i7{B 85hsՂٛm`Dֹ3̱Spi΃?Po $=Ssn/MB""FXrT݃XY(ڀgo hg=DVbO.1 +ꎫӥV ϊȥD4.EDzxdcAW"KHhtMYehq]Q5yrО7 2yG"EfMp"a)^ *Q}6^)\t|/?Z` ҅Hv}az'n,z]P iBжcG=_9 oO4!TOzHlAn1j,8,?yzT㼣KZ3U8` o꜌ ˘DФ9)GLN({fEq]Ok6/TEpt_T@և/@"A=m(tpE晐HU!,tUqcԔɷ:{kȧPzp`ӊLVXNi7g>o2˾gؐZwx_=|}ҒYVg`i0Gn+pp iPG8v R81w$${cQgqiB&#g̘ ۟_s )vC|Il)Us%vE# ~sqNt X>N,G `&$% Ü|Qh{%vV`qZGHNXYq23_{~h~V[舴a:4yI")A~a*a3׻^$#* Ƒ (@Dqy aϴ rYeC%S됳n֒ Hj$e=(Q,i2x]߅o?18ή KF%gSÏduQR]Vs 8rEOb9Tzc =ȫ(*&{3~@2{IFh\ 9*B^)bOZ7ly[$rPJ'rhVAYeƳAz;>@C{CV|4EUTgq)L`:לa;_(Y.5(V~ʸ.OkAxfxYZcA8OW1dmz.3!Ie!!>ۉ1TgM;72 kl(s-Lnt;o4]E/ɔ$Aڶy~ҙw2HxNNj@xt+:^GKϯ"lhۘ $ahzoItP&΄f]1{inlwMcsC9.Hw,C`ygh/.2=q_aerM%$%Wldc7%E2?2eiHr1EZJ(8ˍ-inLik4#ęYT}"]1ᣲ2p3GgRl^M9ZX硄C(~4o^[-Iz^'>6'W$366{Ki,cnaKUhfePM"?,덺y^i  }eJՂ:$ 4wsY62gi5AՍof槾FP@FAKvϝKhvrmk߰,=uf! 0Ԫ|utȇQK%\'KJ◨X~LZZ>1:+(il\vX˜MH"J׵_Krd 6.1U0_ucPh*ہh ח]f?ⵀ$U,9ŽB ogr`1: x!]^WD_]"*NY `[[EIo(11c/AT[WtvDهΑQ~R̲$XCWelύkX`I6-ot$IV:+#3`tԱ;u*8lLpY^|UUE7T%Sh%ͨb8{_;dC~Jp-;"a2\1m gevV(o _ZoUv !ԅШkY_-\V|&1Wz?a;M;DqNn(pWPsNa*TɮOŕ*]MB8h&5ٷ4g#W|/qΈI\E¥U'b UtPGkKpt 3CRZ8Nbl3e4'50Vβ+@K=͌y{O.H)K9Fm1ʿޝ$-WR4Zд5Qh;oEgs} Z"4=q7L$Ϸq &/$#βK] 6-U*<8h6be 1P;ՃQb[VzmpH3Agurv>8@7?☃[nlqO'3d:d4[.;6O?šv<e%{sW1Z:n 9D!@OM5$rĈܚU; x_N_&ܧ;LX&fGJ֌:py8W['FZ轤NJ WFr薮,#{/f&=bmr4pbvh v>xAfg/H{ r4镜լTR5+1z0{{ox8"ٱDQ}rp5*O1! \Pak6G 8e8]!׏}<Ҍ kO9r`^l`0i+<ٷ1&9}6}-BM5%8@cX7DGH[>#§6\,]˔؂! ˏ cJNd$3HTxEh!%RS2 G@#E5ԋ+FHPpe \hjZmÏx[M=PS)б7o["ҁDcdFFb>749-H/VIJ8[B8Dm0J%/PlY(a%e;k!Ȯ%uׄL{|aZCZ0wg`:ԡQ/~E2qKJ4g p7b> +͛j, ;|^&B+z H԰;lg u L4g7moA>m.Ggf S'b){ZF[qܠ@73+$AlSN1H~$cWg]mw64k\wod!)0LwRi(nVJzf-H>73z*?BTL]nn.l#yNıMl7oTG;JKdL% ]S0TyY,k}1yB~a%e -/[;xmZfXׯX-@j;KC:P cлVK=~) ͹:*u[w%nȡMY5Ytx%:N dDIi?+CI A m}Wu ֌@$Y^']G3 ai >0N04,BUtB^U"K< n2礒):.^8I6T#h(v'nVn4e4q +kƘk0sAQ=9BAdW)gf(nd'HL{q{0 5.|Y^00ՖuhdQY\u6h:#hYR@ݶw.v#^Jg+rXGG(\  Ǟr n)ٙ[c'`Ld4P\:.GPt>v5ٿ-MT҃9iU S>ތxi}.$mЋ۞aHFK~rYlU"qM% 30uZzefw!z@  l0#7GG&(mV @X~gX{&u<%MABvW9@H8}ƖC&Ԟq3OlkWU}fYkO+Hc{ý bZ0sT UԠqS n&XG|]泛 !kh<f.OoU]lvLj"OLJp"v4*lykr<c2XOՙBpU|M$@uS|dsEMWQ:C: Cma U[$힠)tK6x%6e>?~ UU~'^d73r4 uhBJ8ETz I#':j,0?]]' _PM,ag|韹WtZFVJnX͙~j?K4EcENm{.yvJϴ/oe^(Bn#-Xve`ڛu7"'q^n2R?EfTA| -xߚo$khx{E)9ƨ*UƐ$0N['s(".(tQ؈ԯÍ;{skFԺ䞷JDSL^񛀰_.Ɣe~twdpPW:0V(V\R5"NE<٤ӨY`S5̻5wh!DiLuX,t"k!Dm֫lCݏ^.e&:{La,s:DChUŴY9ա~·!ȃ[&%V󤖰hqEj Iy3\}{لjeΥ5| 4A;|1L@nn#+) 'l&$GrGŁl ]DNf:(%9QB 1 ] L=sm iW%j*++u)ߩ@9uWx2/ 9aq]|'{+k UjB‚YɅp1TH: m 6;b#|z:e}yz90lJ:1kN- .5ium ("ʦ& $aJ&qͯ.xPEơFG5_=!:P7hj%!1vC:˶&e؝54 ZN4$K&op@mCfS]CXlʮ򋢛dkJgRD({Ҝ[0c+,XM~piRv%a`g'#nw*+ߺˣJgWJ1%o E5;'aB1r$Y+8n~OmXnVPbf1[m 碭26zZ"i\ xVдxggwnԪz~\$S&]fфdi]]]n> 9abN)ΎmTiNOchڌT?uPa:7s#:)7wx;[Ç|rdJpT|NDX`65cŎEY%_1M:d`J@OG&%֩D! [A8`ޣ]#[L5&6F3ASj˦ }]KHܜ:YYMSI-9k-~Eㇵ=3I}?uQ-.>jw%]懍 [“^I}-/x`DM翼iTijp)tlʡ-] K9QȏQXhՉ L:  qqA n9׏قw|˔#L7?XFJ?zMY$xx3e4HlS1"O`nPbta+ME|sV=]-R̂U7BOqe WYJWu5 #ˆ; wh[v#G_8'`c/97lUZ#T훣QHl> o-~35/M> a;fiR, 61Xlqg&;xB,rl(6q_!L/8VI2jR"tlPHB/M4*@nҡ05`@F Ke4S^ɫ CFj)Nm@X'V+e~œp;D$>Е闠2-9n+w[[)a+rE{9N<ndi}%0d_u>u!)Ft!ϟ "qt0()2@jz`&k<2u Lz .nKa֞6:ȰqI%|&\FCyj s} ֨Ć]?wS7"\ق>G,<=p0Ĺ qdq!!^0Ҡm72qӥ+"RN\)}5@Y.4J#p (|*Rܮ~VW{WY&It萇0 NWQ; .t.&>"ȹ7oDT]渌k"21({v뛑E 2]}i|5~RnǗm^,K6p7f uIE-ƜiAWoo,o̚@w 2n-LXx+)fe \ ` ~.{яP 0Z /nl.<;ox WeB0l;C=s>h(Q_ oS]f7q 5a E96 ݞqն-MFsrp=IOʀ)J@qDU cCy|"Y-j^ ,':Cl" uI4mU/2nO{:cgK#cRDRl#$:EL~Z}Q28_ew0 #F(5prNώԲGsq? fd`& 8];柋eTM!.)/0op)˚ 7*S?Z_a'=\F~'PY|)|FpQnHx4z}-41"}$&7UM.w1./>xs3^)4<<֢@G>L?C&_wa <}MԶF).ܽXYR9#PUЖ\ߥ7 Bu!I%˓euulT;%uVi,@gs5 @h-Q^~i>ڻeFKҲt*ù_WdmvY1 Ѡ~?М,a2qoR^ޫ b/1> v5 .Z1H9o9m]RtZV&FVhÐ,?cPM9+z??GC/O}R)bRaN4DIpUy+J i KM6}qڭqPI%_Fm̱_B}"RV%X۸'p^F֠o{) fYqHfTgf ze5l9exnU3|ZfoKHзtapfPr*IoP|kw +tmb 㾂TQ- h*vrr`uL6 ϣ;^ҏ t?Iz{(ziXMg@Z&H/ӿ{v7?y[0'K^7}iX\Ekqx6Q2_ 6X^*U"{*U@;k&U\V0gam \G:AH̐ C)E}|#Iݏ|{xM\  g!l奅 Uֲ<n}oyh:wqC8R1I:]H`bC$`Tl*9,D{&ΣY}Y@717 =:*{:7er4 K?m<"5iYBɼcWQ#4BH*+S-:+{ḳ֪a}؊Ȋg3-iiX"#c⦼9]{bcsﺣ-Fi8!`v\&WፁVuLiNV*p),6A~Ʃsٖќ؍$(5cfmx9#HA^=_gӱQ}A|Jy4oN-V~zQ$ѢՅw_w3F"rСxO鏠/d smI2e7> Ӣ=aDja+|b~_-3n2$m?o;Kd6N%В8(ZNga-Sn;mhs;rHc 8)EhNPQU\cMu#vNt  fn&} ?g߁J܂XJ`-X *[)`-7O.??uHLWY5',vCY:/rB8JljL|'> `H@H!s61~ZݭrH}>wQ/<#FuD>kQnM5'n/"u;=[64*V]ᾕM9 KZ={{(+u.ћdچ>HYt2H7@ 5ܴzt39$y)C yLUh+p'E5 -0)F`(B^SZޱJojA$U9fklod뺧/I8}g#q-nyz YWe+=5I@3 ӧ#Ga֜1GE'6eSZC6_e=8Kd~t[̼Xjŧ >U}ɬ0]hPۏ('[E]s0z&b 8Gmks-R+yU|#~RVװ)@;f"fÎA*U1-hoɟA\Ml $ڭ~Alsd~vKI`3+oV:m;f=lȠ*k?G2eَ}TO`/1\m.hce|M+7h =<}G:&6EaHo8*azd+ ).U)0G'7& mUMnrdG۾gKܕȳsZnr &몱vXIVӼ yy;Eo.0%}\i3Kxp|LvNԾSjT*Gq[*1bwih8bGVMkvI+Ov(WJ*? t2j g ΙdL Ps_LC_‹[ 0~Qap f-F@P0 }ZXrAyVT᮶R:Ɣ5,]:7*U7Z12[?/;g@Kߖ<]l.!N9MA3\BQ>W(ej[14ܠo_6zbv:ϹwY=FλwقIa= NΕoYW˙<84woف\fP|ȦU?~E^+ʺ{$.5aFz&q=0QBtf~R =@0Z+ՉoSsR2 f]'&e߼w\cN>>ϏAɚ`8xfѼ^Ke9O=e}_ɻ :Uӑ<֟pPz3]_oԟ5x(gՇ[Vd9Jq}.1P#2y;Φh-٦CFR%ԯl RJij=mH:0} 3:$BsڄE>_B1r6Ɋq˸W攆djb{PPySs$ o/:*[Ҁ xLVS!0,y>j+x\>I~7$rBvA O<' M)`嗅USMDJFkn%/rC W\f͖(,']]bG֒6z#xqs\TPvwE L> sGbpve'6Id-46By[өL0z Vbq,g>Btw }A}@F!ǐa+z5וEam5bHH P6tBUK@k kD_R_/E AG_)L.{tL WH!s0>#ZS:ː87pe>=E.(+m۸/W2)Ffʩ`[%vxa"3 9OQQB?MNFf\M)Wþ(?%T9yȚAf@[&ْ3W)61H!vYB(t~XB p ,Dq|1s&b8Lmg7ebɊ3T+Sb> zbƢn]o ;Xc0R-k02;uF9gt}R7K:ef,N %H a(.N>3I{3 ԏF&?38eb6L71z|$tI|J߾敦- ; YUaWI,lFOjE,a'׀qy&dHjÉSaIh_XLb 'U6@mɨFЉ]6Y]˘xo/u`u/J0 }IȲAqvQYCONjNpcLqOhLؐêum&.RnD.ʐhR tˊTWXG)`)}0- EB з϶A/o<_LloRG:+}k.ׅk zy^rl[Jn?q\-.1tf ԾƇB욯fGF`k]M0KsyJ٩]>>ދԄ\YVzi~{'::K{ɼX0*a[@ y÷FmTXcy j)EZ}>7>\N*³ ?Nm']{*E#11_ .A˂{FW``^GǞ(DDv?Ch6Ҡk89ѾJl m(3|8 O6ddkcX^G9G-*ÇLK=f`HX't"K<{Y#ⶓ-c,LeŔJqw:u; hݮD{CF+ %$xjtFO꺗xwmXUՔt(/瞭4-,:L9Dk~tbg"k:$Vޓg .3^m޷8t?ɾH"=Y!$FRݱ 9bEH3J0[Q`1F,I'Юs~IK#bC(6M Bfq 7Xqk.F{G+}3Zl|tw ͒Ovvfs.hb75ӥ ՚ p5T0NYYk߹kL2Q=h&) EG|6Lc ECӢtNћ4wcdBI!-YP ̩cD Q늶U5δ_#)h2f1z fg%[$ë T`D\Nk\L"ܛQ-m6]5& gL8~8hL1g*gxG;#,%ދӂRٳ۩g5-xj:ߜVkmG{ظ} UGTHg {g%4MHrq&YbLWDoLcB( ]Jy9?t0me( i?j:-mIkTEN&,/uKvy^ T(XKhWKt]:nF]tduE(pT8ٖ I^-$[8,O_^& ً̑j[e4 :n>{9xז߇)F@F=-Lt=Z ,u%d<ڳ-%ʋ}ke\'H9eMtQԟGU|< Z![xIUobAc6Zr &R Twlu>9;6f)*xF2&:FB]EűRuQW Ẃ2N^905" ~#n楢exiƴoh`U3.2IFD05Aeϡ@$87O.bs;PHd~l?Hnf5nd'^{7ayc(quHp$ӊ6'o.kSD`G%`,qd5}SXfQҀ!˺MxB马6pU>$MPl8$sm%yxLKLԁ@z`OE7u|;V>`{2[mPi)wN‘ȅ!J=I]ˏ1by%=dD\R\18-Wz>pA,U" ܕ[y]3jt%r$wgD-MR`d54梨G_ j]|$Ӫ*l<=O}\,w5)w c.ںַсELBF^ԤķsST~fZ|8L hN EH]{y奖:Dm+؀O5C%#/ectNL9iJ$j*c/HHXhŌ>/yV[_n׋+aJiEHl}#=ǎ/طzӆUhL愇 x㮽t%Bυ:Q'V%qQjfRn yy.tjpUsh+"^#T8g^t)4([8ދl:cl 069Izayc*1n8w# +O>jIPHCFM+&F?"L $'˙~ Jq@)x1 A>?_}LX?p5vi`4Vp#[=jɅ3@EKe1NN$e0*34Se.!=,7X0~# [K4,(!FbkPչ) Ax rf`Bڪsg-zRnmdnxIī."1E>u%jA '[='UҊGG5/y`'>|X횜A5z&@'{'#F(͐0^La)g d1OTDE;Ojj!xK8yYȐFx‘YyI(I#2_3-VIg/-߯Z>@VB)9"/q6qU=Z!n/j13[^$]|ÈJzfH QE8 r)mͥDճFUf|s[|,Q>,1B[?^jcdW:ASX2I[Lb=<0Ч_iKw;9]#  OmE!i ˼^z(FOհy:7Atiu p﮿&RTC x?;o>EaPl9}U5^vbo0I!>Pt6[ɘ?aktgl,Fq>N~7{Q} [ orx>hR#+0o7A eTo_SQӈWgֳ^=NZ@нW׈C.CViCȄ'?)՞d=~2=.73ǧ>Ħ C%Wd䓛4u:)kQ8 y?`R%Kc Jd Dj9\U~<7f9eOEgEl;s8L=LS$+YFWit[{P'nth2T"c@dp|Dh:YxBw~.=ؓ1UzMnÒ` 0h;^``FtG\./w FJd渹"1WXa+Bx^6g 04g/c[QGQ.Z7:8\l@(aӎ$F 襽:oyӲBn9Mh;=:K)ZA|Mn_Yo,>5ˡ˔U6>,(mYY@ء-2A<; PCiIԳJK,4Ʃ&ƺ;rV5uVʺ(^oCM C ?s[zMawWU֛ @jJrǠwI`Ar@޽TjMilk N3!n-,;hf,|"|]Ɂ]rn]5Rrpdn7Q]6jך @ Q0<;ѣնL,no^EJx{K8oy9e_iϺxAK70ig:2!)sPi&$o"\CE—G/_4٘zf^< r'Kmy ֙=TWэ&UbO%p&՛!*1UG2EM? :(JzJwޑb9ޑ-]CRɓ{x ͕K>}|ۿ 2kނko>2ɢouׁoKQ-̞1 њju^h@)$|gpUyዒ#qEjJ`Co5T68FME-3fd^ޕ,3VĊ}NÆ7:[$ۥ;%RiL(,y ;[7GW7,YjU8dgvpDj7/s[씜JQ{aXvPT`;U8$ &فxym_i j; c$ ye74WeNV$}xfq Jmh-1TYK;7$6 te#Q|UL#aЬWpWjl>4*LUSA-^P/) r}=naۭ)CN/E12d:&7\\Dr#`̣)-,8XZhyRʪhL:TPѹ 5H? "Jn/92_@<)N? ,OzK bx WI~@)&م StܓI5YtT6~TҖ'+=3D804'P.BI:1r8OYWdgi~"s,8h-P"wγp3Nsblh+0`p#9R}7adKnp/`QLϥz _G].T3(-O!/OV!$c؆>R_7%W%OϮ'pI40TLӡNx> M$ </HͻqnAOi7&kR2qJ8=Jˑ ~ wDS@eQr[ 7~RrLF Ex8'Veq9 k;Sc@MUI|8bT{kpwU މ[ sWon@:%oǗ; Fݷ0 WGך EMtrZg&u}3B=?l-&COCk5{+q{VO0p1a0lg..P'3?\dH%y7T!QӧMߟPiB IM;`TzXgM luYx5"ΥӋH0+8J~4E?ja؂Iyv:[&8k6/9ᭋS+7Z`jK/N.U#Kׁ(~v!< >4c?)~ Bdz$Ihqv҃{Pž9Mz2_j*A.#,y Bf$:Ϻ0kz]z i 8NqV U9бނEӁ >=s?w}Z::Hʹ-)%+De[cX1+^E3c)u;AW1tsq`[f[ws60lqjmxcI .!д1VɔV4h|OYe䇡tǢmռ۝b_ۧUR8JD4Bk'k$:)vP$ҮjU&& ٲg&u/TJ{_u`l; XIGݖ@^.5/n eg\PEng?!8;OY;jo/J"ՈHz!w9(.-A"(;~U{^&z+k2@  y3Պ01_az%` |6R;7E?L&F󠉕 \5Aj\:-O$:_`|eB3X(Hc:bUyԧpX+I4XUn1Erʭrѕ* >DMLQvy6g {8jR ucݸ,D_b#&pEtgWъdq4a5~'B&wb %V\3ZHMM@whv Ƥ}|KT?Qs\IN0 @[lgv L 3F7x vU:~ez,V y$p[(,(. E =_:Mk;ӃEzv ?== u>V{yat Kl.ʪ1ۓzuOǏl,X<:WQ-: '/.XrpgqM[?ЪGr]PQ TanP'8}.~f_Dlߡƻ_p( . w3:|}_$L+ w%m5guLr. KOm;f! +S6ܭ,ϟ#Ax70#]o p%YSuw,; TMoϞt) PѪ=-70+͉\刯Cn@GǍk|1'X׭A zBmS4`TfB U9֧Lq=`c(2_ _^-M BBcK𽍭?5B!2jxupSܱSwl$78*tcg񫃤$ܸ74Nft*,G΋Ѕyq}ܽN񰶟L\'N |'.C֯h_)s@n掁3ϫ bS|V_fǓz"?Ii\ Z"VR?B6/./ 5>Ǔ翌y;W{puVBRO i) 2$q5s[$pI" AOL@6 +edz򴪚 >1T[T-_BMr'W]{ԕ}cUٕ'̑ЯVʝQTD}Q#-rcO p)(ɵ4u9mY3kdڟlKd#oywR,dQcpL)olY?sو4Sx Bfaa튖eר0q]Nٷ n=Db]kÄV;O+ox ,(3ي&m_6_aSbҠ"77L ?J. eӞ.liR%^N) K~iwtG3~Xa0OL BKQ'9وҮw!qGxŗv`-᧋iVw&Rc*(mKtb8()B4 U§5Wm[s-N"ZK ~BRԶ4u!/Flg [А6ɼNCIM1 ,V]A䪌o(l<>.\ii5]`|o;/wLSz}8Mm-@B9|lϘtdl춟Pkj{Y5Ǯ(;bN.~LTr;ф1{ӪOv^AvહQ}:Ev5aa f/ę6=H dCg\;7U*^zЦFl퇊7]#2!m蚃_:/,S&}0"WsIW(YR>ZHː6ˀ3'1Z \HY3e b"5Z;dzsj_h d0Љ# dU08ԎPY Oc;Cm΋/jWqʅ_;7%ևF!B*JJGgh9#IjBaE`A'o($*(* 5H߈sѮgse'Q6z@G01b`14IV@ _\0ڔohojIRi 7$\lI87mܻmB+J='=[qxsdqqBXfPxTOYĽNktIM+9fZz(V_E&hv:׋*=C<ݟZEOGdCC@)[`hm@;fBb9D S-Z|})HF`YP ,y4܍*pRl֕ ԭeUPicP y~ h3Ia34#ʄUI}:^8+9A }Z~RJNGCeT@vGT"CT>q4~-+y!ҲݘU7>gU 5ZJE91Gt )8bDiIt,`iz{rZfa4[~x,sYRC4y!8Гu _1R^_dSmi8V%Pw\l$( }ٖ<ƣh)3KEc ZH0<͏KѩjX؊q/@e;d3 |FIWAنoUBF3m g;;:$ZEN3?ZF䴽c3wUO̥} y=4/?|{-&!3x!_)qBDbxJVr.0̟p.9ШB%nQHA<>|C!< u_ ]L@BG8IW/me!U<~%ًm.xG鹪l~( [v3h|{AXY?$>ٞBe2\ԩ8k͙{wmj<߈C`TOf{ܧȄDxujCeߵWA ;؈$_G -w"Vt{/Qª6Z+Dv]_![┄G};4U!ѸZb}xqQ:R<z6s Uڃ, VdXKzc/υ `YNJ㍘T`/< ^ '6o!"ɜ6'+{.O2 o,XNC^G<6r)n{^`tW0*T@ɟ8剬3NAZ=̙y5ax|7D_},F%gk3#q,5> b}gh6Su-b:ۓ o47rJaFl@߲ÞQlħ5'諁0w|"XlW0oHN$j1ר}}V> x6#BBѣ5Ou!F= t=.zR)cT[$9ײ(gek1hc9!>W-bA?OO\+b+-{!0ڙ-*+,MyF@tc`w(ٕJ_i:[Ѩ1B2zw냩!'Bw9KC]\04VS&'wyy] <7$͟h[` 3azKIc KBdE|#]f(h?ƚZ~s"%[:򢷚EP1Kݰ(@3+;45 kݥ|7n+.sr8-l+6(kVA,2ys/,ҟ5SN=+KikQ1 %W[mvy1,$}; pklnwx3n3OC[cRj}YEmjg)J&NlkgWӁoՓՖ}=ee zbDΝ'3iO2-մĻ ts *2 s{6o<Б Aie?)!(_)65= N[H [޴(}oa'z> 0cH Fc1^7fdk,6THZ[kc.7XYO+\}pk!Pk&J4MY_ z4Xlyjۧ%{|jarśΞm5~C*~ԟC#V8 {Rf@E]0Z'a o.CCv2)xr>dZ~\RfSƛ- yuGEUsu(%vyDJ$e%R.(eˎTN ;0+Uiog3)|gBVv1XO-$qyŊ %H^ϰBҩ@C!I<eDְ7c {`rdW/?@;oϦ(P80҇iŘՀ{a!2T?nj)E/…xc52~'( 慅{45w^l-k g9r"+[)4-&wPK@;BO p,T T!ӚӦ_۱Ѯ`&igY-YEֶ^ZJADݶ\ndzv!CS)z_ Rܶ8kc>oE!0J`&b;ߟd%(mmN>5X[dj N'/_w\nIJȞ`z?L*y/TK"Rh eم\)[!2~g xB:sEmx54M*ҩȪ= #2vYsg.t/DU~[;lQf"p-qf8us_Q]iR.^!+C !f9qz|W엝KQ_/`ʀ[Hdq4ST&{&[ª*ʇS͟M/U}VG,s!\3}yN1itf_TUJe 2qke?Jxk|S 0 .m|7XjE#SVow#ʎ z/4E30BZKѽ'_z 5aP4о^yƳTW>L,^=<䓰#<@[DvWT4]LHJFwkR`;!(*G!b8@(4__!fֈI%c'y >#*ȫEl Xc;5 s<-t u*Ax@YpiUŠkE+PƉEODB5O};o1)uMsv^hI~9Z=I};8QLoJJ0eê?yHU^nYr3oۑL# ڧ`u.Hv 2m%jvbg a:Q饚el'L ns0=SĺP02*AU57zs.Kl31fͬ(|͗"|rW/Mr̢+QG,l6\d; aٮ%gug#F(ZG3YۯɜѡH'#1YG ՉK;)Ze-_9>p#. A|UcTBD+A8;;sydV^'-W8Gf[\Pk{-ɔxvCIn_&A@@g>1fnz<#1rWRaХ= 0Wr1䬃_ h'u5(6Wr >pEMFl39 xۑ$rJ|W<5B%e.KްxGw7]E=4FB6.=?qn>bx؉D=H¥}ʆp=]Z*0x9.VqTcLxjygJSQ8vGTsos~Y Fe5#gi8qMw!E\vmҭfQ[F21l6I{^\|$vfIq\* GZϳ,RP/=\dt̲*q^OJ!aS&QE#iy4q͸@er_VgJbzhYAZllUrkۣ  Nh;ym6D٬Պl%ʽ?ZnN 5 gŃ`t94^H=TL†~4jpjJyLIC3;rF%3IM#E099Pj IlCU]>~f:^<m\g-Ӓ-F(C{!kYB&+p?p113HJB8n=% 䳸,-´~e*0bM ǟbF{wiXL"]H 2DdZrr֟##0=K=׌O0$ 㗦UKV,e`֚xZ6T /A\-u6 TRo\^@ wgOe<~ D,"~'_#2mZH"e"˗\CbzGiPn,{*q:5gb&Eyv jyRv; gl- 9 6{57S"]"C!\Ļ~WmbN/ܪ@g=j,`4id WG?*z heЀ $C8o 蚏DC^PN =fa^a 53"COrNҼW⾝| ;J8vXФ2#LODSY`!,x }b& K$q&]~deE}`V՚!?! B`39#&AC,|( m9ԪeU>Eo{(ijuhbgU?K: >|j>odcQ-w > Xu)'70ɬiH S'r%1̟?-vd-^Ms ZwˀV+HnfG-`?3 #[,T&)k:HӋ+Ү0*r ZpV=JtmbtTʃmv(rЬLfB st'l0zbf: \]!T-sl'C.^1 YhT0S͏^e K\L6{n;qzK[!Mgw O9IuaS.]cāTs'c(xN<DY34]zzÿ0L)FJ r+cf86I Thyq`e؁-|E+# |yRPM5E/Ή&?  M$o0͉%(%^7B$3)ƭB :-eiᖑ( (*uт%,t%uyG0m$-8%n!zQ&HSrvKV=4;5H҈[|x$Pι:Wlj[qU<5v AJR!gق;{5gd≷K2I?W2-KO_-h ]r˵UïNbFbT,=Wչ6(⴩r'6 -F]m!p:sV? wXi o-Ҳ2n+6Lh<;.o FMj.#|ת|c.2@e` B^In)H lYQD!Y,(dYΜ toMMKQɌL_`nTJWtN-Y-soDI9T'1x7OD$ʦzn^40c3v$0Vdk*4ʐ yǩWBd}|&&_HQǞB+Ł=u"ԛFT"y;ӆ)%lXS[&p*kHfiK ٨]w㓃Im=&,R| y\-~r :QN /x1̓ IY5I&Yn ]C!iY;$ua>nleʞmvaS`Zr44ҽ2Zt쁎Ul틷 5 0jf2mBTE1 ҽNp9#hJ")xBǿvbD @sPLV1SpHG67ޠ-^*$Ub( z*^N[57/{ЋiN O/G 4>\za<n  aQ? 6e&*B dҸvo7G79ƨ#<%7zNnf8Mn[ωwIMcnu=Ŧ',F-רSAj$ hxk_d+IOI]h9sC33Sć-,p|?Wx2bq٢VGK料AtEh n4mvz$,e'x4pp#PY4 /,Ec ',c,Xp p…vSL"`m-QDih }$zyFid^u־|hh@` O/!j($w,;kiŝi!YSlnL ChJȫ߀,Eނt#g;E%vPYÄ*آEp9?|$UdM.fnzY0YH281_Z6HyBut&w0׉`ށi^Y^/:@Bľ5~0Ar96ؒ=ekn*6Ͼsɡ4Ǚrl w,{1e̅ZM.iN2~ 1a+O1[awB(A+PB!xSl|iԖ ;B3颸[=c$)[>'8U{`y8kmfn i_@w EvTk0\w`V Ҽ1\1i<~px| wj5 ϗ]a>IdIQv [Z7C0;Li7hs0@* ,GuY",'@YH$2V )Cw@TȒS7n-̊dShzd'S%k$sD-z~Ե.*6_uD#qy U! i!?5}IR}rA:C p$\ோ=rMU ;r51MQŷ8eF<̜lqۼj8{7)0ZX&Du+{>$bqƄ€ruAU&_jQXJS6FȉZg#HzsOt;nl_'lZg>!@]$^ٯ2I;l)ܥJ4f3&ꃢu~z^+TOhT5zL>_0pkzn-AM XKuUΦAнf%j7}_s&긩&]3!/V~QDK3%#VD֖Fӯ}q-B O?׫Eծs9+j.X{՛ ]yJa{=RTT!  oF%)]Fǯ/3> aPٌj@*b=4$o:]Ⱥv_Wqc`apU w,0O+ֈ$N1`U<@9~ ldh1 L)ƿMMy܊*w_Dߧǚܻ18(j^ <0@s”ʩɂD+*q: ,~P|V2xþٓ%xuj<G&6=k)CtEZS객ִ&n'Sɸш$40׸A}۵ OACQ49{ ;nfx+UehaӍ?e; 0\Ϟr!< PrxZaOK4z̤(YKQ汕Y>u3hx-80Jn1_zx8f+u"xeH|c,[r+5]hU]/K}uϵlZbgXS'kl͋dShO}Xcp!8Vԣ!TfYN'Bˀ8?FN3lqyqےuԑy+Wopu5  6TPr%:ʒ!Ϫ|]o(/N,3i 5kWh"2QC2溹\0*7Ȋ&']*v ?~#5('|.ݗP4-ѾW؞<*,7d2s|;¬ ViB #f:^?='Y,u>cG6[;dS[:]nQgw#5m #2vIM!Il NǶm{V%!0TÓvީtYwSPtO f-z:Z = $ 'A.Q(K~z(с'j@;L3%:n=SŶ>q6n#FD\} ҙ[]pmhœہrV~/РL|F1}}R_;~j9oql_GVt+piX@,ײº+<*T~f5b1>vAlFډLcs.]eTMJVaaRu1(SQ)ʂf "39XaÂEDJ}nZrx^3 oPxy43I>J"ʄ B'?l8RV&Fyr5qjDj`ЇDÿ3ܦR392a ˓I3|G؀)/$=¦I壛 \mxy^R`NR 04e玼$5`)wXn24am[b%zGot pg_436'++Ny _I;O]ʇ rjcK^+bs8 +fr&㕏`6>lSk-xqjǥpqC/ Y7Z4LosCÐZWDP icѧanÏ:<ҸȦ+-|$ɾArT bdMth!u_Ty7),rI}[ 1+t% bG"/}/;/*V~d 7k-uy7IA:_Njr_z=1MB&WeqdiuyF8RYFOOeLuNAJRwvm&V=ـ-Al )qK-V|,C#_RQuUYC0m7Zto"7S[]M^xTӆ|E ;OWIX=H0&﫱`f)W9:7U-Ȫk43ٲB/D'BʼnJ4|CܾD𷯝5qwO1&4@RIL`D-6 bhb<~HnxHJo~#(J2m qLj^-r[Ŀ́^aFC1_}=BٰM! -fʘ= Dr# J$ );B*y䕇W|6,#nk:b1OvD ʬ3t[s7^Jڎ[4h%_~r%͖fycFY~dy^z.+@gA~KAԄJϛz ܛ^SOlN/I*24יVM.çTwx1$#N|y+sV`YE^ Ƒͮef*/Vvqhn1mݥ[ >jč2.Jծ>2X@V%z2it!I6 Dn-9s0 ۱H^K+4+  * `lAYPNyCQaO C^,%q9yrdzt9V g[%Unes4欮3sԓ|}D59" w]yK ;R=Fq,HRɎ\f^s _xGr@5%GhV7-D8e]5=͎Z}V^Ə\,|!;  gؒ`mdB(ufixU6]nv*[n8˫u8U2(8cjw?(scҿmņ-hZ.X\)㑨{_a(DRy(eM, %DMW~AЕگ{+eMQ|V!d$n[*K| \l 1V? {ޘ%sϦ9G-+)\pCcL:c]ai]_?[do$W K{nKؔ>MbkCvy9eF̶ "n*a9t1|2 n]2zX7VZ.c[4Y4W|aƬә Zzx /_0$L>$P8/ $uhYNC)*|Ƞ.WX4Kh3缸D5&Ͻ8cCƷ`:9J,FE 0@8Ǖ/2#aO̼瞀,wr^J =DZ&2ǩAY'F\IC1&N+GU_e !H-b`*QkMCaEzPM}QAwtjt/8ah3[4ďתfq')TC3N6jI;txyw[}J?θqʒ,$-uoM&G) 8+)Nix;Okd&jfW1obx.EAP d]~L 1xy}˒;hDDXSDH •cǀp :1ݕ!ѐ'(U0 +!Vd[zڊ[|vO;^ѓ8*Nw5%icV$ |Ȑu^&F*UŽWr„rk5J" /qݦRhb)\սΤP]O)p1\[gT2U@vo煊IE!sCīEis5k 1@#e ê;K}B{zEqpjvu5ZшLsz 5zL;nd۶1-f%U2\ȽQpE=t '['pxp6-*ͺCఁ=LŗY)n2ljt#KazZ ,ܟ޿/=nATʛ]TZLj#LoK%p<' QHL,U-)sqO';\k'Qt:M/zI ƚƠaS|>TuBqrYRئDb(!OH-K>K ]5D8<&'o\V>X8|5߇>hܣ1갠lыh+5U4;fs+sͪ|) "M̼J"ZXN e3wc,g>'(ƨDx=urh2nC:ԒVP5 /5}fJ^(`1**;<4pGUH:Դ4cauYp,}+ UIG= zދI k\"9%VbE{ ժ U/xw\_SÙqo"zfQZE ~Q4%#5$!Axʈ].$;zT~8̃$i˨LkA*2zv>SV~ g];M",$ Rd&.t=i:+n[LQ2")l|h]&B*4/g1KAW^%P'3l;^>}ƥ^{rPy3  ψIؠ]O.,m-R&0]exYo6QV#&aTnvPO![ /NEvԋaI*ǿiJ[q&6LMĮOD-7Mfj7%3Ns \6hE?#v^Pl^8s'is /,c- Cz_p)[t|J' 4B]C w%]!- $(*6(_ֆ.}noړH[iqƈ+ D*5Q.֬!'qFәY Q:\rf s>m^ D;b`)(P./Nj{듄RdsJ1OH؁v\HY)AD>f"c&%F?>iVrD1~M; bk +nY~WdY]Hu'udg`%[ ˢٺ9UKP>27;A < ^=+GLZp'%dZ ?莍#kLw ],HkyHҘzSa2ޙ#7r\m?h rv0" yJvyn\HM8β1f3!@i'-?ݎJYhF I6dӽt>8k!r .DU|o,eEʊt#T3("9e+zQ^=/S,|eX B,`#H0!Ǩ{s\IJnlҢY {C]Rʚ* ;(ᶤ-/c!&tî]EA#C$zX߉D:ڒ8BGÆ$cv4f\o9འ'(Xh$tNb (tkRba}jc/譴) HڲSp=4Kl O +Klp02CPH!K$a#g9x+xA`ߨ/2"U iP)g#) }B3>az3K~WYng)O%WPu:v)AsL7h-]Rpڂl9#ޯcJ8|i-Gڒk'0< E|5]*MU$g^WͅJ'\ԇzBeeK$tNAxB)fW:CU'6 ?X"lDjq0CF?wTj7| C}k'iJvw5%3{ Vcq{t*e(OQ@B]`Nף\T?`n9?QU$1RdkDjo#]^ jdrII1p;uyA(x1Ʉ?܎_[,yqR%vv HJ NpGSfwS{=@~H&{f!԰`5~>C&y/ m ^$( CwȩFXK!;)ު]3~P'iaĉ75rijn,gT4wu~(GȞ.g5e)s T$÷Qit&Ohɥ''=co4㞝yPFTᄖG=pQp F$n372<.3=/E@&F>2YR-T󡫌H>Fs.>z:AXWk )d.괫_z8!;VN c1ya蝋"SWgAI @i}DZh Ƨ ز$=a*4}y^nJsgsH_kHKq_MA?-vm~Rs sͦB1PwSRq)^Hs_*gQQBYF0"fxQY}]DÆ]0ASD0xA@d }`e_]ju.aW>$R+T"Hb9C(VۑϐTƾ:@޹< I9 Jh3pe5ye)^`mipߜ0uoMy6"c*t53'Kk21!ͥ캔.J"`IVwd;+ul`*o,ՅEYbrL I@xL났T}t'JgɇV> "T 9HOѣ1{q!%R{jfpDz \Pg.LpkEC$;Tޠw?[[Gh[ooɐi o6|eJ szĬނ }:jSy]9_r]؊OVz3MK1Ys|):e`ddaJTu:XLkS@4MѰ&c'KȞы;V <Bu8g6sS9<\ iqI|?iYEr^b..#;Ra;!7HLvfQ.z"O`yesS@Gk* *# ͸6&&">CUW"+Z{Z|v7PB}8^Iv 'E:*}o%u6ѭ{ ׄl[B_{ECvY_Z5j~)&*$󗑼:eq*= R%D(Ie*qr` 3^֙p!`M7sv NN?͖ 4lqch'~nX# /(Zefr2~<`[`E#&MG@bϤjb=;G~.Mt;v$͒up8,1 vVVn Nk=,}5؞LՅLhDhl CVP^*?|_W?Ÿطf;!8aRNYk3١8kV.ϲV5vf$5|\jk9x2v-%wqI&oQ/YJ$Y:%Ot=R 1W40R3Y:&9%Z|A JfêCU_9QP(eEtAp<3b~Kd5P EO 0Uiu `A͌՘tj3\O&_ҽap sެd;e uS#uB ;,2taya ];(jw͠J/Y@8؀(d׬@q2`FNife7xW`ZZMpavgHÚ9˾k}B`'y\PC!2tyTϧr$fIK*6 #8΄!kBU)6.uㄪg7R!f1;s]CmL&MZ@d[(b);jW0f-U'/Q{Z|!a91Kko"~{^мmouDDJ 4na+ i67'=9p ǹԻNy(>1TT@4p}Ѡvɲ c,+`)rE=Sf*e PTVv^g^qs-ͨ #{qFVu&jQˆ vkEAc&%xie|Ҽzvbe#hFjӲrz+ V>='M I%\R6?L 9~M_t>Rɛzf'=>ag p;xGS膘Y{큷й-7("yܦX4J&/oo.TlztsrV DXcj2< D nlBAb96)[hQ[qIʁ˙$Bj6xaDQuUFU+ {^cpJm-a(bH9rԡLK 8,El?,ioCA&8 L ܩQȬ$zr[syjκۆb k|(sTlHN%>"*0ƹh 3 >y'^+<)Vk2O/FyaJQ%P")#i.Lp ZxiQ.졌m۸teYΨB, GLxe/٢yÞ(c@Zne_|+5O>5ntzJø8^A߄N+r** @i-&ȋ07tma`*~腄ޚz9i;x4ϙ1MR#}vӫP~ɻW4!R]ؔH8s2=_ ̕q@hftֲ_wCF`<"1O<(YK !s5ӱ>bDq_ZKC [Vq9>Op,!Ciz" mKxV*#K06k驷AٰS/DoSrߓ8' M@̞ginGcrwA *JٔrKׯJP.!0j5hdD?pXؐӵl >ʞD4t>!_w %5ԢO iJz2n aa(QHw-[wUVJK*xW_ߦ/= @[7iV LodC{Y+wu+pw##Z2S~v @K~3g`*:[٪610@&AC@pUJΒ=,&iVcT6hsuxd޿ߤJ8P7Bۿ k~\,E/G-rGK<AuӼ sb e:윩+=Kc'XPʖj RKl۳7Em۾Qx?$m`eY= pm$D02TD__`I;WQF;̈\w tӎQH|\w6v 0䶞r#Nl0R  tcjMh4-5j:65Q! H;o׉qC:(:]0/>U Pr"9Y>ز(D'@u2뜬]+dTb,)iEрIGTHy(ɠT)PaAfyF]s Y^a^X 3x*c(n"6՘,/ڭ5y6Y<1Pp') 6sydŞZ$7ݴd',i[l '"Pz'ּ扫ca JYr"Q; V 3YW6)؂6l_cO6i^T6M qlyV&CYgp"o=)VTid„N,{' ?)Y({VN"84{3KGLRd>2|X-*]冤 d5Եb]Id g/ 4bxwWtۄ,H&FCU`)=K`ZL8*.Ŗ}M!#jCrJxgnm^F80ZIVR]~vx $'nnhH/Z(Lr7"ōmM|Q:*7 _[5B"jJTa#XZ՝8/} `GE$Hn4nǯn'd4!zag.DPa jLƴ燆Ҥ31s7&a1Q tW@-G FeO8r{›aܫ8/+b0$#C(YTi>Ue[-Ze}EK˭ykUy8OqrޛP&U=a< <0}R,OKQ>_tiUat -p <DDZպ> N9SF*6d ]f1ǽZ f<:So"_SD)@Ǣ#1?3`U ~0>>MGGNeI%Viޔ:xy~ _eZZ!Y D7k-Y ~fTfO`?+/`Ij-)l )`>kN´=^Talbr=9"O5 0A@bc'3 <1ZkqV"ILJWIW1(l7üBވJt_NzоXw7MYє0򜙇}XT-t ߹b!3SadVۏ)s1b@xy ;U۞ >ge" 79)vuz=q}h+ǰ\حN1- XZU4e]U&=GA";?NZJۨ`ՠXm7u$J%MMcҙg} ڥh+dݸe|?TsG*?PRy7^&`tT96 |b$gt}j#Dl֗ 2Ui2voaBpu0"a<\pIC׷!5Ad|3ϡ;jНQ5GqTwR,o!uȂ $c!qzBhTpSk:ɹ^8 N[u5Lz }v$ T +/MnUI['N$,=4<{w]=SMpe#@UM +v0 x)T'j@~Z|J;sf; d D>:jޞe"0BjUXcD\; &UowgJ׆(RО@r?T9<՞5nFhbNǝ"8dG%^H(w9)Ge>=G$j&4 #]j.* RULxQOc);ge|b{,4#4Л V'O\#`׼/PWި,ǫlIےlYܯ)>&d#JFQgRs<`7,zy^ X$;sP*/J,n( ,&kc:exŔ tT8kOh4|.3Fs {^A2:F)y2B6׵h7B3S؈LBi۴m}]5AӮA8))Ï"u7jB`mvǘQ9Ea(𝜚52ĘUVBfqWtmeqї|(6f_Њyg˯go${H k2psnPU'y+;o"K2bZ5)78Wl_G'%eTceT2!^߿6TmBz0N r-UbZuz K_Uv#K8Q]{^COX0m*Y F Vg濁f,)2Wz9}>U]Ҝ2*6ʷ;G#Lsݏleb+>ӗؿyM9Xx@#av-č~?F<3'ZaHVFlQo\o.ͫ6[2|f׀ CVjs7N1zFgXYWj˕=1No3ܷ,EEcŕ4^A5_ x\t~mݏϞ9=<>^fxݽSkSxtMIjOGPOóa z/WJŦ]ʼX5m>5ΚWQj/Urj}>[X *4=sAz&@ GHhHY;giӾEϨ-6Kb?94+UdgA1V?8_4v֟+13rFkd,'+3n91w^ ~T^ .?jGlY8k\*w  jKRv cIglQ /ͺvEdȜh8}`I΄l4 rHf1Xu>藶f`UL%ift"/BfČf%a}ǡ:&SN6&6j&*=% v8=Qz?l OuF4@7ToIJgFZ)я1[Ӡ?C/I"tb".-Tq摒a啖4Y.o¢bgY۽# ]^0PvTP[ ?hwJ[`lE i@6dl "%x+lhHX rU}|%~T]ke,@y D3Ap;"-yDzpRohoj͠BK@$$:vTu0D:QEF?0xO.$LI95Ô5`:3#W)$6bFۍqhUhBH}dEubg7s {L*$(k]r>[//s&/k1YwJ)/$;f}LfGob!5>\B JpQh^#zKjp,{N $^(ôH8J@u#Nxʧ.uŘ8\IIvM)'c;s) 7ZY)t+Q-m!VꊱÄjbU շaS@qލZE'/瘔@#w*wP?d Kl‘RtMNgJ &xf:M@F`ǸS7`䊼%ù1=xqz"#,B[B9jje}-I~̧ bH@Y?D Y7y(̢KMGUZ/;~!]MC9v6WW2k`;(c0@I>hDY]Ηg%(n*UL)Tn$naPDsg+ WIդ7g&u@ݖ@ػȺ, d%E$ׄks"8>7Uޱ#\QoҐ?*B+$S<`;}9*\cB:& GjAKɄm zMaosQ͞9!)e2N()ɉ[w`{D ө,y/e[ HR #}{e8TTLr 2@uHʹiZzQoK2Mw\0m \zAV.+USs”r0bڟnZL}֊ԪՉ<~Oc `";X6_@9j %tpKRn G0,0a;άye# 0a:uE հ9]"k}To69k R ٭ob,~FZ;}msAJ9Ĺb㓮8w\Nsd/.)bU;[/dineRo}O+[tлnU2eOFԣs* WdU5ţEPo*"lD< !sS4 -A^Lǟ=d&aN/=BAf@hs0oPN1]=:Uv GGq&0R?3~3%#wJy6ܵIY&1ة\>{T?^SdE3~{-fX}` "pJDz}{Ϳg !SEXy>!J .S6+2룽 v@S9݄׵(b3,xODs۰| ѫuJMw7QSX-o~IdT HL5-E❿0 %dqJ6E^$q=ұmH0Q3PDZAScyu~Cѣ٨NX?!*%: &w6+=c"&FJØ|pm?Z$f}ݿ2#AT?$<c4kkEVwX;Z=SndnMT_ +Eq4Z\{k(M4ɥgcv+T#޶=g:ݶPK`{'w)n])/I \!?3O{G+Q=K;+᤭ʁ|߬Z3y1zt!Mi-m/V΃ TH8w8>4J#Aٝ5'FmK% Zͩ*';Dwƾ׵YzFCM0c'X0& AKc,]uuys!Q߲w!?/m\SYVR/9fB-γ5=TQ6?G]fҢܚ5rHJ'b[;aVBL< MnxN~0'p^nԦ *6ߎʟG2 i}S!14=BdMY[:B)0 xW&݂XD_[otlX,Uu_)D |^ʗCy峩mEvj"~ؔ񁖓"OfS{8]EF*6>r*LlgN,'O]j+oY@ SꓼsO G$qSZQy$9g@@ YuMM꯿A5>]۸A< y~[=JO!){-b4YfPЪ1d.;CM$q 6M˞Q]: pd>WC_2)ӈ r@d̚IXIJPvN?B@':O:XКqs;fi@%)zB'Gi'5[Sůnk  [ƥ &W;RZ!هȇ|H.bj+[VqK\玥f8;6##Q#/ g`@L;rn*ȶO+\^7r~R #[΍,XBoudJ )%Αic S +źs:t(9U 1e)D{<3ZDG˸˿$02iĶ6K/%=ؿ0rF} F-68^~M CP:QShGEsMZ6)A#}9xY5w-0dɎX&o7㖆EʁZs_u]k5.,YK9TY38;H{ATîN4ĺ*7쿶>e=0نZ^`C YAQ|Xk&g^:X +:Uy˗ΫF)[͚x(`(FQvZqWŦ*NuRb'7q=q A߆w1-m7\WAQLfظo01G@8 >>-L*;+ړ蛰44(+Ȅ9]yVǗ R=!Pd}aRVι"T\J4j#eFJzvz|ޢڱ;F{Try ,qy2[.H\sP:Oϱ6#%ĕqG3/>c=ȡ5$RŢBd CZܡCYg%wHyW!z%TrHLCĀ*Xψ2+P1} Y;iV2AZxZMK>0C&|fg ˒) ~&{^_%86 4H*=Xf :2SQr߸)Ύ"\] ӾStNMu- ;Ҭ4Gp:Er/L=T6Jng vm?@xz)2D# !&ѹ"_@j٣x}y<=H*hD҇~)+;ͻɍ^ZyYn}Y{4FPakr\}y~ޜʟ z>$A 6"T@&RO {57<8 f0lУmL73#CM4 y#]flٔI 2;Vld'%L*W?)P$' H Q`s̷X@61~RCo!z4gfQ!ҜzS_>Trhl}{`6<ϿnB)zH8 }݀&L,cN>yGK6u܀:Sz5R]y1Io5}eI~%Rs*uԑ&)UO58,"u& j̢~z=woh{y(g] L$‹P YV~vA$4>SOn1P|J09vv:䉚`H7ōFf˝s֞xbV\y}l!y$Li\OM'+v*Ӳvg6Ou&'ry  Hy޶VY#שLYjIxoX (o]v2cew7(^~ӓn PӛMS!?B~N/ mF\Hsey&twTlŮH_fR'½ 'Go" GD-`w͒QJg AW3OTRK CQ tX/T @ tnR|p.j'\YWx\IwJv"qg ܄gno{u-xk0\]+`KtD]fGũCy`,oIT9&?7DDCSI~[utu^Z3Fdzw& bAzQ;ltwEt^Z?|.bֵ4woacilصaavlZt:&ћ٘AK_o@_DU ?>;Jx7J &/qnmܺұg~'Q7x~*S!?L5Ŀx8Z޲$|J~qDR[It!̧-w!6 1EJy;I[IնONžVv-:;` uN & RP?>.JЄw*O.ͷxόZRk v}GwUJ:i 6T`Xs/̲L6}Ė"B E(Ա@[NN/(#,ݴ84*`!y`hvPw(̢}⫩}D7n `~klM' )$>b%WyEraR+=\EÃ= Aۃ~@n('[t>(j~D8'N7EXޣIRa;n;5[L`ia(jRR}PuRI4]Z !, a ܍%npTmcLf#B3?~~ O"T |j^ O j\rP{;ya}֭ll m:Pd0ܴMՔ@Lq` SES,6xcF-@@Ev!%ҁyR[V[=CzgQ!%Oр{DKXꑹpvsdn#S aW,{Ϝ_:orJ>lf4@15ԧJ‚1'fw9@+1)h _W_^71t\4S+}Waǡt8)H͏b˧,"Y/ǣ;N:r!`H R 'J2fQ0 e=f[!’=|dz:)Qp?CsA@.؎R}'l ViVBºQ|v;PWTAjIւ%۵x-4j*kJ7ΰ=9'ҁYNoFCO\[W:J)tcǘ@"h"]X3R`>edrj's89?`U7%5m> ԁ6"#jyg<]ޑ1U=' h@Lf GFmACWy?] j]s[BYr8P8oFnՒO % ~%.]-N &zgl`XoF`¾YDK ϶G9j fCHfFM' ֒mc79FSlu2Yd'0[C2.Wk/:e[^@ ~2 )xl"3ƯޖCc!eg1e\p|4V]lۊN5پ¨v q? #r1ٯr歞tɤ³d)^k۶s\L=DZK|`UɉhIdi {\Cj}6#y߂}NxiUWPc<d1D&"̢Y8NEDe5@ +_/+}6Q]U /.dDWz q+'M%ѝ;4j [sz$#@[pp"-_r?whDnI!-PcpPK'rA.kj|NOfry Ƈ7Y{6Qj~P jkR߶m+7\OYIٙ`>xVd5e.&e9MWB;1$H9s2~f"H0\\ه2tgq802|Į9׸s]1@-ZE o Knlh\-ɣi0GOQ1֫zp4P͐IcyV[eP]}(`1i _8zL{9r?#d9 sXO|mo!v/mx5bn9dxm=gcՃ׊a_TbJ]>|%؋ڄ2dQE("aD4T:}j@J\LCn8%ol"PY|DD'5#OxkxJ٠k;D8vgz1 Q;+eY =IgW择UԖ%H1ꌷBw܋؄%yNGA/U&G' KC1~v1X18#Xrސ'ы_D1 :u X=~e|?]ݶA!cs2Y.g!U_"SjSPcV5Br,S` ieDp"!] ɘ>Bcq>wX|z/\ZŒ֋5Rދ5S5@ZU%m1 @hqdJCvuӾb!V֜W >VtCbc$q2sZ'SԠ͌:_}fvs#)soi/]tdPX)Biz^K$# dxkױDP|O(I}/cڢ=癦G gQC?nP}N$3M)J%0$CqQ}jϽ& ,*/uzA5f[A;8 Dqs[9 /oJs J<<^u>fMSCb13R _Xi_6l'G g:$QmŨ?hO)-SNHNypĀkĺsw&i'.rEܳȼOɾm߈v7k5]sjK<:uz- 30(݉( Y`&-*={: ̓\&+4k;t;WϤY?cjʨTLc[a'9E˳־?pҤ^f=AJ(5An[k-m6(U\ڴl˫ c#S)i #V vA6##8Y>޴Sd^y1UΜSM8]eBJ'T=E/ZG ƣh,:ʩhWkwfskުHS8YHD@+Rl`l$6*r/]"IẀ3{hiLZFdRNDOAlC5Vu ՉP)WvRGeꪠȲ Et}OaT5_̴J[;sF'(I3BgcYf&XCꑗٴ{*-{xB#Ox l74GtT53@V4ȈWЇD, G@ VjA%?zzQa(Vr\A9"u\:kXw=x A*mzRwwshEMAj*Qc Yt0pAE0"\;f<8[ҝxD졁Ž}fHc7Tz,.]m2ҞւlТΑ;c-eͧy >A?+ 1RS tZasWCt'3J;r^S>h#!i f>C s_d Ajiݓ*PH7An);X%㹄]rm?Jj-jj%Ṣ3k|Y*lΗL4}PCkGauC g lGLr k5ByD4G"DMn޷2ݜnE%*afz iR}]B X3 L^1 j{2&v]V P S;Wƽ +LOyöp◣*! jB9fnY=}ʷkx@ݰC<-mPW?9gj^ &m )$F@XmsML*2ZqY* !V31HX 3)i DH%x <#Pt&Lskq ށg~0S1]N3,(NF;(uT.;3ɘ$ǔv _" 6b& eK7ڝtoZA.6Lz#*ƊOkfL!@SLZJ3N!/> /nݳ1wY EP=z wʑMGT "^ڧZۖ}mPAeJ⺃>b9nϗ#֮5v|UJdao +73)"M+.Ӿg.#. d]`iMBGNaWCk {\H˽6Z5 k[zK2תѧEqPg7lIӢ!{>Uwո+b ';` 'fCtgҎi;V'yIuw=;T\R= zc]A8501"T%n4ZhQF&,F~eԀpH] XY">Ĭ@ƴm}Ê7(PGrErrd N)(Ĩ:͂0s9D qv uME.-z FiO!3([`f[jrTD6頻/A%d<1B6-"GHO#EL>42AMc^ r7ޥiV O_Vs'\,JYy0\y̯/M{੐"O~Ϗ,DiXd/n~@ Ve@#`d4܆йNy,%-j6y/XAo0# IIDw£03dQ:U$KD9kf$0igbw: [ Um] \%+Eӑ8x@Zʛ³#|j'Nx*J{IĜUX[ˁh;O1M ;=Qcۆ#Q<[>{5@TԈ@EOiKf gV,F؅\[س]k=FtKWzȕE} ڟq]nDO6䷽8:i4 r188z>ӖmT j//}XD7Qx{\Au 56#G!\={y{ʑ MEUojͧ^ XKc7 XyM*A|i H y#A_k~#'%1mg6sHJkKA~Ô>j;GPo:O&G  v/%Aͽ_$P6BZsp11B*Fm 5w,O8-f1=bey<}( r=bb߁ѺfgysPB݋GIbn0eBdHS 5'd; 2LV ISuTX)YJ\u=BpY,+C~Q-&ˮ,ԅeOcQRe] ǻXߒDT 8Kdp'u?l!fK'=>cE_4ߛOڜj6^\#$'4ѿS$u 83D%@-L=7.ݜun8zq7Y˞t9ܪy>2T)^mߍ09HG#)% 8KH7wOsD F(N&纚ES%MkXj%並S{:?{O1n,cbEƎusX`^=ibhUbg-?5'OXyډ?&ٟTRQ8b vG@ 5 `Tm51rZ9@0auUxΨ- y RgtPp˓GWQm{D[zw:7_-+'\QKdNy7nOG7U+'KeE x$X$ξUrm/ 鶛M5\_hX'ڐsJn^>CGm̮'`8h0"x>Nl 1B}Pg@Wl3kbq懦Uk| 1v4GL깥8Lnr9KU(2*e;+YAa( ku$mB%iˡ;::)rnw"$g>2j!ٷcUZ+Jj)] p.IA:Z5jd&TKc1y7*(y,¨ۋPδ\zF.uҕwPQcbHFv'd ވL"$ů:: +_O)]c,y !wfu/skn7Ϣ'_x`{CG깼A1Q)r*̴X[ lpi ܇65+*z02ߥ6Vu _3:)|6ޡ57ߴͨqK[ӡrUxQ3ڴ^~^{֒Nۆ*=m.qWbI``lG2+ LuT;נ*? ss,،ZCx|/Mܕ++҄%5zqIJ3Nu0o!k $!Uz8õ3?I1M~B0Vjc`\I7NKolbaA C@fDSAVGa.9%[XhqH!?Z9 0>)_rU1ib: DD؝Cv .M\Ì:vEً'/iKnUiW5dVDM!kMgQrrU&R5<Lj7QbzrZ53v,M%1*k 'Z7mD]Dñ<̆Idcƀ"48W`,)Q1qrU"]) kv+Puq%>CmXS<dU`2D+_!H]~?؇p\QeMnd+CSȣs0A\^F9Q~h^ (U;iɕF%eӟTb99|ŀaD9< DYP/2X/d46GWM 5 -7Yd^N OMa/69A83ˤ{>[U岽D NVv:$= w3 8JS/az(99)W2>e86z-?Nsjv,mV/bD((fnlc mE3]y𬳖؂?Z噊pND<;n̰ $Dn C؂HR9cPS`FKeox Ѧ+~Dxe_hk$'L29ZV>Xr*]!Íx2geglDSY u~s6gvns4ceyy[s)wWu&Lo$R`2,Eg5ʚgBGT=y <$Փ{Ri҈mL~oÃvOp&!lBeY4^Whm*[70W(pd^$Ɣ )Vl9WӣF k% :^R/kb`)$:Ri zU&BA$^抇BDDڝf.Rly΍M0bcn+yn :~+Q,`㧒2QKvBzW_b6[G?D= eQ^ 4hv([8"{yBg9kָڳ -ȓ!݆*X :f%4:$+# %CԻmφ:ݣ:!R#X.A`ٰZ&2׭tZxmKkTW=.^%9"ìB iu6}(r@wJYQ SRh\[/䨌05ç1tՓ*U;M-gauYK.6)nmKS[0.Q=@.)(VxQ98|pQ9kG%QQv ÊbuwSl V?iA/Q|DuXڃv'Sr7BuiBg sd~)a{"[dMCk)cxU劗HB`p٨nh*.;mU2#fr.`JneSf= 1vjaq}$㚒Sx懌S(f#Mo\[gCkYӟ̧SJNA"qvQ/D$EF3yx(xN#-ޜK^; `QaE?ez!戄Cw8RJa6)\^E``a#FES4c6&SvF0WP_ґ%<:U@ƳBc OlVdL@:_nd}DZ3 j 0&0e}5BxAm/Hڗsu2Y)>! wQ,t?3 js(Q(ddD j^ =M?-*I@E=QXp$.|@~"DC)nvk)e?'h@}N>؛6VEs+_İlL;;Ew&t˦jGJk<&=H\rJmc}ґr5я)akgi3Mrbu%cEE&^l3\,!欉&-; jp7!1=[BE?9ˆ/$꿢XϕƬbڦ&@t9bDж%lqaT,Hȟ/2_̾X9\+o+[KxFoBYr$Շ1r5s)Af/:n-oC(|vn[~aw6`'g6"!P6+9lp6qxceӅrڰqHj1흼F.[%C !ZoC? 1Ψ&Av=Oˆl%> Q T <R"fE&G艄ƝB -P\ڦuth>XI-]e)9[Xکcesh^oqtHLr%ɢ?{掆5$EEiu*`2.A:n:;4nUX `B-^\پ&&r^C*|7o. =R "v:&F008VE,@fo/a8jmѬIcXK*䀄; ʞ/gsi ]{IG-1 =%yK@8W[ y+/:61ZEP!}^Wk4ǙG,P2@/_`HQ4-pY;)2E{Z]N>@ԅBxe[%7CpDVvQY/Apiap;Dioy'7Es]Bg=gՒED?ŝU(f{Aqi^\=N!JVVh_gG>,jx?OSTɻj|; <&(9M(ZF< t1OX X]j\z;V]e̞bUŕONrDO|i.ɦUz~^..Q0blqoA;ޕ[}+/=UjI)`I9K_Fx3} x{@һOAK3;!hA&6Y{ȣpql^mRJbֈC\u,}qFG$5å7 $(,l):s#CЕܞJ&&n~ -8p6T$7Fp"{#C$%~X8/ W /rW=RhHdQ6L鸝!HI_A;|!%0G PlIvRA'xvO7U)DŚǍ"6<} #N.fe )Sq=i_kCh.I|o2| Rl?Øl(+Qx+T @؎zDdAf3τ,#VzBT)^m1R[#|cKV3{({*5aIHjxx0pTi㗊~_`i'kukxڅC9bі2IFL=UxtlXUdzx-hɜB ]6QZ62c i*py>~ׇ;K.vhj%UPn+悔5 #^(sjVGիeh=>?H ZJ Ό2}0p#9)- ^?ˀSu|xEƯ UX q.&:fުXjUZFe\B&`[OL w.;Х@9-[ĵF.-bIF1E$܋tEq&{H:e!|98Q/ƣCm#>M1mY~1MhQŝ{ QkHm4 pÆMfi+gS M̤~оie@'W'ߏ`3XaA*e%B $V:}/ypR{pC-\QhBR Cu>-.}2 ݒʹw̠11تr#^cbo(_g_~ĭhdMDg"c+RL8Y ]d3t&^Y5l]Lm=y&<$Z8nӫ>iQ׋!9Us@z3L d<Vbr_ a[Â,J%Tvۊq$bE&q?$11zcUr%QpEM]LIH+O+ #xXp["*2hXey@dM^ cQ;rV.aC_quSw&,f|-CpF] p&F+{fbѶ1.(^D|lO7+n5 AxW8O^r𯱝I'ԆVXyN^w&M^s 9KupGͷ`I&T21(p' 0pN l7}'$s+J D;(8ؕ[e`n>-0ҵ.-D֡rv`U ;1nkڃ$BWD"k`:ahӠ3@|cx,F<(c3嗇-D;g$9:}hWg|;LɹC䆧ex.B.%h3Q ~H1{~A/3MWUw$k%aWfc&Wq{va #l](I-+?׍ &x \Kdly4$(4~/Onfa3VԸŇ`et0YE@eGe~@0d6מy| 2u=]>z^Qٙ&9D9F nNҒ;Hd=`УWFMHN=Jx9=E,^-$d17栚W+J٫<&au%o@qR%s715T41 4B ~d̊_CX/)Sg0᠜;Yp( +}Q\:4S _{Vv<9wRT+OaЀ&%(v1 oŭ7ZOU6){cs""ĩ@kxTaeRSso""jL͠f*UuoykV.L9f26D;|F7 -u/pBHs-s8^c2(q9-ġ}"yqqySn,z{sA%x-~CZkR>ƞxpں%XR8%A ؼ)PCZ>V=Tn-rHXcԊ%U}.k-[gNumXZ^6-HI^X7V CXƃ #5!CG۹7+ihrt~@hwZ6:@YLlp3ynbQ3yLU2zɴ7D:qkۡATm?G`PS7y[hVԳb(Ӄ"ۄ4Kˠ#mgr poY Hu%~A[ᄙNQ .YcN 0C7_twܢNÀ& bTD¢N)Eҡ=;30=KCN%CI)(G:ts_J$Șnڽ_y]=&/*fanzmއܝrZ{et&-iW 7w嗱6]&1DL۞n9tY-;3?c6 k8 *(l7T:589Q$1$ ;k#ݑ~б)L4w>^Hq eM|ktZ-8h)1}NE,PCum T+w8Y O,eR8q+yW7C0٠Ob[S?wuF}ђf+!ڐe˧ v)ML&,T59wB8B.w%SQ@h2wTG̜ [ qa1F՜rlꔾ ɓ޿u?F<^ib`IC~K5Ky]'PGLEjx`|T^>F&jE^D]OǮ^9mXXzf ,{C)OVhjSƂ@CZ\Xmw~\)t;R7 IuR@nz#>{V si+0o\Cc4*6U t )"yWpcb8S~󬵾'YOٴB4&vqf['$ӠEm $#S`s{!A/)3EڿkĮޮ}r0v^Ŀ򦺪׆@ʸ+޹ MqFZB C(9j@gIJTh]ԚNvm, n ~ -,D 1RUl i4 NIdL2I<)~~<αLQw=;!܈+#D50v!4} Qs :*/`SatC[Lll]#a87ڱKާ&O( b, T=6.НN˳ŵZd3ݡ]ՖYɴd}dʤ皼˺ Wr&)n+rs1`kPG*8~9??+Qz"_檍szցYI j-mwȉID84[?Ig:4ʁ# AQY="Pfފy0q@ڎcF)8BX]&*!?L.R+5"k8byU.6C .@&#>1qSMH g|`:EyHr ez75ߵ kRrf1??Y/]'͚D+.IIQ# ǿfAAxANcZ)wy*6l+ +MRYsʮfp͈CҲUCM&>wxp`ϝ5/|hUzN|DpJ9?yp/BIw2J!-*?\ɢC_^Г)TBQ/&BK=,[bS/8z_%)g I*tyD@ƓМdfj" B51i4ewqM(,ݔsauN.پ,HM9-VX`,IvtYʾ"1k0< D&%]#kQ3 RËTjIsV ]δh(.sŹ?pM9Y e"R&@B?効|L\_iݺꓻ~hQGh[z4+*KumJe۹٣Ðz:]{iNPpUɓwv2ǵgP5t_'Z:}g 1EN֪>T8ER?,j !W;kMee?$.ByrqKj$b մ[-fI2=Hڹn'9lA@r]`olwT dTs43j#`x7XCJ,2p[mjHUTNq0g+ H`Rx/yK;'UnjFݴAuLR/os殃L˘M,ǣ\>i'@}e8/_zlS96{&F'!h2jU=v~+Ww9-ŒMHbI[FA&=B88{̨670ˮj|jQNUKRek&(Y!buQZvf,YŞ!|CZ*.QR%;cfz咊#1%V!*cSnm!US ij6ʉ\i){Υ9#A[ %\f1<&6hYUvˆcduݻP1 ~zX0BZM5N%(*6)ytb&W=Ac8WP;A4ڙO`EyzS$nL!@!x N lKd 7@w7+֗!Pq{;xH ɇOSW0K$BR֮"N%Sϰ)NFc(CD_s>_}0u8W>YPvʡJ=EJ&x2 |w ¯2ٸiE?ٕj 9gR?*yԽKfϱ OzkB#,y{y~t$7㴖Č;Y Q*ƨIk aIn`r`CS)ETY3y@`B4q1K@ QawEGڊaKj Je0= &L?I{BKpِl~[ wkN3*L۩gz>*(ثV)taG2 "{Zؗ,aRu ~>@ACM0y)ѠB桒*O2T:dYm Yqu0k$SrW&Wz4iMЫ8qfx+H~<󅜗p֡C\C&㻑tp>rI6@]sQJIWi0gy07I姛5vW^\toBm0dE;n0Oa,^iSrycN Ǚ;6L6T@EBcU]'R`<(@<`&t^4 &_HolD4V=OX} J:"Mz|A%x,JUb~R0ay9>+ whwL_}TYx0k XN&fJ0!Cogyv6l?uetӘ<Ҩ)JJ=Z@1}%&%}_ _ԡ] )fV}ncŒ Vl<&k<&%r= E@rF08a@,(kSD+@Z?@ڂ"Rncݝl'GeAn2ղr2 wyͫHҩ`FoOV^7ٙL4EH <cP>PAA+d mi6MCQÙ 4u0ymqՒ7!Y'Z*k_QUGS:'F.?RY*'Ø\YD/o/t!-o|wu=--1B @V)`6r%مBѳU#{lzyW)R\֗U!E$pIEWHȺ*(\=^ WF z_}DK F:uPzWaDv9$c8j1)4HuDmh*hЊ㖼Ĵ ͵KO ^K*_1vk;IQ,x)p ,w/q̵fӌQ 褍C~EU"aY@27)Ô5Ԑ|pt.ޒT )-B-o{DD쵆E0q΁ qLL6gRceP ZxeKkC<|Y[/\f4y:L;}y |#U͒ԟ_>>n1g3jE8\2|/%%(lyȝ`T$*d pU._PNW}?s@S(B"sފj:zhJ ,2HƩyQnJ>75`N| 7hg'aiHy?N|=80\Ճ-߱rq&zgq/J@?F e+CC@j.nR9Xg3 ndOET8u0p3ލBrLmq%@󱟳2@z$ +hch=$BQ=ϖX?N345W2.}i6NO([t=x2ًmB8ɀ $YLD[Z,{Xu"){O{R)%lMfr¶v2r|d{kWEGg xhu(听þ*x5e87ȡ"/!~0 KmNwIL 7 <ʬ` Uaj-%[dž[ɫ+@H/TP m , c #/6m@82)Pr@ojc.JWH Fƚ:m_iܾaaf`?P)Rq]wq~q!!(7+g~k%n Uajj݀4}dx+;2F,bRR! yu=}}6 YY=9bY+b. 9 |AZ(%nB'xx1(__ A@̱Rϱe0/(^aXױz7:SJUhgSܐa yC8Zj~!oO+>@nfќ-PmOzvz^wȖ}E&c1x^*$&lqğpx"'@_53'.XH@Hgx$cIQ/sRQW3RO;5L38n߯[M .* bDм–|gt!m#׋6~AZxQ#ll#dWT1#~o*Rʜłif4t#3f'MqXȔ%i>Y*yI^Gl1(D;s`7Fϐ dX7ef~Ȃާ_(n0%!8\IJ6U8N:s/1L\|C$3ELl38eDpud+>0ǟaOD1zg+W-*@0|uڄ X͹[3^@LҒ)nRFVmwoFPc!FϧjPӤ3w #\XMkŨ,\FrZq7́䭊ZIIݷaf`J|"i7)1Q \> lCH6WU ڄ\ &ꑢǰ+,yRI&  zοҴw"$Cs)B|Vߤ"(dR>Cϲ`+4ziX~:aGǙXB"OД4 7y3£?1 L1 A vW\aqz.샜 Z+-O0q(]cyk.8tt2} ֐(dy¥A#̵AAXpegwi^"';bE֒6Pԅm~6ECc$+]bxпK.^PZzO1`U,?@^TS@|kdF"*dg6A晥dh.ʕɛJd 34y%LĆ!s]n#w푚 Б׺7x'y#{WR?]X$wIh,\ r6(뢕P18ZaS0E_"aa0X mБ3/0~¾W `rowK~0Bu\*NFV?+Ķ4H][@ qܛMD7#kfJcBK{ICvFs}K"]תHc[AT1SQ7>@#h7y)yk"oY%&ΓK}KG/>#yX6{S/ ܔBbpd|@Cx@ȓZWKNK`/UY\jy٢'+AU3Cjdz󜍟 bgԿժeBO\oCM)"{ד3>J/_*;`3%)CT)xRlD`P (LNZy,=fsL@]P(rΚd[Ay)e^kod]ᄇsꊼ !Ka^_K<]?V~l%3z$T5\@<> vH|3=rF͆eaM-|`VPnwgf'mD>C{I}+xM0%|<R\bacϗ|#B>t~tۨ6Iz:)} $Nfˈ'Φ:O9 7[`F0L&H}!QE"Xh3 PrzWpfX<uC]X}ir4'$dsbWB6{^:u%oxs`Reo[ٸ1pXSIR&iڶXE4dcj弍mݠ 3wyVa &-%U!pN?ҾT: RbוeFKQbb`Q߫=$y 7Rg e-w>SP\Zerd~c4_,gpMkrU׋^V>YWNDb#!VICϟrYXfl*o(1!z<<6ImŽpDdqvM%F Vݴ)GkR3!a%T,yE䇍6mֵWβXż8l}~ Vhr!F[Śdz!@GWё5\1PC-NQsc pǻ@mk+ 'lӠ>epJљ.@{ /%xX"侣߀J.ߵWӨa{f9&1|:4x O;rm^mv^], -DK^u86rCe͜jip]po\w 2x*Am#*A:~G/6!z-CͥG]J{.T.=65(Ј$bJgoaOU3lhRid1_[OJPh }'.tNϷG]Nij =|B{WA(Og/GUl=xqؒW2@ד;[qt'Bds rdcWudHVև(-bJP{/ݐж2$c s8IɉݭoUU)oH F4, 9lҡ#? C0t_wm(Iio2glɦ),Lf*Bu׾*<4K]KUɊvnD%~E(XCB;^' PTA P~Hco\Ҧ6+ 5 AiHBpҐ<꾫{ehFӖBH̥#g7M(8l~I*6.leMd3#.S6@vSR,HEVIF~N=j=DZ@ ܃,gwu725,9,͜A\oޞFТ4|ŬN(8b+ Y(q:P5z$h(CO }mڧ"znS 4vMG7Cy(6FhA*6)B"dLBI5}3en?l)SL Wd(i=0W^IvvZA5xl2/%*kԍ޷Oz#)ym/HY0p(a롧_Il*\UgؑKNT3OFК07L h\˹==YӀYb9΄aaC@$: ,b mM2/^ yo->8LEt ihOpc2<;aƒ pу$mIoXrJ[Rmxe+{Jv66rpxRzcCx~ <_-#J"$7 ngb:`t6zAE! |ķGyD7DF(%|~W~%4qU+# #|Ը^AsmOKH:E:4NwX.}{OO%{kS݇`5~̹x"թЂ+'[|g^˸r' ~К(faI$^w߂ĺ97oF?leRCk V#WaeԽz}ͻ#)-9\0KQP^* —9S̅|$ax3M7kNRq (2X.6ޣL0o>J$B'ː않}m#HjI/QQT̿\z~SM$ԆBP;eA']N{Y,/еQBBk*&uqeqW@ Em8"&:(T~; #l21mG5KQڅ[);WH!:E,>Wm;㡷DT q\(v@[G.F;'hLGD: \]P9H̓<6YX$r\ºG۲; FU=a]nUou4?Ds0,O-AhùUwC1I,쭚XFGdt.nv <-1LXEu `ww4a5ϔQT<.H ۡԏ<+nv>a?/#eWH>qZ%M QSf|qZQ~?L@a=la04rX \G}䎷90?3[tG?~Dt#"|2UpĦ8x*r$SgL*kljݯ[T[Ӹyh.MJw*3fZtrŮ}7!Fj&gNInπ~4&p?GCrsloԞ;; W= `'n2. DӪvmÔB>Hƶ"H}<ȮpxZbuȑc\4qdܮ4q9xF̈́ѹ#.Oȃo?^Ee{gjCg ^6al©h;zl w> Q}PYwG+bu4fhn:0Cq/dYa )D$K+4zнXys.Wrī¡%D[k`(e"ϊЭ7C HNw]E?efZ"9N8KAεoy|ëᤥBTz&c<;9R0}P'0ᏵT;H銔b6Ldbˑ,aT.Q<_?mi%d'#Iec9.IA$'55\$=Ʋ f 2Ä.j~=YT?U5a*?!2& @0f猠CA{],RO qMF ̤npk{u5 —iR ]n/M}ձ14Q42FC}fpRK"=9Ryf^Ia +;mT홙]S5kayIR3<;o4l,Fr=t':'aL {jZ3EqԤYM'jNX*eY<QqW?eJG>2 I@ +E%ڰuomr%x-13de0ۉ0* cGrQ{,5t*mck {hl/Q:~-h Np2o3 -RqeЄ`yA7I"kB G2D;tÔpBۛ TdjTNw* xr8}X  wIziC7q@VչSE~O@$HIY`8RL^E= k+GL' +p1 u tePޱ()UQ*zGf!?@|a(PTfPRCOQ=cq\zf:140 :5(c505AK6CCFb"qlkdc,pN;H8%MNCotW@sh} d0ZNv'{AîWv˻Op5u"IR_ĬѴbv\Iڍ72(Tib"ZRZ\ϻ/!ch йyn}~.A=2Iq5m %c'̩EZckH=ZO0՟VXӱ4΀`KEP35 xj\FsRFMOP>'ą^ F[i±phj4L,[;VN/Ix^Ƹ5>| CWgC0ěOe+4o:r1KWٸט&~)<͜frCGղy_~/xI aeUYQmnjMIt>J:c8f:m^os~o^n\ p^&jWXsyUt?p'@>Dq⡯5 GF;cUi+s4TS HY|v8aTQDYt+ܪ}uۍ8D )<3Kܒr%`1sYkrPq׬x ̉Kb.iB) cP/0f*OI07F4MtX߇=cxE' QF$WWWlQG?:_WEY$p"Qdž\M>b[(<[K{*Vzͷ5D?H2() K*X1cD`~i9#ԡW8OɒҜSPA @2®#h50NqrҞZ mBU6%7q,߼Un{]5_NݟXsgwuXD|dvQ*+%5S[`Z> ,}-_"Z؎s yVR> @1`:Z)|%t_-YTw*՚7]˙bsx-q$B%}hًaI:EϤ-e ͷsn76LG_+2 F {g'єf/*Ńԇ_!IWim8t'VIh"| ]=ˉ{+;'MfdHGW ?hGNZsPjY#G⳥ ppU0#,33[;" x(rTkp)bDw`8eq4\SѼ55Puά$q 3?~xN v/dd ia4丒1땋߈}~Lzdti@gjQj37 dGd{lnbmgK(Pep5L!pGeWh1 A(*K(ΦU᦭ʉ#M WUPNE,\/i Q6Vn+DQP)}';r{Y?T&$<Ԓ_JVEw+Dw-ZZ`'?[;h&o@ f 4q-dO}%Dy ߴv".mv\_; i\|'^PJE-s05i:V$O, ={:(إ&\8}Wp~$(Y*"鈳Уn;8c` a1.tAIpLGN[,QW~n ⵳Hѕ70=f͓L~F7 k^S}$ hZ S[R)Àל" {7K+!́Lxo?ʃk=5mek.~BX~O R=_m{}e&TumE<&*j3Hg7<P c*tvV%"8Qb7Z{1|/ä,5h m'iTlb ƶ }央df\ZO0M2gRiG:@iխm i腌!d+C oF> {l-VWr%lCs%5|nXϬa-/?RPj#^~X@ @4>?]tV{GÖ}gNlԮjp5<ayx]@3Zv 6 2YX~(tX_'jb Bv' T싎<@2̧Zv3VxCCoB 3{dzZtָ].~>O>fq^Bq] 'ՋF:$߶YSI%s<*rI;+*C#U7l746|Ljk(.=xv6oFz()SՖV}ԵWi;Ph 9;段|LJ@b1 {{X< PAEbZԔ;>4:8^.ƻa/"WDhu ,o&]q5rEO]fkS| H6ᯛ}*#4z\q`GMtNB EiiywF,3pV8R*ݣ + W^J[= MLIȑ`LQI5v_N ۀp$kPa;|Rƍ4# |kP~S@,C]8 f[bU;KV'@{27W}2$|8q !` گ(,M,;4gd6꿚"jfKCW– 9m߇ _qg$tF>QK4g;Fms@;!Lxpڙ1 87U[q)^5vBLt!4&; 9D@A0䧇Y+`u`Nn!k_~m r0VinU*ș C_;a7k"v_EiWXJ~D~6|.m )p8v*{GIעB*Jȫ,ip|CEO67f_5!i{)OaQx,6j/TMqĽw= $ju;tQ]ty=o;88^Y;R*1)Hdl"xrtJsC{v?M/昷 `cךz+ z"xOAۥ{,>#;;s='@U'6uJB7r6;+LHB7arQİ }㊅vx܀t ݥnx%@mzMRߔ!9{IJ -%vGjESgcao&K{&ٶ=gR}T]~TſANgU0# .|jo<ʏWL㿛X>̬~Z 0zF/CEU cY-!BVQ-iNG"  3 pD`p/b| $Xague,|*}u\йAݼ]wCKò!閞G Yb%[S~ޜH~BN(.i̯_Xqeb]8LP^?{t&\}hb6m HlRC/g7C]#OOt^dn4 kp,2OGS1*蹏z"#aa{nfnogRǏ] A]1r'}[ -tfEL/ұ>(t~ՠku}HN!*ΞtCx3)CK_V}іuV(6`"[t !;C'f֧Qi>_*EUQBj>R)'Z_! e<^qAؑDX R2y}|YEaO18i:7=)# 6=6(-L_m˳FwSjS;P[`MwKI.oRJN.ΟY:(Џr}±~6GF}UFײ0\ϥ\!Vt2|+bxxu ^M2yOyUrZV2v2FGe>Y~ߙ=36jEw؎@Wxٱe2A5Eび6SigluJo=Kf8hb8(맫ZFdJҮ(59l3<8N)Dztxͣ'wL z5N\UiYSgl/J%:oG"8| NHFL(\d.jgL[r0噇hE e:OHgٔ KۀM ؕ~w ӋsNK"ਔ S&ܖfW5jx2;LbEťm?sC}FΊ۷A&ѷy]L_-B\bfkľ} ,w Cb٢%h)ܿv޿Ü P쀣ea-J%QRեCD7b T"@4#tL5$|J0y=uSdmvoV[d{iڭ AA(0e eʈT x5?)=u6;H_:|! .cMݴ~8Y]MH;0eM1u̒͒?*ӮK #ڝ%etdI'VJPvSJ2J:!^jgu|ROOJhgSHTeVUd! Q%8noTma!ݴA(lf-*\u-ciY_5#?<JTʓyT.{( A,9as 0&*6䝷{ĞCrq+8=2w #9\LhEMHaf;-ŞОyysx"]G|X A{Vf۰m7 YtӟN*E RSL˻xsX -u<% P'_+`' fmly%JKe!dkbĤ@L0(o lVٸJi*K-S€壕*!OXzѮ>oQ$o|y2Clϱ~x~Ftct]?sTKg'gᾗhN2Ӟ :t9#;93hP]0)[8$@ɐ{Nc) 6tõ>H9azזƥSy #O ^b|T?}fwU;3Fŗ.?P1) qs%pf,tWJX@ [klBզ#}N\v6l6|֜*aizD coY:MLM@ք{z_ 5 r&CӴҹ(jBA5Q,5omoM:]Չn4T/RM-F]|1=G$LeY<>ʡȩtTP6o2 F,-m&*O}[ "3{߮7$Ϣ̈2EdIWjt(R##r4cMJVVFȲͬS8t$׋C3z LU fv 4D4lz]sztoDDvm`J/3̡1$Fo_(!R"i&9_8+ds}䅩#@c#,~q(v=NoVr,́n"c}'T@veA[Zi,űQL4"(W%B/[םXvEϋU= uvhgfc{HݕB;~'|邀pƳO_+Yɱlwu(0-֓tHҭ\cD~g;0)E:>cWZmuոrt|j  U)Sp~$)#~ R?yfI|&T ަbd|[+' c }/*5$6d[r-]DUdM0hqhq.lA}\LM1)l~6ӷ *mV[0ԍ/0(@׽UUْGg5p'2&s%eIMܣ?* ) 1#)Jj&E\vq%")^R!>62kAT럁Za[f8$l’̖ɇJ]:qсEgC.)Ҝpdn.f 1?b"-M*"˺1H7Ԙ@/m Nf֟Ln-Z! ěq8c6G_ί*12_["ƦoR6k{_}$z:Kf :\p`<>Fn&9*3h+~ v+?g40RjA䄖8RH yx"ˉ-@K\fV",colrE3"Ě:{t=֤gm_Ɩϧu1nIts@u | *>8R#攭R{esfaxdlI;[ ^ { */MLK+}+T&[A/p֖Mut[=<2p汛:""+bK҅NKރrN=4AMӜI"*M*x7Kb*ˡaN4 aӱv5xSGx9Q8 bSx*ze}gclz)[)DybI:%ju;N=!fk5/ {V+Y̙)q7i[X'uVRҋqYZv-W? QYNIےƽo2(z{!Ќ#rx5T_AJPHgp_^9pE 3tqhq@̂ۼK44+XƬʛ/DE|Yj>1IaxC+N%޻ޛ lWSp)*CAƹ815@Ǩ)&ɲdƔy KLP~!'A>xTK6>w䅬{ʤ@6-$Ԅm4_3ZzE/CLPW`seac8H esAH'84l>HgTyɴi槶0b$vF o59*jA]$2Y~G(,q8B\ٳw@8C[,z#12Vӊ74/;J9֤?8#J~نNL~H2[)jb¨į LAQTAleEXMEٕl00?HpDWj\Tʩ-8M4F_~cVWl$jnR No%omX]#'xRzr7*&{}>DvQ;fpf5ۆ]\@U(mUEX[:} 8Ji=jŪO<;'V^WH䑃p5ҳ^Ț^G*};NbMS-F69fs.& >ApԌ{rMݬ 125pi辰?0s9 pn!Z0Bm8udC*4)Xp'0 O`$C*K'E?PS.宛~І5eE|84wJ'TYR RGH%mQ8:UQ1y 1EX"@>W5PݑYMQnIw-^ st*.x`;ފ=VRt5boXPMϑeMN{'(q6sHfj9k? W)udYR7~b\5,xraSq1t̬'yUvPS~B_v+5 ^Q3`6@8ѡhd>ja0w;f;5 |sdZ k.ޤ>YtiDs^Iho>} {7J%jib?k@܏YnC y%%L㱇J$[N #5w ϔzd8`DvxECD|"=EmP`'y=jgBfa1]A[X4!NHLqSCyU\!c ޽b))+U7Y4\3 A#/Efҕ<驏#௕Nk/ܚ]U3^wXf%pI2[c9xU%l4u8)5Dބ /a<Ũ_n0C%bٌ;.UZBAՍJqG#Plc*IYW Vspf[^Uw&yF =B뚭I{->;ƫΆ@jVwA܌ l7Abӕ aO~>`L%_~[tQ<(At[܅΍ 5kmVVDIh8 wOP$m~FF9Ŧy̢e=uYfX=zco -$u<2*N'џm,xZ- 2qJ~ r%筸r!r,lKzO I8=BvrI;8Zd5x{s(: 1wrj _H fBum~rDX>a3w5uU K h+2Y/%涛8Ljג6C$p fE˙PX0C{]e[tG咞/JR6R(s슐:Px K/1FߌB1 6#Uy}m"k$@=z< ?36ݩ0) Ifl :e*O^k_Ma^kzr{8rɰ7f4/LXធHxFnn83T|0,ȝm@awG(5#gŌl-[ZS^u1%v< "T?H/^eZTa\X75{Z܎Pu/.afz[no-yJB`p^A')nI1G,T(Yr<*KTDh﫲[;V$aA3g +B z+Q|<{7d@T#sN޸vϵIw+?##FefZd;BQ~[*T3y YH ɇPЄ\,wtOJse'n.'Y%o_#(Z3|ڮz%EI.W `ik*^lxþyPw'7N)0U׍O6 gNqtq>ت\5Чr@CYv%q X&W5>.PGcC4:ZT*휍~ `Qeda׾oQ_<~GQ}q:މ{4)gI{'>vQ;/֮ϭAa%]EFUxau/>d1`KlԀ.FI#dwNƪw.aH?+|vu>wF*0{O:tI=^;_i*8 ZmW|76TV;@BI/0,.ȉO5MG #WZ}3pJ^-cTKi9 3`j`wGPA5B]vzJ RVqpy:AD!<ϱK}ېde+(d^>͇)tUm/ue$~|(FhL%6g-i-vUuW\ʕ ~nn.qICo:|:ܶdk>1dʾ,,Li ({.G /<>P0Bs`]_[A* A/!T]\!W5r&4_H g6mQ8t2T,ܱ qt­_q;:.QSO^ay :9!.6KX| ;ALzGAa$NnkP"v8z'|R.`hTT_u y}im /*;!J:*xB5"1o5HC5Rl`X([di6 6M>M lP)A]Esj"Q2+1x*UgZJʙy eȲ遟/4_4.QK r(g* ܴSVJK'MN_4"I搱VuR3~щ.ty"Y,TG.zKZu4)zb2wl-J jI)<_U"7*%p*6Oa0cڭXJC(DP3{YVbPV}D\5#CX|)=@x~uNY!Pg!I43&LF0мvNَ6W-/f&Zsfk;响#H<8e/P VaaPQL1NW n4j+vYJg/#GX𞧝l 9b_ N!ISI5x3 C[D3CD]?ܮCլqbY jj27K+c(մ ,SӬcHnahOP*l1IRHJ ״Aߺ \1?$%$zqE]EOlS)(QeVs Hߏ9暪6GZUYhB>;3C"2W{ =)iAevcĢPy Yਥ>8YyG&WQd"ƹP0)nTAL kܸs 10gn آT/QMrptb_6gt7eH`Rȉa97%;1YM!wKsÒ V$58> b˫}B >nA(cu{Xg_al6Im_K:hj,e̒Ҥ\z5=pPGSmFІsY|z:3u?Q1/o@zf@}d"OhU\kc9ښ(25}v/xkz t5U\O'Zz^~qj"{ g ǝ1yl]r-$נ#+pVWq{ƇT43UN!< auv)-ЖT`+p q6wg6p;rDPZvc9RrOBKsi%w3C\8`sZMkhR~&9p.{S?/|#rV>|҂vv{r;~j P.v}}:2 o šV)L2ז;#γ/c }`{_-&oPՂSYnp^6COzG?RԇŵEl_J暔%Uf{}7ō97[4zĿpuQf7F\0},9b Njސ0_Y`ܓID]_ãu$d/ eN8:b,}N~ !,KIDYeq\KR i'JITq:.UπOAE&rźj\q|ɃarE1NPH[HEc%J|Q`I\,~B9U~cހwj wwZ==x7NY\>5RRES\Rm+ h^[ҪhWIZ#Vg[]tw:+T/a,\gu\JMT*6:S˹;yUTQwJ:Jk[ D@̵pl._z; {,΅0\uR{#TZE*2]ֺ'fIT=j1C_/rEѢw;\k:3Ror$y=&#U܄}-h=ؤ 1n/0ba={IDUjU\=s#.j>Lg1N5S^"1b;M5?5Cv-'XMiqP2_$FU}7+YdaE;^=++>Qmp=HFSh;t)Wn \#gC s]cP$ /RL|FB;m 2B<-q¶>$kMSTkI5fuY O >% YeCViс r&S~(,0HQb:z~ #{Y eRj:hDCK:7DnQD}QԎPȹ#ކzib`JLDS9!W>E<NIN  0. S !Z͟*Rbvpy$$-m h3~)+֔l WcHYS#ע&\MճiPyc)_3͢6 "91E=gH;֙ 2]6DŦ48he*I4+g.z9 k6ZTe\@L`H]򎪩!p/U]m>z!Ael/=K-]@R#>8%ȩdyF9:0b`wZ2z6(_zwK4nAxiU'JuvU7[ &@]g/$fFo382ҡj"ޗ[Lf\K8fn*S5}rȎr@`M%~:9ͬ`yw:;p8νGuc6!N[ r)6DHTQ .%NP]UǠwtl~i'~Mv czn~o]yhcvCž} %7*m.ozSe vj!V#i%x|RlkZՉ,+k5} yx oKJAP9hӶV0(x/Cەt+@UYi+Bj|ڑ_0:-C :Nw L9|{H}0 Ĕ9yCaUqt~_<{Pt_Tom8(C@Gp}.bf zUIlԽzIAg'ҫhXt^8Ѵ_Bue\ô+꼤WCl-"X+dwr/|j$kFIG> $kmwۊimB "N' ZBfNTi:z\i8'BM롂_1١:=oe< QSeǰǫ\z >wU<kG~%QmlnG6WpLxW{Ϻ*牿ft2b#ڡ¤vXأ]';PM$ss*]+ېo z`kM5cj-X3#4Ċry54=!k*qfbIiPs/{tu"M"ƽgq 뜾~ 6>Ҕ ꑁäX1]P׊bܴ0DĐR5fO $kP[YbXJ_,?L̫sH{n5B'?'kT: }2Jȸg e)- d J[/>]i63u{bUz:˺EjHE˩FI>:7z͵!qԹ !܄ #wlY#g%^נk~`=>*XCX 54U` IV~5*6j{kz~sNfoz^*(Y"eR1\C }N$>.mc̘Bd\,Sc/^X7խkQvkwV_aaؽfjk쎎% EmeE+)0T< ߂:rPoP\ H/mŢai|:z4=©=؍m⼱~K`0Fz\6lS{tm?^E<9Qբ(vB)#DuvMzF!KOy 5C°> {Br/B%䉲 ƒ;(_ ~(o 扽NmD[Ї#mzR1?8v?\y;y8p&d. .] wuhu5l+l >:92s{HVq设 C:'5]S$㗡5xĖii\˿(']ce͗ҥLjA=\*M<7qgiDV;D3@QxlbD"NO}yâi٫=x#>#"u_&B 91`p9 _<UMOм]]yK^PӱR;MeHyvru))82 )KKsޙʬo~%" 7Ъzi^mi X[6P˻Od-D3)b{ߗ~x]Z;4/傓beaL @d:] , !o7紑\8:٭ uM"DvgLo_+׉t~5xlwUYxml锘xh=Hڳ91DZ5z.cm.d0~y#!谩^b=ӈ@2lP'X7] ue]ҩ5=,?Le0fNKhBSU%wCѽ1Ƌ~4R PC{d:+n "i%+Z}C?ֲ.Ɯz岛p!"-f%EEdcB`EVLLJ3%.㝬d_ %**v 4xWV*lu'HXU/ե~="B%Ny>r3Yq\1&OB0d>܆ɞ %b@VHoEFfJ$̲p%".0}2Z v]=#iEl)_B<ׁGM*Vi GP^a_#SJ8)[~Pkn'zj$%"#k!֣148 o)~:*k5[n\=!xG+33ޅO^i~1$U,Pș Q 0,-8* ]D6@="@!QIz]YKB VR&ˍl+/?8yyC<ڔL#i#J̞]PB !ϐU(ȩPmHafd9!XY^O=/̰UeN"=#"3*tciEl L"@E1؟JB'uh$a}vw{ dO7`n{J.. weC"_=` og)"PNgC.D#2`tb~BZti@3ޥk;&686T,5[c3w.\ʀ*Ab'cPyOK7D^.L? %8.؅h04"KacFBitFDV% s˨ 3.5 GP@rAKt/Oxk\tJ>И~ xޗ)MObq 0+aWlyFߖ"ShP]U5A AfS1 gG_Enh$6~kR_1uc(r̰* ^p iFԦhbϟ"XCyj* 8z(m5`(` `'Zq5)֘x xͭĀWqdCQa*_A;qOp$@NxOp>wu@h}!cbD.&WP:^ja1SRr>7ժT Zj4*oP>c,x y:J!@AD1<&X輮N|.|}kHD=i-P<Uy~2LRΡFZ;-yCJC8V$!xdI5Nc7VI'G uN5>Wo85*J1{{L5byrJq eGM+R㑴LR ?EHvF/O{^Ipk sMd@BUH UstYbcb}üîw/xTr\-XNQ+)^4]D} ª#x^Ҁ'} lajL*2 {.ttȘR:fe;HM(LVu;j`^~;X!$N lm_@꩐{OncuzM홡eg,6pَෳ9L}kqL7M+[mMYbh6q]@~!&$Kb*4t:/lҳC9GNOW:Y"PӃ[v Ca2WYx6F }FhmXs%+{D~"3碘)/mzO:Y $o$D (n- h*`tHmWk"kT9֮5̜AAj1`Yatncf\!s}~2&N:LMX¤2QycE×%QHhօvEbiV֛A^N<|$3 LGAzI6ņ _VnNcW܌^^ZY{~.J @kS@ܚO$l=F!;b*G]%]wYG縪+~ nUk"kNrh $a BQOh_g_'/|,|5n΍aWfT!NEh|AzE [Eqcl֑Ss[?Q[#aHD6a (]c p#ѿ{7>$46shh D7rŃJ k9IBf#Hގs;'z]\_\kj>'}fm\耖.aXO8Yrȿa?`/,G[A`t ֗OwH&!*`j Y('/1Wese#v{NhVKeŶ6OPG5X#|TclrVQ^qOasJU;(Z4u#;A J!9sccOh2l̊ LjZq%uw .0 c8Y\Cvf>14bVVJltZ]) 9 = )uցCZ,>HPd3>uQ˻t"U#*T 虹 5-E-^ۉ ࣌;MRqĒu/Fb}ݳ{FbDĪ}b11lu5ϧj4Qt >pWJM9Ą |}^nw&cTAöWy*a!q}Y6:mPZT|t`R@0sh0G2l3~/?(Ì<3m2/sK{9~bSd2~*UpJa&_RhϞ)%H_T/a!őĶ' R9j_NU2 g/ _GJ![w`ȳ82 hG$JQ| Xj> J6E}Z=AgK('هps[ťjtU?*?pKRo!@F~A6AL1[)m<it>{e} s`q5Xj_hrT6:MgB&#L JZ*lY2a3=-E0Ք~D ^fg1ٛkP::5v[c"ٱ{ELy{4tq4į],"~H}_CCXMuH%p1rD@gx܊(G.O z!LsR'ݓU2jGyi6*u&yys+"@fR$P}uo_Hgͷ1iLK!4P4`:ܯ;]}TL2$HbrlxDHN?ܝRwl310]co˞Seuaj|E}.zb\R(Q:F6;mF ۩訡W'?VACeiu8PJѭt6nT-L0:>a| 31@N7qW:;uA͐a{0QWLtIZnLjY ^gts' 4T"1sg}ԤkAB2sp~#L8@~|_2C =MRVTKzұ]|Wev F3S_"GC*׎OeH""ވ_lW:[6 \u2R͜L AJg٤7znCo-"lGݧ֮v {qGֈ ۠?+{mQԊnB1Aޝmm)] U3)Rfl5T f2RVɗmYmb;\Lr)uKUzDp#cQ2)Y#$:fK]s c39HHE=^|ӞaU*dțħZEB H7Epp\ufD\BA.czk8Ä^~0W9jگCqa&Hh9}I څisZTd2=3 Ugi!՗*^$jQ۫p9-B?}AvlKWe|…b57PpJ)5ȶޮ||X/9טF PJ.)ߕ@ܔiEkࡊ"td?M`3eeqa ,&ȋWN.6=9 $6pqyL />>s cH?Q!l~$ ЙG] ADap*$R9b& ڼyJnPAu՟gHζ435օ9tQ>ׅ][>X5*2F!rΥ` LAB"ց-z}gmz:f+tte-AC/L" ʑI+,#14P+VWkƶUIsi0y!QCv^l:Љ~Í2Bb $ wBߓƆ=N?ۡ w"Db\r,ML q^{>>e}SMq'6DJlA+JҖU)CRkb(<(J xw4^7.`^#$ڞ y<9OBo=IWfu hNzř ~6k*JWp4̫-_{Axyq63f-p&^vy~?yԏ4A$J-|4ʂkQhZ3 *)b&#dt6J*([ņɧVxLpH4iB̋ Ch O -;joR}}8 |Y39}oлRBF[\b .:(Y1tZoCE4NwDB`W2xuƋo Fv+NsxkRX&WF>VrzQ{q"y\1ĻiD-oSJJ;}HҕkBAL8ؿJ#Ty#<֟Z8%d y1$/;ddxa~ Q'A퇆WPz9dݨmpiπv\T€} @R8ֵQzG*KE\'ԝW8^PI%`&} OzBV"jA1ެSf$2@ Bʈ#D%H.(!^W-p~FFSl36]Z4owIӋ?+RV|J9uT'?ɫ:iSɌ3)Ofbxy6lP)Ca4XLr:R2itzxΠ@#׾nJ,4o;}ʠ=+IdT!}R_sCQ426U﫲ԉ@^!q6yE`fc-ݿbNIR^)I^m939Xz ,m+ jdz)9*D8PϜb#m2 pks V h@ȅXބ xP8)pX=<Ks,k\EFP>R/E (֬)Jg[F3MMCJԇw=לZl%ud>iuqۍ%XҎp{)h' ckn i7d&jti^Cx:UOHh,ZyK;܈DAh*ô6TmV80G<|yuj((s̤5CZ g\w\qܶߨ`]SP]%C:{-L֙C%Zc0SEt>@#QcPr:(幷QYF1mK0q tE&55 C&GRkR! N1-iB^/B/I/nZ(>olM=;_T={_r's$QE2nLGRb.Zk}Z WKbgKU>NqBdGlN,=J@[uWK-̞e@IhNѪ*<#rg[]#л$kR OȌm,v){gZooWӈ&4A'aW:J&->Ri GN(5SMܜsW%<"t{giKs@eZ1A"*käo(rb>{2:ؽp^T X|.֚a n[UIRY=Ƴ RSKڦU&rjkf4ES~kZZf: ~ G2"iͳ2mCZc!$5( uVV,tĬ;h193N|Va`xį x&E ]6QChw7ʝ-='xy:JLO&I盄nc7s|#?ϱzP &Pa-xzK @`s2}BѡݼUbmBmNxppTed]k7: x,voͣՑdI)44aāHMĩr;wlXe$CG { 9#RG)U'`a0 >]2%ۥ/Tʧ ,V'? Э`fɈ"q;Mi%fd?<ʥ?76LvΨE5;x/i:ʪ=mpMBMDʺ93e-cXEƀ \JEB[◿H989 F~R'ʐE[qJ$G׺[ugYtd niˋadYq)Y. ?7 .c:\F zRpBaRΔ E>Rp맢dI/7m2`zf*N1e&TFS +PHz>%Lzme +Cq.BE DUY(CoTow\c愅T%z,Б';3%/9uaItUx@Ied#Xfifkm,x -~#gJJE>5PC+NPG3M=C(~ m վBn] 8b ?pSBhIU: k<b,9rq{#p5D[w{:5p[߼ .DEJuJiJ%ˉt%0= VRDV4<-&ei.gE܉ |`V|5`< YS;OГ&U!k BYc+}{<@i,8 'xLwTxK{p#@]S I]O=` vI`A7T?-GF3_nM,}0^>)W~i|C}jH {$Ii_I/;zioS'X+ƾ--1W>ݹ#: 5LPnɗ%>(uŨ$g4§s'RcdQvӻV |Oɬx Õɔ||I4  \ OJNSV 㲗iHgy|U)K4xĺ͓gtEkU?$CD Zw2ԄBR[75M)\K)~m 6>ׁ&(5na /=2M+WCFrk,zo.7X !fxs*I\ Ӎ&q_* d7;?ؠf0.kxʺԠE)X"+Vd!.`HBȯ!oBZqpC.`HՓ6J dدdlFL[1-ZUMu V]3TZP^%ߘ /hM͑k{ԭg?;ZpH <_@j@e>57z-BD{wM[wZ-~TM}żɁZmNэ%jq ( g24 ^탉ԃrAJS Z5F W׬6Wyn1kVU vмr6q@ P»ۀCK~1x\{ZO"Eb<Ҧې[d25m51Y`;e.#xgE6~Ƅ[ObV{pºrLiT+:&-7=,Y7Ou5K8&xeHm>jxӝy]_-U%mcа2Lj>fJeU.{v+ɜ寊'iuD8$"Qk}!^F,ٳ>n(7Hz4McSkPEHiN'>[ N,Fxxrcz2Q~{1γAM/UjVpXvL!Nkmַ~O)ZpYHڜXײx! {Q·U`gxna7Wxܸmn E?'  EP k?aƘh]Q4'0 64m$Egt4/cAXI* qfÅz;SC DЅ bx7ŏgUz#wai-s^ m<-YYg`*ڐ!7 5opF[7;z9g 6~R v tx)زʃ f<6\T4w^c](GhНX&0< ۽15dTnm"+Ј&\si+Ÿ c)l(ֆKc>_M6EIV+Fdɹ.')F'(ǴDXIL7.~ nq7nV9)L `f^VkH7=E)۶Uc?E[×AA{uхF1?5H9?PDLyqLG ;Aڛx#?T PM7Mhkcu/"^~q<+z i|*x'Fٮn9T4 <$pDs.kc6s ViyJqcDu2oMwYՌBF_ep\lƼQD7%Y p>s9V7P.O?k u-u}|Ns乗tP 8 0Kp)3@PEsW0DirD; o$lpQK ww 8iX6)s&۠y QM,p`]ɓr_leUy¨ pSs1;U' .QcIz1Ȼ8JzB*݅)*iSYHY0b5!$*֋`ȎG_+:CwZA%DL3̓L#vj³wK(EBKX2_U$F&͹wq h]UOo_29M\X@, /=S +sl}?61sǚ_pS+j)kCԛlo@kc50,*Rkr!ڋE Bԯ+=QPg^ߴgO gwl/m_-V%X\xW.Y { |iz[#aRD'%!7EIܰ\ s'il Ykr2LׂK=靮kžr~x>';\AjrxDXJJG>Y`>EnpjX1 ~\0ӡa+@iXm?$)CM%аdG!wc uSX袡ͼ̸h F2BOg]Nnss>/f1 S&-n_)#>y"X=M"w%TBTq_?,0 /^5a?Q6/FM!MS}}"ܱ8,ϫzs`V؀̯,3t_{R _*A ]j*L/ s@+w2s:Ul\Ѻ-"GZ;.HkaXg {Mu\_B@(|_943υ{g;#+=eҜ߲npA թtk(rۘ|C .n,(۲z=k mG9GP2#6<"yH? hN iy|}w zcZ $hzKWiXSʼnQUV1 ůW :$AjDS&W ˥"VlS(uXf9a>+o^f |xFkŃw1'0RS)2,Lбwoƚ.H=R٩_X_T̽YpS?&rbX὚Q I/dglj,_ 8MOk3nHyq[wNW233LŶIPٮ_]toL]z.䂲VVHݗ[So|PyTw|0hJ6/^Ehm,`v/S2 o,;W'Yz% $.V'EÐIcw=xbxʖQV4c_(Q3ͧHՅwARյxDuʰTq$%T#:5JX2A%*H U5_(xvn{pL@! i# a|Bw뵲IZy;5a,&f¼g03=u*?=$7վ JgbAJ{|C3UmTI!i~E[Džڿ;lV1\0c 9z4RAј) B6$` RɧՁ#*L39= ˷x 8i.qY:ƙEe]OkЏ[4F))U3Mo1l⾆[MoxL뇬22B^Qv]jf^hKrb%Hbe m{J^?TQp5CE~+} ]niVD"w wNi߻U2F㯨}V ,֯<|ڲǩOk5Qm[;лV@iyU-?m3Y!`2%%8"mձfZv iSQua8k>h*VeY,*V;_J{Wl>O,EnTK-q<kPxv~^(7EG?3`j;|&6ɪLԸ%T,)!4?XbVGuR*²ahe.-$ie>1ΔsE j uL,wE&v}f+Mo63T1 zZ3("ɽ͛buEu7GqL Oeu1Igii=̖ҼPPL>bzOG(|<ce),[3hcj(2cߡјR\G!A;+ x5N E4oOP#sgK5Pؖd243$]M;79N S`4a۪vG[zM]ґD?\"6۝[ lZ.>8ɵ 4]oqA3[wJkLYd ӕk/OP 4٫'OXФ b OM vp$v2F}/?rZEJZ5‡Œ$L\B<<0O$nG o`Hj{}:0^>C 'ƘHNy =Uj#f@M= T±[*/~9KSMtu)/_EtP8VS* Z^ HW>5@kaTkkB B_"F]pŁb>pgse@Z8ȃD ̎B炝OEO3ވ|L&呓&=|T+Ue ?պȯ(])cʗgNYJ٤V}RW{ ǂ&Z,1Hn%~.nC_T E(ur2g"õY}\ /*kL{Ğtnt18eԅ |%Tmm8zb;ƾv"QIhJ`W"ug:?:)EԢ\ &E2mAU?úuYYTOV:’LOkiua ݙq*d)BzuןIKr58un3 |ɡWSӨM]q{/c2A{͏O.oW<p/Τ竞D| \M@ 'iKIVSxUṅ4yx{^Dž}(,Bz@1yS ~?d3kk9l3zd'+i^m&` gŲA0zHvj SݣA$IyDޜm[QShE!ў'O#pص>֭%p\⢇^Xc8\WR DiHRVa9~(TAlxyߎj,&M Oygh̫;43ʱ/bp!`\~DRݤF>",L~ELP,]-n.vX(~!&xox Hs`gd8϶YRSx VͣNdPgrW€$1x~_FڕٝlS}ѣ_ >@VsWsե߻Hux6 P֋)-_~*cwra8S@ Di璬רFѿL]]NM-*IM7HơEtlަUamWgv͓GYdThW2}=-G*jK2BZv`-H鲱\h'GBwxcÙ;g벮Ѥ\Kk%&tH#nBosa֋/dBI-?D:.=}C-c'{)[ rT7=!'>sDba97ӧ\>Rk<|oB._ uBv0-UDVxɆjE%@0%Y]A JsHq ʝO %~~^Zy^tk9XЅAg'uSb/Gj|[/i$SZЦEw/T*jj`>Vf,*.ze [|Տ1*sg T9*xǓ,'t^ %U*1o&>DR}7^7d)H7mS7?ܔ7!rbA$L [b{dYN09G uDmv@:Z`rTx_&PKc4#%Hτ#Bu Cc#fcH_h6otnWO Kw5CSfGޤ6j uB?Bi&gqE>S%#_dA:&7YmHN6[i3K6{"|C~ PJ'yݥmZ7O}ER 6_2tYKG lG5 bvߕRBaۨ,&Q) SM:Y{?/&*&Lg%Չ.yA hF8euUq<~\@Ԫ0FAFPN92gTMb@fs tsF82J^3ߊx&lˤ?TTQY@ l/=F"1ZU}~4+ɂ&J ɌD9\$EL&̷Ѕ5 yl_p[;I5+e\p+IC2bHKxU%IsIҔOU^$fieįb A:[VwΞr2{u,H?4/a)TD~Yv ?nZ T\ćw%"Ԕn9ωcċF;T%רFzd 12u<~I_iE aŏDm-`WH$9 t1B4$ ߼GG 춈{AUͣE3]ƃ]xWLNc-pa4Ar OI#߱KAzêg匧["HrY{>K+<lq{Ȣ0^'B!WpfbwQ`Opό<4eH Č<dH<_& {yQݤM哥'_u Htfl"ʠ َy,Mn!L m`0Q{XI sR ]e)5SS@Hkc7:b&ĸpUY2ߗ^k0X\YؗpU b@[* @ڛX^Lص~[Eu7M#rWB d١@#nU}Ц*jYksmmD=c9L,֜>wc㏱f}ge$y1h-g04 `CD;hxʚi{V^Ä.*zޙ(4c o2a2E(&6FH ; Fc*:ãQƣw{+-^YAq9Y4mܪ&w`z|XJ^ޝr([+cX_ )~T?۶y\g/|9Ŀ.(EF*[`9X$+|E ۗuKY#{YX]N$hGUfW7*[s߲_^ӕ@^q9ˈ-@.΢֯2t5Md -⭑]C{`ʣ=8 ïГq"#2><O)/"Ս7N)j5UY,sJ ,d١RKzdx /5N os(yĖe-匲f% Y0|8f@q99ĸ26w6ԍ]ᕷޟN=FA _ȼd$t/ͬH:Ig v%y@w ĉ/#TAœoWp>CeTFߥi ITPEJ~>Ke+t "1 %kjX🚟(샛'x1Sn=w*d?=R_FcFģ!Ci2뤹 tx͟+Z@CNgիѰ5pɔg1jUV۾}M=p׵ɐ%'wKğ7/:`fNX&Bt?$.VLf5 C=@ݭ٫_i':~ s, McOPmecc5o3W+ɻP3 p?U= ?d6o'zzY4u6VF+*`XI2f6^^P CWL8Y N#w8zMkbz9KMmV=%ޫ>ꋉ6CA ‘A'eV 0-aO^7C*1i]&,[2 ./6,qN6ti7ı Q+*K3long1?rlWґ T"vzFݼuK Yk<h*-o8oaZȔbw>"-1K?t7|AߍP68_5"7!F18[s7^FFL<!*}fJGB,fY:C`: ܄R"|Pk ШwxUq:Dt|^4 Dr-\6c{+}.վ-t.jK9~|6+AܬS7EU9soU6 N6-ȯ0[9"@yl֕M\$LY=R-YFtt5lRREs2:@@bjݞs1 TL%ɈLDJ-@QxL !_%q,@ǞB05(.{o(bm˦?%290<}(AyxiMds.|H(*O]^&8xEV)bHy BOrH̉e;cd{>״"U=!kSk:U\R_+uy5 ?y ' o#r1 4 J,T= 8o[t>[hY:$lJ oVF*3WtQ*^#e4?H_AQ>Q9=A$:ʣyfjlWS3L{,XH,m(kMq&qB-P.p*({$7U8*?JPodkݨz$-'<b z6f"p1؝$[4)аy@H-d}βKd$ JU%G:F_^d3pB|!&;YNY#ŢhuĂW|^Cj AC[f1 Ee@3)9'YӷZd@.I3%mlj>IXI2j*!D^@áCӂ^i'j $Mֈ1\kܢ $dY wMB{@L+E]LN!^n yON,{|,[k B{{.:)Ó&Ty@Nj=KYƺ"))9!Ar2G z <Ï.z[,M܄ZFVb2Pj N0qp )FD;EEa6^WnaO$Xc25QshI?Ƌ͆{v8c$`V܄SaPIQCTnΔYLn,:pcKRϵOp۵@PYcpPN0y"6UQqH3ʲ9Z +nθfN0hp:A4[:S_|%j3ɬd|xwsIe#q8 &sf\֭(n%[~ DQ M FqlҦkI)zr|t|R6OXhb뻡7˰0xN! aFڨ(Ж4eJ ̐D˫6 Xڼ&TkVIa =Ru8̱Ww@XJJeu@C҅ۦ{;4$_꿯YT2CzކT"&n:$Q7{7+7rRVBe eKHps7詚Ɏ ^ ŰHn l-\u/j왙'+~vhۋi2pj~#ln̯GQ~\Lzsqrm#XŻ62GFffPK&L=,ⴗ3pOǜ ~ᷜl |FXHٜY0G߮I>B)MEO*]M m&&؈ZO%u8S-HIsK()T􆭟=-{>{~k Fp I]%ܠxUCʂ~hgEc Sta`Li$Rދ,*H.~2^p3>TZeAo 45MWNYtqnU  [{^\nf^bf7퉬Y:Vpd/K%}޽E;r iz!.~yљ @~.LFT;/ 7 Jf+]cI< q,v4tɜcbpږ['Gqmgg,H=Տ 5$0u]fIV7Mw6D).>Td{&".zA0,lT[R-#@}^?mkF˓r,m ܒ3UQԸ{X~1.p&b,'^C<-OwW\WC kZdTaP*wfho{\ŋY1M |)oH Yz rn[,bT&}WyؚN7A$G_&\vBi "We`@bL״ [p{׏XP:Y]=M?d PmĽwm *Ԕ]NECq,\=#ؾ 4_*զT܀eZAL8 7A2ktBl 1%d7]/EvčD{(r6iNJACQ@Ӿnh8O8@[)[uPNL;-CXk1C^ZD.&A|cuD".(i'9 ț}\CZ  KQ^@yY)nj5cN6e- "(۱衻E|qhͯ["R<,a,._Su2,ѸBj̖m8.;6b<ӌҬqe7m}l](2 i?:^ vA0-yɈ){5êskWҚԅr=n;GԗkR2E)v7zXMN-TbָPhswZO}wq]22RNI4>s#R+-DX`կ>a:aFXQq5ǰ0G^,9]0YT`~jP;JP<.݁ވKez.UIjV}pNi,Ir9$Lʰ:_XYI3kC=-›'P[0gɼR=ց7蓼zYBSN=+1W~ia*΋+*m 'O@'^>GU`K^F\iH8~ Au'e|qVpp{C6 Ő"=0B̠4SW NX`ǚ%EЙ^7  NRV >ڥI؜B)~yg.Қ|3 1l<42w<z'./Aw3{;#+Hr*tHǚ8l9ic >/Ú{P{jH*u?(){lOF%ݾw;=Z$Jr2:Ye+:ںpbmuF '~pIsVDF1x0•nE Iɛm_<2WIgԼDpqP}y*.C#Ot " j| QpBV7 T)o]5b]zƯ味+sџ 5MM9M-Ct땿 ,q`N{_^y dV+Ibؠ$=Cr{@$͟}AZqu­GcZRv je^LUw}qVRX&Lfpt:ttpuu%^? ӈW ;e3m..hP al1hTLPEp6_v(/ ;3D7Eמ٠\R^@ #[M P Z6jwRkveΰ=)Nfр0)dׯ@PV*:v%Eུ.RIqPwӃ{gfdX> n^+ĀcdoJ*" A|VUj8\'d45.:Tk ̻鏀&t`w9MU>Sfk|7A"Ul;WќEvg) TԟXtm!;x}iYbLc'v,9/_>1+hkdXDOЮ0-O*_Bw޹g[<yXr!s!6H]y!'64s(1m7a ] J}-pĸoz;:]5Û Q[+^4H&>Uh4 tRrO6G74/LeTeDjQ):!M/BD~aHJ4]-e'OfJ8_O%}Zmh(ϫNiCXx'iX%}] *X:h06  ' u0dc4/mjX> v 2~n&0l}mo8.b]0Bx6rf;<&HYH`\3̻o0` KN;x.N,ʶYe7c+I#5lO4(;4\g WUjIjo4S{0?\0;7z $hc~ i;]' XO09lB\ aqZTFezj!C9i_{Jƌ]de`d= ~|u}6Ԍ;9&[9OH'@:4PuÖ9ic9 փgs^E:$KtrQ`\mRDPP$fu<-[VJeg "@P t `7ִ:FJןj,ojMY.gT:Ae‘ =lڿ ^e DC>ሐ־1`MRdN1|@Nz'h@^# j^h˪63M!ͪ,,Ky"7uPc>buWs8a:o\TNpEzVh6̵BLW8 z'+Zim׊i2e6ϩsbe78ER&뙉C.]Z5MnIoT ZT@s ]ߐ 0fADxJWRn6_F>LO0~Di^|NYqlvfDBjg}㫱٠ Q@)y{mϴ3&u_{qڰf]WNav#dO-ލt#\&E-k \|}Bҷ$a@65*ʦr㒕ab肦LF8[F[RZC4cz>7AdU˩>՝ASG5ΈPvyk87 ;נ]_G8:4#@6q 5(y!XY~/U;-(6WZf臍ԕA2H2.Y.׋lhVd)?pnȜҨ%mG zKOWZFVͮEzXb{uȆ^ eC2>wW['}d-7gXRáHxLB"m4'+m%4AQ @Nѝ++,`}TC?*zVxԩI7;BڝH^;`V! vK^u>9 o4\٨E( 9Hcw>8pC' C}l+pV\\!r9"CVćd5䅓{hÌ)_8=) io8e"bKFv-(\ߠG%uoU1) z[nM '.|pkRN3$\y "[A} 1uYe3h.6Vio&([kы( |-]Yn"}G>` ^9PBDGOc3ك5r*/Әx؏* t\2, L1#J$]3?`TB%k[ґ>ۂ|,ȍE*tنHEᣳ zGPa\n ۧ[Oj-E Ow+M>!i{ƌwy8gݭk#8.W ݠ@g8I 9Ԡ ɝ\m;OQyovV{z*{]㦜RKGCK^m-gycGM?Pd--́>n3!V-8{OosY]3j)0Ԧ gئqm;(yoo{&I$Pɀ迵*6oNYb 9@ z5aQ!wd]"0@OeQ חfOGfLԛ!>k>S/Yݠj0"j5eq Fތ ' {oU̶Kdca O/͍R.Z><|^ jz8[|.m;?NZHOkz6sG]kjMǣ'J}]ˉU Z1@^ 2E I8*3|͂b,D {9_Dhf)E<և:>YuE@W#[׶zqwU*fǭ<& IH>Z\LFԿć|ɇvfzHFNƇ |B+.LQDc"9p̋9 7/G# &E+?r=N)'oB1$:GB 0pwkߩJYW r AցZUTYf6]6xKUlY؏8u~RLCZA6XrϚ`R7Y`'в'tOv/q_oy;ֶ1F<ճeVCW0-(u MNWjVEA;oaf^XGԮ1\(?WI1Jgh,Ա_VLge":PB`tޓqVEǸX1}b` 򧐪0SFy!W@ҝY<ε0W82J7)'N_"BCФf,eLn6*r/75]b=`ĥq1BUIbGq1^1SZlliR(Y7bBZzaylrs3W4):x4ekNfOC<`",5TZAŢLц=c_SmZ-AzvFb ~qJ[܋Xo|;}ے~F?]#J"h 7boۧVۼT˼4>3N(6M_$9f`Ya KB~ oaxߏ5rfB6'YA8ˎWaq!8jZ9& ˓^ Xtfrj*Ib-s%UJʮkxqᦃxGb: ^l>S,іx0:_1WYqK*mH.]#CD'4F\?*Ca ,P_p=@@6Ny%D\TKd8R5v)Fj÷"[x% ]-"W8%ve uMA%8 h҆ 9 sЊ@`s6uˏOP,I]-E,϶f <@&8_(︳W@^܊; }y!4WQ(GV`E6ڻن Ϙσe9ɹݷPBSyJ;e.02yU׼IG%w͟5(>\24a1*WhܻS§Ljmghd?ӭҎ먂Jf ਢ,}DH ժC&aw!ٻ)[G/' 5=Tɖ:t\:S!ֳ@kӞLѰ-~gAat;Δ'Jm>%THI(ןSFhDLJ0&}P&vI XbU|(Ҭ;2i(ƛg0U`i{$=U'([}yF;vNES_ 㚢NHi&'* Oz/)U~+֑i":8%8+:@ub['V伇,8muZ4 d7@'T)ϱ"-1k 7}vcdѨ5&^OOʹYh;9Fa5F4 ղ\RaU nu"׋ۘq>B\p1#rA-~~tb10i5w|bn;5mmY b+HU7',3YFK3͝L}|kY١QIqR[敻V {`)>'WI#\6 ˕q!v>ҮH+p:*z*;oE|Jj[6،ii?+\5xOC?}8-hmzVuv%.RԧV\IƱ'ԍ, ;[qF" FY+a:HyQUf:n_gb1lIk޿axO957Vv)A u2TP3kzL N3B9UVTC^nkv;^)Ix6ʪLITn8R|z~T?=\Q"rVЃ+lZA{dgǁV}cp&9iYxVL,_'QZvMriM^pI&bal1FiP*M &(]sמy8-0f<̋mFXmD&[/nW^H@PBE,i*NȨ.'W0Q֪6o $5-9$H3Hrhɝ0>Hw\_WDo'}(oy{DޓNd1k4[=o vqBxCV_ޜr%6֗\9@d2]@ 7ecF7/ ܠ'b' ȍ{Dpk/zk/cGL~lQ|Og\ spVGmRWm05:y)ljqyv4Y5 A+!Е `i¨9lF%p%1U7<OH Fѧ+?;~ZA,qk]4Q~ܥM~Gۜ&|IW'S~hֵ#BpE֤>dGZJ݆mGDDTv0l}ۆ'E$9E:=TG [ᗾ~*$J J!vҢ.`c p>7bif0Psb2!G^bu$jVd_-xxY aya ۄ?^һWB̶GN8uEcTU%u:DgczCKՐż` Šwf N R߸[ c ƚQjSr]r $V=aƙ4mNPf0BiW}%%%/UtA #L43-}*3&f'*A]]7K}m"Z9 bhpvU'Tgqev)YTѶqX*h3%Da3~gɮHivd{m9Vohqሡ@BORټ ӓ6?Pūɖ.Ÿsoy$e+*!mEH?QG8:v.y_!sWSO(]MJ .w.Ƶ!rAn؉&QH0F[1WIːD2 P 2b3Z0s7_LUi0fTZr6cO)\zi-"; ߋja8G#9RH#Nd1i\rFG脻nɾ(D>*;"-זs0pppt7Gl]=Dz)Α%}Ȋ;~*|nv[ylEw%@C2L_|)sr&]jg~V&4f{dC ۾_ Y/cO1$~Tڝ[삱=PPu-ۼo Gr OCCAp5uR%`MV^4%&UԾ"R 01yG <X6} . ,&Iո,GNt>N]c艠ƹgPLz\SAcO6z!I ;a@z l1,[1 @_SkfvQܼVW؏vVڼlHI v9f2ac/lьxwֆFBN]J3ިGSQY) ?e{s}X&vmcZgM|ѿщBYG*?sn{BIׄ8y~q}@;WYև>2lW\/T(­maV/{3Ap=;Ʋd(Lm7 bٶ̿VW/~~ [MJ={tJ-'@*˽g*F^v,fćHȉSDozCEv+.zABV?c|27θ1#Ɨ:ՙ6: f_"v^gS^N~ưꍁ#G-|N㘧4}@{U .z5ˑNfgVAZW>!Imcn>e|Yc[o2w|JKOOGF]@VnP,]˻+xF>'jTc+5+3]",ebh;ns U1 c9;Uyo(Ep5+jߊb*"M~n6zE񳱝x2ԂiO?:No)ʷ&;/([~(ʾit-I E$knZ$OFaCzl>BBk*9_;)*+o"WbC{Afa )«] D{vIj̳dF4qkm7f ҧ5:Ru"f ouFx`)K&PS&F,s Q-jws`E-yMO.E "}4~K$n:ֳof@j(J|p?AGG,N%p$[䧖 sEyV ^S^DKl3Q Ӥ+9UT;<1?2/vNw3^Uq WG]Mr+w̏} zVrp>ޮP#?\GF_bL@#cEG$D=Z|z,mzUrNI1qItY:QI3E:½6ٳ*8vnM=K-^&@7NN IgBVG%o`g=ĕ}p'#oBWM'beo }W q⹁ς-EFs & "q񖖭^ v]gan§K P[%C]IVw[.#&2+ڢ"&+ݯd`vkzÃmzI|'v!+/Mu[od|v{-f띝%@UҊ.S(vv6C;xG̐TR{?i&}hՆX=v4DnĚ2LͪKy1LlRB7K`Ҝr3Dy?̚9WWtX+.uO[EBD mE? TWflezez{P'osm6 I&'?y5;#Gcnyj?vǬIn6fUl|噤s҅Zթ4AW8}olG]^` ]3Hğ[6eKuDC.񵽜OQBQO2ɴ@_9hhyyZÙMhqLZ?z3pc&]]\[jq@s|p7~<= i~oDGcfA ?6I)S'x*}e)y4`p3 buo^TP]+a.cY*Y1bGc{SoJ {{ elIbx× P`kOP㨵CS|~ ɪ.X2nG}K2pHVZ[5:Ŕ Rw dNrC1(7~Jy\H*m!MP5)ؓSy :F/_z[7(1/L髉=vbǟ{;Ptq8[@Tߺ=捝(LVG$FĹ]?)FCz'je!`a=˥ idXz3 > Ngy[{nR+8W d,"wC<6"VQSYIE? 0[0_['cǨX:b_Q cDdlVC&ef-#W'F>ȸt73@P֒Lg}kB=|ML\P@mԩ=hBrAm.>>л#X!)J|DBP쌯c>b >zR~Ncη"':ӫOaԠuTijL+Ȫn}_H j3!R>i,M+%Ab΢FVȠs(){D)2EOJgZVR^5IT7lFUDƸv24|?HIOhrW*6nu qknvv]\YWo;e#HL}16`t7B,hI;-w S=?b_-s&ll醖/sSj .Z>SkoBOGj)\`l_aΛQ<|VG2<z2nHei68zƠ1ơ(rFgr!T!jia JWGӨ,&‚q ֓x,B-LV:)KfpiUFlhk')jU*Q+IWqV9~޲\B Rar0Mvs q]8|gc`MwqD9M"#|ƔxjQ'*azDܿ0 /#pB=\)ܮ+I=ry* t83.|駌뻗گ@ y!DR! *?}0˦[/y(%kYzzX hQ6`liB㗺#.mNX+,~bA7&KgZq o[&O׸ԥX;vG+Ra:NyV ɯo%>(&ɺ*ᝋ74ٕ$:L=?MEmrђpFUؔ 4GToTP|l1#o<#̊ep55Mo"MP>L_zi1fa#“:P@?8 dF,bh5̤ujIz!ouHjIʒ- `}g0>lqW/1.}ȜI!~ma'#cR X UHޏ XkNlz]#, {~U`+ЫN!H($& @:|``Ơ(J\ Zy88f #bEW~q|Ư\M`_J$*R]BZEf[_)QJх ZǝYѯ˅hePB]_oF.$_R]CxF9\^0a$!ZH`NU5TMAB:֓QL̶ xB|PaZ*xO=p8zȕ.?-!=0[%AnIX\T\}8P~%g@%"J_薯ٶjk;Fl/fmsv6B䝺EDH |09Nls݊^Qxe!+9eȋWvno׸r2%yZOܒkɘc  Dw$}s7WJ@ĸ !^8_#hma}#ACܨ* pbp]4 g"CݡW Y*!أQ%Lw5iXPR.nUX.)FL GSPU-(nRj,uxy*/FN̍\ $S|Тۀfu e'MɱiE7H`45 -._YŞGČOjxlBWjm;WU =Pϧ|1#VfrDoc?A^RY:5s=O=`חuJmeӾBCmt/$"xpx/9{KR3ɓ?cUlTw|RM+ _j!MhJ+s,Kԕc-$QDWJ9vѓ.ᡝi)FŸW$q{\6.9nSGDXŝiטsNm=F4sM] ) %t um,  rq X Ȭr n"$Iq`Z dvU,6aGcdd(:XZ! wIݴG3?+Ԕ^idҞ ʒ5.!o}8HaSz #c+N)fj\ėr*"6NcRB*n:YoAԌ~u h N.^!Ve>"JM|>| q&GԔqjJ.޸s4R~0qqs ͙D)c(V:^G/_Q $Β%r@UX+SJZ Cčk "  GH|7 /ӎ^TjQDl+n4SfJg'fK<,T4/Ϝo48_ا]&Nj#ذ(tձ܄;ϑx?ʯ |5:\:F`ʓPRשNhIoB)!KbWOZ\W7wZ#w#RlNKqy:j,Wscl(juӥm٥P̭Vgg9FpoWC5Ao7^̹)l Al dhG#謡$vCB/pYQ2o%edFy?FṠy{EÝ ̇ ̜Dž~(d>eˋg:r. ࢭ='#w8dc}q}<@A*^pb$,@p|ipWd&402*-k S!\rHڠ95Q>4oJF^sA\*1 ±]֝ ^]qz1$3:dX,Of'|]_xzVE $˂O -Q46~=4hƣt] eMQ}~PՍg🋘`B97 5+G(POeVqMi`I)hnj"ܳQ %[!e)ve׈F)i/RxqۜeIUx4lQ+ڞhcu-كaRE/bsa%P _v~~Z;$z@dL?|^AP,/xȣhj߸ R.+BEA`͎z EtSɤnrO!u0u:Z? &WA8rMĝi\qǠnY[SսuDt AvtUlNQX;6UQ,ݵ)űyQj\ l Cč/f=\`×z8+c\pYF,"#QH nUkW/VF$GM~d3|kRiC?k`.wBI~V*>;mD߳Ć'{.pbA+"E ~J,R."kH5U>* hNr! V1̂u]6nW7ϓ4wg`̬](񾳭&>' Zo(zdӬOOU,ي0b k.bH4{q6^ޯ`қUHdtY#%8|zY ECg:B=ujt;~ [_Th'V6x-q=i7 Scx]+ U㨰|չjj9 jt 9`V{s;P W Ċ.bWЧ}L.|'Z/s d)7S[3z !tкO'D<$zi}/qIVCg4ҧ0s N^;4JoHF"3B"oءyNZ lB$5`9qxt5ajٷnG bu|d&v‡=>SuR6.2Dy|/yy7Oo,*JD,kkVg.C{}8mP`2$IʣP܊1^ C7&JBlzu} +If9Zb A-6k,d63OOiX6 Z 6 0BcTekK#%piA:v|R.s'<\߾σl}e0H c\Q=|g0p]ܰA~`LNQ5hg˖ iMnu.B-QyO})V&:Y\wKۣJ7M\Z%@ =;oRܨͼxP= -hR^w s0ruL0r5b(K$`:B*X} "6P(ˀA29'l*sM';\MT=?}K\iH(9$v-*"Q͆:$Qv+" 猇0Ĝ֕1獯) r>GM:Ձ EK_vb&*ؚqCP/i+B*Uvɢ$])x UPx'HRq| !ob;èHF8Xeg ^B=,g( >)"oԎlڽ @쓶Tv yKh Bց߇r:vFХ.t -fEkVx|Wq@jF'N8@)Udzb=Y쥸Y^Ӷ|jw;a"Ii^w|XEW޴0ewf܆:{dv8(T+q̟a[>MvVaB%JaݞpMwI@|-B4LX64ʖA4!ӓ(b'bNo@PҘrCŃ0b,]VoZfڃ.>4Ǘ;jj[gEjL4͊2-6Ejlc\U 5)]7Bg|x.HO`z\ ć 82osq_ Igsm4'(U_v.z)<^@VVDv?Ӈ|d +B^4oU1~<>lTBbp@#0ax[ 0`E 펷םq81U빬``EKoKhS#Z ~-@!Y<!^khԲp_/Y>+!pH= wl.y& ?''\~I'\*IނQ=h Ogl>>&Rgk _ːj/]R`L2<I.،C07Rg} CX0K?^O4QMRK;Y\{Rx3hӣ1W쌣{aG~ r7%LC+|z4ix66#q^B^ Y'`xtLv>j3#тc޴KfhmU;q?5l#d / l>O73_|@OmzwB1s{@FS+;d9zڬ(ZI(8 pURnhef":o]b'k( 7Ͽ+3.Aan;m&A3tR D RB㛚,d#Nڢ qƉ-C'Mq+X(wz/0|XYABRq{DlVy97`Uu40ʭp Qao 3k:pY8OD_$]-<ݟ>}%*aV!04*^M̎jf(e4"xӻE{/^0akTXC}E[ٌ,k:昨h K2v3EN+@% h+%M'G?;"]l8 "VLr%>IueI$HEǙe!>ݹ 8af$yҲ9YxydHsͽh "yQLgEUpCEy|]C*$ʑU_";cl!f MJ=d덄-II 6cz=z#wXZ)CM$օs#uA-sXƺ܈Iw.$FyzL fI$Kþ]a)%K%(9a+ 齗 epQw.8xJ:n)'ZdlV]vb0|9ۂ@줂E*3Ik|XeћJ5)oZ98$l&{J"&1>F"zH39MauWT Ǒx\Dm<{,YMjr"+;\7Jjɜ7f^lN2+(mR'S,1cn8 Fb 퓤0R&&vŠ m$BY0RPf))@8`hEY`!:\(Pъu''PiQ_cvO3'\>Cڊ7Rj ztqSB@Nj+xnB.hROÙQ3 E%ݵqiOoA%`0cpH]OТ=s>ah_弮z/sVmu-_Wfܱaf{.o5 n)˱:u|P3=φI6nkC[,JFJ? mF#a_9@uZ0^wKF > B}.dnZCɭluf󻃡-7yޣL5VjZDsAhi;Pc 2\iqR!+}QQ`]5:B 7?7 ~8f(>V8ӳ'fmOFíVQ YNE˱19׹sI0иpؤ]uEh,zWĥ/1єC '{|Az3"ҥp.Ƕ/-hTSxm<0<#3L%S8n/+苯ig'7V[xRM}S.1e::,iD(ы(Gn>e8͜omZS1+s6 ̫k@#̪ 7^ۑxڻi@Uc:E b܊/AKK-LAAQ./ɸLyb4p^iA]E DHUoT"2/"KukkXJI8Y"upK'vZ*Վѧe̸?E26zт+ٰizvnBj8YiY*N/3ٔ!Pu3NT [4v~2;|x5YK2H8juiGIQ:lp.ņ3!mxu&2X/9SbW+Q|a>׺;|wG /}6v/zSB\(`}# xpmp hMB^LTG+Mg-p%zKr-[e ʪЈ,H#+WS/McoIAbu SL?ja:";{b%:%\\w6m)S$hcO үq Ɩ^_fLD{7% *}I'`9joZR*4{Cw%Kifۧ{a5W#L#)U5#{#_նtYni^Ȥ_i@~%^B{"r_^s (^9 & ʌRliw`t5(F_ӄtmfjtaЋݱlU~PM+jjOFxרv J Pdz;'ީIo :Dj?|d:Lֻ/Q+\D~Gdßѩ'_^u>H8boja.v[hw#h!C#pfKd||1}c^͝iVc!tHb@a!/mZƉ|sTWLu-jXJʤyEiwwR,fkLl:չF^#LU[_'K5|_oė7Kjf;No#(z_a?/H꿘 *R}}ɭk $`,9mvnMe+kw/Z`oF ;)3/TgKw>,\gS*`2_-?}=SǠu <,  BSzf9 uGkUB2{n>-,B_jTpN(Ɬ6_^'9oL@SіDR]ljk3ǝUe:%R߸p-n ɇG4&4Ҷ~QrÕwWsB>t'،r,a'u+L"LW i{k,wf8ηB !$$P=&IN qe,V Рvt`9C]"dJ&=B֖MlA v 3] nQ'9BdɈ"97ɥ`ghPZ\zgJ ;Ic2IMCqYv>7>K,*GwLcE:hrƧVI?w w >?5˚XN;=Y'yX6<' ]"L?hȸٞ%Lpvo{ ,ߔ!p.ؼc4R"F B3BaOMRW,Y$vF(LtoPu BAFg-t%RaTz^&ᓰymy[P#\9AXbB_=i'$7, ͨAmO^'pӷ9h9C稵ٽe*'M0L;k;\6I>\89ZNSSB晛w.3?O?oJ(M61Ԣ+lG%ӣΆ!z6p  U*FU>&,3]%MdfzzҟʝީKZ)_SxOݚl !fB G*t})Ɵ%a`b GF(Ϡ)0{T-J#mnł(R=ؗB,`aFIj-#Zz|DRF̛9?c|]Xt=8ެ[j[fQw=7@az(C|<9wg{_PDkG N_7G.E4bt5qdV~3jB{Iw6${#503 D۷7z阸_Ux17Vb Yr*-qfArJnH~%{_9g_̣Nn(.t8OqF'7ƙ{IME BS)S}5"ܨQNhy?qsg:ófe3!ՑSYДe ǹ8bsb}1Gf/;^#.ZEӱM-yeMTTLK;I&n4 "D,>ȓ0~q`(2< OS/M.+Z4 7JV? ^߷P@d?3g1+enh̯"̇(o?Mnɨh_3>H~)+D4 SS8k^cv El@a[8z<-;2}^m>`~"27a`&b(&7Z¡$`8%;{ש63wkK} O6QuHTʖ%*FECIWJ@.-m}G6͓_ݔ~<=Ï+wNЯE\DF)76w˪-q:@u5B޸ZX$.4:$|",NV儕xH5t}Cy , 5dO'#hm#}OɆ.8PگTF ڔ_|sQv`,C?Rܷhqof#L=,~O{*p?2Øw754sfH>i#yV&+thBik`?:wiMa @`MxI2[XFK/6ip>_ ju)_2pXʰ%Pf6)vS 78; c*`{{ H}dT04lgyRl.`(M͑X%XihN;X\+("4Jx `,&ptǘ[6Vv!cYYT޹TR _! 9O6+C͵'iq87n3 ,NaVK 1OͧLjs|+xX͚ ⷉ'8y-!ڂaЗoc!C&Ts5; OLaHjqӘVFqG#sY"*$jʄ _K'ə0 3oф̧=~G? b?Gv^qG((чא1@c(HW!GLfݭ$"j8=ޖRf/2﬛ 8W;/>SZ/zBIWzFUb?S'l\97*;v b/%d~04<*-}.;'ij%Bq}PwbwԏF*ij"+̮kL~Մ5۶IZ(Js Hg'nS%BQH$sar=(RmfEsAE=?|1b/t- Ť唸VM)Or/^ˋ~NeT] FDb~rQTtȕʧ3z7GHs ] >YsgZcDxLj+c^VgKFQ)`HUHof8ُ+bOƑv9TUL mz:O6`A@p9lhC}Mc9eAHC2+ݟk;൥>@ "0vmW a v̻)%O׆^WCCW-FsFy^)*Wo /F$M-R0F܍@K*ׅv̈́8gD8n&,C$eOMf$\z_ @3Gmw#+7%fb^Mز E41)1%y|fDK` el"C,j 0W?f:Qۓ7.-ы;^"7`nJp RSAVd30Vwρ-L/3g O[ vZÄv<G~ZOa9MIXQrf{Gdäw|,-+'wϏrS>m6 5$2۞2lȔ"z3InLf듧([Wgnz0P]5?'d[;Ä́<-wYڳPa( -i"yj<7%NRT0Yқq^9]m5<(){~dx+D/kC}xW7 WnV. )Z-nTZ^sG%,zyS63MP+?S3Iڜt2X-s}^Xv;G%vuGnαcaD1"hsA˼L5y׸0fG1 Սs ~3UAm8 >63^v10ˑi|y0NmQپaj"1%:îHtѕ\!eo0 a\/CyVFQ-Y%dcZ!G"G?U)x? ut(uޢ?y\Ĝ2betk*#B'nbkKcPQ\gb&,ƨ qs/]1-zt.Ҙ@*$WC'h)];2L]4Y1Jpd fzʡ!Q*{Bun4tm[TyM>(LWĬ׌ 1Y!O!ђJg_z4j&&ܹCSmriPimxfCia6jyHEkն eSY #=C*C Z*~C{ţ rmpci٥77ٮU:W)A_TiMl xJKl<ۃ:BTWQ+:YǪn"崸x=v Ӥab1߿fp;PtL|ox 9xBd/h}IY"h`O}!ʜ}TuI9b'ӧ4fHxwY$zd05>+DL0SH5q*;=lhk ,z٦j5:I) C4y/5Sd\WmDܴT1F(ѐ^o!0v{pi/T”)Vր'rs ;>?~ZAX wz:hrJenkN%]q{i: EFMxy07 !$!of_VH_,ePHRhcc!n!m}˔( ᱛfDl]VN,Qܙ ,{=-JvSNT5UR SZ|fE;H̠zyhK@*h_ I A_Gt5ۚ:ZEK!vvԴy% P_^ :ߔQ` SхhvVfRDꨊԈY)TlB;ׅiM-GyVtjH4btHc30qlI-Γ sv"W<6zʃΞ܄iz3/y֝WZxK`~%F<9+x\6_LmqE6C%U5Œ]EEPdo񉽺'ɭ)O?>]=uݾ)L:by͊Fs8'=H'r KrX(%r&[bA񏗊+\O|l;S'ƀ{O8\!G5z8$kʟG-f ݓTS> _%6K&oc&'{5lr3z|o`g䇗9Û DxC}ӝǹ! 3twk/OpRNiً0+VHH)EytrSspvC &?'Yށg:A`MW4*[.{! x]v[Q@?@2Y2_S[6?UmQlz_h?am*Xu 2*Q>ŵNdғ9!Blw15o-ui J>3=AL6oȂBMs56 0Pk(MxOItfnOml(OwcUFLrY}X}3v-_,y(̖/vMCE2XP5#fp^SJh}"6sMv I|5/ h|},g9e $Z8:q2m;BW8egfsIpS'>$ B`.;l֕F^R}^<7[a%5¢C!*ݭ2%1`<X\,9,`%|Âz+Ѷx&ߋP#5D R1*:ng֥E-|ժ؍ x]*m/%B5ϷgF13T\׀3h c*(dMيIRe3Dd$-x޿ ` ҁ4O ~ZdDŽuQ{nu~&68)-;Y uo'D,_~`'v8eTSzV܅}Pu}5eIri{:S9%w%qzS)N R(Et/Yp+1ֱ)/ttU\_S=h>io\aB fwRYbs_@^#鲬_ ?TE܏VϴL٬B Hץgo$=2 /)g(f])u{<dӗt:A*G;>8SLXH64dZGPǐTbR{e`$tc0|%ƍ9z@ئI( *{FI*[i.ϔLmI9dG];:ӑO-( ,* "(Y;eIw;Pfe9)]/TF4N bKߏr;qgWu>Xan7P_+J-e8߽Rpp =3)=~gƀ)"E E2N:8Tpќv`/nD_Xt]:!G~eF]c(o,[̐(e[n̜% i"ݲip119bF PBMm sx>*<ԛW7=Ag;|[ɀn'pkBr]h'Cǯg0vvd+=~.UMFRip«b!c z1_ޱzHGg ,5ݭCʗ<}GJ<(BQE'W9BҠ)".k?$}^Utl6фCbY \23$&w)/g}KWS3>Smx^L%1ߍtzwK3r1 lfk)j_z^qp6meJ#7wʛM8*IgM}GG7=l@Y,e^#ǔ(bi'ËKdDiyq'}3fhWr ,_;BlikdnRx㒪` >0:L}0N Z3|Iյ`wbR)k-X1tM-U=]=tV Ex k!|ɈH#vWe=G@7tb0Z]t`Cp./sϟF@p;CIdN^Y3{,VX5aZLBH%!Cєb}-E6#CFuֹavgPn=p]#UǭytL{LHƽ6tW~ZJ sEe\_WlBZVyDm?.|r3c]D)oľ]>|+ 䒫%ыCzj*h%:7 ԭ~"`eO2N ; Gv %utnj@GT\0\*AH "D9sf'!=u(}m.K%qԠb#4 UܰeS '.&8zdLeQw83ߑ X-R&栤X&ߺvɦ£F\E6g f/#{۰T^\gbU[~!m?|_Mr3cIMl_3ۥ4=oqy׻ c*0~v8xʷvz,֜c"貲#a0`{DM]$_=ʕqS0&rb% 5m5C{L gZ'ib \7m<ħBFAb;>o-"{9j(ǫl~ˌ ³{kD1@Y:x$XʇGYB4I= U~)J3J^q{$6Ƞ2De`o[xNeUrDŽ3^YY-3AeRCx69sұL*ȓɡu=ؤGSYx,pň,"4z= 9*Yz@.,-S1fOGDeD* -_,VYA,SR3[ ;:=XCRAt}(`](TcÓc2׎0'l ?>08:KEOxĸےm0i4X1,=gpk .衤R\D+H}(WP㫛8͢΄WcK:4x@|O/:. 'v#*YPxrh({P_xLG~&UJlhIu0 AHzLEwyj(+n֜DZ&c|C}Jq3IG@2$w+J˛t-0ʛҺco*l_\ X_^Jz=K /aL _j >+OвʆFmQ3۽ !A$enuNu\]2Up8*ȰDuػU b)?Gª\ 7{qNA.*2AC0$YדMrʔ(;*xP\hmrBGn@ 0aGFp^Tp&EJa,&uʊ!g ]eӝoƛrR26#؍r6J !bce-x><TLܶ'B&Z׶gЇŝ?uyaZȤu+F3w[f WE!wk澛f[Cqv0̬Grgೠ"̔~T%.-Lە'ABSI;5"Co {԰yck9zm7Vp="cyPĤo /3%»[I >4%5 BD0/BȅGNN bն$`8 6pE4E)>[8hgȋyRI<+k0ESQ%i3!} 'oh ~!"*ƨJsuBV^E h0Aeas]q+r{8*rDV,FL?p@75i/nPHG I\* Uê~Sn*=jh faf2hX/; V83)] ВOU(QO eu ]lm-wq:,#HQDk-9tf[4't:59p.AxPa OIE!?5K"IhP)Dp*磤m{+KSaF Z섪 ctӎױ* :s49*3j%ILӽg>[xK0&kC.cUl,1{L]pߤ]QA&%m1[xt5H"`p2E!L[SцU@$= &1JB2pi.sb[NX`A?ob#Jp̌=A1ƩeZGxJ ,U+,Mē<2 "sʅ2No}S$ZW_il"{Ӻ0t؝zfQfPc n [/^WrW_A/bq-rxSA'͑m&8!P)NnLDUe]玷4lb;#}zMXe|IQ3N1&50g3+=/{lC3 IÓ5RX]B!m}$jmA59\s9WeBcxFۉac}c%YxW8,Q[9\Bh)6+H AN.lm0P_b vuh@u^}|+m?)C?\E)lp"vpx ]ц2PK:7OeΗj됩ehȞv.!WK"<0pז 8"`P npȪ[FS^quk<&kY4aVMJTe>!w[4|ah"W[FL6?v/X:-:06<2\$]xuk?`AФsb(\vH<HD7R0a\oJ`yciD;/Ps }z4P$(#gl&ֶZg] >?]^\$V 'S7|+Eq.|ULObSYd &b%Wj9AztE—p9, OR$~/t6dQ 5uk)t h쫺F)!(~pC!D-[E W[wUi^ϓq>U}/4o#'yd+@2!Sbz*#)AaZ()L^~]YtљTD!Edr" 2Af|{T{P}ݡ4(xm.S}<4zXZnE|@CeI5]rc?Wa'MgS$Yyt؛07,G(N{g8 K9۟Ȳ ǢRNYvQ)NgHRzbR@߅M&۟r dBT`wwSu迖g}e̻FIfɅvle"=FLVo} -a8PڅU!" ̀e5y{C jRțˣ|$A7x, %*tA/&5vR( ⤯#Ѩ\ j~ >XV`$ʦӎ%+7hq=ۀZڙY(.x5N# U$&4+fF4l0O4߿H"f"jÓ>m9+>B\\18f@Q;;,eTp/+O nl $"\/lM8.?C-'|P ϷK9Ⱦ;iq9W9$^Y ;LDv1-J O>")wuôw%{-E !ZՈ@BkDf : ]>8&B5JʿbFVEdɲVc ˀ I5b8y}",U3Hg@ 3oQ_'@>/l2bZ| cKO$Т¹ҹ)ֲ C/' 8+չ}ڽ?SnsB}W:w^Ct cxcݣ ڨ>= YegTmۜ5iJR#'-B$|un<,sa?#7T#Q̞陗o'A"oDM0#?<`hqd!Š=$n=O$鄅h+4;Pоd@.'7?sæ4tOG[l]'T2*62s֮z#F9a/sו vc5_fY gU~Zf V˱&CBrLr܈m,SuIWaR-zwR&[TQw։*p^:C.s[IV0EA@| waL4k@6#ju? (`rtsEv̓ ]:Q+$ke ջsײZ+I#Gh,Wa6:! Y5ӿUkkz /,.ɞz=0R0z (p ;gC"!t:,{1k;oS_fN﹆}գPN2jGltdv@W ͜ vJT$@|BI0MBɯدwKIsi`)ge??8/6^ںMQ"pdooD .@/g|y16[O򍐛 #P̼ʙ)3˥s}W]Ǩ6ȁdXĘWio!fo/NUV}-z\U,֕&xwF}WQ3?oLn|V7vĜl^ V lK e0:Wי#< Wt߱"`~=MexAM'l6كBwDoS]P%qpl;_+7T gx9GݸUWIh֙!WZ^TRO^UYYZ@/mݲT"DθqB.@{AyԊ uJ?cUݽN xSP;{fi{ Hnm_"Jw9BfBjj lqvnܿJ:@$`7!_f*޺_ Y@Dx&o|V(qFh߱Q}LPd8%}ecQyȔX9L* H 5|;x%̥F)ϥ$:9tH&eY1ikK뾳U ١ [Πd&GKߩLt >48&42Xk,maCʨpv LIJ;Z})Tz ,6 ,/Y%,M€Ɨ \\)pS',6YiQuK|(<}tJL8' jwLw؟SgXT%r&cN0HjgceADXpբyP ՂCNg#0'͢Z~h-J]]CDǺ//P21ډP`ōn~& 31da J`N3]KMvvl< ,YX~Ru^O"`Iyc^k0E@?$ ~ O9Mʑ3e!٬nKm4ި$M=;%LDk9TdLPڡZ$)+ 臐 g[=4"$TéGd|l@AT(VSvxSqA=M4Ns!9[{@ʶ7C c怨yd(Zsm1smܝBGht-W೗XBH$!d}aLD)Cq臻TN"ßXx%I7"~@BXvsWoČo̊{RbkK8Nͫ vMntJ=@טּyjg"CT1C.jO%,6:W-,j~pxa ZQ<0Ktcq& G7y9rQ^V9~ <_qKy$v3o <0ϾQ%W|I`ߟV.A@0 ~m>X&$f"u+s`SS=y(Q(, R-MALMrfwRȫ3OiR_M6m{E 5J,¾<)H^L=Zi zK,V9G}r`=svlZ7 6ݤ|uq!r᳤VZhyP29S Y;$j.廳Ї*`FeK {4_[1'q,K A蕎-xl~^<яŦ%JJf(X?0zмOۀ9W w$VGR􋓑C&T+WgC@Ȫìx0]bk߻3Շ#VA1aBVLF[&k "P&PAV5K||Gi ׳S&4!N(huބ_QضG>y)k Fz=[y14o"<nir-=Afmtv›7`$P~Hh%C( EzΕXvG& mFfZjo$ζ ~3"D0)6os>Jpi +Hn6M@9fү!``ǜ% & {{܏xzjs;2\E$}$h]:JR r1i.tNAVyo0B -+IәLXZeܜ_PL4C%4`pl׼0_ѶAkvlJ7.X#ĸ-ݾ[RZ82jM= -GkQTHk߷Ǔ#q3oD-\ :Tb'kSNp2;&}EP\BVsj,T w#:VŲ 2EdduH0mKW\RK[!^ҲoDT8ڱL;ސ)սE Ⱦe벳31W3.QS34L8׏>f}_7Ċ-նY|?cYVvt1?D.ޛ 6:>":}wdb_T I[@;2>+I埃ѯr<UCZs%ۢE$6pnVϴ0b&<~AI2pN*7V8nעҳĬt)ZJ Pzn~Sy\"™bZwt 3ICgx2P[wmON0M\0ʹ΋ }3ͷ~5[t[{%9d)nPvy "5JYTu_-RU$dX )17 ! e.[Q.(e[}KhǃTK;dVnkE1qFu%O)0ډD꼎bヰžmRkA\Jvy=HrSF )!Yn>U ._2>3quEvJ7"Oń#تzu\/#E6r]Gi9w:[JiotzN0KN^+Q"WnMk-?`I=oCD03( My<͉yDo҃^9^Ӗc "(0鹊6p–飙ř/-:VXyނMnm`d&u憱1ҳghWM:|Xqr༅ H.U>qyqc @Ō xIou@ :Y*u.qT}$ww%pnV |%T_˳ #/`:FԴ}Dhz"h']&Da[5DQ8ɍA`m2!)~\, ~o /-JĄq5*NP!d-(E(a{Z Z.Cĥ'cM sx84ak>@0JV| '~igPK›.b5!dY!%]v0!okze;]YuR V)R{/J eyMlwI(YVHوh5`ˈJ1{"+uv;Jۍ>>^!*&@G_ZuiI*([m\&COoC ,P=^"D-HFVKN7;1!x]'QpCqV7*e3EW:~*ߋaۭdg) f.ytl\Kfʅ/&:4c/fL:!"k)I؁6s~Vo AЋhk+Ѐ7k^q0^1{Ѳ$@<RL*MAKZ xC[ԟh U/ī Y裟7s&^`%$#IR" }0aZz鼈G 3GvKP{X>%)ܚ$OyX0VЋY9`:LʠJj03Qq]ۡ %gwF?y^ѨJ)^ͯ:v{C YK&\hvnrM1:;IQ<Ix0,5n&EXd6t\w?q~_bѱe)gk78OU[ب 1ɳ*`bC|; ]v.ˠb@.^d5=PQёFIk4ry !{b-y{#29ֽdX&r)ȁ /KJ[ ;:1]S%墉 S) Q1Ehj`-,5* u4NBc.Zna %M}><ԗl,Ѐ&SYLy,`$D0bt!2 d`G^I#mt(IOYVugh ^h/S_e5t!8Wwv^}`B+i:keJvT~T)XXr@aM`PT |BRa(V1bDH&_8c7&r۹ޅ~1 ( {5׹}WIl"2WN𺁷0 @ʄD=(89umr)Wh8m]]뎜]9$t9%NA6ϴhB/mF'L FPgq>%d|&t6u՘n< -X+ċRٹ w&!9+N=suL\dX撔ubtV0qnDC)~.*"BR0ÖEni0m`ߞ$Xs1qpPb{ӴW *Ps`W3|ϲ`^LvetyU`F=uFE /wY6&Bo"Ve+Rg[S7Ox8SI!KGCKJїiϺwS3SĂTL.ϘZWR7hU``H½yp(`ftٓQl3P@#kUmqX\7ONoh[l yP،6\S z+x/y/Nq(hTu5lzǪw[ /kO}X74w43K:oDYcK zaq0q4N5 `X/m8 '@S%Aʂ<=US ag|7tbY&t2lk2<}$HEr&ݱu~GnycRٺا^Ѡ\7m-X =7w>{&ϵ^>q[EFd{gVݸT¼c_mۍ"xn'%m 䜩1(s$p6YvMʥ2G"Dh QXNK4V窘X{XuVhh}6S|3 Hk3ً1F;ykBaA;>8`xL@1VɻqŖOڅT,bM#KBaRI9lMK1 =6R`Qv>K:>)D`S@ο s4slʦGw+*5+Ԝz;mxm4TtHeY6u:L`>jc0m[ ]@eR,2 8k&ܭmj pwF,颿 Kd}{˧dLN IVg{\幾G&tǗPq:0[GY!ݒ&^T-\bJx e|DwWnAYOūs@WOLlLל޵@cWHT.5''7E>Lx|2 qQ(ԯFBe[=(jLE)c0NAYw`\x_Qࠪ.2fEfu_yp]7HYRI/զ_lcam-6؏,"=t xOjz1 $U̷ ,PA_-"ML/7[d]BXY^ZCϤ/L T<R!Hz]Ȥ= s-i=z f꧄2qH7"lC(NǡJWѻJlcUEЍ4&ssoju?'ۋQ/kTX̫b2vǁ4['j_X&kO~\hGCix eE[!Գg(WEǽ_1ج3zV T}V\ӠgؤR&)A50,L z<3Ϯ {蘵y4$}(y2:P3Au\ CVE}f#RdNfpTdE]UTDHΠёPG%JR=4BK Q>CxôHԡ1~gj%/LL%ʓCDaрvaBbWs*b^֣aL!.ʚSqo),d䣸 #?܃A@rzڪ#Nװ}`ؒ)Y>xsTdh4ڄ{ā HjEtL&C2bb'|-F12-Ǝmmdj7T/+JTS(i\bMNvo~Qe&ϿvF5'0&[$yK}l@k9dڏt^*R1!. rp FU\c&&ᗋxEWo7ز1c,c?D+a&CXΒ͂}94YQkjU.z^FJ8p㌮V5wE5蛜(؞V+ʤy<^pll# pVvEbAqM/5…B2pq,B.cۙ*eTUA:XisNzls:o'{,>$BlJ *UݡrL<+OO]z gyA>̯ \!}c=mfV1t,x# #5R~Ғr~3]_2^2bH 6ך?/HՈhoOȕ3U+GB6#ژ fHR"S@^/Z/UVm+Iߵߜ mWWV_o<]ikY;V;@L~@n*')@R>fD4a%ggvbߓ"!fk$,EP§vP/ ^񫜠?պ-s@ݯ}gi`5K8Dw9uy$F2UW{)LLPYgHAFLu@g&>QX_\J TVc{_ uԢH-I0?(j=>g%DYd8EEk zW:nowʹv%ȽRZjnN'ZޚD#6ֻM[<0ZA3Wbi6Ҁn#H^R]ޅ{URƯXp)J/%5 <hI:,] _2 QԖ(Oe)I*&/>uQ֞@i(iI1&80=ב,&|A!К 3El2.c휿p\f"˅Ca8yxc1v~?o=+B? Gv6dUVl3«JfN.Lb>].WqjInd0'K.^F&J\ƜƊܑa9{+(DAڎI Z>]‹&./ux^C6׌İv6'E_32 ue~4zpECLpJ7=gFE (1\vN/G :YM08V>DZbt퉀nJ:88(h>c{G^ ڥZ V9{^v@d>!O1T={УHkRtu(Ⱥ7[LHg@oSxH\;"3}2ZzbkRXnLjGMBd@`57FTӖs)S[s`yp&.۝@ɠ` \*ܰ,mHIQ!TXj33~eӛ~j7r}[OlE>M"|=%΍,O[wQ2 fzoc\>SfaA HvAi\}˸|$ͣr`VZDrO&W9I%-3/JrXtd:.Bb1bz꜓߶uդ[+Ԡ0z,5;D#@݋<&Q+ɏv4,(ELfu֨ 3#vh1] hdO7+B0طiBF^C*xG繇/~$,bt^1v9o'>͖ G@4[@3 WTsBZRmԉQ4԰zƛ l[GEy˷W`hq0BF D+',ő>X`h_zJ,j߾4!#j&iD/s0;T z?;"aE<?.%tYTEhʲ#M}~mz)Y$̅,P~Z;]{Ţc#L)*w#Dӟ]Be<["`i3dh%F-#yu]-*JApN.*ީ|u1 q ~ X|O7̩Q^p@eǎdVv`#$/ݍׇAuvjԆ rbڝ?bGldt3NyöTuMS8)Q 9 {Cz,_"ZI/wQM 4R'#Ϻ z3jz&7b >Ϳ@m@dET."ۼW3 3#OnIyRpBH\s&`D'ɑC`pr?IO5%YW&RтyàN6܀|>M";U'lWHOAZѾ5*˽-b;W;k@1lp[)$dh1;#(] 5_ds~kk%V1 b%]K{c}RL~qܑI۳M9ShVsjҽai.'~+yo!ui-LUOxh]"GuS@~TtQ3jؔR)p3 #gpd(S&P[)۶2 9ŒifkP;6ؠĚʙJ- fw^G1"!].2pxc-rphlhCFeWf}%S Q&l5 u#@MIIV|&6n>4Pmiau!}mCvTjas[CР4hc]?b]9ukֳ\̇9U;UEg"d_Wn?C6me@Nw+epgy~cv]Owŷę3@( N污H\YCJIU^*`Jޖ?ky DX4Ġϙ]ɍy7lYs#5p 7.eKL걇1 *H'ʆ1hM̥ mp\MC =O꽼 RzJĉHz% yFX|:! a:mGҦucoX=A3"呐a׳z#0*VPţhyjE M h7)scUnX%%ީ~G_'ڋlLr1pJR@nIs3 l/V#BG#>u'ii3_OsT̰Zc SM,,UkB %ő@uuI:+Ɗh[v{ѩ ٞX472q$ȿs43ៜNw,zJ-֞-$jPpa6E:zϑ0@`ZC8v+ǿ5G>d[Ie􂙇DucX]|C3 5O&.D, p0?hmڇ<|4&-g'mu. TkKјֲ@$U%$&s:U#ŌLVUk2*O]rGHgJ/"6J &ܙSyJ-Ւj A@ޣ]{p JA:$lq9'ǟeci5uR-Qճu!Ժm *poąi߰wHՆе|IMWgsi0<sqHJ՝e=a $`U7nnrvY}j Ib#e{ l.I2z" q~r"ohM*ơB8/*Q⬨MC@0)Fن,V@Y~e1sKH8e*JyGsK%O!i[o+56oZ^+@Vik& j}}քZu' r"[w †UC?>~?Vg>1GOO݊9C|@V ~h:+%b2qOv_:ـ&IVEep0sjB!eaFTB@=Ҕ߇"5^6-fs97MwJ;|Ӝ*dn~koCZ X}#9N7ON\۲ stwM@51:*?%])6Ԙm&&NXZD#uG\Q WOieF" A(\uE]8 u1-KZgۚ5SBe"ۨKz{ܬ`}Y/܄33m<CmTWlس>#)uIE}5PvAOտxㄮ*_haFIA4YW(Wt(,i+)FZ'H]3ktum֒KJUh@!/>P[5bdM(,MoSȵNg>F*s ,/.ALإ^kWt^1DSG-KጾNYhXM_%oAl{Ufd8ۋ8ʒNbKi.²c9~xR 3OC|L՛\>>K@cjw_†xxa >"~9J?^|%9S‚įl=|qv Ns< 5I+WV ^JE_PMgn{2Ժ3Oۖ"-Gk3rhKT>U5{XXm'к!U LL R)Le1IyBz7D=xq Kv7٭0N7՝lB_Еԁ>0vK%-jN.Q@5*OdE|! AJ!Ž`Ǭ1lTq3=*UJ`~q) qXχy >=V|Ho|rX7V&zJ]:YՃ 2) `@LKbk&ӓ*듁,ָUʽ1 0)syKU&|,׆w |pSH"5Hv0wݙˁ2]CP+qژFN^'{$郯 /U3Pu ìswiﲦq_Ŀg3f5³vE%p. M ܊ъR'3{ PYgrll!{%D-ჴ(U%h*x X]8_.a|Q&;(lc>]>KYNq% zcAX'ōSkMoDN<8" #!Yvz`QG\ PEE2[V|N[IQMoqU[o?$ ADΘCUe5cQa(THfL$gᆃ8 'uMbV#lr0'7-p!g5es4|I pa`m=n&FGߏ^Ep2; i$-sU[ug8F)񹉑fx7{g#Dȉ(7H|"»ZS$ܡ6@=Cd"eM-R?R7<" <>0/Y~w#7[oZ)1MSDu̫az(`HB3ᛳ7nݫ r9RN iɵ*K[IwBTLhbvP͉l*]y(Aj*ZjtXrkcޛNۂ9NB3+6VB5cnK<N1UAG :bNSW1̂SDw'l zg-\q CTՆPUfZe24)G7uQZJh{r:Upf3bA.QC^^lf!8yرS-+⦦nSCyiGNN+'"jho]!!䎓1]j;zLLB0?.Ȏ? dJsEm䗧kf,6sx#d Xױp&B6i-]Y3m7)b6ޞyw;n@W; sLG8w\^V)fN2 Lsl3'1&W1,=W^K,8BUѥ[R=V6y`o̓?K $cfɫ@(J4k)NL'X8^;OR&W>4O G̅F5rl,q#Zp.Z%r>8@˺fBJZcKUYmaY厡O/Vd,$}YE) f2'>9'>4ۨIKU S&Ԝs* S!}1aA3uI%4JQ/='bַ'Q$cez@NGZ25l> ULznF;cՉu%Y>m %k*B4}ؒ~Nqn 'ȂXlުٯLÍo1⻽lyN JJ d+Jq;bQŕ@za8@f76о%TE*߬iOx8(MzihD!.Dh~Ȧvvȣܱ'-`49^UF}o=E/*B)هQrI>E s {(W%5bȯm*hHb JUQٿDӞJa!b=vK lMѯ-օ:{Vc@ZFf?3Al")rP(zrC0dh~d֗}R`L=Ӌ5D@ʪ;`YW?ɬW'(|S<ޮ{U5W{&,v/+loDcqkc`U", ڮxY3B-" pc8Z,#!#G_z✣u]Gx,S-\K$\e-ؙ\#J#Rne^2',Ibc ;>RX+2u=]\ Lv.CԆe?ΘPM%[C8<́ Bhx- 5Mׯ*MԾV|T>W?b创iNWe2B⮄ף~jV[x mxX! bn]dh}0>atREv|x>~5yy`nO-co2ʮk8 MP@7;ܛ̀-Ŏ|t"jcpFwJwh`p+w/7WkMTH'ӯQe1nCa8ofCuZ5sp-FK`P .l40C(鞟1gҰb-MG̒wAR?OēWEcC"=ګ5]Jucimq3x`yթпDYry6fWRAqQS0 e5 F:,ӆjr03oih}-w(-O7E@ ւpkmbeG-9ֱTߑyy(RLR& ێ a.NGRv,;ryy"Y-]|z;PgfW! DI!Zq7/Sr[]姥:LDYxZDVpTkv[U 64!J;߲kK_xRwDؐw̋v^GIRylK.T"4'hFMf響,nh0_=N^yr^(f[IC'%6Q4YlKDՕ^}3hͅ\M;U:7+0NGO|V&]L>؏FxJBgDHgjj d}:B/,QA+"Qk 2s.`. 3ҳN9^i1"̅XĻ xs2R MT]~1.o[iCNABࢍ?QqvqYW2QĽIpz55 L wNow*,pÄّy4Jg.e1e-o5Sc;G/H2h8tr'tsSS&:&RcI;Vry![Ø ;O LE.|,^MU5Ry=W){ܓ:6LE@rxŷ$0zvg_qop.M̳f a ßƄ@ҏG6G$JowA?^{ $걯'X)JmU ^#ˤ?-OfSXJ>[vR^t%^!su)^ k(QT}/@ԜNJmwMU*87 ;5qkp>_˛KU5Av|XQʼE(}atuMyEC-:#0fToI1.{̓$C1NTәqy.oiCԷǩ)6/;WI?K= 'x|<&DC"l@: Egh hmi* 36h HQ\`]Qbs#; No^}_0iZK>^mD z<9KNH%\V_K3p-{ԌcYYX_cp9C\A:l^R("8~k!ϵkoB:!ieb3D$i-Ք>uCٰ*0 aWT1=>6@ۯ_~ t%hQ fA$')K7 *(𪴿$1p KJ|ߜ i YEՐs\T RX m7:#b/Ni*UEi&$ep#P3=)6i$B(.w*좧}űrh@tK<˭XM潍͹1 #_c4, mbw5s޼9TO*q` \B4zq_20L1k~pB$AJd|ϏemԠ"$V˂qnה!+ H(. i RNƮ2'IM#ajb* |P>uhW9Œp! FNLH.e j%`kDU;1}(략3mlŁ?< ./- t\z1\ I G1x{3 /ӵӞ?lZdPY;طO"w~ 7vmk$Q}~u[0|!Ֆ1%id/r M@}d =&M}.M37'p=(QW[$zO7;\,sdM~"}g'&%O*M"XG).Kr}AOj@VA)ǍL_ d #`B 8Ct,&nFZ=nф[*DX #qG*e?\.=6vjNęZ"^rW,dxY,֮K$ .EӍEQ95e+J^~:Ȟc\J5t  ^̘x"zXvk7BrM1;r\b\~6 I^yP/Aη+oT85&Do~W] (xW0^c{HiZ_h,,mt|+nWW݋B#N)4{16 ?ͱ'~Mm\6ʂCjed/QFʵ^ _V?ő][PS,~4"݋_ ϫXdIYɝ6ԑ#N}/D$]羢gVUH)|)٭g%j/ )Y#\e#^'xO@Y[sGC+|Ӭ?r |̽rដbw= Տ +ދgmb9y_]ytKfbL-J PkzY:|8CDN̲t8@%{-r xXj(Ʀyy4! 9Ó9YUf].Y Q⽰k/N{FFdm^ɵd\e_Ҳ24=|(F8tzsM%^ḚӌVOdۈObӖ% mD-ɱSJrU_WBCSGysC|BNoDo Qo(ʞH~Z .+$&@7r LZ# *y$Q9W7&"<%O¦wǓ-rZOlS51qbc :0TK*™A=.Gjo]9P 8zmNۉʪژM=]K&>\*ZkaF,B7TC3{fB0rc@<QcC'bWT&GӪP;w.0xlBy"D]~- ;H.hh;t[ E"xWω v2#*K){}%XCr(n|FB[P:?Z4ğ;)bHA _AKK H&5KL&K9n6&5# {\uִCL74hG #㎠ײz؍X3垈;[ϻ†W嚟ϙt@Y+=L xb‚'U:N$A.P_IEI e#E:>$oUy[I.΍HU÷\RҘ-T,9&}@3ʪ$_ f]uK&Ɩ \O% /r2&W&\p>0&Ğ8qDIs 0 ":\)g1{yH0DL+V8uUƞN+ޟKZ:ųZQ8H7Z2yenj+,(%=AQ \7 D=8IBcԈ瘘snh5#\)vT6= a*ecJza]- ;<(ʛ0Zhi=o3,[J,=Fn8m^;yT M>N ȹ/8YK[_Օ1gOnա^)( ɹ(q󅅛n[ Ys 0 7tE+nR B5#JTr,/+ KpuF  ^߁,D׃+ hj5<45ډvN9pa>ƙu8x^'/S3ۗ)O I`;wI7#zb<*wj{emVCz4EEԍLхǑQOcPM+ hrcb%\g0 m t aF1>GF]Qzŏ9 ;Fe\7cэIvJ؟_ ЅV!V =._-?b(@Ep.8 cR Y]bT7)^(" I^sOM@he>D| @ &X)5qK J3!)C= |'n4 ѨmA Z?+;zddgHoLڬ~ޠOux\f;KZ*;rBUUZ M0T캚Stq(RbNڈWg|q:䑨;[9cZWM=&?q\_5!0. "?$ݼ4pY'A OiIQ|#Ah!üۚE`4Sdޢ(TKdE__q\Ř;Fz̀Q# $kt//8A `ٽd;lLP^VQlE3Ļ@hC7AP^$t- .߫c!$Y?Hmew , r[\v NOFûb,'J-Ae"Cw+oܒhF˲ >ppqPw384k>vl1 $:Ƞ2Q 3Ō(,vN"*lĽ`Rmu Y1OViL!aA/RsyqZ@fѝV.=G׃ql:$.0G KL7t_捼KJN=}JS:ONT^Bǽj]̴EOVbMӁ(]k) Xax7P9ټ`l R&z|P%H]5p3Tbg8/g8{vkKK{K)^6J6q(=tc6zf׵keE J/y11tcTڟďgjckmQ'[IH=[&|9fHz A$WE^&D9uBiLtKzd6 ꦎjcd7k52Eau3At>. >;jzXUR誶 Ӡ/dk0 ?467I %<ѳcxg2#-бAS*ɓ%*nCzwɩWIp3~h,}x‚HO/ ؈3>Yr E+REK˄Tb5QJRxT]+q7] هd5r}U,`&20UbX<$Ѵ{5XrdW63O~'a&O|uUGXv98- , I%l͕ ]F~SfsOs%Tt GF*Jț "rzVE&hI/h $`y=K('iwee,f4,I+&2*XYݓ92D7C^RwgyMJQK!nQ2NVh xRϼ@IֲY޼F!Ňw=-DEx.yKFP\C&O~jW/ug5O48^1RݻTBz?,"gЄ!o?^FZ"XTY? @~_V2祪2"n lɰ]tIđo)),ySh[ߔ{Z~28%ͅ ; +Y,ً^ܘ-fSIIURYvぎBb$u7/,<$-`{%l#prCSJ-?lSL$0}+UV%淪"`'U 6Ⱏ=! 'MR]ԧGq6cmcѬc³dXu:>YԋV.6{$a7 *֝kÂ5>(E͏PIu(T1xNJH5J >.UsFA&,u6H5c,+cPˈYHէQ4R5#[2OocqTϥqc)na͖͘Ms؉ @[6mF YD{-Wq7 vFw !Ia-m2lw RM-Vƶu*9ooEO+c(gAѠӧQxLp,M+g1^˽ $d3k;Lsk|Tx$:CԮPYڢsE aNeu[ hO'&;zwnlK$CC+W$i`6Р#$LhgACpu&S72*]?J#b7KGhXx_Aҿ\Jb*4 _u݂^-_Lt"xf1^(^gfAH.&\ W B# UGԔ'mOlp$Sjˆ6]SWhMn>F57~HULjH)1@b.n[VAQJNt]؆HnJ?Ԥ;)km׷O! F}ZNޢl|W¤̟u|p/idh7S\LNkW̉}_tvx}۝>Y`^Kdh`G Z"w;kgq儐D Eز-y2_ HQkk+^(ִbNKF ;8L-7`^U+CjSM0w}A p'[Z;$˖ tqu|+tk1%c,\ >YO(g7jAJs'7<"RYUxL%4 :{t]Mڹ] 8 4:?)l 3Gѿdx,yфc$˲w'&s 0KnU &_?FV^b>dЦd$(e<(7"a:hPԏ~ES*ZQw0j)怜8?,ʛc0y (A rSC;'fΥP'K<]OJP_2uD Eru'*jZ;,T=۰1lͰql"ZU L+,2&_w )[8Gte*k o?vR/\ t,gv=@$ΜQ"e{na/.خJ,ZcBr'x!HQ{cUP`fc)CFb}b>} @wXek,Ÿoճοm%T-B>|qJ> S\YڬT0ꗧNvJQ/8)~lZ~I8\/Eo;M?C'\Bl6iiFz6`kkmQCh-R mQ|^x^Y";^wt`e(#J, ' "OTV0Ö'^|X- Eæ'4wpӐP7͕doclߴ>ڍJ>eL>Ίq!7L6.W)'evU$nAdEyFRp=F]Y*?0:CSn\?t} jAi5 `z &ٮ/\U(T![CO{P8T`~qnڑgpGǬ6A7(XPQۈ(蠒gԨ:lbz*YϹPN({`nzjQv]:$N=qb5oG^?&OHY9xƃ{7rZ)wLg 3Aj:va/+YŨ#ŃW:Kc3#X+rɻ+4 y< 0%oC+6ΈKEiss[My3x5,3RzGlw\J9v5e+P'9$A CpXKBbVO(~5,*RP֍o%{^i'BH/>S&D Qr?{˘bTa׸#B'a3xMAKaÙ(F~[*X:^J|F_uv{\w"\][]_U Z ڗ}=4hAF8:'~NwzVyMliU?=FwJl,M"A+K|h{ \OOn=j 7y'GO]ݨػVjY&X.-<NJl+rۅψ:I@r, $&f*M'!rᔵ!l=+CduOoA6 4wp=b+@`~)4kPssٜjm*((i,,u@(1gW55 O2 aPڇڦϙD:bGaP6U]f`5IQ=6.hD@&޶BoiYZ#8ThaqC lRv$qU~Ccd*ؤ[rR tgؼ]b:ڬ zV-m9-}&X<-ڗV7A6>~c]Ou;+ !̋?Z5Y hM[#rMkLYے2Afv`w?vRC3\%#83Ғ_<q Ƒ*A[եeJ/u{2$쳷26T.ǟT&c:L3W=i"SxҤ`MV 򌾃j{gS)"yhjHBИᆇJYVGFe%Ubn~&w?<)W1{f*Ս0֖aZPj3lKYL?/ Wn\"]0pLp\4{UP%%;S ];e叿1dCwGߟ7l:+@/cAG6#|~YH@ۆ8@  9Dp̐pQ;_Ԫ%`,U4_~7(z2~:8t)6nl ͵+^T|yq]f9bC|kQr%­S?'BIcov0Ft0r +# tqfBToh 5#j肈}x95-4})&C.9]HmcŽ _y MC$p=-sDd`S&oz 2LS)o'ҤP?; oAa/ӿX.Bd@ l}}0Z9ނm g9 }&"P3HFɚ k 1H|m8sz CoFul7Bp^QW!t@Vh[j_a%5&㨽)A`4i'{SB HL$OEL}Єws%!"S"^Mzj9p"dfdk6]"ʑ )`ӟ:VftA&(y:û}jwg;"?{%+cSo{G:kJxO<}DW46AEKŴhcS_7 .h6\y(&uLbp3C nۻG: ψ-ɖro1%#S)-,Zih#AZpv3NrNKThʩO˪=r w)e"C]ltKi*\e M6G3 ]*o f$dIJBt}Tac)5\b9gDxH_H1i _A]KK"H? |j2e,k+fwI'[;dZ[6&.-8PDU IL 9@aA@dzZX61a(/&8.kG%K6X~L{S:ED/p>gwaTVU)4[50x2;[sBq1[Վ#K_[fYKmtic$!A'"&|2"Z99+d[ϖѨR;Y_lAvp-|Ɛ+i,ʸ^Nń澟8O*TbEݢ='a &Ԭ/I0 (Ac: JdQLg*2Ox}`INJL!8!{>Z]²2JH?+ Gf^$!ѵU٤jYdb؜2c8ڕ gMoc׭( sTK; 68wMUxY"Dt]V38kJwѵZem 7~;9^P@FanV.ɪW;vSJx(Q MDĝ6^&Pt&uk2s{q8cDmCl]QGX.ytwDv؁&hzϖ-u:GJͯx%q> 7OCpY68-Uyj`T}e_`BH*l?];c\{(XvY&lw3|Յ'9dGPGOz~uUrя/.v[y2((YŃ[8x}WKĿ r\ 0`J3uVS۬^wwYֻl'< m8P QzLtGUadbm6C6G [lq82l,e@$/X[I7=2bv 9`r[ /_]TT^M0/ջ hN9Í~k MzAT'XP%†-UJ2p+^Gcٓ3MˌU;/58& G$i8/)6\2^M\.k-5,K[wDCf}i hXOT{ʊȰԀr f?]MY,-f-vԿxEJ%Bؒ|C9kj&8=$:ׅYEӦ;@&$KF}3V>5hJR;>mI0vnbƽ>v)h|UW{6b{ l$3W7R[)7K*8.n۱+S'eC0HT(YGM͏T.s,bqtd_ YZVO'ZM#yM!%6p56ZȐ,( dSGk&ZaE1[ZÎkt܊MzP8irSTVt1Z>ZC=Sy+V<ӷ&nh| CѩqjGcc;[k:F?QZ M)'XUH?0W1mR͘<YUx+-X+:vm}/:Nn~48'hsTUK؃H0)LrEvg7o6{6jQt2 kR01$2F29:޴:RbԜcڦ$4T-&m.aaC>BOi>qϭw_}X e"Mh$ZD Ȟ L nSsV!h|l =n>At/]JS=+:GpT6pX 6 ŏYgYwkP&jW'm YԽ)l'\%KU~(yNc@4a#>o6ŶCKO\H-Mf|ROOVSپxQ&?{A#gM(j@xͤ)Kn{*qW 6." ٩x dCR90 abה%g98sڭ$zOui]iqkI˅HΫ޻P-Y꙳K#@br; ,_! 5b!YNP!u;fe-҉0q)٭{mkt/+F*#@NҳT[ש/Cu-n?,^0= ;>.TB`c%~$V2jCs+D9?*!bɳZRrHV$ [ Ә0d'ӕFZzF?Y)@8?1=e /Ns `LJB^0\n5F@t  nUQ ͓ U;?[hL,FijBd_[$G^:I 0=d_d]OTEQ`z}<,7f w !E2.TU'{rVШ薴o?SgGڅ 6^%NoKFSZ(8). mDj,-owvy= [M+E87_Q5]<|f|+jZ|\Ah>~p ؟5-뷸xoc}vVg$̼iLdҾÏ' OfIqV2bض}NzyL1M6`28Wڭ-b%j4 @JÑTp GwYEeuڲ ]ۮ0v @$xg݁؛'lܒs.l^D); r m}%p\_a޼訖kKY3OIe a9%X؏Og YN}oZK\: qcqjj~{  e]`!Z B v d'm$zEYǁRulF鳉?`_VF% 9a0jg7lndڙo!A(Xz1Y,*'8>Ro`}_(N\ ,.P)N;)!̰'w~#GfcN\Hvv|A!6C'f sO"z^ IsBңX %t#Pl3Ij68UGë] nܗSヌA/bH8d1dE>]a}iE~R+cmJ2f.žx=aџZ.duUηZD+Uڕ ^ք oGy0Tk䨆FdЩ^!uVPm9b@&d52,R X9X&g=i?~ `郻<)㳪=iw%&gtKGšm;Ojï$ρ [?NQΡ5{f3rHE*{'kkf+@yD5 օ̸-I!!4s[fAnGܧDTW7h\cl+fK:]0y?ǁ s T5Mf\q:n K|J}"5XS9,id߈u)RYmWm/ ,,, |W=oPh EQ8z:K_d/D+K{GFt=)SDX`({ٝJ%J4F6ww  5hE i2 +R]z|&UfE*0X1%|-ђny! F dL+e\@m;{`6/(fp7A*ڻwֈYvl2 =zMǟMqS `%L|rOóSp:mM؋C82|VFL N[8،}_ 'F$52TPb*cb;K>Cj؄DD xŸNsnjNY"Q_& "5gmC iAJ"J w̕`$8zAJwzWkU*-כoctɅ-Hn꡶SR|eGͅ-&٣cly5e~\Fz}~ CX5,hCuԿlv**s53avT4 Л:%d l1r+k$#z<&pFp"}|b N%CEK=|PZ*7\d҇>7,{43d|·q*8MR5p0)09סtEqM?u'7蔢lA5[<!Xl@_B'ߧcaVPvJJTЄ&ihl 92Tӱg>R:B2N&-_'S؀y;Z jA3 p/ -˗R)b~Z3()dy2Kv t؁@#^JxC3S hمmE}//uzxUdr|g!:"okq:?E0u+=*:St<XYn4vPD1ئ۫Q[ңJkS`Ɇ/X}=s|-ep,$مƁUp]jfU؟ݹD-DjL_o!O?t{UOVO)56&2iv6NEE(#^n'1 WpDS(8ey;T .Ȑ[щϠ%:y9qϣh}ˬ3&*, Q?snߑDڐP0IaT'ɏwv r806H9rTgzlG-h\QFaճR|a-NוZwkV5mmad=s添cyl:$@n`Ҕ[ 3"6̗øy}5{֭]ipťʆ1AmԶE|+YwYB- *4֨ R_~ͼi+cMyY}%__t~f{;W3()n/ 0fϙ>-Z," xaMfuy~'WI2Ei:UZ wo?PЏ?e$cc=O՚s#r{^=a)>q `r6lPYG<rx^jոg }<\f]Jt9Wȣ 5H+.G ڑ=kр[)9;=[MU${e?=g1g~AamIO$_rXx ڽ6ugգ:*'r?Lu-rjϦ pxËi= 8,6yW9=4ĉR w:sqp/&['"/ Jy{sJky5ğ:%yRQNBVz_K?HP`l 63E9!?fʱR0(_} 8$HiRu&R7u3hbd?inWXX(,Z bAo.N,cd !K-m__t}uS '3)J)C+u+ql퍏d^QqBg:A9{ owO &ç8[5yaȀYnb>CopfoZ%ǬWR.} L0k]|7Rq5 CjFjwp;5VG7Z1=_YJh)@ǃ;t} y=<;IOWk4SRy`kw&ENp1.m-y,+PPcx$1u+W]tG :ҵ`yLOUk=qqyQ~а~&Of]+^7k̰k#+m NB dw\I4YjuˠmKF3!xEFVm҂A8 v.X۫43wCanS _|RY.ғ& uξBf:tY`as- [l(e;h=.sCȞv_뫲# ='l8N|l={ 09PɄ-"Qԑmrlc6%N캄3 Eq܄EhKkt^UCG`b?|\oR_j%x%ˮT2Ō!U,AK>+b._̘2ہڗXã89K}YNI'νa@,u XŘv$e⹢ydgv}cA0Lmǽ֤i ҫ.>x`sݹN$ӿ%Rsut&cD'Їwg~n[բc\dT`X$%b]"ӿd;N']beN2Z@[P٣Jy0uLծ? V1 7z? e/% rW#^8BP;JauP鎥\GYy~UJZ7>[X[4 1g]0g5J@` E]p܂{@L9hT ;&m)E小ŴTdP>!蜈yRi JbØ "R 챢)EolL4rooIGsl/IW4m HW1,`%NBM;%gI4v@qĿ&<$ |`Ɨ՚JnjY9Ymu10s_( C_] nWӾ_4:Cʐ'<͑*ξ[7H[@ iT^ pvE1k_lmGu4+Zu&K^ UW!!6}R9*! $h:`L5bKHѥ }k}ƙQRbM@ S(2prRI 7]vH@8?\Q,&:Kd(U-:jZ#l?W~p CL4k#=+|i ! kDW!p==+vj8~1;ݳ=ܡtDNp^?g0!5;=p=b3>BW*$vAgh%N<_򸰍s  Ui?zZ:/m5  \Dܢw6uqD4po_Û1t8HF>|r}"vcc~ }޾j9/zcnw"y쫕du2 {/ UMOɩ]f+7̘:~.$l2YTe?="s˽N+߅ͯPF0*~J K宍 Xꈦqs/D5#ED|#,|L$zz^DZGlcz:g0)E'=JK3TZ`_}jĦv;N,rPCaOnlm.U/_Cݽ Y{?qQX3QtZ1 dT1R8wF.?6FSӰ\-|:nN@7=ͮrt15ĠkGb 7hr j╮?m0eKͼzMm/}PCoċ9{yRiMC'(S@x l?.B=!"/O}ϋhݾ1IخO`4I]H+AIۅNGFab znUKFt a 'i ft/'gap{<`bloxijq ҙ̰b _1bƴbw p^ E#w7%z{6">!Ec<#jt{#? 2ҡ #N@ϋy2pn#i!TPJt$y/LcS]~p ?W(4g!3zo{Z[ V`ʠ/\Ge7*e/A1n?{ ˂Xdždprsn*psg~驊!'W#ҧZ=6-1e@ه$d\Wo~>m 7 ؿy3@Un௰[Aa_mmRsnJR &mg28,/xJƅ1 +sn ŬavD99eԟvb;%'ຏC7 T[@u2o<">Th&Bag3:|3k4u sƋ\1RvCcf<]Vl;P6KqY"??D@W?lN+:B@g{ WKȑq 1Q#:"3@j/Oڒ,S6z N M6Yuu?ϡhOݲ\2pU>9<n\D O.}|Gs/ɮz3VߒM;{Rg=f2a20ǯ?.VEPMg+Ov$U1of9$Bi/=(ز\%H"Ҍk>ru_%HztqJXهmhG0SM]#P;#z!ǝ`$./z[e32kAt8{&^~G"v{Uh I0Y82k1 Zp2mi[7JjZ,`kZD4"tq3ŌMʧ?v쥳3RL!e=Ϋb샳w?je'r~ sD׭ f#5qL4Y~>,"zG{omTL1AG;Ƶkäpib"fC{g^1i܆+CaQ岵$ %Z5^3)I 4>ѤM*,"k7|=;TxT"DZҭ:gۓ?\N)@\AiZ42EY:' a,I,@U-@Tw0YtǏ(Qsf6ePLu'p6VA\z|tOEÝ͎{fN~(7lJnכxb|7Dlv`8`Lp2>ed W7$Gj*m} HE)ɵNNH'8;$>ˆ+qws<͞.J-BHs- 7iҮG.h|Fh~s)rN"2oQˮLJjFk'; X D|@Miq_5 8w/b`_YaPbFH ~錦7:m[VJ;cĽvhkt-ea6r.ZT]\Sue6J軶TڋT8g:tְ\/)bК4/֮]|{-dOv[ dț2xZ1L捬A ݶnt2-Bve9MD}0%o`Wj( X oMnZ ڐ|1ʽ)K>64<)ο1ֈ23n: a|.&n*%S:Y6ϕn[`:Ø lk ŝ aI\T5kS<1*^[؉@$6sv.F@} "X0 [){i[JZ izͺu8rO&1d5ji;Jn䄻~m 橥[u"&HAK lwgk6it|N0BHW,Uh7ϖQ*:Es5D4[ ˾ _B2+]הxhJJ=4d=NJ閾˻Dʒ!e>X m. VAo߽%=ȮWZGXgʇC4:q5N9z.SSz3X86Ck_ P"%%7;)X61kgEO8w# b$4v8IvY#£۹hj8+vKYGt_s@S-2@'M\+-('tCktޢylr:hcMeѹݼ/;H߹_TwmB1e |p0z;o>{ts1T!$ > @/Jf)#2Nu2My}C#a'>n|.l6wmҺv9mE @1!&uCn#T/:[`ϓIĔjB#,3|EkSÒѱ茞D 5,hP0]Jw\;bVy8,\S\"7j!=^88{=^_dI5Y 9o'T.HJ[Z/0F>YFd?u+^N5lAfǂ}n :nii|p_ֶAup}'R;n1@X-oQ1x b xJzbtu!@VXNY߷>칄 bÃGE$Uk^4y"#5ՆU4M <4 :rKe(thH'ffzC )9{A`]Ab`k?T8~drK 1{'BZFX|i !Bn*B~gЅ{xLAcq'*`JfA1]ð3H D4)SŊ\楙xZOtJ^'~0 &X93x sʰ *M}ZSl#S7-"CYdL+RN: YPw_ݹjRI44UJg zGu'Nl[/$xiGI׆P,ޏqYbA%(6,"^YW6z5ůWE/(B)`,ٯeiG$2^}c oڲH:[=Ro,*5-!.]łg쑈9Mbd3xV2>"bX.]jhҜc|ɾ"GLJE #03EJ:C@|E~LLΰ0SלK[hS/9}N s1GS2Wծ qUZBOKEEu<=+;q**ß ^MIgRpj茶礹8̠%,6/N.:p9A{[ 8bb/ť5ԻO#n]?l3^@w1 d-~$!`#;v#DBQ|P (nRlW,AI$ Q&PF ʋ3O0|@J cH|ae@:٣h3 82 m:pJ#Ώ8zDtdC#u-6>ET|u>! z]샬UbC$-If&_<0;MHJ DѯUƯQ'A쬕?CC0 \ko7 r|*@ y ݈Q&`|F=tO_B uJ*L}A?[S͂;ݛ/Ǟr<Q'?+= f'\> r84KBR, CdULȉZh"ksڣu/ݴ _(!$!2f_{Fcѡ_$ 6%`)t;';0w~;s1g(}%xSeV; l=!G} FirA%m;FG8!jG\@Q֫MJ}<ĻOI{^9 7Ukz]PZx*} D&(5f!Dٺ(`4ly0g0`r嫰iPni=B(,wCRHt#p4b?m8| ad)sE^ΦfXI(qeWrahs'MX9< P_m{$esRS?ט^Nea1 ޖP{==[e@=yO)ʃK9 H!' EOoS),6Z?T&hbc{BI#|i*y\qd~ WGnCv:LT WujUԑ0ޓS~A)*>/֊~O^l<ܪȅ {UZfI|r*jy(ԕ,[1zVmGgt$1֫h˜z,3BS#\BO@X`GxёBƊ[L9/\ӑ [bGN"b-|?ߡU 9áF+|=!fnO67OÇd0np qBdlVqh2xޗtP/tG|Zpa 7,l+V1EVYk=G$73G7<\Nc횲J"uR}uߞi une']obgU!xY,qxB[*)gXnpoA$@5r\E~]RD4͜5ӫ ـQ)#@ Ь&jWB =Nׂ?|~DhNz P=)u'kEtǢV._=I9`E 6xdV+dOC ҴƬ8 @im d|nX&=_CۋpӛFk^, 4>-e'@H:%>MƉ@UFaZjQōQvQwv20xvM_qT{Re۰ԷKPGW7/^$z R\DFm!x6V]^2t*z0+d֊>võC 塑%q Fy A VyZFWuD+CDY ֥ml.t uk5n<6I$rr^6b\XAP粃0'ۼw dXLlkLnT{)cZ\Ba3w[w$:5DQ*eC 7سޤY]>4AG7G?YB:+[vo Yc9C':Ia%ftdH&ɝɥJŗT nxβ?·> A/џ{n])srVXyZ# ߹ <P&Ko35LʑYn(8ꈧ0 w >߱VБRcȋbL'ԛ5 .jT"!Ϩ9,.ee "=Ɣ+8 @YFIϧk\:c] } 9.qf–p'0k ODoA= }5me$8h#i"mw{\57"bM;$Gf7W % }jO_~dQ;nҫ5&n0\ۥkXZ=oעiul1ȫ8ۖ%"l@>jJHӑ4C; i9,q:U_Aw jBQ&7ȋYA%,yO ]dt[2 ȯQP vQ.[,?_Ӄ,(_9d4υ/l>[\z&Ki]`驓\l.ၽF-16&mu$%9#Vq8;V1#qbb!Sb߅N768?IH X5GY!.Z:947r{h!f> ~ ./\)p1^F7q}AGi&/V \3J*fCQDY腬+ ÉFœ"c4ŭj =p 0W8XҷI &Ŷ˸$O/f^J7fNcbH5*9$.*̂{c3+9hc+:^{ggbrDAs(e<zU5+3gB{Xd/O%kšls6vMZ \5/ZH{cT;]퉅وt u#1>s JVC KXW~jX$J8 $Z[۪t󉹑: Nop:F4Ɲ9[:Gtt~[ϻ?be dQ85bkώZkJ11iZ9q26ľDB=d~L>FX(]'If|=WENƉ og3nx< /Z:@^U:yؒb9ǀh2&U9S`dp@'F/}NgꚀWVH52ku2K&6SUap{ˎ clY΁YxJ}[_ ^p/ʩ .N (J^#3(Kغcz|fY86fُ EQ\a])3^rv  V E薈1aXr s']e  J1&Y@1_G\\V_Fb p+ 5>=P׫ ׼:2[I02(=ʰONQv9AfV7E SKSFMm`+orPdrՋdjd|ςӳ}\|T'3":88]dL~9Y#FVPth EUoD2YfF*%mXS͔s\ W`%erwSvߏfs vل&-nPxaJ!Tʮ8;%FCTP!$ VgU;1Dn5{6ca1t†iU_48L"NyQ$BbDKD/`[iqSuMuP#>r3b[w!sceowU%.V6== A54ǽ CwD" !-9m>'&jRњ0i\hxXm  /5NL?7dGS|KbǸ1elW1Rב+) 3' .Qz) AŒg}c7$CC;?փ\omşN)Cd26diP#iP %[u䝶,ׯZV}.Yg`ryW xefvu狗 X}OR#i:gS7>Y.]BƎ`1J̆q(6q"_;* @}86I-w9W=ZS^zV1w`&Sfy@"bjs[ NV>B/0pY5&5*i^9u)!jBKsQ$Mܙ9w+`O?AcsJnʇI}lڿ,p6=inlakT*F#q6inԮc7ˍNdAJVZxA^{>sTfSގvw*C=WZ͏aaCvpk3_fe4pG,).|B6L#5&#FSjwQ P3l>Zq^oJ]FJV>R|1 y~m}bۡ!6:0Z9 YG:r@jq|Pmg%>fƎ~W* PLh6v'/*vFmF.;^(xHㅷa o/+1p_{Cf }.fxG .4}--8_pCJW6UqW]ױQ^*(@zέ :4%fG1ʠjD|c ]篶9wr^d0{-/VKMD#.z=vwNi1,g:%|ɡ(Hjk8I̞lfRn#6wqLدaZygN +7j AZQ{^=F\td5b~=JJIO3! {ZMYgf ",M^yĞJ1FP}lD{dD -0WbI0}cz`$(x*kmE`w>[byUBRr}fsnt%,#NYuB&߁TQ?NQʉފ+Nx*N2zpza^ARNޣJAFg=+sI cr q2٠I))dp QJmZ3Ȓ5N nK#+AAH~_}纐z8wۂ-*21:<B$Mm~B\}rlвļv F8KjFufJJ@MB-1eon_w4p ܥRӌu2(Pm8tj+hFGS4a}7ȄRz@h4KXn_i>IF;ղZ .̫]fE05z{<ޟNA7w`@Ep~|`j=sx>K?%##YbI,ЮVlW>IloUرcU`-=\LGd0,H y1I+Q㱨76@ClΜ#ث'+zJ*wi|ki\r6K<"Q%x~u!@e+; `^7 AX\.b&IO K*|5L/UPquYͷ7R]\1ojV* Юm_A8!kY5]o 犜L%*ڳȅT;l⿁Ϭn^ER* ~o7FG} l$ZʞF2:p!UU2s9Ts®Q! 3 yMnQҪ[W~QaUm#JU:* Vzh{Hll!"Jzhf܇P^z,"4HoƏ[{U8_I =UYM2%Aq~?18ΫOwg+&K,6 RmZ,"1d\!\ރKcN!j]Zo2+{86pU);Pװ?3nC7 @O gy{}21=G_2CBM|RT4tjl>~~ pC΀#倊'qM2j:q=:HAFr&B*Ѕ,"0VZIFm rG4{E1~^]8=J(1Mz5mmkrg|vl }Y\RɐUzQn]UcElE֏6mx$e 6\J;9/kv>(yǬ*jGA*>ve8pz<1S@ssJ?kxlm@P;07% RB s7ƾqⷭ߁R?e>&U:Lckɏ3) E=ה5Z$=>+ltp~نq+˚nZzc캞Rß;"m)[ 4r̪zaEJq"L+R  'e뇩C#R@d ]qc "J/q۔TJ]nʎ=HUxz G=MCL4z⨏3nȢ|Ǐvyܑʅ$'u!MW<ź}u3h񙙤!Shc '\<ڪzo4TݙԈsӅ7)h@2Xu^cC_Uo}"c'/vz'C̿ ^3w2LWm+XfGOG\Jl 9S캄oH'8'&9NZjhiruZ9աF5͑l䈇Rfw6| v%JǼZF{=W!>MJqN[3E _z˿U , =%&c(cKU"3^^\`xXO#Y;Y݈wb`v)1-=:{ %ǯ+( ?sf?pGE ;a=RrFKP@Gl*||̖Yx>-ϿFrFN&Q8F|Іgg|.Eq4;B &2Öğ*%3wu;|}j}|>$N& QxcW%rQ։UyHvdR #OTOXJLƳ;I+QhWV.؛aD_O hOUԉtIݿ͢KGvlԨպO3_)5t& ijOxsf*+Qʔ=$06>zK+I/뿾".-PnMzɵL6$hKlxTa&UKA@vֵcXp +As  I7"iGPv8.7=aDӒ71v0wgH?"3$'u /Wj) Nj0 K (ᢋO!#!EA[ʘM.ܭ ]Q"G0g}s |+ͱ_ j 7)\j!!8ka-/ "~oR3]mLqq#RRY6tu+N7MYiV Ow"H/hk)hI0SO b g)cE6)>݉ϵZV>&!:{P@@¥{?ldGXb<Ͼڜ'|̈́@hn+)bo-ml2xE%Xu7Rrܷ<~|ןu sc!3mXњLrJVss(p< :PAoXs3O To0fI>V*'GG醲ZƠ&nԾI`L_JWet#Y3(E i橢="/nՋ%֘ ;yN~ڰNREB¦ti|rGNbcxW@ sHXv4v8?ɿ g+h&db&r(߱U9^k>qQm@Tцr7Xk XKZ^:R]O Ɨ~ݲFBJWu73idzٹ:kC*j)|#\Hó"d%v>o1"`|)ٲxG'4Rl.h(_*se͢.irrUQqEbz E\D 1p@M]GvG\914"`ڇcOJBQf WHۼDTzzP:bQg|A}$ii*# W%e~V ;O=eL 5nL;x)#]G'?r?[ZgK(t{WPnDZE; vw0aqͭPdESPO^! Ym^eU]ķh |! ׿x!=cãr![v'S, i%U^44ȿܱ] rHE>NWU%zoyJ:2bB5b)VO>:W!QVќ.鄮 Q[Lo>~&WRǧ.FLD~B-ϔAIomx<)t#ieg;*;t6{0zֱm?w+BKPX8>TJ~vq.-v;;FYM~X ԗlE=c9cm9Jy@ΕSzm׉[*Jblܲ ˔Bz2 ƕb$/?HQy}j@W2 fy ӳLPa|ƏM67GԢ˦Ba\F? I1rw&k\ࣟV>QBS nMdO58)=wғjSD2j(}c*˞8iD)r|=ȍacwfvw;񦕽ͪj7:N>gk?X @l𙧇QQx+/<z <'k=jl`D5*6;+ ќ~.{-Y{h<$djs! DZRt`RmclF2Lįn*8JZjA*B v^^=nTȬ߼5FW9%Eʳ NF_8}I),۲_rgd<{7Gi- *E_M3HwE1F7JIp 3 cg[|HT5b]?R!MvD$^[M _ +4G5Mt)\:ۊX[mBA6'$bV[xf) N(f%iܨyИĸB^Xh$Meݤ,}wJe#9S$;[pNi_kCX iX̏߻2 iMK<܊/d빥\:.+vF7\MQ~EX+"W^KEPbx|\NBoC@ Hgd+ K fR`2zժzOv#ee+5K:N)vctQ+=]XzA( n(I.ttA7\"10lx̍}~8$C.4,+\' ܈U'ls+&/$ꟗ|KEiq'#Ur}+&- .j ?bcJ{v1EQl)ӆn8RBW-} &T(U4SWʳ+vDܓq^F$ozL^K14-CL4`XFlމ2P)A,Mʪ3SIQSz:<#`2o/K+?߱s۵qCif歧%S{%_D|W{D YvӘ,i;v`'*7E6sut+9`ŗwK6Z1@zj;&3۠rvkN;=ā{]?H.bb7i$PaN25G} vEG 7:o⾂2(}0/nSUj[j8q 2LO\Ho7:1/7BxyӬs2h *Ҡ3  OL9zO*2e~F ޗ7,bMԆis$l-׿j iه11iXթ W"]3;+ 4"^! A-+~' -V;\ה'SزLgn:W CO ƠR޶+-Z=rؓNrh&|Ɓ5^>Å54?Pi?lH(Nl09=3vz :AB[p>["tmJFGs:>FʐϐPeJ@<hi߿!/q|-!B-/Fx]>?9}q2g kdX͈YЋ_Zaoi,NA_M~m-U z)}e^Sf3.볅_TA{v&I̖ssHZØN7 +<>M易 gL$g..qyljDW?"woSJxMkW7d+gWPT&.f| >51}Ι= &ڢ-Fb?@_%I忍BT?ktܯ?R?~slRskchmnM-_43<ɓz2㺇٧/o}zS,)=K[VLMÝIp{X܍Vi&N1C-tcc{p/^6䓢;Oip(nvUM˒AWZ; CKƎ"_٨n#DqO l)x )TNt!ҥ 'YW2TI'+ j(|NzVci1 `SO0g>솈Hl7S>39?zN{,sF',OGڱ)ϤS<_,#6o#Ԉ9 (l` l锄 R5&0^h@b#T zFRLkR|OO£80Go?_"]mkx2յyvPMN8{[}g,<׶6!:qA^buB1ZQ .mh:J$ P诙YG|+Tb :z}uok<[>; r"Fކ塏q0ˣUn4WBK0Hbm3%?}9Q3FL{^wB۔$#~U"]-ie]?6 LUY6/#(PhaLka]w ;괌 єfO'9a;o} ^=Y5zÏSåv3ӨMT\v=ˎ03^i!iMA,RM~&ݩ"Я䈺@2VulZg"MLzל!A:L9~dM>pAbzi=2c3_jAEVCvQ :C>:-i&DַʽRl4·P *_B!CÖs 6||zmP+)/ 58GkV w _E_uWKr;H,EhJ8Ԛ< .jZC!p@y,vfjy/ӒVA('.@Ю'z҆bHհ4(aJi,K3!GaCZh(U[pUT MY$D >+xXq0(-Q`Hǯ_@kGu0pVpN!gS^rG)a%Y3OTw ,a~XlxUfm`4EWD F*D3lv,2n~TlAOR9AW:gW ?CӂyaJU xO"% qH@P)PO=;m|" ȼLSeGͦnӑy;{e9u|lzf6U4ac[AORg~fR @A n}jJ}$+cE(^Tuu\#Thq FGXQt=: a%l*(AD\!g<$8>Xb 910SjO ؿ Ӑ9s5)yjV]_9qcH g#'Px@P(Tn r HI4s|IL᳂aMIfp Ս/:M7s<z՜vo&`0BR7G|?®I]HV.uƄA'='2[*ϨdBJda)Kn593БaVO*S9+[LswadOlದ%^DrW^m#ӊfoOKM| wx Hu!e#~ ƜCyUӝ +ř^* S_. xޣD“et5+Y1OAmP+YE@cLNvZ6+-"CʜXBdݝ<]:KzdX`Rud0?ا,9>A  G({1hX-@h F? :wV=q{8= do!X?Mn*e]3D{qOC[=u뱿Yc馵b'jn8ۚ4@Dk  ˤunM.Bm.J1WAL* C}Ă*P)~6!fmHGVN3vD#` n 7-5؞"9zOS} Y @a=ڣZ6[ ]@Y 8,=(Y.윦:3鈗h`M39:~UX":%ISKW#ehH M`+531'`<7G3y y imh[YU7-ʎ֢ QvǾ~fŮ&;~漬}|Ut/ŁDIxχUC7H~-Vwg-Z,.BЊNy l/Q"4$PhDzq!7f? ))1p|}NZouX1!r' APM K48z=*lK@<,/ ͫb;2&}}5 "ùʨhQz7}03zTҍM+K-}9J:d_H &dTC80ϴ"χ*dŅlOl=v1oG$CンVO=ILlz_ 5OHrBF8aM^jf.C_^Ms4hUJICҬ"S^7a]KHGQ|~sHZ_V[RV mt:NZ W3EuܝF^266,\4>?~Wo7fi%lV XXU!0 'iϠj 3=I=m#0qRI5W`-@˃2}/% PM1JVWd=Cd~B28@jW〜ȮƓ,ӓaJLR#' 633m#C-5h-ѿPfNI9<`5Ih =yJ?OxE8w7 zdYɞhek[oP͙]#8p A04l`Le)zû)dz!no.Uzs7@3xdwF"ˊ~JI5w?Y WZWOts? "jʩW"EE.d+q{0( 1/O@ ob0BN*ZcU9L'T3d/i-w%T<Vb<&e\o*^|nm%>- {w4pyu)8$x䪳!㍺Dgx&S ^ G6HW~W{^ iZ2-'kω`sb/Vc&zRߒ6qdK/12{\|L4H*1pPiv>!g^JU 1;jH-g'=0IgDpBfwfW,,{צ_A 8w %p5q+ל l_ AO [n` -O_*9#lp첯ZqJl31˞gHJmCu7b zWP <"Κ11Z$\usˮ^zetvE\ẅ́yJI:|إ9* QJ635%~D#FZ#ꧪlG3:7Q$P8``MDK q-$, !) ^˺? ;Lw--́? A瑕Ӌ,Nz|\Nȅlt.Waek0](VQ:@A9ǜB=*gڡwlr mDFB#r?KⓀ !>>`TaHpshĎ)G_9i"Q p~:b2!ΐcLVM]ΆyZi;8ku~Yw\$ ij{iVPumظn\l23B0/P~tnS3UrBl`vOPg<7(7ѹIjZnP"57|ɗB` 6|ƌUd_ ϵI{HNo){fzk~ tAP}Uc =C5Y*L#C9ݮ8fpwK'M ]["^n_)!qml%p)Yңǃ z[-| aĨSۛS#8DgXF*xĚ>-27cJvIB]$I xj`wwty5,tY^i&ȫ؞ꯝ q K.&gOMُ>/eZץ5r|n4R;idzmJ O;3T7jcG}6;  p4^#r>=5jT!!->ԙ 8 zObttKxBB -RRC`W.* Ar)F&Mrglr@B^%stiZVÏ5;BA.Wj઼F"uyX{*xHwjB]bVt]#QaTe7hmjfTť?ayF. {d7FuaʏfF#l@ʖC&#91E.VѻW(Mf(p4N폋X $f}ZfMg#o8G uZ%*]YR܏k7Ż؈|_ϯPi!n8k)k2c@PHlG@줕>+oplj3n>1 -9\8U Ub⽾^7mFg-Y<{_v7ѳK !T!ČwUWdͷg/ʘn~PC78=FsR*RbEJUD6,wlT0:UdU*9dW@c]>sx(㿦̊uƅbŚO̶*ʄckQ($͚4GO_fhM6΍ta(l-d=H .ad5p'{Iroyt}sBmdԶ}J }c: _?NU/{ L1cpQ/;go^:9u]=p2%`{7 L2:<>.i^f xaHLk'kY|Y[%龜hE$WlVU3Y'3`؁<$ Ji ($1eֈRwTMdy OM޻x#WZА5"HDN$Ж3R)q$'9}/8Q1R;y24i$FۍX .6VkhDw/$1&^X.<lacZ3M34k5F3G'݇-E V=|]!4Vju8}MӀ4X;HqFQVDu y|}HҚrףfwE4ukU&6۔SL43 F'G5e uQPE3HgP4*ֱ%i˗Lr5?_zF w qWSpܼV\B+oF*aiK_+s;ː'mlʽz[ziUo})BesHe@ˡU < ?HRD:ĸUfcMb'gPips\-G崹i$#.[H~G_AϓbP&qc(V4ڋ+opR0i~}#vܡ܋5n*LW\̕Љzv*-\E"<ј_*NmfrU2rK.3_`잙cXYBTb Џ ³h&ӱCNY_o_=ET|1Vd\d=-#˗4c*6ycu(McdާeArL*75Ɗԉhz̘3u\x3Zʏbo kF41!dQ?;7 Ph >,e0/ɵloq 5TLUKVES}$'%,L!($[ȢLϼK>cB.w;(e/Ǩ9vt̸d@Tu&Ma;_ Ғx(0r !d, 0}߬v !8c.l.&^_kP)ǡnP&ic*p.A̸)O`Imz; xcctp^0Rd35grL2杂#N!G;K?BqZSjzYfBK>V ,JTQDa%]z` [lRo[):b痖iOZ_ƙ F۱[͍;1HJc]UWGlO0|kPΏ=:T?Y [xSӛLK:w"70ѸJ" gH`_ӤMQ}02Gg[4ПnQnSD|e\Ѡ_1Hk'|ӦD)3tUkpig¸d,33Vqi&)w!ݘR=:PW4N䲡K\[rX9YuPؠ7i qsgu rAWԑ_  ph&ZY[=eFMEZ܂]y5V+H K(lTz# ɗUqYK>EٶML9:^H8xQ6Otb:UL&WJ^."3_-%~1OCͫF5LOάdcuN_1Hj͎pktg؇Ch:8iﱔ.GD.1pQ.m !5Ғ AVpx[PmMۯpТG.)M۳Nq euo!<;zd٤{\tR9 #DFɆ$9&aQ:ʚЙ,mW_ܗ>?vikXnce8 6o F exch4j ς״y5 [J*C.:Z>><VN]J m wjvb\2,Ii XBK)QP냈l<{RNt9q1JK yp\8߻cRmPQڈfM8n|$qBUL $k c\[ta&&S֋R7@53u!]$ilE[tA7Ҟe w;eQDI~Eڱޜ8 צ[epdlFQF_hI=4)lNۗS@ߚ|ӧc[:zwumd` #lc& JHnp*\63V"g p7 ȸ6,xC#,Rht۲EN|}(ָҨ4ZQ/?<]_[׃ʴ>Np1l9zA L4.rjK40fbftAc붱s32zoߛ/7YFPe &2kez5NS,u:>ad}-;j{!G#[O:K?zKÃv|%{vd,i"|v_ e 0/_A?V8-[}4+* Z qkTbgJnZѴ%fe:eT=:!_9t!7^^KS-Ap|>L>{쵊qw#r]"9˴F3Z2_:\Zdo wZBe2N 5TAE 9qaaQ(4`*j¸:.Ӻ!>.4^|jsܕú?[&3Ώp>YKR|l6BLw2[vhns}~..Ӻ<(7k0=V-SEN.V@i9¼-mpT?9[-:s*5^%/!iVqQ2,ÇP{kv`i BLF9nhuʆαmpSḯęi'->GAItt?uV6Z7 G7zء_)-+4oB~QEM{AO8@s2 ^s#̟e'/R9Pح9#0M)*(q*»`I)^ڵn8C\|L s̠IiO];Csqbӷ׾XbF̪6fakkRcF'_4JvB֌.\ ƼW<6ނ{ (X>+h8 JW0 KzN]#rNXŏGQ37+ dd:{'s^"O~O0Yr݆Թ"JymsnzJ)8a7Im6^˹u6UWb.*T ej&Q$XLm[L.C0w7KR\!(9s@3^?RܜDV?8"(aphy"[3s$N^XGLkgϷ<מk'gET:*%*y$kr"qrQ& AhK$Dh<[(6~,L |iѡԖ? #A!ۓ-S5W3"C/Ysb3a#L@<ݥ vG߱4*jw {OEK. ǜW@ %+)1E_̴ +nԱ 6V`0V筄\?v8Zi;.Z+&'ݚ)y@FEc/Z1pQKg,X8'mI)_OE׼ C(j\n5khZ/I.\N C֘;'6>j[u*ojK([8^w8ᨲ[ 17a?^h21xJ۪x(2Y(8ΊbTՔ^7^zWv@rqw77km-+tAh}4!Q$038;aCDm:U2@8H Zm+J]rC[|ϝIGWgIU?V־ΦK|lSGOytRf{j, jIO/?gl}OӖ  %n˰.& ZuIZDKek!l Ew3TopshMe9ΘSN=3AzX 3c"H(|8qP &'?%2wՃQ"j:NT:ﬤՊ|TO_Ȼ?1:!h&]+MmR7V;|]9X/2.eVu `Y|ذR< UN6ФepuOyK-hk.KX huVFk#+@/ f KMrtޒqx^ak壞n"#_%|!Ի]c?f4a~"Mte#u`f*8T"@7T &1S+!#OP3"{c@p)f$S;5t*jlx8ކ4Nح䉍MH"@xRi/^;݅dQZR0#`K2~@F@#~:paÜα%x7*l5KϨ['7(}$l9pF;Yg[5zz ? !}^"2 %9uJxmz*|ӦT0CP..=zݨ2\7t,)2Y`4hci U{0MvV~*81s׉U;| HtU~^sL?,W |@vT[:]G5tYwع&d|-qs\ K=W̘EaP{fpAV[gc!IwP~#B 8nT޴L2"4"簞|~?1 2[ǹHW޽Y8ɯQϿðta((;#)Xi{JRIՁ?*@:QX\݊ݫhR_V+ /ʠ +ND+pe4)Vr ,gfԩ{B:CI#\η3mq.WxyoQTSj%u`V!;خ}p(4W;޲5'&Ƽ6!GWͺqZQ;7ׁCP`L'4v\?qthw4EUCl_wī#Ǥt&EcUd$~KnB +e0vR n9MW*t"t&<ʟ+p 2"$d!ob9rf8:TUPHyd ӅBI(땾0ԹngLDfQ6u1ݮdxS_@&܌^7 >=mn1L~N=ayT +Uٴ$-gqS$`~uPPxFJ c0EM9 ]]#2Öqoװ)cFS&abHf.w%*glnn|p\~J!u/iY07+A6'W|)8xUdYҤV:L#z  q c'Q=E qþvd"$'Ua8 H6y^920GAǡFǶ)ҽXUT:􊐵#za뛨A)m[ 3f~yBT9\J')~'T1`e(S:B"LQ&I6@7!->//%wpct;-E xuN2BLB&s΅CR0Q֞L_(TnTSǔ-2=J`,Z._sU"?5 ,hһCIi7q\-)AԞD6lw bVrtQ7b>Z;AXt_GeEƮ{u7"خd0#'$׊Zc1S}D 7f"}pjR ᝠוPC2nŜOC8 *d;s"I@;cK{ՙfB6lƳ,ޜUsUA,RQp a(k_rKueRc/8?zQžL iڥaE 4o_.O4ro֭5y!ӊ-M(>b.uIG}FSi/;BXT+bjۊb<yVek9UAx E*:ް\CJ?ZGIZg,Ush_?i=cf=OSZhRrM!qVjv?{j*K2e ]oBx ,:0Cn# ~#DC֟A75y7gƪ/P']vNseTj v'H;QYUm#5AUWfve8v eAg0rŚ["_|+V5fbn-4ٔ=h-*Q*$^OxQ O]^(\9KK+ Ԋ@V.4jUc!,2R({t7WB߆a(.)9d =s0ט,rq[ZJS\#a9zQ"RkZ2bKX*DOU 8B}"UZkXNMG:8$P\X]a{4cKHȵȹ{/bfjHFCE^=&* PtW9 I, $qC43WՓ J|PYZqP k b";?s(Gy- !\jx\VU7O^ b[@7Ā!` .溄a])mre]4\D`a5PA3R(W[J!uSUZ ݋U杷P/m/26R5Uh+7ݜ<67B(lhƕ:ja 3c a!X` ٪d(KN 0yCa5!VYB3fGV'qHQ&p7msFPydʀ.VYi(ԩsW!|-5 DBJ__k`pݳzW`=Tj\i>!0S(iۑBh[2j?2?_&|AgӚ6H fji8US\XOC Ca.#^a3!U/Lƪ%QmӡB)l|F7w\}I "4I/UP.q!EKDϟ/jj=ow?--~ g؁&ϓD&ƜZWTɇO !(:0$Ioh嵳; OQ=^ F>U(:0 ςbhRK;u0 (*U3fy?O)rRuk`_̠J믎{,"FyUf8xOg? ~R͇9뵲> ._sy\FL<|əѯ srcQ3+(frcuv' CSk(2eǧVtNu(Z-fqP@x,`ԉs|H*qJSُpUwEàgި=mPYƿalۭi VAm+Hvǔ%"rˀb&Y*v^yPkZw v3 n^C4r=XEO*ft`7Xiu߭_R_;1{Gc(⸨4Nw'.<=bծ{q0E}'v/mX6PR@`+;úgk~kA7 p;-.C|ݛftChe9:}*|FB͇Ds; Lm x泃\k6J3Ӱ1WkH)s(IsJU#sG(n+Pz7L|<1I" )BEsݟjr{* ~r\~_w]SC WQZm[!ΘQ "ÏMMC9Z/c>w"J%NNuHf˷MҒOj$m ߚ6R[Tѷ*Yn@OAonn˨L d6.!S>"Z8 Y> /:P:8~k.;C+mPI!myM#FM)pt5n5N4%X[*ʚfnnTkFeh#yAyxP,,YBӞ0Aie5ی5K$n<ꥍݷC˘$J8 OkX$I9T,9Q`z6 lZA.\l N:j޿{HqL ?Y:)aw fUDeZ"0ty&< Fṫ%/?5 i ̈́sܹWoY_Rpx2{kP'3Hg1Dstd_>s*NgAЁQ=),Ϡ֓}b)x`s#P6Jv)p`łdRq*ŋ,1C~zyKvqصK*B>68p,)#`j8O [N iy\$ZjV{ϓ|aբ!:NjmǀkzW-<834$ֵkwcz2/и(Yhxd/ZxPi"-,f`ϋFˬdgn'XAcCұڵb̓U>IݜdCfHO^_½E2M%kY VKP,tj%2Ajb$ICښtCo&c40"y!+?#nQm>xao,p:o?VwT=sE٠2~1j8ދ씀\ۨ]cdidc ͔ B@,`n^Sz6J٩O$(w 0Yؿo| iz$*"ȍiK%kBSDsZr+dա׺ |2%)?د!ll'gqO 0U[f_)I)VqhBZ,)5kL,0RlDOgnpo0(xFKDJ~GaEͮ+X^ u ظ]FGL/z٭PP*tp`? I::'%'=6.4eYBWmx )ka.Bn30/XmlhWs5y,W4zLjEjWY0]*,o8S;&gJ^:@=:MtvB6lyF O¾? ^Ky yg=;Qfo$@j =>q 90B>iՌ-IChהʬh eI*ދW-Qq,mf!EG!grF`AqI&v,zf FO?d8l㤵 ܡqVc=Nϔl.Jk(h] yKY]iWD!G+^m{Sˠ &\2~ϿB@y~r5!}5?NK$܀21>ryeMdzb,'\=aµ<\],@4F~KᎾv1$GQhiX5x[xf^@# hzk(v:;װ?3Z! Hin_unֱV飑͐SiwE -a:.I.1u1@ۉۍ3B__ Ób#gEmz.. K='TX eޑ~ʍxߟ+ߔɵnCwr?#:kWw@v\30JOP+sTqnjF,ۈgSv u&0~:06@mΥ< gք.8AL3#cM52?.vV#cmM*}wQnK;Y+fB[UCTg'@OT?rBb< N$O 5(+^o @떥cm\Vh:@>ef()cgMF90e*WLjܳE+c~LρX(jg4Bw%7,†5R`Ru(yge9\^5t'1T'*3U'l,zfٶ2C\jE7% Jbe>NolA Tj { T`j80zM\ qہ^3Sи;ŷ"^P#r|s ae7Q뻒O7~6F>Q^:p.@ɠ#ܹʢM倻|DDvE%iAȎ|g -8D9Mqi# ֶq1]/oċۧ2FRe6{ւhs?H,f" _Dċ9 LR: ?/`Id<7[;V1wB6SؒmL%`nൿ3IЙ|Fq"\nز8Y(ݾˆ(oJ\4@wĬj:I50^d"zRd{bベ9#MUox2fӴ""ۻiA^^]HH,p%JC"7NAzR1A*X\5=Z5H_F >3\<YFd$,zW+:qp`v5>[Ÿ3eeT_뢳.xOE K'G3gk=S//_&Qqs2={Uy&VCĴ?(*hxp [A cHHLW~x+yf[R]9a J`RjI+ }:V0j.ƙY5Aw n#?NJKQ<;c90}U 5'H@_R6]T#[9TFV<cR`R'*,Z*+gx`1J cs@\_EX4l7 ~%6{V i=s.{??` >\\Tnar$kv-Q%xI jpvL,ǒ`!4uk~t29: Bqf~,zNT}DJYihԃ Dlѥd_R]zZX?R͈.M0ZQB 6tkAFjV+:}U 2Dt! Cbn4kmV#ڳ${O\eE4mha4BB^gYI>j.ǀ8-vU׆Dor±NnRpkGYweì"CQu;4=T}0޳Q*qy (V IN%p*VTonqч`sP;!1fmW[McN^8)UƎe[I22vx s:Rpo/nkʠ(5UK(Sԡ"%?tm!Qh'śz F9 q42^^( gkZ.0{Yǚ S>;% `K0t\{%_JYWFhۨ8ώUZK߉`|`d|NjD8e%sKPǧz牘Lޗ|`ry\*r1TM]]wȈqYF(43T6 r9J:a_*b{,SxZҎ>Pc+;DfM8E szz4kgKb`8<){e2N8Jo0*zbq4A1aaeբaNAPr!B8^Ip5٢sNwT9Gcwb-resm'ovaid<5f(g--t,eCp?h -y2{+pid W33GAq4T.|:p֮C[OѬhԽ[[4sPqr$.L;=)]/Sxoi%ZؼI2gsĶ.6蚰 zk5tw|a_QH.xb<\6gꂉ OJK> dd`TX>60J_MRsnL*zډ-{{߷Z(\bǑx4BƏ/]=ZVx:k(hٸ`94o c &Ց0>ІmQ-\aī˻UZLqщd쌢v8z\ BwuRt(`(i^qm1I`@_".lw쾸7ڱ$9|[wF3%ccs _sUt4H¶AXah  Dˎo mlX?1/࿎ ;^ BT5V {_(<&?8NV@ `xCVHMA 5YOܩUJ^m ]G 8$@ْB;Rf~4R3:sb:l5C]YKq/st"lW)fL:uǰDA9fE]TLG¿Vt2,\pN9VMm4y#^xo[[9zV`]5~C֎;p|f/{ټJUt͂EGRӿ~A= 1~wvqm ݠ{Lm^f20!K+VoO2PQ1IIz,#F̀ah<(_tsWȹNw`E`TެQQӾ%;rfƷ3E$#F*N .D6wz)J~QUrɁ)͝g~0Uov *sRL6!:٤@ v몢rynEuUE8\ߝ.Gd/? IJĎB[Ǜcpiˬ eJ*zi.y:{Ч{AMO C7Ju=w=+U{IM!@AGz}3.˻|; KؠU$K3ʢ/(! Uzc]'`ztxʕa0~;(AZ23o}c<+;D+7˙Qf3ǴRֵnLͦ|6y#GY5H6~Y)ւݮ$\.mGgW }EL߾}P_K?ni_r.4e㗓{_+Ͳ&!>iɁKN1ZlwS vc;"tLl|~ȁ1V&  d!Igu 6c6[Yө3hvhkSV47* |08N>Z:-lK_̻ے1[X@ݫůb=ζyU PZS`ΉySkyD3yMl+lЈ]~_%]POOc ^K/A1?864 3ҎU/ 3Qg\g|%'fGYwuJ=T=;ni@2#Oi1FFRb=7հ{*H3K&nT0%R='bljܤ#x oi2ZgDR&sحo5*lkйņxr1~XMc~ktڿayr>ڭx\gOnhr0j<'1/}aBi| "&+G.HU͛߃WoTV/`D,<{ޮ2KVz;XT}NH/G6QږT8.-]j!ibm{iAEs@r7UL`ij^1~T ^PP<W]B).#``%e9c0QYIrOW,'~U3G^ TӲxhȲV{- E/>ji'sq8Y=HxP1Nde5DbcƟ E遪p`K<[ U8r^[˹Og,ׁpxdɝ}O҆|j)l\&|iL4#/J!z"E*џ笴Y+ADcK=P޺˙f%纲ɏʹ4,rݏjmF\]/Jy<*2Mwvʗ_ı^_1Qc 羬"ҭ̻]qϔrS!F1h139DGxGnX9Eie^4CBՔ\ܨdXԡ]'jyNLKp!۱7Dm.wӃϮ)A F1}>NW :*m9+-V2;A(|?s\]خ!= Scy6ggb'̧Tos$}EF4UaB]yՠm#oUn7up^dl⸫#PaH jҘ1 olQH|50ls0vʷMoAn.oOG)4CNoƕGaY)|p?8+q9h:PL.V3j ȝSU6z l]lpJ ^Ѩ;pg+K|k3"~g(=ϑJ<-v<86U4fE5{\',ΈZ 2!t魯=ʑ80<.Z 0=A^4lջ(cʘ8$;9rhAO =@*m"㋰eorFW욘l֜v< ݎxH^F:r金RmcW_6%}yqW\gƷ9Ʊj [ :Cb:z/Hn ΩE[֟WgMXaq lAӄXF5ߩn܄v,= &e%u~\Hl-7[AK^<?ki=ޝPk js; U G>U(ΐK4w殃y^&*.Ⱦ78!Ք#9=1`2)Df6Nz))NFtL`(@@&ءQowqjQdu;R5n2̜0 ~] Pc)o }!D.Bjʃs_r ARv-tZ``8dpjXBSx ƈc?sXC\Z\SnvRޒ2J+  1 R_F Zi/N'%+r¶Wɓ3xiN+n#NKZAǍ̼ 8F[\6cϿʭy.nv&.5Yb3ĵgs2L6쨝MYϴqЛ$lJ0X2kIj>.A 8k`9HVVAQpĀƣmjNzְW |WVޯl^ jpQ<[*trJvmwb'w,`wxERGh$'Z*4ι>CzQ[Fk"iܨx|A%>iߑ;#v0=EYa,O6:fsǃ6z>4]i{ۦP',lK"c@|pADN\~OSEtbju/v#.mP9X u;{&IXJRJ/[&Vۄm6\Af R 3H (*Q@NdMuq x]KxRW@s)KQM*k ia"9+!ukuk,d,vLֳHJ68c]+<6B3 e$Sq0Vh+qG@dxOD]MsbU*[xN֓E9faCpU50&[noIwV{JBdifds\;q&G{IYPÊVjmThVQzMPqV6i\np+뇈UC"[cI#eh eұ:Ni$ݗoj̤OJS7 e~D`x^rˀ{OMJ{-"~PNQS^+=]+J'زծ@ Dد#U%Ҷn%23gv5F/rLrţX?gA/'|DW2MA2U3*`~s-6@N Kn/jepwh0Rp ^ :$Hqʳ G`P; Q}XSёQEGS/vV h,tu4Y+kߢ}x8`&P#W7Q1X% @I5gQOVlΗT5vޡ b rTu8EͼvߚQ (a,N!R*>Sz}|)l͢m瞌iYT4E !ȱ `lJ_s:I(GGѯubMA\(sT_p\a; LmKf;C-[2gP=i+li+q_9"%qOɏ_9⫤! ;Cx FfnL"(E.͠v'Y'1Zd]VΩ%]?Cfڮ'č=5'+X(.&Zverʤ4ʔ>@q#_各` (tϭb&1 ݀Mu.@t]"A\3yH6ٗzRWT:[3(iWA]Z"04BIBL;҂56<2gj='7$AuCzJͤD(԰?Wz]k2%? 6/G$E0IL&*;m}V|!t¾+(8pNl|Ll6/^JH]y² !85{wTKVK,C;htd޲rkFƷE\RCֱ=(Ѥ̂N"9ۓcZkcC57 d_L;5"ֺ0u^kY T@ "&.)G?(V:삫r4`Iԭ@u/aWx(h 9\)J;{ib\^nj)4칱Yw 'Bt+[0ϴx Z7vs[,S1Rk-Zld 9x}PY! 5+&`Oo^OkDZ0[^pQ|aԩЈCQJJ,+,O|CJ$S"S١[$ {vgᱯus$?<2z`lgKJLڐW]B5F`;0C GL4MVFj+ خ;}ksc^6.ZEYʑa(lSֿyZj5v@R$6@t77|G}pEfwd] (@CTHѿFYXJ'8laنrU*|sQ:@n;ɈYD4/re?]+:?NO\;'i/a]) <߸VhNþdMt>Ђ7&wbS.j91Հc;C fUaRbL۳៤ Ȫxɪ Q^(9ѺNx6$:p.v &LQB_ ?{}G٨u@A59LB+j\Jǩ_\e!Z US!8Us;B` j$MKMl>Ʃx#=aQؼFtbƂ!(tP( IjU3G4RAh{@l>c jPui ބCH,hUvKƥg݊+TԸ:q j|!eq&qa@ߦ$G=R})yG+w3Z=jAצ"^*7𺮃94L$@%g`5Sudr{hؼ}46S`u,ūApv띿wX . 9[Op N Wy*l`V0v)?ÇK~IĿN۰G|Ί_#&b !~Ug ̡6SӔ>)ڠsDB44و>-%㉨[CM#g5T! ܉¥9I| -o+B37 Aa4N R,@Š]*-ۭ>ŜQw5ʛSOmJU%HwAd&v+jZq!B*ty/boc^}8x$+%]=IoT8>6ՠp8mf8]ez%V c ¯pMDh6XC|FFt5+T~y y94@|PaB[7S"Fw&B X_̔}>g_`?s2Gy]ܑ!Ck `R .V;l&9Wʼ?SYcuAF0[:7d@ Ge,:jCi OFऐAgV -Qa1#T~eon0`U)RBxF:uzyI |N#&)“J >rx_^{ܢf͍%kc,t I‹4,K=]R^x#yK]#J7uaf%3Wj,>&dica.go;e- Ti^&ހĜT6|-b1|%KE0OiB>,˥+$Bt /PR\KA)p nk7Wj^Z̲i'W`E~6ɾxlO=EM<ℲDu-VhT{p]:Zsx f#N?bY;0~[ޙbne?Q!C2=]Xcvl-DvQ^ϐ P^;O51bd9= F̦JEu^3qvQ4 `ҥ}t[cxN6r-xB5jRQFQuKs@ cZE )m98QÈrƕ :m-(~Kk'qgVN[e@2Y+X2tvض2=:@߀#ʶZ^Űiv \TPZB6HG_j0.tmr0+s :Q5~GyH/\MF_} d#TVowN:a`r(柣ZTr%o{/Isv)3~+m6kNW}o*Y ;YD1q8b[nnw vQF.q-Q8*1z9wS@ϓXu4i۵h Ϭ?u ziP tY9cTM=_:m`V;/%)ֻ(l\8}Ӫ&M~}+8E= *K>$1[$'+ddwcr|Ѳ3cFÿ.No!q=ĩ Oز'=:g1'5P:`H,kSOXs1mw^ |\-< `aJX96!sޙ[.Pfu FA3N Yg†nb rbӸ\?zn0I@;$%f洔35l I*>gSāqdr6t"v1_Au󼉪]UN_j/^L+"URMe1zݣ -n ;0ZW^eLEI ¸J#1$8y2ciap,j9J<ϳ2꯼ DxQqYF .2K Vv0/iAO| TU0&5{H2R9x T~K YmF9(*%}EQdY>@tڝwЁowN#› $lT=}kA3P4u^XʡH_<< vmz69=C2Lȏ8ef(=NÎz' J\؞)n[nI{d1V>mjf+l LŢ.ث`e.)Bg$^ZV'$¨&H]]4 n+Ⓟ $»׬Hլ(iUÕcsګAcjGPA$ܕIG|r+Ҍc^%Gon{mc&<['Q\"?b-x=~FnJy_ }C -zJZ0 p^| k)`%JizXG`wE5< ս7]V ִO8 FIu(i~JyY1 j s؂į+. "MK\dL|"j9l[!gVog]A۹KtY:_cO{!s+DxJ(Ia8yai\:<@3u }ǜɰ" -2+}ŷ>M8ȥ홈46j^uypNcF 트}I|nk%}~LTӛWI81AiljnK]L(Y),&$ho&G6?Ym#C1Wdݏpj[<];J^ LJ%`qkד끇6iS~:g`c5IT_,ӗJ!gN? XnCvqPPɾz2dqUFr0_䰦u3/OK4sK][FN$ .amM]C͖ѿկCL"cGxJSs 6$g'of}pb9i77  C5En=FX%36?,Uo-O5҈wwHQǾ o3 hYVC^a*!꠹+],t열i?I<[Zuϵ! A(Ӂ żNj?`6ќ~km;!G8w.}Oxõ[=3MW_b%A$,>$M2vE[];WsOof +|1{Jd R5vZC~ף5ʇքM=M$ub?cCRL=;9Q"mP|{GtWBI%iPvWO\r6?w r\L a͍ 3tڣ9B]#LjHx<]ӓ2<_}<{eNb~4}I'~Ob3Qf_qMeFӮXe6GӀD\Z>_fA+,Yn3At$&`:ii}KXIJ Df"%4Y+x-E `"Axm߉k~ 4Y, lƤ3h2 sZkGiZ8,-0|#{ľRK(\nQR[ZCE|M&(}({ӯ:<{~vG`ބVe"r}Um*68 ei6KH7w\7=ϩ:vBZ"$*.WCB2;-QN9e٧&Q%t+0D$ۑ'`>֡#;iRQ@p NEB'9aWszXJ>C|J '-"PgQbS,1'J*r&}ٙI{J1s˨@A?9DeKpeYI^Ȍ|#nޡ$- ) g㕂*xZ)FjFJxG9gL6k p<Ժ@$9P]y<ԒR_i%V|Bq!# O/8i ů]R9Q H&[j=ˌ[1Xonm<mRcwmY3 hˋ=Hs}1X_`kKOmhGS}66sak kO+J֭<{jÿ~(sIRpg݌\=ŕg% M1{Ugl VkiP vD!TH[;lpQ1 $<=xM}^kذ[w`+/$vX6CuP*dsɏ{AK:H0L.c[Ԑp1Q*h1Ck_HSKg~n}VfSPZJP fb)疛 z#}*̻"VܝUex`=5fKZ nVV=I55s[Ëm -¦i7ʚ FI;9WWrSAL?BMk[IǘoJ8=_*0`#ir:VlՉ.et,\+ņCaT E_$1lU .0ɰFXzm]bHt 8͉kv `o1<>(RÓ&u\;p7pCmfDb0qAЧYgw6ZBe^sɕ{'Ԕ pmPZ^t[L0]q܈ƒ6܈FvU`*Hrd+r((erߛVa~^+Y1eLgLXiPXzU/E`\$P^R_* 52RSCUFzE33Z"X+GvIf)KFt>cԱzo3;?q-1 bAJAhl}ְތ݆?bNK+.T[Ud~E{wd<]BϤ[ #k8h ̭ w4_u7eMV,fGɉyM}@a &ҹ1SJQbڳGq)tj7ȷ/ϊBU$*4檑`[(r>exl*o65Q amǛ+Iu=q^T%$zݍ~(gW <'Б=?!Yz$FgU.BY*29F8&5%F SL;N xb|@Z/+UF_PJ~J3,M_ʱ^@u^NR&8NzSDFҠG7b%Y9Vu FJL6!&5RFhR i?cLĒ_sl870ش\,5y!Is(6X k̙lF- ):n@YXXY4Xv?|S⁖yu໨+e*χ6sfߴ(߱vM-NΟ,|RMH@y"ͬhhI|@KEDg}6px_&u~,:t83gh@^T~/^W'NmF$483{juθ>|{ʫ;TqoLA> @^4zbW ږ@4ZKM7&@~?:6O ?ev;"ulL m6\a ѽ"lLF kY鎙;&& \LZؿ-EӛKb] gdW?*|L#5;UhݖknSW[T$>߽~~ؘR W+}.dF/x+G'I!rU(c1|#_2Iڅ Bj[ : U_3&zm)F/[z[nJ͈Lgn}~{kGqdP_~lj U3G<uyv9* ǝ̨+6RSq ) RI$5BДy DѢV6߬fHҺ;XѮ]X||P >™z{FLb<<^K& =[%+a:4j S#.Tt\yyI/kQhxﰟ٬Lo8RVJx5l Dk$wyD-=W%C m}#`hqFm.w.g&Wc jhzA#[ W,VΔw9e) 5"w='@"r ^VZӨ6 XCL#b E60]mU^R}]*C{ ׶j OSg?ȍ_:kȠj^%/~T\ѫCJano'vb씏FߞoZ< Wa$Np$-5Aj-;#(njz-3tq7Y(Ȕlaf$3+Dl# ~$D l])Iw;hr&G0R|x噿I >,xh1)ͨ}𚐔J H%Z)"4+ }d<Ҵw+{}:u\<.OVAoxbAvj.]/wT`Z3SUrh"z`eGx,/r1I^MVS%tYDHe*Z2uwъ_7>!0BFcOP壳dycq`գٜ1{v%,֜!8ZlXa4J`֓|!寪T#{NJApe'F35f-̠f†c 0p+ط1n[T"H1DҀk@8yl`BZ# lUV, C{`Rv t`CF޴ya ZsdͲE#)Vڥ,# z-`)IuH^@f;QXkk71|O$G܉gl>NxbiP9Ю8ןYhWIFo"ɶ{0Uh'4- Wjxי-d"LfhP[lٱf犈VG֎ثcD!w֕o`En5 xPܙ2!{}Q8a<wb(eWo,T&_^#Ťz _%RMY#ivhN3V.Č%;9"0nܛH3"A'}E4ڟ%Tu+Mf-Sdo (Qm {~f/Juhy?iX9.7YYZ7'-v[3 ]$ pK'w2<ߦ[$HUr+y[ >ּpXv.Dn' CR҈2$œL]5tPx~M6f$ Z4,0Q; HoSdVd?Ҷ;`0=?#]'Ycw'x)HPr6;\Y}S&IZ &ܠHP2 1mUt $AKu*( Dkhj_x(CE^[o9rI[)y,NkU\ņ#d0 -z6b~Pl*esʫ'Meͳ vqL4)Ϗvm^bǔ4Jَ8+3 V.LssZ&ͅǨN vz~WTP nv#[@Ua;Q(6<*5t`*!  x;@ڗATK[fjD8ڄ?㏲ gg@5DqUK2{2 +A>^s"&SwK߉7:T İR> hsz[-F İ쭽K>޶J 뷬4x_a)钝Q ԦRp)d])X9NiO&x0s^rR]9$Utq7!O8X;EkY{ {%eRTCM9[ 27G}Y֐5E1 ս/ Z؍ّY`0ZVcJ;9OgO;ak'H(s{K0^ ΃B~9jXDG#ŏbY29^Nwc;vtlJHTXfdEO mZz WSZ}s-*E7L:lsPZF,v_L_`T\GH7p6;%%Nي&Y {$Oh^Kr4+9U%}x Ѹkb赎̛x蹼5Rx ƀKx"ORn'd8qW)X2w8Ό ,d#I508qIH9T3P"o^dDOA8?cJ2Rmɪ{9ħM7O9#d_O#w/̯6/ƾzz*(yBLkfLy=Y iwn4@qY/3ٸ>}ax &3ɖͨ02>ߛGNĚm~(G.)c 2 N]T!*Zg?CF 5@0ZJH=ku=PRJݣtB2EUR+ci9ڜTupAp=K(ⳃam)5\Xs6=zsn3ygY!:.AbS K kyZ %l\WҶב0Vk(T](30G3 T x(L5~ 73KI[\8UD'-TD7?d=㞃@\Kp(6[VThVv5#tOpT&s;^7RM(|/1t?LIԮH^IWw%*A^ᄟKy&◱Ż/LӢÔOTc׭v_ x#yh*:qL4GOcHZ򹃖o5[B\Uel.B=N ؀(!k:oђ0w٤ЏM+łsPc@<6t*BX[5^ΥR-286Yze;0v\VPSz䃰$]Sm~Z);*5:~ Hj V+:A^' _[@\M܍{K}Y8E!'EyNt.lTi9D'q"c:oN˜M:nP RW9z{HˏY zY'+)xD3 e橛=^o xɩoa zƉhLKqEϦSlߒ:Ϙ[NYɸ~O2W³lU 7ҏHʯ'=d5IRF꼫-N`p2BbXs5!  G\=j˲s {. хiz8X)9eҍz5Fc@ SO:b]um/MMG=3 cR;{-Ճ=V5JYw!9q'4)łA_68h91.n8泏z~L=b'/+\,\nf g҆n_E .OxK%y6wHV+ġ"z+Ds&/ʈd?ڥ41D<7Ӯ[Xӱ2 DAØ3c dux/^ grh(}v& ʥu=+sNaQaEZWii`C ϯ +Qo,Gv&1&\Rj\dسh2<=o!AևaHB/r쓬AiL۶7şNqp]<֏Ե]6"6ƊM`.Ǫ}.ܫxjT%/+B55 R"BʂkHD ݣgJrg p 5`] ʼl(7Lp/טC8Xl'D.^c@%nVlkJ2Dc< mԛ SWopO:|tSdc\ 3 Ik {+m> TӸ|Z.?wRI15ylu0g$7$%8D+n)-K>s#Iu^f5`)//gp h3B|CYc5IZHFһsۮ-iBlT$ٔEm Q96*t%IׅmFk4O7U(2SH=[L ?Vz9[äj|QGuīJeJp9ip]zs_ ͽL7%ٹS:n` Ժ!N}17K b NUTYJgy˸9 4~e^EVQmqz_́=g#ſ:%'2?b]zKαgtjyH f0S)dAlߧm*|`"L{/H_%YKCM'P+ ٮ{u`;j%qiָF2D[Vl1*"Ø^^17Rc:& <atRgI/'$P "BvxBuY1(P,t-a U{ d0A"<BBS<ڽ^*l~ym*6}*綎jEzC, LWx#kڭۏ(Ua@ku )J޷&wʒqwQ.4[Uvm˛1JYQLbǜ #1z-{B{0K/%y4,f. ku)=BP=Ԕ3rědS?r>` z c#+TV&J` ssE@p0yQ]#5Yf?cZqtafB S~JC -̢SR`wʗ7Qg z^!uy疐-ZWڰB ByJIApM1y6lLn=k;Ц*/V^9Z_>FSre~u$0V0Dɐ`d(B= s̗C]Eߨf45{;9r]Hg1DhL 5*=rD)k<3.91b`33,2Pi:Qd.X|H~qR!tʺd'’۠QX9уNNRWlI9QӸl$e y6Zoj:kj ưp("qY]dDd6)K H)SdqqD!UaTz {4~4qXE@,{q(ު"-xf[#vW uPzb%'~8EBԼ+C+/ǦjVsk}v#rvڟS ?q`?ֆbf{èϘsvг5YA *}"!)yUEgXNtPddu5.?`EJziOw!P>'25,FFx8b p~>'#vq3ثhq5ch m[!6G3vBBeeӁʇs@8лホi#G8x,f5>:* <:0gmu̔dA{T-[Jؐʒ4g-0MNnےJ Zq;p 6`^XpWw) R?Ǿc4Kz^#`8[NT(@ނQnrtԉ?ކC܌/4Q &MAH?%>W]^zX"٘tE+cN+ЩA:rB:\P\[aT3KIhTL$$JFvhvEؖ'tdRs(< e#)#d,Nʙp曧s=J$o2kF춓fmџMswXԹdFF)qL6 r c?Z"ҭ1 eqcA_ћz22SId~Ӈ&dɸuVvx]vuNxq0hK݁e*H/8RUh{y7̞nAc͗3aZ_쏷^ŻT82:kΨ 6. Eߕ쇡G*"\Oo[9D](,ƥ<O )2ņCJ"m m^Y1j'h,,L 'UG2"Yk+,y4MsҐ,z`88`] E'5'[5F)5O?tV ىKkYr+yX]h )ny\(hQ_bc!z0fXEZH)iۼ *S$hҭhfɁuрtf`Mx(R|AB`9!{: PM lK1eq! 5w'=: GruAa GK~Sܓ#;~1nPнj Hx J@ٍmy;YD)Un%zGn뙟уjzႛ q};RAϢ{|u;QfMXfJbfؙAVMeG41a"67WGфE _Cr^A4)z.@ P¦KĿ/7ѐ]Bu:_3E~7`Mt9۳tKwG -ZԆ驋1:1-K_ӽU/6MX~PHX@YxsgE2?RwɕtADVPd i͍!7#Fd˒Á;̓r,57S#=eT^|?<4J7KqD!SbN.QMZ1O\$WAPdԮFuT4=0ϒ6XZ zÒnF1mp+%OQ NU}9x[J8^Wl-ud̚|k87Op]";`:]+Ԕ+8RVb|5D35 T`='?n8K0o43,]u5!+l}Hf·=teDQ}Go@d/.yb]瑚"(.\yXHZn޼V~(cgC2f=(e3xϠhL8zg֘2\h+ebx:G%kv%“:cHl_,.b ߃q{>ڹ2`Qb[&9CDEFh×;+Qʶ4=  G`[ 2cE 'e.-8/9E#KB?(rbtYNq& }ȶGŎ; u!cM447c|h:/|4I],P չTOs":: iɥOtݙE.FRPVUW\FF`Z=;H~&Y_4sķ`28٩qapK݁KG` aP \; ;/selĪF{E#ɇ?!E][(1%LP9o{mWdFJMo,$ )_؊wo´l".e $b6@jB 1\!P⮤骩/pd SRmpmoQ[e# ײ>`67[m7xwmOdh!@:8!~L/_)l ]EtngO~]O` 0AɎrNŁkH[xS\L,/3<-kx'IN1AiԱd8-YW ;xFU4ݠƗgx.09D thCCҹ~jmL='"sJ- 4) ?qߪT<%* 4kʒ>HʼntӒ^҈x|fmX;=e{ۄ{KV)ou2R PyKIj%Jq3E%w!;B,~0mܰ. 3Yh,* w;Q?v:hp*2-0jۜϮt>+jmمTAsFh}-憇P`LG:z,#hPfyȐj67ƣ/ aw?B`D&hSeB(v GZqpt8|<,iapGpRwfE_?ZW.+K슭?Jkf ٢qC?+BGHW ;1)w@ :(g;aӨ @bvO" Olvh#n-mbIFiS $.U:9O4YjUk@83?s&~W tvj&ia⊂ƶvpȩ`^UrC*=I[h ;hK~͜Y9f_.v@ {ȕ?R@o AkH&ݿହ00Z!E_As.X',־ԨBdծW*5ُoCeq0xyY:P!~ cࠒa8:# ګ;񏴾o6 ;vY86[6=A[|Jݳ/9DӌM=v<k[NR5urJW>K[k@,DY}0^ԷS\J^Xր Hƭ8)bs2!wiڑG^*Geկ@oRUd/Ka9C Ժ4 ڻ2(] ݆TY~[(aI 9n !=.h#r2pdE3*.wBA̜uZ.ZDV*z"T&i9^vG2Lg$ȼ7btϥ܆{"Od>r]T26rz |:ˮᭈiooXX_t8oW)q:K`J^$>"4pHgT0{`DgH 0cΦ&, [ b.RKENū$sR"0N8֦|CLLSpkBRd_$Xǰ3r-XY3^̥m̦L3l2C#KYj6~'}d.7`H2;b?P)wiD<+w< _,$E6"_t#B!|HNQͻ %lU|7,CT>k1jJ-톮9=X@0#Qs}݉SG ?4ׯQ1+*'t;Kt WB_J앱ֳcH{wuYT Gu@X둘2U0҅O$)2gE ݁'K3Pj]^#f _!<CJ4d-h!}%2N;:!shDr[I[)˶i"?fkԡļoEک^ܶ0#bULWM'<sg;-kdU.vXo P k+t yxxG WN!h ^ߕ)!m}5۞)KNUѺ\(\TbKrbNT9}ZfQI4D=N~UBq!*? NHJQ(/W@N-,J tljc}coFKYfzsZB'Qk9Ȅh Ws',J؊eyML:;KTlE <)ҹGViv'B6EU7)xNH졔Fkfw Nq\ ϲYH^? gESqc?U.fdVڠVfVGjL2,)snӉ g ^Z&5SVe@5hqjjhSo3=M9BCd.>ݤ}qҹr>Lyv>k aҊ /WR^U`D%=]=LQ(85Wr˺-A]a6P9hhψ*4mPt6tF$2RVxа0qr۸E.@/]|Jչ{ aDlYr2%,aTtw^ f|nD.GjCfMQAyS,=Ph{5Nd[IپY '?3CjTd\bUn[&V-x(E K.mJ~F ݼqx RE-}!̡sXI ]QI%h/yޑmRTAaM&_Y3gsM %ɹ:wO9Y[>И)ܛ 8_MhsRKdv],ܚSԺ2BٙZU~O _"P ´` KIUbv"XĒl7N'y0{:A "Տ}bNjh&/kNi?jp.(i( ^;:M$$I^֑7c%ե/O!, _?5fth㕃"<#eAܨ*fNX@7E !!/+8ޯEլMNI8pH6jp39ղ%̆!CH9 =V/șJ'Zc(*EՖĂ~9Ekeb4]==@uWPAgH$dM|衵ŪB, #|4og0 Xm ZqpelshfHu 95BB23h"׸^Uip7dNxIPbz@t }6Z1\Y0CU5"#+ \0ҾnSgzx-͝x,O ~{B~#NGEXQ1ڷA7;_"S]n5![~_k$|q, + fS2VuHݞ"¼OBUXpߏ]6N&ܗ`@M8eYJ `l{/f㧕 87N{4-+dk9X T.htfӗ1U%~:@Ũ#^X}w-VrgzT|^ۮ[BJQNoTϵ;˪D`pg8_AzGhhݳ>`2HPy:Xr@!Pd/7 ŧ24ʄerJq?IQ::|zt̆5PIŇZ/"}+uꎟ%Opu%C ?@fU7{)rF!xfXޘH- gaQGjy6)ICȦ? RZOe Kã gK}.)V ǯj_P̴R+R&*(/(}<hV:l]n~1xfHqzY_`D%ï8j/h@)1NZ+@38W_΅JSr*xᛀ$1ڪѳK&-p=s=㦉Tcid_~0ݱ*r Z CJ\4[+'Aܢ7*ٴt؞|R7a^Wo'l'3[o~5H.6U~g_V3vAn θ+^0v9  aEVLR|,@,kE͍}W5>P͑C0f})G1Gk9*4Dik.h6ZB̟.e<2@ Nbu13 &ϰʿzA>@O0)累 ӈv]u%H@"^ qz y"' a/q6 >$.='@w7r %-5{ȼhW܄YxpP'Qcԟ %etS^4kxhzU<jM|"TaLSi=lPsz$@p}UoʥZI"hfSy<<]Sr[|9jb Ϛ47[ {;9t1%`IJ}$dU84 g`ۧZ !Dú: @/C/&L]{h s) 'lfĥ [1>kC1Q2E9菜P禁*oR:b'E90 HIAr ["AvL/P ~WKWrYًƎp|Z0V@ճf,1b. ^/:X ͝5 \SUBXSWHY۹oحۍ)8p3E؃k a73m?Ev@ZE`Wu?cZ4Ӑew1K )#\mٜs|_IboҢ8_iڹ;3vrWh9i1)DFH)йoR:ߖ]? ԐTژJlY1Am [0ܓRގNDFN_NB1+R.!ŃW8{2K!E\\y-.:[gU@p@Lh5 O)Xec>̊945ï׉C̯EKypzn8a"H~ +݇:鵲2*5aQ4ka3BKkF[&Rg~2lfZwXZ DAzVWTp1V9k 8gXy|ʟ EPҞ;T{y؆24  aMP4;E !5r$oR oɚDC Sf}`MTψ޲cSfba8xQiן>@,k1y7߃vY)dKVta7K PQЄ(7EnQn+&ioMbY߶,/dEX#$:ަ2<"|DNkQL\;^9l N)+Pk0m'(4%i?tC7x޻b;Wen戜'{ P0eh_յsK"G@ZKa!=)*&U=f`n(dN֠UK-$wÀKqhK}F^Y'0Ӵ2:^Pe7,~q*H D[H'1nX;"(`K7 & Z,D6< *ҿ.ǥYbR?:ӎGOق z_&u!>!)~?Dֱ; fT䕀ROxk@ǀ_@NqףO%Wcxg&ӥ*'05"W,Иaid]1qf:ٛ7jb{A1H+&h*tBlҨlN?;넹Z2Y%Ns÷ғ.ܡIVԼiՄԒLE?[Iy8 DD`豉+ՁNuȷ9{g ':8J|NBсÝ?q;ϹE)lZ LFϒJ xuM UTy>텹Mo^GRc$:^nTn=U-gBs}2p9Nn(uV}*pU;afbBJ"Ӷ@(s!0:vŎ銏>B1_LpV}1k+^G4{<-S5Qe.dN{ULІ|bo+0[صj iU׎4q[0 YЄ=Sfvb5mb 1AM)dđN_F(w;a[1UPYc9[y@o1cʐI.au>EcQ0-ZNר4İ|+<=pk 9>!v9-?G7#?)ho֤2Ǯ\lCMG.Zhdݕ*%tNLTƳ\Aڳ1k]LfoTrm7o SS qS< B59X#z9FM3Mvy$ZkCfL%B>X"L7b̺i{0q/JGz{|cQc,"> C\6tdM/#reߥN+l!`B|:&秵+4ƞQR/fmhϱ 0S&i N, g%oL=+3F2)YbdB)B>KAvF/;Q$1I<_cv)uÕ6؅*uʲ+yo+˙@hQ6bB63f̳0 __-SPoãb&n;Q6" U,e9nMUwŸj޳I\䫞Ŋ"/6MkGu]jZ,=pd s>8nlG,+\.ɱ(iUjO#h5ܙ=O)|G @$&'miA!E_+ݔ?kQ^Sɔy_(! W_/X jgTQa'a?MY\24]\m6 Ljdz ag=;RjuBbm EWfw-JTYP4;Hd)!&A+mkjLf!v~Ip?<އzZa%Pe.Z_DāT9NENz?O%]seI$%Dg>T;Ca" og~X@ulnbvtA4DG.LE|x`׿I? ɿʠ+[61ۗZ`h {3Y5C ;jef V}m Y=uL5/7 K\ %LkEcI ט#+֏G_s;ϳ`Az'ĶHD򳘆/@12[=@8nZv5|R硘o2 |^x8t#~uw?Ls1#bͣ%}x]vYnX ۿ6*6a81rJgDMvyt|jq^{í .vv?8XŖ #Ya(eIMxw|fGZUqBelU9`[63ڟ~켆_}j|P[bF%t*G31iN;@:w Hen\a<Ja!l WmvD%` "ti=)_=73/l}UG9|F&.?O(izuUL)bLzZYY. Vz!D[oM լn >t[xf\$LlnԳF@ 6m Ojߜ3@1CP(mD2# 68C??Mܐ!)]d({ hF#k_'9ص.mpPzggM8yo?;eߍ:HAe[i Zgj+xqkIݗ[&XĺFaqqq_`X#?HޅI( ,!ib.~ʑ} u qq8B֮XR/AꭣߜeW<_[1+K45`5q]Ծ[.$)(Xd< x5KϩōsQ M:Z:uū ޑꎣ4aR꫱)=QɚmNJK7hC\Rݭ gu`jn$;R,b>3 CgP4l:8ΰ`|;6F!k'6DOxG IݲdOAT-J$Oʼ$#ԶJS~EFALWK8#Ctp2v؏2ѻ2!{نx]pM_%dGvB\z33!<6 >8"$YPB,HJ (-Bd(h?b-$}ݴ !y&$!1#Q+;?>nH|AvN83E5g/ p( W˒ա;O0$X]: e_=={fKA^ Nh3v{:2K򂷌CUwB: llUYZHn1,[y{#=ި7+x@ܮvX=_`cYdkfe/j)^y1Zoz{ta6i"}b30}X3,>}<7T:0<Ҳ?>2缧X +7`7L{J؈OȊ @Q, f  {@ݗ{B9S,Ŏ|5/TP:8ǩ2Lځy֧S35:ʫ0er!v9+G۬9%\eՋ>5:Ŗq'=ll֓IOrcHg j92.H=}` DXB y-ܴp&dWJƍT1hFUv 0"gz3s#$8 )VUMRۋۆ,]KzJ}*c|s~ ===N5TZko uߖ}KҢUڵ pIR$5sh(0ҋ0#/T4KD6eC8Q`Ѥ"! S9GF+d.6AdgߧlnX;m5`$-=Pq̃gkizE^L]e cԣIݱyg\1x S۩h%$5uL4oo3/\9ybٌ~]\;DnD2kuQ 5gR&޺>9'Zx-LXpse[hE5`=7,-aoCVGx64ЙkD. %nq<<<:7㩁CK*' ƧS L]suLaIl>ҡh#S8|A֭\pl= mnv&49?Fjx4z2u#8H ޝgcq]o<0=QNȎ E4n6ZFf>thOٯ-$1N>iKU~0%}F~ ۻC|8~Ҩh_0 ,/~K9Eon]b:{$.M2J ̡)w и: N(8iKr|xݰqNA^ԛ*\Ƌ?o/u{/3 x=bPbJ'ڒ*>h ω/_(ΰ}_U0#.X3mszu)4X=ޙ7. h]Ϳ>r4[`FMoGqXɐXtڀɋr kSU[w wb:D9 * V zp݈5}|(ã(muᚡ]HlP2oyHj (f])_qZő(VJG |#uAȭnv\1gNgkji(= S/XN`srTԦ6,\%г5>Fy3 gxe:W,2;7_9$ XjYgy} %6]Qԁ9P]mviwAD)dt`O;4j>M;J-6Km=l4ziEOOu/< b6َ:6겜@Ѩ3+] +h6}giSdh2 AOy4rPdIԬG̱L ȑO,sg~ו)Ʈe ~g2buy 66éy] a6>O827gCs\P!sZs:;=xTG@qfHh8+W=z?PCoc(`܂ۼ@>^TmKS/;םZy\ ^(!\h1W_o6tԒR A 2ڳ?)ѺCXس6ز=\7$d[[*lsHV6ktXeO?d bS PV9V*v\dDEn-,:o=&kM%>G\f"&':3D> JJy'emK &䧝b?qL[ CjH$D=:Y?$H ~P&oy}ΞNqזhR"` QۘD &㵠,C}jMΦR:\ai}X[(kq-d54H9 I# Hǣ`?]uRC~E^i`>"WQb LgdA.{/+zSb!rI,h$8F#U;@s*di[A1xp9qj~Ewjd'/Bt0GQ(&y" XF(vSW+϶V Hui h=īQfdOY)_nH xWmfbbGgt;#IwUv>FF,hfҼCuɿt szXBW),}Yt\LSgy$D9J-B3..lL;hh(nxzTZVF€>QzL!`xtxr wȼ@.ixooJ3ARKO 9 $Vay="wuŇsuqA4'ߐ܉EVt$'!g>S H8>Ҍt;؄ӱzcZfA4JFǐZ󘓅s:em,dQlM?PrYj~[kWDЊZNS w/fZHlݣ!Zs-g:+aߠY4P,wwl PyHAqA!6q44H:B\XcW"~CeGl.lKuLт0h}Ӥ;5y ^GS|> { ?YHRKBrrWQDaXHPt ؚ@IܘgbRGa觔܊PC5ܝB1!tj&"F,'O1*HlC` P|T@|ǽ7$Hu[@\.q4 ~ 珯ŦU}|]ɶJm R$r.}Ms.0qJՕb@^ې!v)zX ؕ>ҋ!Vj̛(AS} ?)Q+Kͩ=@P{W8? 6a?EZwm/agJ`\HN>(si7t`k, *ra6p=~`?M<OwP;ƏݑLhZacp3BY/vZ#80~nBv M6s_#HT6(KV̖cF ÒM,=Q,$mS{oدSM/}8O&|ړj8MdU_-&!u% E>V'8)˩EpZ݆^Y8\gT(RŘʎ. 4-PׅR6 qӥeۏ9Dg%lջZD–jEfW-ؤ 9Cisb5$,.!VFi@z*a(jBP[ sDָX}Dm 4OY 2q}:9/B$HoWhoLJ9^l"^&t*_)8S$D}HyPK[gm1ÍU@&7ne3[ \`Ά4X;O#K{hLB69Й"A M=m݈~Ȏjk , d%RrHV{. cj+W h!?v}ӣ=(̗SXJ.3'tUu\7e{|N|QXRwSw "TДW1b4έNԺE嚼/!Wa8+:׬ i;)e~!^ Y8ӹ?!0_ďt Knz}'B})$#ONfE_Q+&=S]xQbj a>+嗁s'tf(Lf̂OՎFQ^ Zqgb5gc ]Եr|mu%07m Ͳ7R [?po4e0О58v(57谗#r f7%P;hX~t9D' @G$X(ރd*1[cgYΥ:=D"g; x,A{RYƇ,9q ͗d$7IYSW0"7!61x…uI쫒Y:a* bƙ(VqZ8rlM1RB ℴ/#C: =H-Z 77YFOtĞEIgt3Rh+MbK/V~!A /Q^kɲ-FaS׎sɉI q٫Cƙ|v/"8 DB"\K:]#7ȫ:*FJlW4/j7)vÚŪ>oGız, wCD<[\zI6Mv5 #m2ޯeYJsGNiL~51ėtw+ %k1L@|DykB9<!*+ qPkoiG PٽcE/ & `T˘ԂI(p'tژIYOgrGq{7 IO8,eEXJ)sTfDtvJWא1Z=Foj>N(q3!P*Ҁ=YC u&:趨XF} cVP7ϦJbp'+ ht2AA]/g2YHk qwF&1a퉛Pִ`柕V >@!iuY1 t$LLyI7.`Fvi~*U$l 88YR-lb7a߇p`{6:1mۣ"eK# ^~Jd;~wp(S`l2ײ QJSL0+:|b rU-/jB1 NtS1Fog9B)+Uӏӹ$9J`<"KJ?<G/-$7 7qinLGٺɏEJk E{h,YU~$sˎmXG- hk`оvrovX~ 8 ddBÜWbv:A_ [\Ԛ6=zg1L eZymCI(f㷋\KR`?W**YqiE $JSqiƍZVc8̓!=cSjoeoS& 06/>LaɳchOM1WNo Ln:| ; ޷_8?) RJ:{H rx_m ǽtJUgzt^2 >j#K'5 ACP,&Kg_ ߫nC)9IZǙo-e!)N/n;$#ea|^̡[1C`=(%XVJRoSrRO'RR2gEzNEmWJ"`TXN#-o'Ω$D%ZDך]:_05ֲNkGؐ_po$t/9՘oA;ոM$霏rWKQMBR` R~y:BХ8zh] )10έIbH.q.!kqo*EΔ\ԕ{AB˳ከeR6"+OTL}`o?EwKӄ [Y6a!K[w+?bJ{aTG5~N.& z+͖sU / q}Wi=i*MP-2DJ|X$Tq2Ygݽ7D0.}|rd![SҵU>mT}54T)=-pd=\+}0h^=5!\WZ~3RSy*\˜+sQE("lѤe51R2^žn]rgGVvNc}fPlsK HFՂWZT)0%;nGsKN4sns :Oa8Z`jznأ;j{ݘR~9S1(R/x@:37${ Z(a$ٍ0'N02|nzKd "I錶0A02(CL;h<)?Σl\A)ܾDLؔx0]&4 * ;O C΁VP,~#\f@VF?ڧf*5Wqh2NPJ94-GE-:ʊm";*sb3liImu  :0Nx-%r`vY v|"MH٠K⸛٨҅sސZݍw]TT EU"ίX:곰g5dW/߳aש\^GZ\"_ѭ"?`L,jEӳwz(Rr53 uxVL/ocJ3s\sm8*# }n4o"[*G!wɘXW<ܮcJ$6sƽ,|T~/c}7IkENIǵ*2eMv~2+gU{*[zt~7T'cZm.N+d p}4"w\$`0Ii賾s-H>WS2IR =DGM.q|ΙM8ol{jHNs9z1wr@7BszH80!糇۶.wUhH:5CX4卑ňƳG|NE2AxPlռ[ eOlMC4vxj0.()>K2( XlO^K*P.u^A?*5=&T Q? B~{^e8pa"1Xrf}[rk2'DE"BqG>_$t+Vykg} IÓ&svnļ'|yx#Əuq;QʯpIfK7T0CᨅUr#*$9u 83b R:цzFi3V+ \SSQTE_2XC)Fn(]p~̤6nHǠ@='{^B+7tzy3e/ td&CzET$Fo3Qw>lWmj,BF ڌSB2#\KI kexWIAQ".]3'Vu@|ӈAW> /*ʎ" {U?YޠVu~P]ngGiFĀ`$ XECɠB깚>2wjDt^v2^T闦;- y9,6QHR}ܚALW!5\KwNYO*T׌oH;ai:P b߇ZJd!q09uf Ʋ\pm/JLj߬"lN@llfEq)6GZAZX-E>w^5i_9S~[u|䯤X^RN4 Bȩ楹KU.#U.ڏώ`̪=%ˊ܁0 7#>62wcyUr8sj288넗f\XkcdR /Z/%<(j}R.Oq)~)Pn4OJ:B e@hݩ:V:Կ:Æ| T~S_`ӓ")$c=zYyٗ[X3e+tJ=4E."WZOaOE _&tNұV9㧱;] [H YY"\=,UEsUA[oL+Slʿ]6eT0rVm77 ť% פby9md6| o@ _>DJUdp{hl8K2AuZXT[5YD <9U@xme :0 8>JbCAc% #3$v ezZw8.xPQKK+Gv8bnǯC`xT~o !v%:C>Cϭtũ267y'`HA{C8IwdQ{ :F m,)ܒQy:Ju(SRWt$j+.PX]D Ɉ21 i觲~uHR۞~6àp(^#ߣN5j0w8 搳 2P3PrƑ <%\_ }GrViG,ař[iceyղ(;:0ޔ@'[`N,y53+0ז FϟaS5( |sk.RcZзreІď3տSA8&L+ޖqӦ+ލblxL{ll&:A>◔j Łn=⦱f;ǐ^$}3>' evhd單oLBW'AA%^ܿ.q=7Tk7Ӹ+ ܫ<-O`cuJE̿~j'C/>U+XqHxMnXp$4`RbA(%F-12h hN7]Ky8آA$G`{2K?HYI J+! K̰P`U$3&=d&05sV@a9;qe/$trgWտ2 mIx2st$s` yhBlSʍq54b 5}G(G4%QwHY Sx>8:fI A,Qa^ 6c&Ŕbhw (iv_ڽv .džγpSbdzL L&z` v L ." |9ҳ~a)إH `xu]hMγĊ;]p=aR3 !`/%n3Zue")V^P׌źq]xBb^x7%s{n2U4Lf*/oǸnCNDlb$%dޣ ZYw)y͜gix>WH('_ey/@-'rdÈvzݣZǞYQ m|$U]<ֳdbvã 9\N[:SIF'Y?_e-W0O T죖fx.&dicwO2vy|c;IiA߹eUy  O;irXÅ"Dln>u1Qn@/c~싌}f\.bO/ͤvFfY_JZ㬦L.Ͻyu0/ef[4G".`-:!p֢yCr:Y姝@ Aa#okx)-}̣5!G@PS䔪2ܘ)Y)CoQ<\w$viK=ߗ}"|=N,&%0vf^QM@/Balp}Vw[ɓڪ>ic(>E] oʷHDڂFcm{շA(͏hx=/!$c刏AJH;@m@\²c#g; Ȅ_s\eII4 _F-Rǵ2mvȪ[A; +`äV&Jn8j`^mT*70BLe=m}n GBlyZЗ(6CM1A~ $_ _[Q![$ 1/}Nueɵq(f`wuCWi,en*ۼݩDƯ< Vb?Խ>*K[`!G<-f0RX%NljB\hP X9@4hBy>fu250OU#W)b8vV(BйHQpB܊;l] j, l) ofW$|;$h-k%7d ګi_0R?6CO ֕H]3tؠN\cpwaGF/h&X DnCЏ o4/ď@SE+`m|;ͻJP [ QU>-9h*,<s!1!gc{*<쭟cyY ?5&IAf""`&>_l@8=A؜ hA;KS"#|c J}aXK DɘBl JK [iO(;s5s4!ST\:(HTB1LHMe0LƽxX=Vs0S<'x3I/Zx!>΃7Vz=RՖЏ9Ӏ@쨄;Z5.@"_X)@"DãF.1CLJDwq=ulzCG (˩ `L!^ШؒD0DA,xDjg@2r`Kl>Rp Ͽ6>(>^=$=qaIUT*L%Og o~`fEk29KZlUz3T1Z)=A|:M+M{t6U򾍲HmA חEe6O!Ww;O;E`2fobc&Z`Ep%NxQ`".ZJh?ȌȌե9.bk6lM&DO-r-ڑ[cf&HAE^G.!K"=?9Pcz*haܿ)8PH2ZC)i,&+bCşʽCTeR]-6}=rcܦ.~;Lrmg-Ԅ BU %.$gF@6  rH_[?Qx r$fD;{ml*޻tʦ+3ZxF(9{d l:N(eTF!_$'%mq3w1wA߯?@[<^PVdkLV;>+f/P2ǤӒpR̦\I 2H820v+?)/W ˄ܧd#Tሬ3 Ъ-a f 3ڎ `z7X2Ʒ{Dz @ZKwD*8 =0GFERu&Sf'BWTt*O)3f[q D!3?$8j '-~`OPhK<]F GxFj@>L Xygʵz:'l?X=̲9v[FpaXƻOs0|#ߓt+ rKV?A8vc‹юκ`Y`*Z pIu3G6rd>SH)jIeld|SW}1Ó;jaқ]dPI` Kؿ6+-$#7=|"zgvRSxD*žP/P1MQS ЬL4ͬ0\_9 20(R-x%)##{q@Huf縗ÁAx8T\=ʹ  㞵3NwPψj6x%܁oo5 ;0vekp5)1<3@=j$%sD+Xړ]@qI7m-gP튂iSK2]D'ਜ|: N }*4rl[=_M*P W68 l}xPV<U1Ԟ.Gbtb) ;˚)1pFdHFIB5wL3~Iz~zWh{\7a>Vi)U䥘"R~vJqQb#,z.D"Xqr_v&e{\o-ȧ6ܕ4wA7BuP/yG194YiDm]T̈R I P"礽_ePżh呉?L$R4N̽e>G{ڈ#b W@#!YgRp;HL_P c{jL815˙q J]wjSG_ĂD4&p"WU}~ev RZW*Ohb )}O”z_kB@jilc*Bv>;XݧGCLMa+Bˉ ykPv<_mxq\da܅`fY_!+|EI/ P Bq4mjCAM4F'-G~,!v.;Z O&Zv- m{ RJ$͖/A3D{q]&7%`nl K<"@>:;;=GDŴaoع .Ҟx\:~%.F(sj6P9&0j?{QCj)No[ :e{bp`O+]mTHC971Pe3:f-w'xRZtZsx[)ު)ˉ~Fš{{.!PHmqkZ|frdM{V *vZڹg^T4_QV2=0uY-q-Vhg>(%Lu{,1i*^K^.R+YTXi }'I8:g~9'{Y3~niz!J5uF7bLhM>p M-5r<}}z0s6F-Lm倸{y6"!a<'iu3Qi6] < E۱]47kxgy|6~qukkcAn qk#ƍm5sQp_: ўCvb־,4)zs‹mR{x#58D` G-.U%wzFKP +A`Wh[0g.ݕV]'V$ аܤU0ڠ,b{e2./[V."P|l׏XFx% t3wz&,N#&EDCɋ⃛Ul4aC t`[66kj'fs?dZDW4}}HpA`g3Pe#??{)5_T[?NG)quS"$b5Q n@HSֲQO,/D .XH''ɠColz3(Ѡlނ%8\7kyC#Q E XP$f(Žҟ Wc^+KB$eUWZ$#/,z8 apظ"b K՘2;J!\kr1VIЪK hH, Ni7U:t$93:(9;`6ٸ,}ۇ|~$EO0m)yV2=\mGBIKu&Jl[M%5-بi$yڕ'DI62,؊TmQ6,p0y66<ʤ^+ĝ!ԊX#A_.DTFċppB L B1-bBƕqlDĿfaA}ly'#72AUN&H?f`A>Tͪ_Z)$ן)kl4C<{]NbW7<4c,{PݓD%?%Jgѧ2LFHl;OL-keP=QTĝ ُWs.u:z&iGXpj1S ]ZLoょbKЙz29h-61@vhBXbF$JkKbǿT`Ƈ^#d/Cc!IfmQH ͪJ&,nA0ua4G%=a 5ߧ Zy2v$d )!5f͖њs8P?;XpU{[vL0C,h;!G*9Nͯ 5}4Q_tF-;0L%7V +nv3쓞{s\ ,_tImLBst뷐{3z> J%ŷw:%q+Tݵ).(*C-J]5NaK)7b=5g3,e|G_RWN-y '}XzMyi`3Ͱ,F,u5Wpw2ʯI[4uZzf^@dnK",&xj21np~9l=s;|K} )d6 JwQ72+N3s/ clPlY~2ّ ^"Θʐ㴿0Bއ/d59o]C,gO1ӫSfZCtUW<<9E*P@[QU{5R \@ .'=ZlBfEl3oZM;Q+*ҕP"dFqL~9'Fb13uGB zLF)Aq?B7]j\(^uG G6床܇:~Hǃqޙ Q-@}5-O4B"η5v=(\+Sq$!Yߘ闙CBDdJL@QY{ ޻^ 7&be#;~m6T2=ɲuYHƢIyЪDLǓ2i " X rD;nc7jaNPa^v\ ;堙 @ӧ.Zr͔8-d[Wnrw ` bé4:92p4ԽU_8?]KsŻ`F>8G6Wtg5*'EXQDJ'X]ZOUЗ{%_lueaf Px,p%OP,t(zwՁxʾL)ЭWl4gj.]Diт62*a3w}75.fbe`+ܨ 8la4u-헅)E#cʜ;)hՃi8f3=aq[6>Dm=@ p钍 䈕`q{06m K0Ⱦb#TIUOzUan[-ÃԨ&bD&of7XT]r?,:h4X/K騘ȄY!'T2-7H ?.6sHc Dq-+Lt;CIo}^Zk$39h]eyim*0vy!g!h<DXƵ?<{ԑN*<0+/UR5'n4w½Y@'\sA/qNm Fpcr 0pW}FնoԒmZ"Boܐ's?*ZÕֈ[gͮ$pK WT>ʶ7Ա,IkMg~%Lv*该rNV$_s_6?xǒ֛ӔEgmYu=MqdɍQoD[rxߖRu h )kXf &<{ΖTAr._y&dzqӶI.aޗy3~/l6ڬhFEFRPsR@v?T1_ԯQ`J充:8\35u@$ğX"]RiocTE>@K:'89J'msxSyu&΅ dGƻ&G #Y:cQu;ly,*`mK$^;6||襓닙Ch/, Zfb9*V)mJd^G|SCaz #`E=L*!ry8LQnd8?@n mxesM|DI.fhLSAF9W/RFA<o[r'aȑ>V$bI vQ@j[Zȕ;_@/D߃ I ϷS>@9[$1;^.tŲvoW[? %Z@FF-4J"~pKut^BX5;ә]&\SE}ho(9 `7䫏#@*wdm;&J\F!;sy\jL-r p<=mu Yuh9J`e{Ziα+Yd/7Ƨ{QPykɁ6f\T$@GoP'c lKr\Lf dgh-_\-vW0vqw_r;U|=$)QK.ܓ$FY#z鬊{v5Pc&eFc Z8} ̪vf~Xǃ M_(OķG- |;ĩՂJ߿bp  <.#pn}bXoVA5fN.ocD O))­\i{1Saȧ TPӮ͖~$y \Aܑt7TSj\c{i,ģoPBe>DASBEL&?K Ŏ3lAsgbʼnܾOU|-,.j@.ZM'ɗtTTPzhR3؊\K89ҲN{6$߼/yI4ڎ1"ۜ,dZdžEa|ȇ[<^ZD@UA 2zO6eoԔxwqw <D?Z gR`uO tG@4wf9^LĬtg5&:Agtn{Ar$R]w3dnCtpI]}-<⒍jk@~6f(mE7zBcuD. ?'+^@̽qX egTq,mz`v}Ig8&;TԭC5LLid?&Yk1Cu[cqJ,4\p~BoZ!ņ֙ݲ슒$b+%R-_iv#*e հb؅ӪHrQ]̀8z[ZQ3o>Ld]0Fcߣ5:NMljr5-:A&$BS j b@3YEW!\^k&A:O$-DfZ^5oP,[jRxguE$w)B^Hr+۵" aiH'}Q.*gC8b*"Z%u_9N~ApFbLW\_XyC* |~ƞ'QK !44 Oym%ATm/ji\ʺWo|*X.;/((]ZK+*9(~ '+b3hUEn Oh7E>O|S@hŮ̪M7 Ekd(hk&%&yuóQ[QmgsR6)AaH#bLw"w@"[o ֜ɅXpr*M'h3Lxc)> ?*ODJ{Ad3+E!*ʠtɫ_&V,bUN]i 8n_s԰ߐbdKY+Nn--d0^L0v'qZD&}4BӞC rtVz&5hEO!u؇ɸHnIի=trMUf ]Gms'i%:mug&{)-i㎚ bSI9g{1ϱ,K8]G6ZB q㈘\Ȝjԧy&ӎtk] Cy&B P ;9f GH6 #bKQDxjiNMVpWV\'xNvuXSf2' D;̼!Y+|z~d| s%4KjK >3C{7I|_MSqw8 Ѣ}<&+)R/UaGv^K`[FfF|J2ZYxw;wPUzA cN'>ɗun$Q᣿Ҭ` :Л01极Ѩ\\Hl<$?!hsÞ%~Px}IooxMP%X͋7a"w36L%(@HgG&wʒ $mu IKdk@=f}986&O[0ڛk| 7?"lmz@cYpY| - Zfv6q. ,WMb1W @2%&mHt&hK /g.*Mz g ꉨ4VIYms+}@Y\S%1f#Ĭ˂YQ5Ѝ^$8rk%24|Ƶvzd8:2DE'e[ۦ\Z RjǸ?s)ʋ ^/l4%5r{IUq, $Qft薱s77}~ި,*æzbΪ*Ccet.*1Z`kB\,KSd 耫z+UʗFƞj TkJV̭Z #].Q9~\0z炮)bjS}=yBPl], K~Tl4Mj~я078(3QV2x =Jxxfm_ \ksh_ y@|IBD]y.RtᎭB2 Kq9n^D8#'农~^v؉q1OVtZn!iLd nP22ϔi-|K,p7 dM^ªAψO'c[~H%rT˭+$> %bDĶml #z6 ؈*yÈz??>0>Rh5Dҩ]^7z_olhu6wS$}SOю5bYܯǮwo XN\2% z+)s]QYJnaÂաX ɰTx6 Qe=x˶bOHPN/WUVO2-˞L`jkBn}+j yl{=bҊ_ ׃[cFZŷWr);%0HA_trb2ĵ Ȗ75*1 ܠAh zVCM5@e3,k >=P*jKa#}YťyFYn'c5 ފ͇ >b<]rt#_'y 4T[\0@hY\[Xp\x`}7v Shk/B^7IMmc,?=\ mCD@ =Ւ #?׋UpA#WT-B/Wh^"D<Ӽyn6mc汙dVqd ky{biy_)U)&'>Jiv)H,+6z Kd u "_ݰ|pQηVH]3NK/:$z8 %NQ.#l4;m02&r `И2HРFR`sij|yx+gƭW/da0ԉ9L+ %BTFtm 5 O9lq<$#;.707Dna͠i@>sE{p&]PΔq{Jڱu$i5ܘ_TRd r+G74_z.B ц$,|ϰy2LC0U!^ͬpWL"-g8ۛSIPhF)px̄'5c{V `$Nt|r/@鐶oxmḞ_F磞sNj$_sA[yu3!wəvǨ(caB4l"QTnb? G^wp>0NFlGe?+T^R+w+/x"_ju _r1 iL0/nM+C'wYTh FEv 9b1y}#v'$l# 1ƯםᾞDNL[ ;Y 6fw_Y|6yԷ X Q4v[x|9Wa#m M/ rl͗#wq"b*Cq5! \z#﫞a1'B7x ݨ6fW{掉pB߁ 6n^i :+O]a4:IZi*z5Z#Xvo@ȼ?)s)Ho FAx]9BF{EZ:$KOp!{ޝ}fHR*'gt\nuߡ>ŏh!q+hLq' $:]daնCyx[Ջ2pGp&vIDHMӹON, mBCˍ/0<szw8YFrL|#vܞQUˆUY#y5Vu%T7LŴ"%>*7*>٢&c6Ң'd\&'384rdrjӷ68,ops8ԗ8|}Z!9*艽l etRс8lWMFIa &BS40vТI@qе)acz;& b+ l<ī}U%)ЍxVI83{;pI֝I20(b+e=( Z wkDZQn5&|_=&[(oD7cNhЏt[? ]#=4q404ךas2tсbexvu(WpJ5Zf6J 6$.D:^@1ݤF5×` Y3s^륿o`Eo ``ZMHB@@s)7BzNS,tIȥv혤ǿQ  YXڞѮfgh&Jr8AdU7A2gx gwcU5"JCyEE,F}>n}@K}Zѥ) jtxi;{t9r<׭2KZH恏wZ{%vH/< eO}9S? "ZD:b_ -*咔6olaKv2 U'̣ʲFcOrݢt7™?[M"_ی( ~hsne̮a$IG6PV _yHB%7]}[ebA@}NKa fSEFkГ:r/ *u.4g6<ӞG[ \#rNVb#,~\6TtEd$.6~r(#in~+$+11e^GIvV1es}_P `]@:U~B[}:Rۛr =IkYMJiѡJ6-`sg4fj-YZiQu_{ْ촫TgcV:W3e~$)ݢhVśr MqDzM'8rL]Iѯ]I!؅? NY'S~ڿtǣYiYBv5CdkIQw>Y_UFʇG R]>eiW0I%,kMElvо }:%|zŔ,U1)6B8 ;Zy笄/K,#QUyTZZX᎑`1æŌe0(&z U´)';[U&JA3R0&&nMΝpz&  âSx&Pc5z0L=%`5ZŢ-8{rR +$wB?fb:A!5˧m >4so_E"ey5o M@Ev6&KmZsV<DcK-.0Y'#ۻ%/5x5aœuM,@`CY%Wǒ;1HpUZ#(QA!w{4yQBbӖ*x%}T4zc؊X5٧4'%G=r' 7瓌Q8XZ `D9Է5~Q =x֐>AE1I!7NCkO.골l(?VЋ$.)} b6o&'5%MS!{ι\0#e1XBFh#;И8=Xۻ'=b.?dz}[ _bcfaBE@ +oM )oP@c҆"XrH9X(Qْ@c}4Gi'5ň[{`b8i0۬a /m"< RLsL>gМS 1[po)5ɫF+]'R]ɮvhݣRamsHЭeߺr+b'6]ZDI,r뀱kQug7_c;YIމJ7s0t'/y*4cLA: vHCHR.5o-*G3wQмC2Fon'm%JT~84#{B>M1.ZMD<^&82 l!Dž)hB_j;`jR_r!-e;rCapG_p%6鍧&NyOL胚4zN,&tu-h{-)OI`?Q4"euF?*3h7( 'y2&BkP[|(d5݄]f]-?ۉF;،,k!q znM*+ G;Y5]zQdwx%1y\wPӨm% IN'mequhc0l*)\! Ol(1TH3-S Ay(~c>zr=.!u06xmz-GA!Ɂ\B+{`J PDŽ ^ SLa?XQ$  }\tQ{aы= (Ϯ "i[Lʼz[JZŰ 2i>u9J]J(jK2 :k#/~(Z[lvVxӝmbGP:09W eYw@sFY| !uv.{O[&#LKb1wϛp0ǯ> ^w?9.\AaaGq%\3f54bn)py9ɇaZDeF:]CthEkBYaf򑛑3$AtOÀk8wtRws¦GHma 뫣x{(K'oO;A~c ĴҬq^S! $ >8yVRQ ]s糱0:JR'ga]z[g1%;`};sv#hp)7ژ_1_ =xBA .$"N;R눍wBq+FZ=..z,&㌰^=ֿ۸5oh?TWYV&Qg!6i> I{DV1^S+)藕%9l"lLIۻ0 Ql2nxzBz;FkL':!/ x7BSPz^8@;;=FQ+o}yܙ{(rXuc{LQ>_URA*<&mIr<ŶTğ3HJmT#iXNT}_7y }ְ8gÓ7(NHk/6etoOdi'8{,IW@lb=驲mdg A8xZB3@r;{$nK7IMX /, w4wβIb}RCVۑLbV=f.Fhӂ^F yaZ"iyxܦ=upd/;Z_g#,Dkw5)τ׸>[%AXd)V5ׯ40zǰH^fzMMз3d+b_?/Q4NѣD5-z?$4DX{0ve{>n}]U۝"+0o0;Z8LR_lHSz8f7X(Ho<#F|P溛W$@'Wfw*iLYde=Hs9͟a>jxͿV>Nr 3u:=dW7w#9^)fe C5P<]jœUaW>!u2~evȄG:3Pl\yHU_5@|Kl˵9knN{8$L7gR #NJɏ\`W>Ԩ&|0YK 3Y 촑`tkmv޶IjrM26R`rIˋjV,80 ,ɯ y7$0I; q}CtzOٶSk`+ϸa&<{8*/vPa¡zIwm|:Bb(cnK v7P#M?V=;-"-W ~X]׼@!Hv=%Ļ@m),^dzZVFӖ7* Hm贈AfC (`8=OG$^=;gK9O9]}FTbYEћjΈ9"m\@)P9$"\^  sAab{$bOewĞ2!SѺ5Ч<_8*\@-;=ރo_ WoOf&⽪f9E< ]9fsqRԆ0|%7!Oopt^IFQ' J䥕=MN sT 4(Y)ҿOpKHj+(9n kJT>͘SYvzV S@JI laDd8QZHUL, E %3Vzz$x~L7 ޚy5\FYB8[SU#ӾHE?My6E:㣱FF:驫4l]R@sWfTf $T $ lhhx40{R,J]}ï=t_Pzj`p/m? Aol { u*+hBEq7^ 6խu13AbP]\|`hqw%6L "]{g=҅TK5>5 y{|z6Oh1eKWЙm]>,Ύ0T|ֽ#VJ ݰv8Z8|SO :Žյɔu)&)lb {XGM6]YFTQրevSН.$fԺqC0>ҌBIC RrPN*P]{ځђJ:%v"8C4F k =qOI|"hᎅ[7P:1d5=5`_ʞT7SIXu7)E8pK o9 { r.ƨu&B 9sa1څg5Pa!]CY?Ù:k0_C0' 3qy~! 9EcQW[J*f+1BmGfɎ?ssCw0vc¯ z *1bg߃,]kIJ,BS* ,M{^.V1{[]9>Ӕia7STv&9YzG j3bGh+}tLbU,LJRs<7i9ߕë(x:D*8i&j joBZQiڀ ֌y@)&;H>F B`43MF%_sۼƤEe0ZZ3.qYݣx"ӂ:>DB@ee %;-,yԎ(g܆^/JHQ!MMD iĨ zh"gZ:t3J &/[eHF,rSyFsTPn[N;!5y-SQ&hQX` jkk y+NeR>?>8׹Dk[RȃdT/l\t:p+ߐݲO\]|ؤCScY] Fps$( js-ǥ%\4&H-d wQV~4KtѺ[!jQ_e0q,¹3>k.bLݶ19JoW78 m0kKRUn7NS]R^Hn6eLL4Q&*qYM-tOCA| v0w//̾쀜Nf$~nlmܫ- p}Aۦ-MQ**ehѕ}A$侹\ZɠCwyJgIؘPlS(@'Y@r*pL1_~dܤpR.vb-q/ڟ~QU%:P -2[vf\aj9G(3ڞb#@OXVb>Nt~ؽ@D"qIFSG,> 2VʸKAۑPg?T5_?JqM кh:.Uvhl0qI躷k.Jχ>LV}lm {JMY#w־ #)OXgBd=kfZŐ0345}>dDXpeftkHQF5n 1.Qч̋Er-6Hw Ǟ5>mSJޜmVPX#CG X;瓡OMXМfN}`ЈNuanՀU[:t_r n֓N=ކG@=K}CF]m34*WoB8n߉fm s6!γєIUX/'Ƅ%+,nN^r27 07Er0ZLj5C_ F7vuMrx-^κ$Q5',8ػjL6W2| V ZZrp[7T\H-Ru>r䤺P` ֏QTE%ދ eJ #6?\r.[uE- 05Kxw\`'Q}3*UOb=ǧl v8}XV‡j7a WN&F,H.\-w '&OUF(j! |ou:Rժ8qp`M$1VZHgS߶U輒9> F:J]Mή̹1A@vo"P/I'vuE #1.j/ܦYvaY07H-ba{ɰ́ȻޘFao%cYC:c<E˃ȝؔFd4ʘTCdě1\R X,V*⑏dxqiJu=c`r>h-0`DIUZʆށ3n Oʍ0!1&o Nc.t՜jA"b1Sn&g4cX}Vjci#vwU#(l!Lږ3vfCDukݢqڑM͛[!Zc#*{nED ƅz&nyZr3:Ya|  RWq#U8IDBrb# ˌNcQ.DqypVU "喇Y5wҰy! pnh;Wǫz5?5Q?B&cl0i_f7$K#{,, r?d_F0dy $vxK/wpƔ觠dPn?oPl#Q\Z%mK㤶}!|]zPD^"D:kP._ KMI4R "ȓh|ǾNvAm!JUXɇ0vѧ"0MԠˢ-lU4+p,6R/t:E1Ow}k0 C@zW}[:窨JEŽ=tg91/lC5Gp:d[ZKp69 sDeXs Ρ %Y2 +wk#oqraw`iB^AX uPLƬ%|z%\̠26gÝg"2VM ׯg`hpu"macH vvCPjQ~j%.ɩBR Q[$xd1bгNUWɃW]J|&P>=WwԴgM[y'3"k1ubKnO)Y굵B+ VyD﫢NԦl#U玙wMQcM{J (SK!𺟆bbj[]o֧bVW,٬JS8"k42w-e`_We^x!6') {Ap.B;iٴ?tZ$s892 (Rvѽ#\(0k;\ȒD5+Lgcl[Ml'f'g40USҬ_:Ʒ HkJ^ yPj6#27z&tvlr% .<Up. [m+Se k4 wDe F6ۮnb]Ǿ71?= fӼL9h!ob- +͍b+dͭsCr &wx/HG`)>)aqF|2lM,Pi}8Z%Hh7A7,tWap9MEWY%9bhjM<5'jBcR)B9sY;P[slS$1\2չ5pI"~jƶXy-?ľOz Z|t  4^bCp"iy Ǻ0W0kIv("~e|3-k5d@&sB^h<]M?]K8Լ( W1߲P&`B=$ύl-ͪ9Q<QF8^Hv#ns6!1,UncJoܮs硃$Stx&.PRgM"Ah07t'[XL<[8k<KLnr z'nF&7KYuݏ'd[4.|VkpnJ:ugesi@|XElވy[%'Lvϟr h81+:iaKYX_!*duɜ YSzz.mӴ 77؄͌ig*c͸9Z g+=5AT}W5ICA."]9pUIL8H):֜_֖%H |3:$ClB@zHְTj4L_ =h$1i0tZN-9]Bcw9dt9%Z)۔RLFF2p%(0ub´ Ŵ[4EtEh+ H pn SLN(Ą$ 1o[ #> z7ÁB O/TW>nd6;%m(5}x'1hc q''0='8\w& OMOX/ nƝ {R:b95JCk/m1`#Rr,BLzYMNh)i ϡ赧l C=ړC_R4*PxрvV?R;~ӮFyE;~3|PU(I0"5tt6-o{hkEwsb1XdfBӕ((+"5FT>:6ӫxtn'-A t4>%VޫN85w:[jZK<˸KB|i95v˜iy[?0)Mx`dX=>7\MS) /d *è6x PІ߲CЀ^b>ّ*vRC\?k "m|:-k(,Fzn'`7 'x=]rY waL* ,P_''2v2v;9.yv}Zܕ4{{2== F_ǟK_\ã-CkqGY@(tx1 {$FV#~$fVU⌊S5[WO6Fo&uhtNEFazכv8 P6RZU3,; #w* O8XI\6itxhٱWkW/kh p]:˞hK< +k3NA85Ac'RXnP\ }Zsfc`4,I-,,FrhVTp@C,~[U*\ G{iX |g҆f - 2xQvdm4m wgAP kMUԎՂ3@xHd`3YGyޓi0 <;ԋ|2PzRzz&ѝ`h. 9J 1`+a*9#(X֑!L7-UsE@-On&x'u8N>#hM;b+ exk"v2sWmTTM֎%)u7x) P&.ԉ4ڭS])WED|i?o4 Z_VưYHȧ4WH?w]Sfw'[ez6xG`GUKBvr#xcVGrnWf3{iR5=T¢ D]T 7bЪG PᒓF3 (~6T0k>?)3xOAٶٚqȶ.bQmm\vꧏ8oS8竷/jgaWh}є_1zІbDms#^1l J/103 2 #!N pmrNR1q#w%\ݦ$l_&wd,CqG;mП0j3돽`%bu#zZVyh:,V%ɄXP6v^J-lS 6CZND8T sMnLShF)4z *G7w3;rp.+C+/J1(ȿ@ɽ> pCBnc 5AQ"CM Vi%R3Qt2?/KŖtPE8r | aOZb-;H"KaGua`&S}KzY!(;Os mCA]qS}{}*fp>6Qr"x:h"~KwR[(ۭ[h M"bH政h9^_8:ㅯ{7v{ٻ{/ )'64[e)%Ֆx _ej~/0ʵ5.g|q3Al.6s( 4D73 pB9kJн|ip|Ļ;R#;5X/W@qǖӖ'hH{bՓ{7\w0m=a rsoM7f`8fC,^7t. ZڗLվR=ퟩc2xznRsVVOHa^{raHX`prH~l4 ɰoYozy)$?Za eǿa"NA!,OV=`$]~ƟipvBT14 m^ڊM̵q~oa(I?4HDN7b5lmxԋ3OP{M[1/kE lT59i|mgF490Yt9}u hlލ1#øMH)mT`3jWxvn~_c0AB{mz`u\+E#|QPz;)dB82Z.4?n?wzO΀U`f$+I5j$<ԥ%:[ѣy>v@x&~qfhr jW0n|(x*xMQY=3|[KPpɰ0@f#xjh]["#PbdS!!Gӵͯ^ ;7FAr0&DTsRvZ:Huhw ޞvO/4&MNGq>nM2L\TJHޛbDR,+iDbS3έ5Zi3$4}r=+t GhV!,.X8LGM"/% ^J5bo*!MM->p课M\ *87h1!)86Ila̭XDbJaʄݵ,U;"ErEt%n4&ir_k.A4IOQA V?M=ZTA{E*QރZٜ `'w!a$_{ҶSl`kCX 2 pC@GП~{L:j=q`gF9%a߆g\ǍqX֖H3F@R"H-hϮki /,].5ЄA&xqճ\Vymvth+Á,^g4੒?̗6d+`/J°Gg@%Z? ^;?wRVa.] Gfmmen1V⹇7IlC;OIlC$}y-u>%&vW 9nPUQ~5xzb2NRVE5 AlEH ɨ3t9ٰ07p&L0]yA*oy{J>*}XNGq4/ o>`p3hӧA^%["C#JBYQVG{cb+Vbㄧ;53)i m3 *g"-TIxPRh>[Z=yNN?oT( 賯\UCu{+D |{vBBCiA7kN*)J˺ TM4-~? '@^hD^wT<J6@.^VR-hɉ:j.:eȵ3mw !L} e.렓%|=2|ghmC㡭e\޵zC+9JQ;BGuO+NqcUt zN]HY=ɰ3J3RSA%4b 0 kYgϨGZHFde@̨g12%7Ǔd(cYSd_LYp^_#\ uy!&ۭ{uʛӪYFLg@¢QxtRņ\ wʂ?5ALEx1M>a`k(r#$z0ADҪ%hUߧ7EVeri_Z:5(1 W`kt~cmTFb#(D4(q ]NŲǧKzjsa\Oų$#fŋfi Fш|+UwDʩr_d64]/땹 u\{e$8XoA7yߝ eOF9:ɑ 1ӄPu悜6%Zte%~{}} 0oj~Cמ ߪY!|n50/'͒{Bc$X=10JĽ emuH6ZrdTgyg=~_}#VŊV|fi֧jS !*:}ibՋXm\UsuMJ9pk!an㛘rVb=c= [ˈ C%w;Ë ؾ c7jU#l|Z>Iu 7fr<ۊe F&tQ乍U8]yͪg6bl+r !F8"ڙ]qC8,r>8.҉VG\Š逍ǘs4X$s.hڦu2Z;5U^xh&_|krzL/% ||sys lޡ&L#-:ww}_*}KtEOel zB[c mF_-{LqH8Uv,{cb@+ާ~XhT9iE0Hmt17\y Fhde5tCwe`;j!&t}qb6"4hvO҈U5;Z'D94@96ev_ XnR}Xv7uۈ:HKvEnB|+'q34:8P;no^>ch,%إzTf4GEUa;1q):( z6:$Mr]XY.8$3_]C\**MQ  Dmh)jSn|RUrf*,Ǒ2?4du%}Vj)/|{N/X<?WhA3u\XDr{]=jg6a G5sD̴Ujzb3ǯܣ=p[bVl̷B{v"λtF牨 mn?z.aG lM9Fc? |I͋'B_=4λ͞/x5P^)`^MaJ+1;8^3Wiyk bgP"&t=p\_UO[T`yH8zai9ue Y l( B#QGB0}V"~fth]$v*܏t_Mu`qO*$] l&%A~u}jFrm[ʴ wo&`FR!%xqis8T)`/2דo a9j'1FiBO1."f6{ Um7C-e!~Fc0BǙI˯%Z"{:3<( O/l&F@ZHA[* Fx I'05iNH)TŘu^M-6~:@fGX^zc|ǞR&Ue4(e6# `[/,ħ32+T_")o ۦ-{ GכR*'} 'r\k^(RC |5:=={nSbO^`75<^ Sy nᵕCAզ_L,10j6DG[6dw []c T56b$%jGivy_i[mnUUN?#[qZ d@}N ȗVOZKO:0\iW H&fBcY_߽~~%87V!X|.>:jI/Fn/(xR~U7GA:n=T01UqׄO]BC/¹8M{(M4m/z@Z4{/PlVՇFѩ(HϏP.ɅM)4$mA%ҋANI= v_ką稦)BS>n_Bm!QdmePN<Q^Ts !Ŕ}pt)ͩ;I1e^~zi^j ZH 2HN&7Eʃ)M,"CdMb"aD1m32;SZϾ/iާcݭ'Y3·g#~PW@se!g0ҝ'TMҴ}Dgb&: mNHk~f/,̟% 6yPN^nAGdา[)ܯ |^mJ}I5^PpW<&"%&y=0+qOfK눔R7P/(𖀻%ȑZXϵ~f4+}'v|$WaJA!bgfa1CCvH1/+Bq ?Z3KbV,YٮT2f\Kg։ ^G8iYeJ޵"$W\Txi&K# _ eoș8Gxk%}r]T"יdc`tEA :b([x_P@ ?;Z?*{# i]pس#00bR+P  >1X*H[^vM@y~wK1xVcwUYlkw]2Wt mV`zVƓ& !8)[OQfBƬ{CKp]$2م4^IN$G"ʎ IS\::9 -rkɹ`I7͉ƙ@W%~Re їa*VƱwz'sU Znu_(ݣ#l$F9RYuS18߽u}Y>u(-&K}Ͻ~llܮK{VLRQADQeTvcߢH)ùT: tT^&ʻR_F|$"+Bdƹ 3 jz[?* j(BT#wߍENnp/DPܘ]"'*d(6TJCQG@ng 4T[R@L._JWh<4ryK}&͠$Γc/b̓xϙ HG;Ot[r^G2t| t؂u0j~}gnȨӏ;J^?傗Jv %L^F4ߏƝAэ~Fyj̚(lFj{V&~S_kAp 6%K> [I j4 d&ljV#gS]6R |ٳZcy)f.-ϠK BNTC6@ hriJkA\]-:t^&ଇ}D  )uV> g+;E1510+KӉؐ*aւ~I1oCPQ*'y7OFf2Y&FseXsXϡmEJ^_,o<W^`"S= 3~!ft GNnqV:ΆzVI(I# ' h p/3MM"_ٌ?7u:Bq1=CO6qq$( AcZq-/kV', CkJr¦<ݽY1Ka9n1+',zm<~(,v'^?.{I43eH2_@u-7@4y ](s+pz!`jQs8V#+ۤ_O:e ʶtp 8+ ̅Y84͇C"(ʣ\u6dqfGVy( 2Q}"똅RO)-KFǍUr_)A#+e-* ./40Q&߯4483I3|5騸y-!TڣP̰Fع>eOUX wĶ~g onw:ɼD"rxt@px Bc Ɩ|JcUW5أҌq/z@;cA~3rac0!/ٱ6Rρ ](.@#썭u>A"%SQó)ZAZ4OGWnΰuN-&Eh1-]E8 t+@Y*= 7~*Zc|e 5ycjr̾/8,6cI$SVIɄڳSJ7/ϓ{}RG ]#"G׊$wݿƾw^ԊrJ/i=c-kN9Ӯ>3$9%Oh:ҳiٳ"GFۑQƙ( 9ޥ?鵧Vp(yPV%Q WyMENElpǶaQԫA>|>1*-PJ5 R)FڞmER&1dج#^C#H&@Tq+iFX&&񿤅++{(9`[ ?@0O%Mlj llw`gGoxgNÛʡ1L:J@>,0L yqpNf٭,r]rLuLS _ot KDHʕ=)vOˇ! oNejx:=Z)jvƬC!{]WN \<]nThz˜6-];jǃEP4ֈÚ^9^Ƈ@vX-{8 #dM,,/CsiۥMVic]q\xa5c}ׁ @o6݇H,9;Kf3A3ys=4$Qkה'~GE{T˺OՊEPI@iB(5_;1"IXq'h&)CCD6:{h7"i</̪;۬Ҿ3g9L w[g+=^ -,T j6ZjA4T\y75p&48Է︃n ldB,2%BZrcܻʂHN.F%@by(ۻ.#~zE9N.]T eMGm3e$Σ'l֖,,Lh&;f5Q+ӈ _c.N^kz)Yh&.y\Uz')ZvvqDU )9W 6߈gE~N 6sOs,T6m6G2 ŧIst7H>cop#a=7DD(y0= 74̉7 A/ mc@;7V3Qh | r/*Ǭb3()A-㋷NٝTt2z !CψEV8~=M4IP^Y gBMђ>r {>j>y[9;Y#RЄ$P.,Q_D4ЈqB"n2.Jr/O*)v}wYlXxk4~B;q3:-}3Sx7Of؄(9!œ`qO2"|$CBH{k\"m0Ҿq.J\@|F`1RZîpn+Ҹ)y+@}2K-pw&U>q✘kį[UapQ$WsܬNXW7@nau$P88KER H,y?g@ X7͂i?&XߣVG,h))+*}YI"9r7VR -gf7jݴoGdN7]NoD*4ԩ0x Xgl2wפ:(5] xHD&>DN{o4\Y&7L21jADKH(ckt0q'.`3%):PLV*",7pE.(e>OD?] Fʴf f ^hF'_4X$ /EZ @=[^$"ؘZHNQ[wi'<~ ؕhdnboƁ)];ST;&ThWxq&֤f|0pٕpěE0գdћٲ֫YDܳ ޕAͣR1;rWmah&t&c0}IhrBsƧD}Ҕy zA6zdFh'ΜCIBoKɢV$75PE`mGGn T'١l86C :|ȘEM7J(RZی!h߷`TYkRʦPKWlYӝۋCO52WW, ƶI8Dtn,b=J˿8͵h=3q`z?ا4Ly$ݫOY8U_ _G9jJTb8rӜm(j0s8;ǯ .)'%՝b"nmTcԙ](L ZuHzJ-g:*U ֢u_OqD!vr9Ls<b+tX-wJv1JiwUɋɜz 8J>)* 0EdūwzGwW8i0ߓPx?b;X ]pr7%c *$0S,Dí/bź_w)WӔڮd RTjI- wªt\)i'fL$ٴ_4;%XBc`RW6vaZ__VrZNqU!K@c)?r)K3Eؗ%|D#HܞO)\mI$-$e`s#o* >C|#NOʘF W}r$INҜeόvXB隕`'{ mk@stE$ۡ/<ȺOkR7omGHf@%5M36N@W PfNx޿Z91c3P_FgA46 ){9Ӂ$~e}Uz~,#@oh9#) {0"+CqăfGڡ%p7EЁv::}!jJ'-&T %Q0G W7_}xO e So( %0 m!|KS?HD u Xv0~x#_6gίif/oI/FkǢo8~F>x2>G'7dv(~mL~iiꯥ~T# b`hީB#8MA4% N={,>i$&ضpG0\41DcDM- 9<0T')|1nx*Eے2eYkOE$ML}#BB5ۓW_dle*{=q8#8^|(8-zZŀ7=}ʃ 9fdŨk3i&=>ie!!L2GErD|8גgd!MrɈkRKH"`hyF6<& p\)B#a-[%4u7]݈{kjvbgLN合]eQ.@bEao`PKh6di FT!%ql(IIfǨ?)4_ݤ1e+h"!E}.ť˜'=Q xh>QפuFt߻;R?92DF8.~orߍת0ԋs537\+ ^Z6RWZkByVbemd\WaaZv`+o@WXܰw )?rhRwD˞;2h ɏtdR/ܦ+/z;2/?P ܫ] gXć̀LG󞊤#6=1|ޮ'] mkx%Xz3ol~_3B*j"/4j]uIӮ J +㫁o8u`D#eZĬn @H,x 3:dMU]j]Bpƌe[޸du'W,HB[yvSWcȞq }gD:BI)J6⑵iQue̐Tҗy:< $QGu>B!ٖeq>? Ͳ TO3H50؜dP# "!JB)}cmzdN_@(;UDT2&Bz4ip/͌B, X&*Nu~h#9X@J^˃)M΂~'a͌PđzZ $y @B6l@oߙ :&<Ta?g|B*: Ybo kꁕ.M޾`޲@2_H34WS5'k>$sǛED<3$<.}S\9y/9}#٫дm@Wai\@Y5細~ZGw †>LDƑ'*4 ?{͇ D5ש'Gk] 3ħ +koI)t6-XrEL釴`-'0.H&:KA,}RփxldC {RQGIelj[ˇ;s#n'h2kuT  XVob\_Ņ&V`%c)ӣn1媕hZf?tOk3g$~sed⭓z85G|+J^~dOp }ލ [B4弌nN$5c_,tC.WdTRW6-a0B  b sZ%o_wbpcouCk}ћE:d""6}#!ECf 8w1]Pa+9}6:~6 p)}vZ}3PۇDӀ׏~d?1(.0e u9<]pJ:'tǩFM[Jta7:7Wܜ%wJlS;͓0KH"J2]9!^Glp&I"{f1kblZd(͸{(g9@yܳ.@s />wLn'y\j$Ǹk*zKz32 /%WcʑA< <-O@i:v^yx,b)xclqG+bM y ޓ+Hj}CcriƖRem%leA9ԳOl)TA?{of> (9[^\`إ'pyH5j[_xn=.3%-H&%%XXeHƱLFlž_Y3հ/L!'O;Ք+d.ˆvt38U f;$]:RDAL+R0,E!ѵ%ͼzS)oIP/TM-H \5K;G9楖FqGgD>9jHs&p J0q+Ϲ x OX]fI) %bS8(B ̚Ҍ`o-H;L/@ tRnq`ud/1Rm0l2 &/@W 詤F,iʐA?]Η߸ޓfB ]U#XQN[4lf ~>nXv)9oES/""SgM!1lY#V2˟ wzXN::1ub''zת$AЙT/tz^ :ͭ3h=}J.<; yXM84P_ %4 ^w3? (#6}J)g,hT;m8{%l@wG=)B[-ϝ)+Gf#r3 :Rܼ Z&LMdjxL@5/SkA{d6/WqՖBYM%EJZr'- ,)wՊ [t%'L4@xHՂH=|t'pA 0*P(|sd䉺w_j{b{`KyT2aDax۩{oIH1EL 0xQ-9G0܆]j@) kHOj{mm|nlA(1.YL43VEau#W7fSڎWZl!ڶU;Dbq^ w }mQI85ehLKUܡ(/P,U/~zo %|h G ho9Ɏg#h:p6 <_q{i< Kh-$0Z[< m @ >"Z2 {?B.ũt2 9,Ϗ5|_ԖE\ s;u` >SL3cw4f*=w>2|]F*HF صܯ2 R/j~:&̚zB465wP7FuCtk:x Fߎ{'I|3:7a¹N̛4=+u֝ txB՗bv3Xf;d% 5~paprX>gZl^NTb+r5ҹ;4kg,A1Ӕ Lv3nN Pceɟp>MU FdиsCKOۑ* hC'Җ9N dc8s٦2- żfBxzY\z;0Y.?˃O厍)? GldKŪ٫KLm<תA&AV: \ oC!_1ӰԬ>.w`+{s67q &/T[B VmM )u}Yh>Mfm X.ea[fUҰ5Ԧ,B="hM^G&Svߵ jJW,,]xQd_}M+!]% 5a~fJΓiiD[NՆ."dp՘d}}&SR^,?=dGpJzH & %,jH1w\}ebi7Z: )(Ī~{&,oϲ|C+y*.H?q 'WH8v/U(5# L4 ^g6LN!뗔IvH 1X%{2мc6~!ՇxrO_\#2pW%aw3r/TZYYM *o2H_[@[{6?hwl`[kDo_~i!W.:aIřnoeC7i?T׋Ё{Ϲh VAxo!=  "Hxzʱj'sJGmH#{s.\˵)q=+?ZƊFB2.zoj9ͥkyK2zA`񋶆& W?ձo<:'H?ğ":RܡWQm$ddZZ$Q~9̣!RL߹#)~Q;wD 14b˭襌R&z"F3|TCgᱤQDgT]gGS"?H n. I7[S 2Ƹ4j5!^7.XBj-m!6nj&YM8S_}=I\;4#= եI0w8! h8WmjHrXwEz궦C_pDeYXnwG|\okj]sĎG]"2Iߎ0`pW'mb|&##SHMzl]ڣw԰L*Y<[til5j!^t]':̣mo폛sS^2ž1O;)YcT 6\4d7C[R(GBXj8g0#V%.ۂ`ymK9*< +_'QP9ϰwb ?7 AaJ9(f qtFx&I,mOȐ㳅`g4)2`kY i9x)a8~lYpZWplNd <~ZsɈvOxieJAGN$ }uax:W$c7 u4+ɂ.lCCsSA@Cv1Hoi}l~*v(6j{t E8-kH_P)*aU6a.s&=f1&ҦqO:td+qQ"Z=9!Cz=1CFSdj {)o#90ȫcوݯeTsZU2tgECD`pGrS*KFus%b &7]f[u+Tr cN'eXTn#\aefC0n ĮϘ8<%>vyymk  y m3)<׵|m9|o'vVg 5yLz@N\3ITW4K knڵ 폮si+qJ(j!JV t5S Y}vfO:8b8ڥ+3EiZb+PpdBL*=9B $;ސ ZKUVׁ@_lk-u[mj^Po0E̟ !AгX9 &ZHN"lcL)G[2biJ<)-a2"\eknM{EI_;jBh#>NCl^BI` RhIuOlg-5NЍ0µx@qKUJnj\,Kl)6N\$Hl069D7sߤVf'qw딊E/V DÀIPF %Jd7$e;vl$^;lX8GcN.\:VrjqfO_{T(t{/s);傦˿Co*\gٗ?ɋI6r_Id/mGD28pNPQjZigKH}>UkjnxMRqzҨWZ]{̣& ˰J)64Vyq%bF} yT44{c a8>x=Z,ͧ2}r&8@##vX@F} _CHL2qu ᳝'dIʿ/Fíc#⯠z1 {ǫ N8X~0-LcpCnYQO9u rXnrIZp&:}Q-^}#h86G;m n;33;( ~%q3W2y [NJۈR5 vA1qlj:_tD3\ p\1-~+uC y$Ӄұ/\ 3aoKN1Xl+zdΎJT?lr}B1+ԃ:R>~븕ge#f^S_ZU;ˎ 3Ki)"UPߺukFC# 0M8cb^q%qodg:v0-EBU AƂHȬxs:~AFǣnr# ̷;>e\Ko]WӖ9~#pEQIad AozASP/ sx]_яO#h~k:3R']̠[$+X/ǒ% G $K1~K;X0&nB7:pP`qZ.{`ϋCQ OE G@˺U`@ :X=3YV(?Wl96h:2Fw':x(`FfY5C? <2Sy#aᙘ%O^?kJ7瀊e>&)ch*-aFl>wu&'?v3 \hy8l2 fw=!*ctRVH3TN1%u?Hց, ,J!JS9mA7wC |GGw/-ϒ|/ms RrBBLuAv`"_*̖4B ՅmO-lk/O&nM ˜^k[NŌ _!G?ڛ茦#La{\YȨ&ó̽g*ԡ(~W)U,:AoY-f?Ӳ5j" y tʞ}%Z&7z̽Sf.%&e0 vDZЅ2̅g9aH9x#vɵb-A΂7p}7R/'OWKWZvn!~l!R{۪1 /|p?xsIWId3h}Ab$hVDCԁ@j) h«W" |й4X湿l;H6!%\fxTKu(RKiX2lW6; A,P &$[يօA[ǯTU&7 ⷚi0r5>+2nP/JFxp=OU^xt]c|W-9_qqIr\1f{S8*EN~@xVi8Q÷CC9$Wt+3 F"V0a V~8UnzS Za.y0&j[;Ja.8xGtݸz#s TrxPbMxQ2hIyNb$ ڗg|3wDPT17(p;k6I?qb,̌zquwV ٽa]9s^  jHpOoס@CL=@).7JF]ܹ O5^߸J_R-;Ē}v>Ɔ{\'(?BFѝDԾd[gx(.bq&4Α{H bJU'TbH].R U-&۹2]ӽ %tLnkhIHdjxz%,TǂMݳN>TS8_漤t6#ưTlDc~+#/.X)a@"\ts%_9ȝDf4ެ@n>[sZ2L2ib %S]٨x? D!iFYz.r}GӇ'wKFSD&IFN̟r6OWкwkQVhԚ^xqJUfr5A<>WM,S35,]դ|=tcQ=R#yss@xXnå`%bERG=e+';ii>|,97h~Y),Blr@Ḇ G3e>uzԥcgeݯ`G gZ]׃HO-E1c/7bhOB JS4vĞI[_&K޾8UjCx4V#;rd> @^<Dwp=>[Olᦳzm(b:PJffnN/IL)ÿw1a.KʒF4 nͦAd1oTАg_Zpïg%T#蜯Mm-b-XN5>ϝk2.lD 0T 6[]ȔbC%_CCd F(ߐ]i#z?|5iO#MkEp5PvcN_B^h*1g Y4cok=v7J}3Ly["xJF^e}o_]F }]! ixe(jۯV}NJ .BRSxt*ڐm{nhl+OOҭ9`+_Ϡ1ST>y*;6  ziԪ6At{.#]Y{zŏuCsnV"̅ s݈s3Э[KC KT0sB+(!B^x{e]%Ho!g3v̈dkן2;`;i7<F% -f$%ʱ WmNCs7 \z?^7 S2;>p,b"b[AxҼ41"~3g{ Qb|4[ `qpl0?;30jǶMo hLҥFI!j!+iCQ $ox%kgklBQfRAyuh=WqҀؓh?R_7"Kd]) 9ZQC]O:eqȆnЁ#AH'Ph!3m+yC>e4E4?5@;4N~LΔ~aRz$o?fgȶe<ȗf+;SqADo]s#k$34ݧ ~'l.%62C5G ]3.(^c#76lIdJڤiyHbDyKl% A!#H):(}Vek`ߜ\O>˳V'.FLw۩1|T",IRR zo]TrDI\̄3nj'}%qHUʵ " FMPc,fEQ%fҤQ% TP|NxA:ٞIl@lRfb-lD-K %Z 2Kؖ:Ǘx&h,Zv:3)HNm(97SF? qLZ< rȂDC?#v](&ܕrE,Us5VVT ja_,n)j '`gС^Ru@1¹?:7 (pSMxƇSlM~S6rh2ƍqL8bEj~Tk42(*ȆZFMVNZI`e/jtP.Q\NP05\k뮡 r0̷jwV\Ev*e=7N-"[Sy%1bekd*e;,&9b,_UEH=z>3|1TJ6:&-cvv[?,f"Yo'#Sg>ul1ABa#&h9yf`&C롕8vde6BSqT!ZEeJjEM^Gj닧"E 'EQ?.ɪ>:NCRKOQ!n  ҁWR:7DϮL"n2yt a4ik9f!H>ph-j"P/`=Þ3Kаq>l r@jsȇbx# Ɓ wETf*ΝP0 ]jCO7vG Ȩȫ:z ;AmM'DDso3b}B+z{H ";a*IIAqAQ1OާfsYfxXYyHКcGcϩ)og|a#K_x"tsZo|[!rC-q4Q-O2up#;&09+j{Vúձ+A(~ *rxo's6( ~FWKuM\@0W2E5/J,/7Gv wX%ߡDA=߷[_ʗ+$|+Ȩ­,ª7xx@V,D|_̪؜`hfu`g3oWx`6|ImvSϝdopOgDO -s8$I.تo>6^/n/ͭ/0f: ݊&PL{pl.^Hvx>殤,e&1Ռ6RKFT2kFIo,|M~rsW! щ(D[e\D0PC͂x: ѡ_|P+iu~g)&vyrٓ2Pi _^5$׾"Xl4xD͚C5powfS!a?{'{Ar'%حBeeX@~"%WOWgk`T/(PF k >B?R~rm4kHM7Iw3!>n7 ؋LۋT9WrZGȾnH "/#=}D& PC_O/0=\7N $JO;b86mgd2x\VsN鱸rb&&] VT)CJ4`U}$T9lt.,NMvL9&Sw$+E/faw/[Oz$L$7}gsK|aa@PlA0G,]n<[; sɲZ'UL'12xp2>OW~2@ <ƕYAS&؈awv(mȘ x:VDp3u`g߃gCzc:\iܨF'8k6(DǸ}0kiHrik8Wf~.+xۂ_;ʗ"ۚ u0aTLIYv4&{-c L SMbbBAzIwNcM f%M(f G4CQov0ޑ9'~='k|aݯz:3-nQlm&t (S-G(!xȎ >? ث;ȩcc{=g}~#v!U\#$!<&۵3uW06 ^x"D݃ʴqAbVC*3֨1p}hAw‰HߐGV>Zcgxg? 1 Z*_@5+i885- ]2bIx}2f5^kFfO>i#yP Lѧ~#;{=r @D|:ྤR>^S&X@A]/Ic']`'D :ě X*ïưbv5LϨ d,eX(`5"@[/8&wA(/p8/ھM\JzQCoɏm!/G=mI6$`ӌ3*q[F(Sl;&g`Zu`X[*V$^3DL[/c|[Qp%tzˀ!'TqNw-C[9tx*"%d]<=".V9qE}'ژ_{Y=[viI=&tpo'vW7;PZ/Q *gf IVm|oo  zd`uW,Ҷ*,._F^[1GOhtEkN%0z5|mM?! 4|T"MAϑϿXd`VR a)UkfT#jݼQ$)#LyL,--x\ @B &Y|ϋ~ɷ\JT5'ʍVbp:6jy `Aȿo2`Ɠ+oF.xtO4Xѯo( +4M?R&\=FA]{LlFA TS<%=2IfSRVd VV ~g m'4Zi+T+ʠ !1|xQMVEl*_?k[u.XGr47d]j'ed"a*˸MH?FLI mRȁ&45=5} ]eB/> `pΙSfQC[ģ'̶d$}.7W?3=ڗ&}N.B3尤PUh`c?-釯J)V2J3Ynܣu ={Te>jK,M&S2SC --Lܣ^iYqB Ӎv#VuԾt0wٕ#ߵ-]qT6F-}ȁ1j A``'`g6D1 w]tǸxC7mnHәl. KC%p7*/D ]( ])5s&^촄zw.|F0vI-HjYە}fO`>MnfؓNg>ݕntY'e%vB{$ U,ՙ}r5uM2ޛ:ݮs(!'+0 4\&'"v2C @8t- Z?4g ~I\W[4/Wfp4<&4wrg>GȐktZ<l=b{Bf $|y9+\ _n^D>Qu ck(ԻCbݸ`cA%pʄS葘ɷfxP7Dpݼt)Eġai CNo> tJ"$GWsD֑l<[یGK(Y-1ؐ]X%$3׿ "ܜA~ VXZvn uCxjʭ?1ߓߗH:nȈ}oC;ՏbGd/9iʉv)|pEÛO!jGkˀˆ$$uoRT/V@B(/WBɔ5>˓c¸-ГhB wi[nfv 9dcMg)Y6g$B^řS_6#kɾƢely$ OoSoYs4/ԥIg!JmY%mA|&NMo+w \wW&oG㋚zY;KVϱѓl n5"DҮXrL_A+8S FL'07wmw/[Sxчx"tNR8Y=_R9@$^-&z:>jժ.ZP5PPX3IhnY4Ͷ eR.gLxnT=xrj`؀01'0AU&k;ɰ=')N!ዣc=謃GΣɁ=J@~܀*ԂMME& sHb©_mnQ8T N".>iF!c`6]ɼGԙTe¼<<ެgt)ɢRu{%ĖI`5SMP6%].qOs"k( _H>,E4P}? Q{w$>H[UuN=YM%nZaΒCc׾`Cɷu’FbeJ}N!(7j\WA_|(,8-A/{XC]p4C߰@XϪۥCh"YV,^g{3JdI:;[FJ MNHlĝ8aCBlv{ߪL#?|a6}rN~_vFTy[k:#Y s :$^PK}ڨRtq5yQcBO%3Zf/Aܕe#~|`! D@b(<}gdѐo (ŷSz^Y+%QlS31`Ϭ?q YK"[p "BP\e[3-J덯m4)A_=t)xO/HJNkOLe:/n"5 qې\U #;|h?c|6I4(nt* ԩv1*U \CެhHBz$G@o_8d=PHm6kb/h38ٝh7tyPO7cx,3 t^MwgUKOqW cgѿaT)a*kw4䛺k)n6wG~iOB\Z588s;G2-W]J\|XsǩT$vؕiu$qj~ϩ,g*l5%Kt{ޔ-HWJr((svP7i~a>8i>:虊둘 n"W6aDy^~OLl(i2n3]HMބ. ͡5a?HW;.c^S<\\0x;gnFG-mΜSÄc9Z ?R.0V$O@7/*җQjoJ0?lCERg/B"r嫷OD 3V^vzpE5jd}q.to&-"'NbF殁G’s\ؙrv R)F>"M?^CrYzRbhå"CVL1b1Ci#zP:CpAH6wR :;*{iquJ|c8IMET1!;9TvZ!bMt N "5)n(|s߂_U(!&sMꝏhV,幃/\,09C<%V>90D y5!g,Q|eSVNIQ>l<%Hu/Zlw<9>1{̗ 6X(W m_B7C ^pz[ K<~kY,ZsXԪR$-4G}g , u,͗HF;cغ~j.J]P L>ĬB3Ű5b"py#*=kMVݳ!5: >[x(Bߪ¹ /Cqw7 aDexʞhy6~1W/@pc5boj |7f&S555DB !J;&WIv}4"c"-઴Qg4J" ۫V~7eCt2wwM! K5P8l̫Cr QmX1h U٢I\;˵ 0P==ےܶoJ)ۺXɨ9ă&U1jh2iQ,n0-u=j7) hpu6s p ,16V9=G{ ːҥ@f/kqpl Xh*~hچJ9=b>q58?*=DV/oa,nu->`*_* HrELV73f ]A K NRƍYE]YGWhhhUHF6' $`09╄w0,|˱t HCL20NlW-<.ah>{O6A~2+4-Vǰ!pO@{Pu6ʷM6 >a,}zn5ؙacO/PP@ Ǔ풪{Ddp*Φ{B  >w`o"T*o >_(֕B f4&SNdk0_}e̙IOx>IfGH-uEP7Gۑo:`k)RI6֤̀/wf!V*'::uEz|1v,n'cc:t,y`v{ uw*|n_?)ͪLb<"Th0D%9έyL$Y`zK)^ysW|.u ّҵǶꙋF6EگZpQOFcbDoy qeC/UtDRG}[3bl^zn;|a\ 'ZpU[$ob'1r]?9 .`<97z+2 Sj06Pb|We4)FV.6v q+tp]i7R} H<%B )6EUW)(Fs6Jo}F;5a1OD79j[ڲb@[aĭ!?uX]6Cj\<33.5iwؑXp󴓳^]D:!9X3!7:t>4)55c(Xm|'1wN DE*lt_Jd\4qYnr7o](Y}i'H+ß"Ј5}3Y6Ñk39飔Dɴ [51ŇYƴ㜀, I^Y1ylݡe>S9!{ T͙AnĦ89Bc, M&gI f ޅzi+.9 3T'f)3^GO_]e5Ԝ \ Qд:1V? 뎌!\oكA _ ;&i,u{Q>G4?ls7D_i{8ɎCslåƤJbpxh3rE?$NJZ ;eAJVlW`YV-ռer*GܺgQZ?0!nH,Ws :ek值&,R#*gz kF-9ND4=r%ma_( GI^*83E$aо- 2†0Y[>5WGBv!Gr\{5@ wCs돶d(BȫYGtjk9rj.`#W?{tĠPnC"ܶc]vGdbrM{ @DL얃՞8&< YV1vo,n1;"ʉېγh)my!K>9rO: 8b CD+g!鮏Xh'CWߌR#_VHza#gImkQdkXCA)sŖ bIl{-Bw*i8!]0tLd72Mrؙ|CXjIqq& GضW wB_|AF;"E,kڙnF13 9fV)/h!U`{;6o xATȧ+bOiorlHϽ͏ӽV.[hk^ݜЎϡ-X;N϶͊pC>/Z,~^ E䝘_+hp{;ȀСj^?b]6bBWH<&?xgD{TTp7*Hϩlc,Q/MK7H"sAඊm ƀVS D #Z8JΔ4v&;djVq+8Tڰ }Nxr?0iKeƤurXďP'1]VZCŢ ^Φ=3yސϺ7>#g] nާ8O~j48p*W>bp g@ 1fH pN/ƈFNݜ|\Y/d)]I.[OKNoZZR|Vpd`pJ djU;C|ƾVTOΨ-=RΏȟV+m|n* ]ߥGQ >eJڣs ;ʑC )Xa!LYs^bӯg#b&\GQ] &XapًuT"=;-xu0=@2sruT Qʶ>*nIfBʁ#M9$uH Y{QP]S;}-]o9.½Z¶]ه5m]yYq:`tHfa^q[Ml^rh\"7rdVU1'tT &MsBnf 690]pZ>$>u5 Mp3Ӗ]!<$!*s.Ia=<,SkM 5@KRyBĭ+h%_3cxBJ&%΀ DCgST.)K^3緉 6{0877Dž[/z$GUuy's1q@Y)/֟CIJFSp^HKxύ^(}QY!w*;n|ɿ%VպêcN>Ut"QU"wRZEi@O2nZs vCFT7Ž{DeT7<ޕ1ZbW#'yG\,kO Pu,z;䂗iLrvX&!8WBCYh}FxjᡮDw7QG2rI 2d?dCuv ֔C []eoF5[%S_u<~V PU{\Z䖍&yju6N#)$/oR+M0+YD7.N^j<5sY+hNJuI]Pk.f^`Ѳ?[(ƒEZ;-n"1P Sj;''vxOw M'f!4jD3r h󥥫!-؛&i >O\/ 8;sH5ro#~N8,TAq|rbP3NDŽvy~ݢEy) (̇Hp[zeUp<1mxY@]=PYASֶVŏqK|Ȕ1ȦyQ3If풊dbgFk(xgȷ(5%o!](nfdpޅhcd$ӡ6I\WFz#H1gxc% QsOhy5/1@:ΈfEæ,:NM\[@'DB}p(Jz'%g rPfvW ۹jZA1Ƚ %5+z#HRM5߉NNyt~R9{,Uxe(AJX{ɋ'wR]Az No'9/jL! j\7 h,aɼnvMYb=̘I>d/5FIs_~uؒk P%`hQ~qt|FZ&!,GVq/JJzȁiKVh/+y%'dY ?gcdm%]XkN F6"Weߧ?8 i,2Ru[+m*π㟓0C(?Bם6c CƎ"H=GKT*gg9:(7T!etgyƛh#h6.WDQ^vs O2$4*=- R]+dq;xz.EIxjK%WPhprjƤ+b5}bb9zѮVx֋A79Xl0E8J (Os"9m=/m³#iG;f*b.v79sMD*;z5@o8Adq:P鿉[Y&HbܨOiFM~0PIq#Of'g%ŊLir:Ɉ9ݿv_YM7wzR]{J6|rkR -5BN/Bi2쐇Qby852mH]mǥa[/۪"p~샟4酔r&8m!^wW-&hVܒ;<;E%<4&/& 9*0;BXC#eo#Mdy%h#Ŕ{Wib ˪.Z (e}c}o~- g&'] Sr ;o و;/$/w@?Ԁ`1vNJd/>VVaPntr&X䩬qゖ 嫼VEiKڼOx7vŇ溘֑xi&@~WI? +ANa0Z՝iH뭴Ȱ!Ŭ@,j$ Ȯa 3̸~39Jnr!,Znq54A-叭VdrM%gm[Mn 7rc*-c8{;A~F$knYO(y_&x%X0J#; hu@qt{i=mjvx'Br)VIȴG -alSZ5 ILބ7̈J54d}44Oc֐ !mpAeK, 2OXt((3o8iVg).w,}>>UT.*N10 ăOօWbg0C nRC^X [0~Ay1[(fZ ɭE|=iP=y7~ҿl~mcE @& J7+Hio.mm>4ʇ^W0Ziu3whm/Ï9:ra(YʽɅȶ+[hۂåPxr:*ܨt}.}gW\vHJX讷 i|&rG̸C5l+nq\L֬Ȟ FΨ $~OD$*->.R2nܷHr0װ-6"J 8n-dHL| z&`p$NU[[t_^9'g= 4 ,Kΰrz7jDqb9,?ir>U"##ljSgt۽X|N!&O,} PE'.64>3iA%sa iXJd}U tEO;--GMH"ȕof fj0+2@&Nkvڕ./PaxHeaders.X/src_test_resources_com_android_apksig_internal_util_random-data-8192-bytes0100644 0000000 0000000 00000000034 14763776540 031010 xustar000000000 0000000 28 mtime=1741684064.7500000 src/test/resources/com/android/apksig/internal/util/random-data-8192-bytes0100644 0000000 0000000 00000020000 14763776540 025504 0ustar000000000 0000000 c#Iͮq2t\ wX0AU C׃+owغImc]퉘xRwk_V%RjQCjMQ_'uM8ND4jܓ'%oڈ K KtyM o-/)w[`;=TҺ2}cل*m[bx&/ 45UTy;u*MrTFgHط v*\nIH۵R  oUf/Kҩn,)`Ǡ3EA̪ A.uH7ni}_E9Q"eՋйԏq)GБ"Z39-uzW #Z#lOB=UFumkuO .A!5N y_K(jRneY= ژb187H9'kѶZk?[K= x Su'oMb #\򝶞,k䩓+8i#s݋gŽt% He&.Qv(;Wq,f=i!l9 p‹ >8t$Zn $Xc-?';K_/&Ewt f:y@Qci+ٍǗ. G0dFJkBHԸK#Xm7жͩӑ{0ל@N+k'2dS#sD-ei!GՍY$5LJ!Fc#!㤧S1 H~^3N]p\`=&yv|[HkF)KFg(Gf!ֽ[R>RKrdz!,z0W >x/qjs٨&77Q5e}Nj0&L=>኶|G~E!o/օeη_?ó؊PoYwN% AOQ@Ŏk@2C;[1vȃB塩l8\bfd3H9`я ]6ҵFg+b廣<t9E3~y_t"F+0j68*ODoYŪ_ e %ؑ!C[VwOyE[[J'ɬT@ Nt>jD}2v ":kLHoEz*K;QF;>D``v2`ZY5ĴӪ}@4$7Tp1e|Ǧz {H|&zx[ӓ#y!@pE50 4:V,bsxnok[}N^g7W,*v2oB Z/1F7ITHl Kf $gakKx uUjq;{^pkS $Q[Ot_G:go2bO_g}_B!Jw-#FQmv M[(qua/J-p*5)D^ӄ TaIb3J9vo]-5 k*cu&3@_O^75v]sc 9afXmQ|q373(E0˛n5{\А\ k{v!qdmHJRư^AT<+(_V_<R6xqwlX~8be@@rIޢtGw "-E騽x"HCm˕<*4FM;H'E[[|Ƕjjp.Vzuz?,h@GX `j: OB?hpocE'r™7p<>vS;l'mݽɍPs!&HoXmGc` y :[{S>LjfcA *6fRGW$*Ŕ 7 /=F=jN! >ߝj}) iX3S'W]}Y)/Lv-,Si7}rvA[A1S XxyKڛFdsz$E@P={VrGE=6V w|ݦtՇm{(5!ͣiReýx&X{#G`uo0Vs}G?X6&]% YoRߟ&z#ĹDЦ d!Y}w&1Q9i 4"9M~5jJePW[9 aB#Խ-K-B@4(0˲mȍL՘ F(8TH_f{ *q%Okpm21)onk/ ;}C:Ae =-9 n6ϵQoSp0= 2e?]F%qR>]ɋWikhr9A᝱Tﳤ)轪y`2 QG8@;:3-}B{b,d]8wL@"]pGb_^33s.oaFAj!c~Q>4s"v C|l j[h3YH8bL}dzd^+߹8 h{H+D9(jQ-i V~{Zn*jscT@ӋW/00.N}t"|_0׽COvՄl i8`wN+4͚$}kZf#r}!q $e(su+&.20jJ=>B?FBh\g E9t}Ÿ۴@޹ãjc:)ֆb߻l52!kS᷄fi{d8M#AmuV;2t!| 8c 3 4&YꬢAzWlX6r6 %aqaCF+\^eb̙j+~~Z vZG0$L)92?@>*$b,"vɬ=L{y&KּDg4?XVvVX)RdP.!k Nw^oo!q{3GͳyKiYoj7wb|' ‘!T,s8V:6gtKOt.=~(4)5ۻN7[+QP7ܨKAf\a[M1Cb~+o͖҂Mr[;(Yb ~/.݆dnh 3L?%JA[rh'\K|?U9ۘs|]%VͣW'V uce.jK!îHPZ"twCkLlpȷY"QXSn@X|t6>]x6Q|ЂDG)*x.OyR!w4K B%f?Q*gsk]؝*r#_YKӛb4)nήc(aK Z!q*rP),]a$_Xxi;,c\_9Ed˽+v[KO&&H g. z XڧwPڭi ~aJ<,},黯s1|Y?7T2 e?aďuUpV_ޣ`} bZ+?ri=6pf#Lmp:Nˁbs @ #q"hClU?) `^ׅ,iGM<`d`9Wv&[3:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW-|{^r*e^Z)n)k]NA#6nwvkMSd^<|W*-OuDD|"8B{2xY30^cF8n+pKg!9`'6ۢw)Rɕ΢)ID{qUkh &}\oG}feޝ)Uczn)g$^i)W}m<6Ϙ]sy5kѦ+0 ·coCBDtj-sz$DF3{.z54mse`l29q/O+%C»gn}|'[MyPKPK!: classes.dexMlE߮w')IKW2J|&.QTEd5ޝ:سI$r@đ^P"Nnp@?]f{[ͳ&.ׯ_}`[K矕E̐~J]LLP6 @C iABǠ;.KX胏]3X7"x\ <. >GK 0Ծ^lӘ:e`J3'ti-Q&~Oz'ot2fvv_v ~~:5j>=rxsFzE~oӰijfSǪ,f5˥jVX@UP3XB1ӿPKXPK!:META-INF/CERT.SFen@= M;򣈒@Z΀O_kl͹ Hò,1嶢4Yq K;'GR0qavKR,JlVҥW^%:bb\Qmr=0u~ \i,fVG-KCmhtV$x{#`=wN$k]=gQ}~/XoܵŋI2HouD)뮴UN4Tӏ?$ǂU<9V0.HBƮg3a XrFOBkxb]/PK/PK!:META-INF/CERT.RSA3hbƩiAr&Ll m,L  X40hba"W*ddkbm&l(l ps9'de&مy}KJ32S  @\y)E)0n f b‰q(k 'khh`ihid``fb%kli`d`Vg71* 00byLM 﫺LnʲT.`/@="Nq|fvk#Ei97MZKۗy_uv 5wn5iUwTߗ!f۸얼$&A,hgg?Zd5~wpP +XԞZ{XK*]C;$M}^_;*.2+?2奙߲E!k|7]sy$M)BkSv̋Ng9=o'312/nc@|,b,"L?:*ɴ~9+afSϻͻ(y; /m#aK>%PQ2{;|\S OsYkYA^yj{Z;iu!Vǩm&n/nu>Rnvgq_s3ذ0lie/jgibdHU0k0p= {Ɉ|X&ηQp2. ' ~m>,'幱nnuW/+)g}{g^ )'>H|re[Gzk/*LNl:mzHoז?}ll+ {Pv/G?6Cm05'͖főn?wou |T.TI#UG-KIt ZڌO͓&n8?n=޹PK:PK!:META-INF/MANIFEST.MFeN@@= f- Z咢)6DnOr @WS*Rdi-hR]}Y -ά%Y ZKѲ2e)l՘êK'sczGC b7G?EPKQj4@ q84,(  aRPYSĂF\a r00 ƞ0  *H 01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 110919200642Z 390204200642Z01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 0  *H  0z4y92aP& ،Y Ɏ m'p6ڟNr[*C_f*Jo,&v'g&>3Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`G00UfAti;iWf:6*]0U#0fAti;iWf:6*]01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com ƞ0 U00  *H G2ޮDDyD #rO;*863~\GOjx..!3bT)_'I>!=m 㶵Vt:V;dEde gw cA3Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`G@hS84,(  aRPYSĂF\a r00 ƞ0  *H 01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 110919200642Z 390204200642Z01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 0  *H  0z4y92aP& ،Y Ɏ m'p6ڟNr[*C_f*Jo,&v'g&>3Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`G00UfAti;iWf:6*]0U#0fAti;iWf:6*]01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com ƞ0 U00  *H G2ޮDDyD #rO;*863~\GOjx..!3bT)_'I>!=m 㶵Vt:V;dEde gw cAwoUeuBV$0 0  *H  0z4y92aP& ،Y Ɏ m'p6ڟNr[*C_f*Jo,&v'g&>3Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`GHwerBAPK Sig Block 42PK !:p:2 resources.arsc5PK!:@AndroidManifest.xmlPK!:X L classes.dexPK!:/% META-INF/CERT.SFPK!::META-INF/CERT.RSAPK!:Qj4[META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_original-minSdk33.apk0100644 0000000 0000000 00000000034 14763776540 026112 xustar000000000 0000000 28 mtime=1741684064.7520000 src/test/resources/com/android/apksig/original-minSdk33.apk0100644 0000000 0000000 00000030627 14763776540 022766 0ustar000000000 0000000 PK !:p:2 resources.arsc5 8CH@@6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599@@9369370ffcfdc1e92dae777252c05c483b8cbb55fa9d5fd9f6317f623ae6d8c6Tiny App for CTSd|[6å8ɓ96é278é58ƒ62çƒé3584022çéç1ð0527ƒçɓ85å9é5ð2é1694éɓ0405ɓé5ɓ599 one two three four five six seven] -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three]DL‏‮6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599‬‏ @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp dD$arraystringd(%known_certs_ec-p256_1-3app_nameknown_cert_ec-p256_1TX@T|\@T|\@enXAT|\@arXBPK!:AndroidManifest.xmlTn1=I&%mҐA)bE@(5iQee *B,Y>B;c>^_{|yP#k@Ԉ;F &=H|!~=8!N_Dͧ.񀈈ODhb@8sk 1mrui^!$3 h[ܕ.Ej7Y4EwQ"5b#j̀mԤok̻3eߕJrLϭ=Lӆī#?檏[\ɑ3&dxz͚lFJ%8L!!ch|}$;}\I.Q6'גtX}st}wr(2Lw'> iO>-Ct2jG'ϰ0 5]H#GC9UBG+񐧯uSnlYPj2P! *|H=V:1$g JlHVWUq"^'W3\KYv.XUIb߂Y_s9zjqC`|&?#9ZK_>cz{o+r{ƜvKE埣g4׽ֿ&z%4mse`l29q/O+%C»gn}|'[MyPK%PK!: classes.dexUoEuܤ' I\l*ġ|D)QKrƻcgjgR8DNHpq@8 U88ĥRfw6߾7s=폖+n㍓?6^|~ͽ+ofX}i';K4?"KRH_Ez[HuHOɠѷc_tC|8Fa,"xBX!!G7/<d (b 10⏴cB)モ"J`F儈Z1x=R)7Nƒ1װ~ ?;G/F>X~jhыuyy 寎j}qꗉ4}F>ȑ|a0#H>9RG$}Xǹ<(^K#}13jȤ*ePK4PK!:META-INF/CERT.SFen@= )HE"TD|ZcӦNnJMEC* Cef5 '5" 9t}PeR#$&&Γ9a[Q[,H/X{f5e㯍:4P7IpNd(ܽ31}"_n=mZ穬]AW1{tKaHG@XA3|o2c^pX"[T/fY-*[& G,"/je%3_FRڛü_PKn/PK!:META-INF/CERT.RSA3hbƩiAr&Ll m,L  X40hba"W*ddkbm&l(l ps9'de&مy}KJ32S  @\y)E)0n f b‰q(k 'khh`ihid``fb%kli`d`Vg71* 00byLM 﫺LnʲT.`/@="Nq|fvk#Ei97MZKۗy_uv 5wn5iUwTߗ!f۸얼$&A,hgg?Zd5~wpP +XԞZ{XK*]C;$M}^_;*.2+?2奙߲E!k|7]sy$M)BkSv̋Ng9=o'312/nc@|,b,"L?:*ɴ~9+afSϻͻ(y; /m#aK>%PQ2{;|\S OsYkYA^yj{Z;iu!Vǩm&n/nu>Rnvgq_s3ذ0lie/jgibdt{fWr ƾK~sɩv kjJeM׌ݛ.ye73z}3^t_{\Ixޥ&1 _ sxxiU\?`spk$){V2k6~hzdg_>^?+>{Y#%~sI EO=`TZLP )toƏA]w^;ꭏjoN1mC _&o` PKVPK!:META-INF/MANIFEST.MFen0; d$;Ԃp@wYX֊O?,qw*FAl9s5`Z6VW~$@] ZgszUd\Mr@;iAZ;pNzI_IIY l}6D|/:hߠm[NoLTQZ IS#n@ 0ʮ(%ؤn93Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`G00UfAti;iWf:6*]0U#0fAti;iWf:6*]01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com ƞ0 U00  *H G2ޮDDyD #rO;*863~\GOjx..!3bT)_'I>!=m 㶵Vt:V;dEde gw cARp[#X~Imi?NfU$KMR zk 7S><~q H؏0S!d OZ^w~Ҏ=;$Py%PR,9zz.!N?BȽy/Imf>%# ̋߶/NHZ$0 0  *H  0z4y92aP& ،Y Ɏ m'p6ڟNr[*C_f*Jo,&v'g&>3Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`G@hS84,( S|$F|fWҊo1D"tn00 ƞ0  *H 01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 110919200642Z 390204200642Z01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 0  *H  0z4y92aP& ،Y Ɏ m'p6ڟNr[*C_f*Jo,&v'g&>3Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`G00UfAti;iWf:6*]0U#0fAti;iWf:6*]01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com ƞ0 U00  *H G2ޮDDyD #rO;*863~\GOjx..!3bT)_'I>!=m 㶵Vt:V;dEde gw cAdo`zyc乆=4(0 ˳ gھ@>um}TvVFNJơl>fFOcum5wO5sZ'v\,ŏ^+h/DT"Hf % ڎb1r$BLpiEoNG9d1 v/LJ$0 0  *H  0z4y92aP& ،Y Ɏ m'p6ڟNr[*C_f*Jo,&v'g&>3Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`GHwerBAPK Sig Block 42PK !:p:2 resources.arsc5PK!:%@AndroidManifest.xmlPK!:4 M classes.dexPK!:n/P META-INF/CERT.SFPK!:VMETA-INF/CERT.RSAPK!:4NJ4META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_original-with-stamp-file.apk0100644 0000000 0000000 00000000034 14763776540 027531 xustar000000000 0000000 28 mtime=1741684064.7520000 src/test/resources/com/android/apksig/original-with-stamp-file.apk0100644 0000000 0000000 00000030726 14763776540 024405 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ][Aif3m$wGwZkM-UbWY`aMTwlf18sN_[1f+KX|ʪr4x˻T&0F#,f32 hvU_5[TDm?\&:aE7{]zc1Mķ#~Uⶸe¦?\Gz%xf_  w0 0PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y8jN,n{8WY %E}bs?rdgU "3ǰCw^v n(Wӥ<^$!h!,%t3dKQ0%4LƱϜJ_!K(28Yb<ڔ5Fvݵ 2W 0pvei(/pё-_Zqa~P˅5&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( V|8Qّn^QyZbGh] !)5pj/K}"nTA."yvڄ4Sg:z@Lt&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/RSA-2048.SFPK!:ܪ% META-INF/RSA-2048.RSAPK!:,{ 2META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_original-with-versionCodeMajor.apk0100644 0000000 0000000 00000000034 14763776540 030741 xustar000000000 0000000 28 mtime=1741684064.7530000 src/test/resources/com/android/apksig/original-with-versionCodeMajor.apk0100644 0000000 0000000 00000030637 14763776540 025616 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlTNSA=KJB( WF&.qDJDה@7`§Q|Nݙv:psΙ;sfF(`g 0XEi|s{X&jD#?oqJ|"߉_QF%*pxNDGh`mzuq} EnclwdH8c6nV85DGKНS')l%;h2XmqPq:}:qyaG'١e6ԭ=hm'튜°?ʙ+0p7`j%1,Z T8PAS]mz#W k5d< qPg{n^B∹M{na>kdR?: cedNd纮s06Ό9;&ftK~PU= 贂ZnձS9:mS c"DsƬDL07&!N05 #mȑTv,pSћdWԇ۲ujEhl<}iEo[}HS^̬1c,{9/^WzeO/s|LJ{|]-+j?PK$`PK!: classes.dexMhA~d[[iiEڃHmVۦ ~BEPQ&vllR *(x^T(T/zPA"XXH=@Z]US26j` ʶWo*A5iUvY ߉|¹G S< +f}_"w.ŸD0a=,n:kqF\Sul'mbKhcj5mSyR,:'ȯѵ, 49u%t;nĝgׄv?|J>Yس! s̼fHPXsA1p[h&%5`iZ)EI<' l9nz+!YdNF{Kq!ϭp 2r Dͳ@6aW͑ntDr{znj*ye^3a=Lgr~..ekdm5P^ꮸFX Ed->Q26Jَ+[Al_p|}ImL~PK!:#META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC6kٵ\|{cZ#/)!>٨SM-{ md~\zSOL'Z5+=|$ 뤨du鲘Q2r ?UrOW"}ljM.?;e'l4`\B61F6 g`@65]ä6L%CJגEh:ރ[ <;dDBiYbV1wGӊpWʯd_7/=yR%[q5?fC:?Rrh/P"ޫf\e=U 녝3^SH]t?7 qI,( <'Yk8H7EWsҝؐ00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*E'5UNõt$u<nmRDb0'd-bwu|e)0.4{P:M t2_sg[{:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:ů'META-INF/CERT.SFmKo@= e3tC ).'̨Cy__k4inMsBr,!o(1T.(XC0[ =%AϢ. LU`#fx$KHuU?#n61v|&Ql1޶^ƫ($N_#]RDA6t?a[uIhtO~(|Dvzk]e`)Yfḿ'Hzlu*& Vi2//OtHͽZiהT)%>mqwUߍ/"ZLc]7վzAfPK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy%td$NN>]byT+rg?$g@d~_,(l+LEw{4YY*=&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:ů' META-INF/CERT.SFPK!:A' META-INF/CERT.RSAPK!:DmQVMETA-INF/MANIFEST.MFPKy0./PaxHeaders.X/src_test_resources_com_android_apksig_pinsapp-unsigned.apk0100644 0000000 0000000 00000000034 14763776540 026201 xustar000000000 0000000 28 mtime=1741684064.7530000 src/test/resources/com/android/apksig/pinsapp-unsigned.apk0100755 0000000 0000000 00000004235 14763776540 023054 0ustar000000000 0000000 PK!< META-INF/PK!<"00META-INF/MANIFEST.MFManifest-Version: 1.0 Created-By: singlejar PK!< classes.dexmRK@4UDDA9pk". n/ɵ=m~5"88*88NN"8&(>x߽_wl9\?N]O~=>hw ^ !Ve6AeBBroHп!>#x^Al#vNSoɩyWDtߋX.:vq[Wwyt@-Ql3> ڪ@ OxX܅.i\J+Cj\֩bZxqUfibbvY:U紮@ѰjTUR4+:kZì5tb̕`a. B bUuQӓSos#칯q.z `Fki&Bo9BLR'IB7y,y1,}PK.*pPK!߉5c5ثOX>"00] 0  *H  010U rsa-10240 160331161443Z 430817161443Z010U rsa-102400  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbP0N0UDYxgSoFĿy90U#0DYxgSoFĿy90 U00  *H  4ۀ o=Ms<lkIÍvU)?RON^c*r0 \*B\ڏ. HTfUDa~%j>_@ 8vJdV2.00_ \ѷh0  *H  010U rsa-10240 180620224931Z 280617224931Z010U rsa-1024_200  *H 0MҤ}ړ)֛[ELՠh K[3T]FI-7 w2`Agʁ?ZWoRq / bK!gT2:% mVC[P˳&mV1P0N0UKz2ۏi5 X;0U#0DYxgSoFĿy90 U00  *H  9f`]9]a5dYoN,grPi݆ hc րa❡=Ɠ W9 Oˑ p@?*c"^섉)^t+뀛8s/!>6*gNԡFMOxl@^;L&ЛY˞{yc(ԔlZ{|?Xt_}'SP_*%./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-1024.pk80100644 0000000 0000000 00000000034 14763776540 024015 xustar000000000 0000000 28 mtime=1741684064.7540000 src/test/resources/com/android/apksig/rsa-1024.pk80100644 0000000 0000000 00000001173 14763776540 020663 0ustar000000000 0000000 0w0  *H a0]㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbBt]YW,y%ytΏ% K8tTGJ+ 4k 6A{$MR?F@N`㬣uxgLL݄ -gkqZr}Oiw1Acu\Bz]C(wB2]\{aQ_LR=Y|< t /;'AJ+`tѐCgBߊpGAXs!Ϧ=N^bR:`{A HRrSqpL {;'PZכ mVL&<[,7oUa~_3XE@E:_ҋ=Ġ]Y];Ix#;c)̚+ i ;V<.1QA,~DYK0hGa_D]xC뾭1&(b=Lo4VG./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-1024.x509.pem0100644 0000000 0000000 00000000034 14763776540 024600 xustar000000000 0000000 28 mtime=1741684064.7540000 src/test/resources/com/android/apksig/rsa-1024.x509.pem0100644 0000000 0000000 00000001341 14763776540 021443 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIB9DCCAV2gAwIBAgIJAP0KtYjhFu3IMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV BAMMCHJzYS0xMDI0MB4XDTE2MDMzMTE2MTQ0M1oXDTQzMDgxNzE2MTQ0M1owEzER MA8GA1UEAwwIcnNhLTEwMjQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOOP r4Y+uCAihcVjey8JmdjyfhZXplVf3HnOQfvWcY97nKnJ7L977QiWajIn7iZRAdVX PamVrEbaU6uklgcJFG/qirtscOf6fMBf6GaP2PAhQG89MQnUt9rAjxUAakzWOBTz bH0gHRDEGQ30LCA1oSlbLldHz+zBKSC7nsSKYp+9AgMBAAGjUDBOMB0GA1UdDgQW BBT1x0TcWRB4i9JnU5pvRtrEv+95OTAfBgNVHSMEGDAWgBT1x0TcWRB4i9JnU5pv RtrEv+95OTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAKs024CvDYFv rj3GTXM8A9Jslg/86WukzEl/+PXDjbPljNV24RSsFVW5gO0ps6Q/EBkbllJP7xNO XmOUDyqUcvcwC1zzySqs8kJcF5GCuRXajy4KiEiA5VRmVUSShhnkYX7g1yXkhWrP Ps1fQArqHx84+naFSh9kVqu54QIykS7z -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-1024_2.pk80100644 0000000 0000000 00000000034 14763776540 024236 xustar000000000 0000000 28 mtime=1741684064.7540000 src/test/resources/com/android/apksig/rsa-1024_2.pk80100644 0000000 0000000 00000001172 14763776540 021103 0ustar000000000 0000000 0v0  *H `0\MҤ}ړ)֛[ELՠh K[3T]FI-7 w2`Agʁ?ZWoRq / bK!gT2:% mVC[P˳&mV1>7cUImIDAGo&V:Ha]Blj Hdc=k=Ѩa?Z;qr#"A/ˁݤCV(4TwSٕG72c,/A햝#<8Hg1=hhɍ3jA*d٬s377|ַbaU?AOÜa_,aѪ ,LГw^4/I6rAfoZ^ro)Xe+@QD6p7YӰrt>/b@Jm)/^֛qkšeA@WOSIT>0BUyc4܋c6^,Fp/e>>. @u+ƌyWѳ;Hx qW4M.39R_4|s]>BxS./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-1024_2.x509.pem0100644 0000000 0000000 00000000034 14763776540 025021 xustar000000000 0000000 28 mtime=1741684064.7540000 src/test/resources/com/android/apksig/rsa-1024_2.x509.pem0100644 0000000 0000000 00000001345 14763776540 021670 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIB9jCCAV+gAwIBAgIJAOZcv9G3u2jCMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV BAMMCHJzYS0xMDI0MB4XDTE4MDYyMDIyNDkzMVoXDTI4MDYxNzIyNDkzMVowFTET MBEGA1UEAwwKcnNhLTEwMjRfMjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA utVN0qSVfbS4q9qTDimf1ptbBZaRRYtM1aBoCYR/pktbM81/01RdRkktN5ykiMsX 8CD3pHcymf3HYMlB1BxnyoGO6z+0WqpXbx2ep1KWcbKiCi8M1/ANYrEaS/ghZ5Xk VDI69CUM+LjrbVbBQ1uTnK9QF9Hoy7MmbZ2umVYx/gECAwEAAaNQME4wHQYDVR0O BBYEFEt68L/DGbkyr9uPaTUeiApYDo87MB8GA1UdIwQYMBaAFPXHRNxZEHiL0mdT mm9G2sS/73k5MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEAOakfZuNg XTz5olTBI1xATXs+OV22YTWblWTLWbPY4oVvTqMsZ6dyUNlp292GC6mk+bTfaGOx n7EbCtaAr/61HqWBsYoaYeKdoes9GJfGk6wJxBdXGuE5C/9Py5HGHZyG2Qxwh0Cq P8Mq+mOzjIUiXuyEifvuKV64H3QQFqYr3x4= -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-16384.pk80100644 0000000 0000000 00000000034 14763776540 024114 xustar000000000 0000000 28 mtime=1741684064.7540000 src/test/resources/com/android/apksig/rsa-16384.pk80100644 0000000 0000000 00000022106 14763776540 020761 0ustar000000000 0000000 0$B0  *H $,0$(س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0g H;aeYE;%X )$_Yǐov:KѵA!n n@nKHZ0%Dk4:_٠P!Iz~ܿf<-Mjb>;Е.-(\ҁ L**Ladr9@q=!/YA ۳<\Q0wcDtnN<4 nw Og]:vu._3. e# R)1; љ1 oBA uƈS0 W,m[9Y&]+"]m UޠT-tkҧ(LhÁXf 71iY|q~ _RH9WbC5QTNeuG]SFlhb`Rkv5o%1<4%0&0OЄ iC?9ERo"#ºr[]@M6s ;vh-#Ҥ_~DZ@3fȉX.$& T0,yY8FE򧠿M~ +F.11T됺y~fAQU ҙ+ J⌎ )H ̓bS[/ dkّ*8+Jϣ1E 6Z|iv]ĶM0ZѻvI%*n>EFr%Cތ+S:2AA:ɪl"{϶ɐLoStM/:*} Z0]dz'g8)5쿄tf*N*^FUqĝH(d1T r*xMָߏ{O*cXLi3t6.k/n2(xyJV`sV iFKBx߷9@+|k]& !?d)¦Fq6jԐ70͒Z Bnvu9t쬖0?AX͸O>OvmtM@Ty FUpg]MKU̗nbj:O6(w{ Yp[ vH(FSbLz0ġԒ y܇F7-<DZ^%@!'|4.xd)Ewm JI=oTگdz/#_P%h܄YHCy0:[#cKƒ 3rZ1&h{ "+\c&[o?Ռ0],DmCf_A*}~5T$Z[ %>?C7mf3A邼_NP cQy7_{*rԤŷ;%`(qs)k%ʺM`f2AMaLOde˨̿4N-'EHhpnYYFtZX5ًИ̶Akf>I/q+d撅K;;&}%!t =u3% $QLqO_-zX:cK!]L 'N0,9SyU@#QѢZ̳ ܁k$/ SDS{ 0! 7iTޅk|dY_$=0[[Ku:dKhwvQa_+tpE~-]xĒY$2-_rK} `JZg"cQg D%ԀE.Ϯ,儨gCYfϷPg[5 =ef/& oI[_|Fΐ(Ͷ8KaSʂФ)g^K,1# TCWO qdوM$ @+K7U98q+}Cīcxjp:ٮؚ!2 kĖdF$SZgB9gN\-wA}濨,sgӀeo)oqMNARUjmQB<>v.}Ԙܫn(}*{3I8WdHڽFA/,p! ٬t$_wVmĽ9H!J&R09 xKjC֌J'rt[yå-!_g۱ Υ񷃿ɤ8 #w84 l rvʸo:ƶ?KM "/a˼gejN6&H_FPt>w!#۝l ڞ]ci#UX-=` Ua?"զ}OzВـE{Knjz3]T]_-tgfS:om{˗LE[;S@(nfG/U"tB@dVlG<޲%D;š}M* G ÀTڤ^[Pe>lLJ&SS)ejOd9O l;`YEyߤam{7ڼ/~ƜHeյf3zתogr\}\]P$c'ҝQW.Մ>d!#@AW6 mG]xb_4~'}wƭ9|ci^xC| `LDD/cF4Dw !2WgA r!vc,?!0_J*.@++ǐwqT&WuJݶMg8N.b@BW LuE)2%G{_@6G^N&xT!†6Gap8IuUboO\^z<2 8'u~}a3?#FQ{ZT|ÒY*uTV $Y4'T4O{BefL&(f'LagvH`8flf|n,ń`5#<&˳v䗶oQ\o¬SD` qx*\ρ+c7 tkxUuN)M<`0LY=hmsudlhztxԐFg]%$ 'P'o$fU$Tc8uaT 1V4qƾ=7% Š;CA.0%?OL;Py؇U~fKSd%ܞ uǮVw0eW"o#qX;Mb/-V<#RF]ڂW 8E4z}^o|I$yt:.tͩPpRe币q~nU]#7OkPD5 - !ykiPT߳ p&~B#U'$ӿk$j /m]̲su!$+KTq: ~ ~%x$ŅR熸mm'.w› L=s)~?p΂%͹!+} J9Fd`1S]@w št!j4ҨCyRM&~5 ޕDc ~^AWL]zl+ƻHwdP:B-uΦ87hNppvAX\ &y5&\D~ݙshuxo5mG\F`YԞ;hg1Z/@#Ċt2-XfE& F"5y#Z(Vq40Q?,@% ~@2,kySw,@ |cxW ̉WԘ \1m"je*D갢aَpA{6:  "B(h[_0 o^Z\WC$gh;B("uĂYs#litW8Y\p v^YK}SN"jt7ǨL!x-KY";B aad h !̫Oa+M2XKNG808]b%!4`I!n$|ftK:UJ7pELvÞ\|jK]̯ni\F[i"=.gQdS |'ڕAв0&؅b$\ߘ]&+ /ʕGWCE d,Y#1څעu:l;;Vş TJ73I6FP/((MRP9IA,ڪ(fu ""' ˷ CZg@˝%ݛ Qnp-cQ.Y!GR]_.'9\Rbg}?h>-/qzXHT)$$Pj֥v9jqLِ zcF`Ǎb).4r 'D%)N1Uܥ{_^Bn{]jf^^ֆtXfo'*PRV*&`t5rub޿zm~ruL"M az&5H=#9`Y,QzS|M]]yN_ \e ?{ךF-|50n]͉2I-x{=QEGDzS&ԷrD9naE~?L4a`!%`!laE_3 (T&Hj.Hav&xskFz U#ٺ L:ER[jN4t@o8aRs[gSFeȩsbO@&$6̺KojRJD{ef.X_yC& *8Yz[2xMPޏQNB./52n/f='jnЏ_FNyHIA[xÙG^gG^1fҳm*cjTN 'B5Wh1 "@W ]Grn-F,k祤"˓?ʨϬ.g)6~eҖdWR7H+Ac܀-6΃G_,{(jF=9#?FgzdڂFood\b/g~FIt >s0v>@.Q@]9nO `=>y_spu_%FjvXUFE>eBt.~Ծ2Ў@? I;#mHUdMʝ".+q,I@y!#T 툴7w~Wߒk{o-.Pi=(7_qRM"~io]NpM EmxZ[۟gqtXHIQZm{o!#&T8=<{ J籉$T6./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-16384.x509.pem0100644 0000000 0000000 00000000034 14763776540 024677 xustar000000000 0000000 28 mtime=1741684064.7550000 src/test/resources/com/android/apksig/rsa-16384.x509.pem0100644 0000000 0000000 00000013475 14763776540 021555 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIQ+zCCCOOgAwIBAgIJAOd3bpikuRKvMA0GCSqGSIb3DQEBDQUAMBQxEjAQBgNV BAMMCXJzYS0xNjM4NDAeFw0xNjA0MDQxOTM0MzFaFw00MzA4MjExOTM0MzFaMBQx EjAQBgNVBAMMCXJzYS0xNjM4NDCCCCIwDQYJKoZIhvcNAQEBBQADgggPADCCCAoC gggBALDYs7aIK08pNr9fdrpLmUVfSc+3n/RNU2e7o3fzwQf2IMExk/ZmxF/ORgAd pXPEkn7uOLYSp+fJHUgqsca+8HsxTN1ypKov0X3OeFbnBLXmsoXgjRzaMEgluupd B81xnAKE/Vb+HYaEJ4YP+3UgBLIItnwLtL6vdh+e4qQGF9KEAv+T+PdrNQF0EGWK weCaOuMKRKfSnNXMkgIgWgoEefsBXNOR/jRJVFxsnJlwsB2iw0FbKjrxnlmwBL+f A8YxdMoCLxjL0TuUEmRyd0jO7Td49kUIkW6ux/b4qOvcUPQiODXeayesJ1lmFepS ATjhgCz2+C3pcAJOPvOvC4v0+5OQ3YrU2kKsnj8G2ic3SdPUq4UUvQWqY+rCZVDH IqsEs/+XO++wMELkN3uiQcRIEO8aCug+VxMSRiyRSSNtxmcmedUZevbZ6+2leBtn AbLP33E5hC80A7WKuddQgOiA1yTUVxatZEaxHFVZlg2nzSYHipvwMVC2+zQ4yrKA mbNp5wiVUV5WLsUkYEuSwl+MG9vEjr2gTX+qLrvpIYS2AF/16SOEw5zQBPUcfVIz eJU+W//wwXdlNmA6qRCDXqLDBX1MQrChpaI2eRbv64k5C09LtEA5nt/lJt5dm8IF XpurOZqHTVG3CCS15SM8PyoApYKN0oM9+6Z+FDUOB+VbTW/hAqKYLNBJmNf8mRkK +c1peoOAYw5iG8iaSD8f8bcIi3oFPMdt0Gs5vDoLjbWmK2s9P2wknA4hmVe7hvSy DMvyxj8j4BLadc3HafcXYPfoNPhfgoiZVLm6ijEj74iKeLSkef8F3x0Agulnitz3 CWt7X6EXHbqR/0++VTqgnFDe+enf9oFzMsDDbOs59dpyGFSP159dTqJILimF5xUG Fw95hsTbdEblZ7Q2MiibB38ifz8WT+Pix72SnHrlcnKonv8Tkeoie0AP+dYkYXpC Dy0oIzl7Vhy1e99RjX8kKjZAfCuiQ7wnOGNu6V32UyKMvWD5E6mNLpsvyBRTxDhs ePpH5dZWbhg6WxhD68QG/Wi/8FRmc8/TPpPXilOG3HHtX6Q3yfGYHJB6/dJhWQQy iZawyEpcyZKjEyWoJayRsKSLb6gW6Idfc1Uf/yb6IHDRlYZEPF7JuznjLCaz2QC9 8GCBGfUE9OGH+LtdMnsmn2IYEd1FtWrRGduG0HNKubmx8bJmc1HkYDQg3cksPb2o jwCND2ALGtTcR30yllmSmEJKpXBYB00iRvxvkBqCkL4THOXhN2V5uEMrS0r8GRZN QCvCoFKulPjITmlQ/ciVonN9y9qVeRbFE4ZxiiBxF41K/Mw45ugIqfg4ndJqbub6 5p15nhYJIC2CbsVL5+1THmd+kompQhUo1ttwof0aHh3KLwe1uq2OEU7Tz71Ct5+G b5JjIAlQDuGcfsx3utJY3AE5ailtvaRNAz3FyTsZGkIOeqX4uswWltxGnuSMSbPi CPv7ngFXkytkK+3Oqs92XM8mhO1yqPzmm73+qZYDGFdy32C/Rr0enETmgkUFPToH 28hMcOsEt+7L0GM6A5N708BaF9Csy7XTFz47Hk2CjaKTvP08Tl3bFPULrgJ5KCr6 4hc/bVIDkU7c1lhDISJ1yuw4KHizmjuf4UeRBBDoB3MpPwZSeEkggGXuX1wkFApu RGhjCXQgFU9bNtUHM6Kj0tPQZrgsvx2wwBUFVOfPoej6afrJqpvep+EN1+OJCE4c AzuZ1CdE96vEleVbrq3BQaJbsm8JqHs7lAn1aiCK0semqAoXD4cT4L2H4E37IDlm PHsxIeTmgD7/TP3InuFB6sYvVM8moG1TLmtUJkpc8kkPBPNbTRHwuRvMGD1pSsP1 aNbgKkEfGqL1iOrfoTCRGKDeDDxWGsxDOgW1hRs9wGzJuMQjl1Rlb11lnnX7UYyT UnD/yw7YaplScWwpsqntQewI59A4ad1wOJlabDUFwkD4i4ERTcjyea1ydE03qaOy 0ItJbc8kjsEMWFAv1+y7/cxD7kALcytvBHhD8OVJ40qJrtwRsXnv2T74cTPh87qh 3j2tjdJKVSLxwqg5ZdvIemnWmmFAPuRERResp5j5WLJcOFcXhbp4CAQLViRoktLf AKtgKSVa9FQLvombOq8GqWxqeGJEsq+8/X6UOj/RDPu8gUCdodmlxgC5BpJlMTfg 9ElvHb4oRewPfc5MBV5i++8xagxS1+5NM6z+1qZfT+XJcJ/wFYyeSvGGdEgaDIVd XGqOFDmA20bUl0xWSM5j4At2CmymAn32i8FR5lNfB/f8tIGBlXQyrYzpAKoj6FrV 3u43zKgYBzDTqNs61e+A8MScV5666gvpcOltFiaucc455JGiN2v7N+SAygWaX3nb jidmRaIAftup6kXVXxy1ZwpKCtUTqo0M+S/jO9clHy1EYaSX12blCL1B6OFpMRV7 1foffUHwaPrMqqbPphgN4QRY2Ao2sKSliiP1T8s1T2iteEGiazCdWSOVVi9Acbo5 DfeBlWwhW6OQpv1nMTznGscc4+ledP5yG5C6boz2aNwK6H6cpDfEc7tWTWhZN5Jm Jlra4pseqf9Lsc/Y4QMD1312C3Hu8EtJ/qlxEDhLnCW8PHxdlRsIkmcV8w+as/hu 6/MlOYcCp9ae6nooFL7ZuDkdDWm84UXdce2ZVVGVoDa+RdpICnp79y/9CLxWue4H 09VFEFUH//E6lOCBymUjTSO/DQ6z+ceb2W5B3WVV8gqADTDhAgMBAAGjUDBOMB0G A1UdDgQWBBQkHp1wX0Sfr0xz7xcip9UwEIFTLDAfBgNVHSMEGDAWgBQkHp1wX0Sf r0xz7xcip9UwEIFTLDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IIAQAd FhlHg4E7Yp8kIOfZRU5Cma6wbOSd2eHkV28WHGdwpKsvNhzgQEj+scYWSS8geozi vqSdJCoMmY8hWJh4SY0ED1DjPMoRvE8OotyGoCJovvYQia+gbVneT8JnfV3fkdwi hpUmAhokrsHkBj0jp2Ubff/D5yflA+QPCmhZZnkow+5QHXtmpy8CL9Fzfonz5uq7 yCV5uWRicczFbQw3pDSXKn5OFqXuC8H/8R6Caq2TkJ1LusVtZJevcHBkEQ6e+XEX kZ+QCNtHw0a67LQEPtMXSyqZ/zR0roqwT6udUgHhdvZjbbb9GDpTW3u472IY18E+ bf+npZl9kyuv2kyK1d2IjL45TxjBr7vLbjsP2UsmZvb3Wfb/kiMscvTBcxOL7/WA GNJ0XifmJWiDTDC+gUBC7LRN9lG8F7ykrMTtMUNlhow5LpIi0HA8TH58yq3/ulGq XWpculy33kcVAFTMGh57r8zq9DwkeW+RWggvHO2422S1bFGmKpizKASTvY6iqo4H yKvE9uZ41WaEbpp9WKPIaeup+ynxFpcgwMCKwvs7Yaj+mVexv7CoJ9nrEhMOHDxV GRyWDdBoIi08S02JHztZBXp5NZ+hqey5HrO4dhrnV1nVYJmH89KcAlXMfTZ2YHsv R2+dm6K8ToaX+Irqbz7Xbv8WG/aAdUqMSWkFEss7OT7VZBUiAdFaWqg0D0wtWSSE jZJJs9ISUTClq+97o9BEH2sAebchLFP56nY+Bj/zHBq2qPxTKdKE5BH13KcK1fwO eNn8a3SlSEHraa0oV6VjgSoMNdFz7b7b0r/Z8L4PEASJgH+VaGRm2TtuVWFHSqvX 015UGITk6YgAQ25MTprJc/oAd0dut+aCPtOVElfukYvdrbw1YYQ9tc7kU+AVrzaf ytWj2GYR5Slfhle0inKlBvbpLTAHs82bp2Dgy9ZSQzW9/gIvLvCt+Hj0kiSYNcbX W7Ai5z2i6XKE7DdQO0Uzt+1bXGK3j2PI+81lCw2ejCFwjdYDWQw9f2nzQ5FgeYwG tc6fS4GbJM97n0yH9rj0Jb25AummZGnEL11ytPpC6Nv9cQdCuKDbaWQuQyRMCLEm hHaqV6k/fI4Etvuo15pyfJ9w7Xrhc6emgdg2HzJ99lDGkzZAF/3FR7N5pLPk5E0/ PPlXUSiEx17IeWp3rNt0YSMixkGz+EbKyv9RIZzm/LV4zAzs2ZyUHHavUgZ602eg 89ppqafaBrCwIWB1jUmnHJop9YlXQ3hE7pAV5qf9GxZLwUdzJcyLte1/vkn1yt94 nLOZPPWUwUjIaBOZ7e/g8fHBjvAYwyoy3toKVpvkhR48NvcYD8pQ9cB6rIL0JkWV nQEYeISlJCUOO3K2eZ3ZH02ftha5gLshcGRXy9NS+4fNxDT3H+102RqSxmKPIxV0 onV9RyOxUPKLRGjCZBZxs5aSxTYjFJ591azt3yAY4vwCnnHqdNGFbTat/Zc8LUOO J26n5cOYFGKPvZVvj8jMNYC1wo+R+A+1FeYXBV4MSVxCB7tjBlbU6OIyZSWLZ8Vw LMcPbuZ7ESj1LeTONwS1vspZM8Y/M8+RXv9VA8Z998tnNopdU3izVC2z6Zn9hNNI XBDbSe6ZRwsjXrm5TZCBgA4ZE18MwxPVhntSvl87Gc3wF4hz4BOiZXmffrXK4nQJ aVA8E2IsriCV+GQBN/ui+w3U9LbtsHrbauhjrru5EfYohpuInwvgPlAnTztdv9u7 ee8RhwaCa+MInZanD6pRAAcfM6O64CPxHZtfVW6JM42N7wQXijvYzJPVR30F+6o1 C+KwySuMBOGxOctmzLj938/OMrxuLBOmv3PJvSnHV0pWtbR7r7jH3v0uUm54zH56 Qo/Rm/Aqf+m4Si3Xtsf9zvc89sqG5v2TT882Joja76zlJfaS31QgbnLTmKtAHtIC mqfQPvt1LNEJIiB6FZDHJIW5Ccm7imsixerxCBBoAt/J/dhW6N+cjZ0EWG5NiSoz 9LmAZv8iWyqK/KvdPXUopQWqkYUvuIyNCYqzTRLKudUMohefNwghvl1gSGp4IMZ/ 4LyrJHi9eCcD9Z65PJsRTua+742N2sdhFfU/C4atOUGSK9x/Dl79Qkgsl6HqAoce HXiHAIvoOqC+jzEkjjxow30BzJeGsZoFwNvMUW7HcQ523DiIOx6MX8oQyKEo+W6C ayFvvvT3qHu2hL2ZxOXE+rGyUJnmwqctz4ChLvyYXa/eNrycs382x2U5XNXgXzNT 3bwB9B+LnKSMJEB+UvHdbBcafYyevLptbF5xiiiUA0P3fq61AfmNiCzJWb+kaO11 oHHQNWyG/fO49u3bZJkhvlsk8GXAp9uTqdW7YAqxjy8NohFewmtpTJPE62XKIqiq +dqo4nUT761iaUBxgyj1v5jKcXT2JiEMnEe4AN7pZJ01pCNXQrXl+6ru4TVV3tpy OsDJ9UfZo8xZXEAJ/gvSyiih0xq6xhwGuUyExC3GldBz2frveWImxVEiqQIdHULH WwB6eAm9T1f+2hOGq7AB9Jb8CRyQniJWXtWu9uJBt+XwSt5lN6VUjeLt95SitvjO llqs0zhvTf52H8siwaO83Cui78iamqv7jVatB3JYW71S5cOyZ/x5Z5FYqKi8/wjO L4OyUs54kfcJllsxAmS014UgcTrJpbMNw7jSzLX6FxT4MEbyARK8wWQfEZQ2tCeo IOzfcYvlY05mG0KSzs6ZGBrWRZQDPcbJ0CKNSLTFbQ== -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-2048-lineage-2-signers0100644 0000000 0000000 00000000034 14763776540 026534 xustar000000000 0000000 28 mtime=1741684064.7560000 src/test/resources/com/android/apksig/rsa-2048-lineage-2-signers0100644 0000000 0000000 00000003504 14763776540 023402 0ustar000000000 0000000 9>800 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? ><00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t 00 Г}=׽0  *H  010U rsa-2048_20 180619164943Z 280616164943Z010U rsa-2048_30"0  *H 0 {3j?[3+2 b1Φ2RY~sw*^u?iw}ER mb3KXTUßUgn~'[\^cvG#aL5=Zɖ3cM$IO*o?8spPDL oZFQkE&g$>/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."_ 2fڃ8MkQ % ƿ'a} >)3:hR0knbi*Sť5XiT0j wҐfbHaň=l̡LĞMe@ Qǣz#٤c r.F΍ 1~ r[-&zLQq%x [Pf6<7E./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-2048-lineage-3-signers0100644 0000000 0000000 00000000034 14763776540 026535 xustar000000000 0000000 28 mtime=1741684064.7560000 src/test/resources/com/android/apksig/rsa-2048-lineage-3-signers0100644 0000000 0000000 00000005541 14763776540 023406 0ustar000000000 0000000 9>U 00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."_ 2fڃ8MkQ % ƿ'a} >)3:hR0knbi*Sť5XiT0j wҐfbHaň=l̡LĞMe@ Qǣz#٤c r.F΍ 1~ r[-&zLQq%x [Pf6<7E./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-2048-lineage-3-signers-1-no-caps0100644 0000000 0000000 00000000034 14763776540 030231 xustar000000000 0000000 28 mtime=1741684064.7560000 src/test/resources/com/android/apksig/rsa-2048-lineage-3-signers-1-no-caps0100644 0000000 0000000 00000005541 14763776540 025102 0ustar000000000 0000000 9>U 00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."_ 2fڃ8MkQ % ƿ'a} >)3:hR0knbi*Sť5XiT0j wҐfbHaň=l̡LĞMe@ Qǣz#٤c r.F΍ 1~ r[-&zLQq%x [Pf6<7E./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-2048-lineage-invalid-magic0100644 0000000 0000000 00000000034 14763776540 027427 xustar000000000 0000000 28 mtime=1741684064.7560000 src/test/resources/com/android/apksig/rsa-2048-lineage-invalid-magic0100644 0000000 0000000 00000003504 14763776540 024275 0ustar000000000 0000000 9>800 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >800 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >800 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >6 00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=y6u8"l|ݤS#ߔD0T8 \|ȭ],YM>&ɺ꾣Nw'Vpʠ\iqQUKΧchGxJLf%: $ȻL aQWODQ@{lA(R+?Is^s.Ŕ,`; U|B6Rԅd\0iZ]1-! Bڌ 'h]FgƦ12OI#=2f+LfR/NWoRn \3Rp_}AQlL`=ifub/;B`;q(f1_I@%D(;XqZ#2LWLFsQRHlYZRs3-tlhP/U7^ z6[ʯ;y3eySPNqClnc9GGneFM9l1>VsA xN6ؓPmLq}@a˙RYСƧ *yaA9b5@G@L+Q <]F*$'G2FHpKNj1W}{諞iJ aVct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqBLh:IBoa䌇p0 >KP5brHsӕ /pt$Ǿn}Em_M3g `yb-{e2 pH[]cXxHC`A¥ڢyFs}.^ WVIx^ۋP0*"ߢ.4 +jUs!CyG{b)ՆW莋\Ҡhg;USRL^L3 ,}; !D>V3r O(l˷6[Gg"X soi3wxgA@91 { F*:ewAN !|NIKp _T!xWf'}' {uLh{ { $"x.@iEw%\8׊+8uPuXv/#τ\Ֆl&@ ,8M9q5|s52bߖ _T*&^ϱz];04#DqI87ٻ-/cg^vu%K˷Oic'[+$'8GZ./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-2048_2.x509.pem0100644 0000000 0000000 00000000034 14763776540 025030 xustar000000000 0000000 28 mtime=1741684064.7570000 src/test/resources/com/android/apksig/rsa-2048_2.x509.pem0100644 0000000 0000000 00000002106 14763776540 021673 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIC+zCCAeOgAwIBAgIJANh7AUYJKp9PMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV BAMMCHJzYS0yMDQ4MB4XDTE4MDYxOTAwMDUwMFoXDTI4MDYxNjAwMDUwMFowFTET MBEGA1UEAwwKcnNhLTIwNDhfMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBALgwhcEJHldw5BD4tZP3aeeYyi8FCVx68WvuRcpJ1IwCbXd03giTyc3Y8Olr 7D67Y5aLdCW+XE8Z9pNHd43dXe9aRN6kcbhVynzVfe5PKCeYt3dVYgxh8eQqO6A5 f6DpJjF1jkqRmR6BipsDw5t8PwiiJ03jnoaepdGvnQpwxHEf7izWte+XHBPbJH6A vqXCUVlHw+CpI4J2NhZqfSa60F4y5heKF4mF4+97JPODopVeFXvS1VctYEY3ycsB uumZy3Q9Lp2E07KP7SP7oKAegg0uReqeqVcofBQESP4iMefw86QhqkTysfG3q3Sf RmmbTLnovAxSjjQZ5oZzGJuBQXECAwEAAaNQME4wHQYDVR0OBBYEFD/NVrkySRE2 6bctYSVM+os/31yRMB8GA1UdIwQYMBaAFBcCLXMQfzibORLraiGzAYZZLB1vMAwG A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACIYKbWz0byV8hCwVhK3UGrI PV6+DWAbRrx74y0cqhlPIyroOdSSEsmGythQMMVdvkvHGLqdA9gwQqYkNkWFQQqp Cfj9+YJdEKTKJtMO/ZTATJyN0ACEXr5YQo9ivAASY2pfiTfQXbPGsEhycuG3gJ1Y rTC3imERhkHomf8ZvTJEwSOF0bQYNg/Ar2nNYNdf3oCrrylLIxUaq0pp/5CrvZT4 SMKLBfimirfxGS31Z9TEuvDma1Rn2xwfDOhmiSvC30PK/xPsN1xvallHtJDmTdNV M4E7Z5hDyCOQ3y1o3VHr73dZnA0M0WdM7JIH9Vcs+R0otFdyXrfeMw4YvMZ0/Qc= -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-2048_3.pk80100644 0000000 0000000 00000000034 14763776540 024246 xustar000000000 0000000 28 mtime=1741684064.7570000 src/test/resources/com/android/apksig/rsa-2048_3.pk80100644 0000000 0000000 00000002301 14763776540 021106 0ustar000000000 0000000 00  *H 0{3j?[3+2 b1Φ2RY~sw*^u?iw}ER mb3KXTUßUgn~'[\^cvG#aL5=Zɖ3cM$IO*o?8spPDL oZFQkE&g$>/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#_rs$uDu,>)O>^5yq=ǟovIlEB3mQw]rcG2IқGO.(D_J"r@U*3?!,~ҟ7WLH⑈٨睑==8$z,U "o?Ȑ1KM{jqY k5OE[޴ {b3xMN; Q &C{AVϊnk?ФUt4HfaAw|F&T"wWJIę-1,\ ҡ`XKnd<mbmYb>ϦR/B4~#W-Uǣ $_5vME0V}˪֤Pp;"QNż_SC5- ĩ Y6iHϧF $ E#%ʷOpm,hG77:fpˎ?[ ENAKLf7hq4nC~f3HFggsD_Z&c\$DnL^_!fǓAS}㴆 ֖k!\S/Yҍ۪_wlm'CjQᷡT#\rE{z;E_WMm#]0A2JC8`ai@髧EݲU =hb֍Zj#f{I;݆`๓]wg}y8P )jB!+Nѽ.. dV{fx~*L 0TWķVR Á3]]i.}8Y>{/i>Ih_bWdFPU/ ڋlBٌ>2䊡>ؾob7A'B Gg;*?d Yf2J&翌~. P[kOB1PAL$xÊ_i'<_'az~tWo|k>d20wr $*>xzHf}4O?nXSg{/?, 1EиOCd 6'AE/ˠ{}<sgڇ{Ўr'Ql!7Z*գc8jǂ/'53gC "M^DyZBf"`\Vp66 WJ0̭~ /EZ/H9qSou$ޤ_*SeFJL~nޤE#kb-zXZ] F3wKe~d\&;3æ`b:|'=aчEc+K8rYZSߨ⮁\6@+lS3SgY?lKQIM$BaDs|ϡatnZT \~.puY`|ia\(`ěHEKwMАz"O;AYmݻwᰱiӐ(Ǖt;%X5J퐼 mwJˤ̀zBؤB^,Co7Ogb;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=]0*k(^d`C+X/*P()Ibk拪"1?QTKAaFm k/>\dCP|Y6I(ݮH٪s%5U*/;S_<$a57X!ɱ-J_Rh'6szJtSul%3 !wnq3#zL2X ըqoS1ߵ?"р\]gz Ssm>}C҉ԻDVB !EVk h($5էmŽHJ0{r?Qy ;7=)8c8w& ZYd̷-;@IPDlHoܠ|D3lEL*#~2rrs(>ƛ͊@.j22ټq8bڗ&Yh";K&h^؇w瞖SSʶ > ]23J^.Fy,o=GBJ3#P!G':9/PMDSyRҽ!Jx7{/ ߶H[LxeON~U)-Yos=5somEHmk=mRɰׯOIgҼa%f]3JY(IK&˔@./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-4096.x509.pem0100644 0000000 0000000 00000000034 14763776540 024614 xustar000000000 0000000 28 mtime=1741684064.7580000 src/test/resources/com/android/apksig/rsa-4096.x509.pem0100644 0000000 0000000 00000003371 14763776540 021464 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIIE+TCCAuGgAwIBAgIJAIhxrQmG+dcZMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV BAMMCHJzYS00MDk2MB4XDTE2MDMzMTE1Mjg0NloXDTQzMDgxNzE1Mjg0NlowEzER MA8GA1UEAwwIcnNhLTQwOTYwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC AQDTPhE7i4Bv3BQG1ifMPZfTGA1ZvE+mQWOgQ1q/3AQz23I8pIaoj1wFbYDvnTYG UCDnlJqKvwL4mrAnxvqTxm+0g/WFOoBKFoNY5OhgcDfomNEFQajY9bgXaNiwEYDU 0X+q+2KAnr35MF7vdEaHcN8gFh15wObVXmlerMIOWejnwrM4MOXjGjNXtiB+Dg04 Fb9eHufZ+s1dKNAojD08mUJkxTC1gjmBKPr8hOAPo9ay5NZk6mGaI1E0U5NbIpDe 0EhYxRXkDjP7f6BzbhFEupCOzZNMC5CxD8fsmku1CaKBayGpsxktYFxsw1RFVTZj 3pKYnM1fJBHj/hU+7zy60MZNpdU6i5HVa8FPldwAxv6P2dz0wIl9S3rrqDrd2/1p r3rKNrMbVo7wzZ8euVhmjttsRvN8NykCniX6oWxuX7EFQxT/ZM0xk055gMMW7xbr oLtPAJ1Lk7R6aTCS+ObrhBgS8+38sIT8rF/CCz2KQ68iUpMmMBz+dkYsDwHPq7vG wIy8J7fwzTLqUbyO971+4MD9/9HbqfjRAsjfnSz8HqfyHrSDKeAhgqlr8j8DtQ7X B2PIZIEmyN//A13xfZQ63c/KR/yf9nPig4KfHulODfWXBD+2T5kzakTSn4My37FF mzG0giMMJ3zGs4QpGZAKFGdwhUq6nAAqEKYo+dptlFhbzQIDAQABo1AwTjAdBgNV HQ4EFgQU+f3rdfojsysUqNHmjE4c+0I8YGUwHwYDVR0jBBgwFoAU+f3rdfojsysU qNHmjE4c+0I8YGUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAbOd0 bKbaeGNsinFSck4niiHDhkeD7GhsneReA73LDJVwAsr5SLAYlmZL/R+1Jw2FGa5m 82q8412xoVxqAbsz/OrBjoW+5RjAUpHSv0vo3Ny9EpFDpRI1yO8EFU2PhpbcC8Av 101/oBA8tgKDKgb9H9GrnuqKqxLvrOKDle0TEWAb4X+yyuLSneU6UZTn/g6hhKMw pRcsCkwwKa2266auq93NA30Xb1UME5med/5fBKzjL7TvA8BTbr8EdHxjbPmnyI1n iVQJnkvvk6rDRcWeOt+lP/pmvaCcVFp9FJHlWmxrus7x0PuH8WxecVl6PeV3MLyR HXwGLzlqJWFm753FP3sTV3xNqemo1IhtlHR3jgU8Y7Ixq2Ljjs5izucqM+u2Ioo/ wbzlhjfRmxqGE3RWvq6Vv5C22Th3hEA7PDUKAh1/cvUwZxOh1Wa9Nedhy0SUXAR8 nY2GHJEN6QmR1ZAoyABHuPqk1IjduDONo0usGk/iYIFMgfwitc5Tv4gdjjBLburF NRU3QkoT1twPueb4Xnvmb/yHmTmqm7MM4OlkmTSpYRci8C1JNChdqIjNSF36Flnm DEMqP6petbkZcORD7UtgoB+DCPyBWybtm/GDOW2v3CWo94Q5A1yRDpqyQOTIJChe sssLUtp3bfmTSFFMfmsovynUlFpsazeFKjQOkYQ= -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-8192.pk80100644 0000000 0000000 00000000034 14763776540 024032 xustar000000000 0000000 28 mtime=1741684064.7580000 src/test/resources/com/android/apksig/rsa-8192.pk80100644 0000000 0000000 00000011106 14763776540 020675 0ustar000000000 0000000 0B0  *H ,0(=$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[Wn r|%qYfD0ewQ"xY\5![Bx7:mҏHkNa`(&2ꉧ eN;YA}Pv^gC{]eKdeWt0={w6(m X@BN);p%`=BMa1rLB?n˚0@V0Cnp#.T yN0O.Mɠԭ{pYK }{}~` X IEE-8wȿlɺ?nqk^0~iNБXoțR?h,;OFb}vԅ9r B%oAV̂CՌrLecX#lӂsX}>X(ߔ@ξ3$XTT2al90s52<[Y 5=pw>|t*8 |8Ǎ[VVw] ]\W"FԦRxGKa,!EOö&^~ ZtE? żLRs׸~m ղ=<BH5|dƿ}Xyr`|{ w8BzsCgf-RSWrOŽ`YV.L6.OփjdJ4 j &/-K~lKIƘʭYѡZ9yZsn*,Ke\Wgc(Yg‘{%(N |BKg3*#'Nj}IJ2r!-}+Ln 3* T]To 4z  En-YB|I?Ff"pj merTc&'"S:=rx7v8*:~س$с^qONP! Nosj6Aڻ;٠Fd_b="W#PW$:)+zFb$!Y˾;zk}-w/F{H]Bks|  f}{ DGfr0`%> 5~6#X١O=kV5@U]W_ˮk%@ݦہݣju>,=XcRlڔ0GBubɬ? O:BRʀ#UklfC'3kbFn2cթh 4Y _5s5&]9ӏy (N _&?,%z`:lh0G/bdᾪjuYy \#/ 6U5lGU򘸟%FW[-.B"[IJgo3>r$Ȃ|} ½Eê}}hw@lG\\f1U$cڎ+i"Q֧T,H%GZNDMygLB+:sj .~洶y$e[UٯG3rðS,?˽͝u=q>hh>}Eg (nȩ}B{eNυ; lj;u Ä) f/"P dщ AgӠy:PZb-sé`ZLlfjD^mL$K-iJ;og$<L Z02/fQId2 N &$L#yj'H˙j=fTdwi4$++W|(E4U vD3jtL͝MYdV߀ߵ1p7:U ,ޡT$lx>;K o& 5! wkŭ+q)h;UB#9玝IT,DW9cF$-̽ ;C1,%q"Ol$`L fYs b!wx#}S+YƔ9@t;v$HgIg.A/aI_S{^$QzI)HuF& 1`l^np?RK7atj Y!j܃Pn2/sΚJ%Z.`UI?*,@u ]u] 1nX#7Wq Y$@!GQ>[WP2ܩQwIP͌6m0.CmXwO@!P=+6O%u rծ൲CscV!e.ӍI'X,vC3PO$2%z׆" ]sХ1 ؐf5`$ye.H"[wYankiB ˑjȵTz~c$leI[`:sAiG sSN| Ltfhyܳ{W.ي#E1ķAmp8Q˔hYDRLoFr 1ٺեV_ s͚N7YMA2Mɘ5&zn&rdmR঴}1"Eٟ*a Tl%?V4<>AI9 ǥujb\?izn\3%1/Yi^W.sv4 ce&1%O2Ev Na|w!FM ( fȃ W,ƚ s#OYt6x:P=ДZ./PaxHeaders.X/src_test_resources_com_android_apksig_rsa-8192.x509.pem0100644 0000000 0000000 00000000034 14763776540 024615 xustar000000000 0000000 28 mtime=1741684064.7580000 src/test/resources/com/android/apksig/rsa-8192.x509.pem0100644 0000000 0000000 00000006142 14763776540 021464 0ustar000000000 0000000 -----BEGIN CERTIFICATE----- MIII+TCCBOGgAwIBAgIJAMMCN6DTJaAOMA0GCSqGSIb3DQEBDQUAMBMxETAPBgNV BAMMCHJzYS04MTkyMB4XDTE2MDQwNDE5MzI0NloXDTQzMDgyMTE5MzI0NlowEzER MA8GA1UEAwwIcnNhLTgxOTIwggQiMA0GCSqGSIb3DQEBAQUAA4IEDwAwggQKAoIE AQCt2gaOPSTn1A/WkapaytoE2RGF5nBzlfLocagG+0Bgz0RH3QQq0xBEmQivmbj4 egyKFQQ9dYrefXRnayLQvHpfeT31QXR3iKOfMnvtxK9UnkXo9S/T+czyj91Ox51w HFUyMlXjFs8btxi6Lpkrgl3jmL+e4/WI8khvTrdszrO8rBHBYlD8Y7SAvPcVczYq nn4m6Jd0/abR5slHiyGtkTREoHmW/u4r4PfTzanLyzcsWH9/td+sYHcu1Y0XTWTR x2qrUmpD+7X5cvXP9rsECU82dtnyPZPIHBqzfLJ8oGELsJxgVZ0u9gW0tlmJQdVo Q0QPJSWivnNxPl1czJYbXimbY3Zh5hCzdyNqQewVx6iNsNh008JsXJ5/yvJQgLgZ UR2gWZMGzLc4V2wiqxuLzELt5RoXPehAdAiOLQu/3Fw+u4J/d7NEwpKgpdDGyG/U 9nV6mjvomcgOdWKblaRAeiXauYy/MgW68kIu4O4SoKWRRmOF2p+tch3aDKw9M89O 88QhEzbzDsioAxeOAqL7th3bSPZ2fX05xtM2vFK71QFM41q+FOL+062Ev/VJyU4g kxK643V+Cxso9CZEVtFfkNnVIGOI2h8knkOiLtbMnCx/hcxuZLm/XvYQxOO/7ScL rkHLUuKu/dK2ksDn8yBgaHfVlHkMmVoSll6fKB+UXpPX7xClrGQAFpVUji5oZN0s +EcMQdcMtsb6LxnwC9XS1ELQ5FQGuJFatwY9RSV3cbwI6kNKaZiB5sDDAaAHX9GU U3EA2fm0u+4UFOSSmYTfx/jl0V1NMQ1oOAY9QMqzckMo2y9cepLQ3temQ/k4bNqj rYB3POdqS9OoT74UKU8gg9rv01PaFlLHhPKwVc+MjY36aVefbfk1KZzBpm2twUUm F0kz4pU3BYj0aSrTeKWaTLjdn4ndGMSe3QMQQ5GUchWCcygTyID8Wx8+eDzPIs/U sc++7fQRuPawcd11oQ7wTjWIjr7VnsV8hiHSyMupEad8HuBt8CNtDf/m/hD1LsEf 0dY8nDGSPvV/ih2YqTfhcac65D4JKKbV5x65aEwM7frYwi9QsB7CK2xLMHnatp2a TRcA24VEsaBP2+UNSLyxkOeeuzSCksHLPBWkVfcRbKUeD9x4Dtwf2WsUtYjGzOQ7 ApLBB4XHe+GPr/i+PmJP4IDoU1Mifbjla8rz2ZuebZgPnyV7nSvkG/FUqUYpa3Ut LY0nF/2dDRF0NirbWuB4Mkk/KlxCQ3w8EZFY5u6L/ee23WxbnVOEQJBtKh7/I/UV 2dGqoVJx2UqT0MpjUCFjx+KjWvaEt+FfmjfRRqQfHLkJlYly/YdeUrGPdGgJNr0C csnZkAZmKDo4OVv+wVduHxHfAgMBAAGjUDBOMB0GA1UdDgQWBBRdiMpGc1L++yQc MtftoeGuLwJ+PzAfBgNVHSMEGDAWgBRdiMpGc1L++yQcMtftoeGuLwJ+PzAMBgNV HRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IEAQBo9lUFyV8jI0IpcQLC7hC0ILxx 6DsDw/YpMd9lRyujKa//OVJOEO3Z+SalfoLslMPCD1eeJBxgx9/xTMxZUrunOMJu Ddxc5RypVwdt6NOUQ3pNYU+Qkp73aZzakm6/i1LQDOxGf1PaZooPqXaKMfyb9CQ+ M5wR9XsLDAyQPlylft3cWsp7DaPsrcp7Sn5u33TtvCDgfMwbKZrzzmCdAHS52p0A czMpDgYxhxCuHPwN6XSAxlrWnfpkOsdKneiVr4aDm1fp61PQg+sOKNCNJCMmUB50 XFj14qLmvjLERNl2vUfDqoj6DxC9OceioZljzFM2d8/DEC1/YUT8AGYZQt0ISLlq QW4e4iIxXxVNKwXvJZMCd3z4XX8SvAOXL0+WrOffgkQQpSr2jBXKFe0BpCUnzVkN jw/bVJ89jrqfupExJ6kKTLzI+H2u/7LgZUn9/QlvhWR3IL1nEvCCOPHXTreuUp2U KauH7WQU+mzF/K+obzYiaPc53dz0C3JcvWgv5cBbhDUsGsziZLPkR3r5TvpTNF3t c3Ky84q7CfOKI+hGN/S+BNSv+TdA5uOG+h8GJ6SAwVpimzguhx2/iRg7OiHmhoy4 UfsCF/QdoF3cEoSMMzorOIM+szZ+XggJMbtWzIv4NAL2TCKJcpB6t0Uxci6JN6lM tnbq78QxeOoJ3D3g6ZQoGvCdKwHcMQunS2MK75j9hckX5hR+0xnH++bI+D3qXaul zCOAcMoGpM7WgyzAc3mh9aAJFk1J9OYzpFY34nX2cDMc0xtI2s9p2/8X14ir5suK R0rqm3BZewDaQVNtrZCm/sch1I0cN4GomvxZ+2xD43vsZm8QEB2fyTvazg6kW2O9 G0coUmszwI63f7fVx+wzZZxS24N17lIO36hjBq12SKDz9C3cIOZpPYdcaNxDyiwq +RojbgUnosJji+xZtyK5Bf1kNB+jNoNT5OrWCXCR62Z5CQ+fAdlz4NPdibYnwscF SD8OA2I7UIT42KAuDBEMKVXe4SlhqLzOdqXwZ7h/ZxyUlY+zpwLO9vfLVpqZiavl bx+RO7lkvxPGEtM4mjXt5dNx2rgML/lXSE63qaqspYLJ806A3GE0918/b+ZDfvzG HLbeqIVtAlXwOGFLdifLvsgFybQ5yriR/yTlHXN/T8K+Q6wPo3uNkKNgUDq0FmzF N9UDQ+VFAdwaGPfBox3okGPoD6/HdiokuwZm4mlioHjZc513qhDapS3fJcXV6tu9 OhIPBJ5NXVw3IVXwB+eskd2W5y8QlHZKBNTwzc/PBqL93QA2PxOFispDtevAuyTq OhRConYsMhoLf/9NqhEsyKVhwtYhYrUh0Q+hCiAg+YpjKuE2YJoUNMVyF3dY -----END CERTIFICATE----- ./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-apk-hash-mismatch-v1.apk0100644 0000000 0000000 00000000034 14763776540 027662 xustar000000000 0000000 28 mtime=1741684064.7590000 src/test/resources/com/android/apksig/stamp-apk-hash-mismatch-v1.apk0100644 0000000 0000000 00000040726 14763776540 024537 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ][Aif3m$wGwZkM-UbWY`aMTwlf18sN_[1f+KX|ʪr4x˻T&0F#,f32 hvU_5[TDm?\&:aE7{]zc1Mķ#~Uⶸe¦?\Gz%xf_  w0 0PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y8jN,n{8WY %E}bs?rdgU "3ǰCw^v n(Wӥ<^$!h!,%t3dKQ0%4LƱϜJ_!K(28Yb<ڔ5Fvݵ 2W 0pvei(/pё-_Zqa~P˅5&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( V|8Qّn^QyZbGh] !)5pj/K}"nTA."yvڄ4Sg:z@Lt&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[U mM00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*=@h !_gnr~A(@M"h(g~ST7&ȕU@T Ff:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ][Aif3m$wGwZkM-UbWY`aMTwlf18sN_[1f+KX|ʪr4x˻T&0F#,f32 hvU_5[TDm?\&:aE7{]zc1Mķ#~Uⶸe¦?\Gz%xf_  w0 0PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y8jN,n{8WY %E}bs?rdgU "3ǰCw^v n(Wӥ<^$!h!,%t3dKQ0%4LƱϜJ_!K(28Yb<ڔ5Fvݵ 2W 0pvei(/pё-_Zqa~P˅5&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( V|8Qّn^QyZbGh] !)5pj/K}"nTA."yvڄ4Sg:z@Lt&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[U mM00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ !_~_ ).VaF^PJ^SU#|OS/;"H3 NFM禷]?3òYeoI h|\SR{P 93M?vJ@uφoPvNW,ď7j,%s D}cc-RqϹiYxy+!ZGGz93=]ROHOZDhAl_8:iCUlу|^>=@h !_gnr~A(@M"h(g~ST7&ȕU@T Ff:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ][Aif3m$wGwZkM-UbWY`aMTwlf18sN_[1f+KX|ʪr4x˻T&0F#,f32 hvU_5[TDm?\&:aE7{]zc1Mķ#~Uⶸe¦?\Gz%xf_  w0 0PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y8jN,n{8WY %E}bs?rdgU "3ǰCw^v n(Wӥ<^$!h!,%t3dKQ0%4LƱϜJ_!K(28Yb<ڔ5Fvݵ 2W 0pvei(/pё-_Zqa~P˅5&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( V|8Qّn^QyZbGh] !)5pj/K}"nTA."yvڄ4Sg:z@Lt&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[U mM00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ !_gnr~A(@M"h(g~ST7&ȕU@T Ff=@hEwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/RSA-2048.SFPK!:ܪ% META-INF/RSA-2048.RSAPK!:,{ 2META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-apk-hash-mismatch.apk0100644 0000000 0000000 00000000034 14763776540 027336 xustar000000000 0000000 28 mtime=1741684064.7600000 src/test/resources/com/android/apksig/stamp-apk-hash-mismatch.apk0100644 0000000 0000000 00000040726 14763776540 024213 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:k3# stamp-cert-sha256ː [>͔#8h#Zw}~r_ PPK!:/^\META-INF/RSA-2048.SFmKo@= e;XQI\B!bwfAy͌%MM;! Ô$֤f! !ׯ@zcDE@i`Hq`˜Ci=##hg:kR|(F}=N#MR^(FXn?":$r:/g#%y-}τ?jr޼ 9̓?^Z?nᄍUj5IZ%.u ϔܛ}x_~㶰?Hr v,bo*86o~ D_Uj}T~![N|ZƖj&YI{$ӵ < Wg7yҒ /[d9'ۭX{om;/?V??R@..Z]MɬPK!: r/META-INF/MANIFEST.MFeMo0 ×Xܜ8, Tl2q~lY2o>>$P8d&. CP\_SD F`%? fe ePߣ4LvI(?YJ-]edN}k\w5N[iQYUxQ0?UsuR4aį; MPGiI4ϣZVdӖe= U%!h(f A#v ; K0ߗ7*n 1{Lcv>{K[b "88bjlI7 q}`( S8r."l>p^ e50!(x"^mڳݠ%Bz;HX!00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*p^ e50!(x"^mڳݠ%Bz;HX!00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*b~b~ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t !e7/0B(./!*ೠԿZʊ@S wmf97^ikt35Z [ڐ 8irHN+B&Wv=l'5~*lZ~,?>"tvV.Ɓ F5<\=wvoRwbKO𠨡@]cĽ&eR}5X1S$’t9D;4pnS9Va:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ][Aif3m$wGwZkM-UbWY`aMTwlf18sN_[1f+KX|ʪr4x˻T&0F#,f32 hvU_5[TDm?\&:aE7{]zc1Mķ#~Uⶸe¦?\Gz%xf_  w0 0PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y]Zȟf vRES"?/HBnro }ttB- MWDMu1ݐ\i>m0 P0GD4bE!]%e0 Hc9 >& ]Q)\y"l''݋TqC(ϞQ%=&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( dzx 9p͵gjh#00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*zXRxg`hU$MzVc̔)u#}[1R9aJUF5 ~prbvJ<{Xv={.P-픭*>ƬZ_oc(5Y5 fDF{TC.~h3KgoduUQ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ m00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t ! ;c! OV ыjK-E34xLmI󏠽Yf2e|PK<#dB T 5 ]8-M>33%h# s/ 8e;l2 f=44+jR~v・Qkz$%0FWY!e.h;wbu|[ 1y058yYl3K*~ET:EHNpMrb ʕ:2鲛werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\META-INF/RSA-2048.SFPK!:ܪ%DMETA-INF/RSA-2048.RSAPK!:,{ 2META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-int-timestamp-value.apk0100644 0000000 0000000 00000000034 14763776540 027744 xustar000000000 0000000 28 mtime=1741684064.7610000 src/test/resources/com/android/apksig/stamp-int-timestamp-value.apk0100644 0000000 0000000 00000020723 14763776540 024614 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;XtkmylA1 :V7&dw}n:bι"M2r8ʤb7tsmMe&, PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ 3g<d8+O]VT4 F]5mX0$>]Apv '~s@$3v> IF_4Q6:Ȟ~Yd*, ˩rp-55\UŸ(8tsiI1^no})`jcó.odg #m{`&Ph{O=C'%/=A(AP>/ Zk/ 3g<d8+O]VT4 F]5mX0$>]Apv '~s@$3v> IF_4Q6:Ȟ~Yd*, ˩rp-55\UŸ(8tsiI1^no})`jcó.odg #m{`&Ph{O=C'%/=A(AP>/ Zk/ FY< `\Y]Hӕ㿀Z1Q8pʹܖTyp7QySw0cC{#sq,-&sCv·9" u!lG0V9I>kۻztx׽yamTE[<CWϫWwUXm6CTha|Z]NLan6A7!lL%YņCn񺷎 ` dT3B8`\cDwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/EC-P256.SFPK!:: META-INF/EC-P256.ECPK!:,{ 2 META-INF/MANIFEST.MFPK ./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-invalid-timestamp-value-less-than-zero.a0100644 0000000 0000000 00000000034 14763776540 033116 xustar000000000 0000000 28 mtime=1741684064.7610000 src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-less-than-zero.apk0100644 0000000 0000000 00000020723 14763776540 030321 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;XtkmylA1 :V7&z sp>ۊ gTO^'`kK7&:S^{5wl۶h6l-5?Į)JPK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~yX ?'H F!ZBƆg3^K?}(c*U[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDhSzv,( Xy'?YNIW]T8htp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,USOG0E!vN@cV#Ĝ¢̥x F:DO{ihwT9F[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD} mu00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ ~~_ա%RES8nU(x';}INxٚxE+"O,Rt}w6JTu5g@ oƘN! Hn!]`q^[O]'[s!kt3V$Nj,k_6QR h Fl|W*:=E^fM51 PSjq qO?N Vde\Ļ~"FG- Z$Tm|fʹiNvF ~~_ա%RES8nU(x';}INxٚxE+"O,Rt}w6JTu5g@ oƘN! Hn!]`q^[O]'[s!kt3V$Nj,k_6QR h Fl|W*:=E^fM51 PSjq qO?N Vde\Ļ~"FG- Z$Tm|fʹiNvF FY< йx/: 7c2{&Y!^#bTf9/0P t j-[c!tiKh[UQc*+-h$gπ{'c-Cú8ǐ 5A*$ĥjh`3|62!&ە^MF7ORjd%}i3˽9AlhFwC0l8-8`werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/EC-P256.SFPK!:\I: META-INF/EC-P256.ECPK!:,{ 2 META-INF/MANIFEST.MFPK ./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-invalid-timestamp-value-zero.apk0100644 0000000 0000000 00000000034 14763776540 031555 xustar000000000 0000000 28 mtime=1741684064.7610000 src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-zero.apk0100644 0000000 0000000 00000020723 14763776540 026425 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;XtkmylA1 :V7&m~3Ydtx~SQ^턅%˕ndR0w[1-l; κHVsúPK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ gh,q7"YCT[>r'ꑃ&S!~V1__?R_K`,K =ўoUVƢr'ꑃ&S!~V1__?R_K`,K =ўoUVƢwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/EC-P256.SFPK!: 9T: META-INF/EC-P256.ECPK!:,{ 2 META-INF/MANIFEST.MFPK ./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-lineage-invalid.apk0100644 0000000 0000000 00000000034 14763776540 027067 xustar000000000 0000000 28 mtime=1741684064.7620000 src/test/resources/com/android/apksig/stamp-lineage-invalid.apk0100644 0000000 0000000 00000040726 14763776540 023744 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ][Aif3m$wGwZkM-UbWY`aMTwlf18sN_[1f+KX|ʪr4x˻T&0F#,f32 hvU_5[TDm?\&:aE7{]zc1Mķ#~Uⶸe¦?\Gz%xf_  w0 0PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y8jN,n{8WY %E}bs?rdgU "3ǰCw^v n(Wӥ<^$!h!,%t3dKQ0%4LƱϜJ_!K(28Yb<ڔ5Fvݵ 2W 0pvei(/pё-_Zqa~P˅5&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( V|8Qّn^QyZbGh] !)5pj/K}"nTA."yvڄ4Sg:z@Lt&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ m00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ _gnr~A(@M"h(g~ST7&ȕU@T Ffct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >#x `.t tO}8XRe M:Y%# g ZsAˎ3//匐5V+f[i5c1@:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:k3# stamp-cert-sha256ː [>͔#8h#Zw}~r_ PPK!:/^\META-INF/RSA-2048.SFmKo@= e;XQI\B!bwfAy͌%MM;! Ô$֤f! !ׯ@zcDE@i`Hq`˜Ci=##hg:kR|(F}=N#MR^(FXn?":$r:/g#%y-}τ?jr޼ 9̓?^Z?nᄍUj5IZ%.u ϔܛ}x_~㶰?Hr v,bo*86o~ D_Uj}T~![N|ZƖj&YI{$ӵ < Wg7yҒ /[d9'ۭX{om;/?V??R@..Z]MɬPK!: r/META-INF/MANIFEST.MFeMo0 ×Xܜ8, Tl2q~lY2o>>$P8d&. CP\_SD F`%? fe ePߣ4LvI(?YJ-]edN}k\w5N[iQYUxQ0?UsuR4aį; MPGiI4ϣZVdӖe= U%!h(f A#v ; K0ߗ7*n 1{Lcv>{K[b "88bjlI7 qI,( S8r."l>p^ e500 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*>N15 LGK.Gsnm[G M6kKuY)Ї&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( S8r."l>p^ e500 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*.o~露V[t/dw&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ m00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3tH L/T3G k`BT):tQ+ {_D.t=' ^1z5N∁'!qx p)רE=菦qL z"kѦl n">PHh;0vqAX/+ۉ6chTzWL/}+~`m8B.rlJPC2{zEl{λLi%6IY$uĴl| o^&tsag+:)H} \Θf I(:]QX(}QȤdk]q"ʽWf]Vli(CfK. As^E"uxm*DBŦhA=|k(ڀ~ӟ ^?t ھ]^d !/<ƓkMv?IAjW'e).UG \Θf I(:]QX(}QȤdk]q"ʽWf]Vli(CfK. As^E"uxm*DBŦhA=|k(ڀ~ӟ ^?t ھ]^d !/<ƓkMv?IAjW'e).UGD@<c00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >_> IWǵwX`VQ8 Ӑ*(| BOA遥J|KjAiٹyw0}7gPCqn뾻6ed}A@y Pd6Ywn~.̞j%RrH\󈲶BlFgy_͕TwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:k3#  stamp-cert-sha256PK!:/^\ META-INF/RSA-2048.SFPK!: ' META-INF/RSA-2048.RSAPK!: r/META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-lineage-with-3-signers.apk0100644 0000000 0000000 00000000034 14763776540 030224 xustar000000000 0000000 28 mtime=1741684064.7630000 src/test/resources/com/android/apksig/stamp-lineage-with-3-signers.apk0100644 0000000 0000000 00000040733 14763776540 025077 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlNSAP~*P 1,Ѣ;c MPb[J@dK/^ /W;g33Xy̼ϼ3L#)ꨈe,Iy`&o{:p } D# k?E9]y%-z rĬLْғcK¸ WgفK s '2!ߦ -[L{KY2$4uAek?xe]9'ɐ0:a6[z;|GL}5KL]Y㳏GXIkku>x7yʉV*jD'Z߶ֺToWp6Os=-hmctz y$7i0Y0f ,`IUPͫnVWK;UD78O&W bx!z7zނ7rU7p;0]}+n !y>hƽ..C\51ϱ6_6e\e41X"/xQVg΄Rmpz ;+jeLP,Dn| 4ÐB0RbgClxO@Xnd4D\Tc: \ s%0lgmzg&n%}Ukٴ39)|\lG7,JrxH/Z,^Gqf{}nF3ZzϬf_HFd3XIVVF˞(fZAdgX9e PKF2PK!:%'c# stamp-cert-sha256]\O³TեS7UpT"qbgPK!:s*]META-INF/EC-P256.SFe]O0%c0d|me|(]V`zhsHD̥EYve#ɉ ?f3Yvkvcƕ YxoiTtM>[$8Lz{;NɮS؂o f g,4LcI2Yߍ9;I}= n˸Q YG wjҤj@Zc/]z 1+;XtkmylA1 :V7&u6U}XuNv\r)EoH~VFMU62tiBs WZ*(/ULEB3YVq|tdFg;wZޥL3n qzv,( YYXCz>L ۉFǺ+9Mtp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,U  SOG0E O3Sr|7V'ng$u\I!@UYb& '*Fj@i8Ɣ^[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDhSzv,( YYXCz>L ۉFǺ+9Mtp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,USOG0E @d/.SdHv[Vh6%P!׾fMTiXgǯZ [0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD m00 Г}=׽0  *H  010U rsa-2048_20 180619164943Z 280616164943Z010U rsa-2048_30"0  *H 0 {3j?[3+2 b1Φ2RY~sw*^u?iw}ER mb3KXTUßUgn~'[\^cvG#aL5=Zɖ3cM$IO*o?8spPDL oZFQkE&g$>/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."^MI^l%餔鱥I'8#iV\7 v쓎Y,Qe |Am;^ݵ~ | zPEuUi:1~k׎&dffH6o#xBq>&+E"DGRveؓ@S]nb#VkH/ i}`XA4Nۑ()aR^ BhP,JNPɞM`ej 6`S "4PkؒpZ| KJ.&~+'е˵ƽDPSi&+E"DGRveؓ@S]nb#VkH/ i}`XA4Nۑ()aR^ BhP,JNPɞM`ej 6`S "4PkؒpZ| KJ.&~+'е˵ƽDPSict%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."_ 2fڃ8MkQ % ƿ'a} >)3:hR0knbi*Sť5XiT0j wҐfbHaň=l̡LĞMe@ Qǣz#٤c r.F΍ 1~ r[-&zLQq%x [Pf6<7E qy{"(IIR. U _zic ';O!4cU],/#p_VI\W)la\-]F;(ZBrJ3b~ݡt4M)I %`2Y/-5nB!lM\Qe t@}HkM֍H:Zh+:{U& C. )aXx&Z/7werBAPK Sig Block 42PK !:9gGÄ resources.arsc5PK!:n&ę$AndroidManifest.xmlPK!:F2 classes.dexPK!:%'c# stamp-cert-sha256PK!:s*]META-INF/EC-P256.SFPK!:~:}META-INF/EC-P256.ECPK!:ĹV{META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-malformed-signature.apk0100644 0000000 0000000 00000000034 14763776540 030004 xustar000000000 0000000 28 mtime=1741684064.7630000 src/test/resources/com/android/apksig/stamp-malformed-signature.apk0100644 0000000 0000000 00000030726 14763776540 024660 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ][Aif3m$wGwZkM-UbWY`aMTwlf18sN_[1f+KX|ʪr4x˻T&0F#,f32 hvU_5[TDm?\&:aE7{]zc1Mķ#~Uⶸe¦?\Gz%xf_  w0 0PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y8jN,n{8WY %E}bs?rdgU "3ǰCw^v n(Wӥ<^$!h!,%t3dKQ0%4LƱϜJ_!K(28Yb<ڔ5Fvݵ 2W 0pvei(/pё-_Zqa~P˅5&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( V|8Qّn^QyZbGh] !)5pj/K}"nTA."yvڄ4Sg:z@Lt&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ mwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/RSA-2048.SFPK!:ܪ% META-INF/RSA-2048.RSAPK!:,{ 2META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-only-unknown-sigs.apk0100644 0000000 0000000 00000000034 14763776540 027460 xustar000000000 0000000 28 mtime=1741684064.7640000 src/test/resources/com/android/apksig/stamp-only-unknown-sigs.apk0100644 0000000 0000000 00000020723 14763776540 024330 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;XtkmylA1 :V7&u7^KGnQJ}smJV_qnä20"y}3IAV2 Es; PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~yPa۷z YC,:btp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,U  SOG0E! I=҇+\Q3%JHEj@'㦼3X bm0ø ^&+'f%i(/[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDhS{w,( q[>Pa۷z YC,:btp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,UTPH0F!{г9HCM A@ W+5o<8Ÿ!B\N=7Wri?+d9."[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD m 00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^^.27ZS 0RhI󗵶b9[Q]fH6+*1B~o^>9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ f}+e|6Vt&B,wC^[V?k[ċCt`倞9>nKȱF3P4eh:Y\ZRTIz+6 NVA[2cGmq`D&Ԡ\$Y`+d!}gIfPwI:r5^be3#=Cpo{@|ȪUa/~*:-4zP-"FuUM_$2f}+e|6Vt&B,wC^[V?k[ċCt`倞9>nKȱF3P4eh:Y\ZRTIz+6 NVA[2cGmq`D&Ԡ\$Y`+d!}gIfPwI:r5^be3#=Cpo{@|ȪUa/~*:-4zP-"FuUM_$2 f}+e|6Vt&B,wC^[V?k[ċCt`倞9>nKȱF3P4eh:Y\ZRTIz+6 NVA[2cGmq`D&Ԡ\$Y`+d!}gIfPwI:r5^be3#=Cpo{@|ȪUa/~*:-4zP-"FuUM_$2f}+e|6Vt&B,wC^[V?k[ċCt`倞9>nKȱF3P4eh:Y\ZRTIz+6 NVA[2cGmq`D&Ԡ\$Y`+d!}gIfPwI:r5^be3#=Cpo{@|ȪUa/~*:-4zP-"FuUM_$2 FY:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;XtkmylA1 :V7&1_:|S`/l7뤣N,bR=չqeV>w;%+mf?[ucUS PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ WgʘhD_A3O;p]T&FuȢy N0޽ +3]Dډ6\;_>r%}]֬^Zz 9BV^ІOU :} - v+{4jcUZ~[&8 +Ub8CxDP`RcSD-(U/n (/g3o5S\ WgʘhD_A3O;p]T&FuȢy N0޽ +3]Dډ6\;_>r%}]֬^Zz 9BV^ІOU :} - v+{4jcUZ~[&8 +Ub8CxDP`RcSD-(U/n (/g3o5S\FY<b -DY[L `p)'At*׾"Zp& B7c@ﳗMܾsUkQ%!"D"r19lFPb A@dUga46"\a5sI|eTڏ1>(t1Ϥrğgz|!?_FzhYv:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;XtkkylA1 :V{Z0n7Gw2U$ĎEf~۲n @%+unO( Vw_m2C O$PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ &!DlL}^˶YxBW;51ϫ> S)P H UA,ZΒ$+f}2l?[WL~bnuo}bt|m / )N=57{-@-&vEgx3CP NQʈ&꼷}Scui M~ 8gZ$} \>Ģ^_^=غ1@@9_?X-E6AZ &!DlL}^˶YxBW;51ϫ> S)P H UA,ZΒ$+f}2l?[WL~bnuo}bt|m / )N=57{-@-&vEgx3CP NQʈ&꼷}Scui M~ 8gZ$} \>Ģ^_^=غ1@@9_?X-E6AZ EY<ιb 5^M}rŪwOjvW&F ~Y)ց B ^zBbC=췀#ͳV q]WV?ůgSJu*hQn/Ec}n֔J^䲞2۾AP" ja@werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/EC-P256.SFPK!:< META-INF/EC-P256.ECPK!:,{ 2 META-INF/MANIFEST.MFPK ./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-unknown-sig.apk0100644 0000000 0000000 00000000034 14763776540 026316 xustar000000000 0000000 28 mtime=1741684064.7640000 src/test/resources/com/android/apksig/stamp-unknown-sig.apk0100644 0000000 0000000 00000020723 14763776540 023166 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;Xtkc ylA1 :VwWeu{t\!7ʋkE7g}pleL 1 _6Ub:%l?Lҕر*ި߃PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~yG,@2 !ch$Yx >e>IF^jA[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDhSzv,( Pgt Eo.%Bd<':r *Gtp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,USOG0E!砜zDT!ɉ|\ 1(o{M3)L zjnJa%[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD m00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ l.Bř"wTU=Jh͵5%S]Gmtl9+贔Emo9VxPځL=NH;ȑe̶%ΰ8~(j}l'vʴ Cpu{E0w?!*Wg93xb psHISZעށ<Ӊ$~ K- qJjB5:b)ϓm@*L?N:+_zw$tO鱎aS l.Bř"wTU=Jh͵5%S]Gmtl9+贔Emo9VxPځL=NH;ȑe̶%ΰ8~(j}l'vʴ Cpu{E0w?!*Wg93xb psHISZעށ<Ӊ$~ K- qJjB5:b)ϓm@*L?N:+_zw$tO鱎aS FY<'bL=+{~8Y%N4Kk7$',\$(EX3(*jQB%z% *y32޿zgQxN-_e)6m}' j>w@cb]NVS5>8vEfS_k`f9N:jx[B皪8q Y|| "T[ _dbw@cb]NVS5>8vEfS_k`f9N:jx[B皪8q Y|| "T[ _db:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;Xtkc ylA1 :VwW&<UkHI*]1ͰI3_g:YрiK܄?;'u'yRqګ[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD m}00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ <Qf4yJ?Ao~8DaiW넾-}%zR tGspd64@r-;$PȁfءKiG%у=xGe_Y\;Q`n.T93#L-5g;.|B8:'/on Nwh"H"+Jd4]SFx*wPݫ?{k+|s5]'}%J:Ư?fe-ϟÄ|N <Qf4yJ?Ao~8DaiW넾-}%zR tGspd64@r-;$PȁfءKiG%у=xGe_Y\;Q`n.T93#L-5g;.|B8:'/on Nwh"H"+Jd4]SFx*wPݫ?{k+|s5]'}%J:Ư?fe-ϟÄ|NFY<䂤b  q`3tn)48هLulІkxkVIב,;ܬ W# ujsO[A4{]B+x.1V3Cq4*=}yNCЭЛR>VcCWl A>&+ }/siƺZ^^Er@ǒLώ}Yg|£ߏ 5 ~z;]X-j1" :5werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/EC-P256.SFPK!:#; META-INF/EC-P256.ECPK!:,{ 2 META-INF/MANIFEST.MFPK ./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-valid-timestamp-value-modified.apk0100644 0000000 0000000 00000000034 14763776540 032027 xustar000000000 0000000 28 mtime=1741684064.7650000 src/test/resources/com/android/apksig/stamp-valid-timestamp-value-modified.apk0100644 0000000 0000000 00000020723 14763776540 026677 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;XtkmylA1 :V7&/B3OSzvz_>K_ߐIBSWwus$0>,èaŧf.PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y5v07!n }0!+S+Ս%Җx@[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDhS{w,( aM \qwEG}cjYGo7tp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,UTPH0F!5'{ΏcՀ ܈M1!arAr?d]bfH[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD} mu00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ VMŔ\eFqXTǦ0"3XwYp`\QW~rݪ67P af'[0Êw?MJHDVȄ ys!`frD %iK$iP$􅁘O6&Z5{xKۆ/.r7ڦ"6HzYf(xt'̼|7q ƍN" 8 hr CP?D VMŔ\eFqXTǦ0"3XwYp`\QW~rݪ67P af'[0Êw?MJHDVȄ ys!`frD %iK$iP$􅁘O6&Z5{xKۆ/.r7ڦ"6HzYf(xt'̼|7q ƍN" 8 hr CP?D FY<9 b 4ĀRH=}"o+Q5⟖4t8ș`H\$2`b?9vFO o}niw_@xd_ af[d%`& ⡁K#^Scr+\PtᶱIɮ:] olK63ѮUEvcѥKEaNPU0(*fm;ucOZq(dy͂n>>werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/EC-P256.SFPK!: e: META-INF/EC-P256.ECPK!:,{ 2 META-INF/MANIFEST.MFPK ./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-valid-timestamp-value.apk0100644 0000000 0000000 00000000034 14763776540 030251 xustar000000000 0000000 28 mtime=1741684064.7650000 src/test/resources/com/android/apksig/stamp-valid-timestamp-value.apk0100644 0000000 0000000 00000020723 14763776540 025121 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;XtkmylA1 :V7&/B3OSzvz_>K_ߐIBSWwus$0>,èaŧf.PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y5v07!n }0!+S+Ս%Җx@[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDhS{w,( aM \qwEG}cjYGo7tp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,UTPH0F!5'{ΏcՀ ܈M1!arAr?d]bfH[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD} mu00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ VMŔ\eFqXTǦ0"3XwYp`\QW~rݪ67P af'[0Êw?MJHDVȄ ys!`frD %iK$iP$􅁘O6&Z5{xKۆ/.r7ڦ"6HzYf(xt'̼|7q ƍN" 8 hr CP?D VMŔ\eFqXTǦ0"3XwYp`\QW~rݪ67P af'[0Êw?MJHDVȄ ys!`frD %iK$iP$􅁘O6&Z5{xKۆ/.r7ڦ"6HzYf(xt'̼|7q ƍN" 8 hr CP?D FY<8 b 4ĀRH=}"o+Q5⟖4t8ș`H\$2`b?9vFO o}niw_@xd_ af[d%`& ⡁K#^Scr+\PtᶱIɮ:] olK63ѮUEvcѥKEaNPU0(*fm;ucOZq(dy͂n>>werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/EC-P256.SFPK!: e: META-INF/EC-P256.ECPK!:,{ 2 META-INF/MANIFEST.MFPK ./PaxHeaders.X/src_test_resources_com_android_apksig_stamp-without-apk-signature.apk0100644 0000000 0000000 00000000034 14763776540 030312 xustar000000000 0000000 28 mtime=1741684064.7650000 src/test/resources/com/android/apksig/stamp-without-apk-signature.apk0100644 0000000 0000000 00000030531 14763776540 025160 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlNSAP~*P 1,Ѣ;c MPb[J@dK/^ /W;g33Xy̼ϼ3L#)ꨈe,Iy`&o{:p } D# k?E9]y%-z rĬLْғcK¸ WgفK s '2!ߦ -[L{KY2$4uAek?xe]9'ɐ0:a6[z;|GL}5KL]Y㳏GXIkku>x7yʉV*jD'Z߶ֺToWp6Os=-hmctz y$7i0Y0f ,`IUPͫnVWK;UD78O&W bx!z7zނ7rU7p;0]}+n !y>hƽ..C\51ϱ6_6e\e41X"/xQVg΄Rmpz ;+jeLP,Dn| 4ÐB0RbgClxO@Xnd4D\Tc: \ s%0lgmzg&n%}Ukٴ39)|\lG7,JrxH/Z,^Gqf{}nF3ZzϬf_HFd3XIVVF˞(fZAdgX9e PKF2PK!:S% stamp-cert-sha256 ]7Ch;BgR뒕n#per&؞ /BPLv^ 顎AgžnqPsr[WdN9([ɰF`8s- pF9g *yݽU/:{owf6aٚ|TM0=׶= m500 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ][Aif3m$wGwZkM-UbWY`aMTwlf18sN_[1f+KX|ʪr4x˻T&0F#,f32 hvU_5[TDm?\&:aE7{]zc1Mķ#~Uⶸe¦?\Gz%xf_  w0 0PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y8jN,n{8WY %E}bs?rdgU "3ǰCw^v n(Wӥ<^$!h!,%t3dKQ0%4LƱϜJ_!K(28Yb<ڔ5Fvݵ 2W 0pvei(/pё-_Zqa~P˅5&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( V|8Qّn^QyZbGh] !)5pj/K}"nTA."yvڄ4Sg:z@Lt&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:S%  stamp-cert-sha256PK!:8D\ META-INF/RSA-2048.SFPK!:ܪ% META-INF/RSA-2048.RSAPK!:,{ 2META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_targetSandboxVersion-2.apk0100644 0000000 0000000 00000000034 14763776540 027227 xustar000000000 0000000 28 mtime=1741684064.7650000 src/test/resources/com/android/apksig/targetSandboxVersion-2.apk0100644 0000000 0000000 00000015053 14763776540 024077 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=㵝q'8ֆ(PhB$CAD*  MD(OB|AEuvf{cf ķ"Pb88눸F<"^O;qQDB| >|([CH ~8B} zs=4LTrcG]Z= `>itd))K]]<&L>-;<Si;'BGPK PK!:META-INF/CERT.SFen@; YEjŐx`](m]uAYY@k[66?mFPb0^Gkn]=E!2F ;)1]dj*\妲恦6; z:M|[scDWG~tVB/dv@wH(wlrB#: ktRЊ7"XT:x׍l"q!_[uY;5- PKb,PK!:META-INF/CERT.RSA3hbƩiAr&Ll m,L  X40hba"W*ddkbm&l(l ps9'de&مy}KJ32S  @\y)E)0n f b‰q(k 'khh`ihid``fb%kli`d`Vg71* 00byLM 﫺LnʲT.`/@="Nq|fvk#Ei97MZKۗy_uv 5wn5iUwTߗ!f۸얼$&A,hgg?Zd5~wpP +XԞZ{XK*]C;$M}^_;*.2+?2奙߲E!k|7]sy$M)BkSv̋Ng9=o'312/nc@|,b,"L?:*ɴ~9+afSϻͻ(y; /m#aK>%PQ2{;|\S OsYkYA^yj{Z;iu!Vǩm&n/nu>Rnvgq_s3ذ0lie/jgibdlԹAmU%K>-z/E'̳kʑܿ>!o⇜anߞ6IۗG}*#ǖ%_4ACRMKjzLb)~O2?f>);^qҼ-Y'٭wfO-mHL4*{1"TI@ sn ]7YvԈT^|Y\Ϲeủgk]Ǘ{7ɼ\To^S\vxq7IaI?fU/Cc𶻽 PK1m*>PK!:META-INF/MANIFEST.MFeMO0; 66x r@l3^L-A ma_/Mv} :!)gsõ] ERk'eJ&Ā\\ 5|k,l_R^xO͍\'`ݵ- n1aDzLD5&%H;%kf' Q#*oMp|`DVEBke׮Z眆njZch ghq<_PKZ\QP0 q($,( L|IYj9Y/"1صLW@00 ƞ0  *H 01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 110919200642Z 390204200642Z01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com0 0  *H  0z4y92aP& ،Y Ɏ m'p6ڟNr[*C_f*Jo,&v'g&>3Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`G00UfAti;iWf:6*]0U#0fAti;iWf:6*]01 0 UUS10U California10U Mountain View10U Android10U Android10UAndroid1"0  *H  android@android.com ƞ0 U00  *H G2ޮDDyD #rO;*863~\GOjx..!3bT)_'I>!=m 㶵Vt:V;dEde gw cA>!*@m^^2ث_,ߴ"W\7%.oJQ>7ΓrbKbpg5xl?&*)CW u.0@ t, Wn-exvɸ.ְQ^%,=RB84^Dy |-a~afթ$0 0  *H  0z4y92aP& ،Y Ɏ m'p6ڟNr[*C_f*Jo,&v'g&>3Qyh;e]Z# 8>VR I ;8j@L Zv&3'C>~5z-6t¸5MVɪgl$6NwT4V>YJu4a_""Qtz>ONʇkš`GPAPK Sig Block 42PK !:!O resources.arsc5PK!:z3AndroidManifest.xmlPK!:  classes.dexPK!:b, META-INF/CERT.SFPK!:1m*>1 META-INF/CERT.RSAPK!:Z\QMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_two-signers-second-signer-v2-broken.apk0100644 0000000 0000000 00000000034 14763776540 031537 xustar000000000 0000000 28 mtime=1741684064.7660000 src/test/resources/com/android/apksig/two-signers-second-signer-v2-broken.apk0100644 0000000 0000000 00000017167 14763776540 026417 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWԬDk k}`V޴YFl+PK-PK!:META-INF/CERT0.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICԬDk k}`V޴YFl+PK-PK!:META-INF/CERT1.EC3hb2cjhδIݠIѐ߀3̓1qAcAc .`fbdbd8ſw".6s,LB|l̡,<ɺFfr⼆fƆ&fQ&P.V=l`S333;1,U:)<};/+פ][tn¥-NsOl~rr~Ps˖cc"l3@HUh3' A,b 032GfO7&E, r}".]8?9$Yqk@%?-i>@Zc/]z 1+;XtkmylAh1wW]weײtœݽtm}f+ I@h܃u:Ʀ&|Oq DPKa:-  qw9,( F8Ha yV (lH00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWԬDk k}`V޴YFl+PK-PK!:META-INF/CERT0.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICԬDk k}`V޴YFl+PK-PK!:META-INF/CERT1.EC3hb2cjhδIݠIѐ߀3̓1qAcAc .`fbdbd8ſw".6s,LB|l̡,<ɺFfr⼆fƆ&fQ&P.V=l`S333;1,U:)<};/+פ][tn¥-NsOl~rr~Ps˖cc"l3@HUh3' A,b 032GfO7&E, r}".]8?9$Yqk@%?-i>@Zc/]z 1+;XtkmylAh1wW]weײtœݽtm}f+ I@h܃u:Ʀ&|Oq DPKa:-  qw9,( F8Ha yV (lH00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*|([CH ~8B} zs=4LTrcG]Z= `>itd))K]]<&L>-;<Si;'BGPK !:!O resources.arsc5PK!:z3AndroidManifest.xmlPK!:  uclasses.dexPK ./PaxHeaders.X/src_test_resources_com_android_apksig_v1-ec-p256-targetSdk-30.apk0100644 0000000 0000000 00000000034 14763776540 026470 xustar000000000 0000000 28 mtime=1741684064.7660000 src/test/resources/com/android/apksig/v1-ec-p256-targetSdk-30.apk0100644 0000000 0000000 00000016703 14763776540 023343 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlSn1=$MȣiR胖+DS*X +4IFIhf@TB`Q!>o p|cgӈ{m4 (N'3'V)'Έw"{KCO;I%nDh@vI㘳g@}ghBDdL1{_y2Q,#8!g r5e5웴>}\oGv951gN~GLVvdxeM6!m s>1^E+bKD'N&_AǒK,zIT:]}[4m?pGdb6G2Hږc6t KSo#H%c9ԥUBGܑxk?>;"86 }ST@SJkĈVQ*$"̽~]f_77튼*:rpi9M᲎2\ w(ߢYiƳL yI&Cf:Zv/=>O8>Zt/k75h--۴͎Ѵ6%c[k/8ޒWgU^,`yN"jPK`zPK!: classes.dexMhA~d5mmlH\ iZjRPDNm71 -ŪAzqЦ !+|9F~ɏVҐs v܈;Vv?J7 >˦4B\(2/i3 JC #!s.179<plc?MssJ@QҚY$%eEFih7%"6 lR'Peb$GǗ,Շl@IDsH, V"Aë~Pδ/z=:Wڽ}ux1ypk|G=:0khc$ SC}~薖0:z]]ZhDM4LQ:cx]nQ WM]'4w5AԵe72d&f>Š:,)-)ubVj-KX1|efRƊAEǒR/؈DMVRE:SPKPK!:DMETA-INF/EC-P256.SFeAo0; ZΑsAVh1E ڂC3o_APrE ZWzYs5| ʓ]a^S,Kг5j.hh~*;>~IeӞuw>x%YZ!F,cz+XJVqIKGl=^UC}ODl2J$=LG24iњMc_|7)_1.F4w[UwsHND".G՟ijYrLkE`LQ]X<;PK!:h;META-INF/EC-P256.EC3hb2gjhδIàIѐ߀3̓1qAcAc .`fbdbd8ſw".6s,LB|l̡,<ɺFfr⼆fƆ&fQ&P.V=l`S333;1,U:)<};/+פ][tn¥-NsOl~rr~Ps˖cc"l3@HUh3' A,b 032GfO7&E, r}".]8?9$Yqk@%?-i>@Zc/]z 1+;Xtkc ylA1 :VwW&j{~rL^#׋jgIӷaZ9󫳠&Z%PK!:i@FMETA-INF/MANIFEST.MFeN@= Rp ZjPZfr^M4ϟ/$ur 󦶤ˁdgTtw oNiQؓ ,ɮorrPyX&L͒]Bsyq^2XI]Q5joL7gMK0nBp3ΖT7t}W~` |eOGLlON'p*S4;:a<Ġ9ڴ+Z_yQPK !:9gGÄ resources.arsc5PK!:`zAndroidManifest.xmlPK!: classes.dexPK!:DMETA-INF/EC-P256.SFPK!:h;META-INF/EC-P256.ECPK!:i@FMETA-INF/MANIFEST.MFPK'./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-10-signers.apk0100644 0000000 0000000 00000000034 14763776540 026110 xustar000000000 0000000 28 mtime=1741684064.7670000 src/test/resources/com/android/apksig/v1-only-10-signers.apk0100644 0000000 0000000 00000043725 14763776540 022767 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:o9%META-INF/DSA-1024.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:nhMETA-INF/DSA-1024.DSA3hbNajhδ9Ԡ9ѐ߀3̓1qAV&@\_,/Fl@,X  ٘CYy8Ru L y M yM , ͡\욚6hF Ga2?z?IH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBiRb`6PʐS1ZGpXX@5ǹg=8|kKneN1.0$#[oPK!:o9%META-INF/DSA-2048.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:iDF-META-INF/DSA-2048.DSA3hbdjhδUʠUѐ߀3̓1qAKAK43-`fbdbdXt@=;N6vs,†l̡,<)ʼnF&r⼆fƆQ&P.vMMML0&FFܕ>lӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc$2PZ" 8h%ti6m6:";\Tr3կ)2Ǎ1PK!:o9%META-INF/DSA-3072.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:TrMETA-INF/DSA-3072.DSAmiTe5a'$"kB B""Y E hC҈! REM@UY ,$)Ι9=={ARD[j((,Pl5/s}08@@YbqaP ECB 7D#Qu(XpilL&R v@ӑ P+?>ĂK@lV O,6܌0%% GGa wG`9}Cbu ÷Xjaq P"G!'WEz a.Y.">&V:4;Um_[ ruɛjΣaCMjnD>qȕi^w+irF!dfaSޖWqI"ϾPxIkqPCT<1>ݛ)$ϗ4AleJt8_R0Nzgvz~3*ePKz>lϾͣxޗY\y{–P]Mx3֘DqYΎ/yDmPqyq.*[UcwMm׶{7/`Gu--!rޑʬi% 6^-BC7i&+6x`h6=vf"Fw;|~aSZ7!~g5i]xFܨz')/&p#gT|G}ᾮ׵0Nz,߉,e_EZ Ș\ sٌY4;rsMIܧk L\%̋;v8D!:!{6Q-]uM4Mz+>b0u담) Uc`cr3C4s:&Wi^6NI`ɜ^{:!ZpICs wviC-0V/ƒqA'f+ǹlZ-;kQ^ml濦l?^ITW/םh*oI4L~Ws{iq< مEkw FЂuTCMɅ,^!nC ng~ K\Ӓڅ6+:QZU&͆g67<@?ݶVo/>($NbëvJVU @G#/ " E?Px`6IOz:B?@1N%͐5Mv'q~|D[OPK!:o9%META-INF/EC-P256.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:>;META-INF/EC-P256.EC3hb2gjhδIàIѐ߀3̓1qAcAc .`fbdbd8ſw".6s,LB|l̡,<ɺFfr⼆fƆ&fQ&P.V=l`S333;1,U:)<};/+פ][tn¥-NsOl~rr~Ps˖cc"l3@HUh3' A,b 032GfO7&E, r}".]8?9$Yqk@%?-i>@Zc/]z 1+;Xtkc ylA1 :VwWeO6hΫjr~/ZߓYpbޚDU~ooJ?}r$>MnLy/yPK!:o9%META-INF/EC-P256_.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:zU<META-INF/EC-P256_.EC3hb`jhδIӠIѐ߀3̓1qAcAc. /`fbdbd+{i&U.6s,LB|l̡,<ɺFfr⼆Ɔ&QF k b(d odi 6j3#;)ټyϻ6iKfyg_~zaZsQN]hY.J-ccxSKW+Oq:.@$"a rE4cghBѬgN032GfW&?դȺI˒eW ҙu{32)2\J-+/s*=Y?a?)?3l1hƍ _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" W4ƫh;+KA A_&)&p;dbg[nsLe29&F913s%w<r;q1'G;^m:թPK!:o9%META-INF/EC-P521.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:LMETA-INF/EC-P521.EC3hbffjhδA&FFC~^6΄6Tf&FVnBM ?qf&F&&N7%˔ 8 ٲ0 10&ȉEXCX46`cƪ X_Q ׂ<rj}~ʝ~KùEǒ_ͯkwk8ɲle,_1/6UʅDfjfry ^~vyD:~cG}B,^mq,Ų|,b,"]>9VywO'eth Vf0k@Հ?r2704v091&ʷs'W$.Ȫ}6-JJsk^Kj?5DֹlĨTܞ,I_usڳPWf;yf Q6,XpAƋ)@[L!'@Vn( n[/paU;w$8C|˭ϸSD)^1nU{;_3^6`W,]o?^xǙ쥶SO{ZD𛞞 .H*}Ǿ& Rt6ݶ\~PK!:o9%META-INF/RSA-2048.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:< $META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICSIx˿CXE'.k٧G=vnhrX87Gꕾ{B"J<**&ڱϴIKlL͘qE)7]xnƦj%~<1Q.֓klS="q]= %fo4PXZjF[˼xx PK!:o9%META-INF/RSA-4096.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:f8:META-INF/RSA-4096.RSAmy0ۉs|Qughs{|#qhiku%EjWHu:*h5lTڠUv;<)"¢:4+ښTƤRDaP(JӬpbL RH}daPLL&W[H! 8Ae9 >zXkrX h6Sw&Ȥai=y'/̶->ɴ< `BL i%.Bd~Ffb3l=Jm#TK]3&_:Mκ:]2Z/wk IEm 6 @?\|aY>/⍤̛Cpj j(I CfU[롙iVE1؊G|5fuL2m*3)ٌfDZ6$qSR^ ~35Hiqwb˥=Gu;ؑ97mnXg6mҶ<8s?Pb<21텐js"˗]`Sk} Rb*ވ%Zw }[r;oNZklJyc[OWkG(+88K_ij+F}l0yq~+OTJWgɣꎽ8kFj+엾w1sXK!t C!;Tڋ$ H ~PA7Fq󌩻7mȃ Ŀ PBwhgBxBl4SE̙$:ӂ`*t KS"N{EfkuRoSzO3~vxInQ$d.&҇8'/F3:*r)~'j" ~ctuǧ)c{̎d~lX$wl5n| #]Ge'kpc`3IV=Ոs% а$!Sjt]3::Dk̀u [eZsJݠaX~Rt*xӤ7ip"rsvބẓ|AO11[.$8ߪ'cp0azpoaTU#>>畁-S]:#V˵$k!<-n+#&-L꾭YT%kFpdfdQ-BuÇAuVv/c/q:XIz,q)ߥo? r?gLow.$ ?Ia{ѮÅ^O)hLkJq& j~;1 1R(z먣3Ճi_݁R L=bwx9y^ 6MWKPSW̔塺/dƯ&$7ldJq 'GgǷ_*T병ב&!lp>Y w4DSт$]c>xHW-VQNK ]V.-YԝBXAۚE(S,/0 0w{9FOd,;V7>+.n.tYFIˊ"WU?3Ƕ;ydžQS5rj?wkq5u0cJum: $#`ïȮ/"*' ۼv&זpZ Yf:gh]~PK!:o9%META-INF/RSA-3072.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:WpMETA-INF/RSA-3072.RSAmTy4 ƌE md7 OX$YA%F xkǒ%F 6 %zCe4's::y{~DA"[tmR ^H[(b8АrO @p@ I ):DW! ܷp ~W ɓmHJ9kL@*R8cLqR<g51|s(d648E3sƷ/%iZn#)g'D;z/tdG{,X5;t)|M:~*+nlپvSΠ姇6܌#9"mYc/x9"R@;u!ō e'C6VZNG^鿟UJ|%碖U$"0̚νUÜ, ?/ͥz/I>S!BS/ȁ A'F؆~2,$/Ί '*9 *▖w/sA0"T|^+?|fXe錖 Ӻ7џ|7 <Ȭɻ(0z>!Eqp%G#ftrٺ.QDHQ}lÉY~)k  $db=K. avZ?yL.?;qש檝wf;w+JsDKb|kvGc3}&#:r<H[œ8g٘+.ՎIK9뒍/smVRg֧>U: }s^⸗E ]ZBE9KfmE߉}dsУ1u<`#vm/˶g;JэCm'Y9,׹e TIͥ8iCyLq[Ӄ95~T%-WH=+sgÎ艊Hyzy|O8!&+dN޳D)C@@o-MjIEr]UjLRƾq%Rb]IPr~M@YX4+:}Ϭme22(r/HDqƊ2,"PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy;META-INF/EC-P256.ECPK!:o9%META-INF/EC-P256_.SFPK!:zU<!META-INF/EC-P256_.ECPK!:o9%#META-INF/EC-P384.SFPK!:eo5d$META-INF/EC-P384.ECPK!:o9%&META-INF/EC-P521.SFPK!:L(META-INF/EC-P521.ECPK!:o9%*META-INF/RSA-2048.SFPK!:< $.,META-INF/RSA-2048.RSAPK!:o9%0META-INF/RSA-4096.SFPK!:f8:1META-INF/RSA-4096.RSAPK!:o9%99META-INF/RSA-3072.SFPK!:Wp:META-INF/RSA-3072.RSAPK!:DmQl@META-INF/MANIFEST.MFPK$A./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-11-signers.apk0100644 0000000 0000000 00000000034 14763776540 026111 xustar000000000 0000000 28 mtime=1741684064.7670000 src/test/resources/com/android/apksig/v1-only-11-signers.apk0100644 0000000 0000000 00000053431 14763776540 022763 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:o9%META-INF/DSA-1024.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:DhMETA-INF/DSA-1024.DSA3hbNajhδ9Ԡ9ѐ߀3̓1qAV&@\_,/Fl@,X  ٘CYy8Ru L y M yM , ͡\욚6hF Ga2?z?IH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBiRb`6PʐS1ZGpXX@؄(f1юjYP*&oUoxn.1 ){PK!:o9%META-INF/DSA-2048.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:oHiJ-META-INF/DSA-2048.DSA3hbdjhδUʠUѐ߀3̓1qAKAK43-`fbdbdXt@=;N6vs,†l̡,<)ʼnF&r⼆fƆQ&P.vMMML0&FFܕ>lӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc$2PZ" 8h%tr5K4=/;븠✆Oڃ+578Gׯ~(K5Z]{&]VPK!:o9%META-INF/DSA-3072.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:dtMETA-INF/DSA-3072.DSAm{8ۇsO]\֥qI"U`8L]$(SET#-]ԵjZ.D5lyy?|/D( -0e"8  &ZgX( ?A0 ɝbr0 #@!; @=jI&RW'HL(2ٍB4rnd1aR a@ONۥ&razOT/q 2謲j7mP3tk&ډL@"I~9D&z7~t@N CgQ=[iurpO#btɓ|tl|a4 ÷0.1c۽i~C~ozmF~t+=;êvճ %ѿv>M||E1DEnDI;>KPhEA)e16H}Hv}1̓NR/;nw7bۑ|<<|NK/d|aruvk=uY m6bRm+SUc`EͺwҒ14SnH9u_B_7}5i]H y^D(uzi髡9`ŇT#|68Jt-W5a%%Df0Z`^J(d6H &; \kho*㆗`g`kzWE|ho m|'/;XyNنS~Lgx WYl+IrI] |]xvl6qSS-yw4/OoJm9~ژGm&h2 sHYtn8SL .0מ{zKcb'fʔaD#azh|O<:\Q)aNj !v Of`P/:Wθ?-nZLJbЎxK[:߹xyo_!@Z$FZQ=+W/ڥ;UO]a~yim\=a* y!ޭeS˦+= d I`r}%PkG;:E Ŧ^i:Vy@#Mj2f tϘSnR5dKHY\[W͘P]&cɸ!P#= kS^ 谽9;@#.oט 9wCzR5PF~ɧANk aDxsvnzuѶ4`p Џ@K\pMQk}RuEGG0kdlu5F*8&y;0*P; B~S}|͑sх3xk1qש9M(+@Zc/]z 1+;XtkmylA1 :V7&TKV/2 K;o2bwĂ1wĆ L )637kb=#TuXx.(PK!:o9%META-INF/EC-P256_.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:2 ;META-INF/EC-P256_.EC3hb2gjhδIàIѐ߀3̓1qAcAc. /`fbdbd+{i&U.6s,LB|l̡,<ɺFfr⼆Ɔ&QF k b(d odi 6j3#;)ټyϻ6iKfyg_~zaZsQN]hY.J-ccxSKW+Oq:.@$"a rE4cghBѬgN032GfW&?դȺI˒eW ҙu{32)2\J-+/s*=Y?a?)?3l6hƍ _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" 4ƫh;+KA*wLw%|97j~;ft/d;큹:]˙g2޸7%VT`1UTKRT<wLXTqn~ PK!:o9%META-INF/EC-P521.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:xzMETA-INF/EC-P521.EC3hbfbjhδA{&FFC~^6΄6Tf&FVnBM C03121q2 /֟]dTxΖ@P9=5Y@N(JŪq;X&F6VFe6ʍST[k΍/Do=,h~]k[ OIf.SIx˿CXE'.k٧G=vnhrX87Gꕾ{B"J<**&ڱϴIKlL͘qE)7]xnƦj%~<1Q.֓klS="q]= %fo4PXZjF[˼xx PK!:o9%META-INF/RSA-4096.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:f8:META-INF/RSA-4096.RSAmy0ۉs|Qughs{|#qhiku%EjWHu:*h5lTڠUv;<)"¢:4+ښTƤRDaP(JӬpbL RH}daPLL&W[H! 8Ae9 >zXkrX h6Sw&Ȥai=y'/̶->ɴ< `BL i%.Bd~Ffb3l=Jm#TK]3&_:Mκ:]2Z/wk IEm 6 @?\|aY>/⍤̛Cpj j(I CfU[롙iVE1؊G|5fuL2m*3)ٌfDZ6$qSR^ ~35Hiqwb˥=Gu;ؑ97mnXg6mҶ<8s?Pb<21텐js"˗]`Sk} Rb*ވ%Zw }[r;oNZklJyc[OWkG(+88K_ij+F}l0yq~+OTJWgɣꎽ8kFj+엾w1sXK!t C!;Tڋ$ H ~PA7Fq󌩻7mȃ Ŀ PBwhgBxBl4SE̙$:ӂ`*t KS"N{EfkuRoSzO3~vxInQ$d.&҇8'/F3:*r)~'j" ~ctuǧ)c{̎d~lX$wl5n| #]Ge'kpc`3IV=Ոs% а$!Sjt]3::Dk̀u [eZsJݠaX~Rt*xӤ7ip"rsvބẓ|AO11[.$8ߪ'cp0azpoaTU#>>畁-S]:#V˵$k!<-n+#&-L꾭YT%kFpdfdQ-BuÇAuVv/c/q:XIz,q)ߥo? r?gLow.$ ?Ia{ѮÅ^O)hLkJq& j~;1 1R(z먣3Ճi_݁R L=bwx9y^ 6MWKPSW̔塺/dƯ&$7ldJq 'GgǷ_*T병ב&!lp>Y w4DSт$]c>xHW-VQNK ]V.-YԝBXAۚE(S,/0 0w{9FOd,;V7>+.n.tYFIˊ"WU?3Ƕ;ydžQS5rj?wkq5u0cJum: $#`ïȮ/"*' ۼv&זpZ Yf:gh]~PK!:o9%META-INF/RSA-3072.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:WpMETA-INF/RSA-3072.RSAmTy4 ƌE md7 OX$YA%F xkǒ%F 6 %zCe4's::y{~DA"[tmR ^H[(b8АrO @p@ I ):DW! ܷp ~W ɓmHJ9kL@*R8cLqR<g51|s(d648E3sƷ/%iZn#)g'D;z/tdG{,X5;t)|M:~*+nlپvSΠ姇6܌#9"mYc/x9"R@;u!ō e'C6VZNG^鿟UJ|%碖U$"0̚νUÜ, ?/ͥz/I>S!BS/ȁ A'F؆~2,$/Ί '*9 *▖w/sA0"T|^+?|fXe錖 Ӻ7џ|7 <Ȭɻ(0z>!Eqp%G#ftrٺ.QDHQ}lÉY~)k  $db=K. avZ?yL.?;qש檝wf;w+JsDKb|kvGc3}&#:r<H[œ8g٘+.ՎIK9뒍/smVRg֧>U: }s^⸗E ]ZBE9KfmE߉}dsУ1u<`#vm/˶g;JэCm'Y9,׹e TIͥ8iCyLq[Ӄ95~T%-WH=+sgÎ艊Hyzy|O8!&+dN޳D)C@@o-MjIEr]UjLRƾq%Rb]IPr~M@YX4+:}Ϭme22(r/HDqƊ2,"PK!:o9%META-INF/RSA-8192.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:i]@ META-INF/RSA-8192.RSAmy4l1$X bYBeIvYBv*2ޱ"{elIٗ(f,2c'!Y{wη~}~B 1`8ø q !!`8CmFĀd㘏8=$4g@@e2g0"v%r4B'{x  xtp=Gqyn `䋬8lX^gd7LyF&ACn,@]iCGvz $UK- 䧲Uh( n~: %1r' tZ,Gsvmʘ=gB"Yy%.rNb|"o2nNtUq*>٪ui6]ubv=8>^ q*>zMtRaÂw&EsXXx2r6'euE(}c.!139CF̄GN5O=YZ=*Z.} `̑H?q8{,D俯T#E@\Q 1!+4ƿƓn,"#\l 1~KnH$OT,L᫔ %0|Q>o~[Rt]ȸ+S4rW\jl+XK{"Uh"ŖB.v8+Iĸ]o8Rnyw*xEp:W2gKCp#5!HA F=WB~Gu}wS%2_i9*1$*r\ lb'=Jh0x 0o6~wΣ3lxG0ʙDX:0,b{YVN^"E˹ (l y *Hn*hJE:mB}Xuvգ#X0V38UyV8}=wT&ƷXZ=>\9?V;o`tRq5(fb3fM_y 4\|yq.n/갻-=᜚bܓ@-~' hZH'fTǻ@pB`ʃ+@Wu˿>-kLȐ8' G^Ü({#Cu N-8%5<֪z7qtP^?T:$5 M0(iM}C*NQyVkvRk7[`1R3J.9]a7Q8е?aǔh+gDmR0 AB[/Mj4#J3kWM7z:fjg_S=rHf$ye}]y٫ۊ0 ,=+.ƑFI5$ o}hsemZnMV".Ō\RٯkUoJM# >V(O%}yͯvo!R~3ҡc"OAR61iSo JO ڧ+;LGGO#CN!ָ: o;T޺ĄLQ !yq36r$RW.fy~ $"Ev 2b4W\%O]5uܰ`" 'hlWftXJ*~®wݣ bLzr۶uX|0y˄dnO|_W}qqPy…kw._?'_@I߹@+vnѦUDkt2(]pQ&¥LrMC> 9ޏ"{~qݩWvУg;7Fxe3 V*DFq:;u]MZ7٢S …oo(Dy3x9~~6ƲW4QNtW^qhQ/3I^lonLv[!O HA=7QI^V(Ojoܽy0+DmeǨyQbťGn 2ςųVHd,\yq0RQ+#YunOM%J:xx]vPW%_PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy9META-INF/RSA-3072.SFPK!:Wp:META-INF/RSA-3072.RSAPK!:o9%q@META-INF/RSA-8192.SFPK!:i]@ AMETA-INF/RSA-8192.RSAPK!:DmQ+OMETA-INF/MANIFEST.MFPKZP./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-empty.apk0100644 0000000 0000000 00000000034 14763776540 025356 xustar000000000 0000000 28 mtime=1741684064.7680000 src/test/resources/com/android/apksig/v1-only-empty.apk0100644 0000000 0000000 00000003060 14763776540 022221 0ustar000000000 0000000 PK!:y|META-INF/RSA-2048.SF LK,)-J K-*ϳR03r.JM,IMu (h8ghr{8d&eV aEA鎹nQaQnUi.I~&nU!e\\PK!:KȨ(META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:o9%META-INF/RSA-2048.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:zL'META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICdZs3Ojݛh~JK^_,<8q_>=ζsw]/8HGC|¹9R⶧WHpPQ87ю}MZbc6oƌ+~?NyreSv66P+xA~׏r4]c/\(0S'xէ.25^*>V6J-ZųOPK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy fmH))XP@g`Q:VO˭AriPjns Yq[`'E;1"RW\a)~>jTR &ڞ} m.' e]c;J ċkB7D=97`:_a:eUW|?PK4E$YGPK<META-INF/ALIAS1.SF}]@M\1 I/AEia0|׶ikvOsNL ɰ@w;PTaʗXR%$\S}vh3B !%HFR`*nhk>߇^N>Ug"c5Vs9!v,0sTV Urfe 7)+IB'~563[D d@Ï~~TN'!ƾXx{r9Zoyz6.d^8doZE|S|CV4E)F;ݼchG\R +o` &R2t_ 2G'@{Aw"~PKR2VPK<META-INF/ALIAS1.RSA3hb^ƩiAt&L|ZcY.*hq?3##Zd14=ȉrcNYݷl&~r*nŏf׸Γ8r-FuF:/Hh0V:l68zSݧ:;qRcͻsC 3su֥kܼ3ﮦZ_w:% eI J_Q#EZ1Dt{Y sj#4uuʶ%Yx(fd5aySi^{'BK#hGOm,p[\~~{4,tPyr=J0 _1I\[jPKڇHhXPKb<res/layout/main.xmlKPƿgm"EZ\EGlŘ4Z9898ZG1׻뽋PkQGK#mTp`2 ;XRJ+SJ mM6y u^sYf:R;׵#sn-gf]}_QlPLu޻/եEL}sܿtc氻y9]YP>1Fx>8 oca'z Z-ڦL֯)P^5nsw5kKU{PKً?PKb<AndroidManifest.xmlSAOAfZ(#5BN푙!)UbW~L瓺l}kT7%IY\HVٵX~6{,-';OGҟ:KV*㊾>Nd{;ŨJyrWjZ-03,r<ڡLc=Vf-ߣUHOCmc;@#]1f%0IBO늿3zXOe^S}1jZ?šC_lvۜr.؜3ӧ|zV\;jVU\QWlz!hΖݏ<GV[99t)=PKs PK co(_ieK|7I9d2F2'a,I[/ލFkf(gFU88 ~[1 Kņ,Z51'GL!ۓ ^}cNO*!#[Fv rmR پ Մ#|\Î 1VU(:1ΰR#|pu_=FxIHǬmHp% vXc:G=|,9/T%Vqw2uHphNOV=n,z&wEA1QV*A%kaOY>2heѡP*$ +PaAr'/di `$* E 0°B(%h(Y2* KX#Yذی CF#b8y2qQfm52U]͹hHӘXJoq ,?ySy^w nѽ7~Pȕ|֝[{bo~ć?C ֠LH[z _.cd a})ņ)8ΐLCJ^J/g_E)~+]̥,|Lb l?2M\0kt\k*aϹf4&&f8O \^B]fOߛ21显&IxL` UKu?$F61ȆXɁu[C>~!$XEЕwͶߐx\mұ6^nE kHB]+B&N›L@q7,}Rn%sE %sffV̽Gx,jƕQ8aG4vD Z0A`V+kUh9&CkP0PZkuh 5uhmh\kѵ+J=ӂa0!~n:ʁ N[O#A9v8ͶCdDy3ȡ(hx:=7I)*fsփĄY$1gl)Agbժɰf tT쳳Ss( ӢbL$%}]ӓMKjD0AH=dqe)Gi8.g-w_y 7b 2.Lp./i^0K290nC_[ bx>!=H#|6v>A{մגiJП~[4WKQ*A'롔)#5g1qw֘SWǡ#Nn9s,Py$auy-(SPacŽ]{7;婆*OEE2b5}œI"Pʦz]9\֙Lԋ91؛{>)$1tf;au=E*fq$J'{m@[z--U_LFfj;%Fړq0P@ɦXzz;ƚq 4AM`{zBS}'k_g$NAL"}ݸ25]`r]Oqam& O(qfV?;JRl%.(1=tTd%Vapf qte9(]WZ,@6іř T}L'w3-1d4x˕ơ&J* %Gyq_\ccgoqer/&\| >p4 UQ@^}c֊h{ܾ`G_6\m`;ul@B45lXHl'UT BH HQ:3u!W"P ~`"Xkd4t,) ԝ#P\f<Ň17S\p!VG!MFUjކϑuf2'#0h[eƫb-$Oc# @i2]pԀuܥFL{|}IENDB`PK c<res/drawable-ldpi/icon.pngPNG  IHDR$$IDATX]l缯_N]'Mh_dRcLc$&@j+ФiҘ& &.m_TvBl4Vjc@a#m$TNj;_~"NdTHl?9C^]p PwC?~[}M.= b෇=0Pk o& ,SOst)IR?4~vGJ@%`iMl {)rcP6}8@΍_],gJKh$vu-l(RdSf߲9|Hk6@ZDdIOcXkƐ\4CJ϶~.V2Q*Ilk 3et`B5355E"nvh页Rx*KS:p%0\0 nՀ=ZS{*ƹQ%Togw<9CT"L.j9(\26IDw7FgdZKNj 2Z '&tOd 1[!C"$[s4S#l߅?2Z3Z9бGWpşרvwS G9S8RRaŐ;ٻ>QI~L̾SY&.b8ːeCǞ(s} bW%䖁G(GS}'<Q.K%םVdYʲfI|N@|t&9v &O%%o4hi 4\}7qB C׾+q/u=HY>ȿH|ekPh k\[ƅ0g]hߐl$ܣMIWj{zq|YЬ[/P>9` (NtbmkmcYU*{zvg ,OkK%G2 #>Zik@R`@HE>OkRq ?קZc `Xz.o," B VJe(i;߹}_`YÍ4DVbOXK6cc R.k `ٺ!D/e$@Ww|z^=sF=|fDC99˛%qi6Mf)/7"o"XO@_h/*in#"XcioSOVES.mYц5H;\gF`iw sh[t%N=ޅEMl]u<[ʎljcqt ~W?GHG~ŃAPOv!EgQU&V@;t>yuƘQq8t{\s՗``2꙳Bh#Ϯ8=St pkh#UofU->CCCh<@Ƙl^=8?\@(g=d.B (zEBƘ!Okt3 HW?:}7 @@HV)IAOqglZ~~G&5ho#ȴn@ptmIaD |ΒaH}v4aRc\ o|+DLj.JMU#X&#kl۰C4w؉]9:WLC1RdCvZb0b1\Rw#)'/LMC}rE9ˬJBC:CHG(λVx&˺%cC-:=ANui?XU+Ȋ.V:ıAcpTM_a;wq=%>ᝁljujU~IWJpx8NZ#6V34NעM+$m~=n`,Ԇ V0QWk~R./|t%tC}`/ۄ(kx o VL8@Kt#7'vwUΪ6Je+;{ǜVhE[ٝx jtlCpOjb5 _bS88Tt帒A|ok}!Φ^cY6̟( &tѠFs7#N>SaRoqBTc\a0>JJljseXd,\ٙLuVQ"bX߼-(N .W`LxY Iq7o`umP:'[vBRU: ?ppggg}]Q;bElsh 46a Stƀ+ ̪cyI  cJS,(M.j 0OiN9(6Szcdd0NV,P H$X=IENDB`PKb< classes.dexU]hU>󳳛M7Ij'҉Ծ[A r3{INMBA BI؂ ZPT|o}ݹwۡ!||3{9~:{4}2sᝣsƫvzsN)Kcd?<)/XZ9  WrDm݆>`D3@~x M p |w{}B@x@:d剭Rp8 35;,2f^Ya=D:&eҦgLl2' |U1uF>b(y`=h 7}|`Ǟγh~2r=_\ȤZECz{_{׻v󜧿ķ&gKCxCFaS[&o.|h1&Q'`kk롌zl#}gE'M: 7H ]+;/3)MׯelIܾ1LLe7MϳI>.Zdaq/zD2%"8'8Ir>fiڤ',>yd8\ZX1Kx{N*%*$VТa95mS:x^`ca:jT븪.guV}?PK.:PK<4E$YGMETA-INF/MANIFEST.MFPK:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7SĦ:<Lj|X֬vg^[*kO [!DML?}9wEw# x7157D;]Sz713Ź.>Q|FkqMJ<'+x뎤I-XTl։S|zLYpGs _%942gN8eU?>=*LHdx+#f{EW/B~?dgG]EJ}g.d5";4 U|qliOIXb,͌m:(iiԌS~nkz"{ƟNg?g?1فz~nw՟3ë^ [|OBՕ~K_|}sBMOx9<_RfM ֬7'aBe__#RcϫW2Hg_8N!s[ O20mټ3Kc?]Q%WsG71pn|V0q"%u]yԌZ_9AksM7}UTusumiZjLw@kjEH'jeibdc3ukOMu^0;Eiך^i'`I5̶:?=RkT68rR1ĕR22SjxS9g^ eRK*IC: IABgoK.<PKxPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPKrHx META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-targetSandboxVersion-2.apk0100644 0000000 0000000 00000000034 14763776540 030532 xustar000000000 0000000 28 mtime=1741684064.7710000 src/test/resources/com/android/apksig/v1-only-targetSandboxVersion-2.apk0100644 0000000 0000000 00000011073 14763776540 025400 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=㵝q'8ֆ(PhB$CAD*  MD(OB|AEuvf{cf ķ"Pb88눸F<"^O;qQDB| >|([CH ~8B} zs=4LTrcG]Z= `>itd))K]]<&L>-;<Si;'BGPK PK!:XhMETA-INF/RSA-2048.SFeOo0; ߁"sbHv6!CT[iJ *~f3o/{/f 5Ɗ xh*SAQCB{TDpFU%'<2 QŒӱ56l`:6ea,&/*!*LRo8چG= 4 ]-ܚ_xpH%: 3[/+g(sqͥw.IO'HTn]̓}p?ot{0eu;]vW,L } ^xPK!:Z\QMETA-INF/MANIFEST.MFeMO0; 66x r@l3^L-A ma_/Mv} :!)gsõ] ERk'eJ&Ā\\ 5|k,l_R^xO͍\'`ݵ- n1aDzLD5&%H;%kf' Q#*oMp|`DVEBke׮Z眆njZch ghq<_PK !:!O resources.arsc5PK!:z3AndroidManifest.xmlPK!:  classes.dexPK!:Xh META-INF/RSA-2048.SFPK!:~* META-INF/RSA-2048.RSAPK!:Z\QkMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-two-signers.apk0100644 0000000 0000000 00000000034 14763776540 026501 xustar000000000 0000000 28 mtime=1741684064.7710000 src/test/resources/com/android/apksig/v1-only-two-signers.apk0100644 0000000 0000000 00000013043 14763776540 023346 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW-|{^r*e^Z)n)k]NA#6nwvkMSd^<|W*-O@Zc/]z 1+;XtkmylAh1wW]f³Nԍg~f{]7,3)H [Nv GҋQq_W|PKӄ:PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT0.SFPK!:/&G META-INF/CERT0.RSAPK!:.YMETA-INF/CERT1.SFPK!:ӄ: META-INF/CERT1.ECPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-cr-in-entry-name.apk0100644 0000000 0000000 00000000034 14763776540 030256 xustar000000000 0000000 28 mtime=1741684064.7710000 src/test/resources/com/android/apksig/v1-only-with-cr-in-entry-name.apk0100644 0000000 0000000 00000011325 14763776540 025124 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK&sJ test.txt HPK51PK&sJ|"AMETA-INF/CERT.SFmKo@ eXEIQ4[t7tP@fn;tCQ1DqZRk++&9fŏR_L0*&.ir=t,ˀ׈yřDی>~l3RKjŒtC4 6@Ȧ - 㜶Qs\i< rNx 8}HFuaXѴ ҝ˘{v-#q2g$Y* JQ]J4э*>A !BҾ:WN=YE&7PK&sJ ag(QMETA-INF/CERT.RSA3hbejhδΠՊѐ߀3̓1qAsAsASf&F&&A -P5@-|l̡,lyyy0 v . n/A@fa9q^C3Kc#C(q^#C3(NibTB`1713Ź(bujkva}GJZGBNsJzEޙLPlHМ 6ԱLvPO:iÚK"{Oi;$Ԧͫd'޲d*pouyEry[D7_H7Ȝ ''e*?.ȦЯM,z8xJEkg>͙R4a 6}}W2wm^= i'|;3j31320.V47,Vn&oƚpYyF{ ;V+,~M({<ڬϭauCPFO6ln}ЋdX5.y9ُY _3niL׏~sy["^s˧H'{զaK7^=k v9y5-ܖ}kw~ {^0D~yS!5nN gwo~Җv;!g?»jwƭz_~_z?a6u{ywꅗK3Xu ðq0!PB:&jDF.PS9K# =*< t+-$tKEW-W/*~p](EyZxsmse2+ۊLEdŜo6utދv+META-INF/MANIFEST.MFen@= DAѤ .JU*l tؙO_mퟜ/9!3H"x)iX loT!#!Šb J'BEA"pK% de8jc&.cӜM]_M`qXZhllc)ɠsEc{3+t4|djVyU쎤WPV>ߟ'Ί&QhQcܩv\|/: <'q2\X56~PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK&sJ51 test.txt PK&sJ|"A META-INF/CERT.SFPK&sJ ag(Q[ META-INF/CERT.RSAPK&sJs>+META-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha1-1.2.840.10040.4.1-1024.a0100644 0000000 0000000 00000000034 14763776540 030377 xustar000000000 0000000 28 mtime=1741684064.7710000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha1-1.2.840.10040.4.1-1024.apk0100644 0000000 0000000 00000010340 14763776540 025574 0ustar000000000 0000000 PK!:>%BMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPKlP.IMETA-INF/CERT.SFmn@F&, ԖPA-iFr̠׶iښ9Y8âd qgReZ8*=f8A3})h,Ϥ0@gx-0wWOSyP+q6}i "YGȣhD3, @vΊaqo-` 3#,JOviHg&5SrPpaEext(r 1﹊/`4YEko)s~nuIO˦[\?wnߛ~PK}PKlP.IMETA-INF/CERT.DSA3hbbjhδ۠ٝѐۀUIqAV&@\_,/F@=@,X  ٘CYy8Ru L y M yM , ͡\욚6hF Ga2?z?IH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBi|\`6PPS1Rʇ:V='y-X?R% L"fNmfbqGPK/s^PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:>%BMETA-INF/MANIFEST.MFPKlP.I}META-INF/CERT.SFPKlP.I/s^cMETA-INF/CERT.DSAPK !:!Oresources.arscPK!:^avT AndroidManifest.xmlPK!:Շ  classes.dexPKyQ./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha1-1.2.840.10040.4.1-2048.a0100644 0000000 0000000 00000000034 14763776540 030406 xustar000000000 0000000 28 mtime=1741684064.7720000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha1-1.2.840.10040.4.1-2048.apk0100644 0000000 0000000 00000011275 14763776540 025613 0ustar000000000 0000000 PK!:>%BMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPKP.IMETA-INF/CERT.SFmn@F&, ԖPA-iFr̠׶iښ9Y8âd qgReZ8*=f8A3})h,Ϥ0@gx-0wWOSyP+q6}i "YGȣhD3, @vΊaqo-` 3#,JOviHg&5SrPpaEext(r 1﹊/`4YEko)s~nuIO˦[\?wnߛ~PK}PKP.IMETA-INF/CERT.DSA3hbcjhδߠѐۀUIqAKAK43-`fbdbdXt@=;vs,†l̡,<)ʼnF&r⼆fƆQ&P.vMMML0&FFܕ>lӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc+2PZ"8H`e`q3paRp+Ms'D'9_z n:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:>%BMETA-INF/MANIFEST.MFPKP.I}META-INF/CERT.SFPKP.I@"cMETA-INF/CERT.DSAPK !:!Oresources.arscPK!:^av1 AndroidManifest.xmlPK!:Շ  classes.dexPKy../PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha1-1.2.840.10040.4.1-3072.a0100644 0000000 0000000 00000000034 14763776540 030404 xustar000000000 0000000 28 mtime=1741684064.7720000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha1-1.2.840.10040.4.1-3072.apk0100644 0000000 0000000 00000012110 14763776540 025576 0ustar000000000 0000000 PK!:>%BMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK(Q.IMETA-INF/CERT.SFmn@F&, ԖPA-iFr̠׶iښ9Y8âd qgReZ8*=f8A3})h,Ϥ0@gx-0wWOSyP+q6}i "YGȣhD3, @vΊaqo-` 3#,JOviHg&5SrPpaEext(r 1﹊/`4YEko)s~nuIO˦[\?wnߛ~PK}PK(Q.IMETA-INF/CERT.DSAm{4y=f#GgA!`0(t"9H9X"؄ABbH L$$@#T%D'a4XXB4P3F\'mt~~E\obK[!"kO1eCd+qTtm"/Y@*%_&48Z"*~C'Qc'VQZu[] яfEjv- .sˁ%1qfh0ZR Esc Ɵi~lc<Öm֬m͹\Eig~@nwn1$nQDoJqּpδ@_넕W|q$y=&N;VZGxWS%Fh 'Șu" /L]v";3:ʗ Me.!]KW N^.dEQ}ەiy-Ғ:y.Rtd1[:li\0A8R?/TK*w= |Qb}/b-f5Qֱ{gѸ롶-Vg"uyF!{<զTu訞L;Y*U ԉ(ga80!^֮#oN%@ϽJ1+=z^m.Ձ"L ^ I{xTǼ* w zœ/+q"i('\u\;νȐ+uTsFЋj\2";.F0̹מck6]vǡn C  &}ƹI ϕJSEsv.!&inP[x+įqxCSVȪ;Kiv r]9}A Lc7͑<&5b$K&6,z֓T.hFdu ׃|ة4G9}rU껡 %2?lhJхAg-r$?Џa,t$wb󺕘eOGEA0w`7o8|F^I@JTi)ֿd ]f`78Z(e#[PKTjPK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:>%BMETA-INF/MANIFEST.MFPK(Q.I}META-INF/CERT.SFPK(Q.ITjcMETA-INF/CERT.DSAPK !:!O resources.arscPK!:^av AndroidManifest.xmlPK!:Շ  sclasses.dexPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha1-1.2.840.10040.4.3-1024.a0100644 0000000 0000000 00000000034 14763776540 030401 xustar000000000 0000000 28 mtime=1741684064.7720000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha1-1.2.840.10040.4.3-1024.apk0100644 0000000 0000000 00000010271 14763776540 025601 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7IH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBi\l`6PPS1<#cYgVxDcҜu&ܪ?uv`JVl붟EpzPKF\PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:F\ META-INF/CERT.DSAPKy*./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha1-1.2.840.10040.4.3-2048.a0100644 0000000 0000000 00000000034 14763776540 030410 xustar000000000 0000000 28 mtime=1741684064.7730000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha1-1.2.840.10040.4.3-2048.apk0100644 0000000 0000000 00000011225 14763776540 025610 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7lӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc 2PZ"8R wK 9lg ӷiN1.s6ܟtěZB=Vw8}Y'uqӋۯ_npZ#fprAPKl!PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:l! META-INF/CERT.DSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha1-1.2.840.10040.4.3-3072.a0100644 0000000 0000000 00000000034 14763776540 030406 xustar000000000 0000000 28 mtime=1741684064.7730000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha1-1.2.840.10040.4.3-3072.apk0100644 0000000 0000000 00000012036 14763776540 025607 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg73 픙uD=+h ր5[곤Ȳs, ){Hx3=F&HQz7y,/նu٫Zr1m:zH ni7@?Lp9C1f&*ET 8h"{FL6H%HgOƱLރ=Ü5]il5۷BRV@2٠?h0 [e92DNzPѫNX/ ^2lE=|>Ц6 {&|dݶ|:*j{He*7vx}mܸ\;e W.3{SydG ʣh__lqmB. 1CqL~;*pؔ[6%lƯa%RnFv@~˃+qN6Y'mYY1@M>WJܾ@M;"L9tƘToHth0g/U"7|_Q~zJ(7ZFG* eV}QWG_=x10]ĜYzvLH݌Z08q\_uρGj eVd sv "K²W?p}r?Ep|A2Y7jĿ^c j jOňF-j-_B"1HwikmjIOPK&%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:&IH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBibB`6PʐS1FꇥPK$ffPK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKcQ.IP/%e/META-INF/CERT.SFPKcQ.I$ffMETA-INF/CERT.DSAPK !:!Oresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  classes.dexPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha224-1.2.840.10040.4.1-20480100644 0000000 0000000 00000000034 14763776540 030336 xustar000000000 0000000 28 mtime=1741684064.7730000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha224-1.2.840.10040.4.1-2048.apk0100644 0000000 0000000 00000011457 14763776540 025764 0ustar000000000 0000000 PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWlӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc$2PZ" 8%`e`q9VgA;7ܭ>lKצgl׹ͤŴ=ȮUۦ_f}3C65 {PKqP^+PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKQ.IP/%e/META-INF/CERT.SFPKQ.IqP^+META-INF/CERT.DSAPK !:!Oresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  Zclasses.dexPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha224-1.2.840.10040.4.1-30720100644 0000000 0000000 00000000034 14763776540 030334 xustar000000000 0000000 28 mtime=1741684064.7730000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha224-1.2.840.10040.4.1-3072.apk0100644 0000000 0000000 00000012273 14763776540 025757 0ustar000000000 0000000 PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW[zl}!Es- GLog=WnJ}my[q$`Cv^U ΦV:Ќj2&, |{)\ R\>t+b@lZLt!U%Oth8FCyj^>LX-$;B>n[IѲWmbɊqQeMad6/UTn"ɤP"RʛyLn]v}.-6ZX{@Q!tyEdrGQ9"5<>a,׫aj gW(񼸠?<̵%? N{ecౝ6ܴ0wf5o7mS.H=['t{n MGbGHH=o7՚7-Aԕ{b|/i1|0:tw 77YkqJ, HN͜Xu" '3M]v!tJ5N,_Էt YrbuD?ʼ}f7Emwi៶NK?v+%RtdQ:,i\0A8R?/TK*s ^x$xRgٓ6S51NѺsfsm{Cm[θ"u&!w4ݮPu蠞J;U&UtD 0W8aB潬\F&J"4+bV2z}zӻq11(;-Tt߃#J>fUac6_sᭋF+"i)&\ul;SޛgJy% wc/AְX9ǤFLbaZ'цER/|=I`AH\p<ȁԤY1˥/h15 =$B!= b|O垕˻;S߳7=%LMA2wjO.uMuʆ0-@#@`B++^G P+P*S T*>h6E cam_+x$?>q DXr }Vɟ*⣦ @a_%zU4HF+`kX,˔@pnnףiw pӁ#r7v7/ PKy8nPK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKQ.IP/%e/META-INF/CERT.SFPKQ.Iy8nMETA-INF/CERT.DSAPK !:!O resources.arscPK!:^av/ AndroidManifest.xmlPK!:Շ  classes.dexPKy,./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.10100644 0000000 0000000 00000000034 14763776540 030327 xustar000000000 0000000 28 mtime=1741684064.7740000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-1024.apk0100644 0000000 0000000 00000010433 14763776540 026332 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWIH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBibB`6PʐS1frY@j쏪xs 5,bY$p3[V.fx"?b4PKQfPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:QfF META-INF/CERT.DSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.10100644 0000000 0000000 00000000034 14763776540 030327 xustar000000000 0000000 28 mtime=1741684064.7740000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-2048.apk0100644 0000000 0000000 00000011365 14763776540 026346 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWlӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc'$2PZ" 8%f¤_IvuX, Ô?'3U_Ƥ`6-߄ӵ\okC PKOL*PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:OL*F META-INF/CERT.DSAPKyf./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.10100644 0000000 0000000 00000000034 14763776540 030327 xustar000000000 0000000 28 mtime=1741684064.7740000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-3072.apk0100644 0000000 0000000 00000012203 14763776540 026334 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW`t/_(u WjP5H>\= h]73{9búhqTIG@ exY4K +o{+2e77~Bn[qwq5,]^p gyk Oܡ_Ry6# !YG )W LsD/)Άc݋u0zbkO&煚1r6y|;P )>>ט뺜 nJ~l+̎_Ɯ'z3J_@k3Yy1t-N%V2U3ɣS!WAt= dB0Lp0˷]qtA,=PԮ[7X L[k,ơtбOΪT[YԆc?Y $SFP'MlOωQӮƶm{7[f ;ʔzZWb+@wiPV"Z$ئ[J^D|Se|B1'*{K٧l|E9eݴ&E./?\(Iǭ}ë㉰d \VP"w.ʈ!xrR|ɜs)8BkF+Sfg 2jngmScfCw$ڨ_@mx^Ny͵I ?l5ㄝ`FW}+r]e)/wۧ{^%ӓ_qho*p-,ly9 ;0 6Z~%.ck\q~yj 0rEzX آ+V]$ ?IH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBibB`6PʐS1Fꇥ:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKQ.IP/%e/META-INF/CERT.SFPKQ.IiafMETA-INF/CERT.DSAPK !:!Oresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  ~ classes.dexPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha256-1.2.840.10040.4.1-20480100644 0000000 0000000 00000000034 14763776540 030343 xustar000000000 0000000 28 mtime=1741684064.7740000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha256-1.2.840.10040.4.1-2048.apk0100644 0000000 0000000 00000011460 14763776540 025763 0ustar000000000 0000000 PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWlӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc'$2PZ" 8%`e`q3paRp]a:I)x%0}1٫`B z,/dmVW=YyfO+k]DWPKcIz*PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKR.IP/%e/META-INF/CERT.SFPKR.IcIz*META-INF/CERT.DSAPK !:!Oresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  [classes.dexPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha256-1.2.840.10040.4.1-30720100644 0000000 0000000 00000000034 14763776540 030341 xustar000000000 0000000 28 mtime=1741684064.7740000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha256-1.2.840.10040.4.1-3072.apk0100644 0000000 0000000 00000012273 14763776540 025764 0ustar000000000 0000000 PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW)s eN(Uo 7zV0NB-|]վr숢@?lZ^+~'ň6f>f P|LNaQ`T򈱀-;Zu( (/2'dvHhË{,Hׄ/SM;+V 7\FzVc0'`]>;ˬ;$~u1ZtTq[liB0IZ˿$TO)w '_{e"q?11~zhUPe{LA[ROۊZ+eۨ 6kO#~jpC;%lDWP8]bKR%qڜDoYϗ#Y,3usϔ'?5)! cP6RZ.vY~ @_Ti-LzPd!+j\ V\Eq+Sq[ٮ,XE։wai PK#[DnPK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPK R.IP/%e/META-INF/CERT.SFPK R.I#[DnMETA-INF/CERT.DSAPK !:!O resources.arscPK!:^av/ AndroidManifest.xmlPK!:Շ  classes.dexPKy,./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.20100644 0000000 0000000 00000000034 14763776540 030335 xustar000000000 0000000 28 mtime=1741684064.7740000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.2-1024.apk0100644 0000000 0000000 00000010433 14763776540 026340 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWIH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBibB`6PʐS1frX@ڬ|M:ˏ<$Rm cdJW-|PK ofPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!: ofF META-INF/CERT.DSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.20100644 0000000 0000000 00000000034 14763776540 030335 xustar000000000 0000000 28 mtime=1741684064.7740000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.2-2048.apk0100644 0000000 0000000 00000011365 14763776540 026354 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWlӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc$2PZ" 8%vOOkSWw:koJ(r &Ԑy..}x䟎 [H>8wDPKr+PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:r+F META-INF/CERT.DSAPKyf./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.20100644 0000000 0000000 00000000034 14763776540 030335 xustar000000000 0000000 28 mtime=1741684064.7750000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.2-3072.apk0100644 0000000 0000000 00000012204 14763776540 026343 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW[L6@P:j7@! 1z UWJ P(d nH\1a =]a aA-V;nFҺ'r(tNY1TEѫ Em'*f6SUWR`~"ErLwOn-')zQEÍ̜rAbΫ$))%QSё7B(GMD d^GY}\ >J $.D"wL{ ?5\RjLK8*z Zg_)8v08{gg~vfRѹنS>e32git;Q|YQ|{]iwվ*GDMu Tso h[#ю[O6}Q8]ʠYzi>]5[@i2yywO 'j)smLjzA]z0~I0ߟ3H;:\IB9s1BԬ n|U>"W vu}{j3}Dg4wsxwMҤJ}~ZIT9ս|1.:!ǚWA[f-;.e\5i,) zHR 6Zt˿j-0J=X&]/L]S_ʐlmV4+Ô wVWMs䶶S1"QA6VZY$ek͖:d0&e[V-m+F 2r[󣆻 3va`^0A5ɉ>!CO 2W 76VCEo>PJ*ҁoxM>LWG1>Ɯ]~wLh :08UI\ {גERA/y'MФjUNt&򅈋]$06at}YqO,(b𺇜}.}ბWB`ހ@lx SLzoOH_u3Os y~bBwVG-ևfPKpPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:pF META-INF/CERT.DSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha384-2.16.840.1.101.3.4.3.30100644 0000000 0000000 00000000034 14763776540 030340 xustar000000000 0000000 28 mtime=1741684064.7750000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha384-2.16.840.1.101.3.4.3.3-1024.apk0100644 0000000 0000000 00000010630 14763776540 026342 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽIH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBibB`6PʐS1frY@ +1u*iZyc 1dωߣ6kZSPK+ϷfPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!:+Ϸf META-INF/CERT.DSAPKy ./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha384-2.16.840.1.101.3.4.3.30100644 0000000 0000000 00000000034 14763776540 030340 xustar000000000 0000000 28 mtime=1741684064.7750000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha384-2.16.840.1.101.3.4.3.3-2048.apk0100644 0000000 0000000 00000011564 14763776540 026360 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽlӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc'$2PZ" 8%f¤`St~PK{x\ 9/=*.=Fp[Wj1:ƺ7PK9*PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!:9* META-INF/CERT.DSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha384-2.16.840.1.101.3.4.3.30100644 0000000 0000000 00000000034 14763776540 030340 xustar000000000 0000000 28 mtime=1741684064.7750000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha384-2.16.840.1.101.3.4.3.3-3072.apk0100644 0000000 0000000 00000012401 14763776540 026345 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽDG og;d84{c2Y op m8'CԍELYXq:uV)Bl]}4|Sy[ckGSK.o\>C8.r6Y%~Gi$n΁q2sJ2ޗ?0JUB!R$wXgrLE9zF}Mm :Ʊe)~" }Ӵ?RPB<`9狞̷r '9Qzd% G aOz׌/ZrmD. OcgfU8,r=KA^"-Lo/%R&ve#SUd+C'K)x񵷖FL3]fDX Y⓷͓uN(;16&UH FLJ䆜O>>@MY_CVcR("# |sI|a[gޖީET: UfӅiH[=FӋO0C.3}黴Lz?Ђ(B?T'ɖ-,MTjEhH,8o(R!y! +cS취 Y}ݸ(Ǘl6}Q {-WBajZ)zD*%'<<*=A;}_7.9!;3e, 8X'PKoPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!:o META-INF/CERT.DSAPKyr./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha512-2.16.840.1.101.3.4.3.40100644 0000000 0000000 00000000034 14763776540 030332 xustar000000000 0000000 28 mtime=1741684064.7760000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha512-2.16.840.1.101.3.4.3.4-1024.apk0100644 0000000 0000000 00000011030 14763776540 026327 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]IH͛qM|M z-/6$>۝y} 7N9P$Svʉ)D]M|H|G 4v75W}SٶIaw0[+Үf2g 9{2{?̙}D칾%A"@9p[&_,|lq,0:eXXD[qϔVolg A,b 032GJ` :L"fm?+CNab#D%6",%rT\vBibB`6PʐS1frYX@ =lq-<ً20kzzۖ|w~ML? PK:fPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:l META-INF/MANIFEST.MFPK!:1j̬DY META-INF/CERT.SFPK!::fC META-INF/CERT.DSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha512-2.16.840.1.101.3.4.3.40100644 0000000 0000000 00000000034 14763776540 030332 xustar000000000 0000000 28 mtime=1741684064.7760000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha512-2.16.840.1.101.3.4.3.4-2048.apk0100644 0000000 0000000 00000011763 14763776540 026353 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]lӑ2F9f22<{7j`|Ӣ 6<ūh %#ƺO8^,YQW5qadrݷvE=w+T_;]g=&m:`au,a>/Mgn鶛e+g`ˏWSU\&tMHV'Ur+xSYr<-gkh]#vUi/^vHCnV1׳f伏A9`[ssn&E9h6[S)}>]/RtvwDpb1`%m/67~K.gVdMgx^#e쓦|rɢUxl6>B-?1vηf:WPsIaΚ[ t8l O}~s褟2k]go{9nŖ^H[zbf-SV~ ,i-_} ִvN0Ŏ˶?c\ [НѨ9/'n<5yƌNYF׷~ZxRMU2Ʌu<ǀC<=}NrYtsnN,'>Tv x n#4ѷ̣3YJ=3$U)mo//j%;…ΉK;?=1϶R Qܘ$P׺'okx]~杺ȧ'~e_*4iΎ%+l.(y#x?Y`ccdUIG__iɯ0!y2Xv< ,̌r<+0I(N]>Ô5Uuz/I>+>tiC rצ8w֊WT/s`69}8vacAc'$2PZ" 8%f¤T'ֳw,͝76wN޲TfdTҜyN+YlI.PKh*PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:l META-INF/MANIFEST.MFPK!:1j̬DY META-INF/CERT.SFPK!:h*C META-INF/CERT.DSAPKyd./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-dsa-sha512-2.16.840.1.101.3.4.3.40100644 0000000 0000000 00000000034 14763776540 030332 xustar000000000 0000000 28 mtime=1741684064.7760000 src/test/resources/com/android/apksig/v1-only-with-dsa-sha512-2.16.840.1.101.3.4.3.4-3072.apk0100644 0000000 0000000 00000012601 14763776540 026341 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]{ycL\} E@$=$Yy`uGKl; ,M`ɫҞq-t|0}*D!;k[u65CGdܺ)9lwOQ&7n40y ;z yEpV|-@'懙QLM$澧.Y&[v/CfJl3QC ^gMVI@nLjv (o\3˰n}7 j]\97]S cpwgQ$WX(MεՈHS+Oo}24)n,߻Ufn:.9U(w6yOvp#žKڌf=HHo2g9jߋgdV ư0&k,hr]2' [s.U8=P֫_',{2zGFJqG}L\&BVՑިC*IQKg_T'cGKG'%uu]d0n/S}ལ4*t-\/Y%R3%QZ5^K٩n(Ē^F %ESo;M/WiU kVBEf\O!E ޷<Q'9X|Ȓn!yyi8 ًCohvb9ITueŽ9{J7Cao1ilHQײtj0k/S7|o ~Fʸr07r#˰#K>~Kz;(R˾=-WH1_o .B[ mwidM-bq 9N&Mn_-C?$.08e%BMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPKp-IMETA-INF/CERT.SFmn@F&, ԖPA-iFr̠׶iښ9Y8âd qgReZ8*=f8A3})h,Ϥ0@gx-0wWOSyP+q6}i "YGȣhD3, @vΊaqo-` 3#,JOviHg&5SrPpaEext(r 1﹊/`4YEko)s~nuIO˦[\?wnߛ~PK}PKp-IMETA-INF/CERT.EC3hbgjhδIIѐۀUIqAcAc .`fbdbd8ſw".6s,LB|l̡,<ɺFfr⼆fƆ&fQ&P.V=l`S333;1,U:)<};/+פ][tn¥-NsOl~rr~Ps˖cc"l3@HUh3' A,b 032GfO7&E, r}".]8?9$Yqk@%?-i>@Zc/]z 1+;Xtka ylA( 6VwW&y&u[6u_+}{ʖ-GN+:9}| 3i:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:>%BMETA-INF/MANIFEST.MFPKp-I}META-INF/CERT.SFPKp-IM*3cMETA-INF/CERT.ECPK !:!Ojresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha1-1.2.840.10045.2.1-p3840100644 0000000 0000000 00000000034 14763776540 030603 xustar000000000 0000000 28 mtime=1741684064.7770000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha1-1.2.840.10045.2.1-p384.apk0100644 0000000 0000000 00000010013 14763776540 026214 0ustar000000000 0000000 PK!:>%BMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK;q-IMETA-INF/CERT.SFmn@F&, ԖPA-iFr̠׶iښ9Y8âd qgReZ8*=f8A3})h,Ϥ0@gx-0wWOSyP+q6}i "YGȣhD3, @vΊaqo-` 3#,JOviHg&5SrPpaEext(r 1﹊/`4YEko)s~nuIO˦[\?wnߛ~PK}PK;q-IMETA-INF/CERT.EC3hbfjhδƠѐۀUIqAZ&ƕ@Hn3Y\l@lY  ؘCYySu -L y M LͣyM , ͡\z 21 ĜrDrN{ &ݭGaݖŻEgɜuԞ󚲰_즅= W놈ܞ՗*~.^eg|Y8@Y>1g}8'p3 A,b 032GL4&Cc׷ΐ< oAhr6~bUoT͘;?^N뛵\ P NKE)U3Hc͑ko}ٷχS} *En%6.7h\Syl}(Q sV4&C8z|2|m>ޑecS#sRr^7fN&Z7m^./>z~񮳦o5^+S2U.PK2/PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:>%BMETA-INF/MANIFEST.MFPK;q-I}META-INF/CERT.SFPK;q-I2/cMETA-INF/CERT.ECPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  7 classes.dexPKx}./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha1-1.2.840.10045.2.1-p5210100644 0000000 0000000 00000000034 14763776540 030574 xustar000000000 0000000 28 mtime=1741684064.7770000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha1-1.2.840.10045.2.1-p521.apk0100644 0000000 0000000 00000010200 14763776540 026203 0ustar000000000 0000000 PK!:>%BMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPKIq-IMETA-INF/CERT.SFmn@F&, ԖPA-iFr̠׶iښ9Y8âd qgReZ8*=f8A3})h,Ϥ0@gx-0wWOSyP+q6}i "YGȣhD3, @vΊaqo-` 3#,JOviHg&5SrPpaEext(r 1﹊/`4YEko)s~nuIO˦[\?wnߛ~PK}PKIq-IMETA-INF/CERT.EC3hbƩiAk&Ll|LR E 41~LLL oKg)pqeaf120cceaaOM-052454306644564251604rim 6Fe6ʍST[k΍/Do=,h~]k[ OIf.:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:>%BMETA-INF/MANIFEST.MFPKIq-I}META-INF/CERT.SFPKIq-IcMETA-INF/CERT.ECPK !:!OEresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-p2560100644 0000000 0000000 00000000034 14763776540 030603 xustar000000000 0000000 28 mtime=1741684064.7770000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-p256.apk0100644 0000000 0000000 00000007601 14763776540 026225 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7@Zc/]z 1+;Xtkf ylAH 6FwW5{Xc\v/[ҵCM_;bK*v\j Y%IIA~UZ~0,lز%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:TI1 META-INF/CERT.ECPKx ./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-p3840100644 0000000 0000000 00000000034 14763776540 030605 xustar000000000 0000000 28 mtime=1741684064.7780000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-p384.apk0100644 0000000 0000000 00000007750 14763776540 026234 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg71g}8'p3 A,b 032GL4&Cc׷ΐ< oAhr6~bUoT͘;?^N뛵\ P NKE)U3Hc͑ko}ٷχS} *En%6.3h\ Syl}HQ sFtTŖ yn(p={ˌD%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:/1 META-INF/CERT.ECPKxZ./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-p5210100644 0000000 0000000 00000000034 14763776540 030576 xustar000000000 0000000 28 mtime=1741684064.7780000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-p521.apk0100644 0000000 0000000 00000010133 14763776540 026212 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7OۏQ s^܋Q|SB ֝\®&'?L۲hV^bPKa71PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:a71 META-INF/CERT.ECPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-p20100644 0000000 0000000 00000000034 14763776540 030575 xustar000000000 0000000 28 mtime=1741684064.7780000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-p256.apk0100644 0000000 0000000 00000010026 14763776540 026365 0ustar000000000 0000000 PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW@Zc/]z 1+;XtkkylA1 :V{Y6|ߵ-jg=K>_- wAO:a?PúzZSX٥7N>Ƿa܅<PKJ7So<PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKr-IP/%e/META-INF/CERT.SFPKr-IJ7So<META-INF/CERT.ECPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  B classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-p30100644 0000000 0000000 00000000034 14763776540 030576 xustar000000000 0000000 28 mtime=1741684064.7780000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-p384.apk0100644 0000000 0000000 00000010200 14763776540 026361 0ustar000000000 0000000 PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" W4ƫh;+KA ϋ]7]R>5 Wʔ^-2&Wv˛7Nvz`| g/j,إ|tWWMX|mצZF7PK/5PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPK s-IP/%e/META-INF/CERT.SFPK s-I/5META-INF/CERT.ECPK !:!OEresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-p50100644 0000000 0000000 00000000034 14763776540 030600 xustar000000000 0000000 28 mtime=1741684064.7780000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-p521.apk0100644 0000000 0000000 00000010364 14763776540 026365 0ustar000000000 0000000 PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKWs-IP/%e/META-INF/CERT.SFPKWs-I sթMETA-INF/CERT.ECPK !:!Oresources.arscPK!:^avi AndroidManifest.xmlPK!:Շ  classes.dexPKxf./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha224-1.2.840.10045.4.3.1-0100644 0000000 0000000 00000000034 14763776540 030476 xustar000000000 0000000 28 mtime=1741684064.7780000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha224-1.2.840.10045.4.3.1-p256.apk0100644 0000000 0000000 00000007736 14763776540 026546 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW@Zc/]z 1+;XtkmylAh1p#+Юe}˜r&Aܸd{ϴ~~*ʘ+ˤ[gK+N^p3ыPKN|W:PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:N|W:F META-INF/CERT.ECPKxP./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha224-1.2.840.10045.4.3.1-0100644 0000000 0000000 00000000034 14763776540 030476 xustar000000000 0000000 28 mtime=1741684064.7790000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha224-1.2.840.10045.4.3.1-p384.apk0100644 0000000 0000000 00000010104 14763776540 026527 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" W4ƫhG% "!caNS:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW9VywO'eth Vf0k@Հ?r2704v091&ʷs'W$.Ȫ}6-JJsk^Kj?5DֹlĨTܞ,I_usڳPWf;yf Q6,XpAg)@[L!'#KcAc;[љ ǺwO{]*F拃 [%}Yѐ2_' +e|;ifߞ@Zc/]z 1+;XtkkylA1 :V{2 Q/5l;S31mxAVT*ue=BSIo^+d m&PK<PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKt-IP/%e/META-INF/CERT.SFPKt-I<META-INF/CERT.ECPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  A classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha256-1.2.840.10045.2.1-p30100644 0000000 0000000 00000000034 14763776540 030603 xustar000000000 0000000 28 mtime=1741684064.7790000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha256-1.2.840.10045.2.1-p384.apk0100644 0000000 0000000 00000010177 14763776540 026403 0ustar000000000 0000000 PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" 4ƫh;+KA*[W~OfXUMĖ|*:{tS^r7ۡ@΋(>.qo; bz'nN:®E/{% PK4PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKt-IP/%e/META-INF/CERT.SFPKt-I4META-INF/CERT.ECPK !:!ODresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha256-1.2.840.10045.2.1-p50100644 0000000 0000000 00000000034 14763776540 030605 xustar000000000 0000000 28 mtime=1741684064.7790000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha256-1.2.840.10045.2.1-p521.apk0100644 0000000 0000000 00000010361 14763776540 026367 0ustar000000000 0000000 PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW9VywO'eth Vf0k@Հ?r2704v091&ʷs'W$.Ȫ}6-JJsk^Kj?5DֹlĨTܞ,I_usڳPWf;yf Q6,XpAƋ)@[L!'@Vnw/>6QIDPK2约PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!: QMETA-INF/MANIFEST.MFPKt-IP/%e/META-INF/CERT.SFPKt-I2约META-INF/CERT.ECPK !:!Oresources.arscPK!:^avf AndroidManifest.xmlPK!:Շ   classes.dexPKxc./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-0100644 0000000 0000000 00000000034 14763776540 030504 xustar000000000 0000000 28 mtime=1741684064.7790000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-p256.apk0100644 0000000 0000000 00000007733 14763776540 026551 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW@Zc/]z 1+;Xtkc ylAh1s^XKiAk6ɪ,zz[VyFʿ -.Z{FbOWjŒ7lҮPK5;PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:5;F META-INF/CERT.ECPKxM./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-0100644 0000000 0000000 00000000034 14763776540 030504 xustar000000000 0000000 28 mtime=1741684064.7790000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-p384.apk0100644 0000000 0000000 00000010106 14763776540 026537 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" W4.ƫhG% @ɓ M7*Ϸfwcv] ya袠ŭN+f>%uLWT]M7奾hԜ^;잋W{7%aM_ƳgvN]E?PKIk4PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:Ik4F META-INF/CERT.ECPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-0100644 0000000 0000000 00000000034 14763776540 030504 xustar000000000 0000000 28 mtime=1741684064.7800000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-p521.apk0100644 0000000 0000000 00000010270 14763776540 026532 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW9VywO'eth Vf0k@Հ?r2704v091&ʷs'W$.Ȫ}6-JJsk^Kj?5DֹlĨTܞ,I_usڳPWf;yf Q6,XpAg)@[L!'KcAc;c ֜;hkzx3=+*l|sLU>a"P3z KDםQܬa0TWUj[+y@&M_+vFw;NșRKdUi> PKPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:F META-INF/CERT.ECPKx*./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-p20100644 0000000 0000000 00000000034 14763776540 030604 xustar000000000 0000000 28 mtime=1741684064.7800000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-p256.apk0100644 0000000 0000000 00000010245 14763776540 026377 0ustar000000000 0000000 PK!:Z"Hs4META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽǎNwD^IwܚevT;7y.(LE p$mo%<7FW\yn%wIt+{ʰS$vuoo,}Bz*O)ΦYMȞSbޕ@J쾾f{uэ]? R.Ǘ7KPK?ӤcPKu-IMETA-INF/CERT.EC3hb`jhδIӠIѐ߀3̓1qAcAc .`fbdbd8ſw".6s,LB|l̡,<ɺFfr⼆fƆ&fQ&P.V=l`S333;1,U:)<};/+פ][tn¥-NsOl~rr~Ps˖cc"l3@HUh3' A,b 032GfO7&E, r}".]8?9$Yqk@%?-i>@Zc/]z 1+;XtkkylA1 :V{Vo^zί> =o]|"{\xE'?spf7:bw˗v}PK <PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:Z"Hs4META-INF/MANIFEST.MFPKu-I?ӤcfMETA-INF/CERT.SFPKu-I <`META-INF/CERT.ECPK !:!Ojresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-p30100644 0000000 0000000 00000000034 14763776540 030605 xustar000000000 0000000 28 mtime=1741684064.7800000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-p384.apk0100644 0000000 0000000 00000010415 14763776540 026400 0ustar000000000 0000000 PK!:Z"Hs4META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽǎNwD^IwܚevT;7y.(LE p$mo%<7FW\yn%wIt+{ʰS$vuoo,}Bz*O)ΦYMȞSbޕ@J쾾f{uэ]? R.Ǘ7KPK?ӤcPKMu-IMETA-INF/CERT.EC3hbƩiASAS#!/gBc*3 +7B!&ƵM+Xo3#'Ñ0Jg 8 ٲ03 10&[ȉGXCXMebdcndaPbNb`~"tl#0Nnyݢ3d:jJjyMYXd/ U[muvBmLuCDǿOnKrSmwE3b\`g t,> _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" W4ƫh;+KA 2QR_~2|,cxi傫BLjlyzMǮLߌ5 ߸ħ?[jNb&=E5 ]f13Vh޻<PK?/4PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:Z"Hs4META-INF/MANIFEST.MFPKMu-I?ӤcfMETA-INF/CERT.SFPKMu-I?/4`META-INF/CERT.ECPK !:!Oresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  9 classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-p50100644 0000000 0000000 00000000034 14763776540 030607 xustar000000000 0000000 28 mtime=1741684064.7800000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-p521.apk0100644 0000000 0000000 00000010576 14763776540 026401 0ustar000000000 0000000 PK!:Z"Hs4META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽǎNwD^IwܚevT;7y.(LE p$mo%<7FW\yn%wIt+{ʰS$vuoo,}Bz*O)ΦYMȞSbޕ@J쾾f{uэ]? R.Ǘ7KPK?ӤcPKuu-IMETA-INF/CERT.EC3hbffjhδA&FFC~^6΄6Tf&&VnBM ?qf&F&&N7%˔ 8 ٲ0 10&ȉEXCX46`cƪ X_Q ׂ<rj}~ʝ~KùEǒ_ͯkwk8ɲle,_1/6UʅDfjfry ^~vyD:~cG}B,^mq,Ų|,b,"]>9VywO'eth Vf0k@Հ?r2704v091&ʷs'W$.Ȫ}6-JJsk^Kj?5DֹlĨTܞ,I_usڳPWf;yf Q6,XpAƋ)@[L!'@Vnw3ȏqz0lIQ$[-E5~k0Z7WI'lӻxs˾$+EosI4zbI-3Λuj+AY^3Ăj+93%yLigqPK4PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:Z"Hs4META-INF/MANIFEST.MFPKuu-I?ӤcfMETA-INF/CERT.SFPKuu-I4`META-INF/CERT.ECPK !:!OCresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-0100644 0000000 0000000 00000000034 14763776540 030507 xustar000000000 0000000 28 mtime=1741684064.7800000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-p256.apk0100644 0000000 0000000 00000010133 14763776540 026540 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ@Zc/]z 1+;XtkeylAh1p3 ߠ3OptPvQj: ٘lbl{7g/]??3.>=PK:k9PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!::k9 META-INF/CERT.ECPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-0100644 0000000 0000000 00000000034 14763776540 030507 xustar000000000 0000000 28 mtime=1741684064.7800000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-p384.apk0100644 0000000 0000000 00000010302 14763776540 026540 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" W4ƫhGr/KA*Vg{vܙX=ìLhX8caTۋ :r <$Lͫ޿Owo*=vmc*I>zˣ?S1PK>2PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!:>2 META-INF/CERT.ECPKx4./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-0100644 0000000 0000000 00000000034 14763776540 030507 xustar000000000 0000000 28 mtime=1741684064.7800000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-p521.apk0100644 0000000 0000000 00000010467 14763776540 026545 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ›mr}lr'Ο0y)²6n: k!9OFj>t(|h=Zܑ;;ޥw,sVOPKPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!: META-INF/CERT.ECPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-p20100644 0000000 0000000 00000000034 14763776540 030575 xustar000000000 0000000 28 mtime=1741684064.7810000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-p256.apk0100644 0000000 0000000 00000010464 14763776540 026373 0ustar000000000 0000000 PK!:lMETA-INF/MANIFEST.MFeAo@; n*D+eeb]Ӿ*br17 nc|3(P i^-DX6vΙ 3O`ȻܾN[YY\í7ۦ#)lgʑ,lcB;pshxoUBDFn! ˉ,Y. 3mBIڔw4ni8:ۀ^a1Jtffnz}AWZ`oO ?7PKn&PKu-IMETA-INF/CERT.EC3hb2cjhδIݠIѐ߀3̓1qAcAc .`fbdbd8ſw".6s,LB|l̡,<ɺFfr⼆fƆ&fQ&P.V=l`S333;1,U:)<};/+פ][tn¥-NsOl~rr~Ps˖cc"l3@HUh3' A,b 032GfO7&E, r}".]8?9$Yqk@%?-i>@Zc/]z 1+;XtkmylA1 :V7&/HX˾u&]}uG<$vdS&^ml2[$prf[t6 PKVoj:PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:lMETA-INF/MANIFEST.MFPKu-In&META-INF/CERT.SFPKu-IVoj:META-INF/CERT.ECPK !:!Oresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  ` classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-p30100644 0000000 0000000 00000000034 14763776540 030576 xustar000000000 0000000 28 mtime=1741684064.7810000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-p384.apk0100644 0000000 0000000 00000010636 14763776540 026376 0ustar000000000 0000000 PK!:lMETA-INF/MANIFEST.MFeAo@; n*D+eeb]Ӿ*br17 nc|3(P i^-DX6vΙ 3O`ȻܾN[YY\í7ۦ#)lgʑ,lcB;pshxoUBDFn! ˉ,Y. 3mBIڔw4ni8:ۀ^a1Jtffnz}AWZ`oO ?7PKn&PKu-IMETA-INF/CERT.EC3hbƩiASAS##!/gBc*3 3+7B!&ƵM+Xo3#'Ñ0Jg 8 ٲ03 10&[ȉGXCXMebdcndaPbNb`~"tl#0Nnyݢ3d:jJjyMYXd/ U[muvBmLuCDǿOnKrSmwE3b\`g t,> _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" 4ƫh;+KA*;z ؎%}tּ77/ee4;q 7$zv{V2teW1_K<=mN;Vlaљkqv1O&MPKUS6PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:lMETA-INF/MANIFEST.MFPKu-In&META-INF/CERT.SFPKu-IUS6META-INF/CERT.ECPK !:!Ocresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-p50100644 0000000 0000000 00000000034 14763776540 030600 xustar000000000 0000000 28 mtime=1741684064.7810000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-p521.apk0100644 0000000 0000000 00000011022 14763776540 026355 0ustar000000000 0000000 PK!:lMETA-INF/MANIFEST.MFeAo@; n*D+eeb]Ӿ*br17 nc|3(P i^-DX6vΙ 3O`ȻܾN[YY\í7ۦ#)lgʑ,lcB;pshxoUBDFn! ˉ,Y. 3mBIڔw4ni8:ۀ^a1Jtffnz}AWZ`oO ?7PKn&PKu-IMETA-INF/CERT.EC3hbfbjhδA{&FFC~^6΄6Tf&fVnBM C03121q2 /֟]dTxΖ@P9=5Y@N(JŪq;X&F6VFe6ʍST[k΍/Do=,h~]k[ OIf. 5wPK 1PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:lMETA-INF/MANIFEST.MFPKu-In&META-INF/CERT.SFPKu-I 1META-INF/CERT.ECPK !:!Oresources.arscPK!:^av AndroidManifest.xmlPK!:Շ  > classes.dexPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-0100644 0000000 0000000 00000000034 14763776540 030501 xustar000000000 0000000 28 mtime=1741684064.7810000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-p256.apk0100644 0000000 0000000 00000010332 14763776540 026533 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]@Zc/]z 1+;XtkeylAh1p B!CK-.wv˧/Ddص挽"&ߤU\ʦsWS-nPKk9PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:l META-INF/MANIFEST.MFPK!:1j̬DY META-INF/CERT.SFPK!:k9C META-INF/CERT.ECPKxL./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-0100644 0000000 0000000 00000000034 14763776540 030501 xustar000000000 0000000 28 mtime=1741684064.7810000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-p384.apk0100644 0000000 0000000 00000010500 14763776540 026532 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb] _]8fe ie  x@ YX #s&A!ڱ[guӠW49uҍKvmΪof̝}/U{Zb"[E攪K$~1hjµܾ|{ߩ>" 4ƫhG%l^UiE=>\\X6k_=\ڗ/{IdF>v:rPK NϬv_]xIb Z*a<-cfǮPK1PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:l META-INF/MANIFEST.MFPK!:1j̬DY META-INF/CERT.SFPK!:1C META-INF/CERT.ECPKx./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-0100644 0000000 0000000 00000000034 14763776540 030501 xustar000000000 0000000 28 mtime=1741684064.7810000 src/test/resources/com/android/apksig/v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-p521.apk0100644 0000000 0000000 00000010664 14763776540 026536 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK&sJ test.txt HPK51PK&sJj~+BMETA-INF/CERT.SFmKo@= eET$](1<|t7TA__kn;dC^Q,0e VDaH18=^<_E!pMYkMvqysCjRx Q|0IŒhHeyNƭ5Q#6iCҮYvY_GXMwŒX ^yr,*M*~\x=g@蹻s}sӓt8w PK&sJ*QMETA-INF/CERT.RSA3hbejhδΠՊѐ߀3̓1qAsAsASf&F&&A -P5@-|l̡,lyyy0 v . n/A@fa9q^C3Kc#C(q^#C3(NibTB`1713Ź(bujkva}GJZGBNsJzEޙLPlHМ 6ԱLvPO:iÚK"{Oi;$Ԧͫd'޲d*pouyEry[D7_H7Ȝ ''e*?.ȦЯM,z8xJEkg>͙R4a 6}}W2wm^= i'|;3j31320.V47,Vn&oƚpYyF{ ;V+,~M({<ڬϭauCPFO6ln}ЋdX5.y9ُY _3niL׏~sy["^s˧H'{զaK7^=k v9y5-ܖ}kw~ {^0D~yS!5nN gwo~Җv;!g?»jwƭz_~_z?a6u{ywꅗK3Xu ðq0!PB:&jDF.PS9K#w{RSgʊ9ɒ{۷?{zWjGWӮn=ñWݼwd\6qYi:js_}yG ;ؼ <~CɮU7~̾Rh:K uLEV}lK[Ǽ[QHׂJ1Ow7fk|y?>#qtaƯMߴe 3C-Vhu_D>op[b7|4('6x [ PK&sJvT*+META-INF/MANIFEST.MFen@= ADAѤ .JU*l tؙO_mퟜ/9SH"x%(K:߂1|h0Fz(((ل{QA WKJPmDՔ\vPum+ ]fmv6Ifu]Sg0(MQ Q:?]41%Ise{#khd4lئJEڕ쎤WPV</ߟ'Ό:P`*lm^ߩv\x/&]QS'X%ޮηa~PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK&sJ51 test.txt PK&sJj~+B META-INF/CERT.SFPK&sJ*Q\ META-INF/CERT.RSAPK&sJvT*+META-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-nul-in-entry-name.apk0100644 0000000 0000000 00000000034 14763776540 030450 xustar000000000 0000000 28 mtime=1741684064.7820000 src/test/resources/com/android/apksig/v1-only-with-nul-in-entry-name.apk0100644 0000000 0000000 00000011333 14763776540 025315 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK&sJ test.txtHPK51PK&sJ}+DMETA-INF/CERT.SFmn@ЮIm t!JZMa"__k^V((H"Pu0pK<O-a5I@&(Kr E7tN܆Es3++X1 Q OžKKh .PN;z~xQ:kbQ6u?:JcubXߓ^5c1 ƥߔY[![oFP3thxMj3g>Fg^ₜ䬴QXoT8=xPҹq(t+;ZJ]X,LkgSjPK&sJ(@*QMETA-INF/CERT.RSA3hbejhδΠՊѐ߀3̓1qAsAsASf&F&&A -P5@-|l̡,lyyy0 v . n/A@fa9q^C3Kc#C(q^#C3(NibTB`1713Ź(bujkva}GJZGBNsJzEޙLPlHМ 6ԱLvPO:iÚK"{Oi;$Ԧͫd'޲d*pouyEry[D7_H7Ȝ ''e*?.ȦЯM,z8xJEkg>͙R4a 6}}W2wm^= i'|;3j31320.V47,Vn&oƚpYyF{ ;V+,~M({<ڬϭauCPFO6ln}ЋdX5.y9ُY _3niL׏~sy["^s˧H'{զaK7^=k v9y5-ܖ}kw~ {^0D~yS!5nN gwo~Җv;!g?»jwƭz_~_z?a6u{ywꅗK3Xu ðq0!PB:&jDF.PS9K#lkQ>׳<+'[S}3D-f/̯cKY-$|c9Lwj2yNՖR <zh^bMzl3X_m8* d`G_?܃rw.)<5޶ַ~~PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK&sJ51 test.txtPK&sJ}+D META-INF/CERT.SFPK&sJ(@*Q^ META-INF/CERT.RSAPK&sJBZ,META-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-1024-cert-not-der.apk0100644 0000000 0000000 00000000034 14763776540 030643 xustar000000000 0000000 28 mtime=1741684064.7820000 src/test/resources/com/android/apksig/v1-only-with-rsa-1024-cert-not-der.apk0100644 0000000 0000000 00000010126 14763776540 025507 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:META-INF/CERT.SF]Ko@= am ^!X,r (KLgea MQEd@jeZPVfćث(x&f)Zޒ"KaHe赙c,ODAlLihЭa *ThD;'>΁+ڧ]siUFU"9c= xٰxzܥѱc7U܈_PK#>PKjHMETA-INF/CERT.RSA3hbƩiA3&GLl|LR E 41~LLL v<{{€OPЀ98Q@N8JŮq>ٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G L&F&6毳=[l|)gKxzwӞeEֈlxy ~qS}7|Rk'M;Eoqux4< I u&$u'Y;pU%zC&z 4xrZ@J?Q0uF>-a[ f kf(9Uv9U.s3ڮ~/WiE\{ |U18vS&'Wy40͑uD֖} 5Ǯ3|e"jZŌIo.nPKPK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PKjPK !:!O resources.arsc5PK!:^avAndroidManifest.xmlPK!:Շ  oclasses.dexPK!:#> META-INF/CERT.SFPKjH META-INF/CERT.RSAPK!:j META-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-1024-cert-not-der2.apk0100644 0000000 0000000 00000000034 14763776540 030725 xustar000000000 0000000 28 mtime=1741684064.7820000 src/test/resources/com/android/apksig/v1-only-with-rsa-1024-cert-not-der2.apk0100644 0000000 0000000 00000010126 14763776540 025571 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexTMhA~l4i5?DvUMQXIi*ԣL7ki:AEEE衈Aăovf4T&߾7}fc`Wsdpn|[I.CՓ``~h G"tk|<(㨬|{wĞYxx9,b1C\@PDqqxxL^ľ0ܶ8DQ{!Z VumJ˹ /5TA_J{Hىa%c |!^6Cj߆XIp=n.Mdf#D'R׿| |BJZ$3"sf4HKTy/E}O`&Z7McA7vOqɛ\6OYܩ9|! }v϶4L{OP+Y8LWVYؾNr=tKvi̚1S<ìa,݄c̮(Mܴ"HIi% m,7fOR}jZeٞ7Zx@D e9ەLZ~0oϋlbSnCʣ5;c̲sa3dǃp6Z$]Z:bAxKhՓkT{Njuo_J w?WĿ]_|k?DUpB0HE рz %qu`###z~R|^32pFEOe*PKՇ PK!:META-INF/CERT.SF]Ko@= am ^!X,r (KLgea MQEd@jeZPVfćث(x&f)Zޒ"KaHe赙c,ODAlLihЭa *ThD;'>΁+ڧ]siUFU"9c= xٰxzܥѱc7U܈_PK#>PKdJMETA-INF/CERT.RSA3hbƩiA3&GLl|LR E 41~LLL v<{{€OPЀ98Q@N8JŮq>ٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G L&&6毳=[l|)gKxzwӞeEֈlxy ~qS}7|Rk'M;Eoqux4< I u&$u'Y;pU%zC&z 4xrZ@J?Q0uF>-a[ f kf(9Uv9U.s3ڮ~/WiE\{ |U18vS&'Wy40͑uD֖} 5Ǯ3|e"jZŌIo.nPKKPK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PKjPK !:!O resources.arsc5PK!:^avAndroidManifest.xmlPK!:Շ  oclasses.dexPK!:#> META-INF/CERT.SFPKdJK META-INF/CERT.RSAPK!:j META-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-1024.apk0100644 0000000 0000000 00000000034 14763776540 026342 xustar000000000 0000000 28 mtime=1741684064.7830000 src/test/resources/com/android/apksig/v1-only-with-rsa-1024.apk0100644 0000000 0000000 00000010122 14763776540 023202 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/CERT.SF]Ko@= am ^!X,r (KLgea MQEd@jeZPVfćث(x&f)Zޒ"KaHe赙c,ODAlLihЭa *ThD;'>΁+ڧ]siUFU"9c= xٰxzܥѱc7U܈_PK#>PK!:META-INF/CERT.RSA3hbƩiAS&Ll|LR E 41~LLL v<{{€OPЀ98Q@N8JŮq>ٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G LfOWnXۘo i^f/9YO{=Y#%ӂ %OךR݀;I5b'6կ4$-eRĺUf;J^WY|J&}6lCMC|PKjPK !:!O resources.arsc5PK!:^avAndroidManifest.xmlPK!:Շ  oclasses.dexPK!:#> META-INF/CERT.SFPK!:; META-INF/CERT.RSAPK!:j META-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-2048.apk0100644 0000000 0000000 00000000034 14763776540 026351 xustar000000000 0000000 28 mtime=1741684064.7830000 src/test/resources/com/android/apksig/v1-only-with-rsa-2048.apk0100644 0000000 0000000 00000011017 14763776540 023215 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:o9%META-INF/RSA-2048.SFmKo@; c"F$=P+ UwCvzi67#a5ŌP%a 0R^(OvXEг,햶O #G̅nR*|_Ӥߘ#&hEu"_eI֠ 0öoXxtN!~""ttp 0쑆9!_Iڇl̜Ƽ?u븯y0$üjqw}FЛ7 PPǔ8wu05͐a+PK!:< $META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICSIx˿CXE'.k٧G=vnhrX87Gꕾ{B"J<**&ڱϴIKlL͘qE)7]xnƦj%~<1Q.֓klS="q]= %fo4PXZjF[˼xx PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg71]D Tt_Jv ie  x@ YX &3ЧMn7m_g{̷؆R4?/p=W Vs{IiA⒧kM)n֚ON1vhx2MIb݃OZ۝wz%/oK>%l·LF>60h< L! X 9) cil0\2㚅<n e4yF^Ӿ/G2KV߃{5?' ̔*z)cbKi 4&uԩ9g#rO[Iol jpJk!7W&S/ VL_X PKkHЇPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:kHЇ META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7830000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-16384.apk0100644 0000000 0000000 00000023363 14763776540 027030 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7;@xý&o+Gxl>oq2Hc{/U6vvoH _4"sy9I&D=?*L ɂS\F*~ m810hC8݁ůnӒX)@^eP\QF}"rJ *\ɱ(һ 1-߭>+%4lzEp"6Ƕ/.3#Jc bBͽRoyo nr8ghb0WN7'_L.R zyLт`v{L17 7_}7d @ Rn(.]BWS&)Tϴaۏd[- fhާ`E( O$\Lo f*f-d=N9 ²$Qj#: u '49I83afuF4bL[05pOl~`EI~l~`VLLّW.Sx<޼ p-tI\hCBdbl5o}o,(!t!Dj;#ݩypI% ilz]MV~@6Lɝ]|#~e\QAAg'%]Wa#߃ȯ/mfӖc"=JƮ(_- @1 Jc%5) qnDGQf^:p'wcBLM+ݗaNBK_͎sk^J_m4yi~uW$!$`X4H͒V*sGQ{qK& OkaSVz!%Rd/ҬK}: X# NPz<8W>;<p)iQTJ,QQs4w&_n-tKAU 1r|](64&BJ&_1I.Wش~~.襱YMW0EyVvD$4.M \ILemT7|bJ1-`אxfyX\J5<*dC,ᢉ~}I_ oSH| "M͹1;{+ToB5#Eh¬mwʆ^B$Ù' 6n =HwՃFu}RPF7Ԋt[G 4b(-g![.ڽPqh0 S['m&%?ؠk#UvI1[-V)j2mf1$hCTlRF3XX m7i ٚo+ϤN %pUGzz T4)0$as.vNʜK{|e.wbq]?xl_wo&+]W٬;,XHs%e=7 uS70h #W:;Zkz Nx /^)FhGNغQta=VA2|r\>WTn?^xI_t@1@(tM]i-t4r`]\Ɓ˷ oc*#ōs BŚE`^A SǴ^ TY`/t)o-VVy"hFl_ȹ+H΢˲>[hہ0`:Hi|s,VmO;W 7¼p5*I.=$=6IgvW*0թjo jdKU W:sq9RYorchI98Վ;5PTpBг&Dе-,[G#GvU?ܥ~(N W8*FZ(&d@CC((@D |ww*^tEN;2=BAHD" ?%0Bu {+{znDv՜[ )r3bd'g*&wEq԰Et=w81E.jvA&)tL@2=d1z y|yq֏e\2 >8ekr(~EX1bу80&? q $@̚ mPlYk]X,aeAil*{H;'\EWl 7}^q$ަT =aҧT4Jkϸdl"ަͺ;4L;[7 'ɨ'OnDR gmkYM,DhU"h݁mkN~ɨ7WD_hz{cE,*f}PBT"s֞CTY5Zߏ򴝪͇P`<;z>3c) z!M|ND~A)^ oBDH|њ0ghXa4)(}GJˮx#,qMARf>bv] ۡE*ogme6(&4$vEUB^zyT$b8AU}ΘGyփp <_]|b{Z[%?mqߘE},C;怠4M̷&4e9^>ޤ2d /5A=#NEdyZό-[Fl1 i 0BeR⑛ rf Unہ&ߞ^=Pۊ02ETUJ&Z+s< `a?ԇ%_JT%D4X"FLG?֬y[ K5=ȍ5h^}'1NW)|5슮W*a a9oncd|ߝ'^ɉ'Bzo7}EoF./IX7Vts>Sߓ`ׄ0fH|#D<[WU [C;iݭ&4yϼWR- 鿪{goMє:OOPXsIC,8_EC&?E`ԝ4:ܟLx@dh,ihۆ1nȝ} coZ3n3,3b A5mh:~XP0YS<ϖG7moWM "7_ 1!$‡7x:[d붻5l#K^ZZb9$NL(iJoqՊXng{x1bɷ!l[I7?Wolz ʯLKEg)^ @nWΘZydҠRRJ!v]ŏndR$.H_*mi'ݧC[3 kTPOtA/FsrtkQ<5^R+*iOӾ)F\@o˟i<ʚ0'  rx]}Ըߘ+hN7=ےKNOg-ɦ# %(|r +C@BZ{()PGb,ZTնbg|Xcā͕t'AU2:QQ !*O>h@ɕ^d:Bp= Uz01e ߟG ~mR>>= 3 ڙoSZ#Qf,Ȫwf:dCb'6t0%AK(ns7s=ݽ pQ\ أ0sDh^QSS[O:N9=EeIl;Z:P\~hmcg[H0*B9LxWȠO?M#qT0"˒Ɉ Be.gdžD&JԴDlG-k؋qR?o$ޠ֣!Qg-nIPQ}Yj+ܺgŏ+ZQU)Tq-3o$zA~j,T/!_u?KīK>.I(FйU=o/ϩ-SugM]nPweqIR?k:֖NuwpJwX9|m w1MS!KDP$4U]P "P  %ӗ5䅴ADJ-SW~at[Ā 3h@[!nCS7_si  0 H9Pu^Հ]A&YReM~ RMf=V=}>d_\vYt}3n̟s6x?~ IT)g?%uk+nbMtFD:Qgwn!*(%Ӥ = l38*Iߩ޳wh\pm S0f|5.`#g#nargR;~%&(3*M xH UΗҪ2llc2fROv)52KLKO}7_ƘаF dnzi PN=\n8-*j0Cwr4> [pPN`noRE|&m_K2{0 XS򡳱i8 QyOӐtTO42 USk$,mx'c XKaݝ&!hN!a pdפD*M^`jh( (3c!){+#]Vz^n`X'u7e!Tg}͋ <١8-A#rܳ%n J'_j(;+%x U=P;89$8ntL@z'R%USIa6ڟn:ABIe;S41\%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:B: META-INF/CERT.RSAPKyd%./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7830000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-2048.apk0100644 0000000 0000000 00000010731 14763776540 026733 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7ʬ.w8/OU6b_?uL0mR)=w=_N/_w$_MbnƢbXNt˸cʂ;V*ɡ<[%p//W6QaBF"D ^1+ }e%k'Ǩ?{8ܥ'-R>+Uta&I6ߡMc 31320.03,8n@lKY"ud A,b 032GPfPP3~Or:d)߷WJD2ɻ1s{UΌ {փ'l1= VW~mo .} 5}>iH5|K-56Z]d_7 eޗ}ՎJ?^ɠ{"iQ;oF6/kËtmvQ3^k|o|zm6|}VSyٷia0ݹ3Ԋ#P3jfibdXJUM/?gtc7wJIK7ײ?J~i_%.?|'Me]Meadh`qo$<(`Y;X2j[䌭hvAٷgvɾ;M"9[X!j2aOS7.Xu:~v$c\o*u녎;K%cr"]]J*-kj#./,?g95jPK=^ PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:=^  META-INF/CERT.RSAPKyJ./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7840000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-3072.apk0100644 0000000 0000000 00000011550 14763776540 026731 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7H"D]n2$"(8! OC@<6<ӧ2:Kj_ f.aS:]:Ia:;2j?iش^m`@ m5l |G =]6y˚pcTvwwU˄?Ekg,tBv_zo՟,9DjXN;5Mk/.' pܥ)ɪN ?h"Jwp9o_JXaٺmM#^F]1tYRYYl)]%\$%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:W. META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7840000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-4096.apk0100644 0000000 0000000 00000012355 14763776540 026744 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7,cȭh ]aԛC wR~cV^Exk|оΖ隍 _M t}_*S([ڰ*.W[s1e4)d~׮6O{Ϥ*NUR#:+hq'uiS2z;c݃W 70mvfOWP|.bCo@|3 =BþUO+9}3stCY"/еkT*L"WI#ÅI=fʲ=9̈́EQ ^/G@ǘ>'xZ.0NiVT9ŒBi5ȧs7yh`IPX*%#^4srr˻J6wa=ݠ#Cc2GMjdݶ Fْ=z7olx KΤ( $XMr\LlK~Z"Ra:4oT9ZeM哀hm5DjT^p~dJG.q0!׃lBii0}[ {Ckf!>^1䖢,(p]I!/ȁGiavRk/7^%,ɼ4u}'{^4Xq\*Ǎozꞗ~(@!cqi|,y"W&パe-JKg3EUFH bUvo{/_oG۪ Nq'ɱ8ˮ-A勭iЬVvLrB?_)IA.$ͮSAϭ* 6˹K% R`Arh-SʳD @1.NgS[Pfk ]LW-wPq[ً1 uGh^hVSӤPKu4PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:u4 META-INF/CERT.RSAPKy^./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7840000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-8192.apk0100644 0000000 0000000 00000015362 14763776540 026746 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg75BD|z4FOÈfS^v Ei4; Eˢe12ҲXKv ZAC8PQ@ T8NP\g\n9@M3FRymzjADVmM  R}!Xsc]uo<57rJɆ9͖ͤF.|Y/l]ڍu7lpy\q}X>V47Dx+__UV?eBC*INij8zT<<;kA[*9ѽɾc#G"F}ISAHCj(ekGx[fʛb:e縎ȷxjP0wz6ɮM A6kڄ {ՙ_2>Gs7+X-@&MaKCrS~I}$)AVȌncnR' 3{=DlI@ɲe7*mWߐ9Ӈ%:.aۤ ^hR0WPPxh䐛CcREjAO)+ۼw&RC3,mD6'K*n$H:9t#)MOc_oɖ `+B~ʹ5:|Bi䟳%zPMo5%#b``m9)UyjHY}|yB* ԨeNI4%Fl1?r6s=Cewr"̮'&O,J/Ǔb>撩ɩ8/澰HUKoG[7v{o<>r AQ|_̻n (ỒHK٘X2J_U!"&VxGSRox:\s2 ةf",t&6R:/30"E }Ut \H9hUk4jj_~8=!O(0G+ׯeڨ>Os`XyiˢVAoQ~<;]'1PKr9e&xL_}>6`og߻XdѰ`)O.Fr7Bb. vu2 r?daZ 6k]f~u``gMJqk{f2]5%92bNvUew$U@ hj jۜF3|*s{Nw#)KEPwYزz :ej~!e^2ӂűn.[fn$|Xv^ v)=>&NפaCIN*ˑKIM;i_?LJU;gVN⢇n4 ;ퟭ[47Ҝ:"_莱{6n j+Y E$U^_Q&|ir8DbN{L(JlJe29}E {49<: .<%Sa/3Gh֭7 ~Wb( =3(g+wPp0^t{'Eۓ;oi忉AQt._+#3oM8Ncnai_oiF@W 0BmޙA|qҫOxvT=v˒/armOS ?>f>w[=Ku|cYr`$b;xԪ83M.HFQ*GCmrٞ}{ȬtܛB{ݜ$T Y4D:(ce=Ey0*'dCcJjM\/wcVlb{(ɦe L D 4>  ~v3H,oq%D5WDI>[?*<9(9!C Ƈ}hdvu0!l>+V\6!{Rw^պK6bNgLQ^q8B=Gc'*,l#aYNDZQ{0*n+{0WPA|^ܿ& I}F\yI3#+0P2?|oGXm8 Oڻe]/?%. =PKJP9 PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:JP9  META-INF/CERT.RSAPKyc./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7840000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-1024.apk0100644 0000000 0000000 00000010026 14763776540 026724 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:CोMETA-INF/CERT.RSA3hbƩiAk&L|1]D Tt_Jv ie  x@ YX &3ЧMn7m_g{̷؆R4?/p=W Vs{IiA⒧kM)n֚ON1vhx2MIb݃OZ۝wz%/oK>%l·LF>60h< L! H$ &䤀ؐ$wѥ^6~W3/tM8z߿1 >w?%3hzZ4,:^v۬ >SRꢥL UُSZ$"kqP!!.!PK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:Cो META-INF/CERT.RSAPK!:j META-INF/MANIFEST.MFPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7850000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-16384.apk0100644 0000000 0000000 00000023312 14763776540 027025 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:I6.?META-INF/CERT.RSAuUTEih܂6 5]X %wB}gٙݳYU;L&v @ &X $#"Ar߽A=AQspR Zq! 0gDžCB"`C9y  Lv( ɮrQh=!{4`\͟o*^m iRrS9Jzozo' >62v]+O´{udKY#C LW0'O*`JzCWeVh6-Ξ2w#Mx6s~<J 04pLs!g֭v {³$Z}/eZ.u9aK9pe3J=_?ztc8:M=cĐ8[fk[;XC:9\uy[Jnm;GO1͒fBrH lﰁe](crMPgBϮ[I^(o86+#-6YZt *ǜ'kthxAiW!m_c([ۉ_HxŦB}jN8q!B#lԸg2,eTfX`$!" ҹ૱#LrH;6 }"\I z}!/}ΪT rش|kkeڎ PgI,\4 :ɯ .hHv7y,[$䟉 DrիmU_ǖ ⅹFPyPJyf QX~*jPXN/'`ㅄ| 2GynS9'9 Uk3ُ+>JB?GIı| HgDo<=et-O'˦ CNJ2:_K#鸭tKwRdJK! #/+Ʀ}z@cs a ĎAee*xoF-:B4'tMxd"H09@3c0>9>t3/vnڪQsHYZ&v82@/ɻ]^<:g_$;١4WίJOT|V;{o/ٛbGlS5 ap.ZIljlol~o0Q|;Z']&Gl %qiMѴt6|RJTwycTevՅ7MρO>2GiwOi~t8*I˸ę(;3&q+-^S}(|ʹycM2zV^沯ryw8c( D$̮ÕӻD F*uIUw|NxX8׊`γq-L(u?hdtnpYXew% *[2C#./pYWCmdڞ1sCPPGe!\x 2ddg'p1{9J*rԼ1Y5<<^Y4۶%iAʂ.J-;#؅xYT&4hMAaM%L,K&Ƈy@Wb.O2D}hFukv9g][F'^oi+Mv&‚مk;^LA?dRk+{+:ſ|[1m;6V]OӴԒs]RKh>wl/(ڭgsRﴐ.6F-ih12V-bB6p|U2݁ HSdKT+h!4X < 7OAWAg '$$`_ROqZq&$v)(gmʾʱQDmud0b?䢁تۂ8*Xy+a46]«suT Wh’IU;HBŖo ;h6VތgVll3ïڇ[-\:bp0}V&,85 %GVvp5NhlTCsFX7ip+D=d OdlV|gpQyNr.  Hq65ǚ?MXcfGQDF 7BjQO(~ml?,`3tkx?X~qEo] VD*)6=,帜lRrߪ&BEA/?e wvDtC>U,{ KQg=71N3ȼՑ|slKjjM<߄H$_H ze#OnFai7I6xi1DLkKh;;"Q՛kz,,L%䝵HDNb36ubc$a,#_VoMkPZlu*gl#PGhK D Sn*Ylgs8w`c_l=v#C@I%]O!l=_֝I`cר.Tћ ݘ54PVZ2'knY,M֨U).(o>WXx}/;s KU) uiϲ݁yU{+-cXƷ aUvsH(Yߋ)96h/39ρ?2Oe[5#QZ1I'nC:ڍ -Ck/ԝ`}cg2.[Y^&oG^Uߖ+vn5RE)fӋXό~tR gn zgx._d,D,ekFWmPw6:NMv`^ytҐ)zX%XJы,Kʏ#䰯p]A~xJXm:bӐ+ 7(w?J|B:vkTY|xN5^vd@, 795V;הQ`ꗫ 6G#$Uo,#6a/W-i] ˞vX6<>{[ācQ-OJ3[Q/7ݩ2-pkY:⍻OegZkKn=#72賣?HɐZCXϽє“P;Ut^kߟQinĚw/,y 5N'U:+zQw0?EkW"}T~XR2N}]ޢa!ʕev 赠I60PF+['LNJBm F ++]Oo} FV{XA Yz'Sa(27NUw7x,dkl_i,ťYD+ mG!|H>wKb\HOS)ђomUMp%uzns^`VM~iZj&Pf7 zbAa03ָXMfX8s$V e qe$$3j_h3w?{u?qZm(܇ed[Qc+!wΰ~J ~ OkRL0= yq PMi&3z*'xI| 55.e+wx$ /ѡF\\S "ǾI\%@hG@\{\yp$G?`-$4>Fmk^7̊nqWsVxY )OHQae9nw27"+CX_cNGd^c#8z54~vNDs-Oh*KN=)04O'=3{q[lsd <ނyy{3 Erb)o0r'^N$) z|9cmv{n"04b{ԍ[I[ZpP9XO+3A-uJ,oVeT ,l5#1Px̲%!sC\Vg]$j54d"|Ax(m2{)k>sN`YAJGc'ƇA0K?غ$(@p:hKG\a^$IJ {!HV{_ay}>3(H >XZ /?~l{eQ;iF~,ʔh)ͳ#c߮1u~RuFwt:Zyd1i^DDiB)^쎻ub\ [{|_>h46ɖtEQ+4eX}W1_ Mh9 詆׫5'RA_IJXc0fbVLtq7Y] 7GS(q(z*h(D#o3[dU{(~dspApT0EG/k|(!D& g\H)Y^1e,)Ȏ'rz4Ia23e^tnsH*>M#>HCyDb}أ]=IPK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:I6.? META-INF/CERT.RSAPK!:j3$META-INF/MANIFEST.MFPKy;%./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7850000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-2048.apk0100644 0000000 0000000 00000010664 14763776540 026743 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:Z)META-INF/CERT.RSA3hbibjhδؠ%ѐπ"`PǸAO&Ƈ 8L r2~7+d64gcea(*N52005430664415751604rkjbTB6(p$Sۿl:42y . $t\y]Xg!(Zij>ʬ.w8/OU6b_?uL0mR)=w=_N/_w$_MbnƢbXNt˸cʂ;V*ɡ<[%p//W6QaBF"D ^1+ }e%k'Ǩ?{8ܥ'-R>+Uta&I6ߡMc 31320.03,8n@lKY"ud A,b 032GPfPP3~Or:d)߷WJD2ɻ1s{UΌ {փ'l1= VW~mo .} 5}>iH5|K-56Z]d_7 eޗ}ՎJ?^ɠ{"iQ;oF6/kËtmvQ3^k|o|zm6|}VSyٷia0ݹ3RjV2o͞ ^]x!=`29?r?!1+@aϻWUotU@ֆcSsvGęwz;-ߞ2ΈDW_9>{/9Lʹ^~ _7:7^|W"Bg0[~v4?$'~H1Q~<:qgkm|fWij&Z9L- ?uivRTTj%OPK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:Z) META-INF/CERT.RSAPK!:jMETA-INF/MANIFEST.MFPKy%./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7850000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-3072.apk0100644 0000000 0000000 00000011500 14763776540 026727 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:^_m̵META-INF/CERT.RSAmTy8 \A2ko"b$+G &G(E ȱ (rĸR,lYճv}}A*V19THE,CF/ȡ¶A*O u΁A(2-ry:AȏYa}~ʪ1PBx'DxTx@I=J"!YT܀j Oo.{ږRݪݨ-#jBG3UrUghG];Kz-o-nEV|Oب_{u7r]\*F:9sqڋĠm/R 6z ֗0iPZ\zn(l%'ToG15?6{C s1ݦ8h s|&.1mINZތAv%OSZ0zN[2Ezjd\hM}S89.Ep}p0.~F b%~՜zΏr}4zAH,}Ҽ5y 0PZ?,SmưZn9)by|Aa3M@̮u>0]^ԍ{bE]%(';{(WW"5%7ڮ)M-?g[mTt%?MtǓۣGw0NrXX#ڈg!^QS+8a_1i(vD&ZG ݐKG_ d>O1o󙌪pU _Lnk-b[!y \hy-&M;+]}̰x;kvcq/9_[(GQ !{="d TZvvLԕC&zg &RR"u̞ₔEDYaA'Yw!;Z35k1w7 >dỳyE^ Sxapb #Ct] I[K.e5-y~XN~T]r(ϬX_PK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:^_m̵ META-INF/CERT.RSAPK!:jMETA-INF/MANIFEST.MFPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7850000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-4096.apk0100644 0000000 0000000 00000012307 14763776540 026744 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:r <META-INF/CERT.RSAmy8ԋg~ 30 eMX""K&JRbI,Ti Le'%dɡQ"ٗܡGϽϹ?T1*RBaXBCU,P(VJP R/ HRc Jf*A)$CGxPE#x<gH஀ǃF8j" 0* oҹ_zߵY7fw;nuGppFMDP <:\,?l[B*i &4IP{|āsOqE?gqW@ &1tPU^Rx7< 69TalVAՓT&> yZﴮY->$NSj)ٹq">Oa؇%Sʥ^M'VI<n/جAn>nB|}i 5ZrM8p5=S`|Ytb /[ՏOc d> .GXɮplK΋#L"20TOolEQUыwGg[g*(͘ M Ct~Z Ju=`PQTި2.]Y T%[,Wހi ]B~V<I݇v,Q3y{3Ö9s㶣nm+vOWUi6},}M72^)6c'N peXV2MƝɆ==qLru8k:XFP2L=%i1ԉ5#0WAwaPqL^i',V!MˊZ; /S)58#c|+7" E֯Lsͅ~\1wrw?ɤ0h(cO2dCTAWRPjdaW|fqcB?SlX70gla`#O,]R(n`ctq &zc2o0X) VwV+ĶFӕdˑa/aHù>I _Zc? HՎ/.ӌ2*PCSi jsz< xvByqyvs,ы&.91O3*QD7y0O-F[5HO%E_ ԪJa>EQ lTVؿh+ pӶ=EgĜS@zflc}uv#* nb5 )֭toEx:F_w8UT-Y;J[qzcK^Y*%Y:6x|; 4rq&a 1?\l3qיH}[LBM3]hw+Ët qznk[TLk0_~F.{uI$W0sS]\=#ͤ^4UK3,Wfٕgg@sf fTކt_ދR,l׹@v|$rw2PN҄Ĺ"/]:Kᔇݕ Wq:k(28 rq5F6=!ew_ۖPl's5 F;k;T2^g PK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:r < META-INF/CERT.RSAPK!:j0META-INF/MANIFEST.MFPKy8./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.0100644 0000000 0000000 00000000034 14763776540 030637 xustar000000000 0000000 28 mtime=1741684064.7850000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-8192.apk0100644 0000000 0000000 00000015312 14763776540 026744 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:q? META-INF/CERT.RSAmg8 dz b{%DlQTm5"Q{-NTkV[{oZ4IPBWzy\a(i$ T .z8N> C}P1%h %AGH;nj`3P `(uP2BEN@p$BEAgC*w"r 0~0 D!60^a?L{e?;C$ 0\ܦHh +D#4oȻZ!I%Q[}5 nNG͛VV+lc\W[ds0+9+I{&>j۪]~bێYQѢ/B_O T#uq/ GFmcbeMUZYt8v 1E429k|xs} +{DQ`5婣 "R0I"BՆ dŗ=}Wǡ fx,ÜgI9ڨl#XxT{k]czu m眃f+&&V'W:>0?y;HꚟzIl1;`6A=tvΠ}ZA+=ѧa>ћ>i`(*Ct1vRaӪ>17P$ +kvwS1s,cJr~k:mۏ}`-㯧J^ '*,h34~(qn҄+>fGNxҠAAC4JwqUe;6~| 0,1y~O= 4lqS[~fh(oYr п  8A=5GVCw%A=۰:u5pb}K T4`S _5h}G7]nCN҉4v1M(8zI|l1ԋ$0 ~k*d 9oY;X)-9 Cdh'yDcñtT(6`է8C +kУnik}&V,OMwpHt;BWjcL~SᏗq eh0?K㑃ΰ)Xq]`3Źguc& { C!HOnS\ş2B%,I} +h~Vx:/7r՟/]٭yJ$5ANm L+J1C&s.:ѿ%v]nt R թ 7_ ǘtT1D=M+q6ScWq^=.YOC3$[)ݗ\q TfAjc]R`$`CV>|Mf86FY|>@]cNH֩*ʢ]&-Isi\(=X3-Z/0Pt4-rcA"0ܡ?As@0kX\r-ѨK֌H氡)aI>a)q-a#1\othp3ȚHEg_[3m:xE>1@r<+SukIfyD %Y5t['$&Jr8tâus78I nqI wYmۑ%ݥ)3fSܨuiqʹe)CϰZ[+VƊ_i7К;K ˽{]c.p-e}7~E^j]hJ[ZjɍeqQvĭ댫coZb{?" ugX 9@7J'gLppDlX8<Ѷ/:ۋjW~} Uibk]-xüٍJs!ҭ;b0W͓f&44ҍ#bSv3P?pJ8>kĜ"^o~6 *[,o>i  8#OCA].END_~FqW[-Sڃdiq`/m?ԓjH/%6*T8ΐ$ڎdՕa#ib@ʾCZ}bCTIT*m{ySB"99za?TگviLn8}"x3ec{#Yy:y$g,dTv|;%BT /A6\8u;NbMSZá^iX}>PK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:q?  META-INF/CERT.RSAPK!:j3META-INF/MANIFEST.MFPKy;./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7860000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-1024.apk0100644 0000000 0000000 00000010101 14763776540 027062 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7ٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G LfOWnXۘo i^f/9YO{=Y#%ӂ %OךR݀;I5b'6կ4$-eRĺUf;J^WY|J&}6l10{˔ ?g 9Q]`IW[uhO^o:&:|g#r.[r?3k[s.{5+)N0}ڤ-{>ݙ?w֖Kmo[wrwSSOd[nPK*ќPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:*ќ META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7860000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-16384.apk0100644 0000000 0000000 00000023362 14763776540 027176 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7ʡ%Ռޗ^Ӽ m83U1kCo9+5=> RlN+fLg#Nʞs_?#xmZwSńQWT=~#MZ #VM6ϓP]ϢfD75@fևR Wmd2t ߉AfhcHB2!76T6L&`CKa¿/kc索#F=r0^Q#:%H&ٻʡ=~)I;zYHm2 Rh­-GZ!;@.PWmNBWκʦpL/L9*8bo~$n`SL#7Gsoеu7}ԊOxYrtai(?:A;t\2vC W_&P^0:N2\<|9fsG\)RX_,~ɳ}eC:aNYt9P&d:3iUi_SA(>!enҒZcp-JJ98MX;cWdޗv[.k%wfܠiUdss.',35_-4= ./|)"LBWd+N[oaa|kX=5pW!T/l LBVC/r ;1<\IksPR:2yz밆q[,0ݩ`$bhC]R?yvR'%, Z=nv+h`f6!*lǮmu,=BE;g.39<=Kᴭ [z5^lƿ9nRz0B!@DH';P <,d jUIA 0vͿHd@ A^WAWԠ"!W*,˩KgԹ-oP{WrSnI*ԏ(qY>fBp5Nwiע A=xk ]646@Q@ pM.}훠=]\ #;3M@RN wWǝc 6VSîXż$%g0Ԥ5uyڀm[kזg2as+~qҒT؅! I7eF;zv~o¸P9s}pUHOIڭViWq)mTЬj 3' b) کkQ{1'i۪MWFfDxE"X>t.-;Ȱhw#%Im__Ȼ,޺28z䔟g@*jiD S7Q-XfE_,9r]Qn$onC} &ElAeF9-HIy`Ѭ,3+c?q+J']R'Oojf0V,ާ=s=xHCClE)(W.ΛE ȸg{? Wpk݇ϧ%ǭWwk"/X(Zc2h*A=†6~)ܚ-|{Q}Ng,ԁG^'"r<&Vbby$D4 qIGi.P F߶]3t8qy5=H>{J:ڼ3FUK|HDjy,[Pq]5l;Vs$_yXѿqwWMBsĨUw{W-frn_6J}Պcy J]oR)e6GY~~oӐMhncMZae + ~n$(M_@Q.=@(3x-ӨvyLk?F⽣9YHDO R'0`C9e(?htl y=ҕ9CC&ɭto0Sw)OXJҨ(?wm=Gqo8v{&xF$xf_G&ctGYxJgY jIa8X Q=".Hc;>莚V56G#ӺUCv[樜-qM& Nsh/ϥZ4po8*RQI @ѻ5 ^V*ן)A 5;R{% ;wiUj/~456Xvvc~5I`}aYY+4gx44 Цi1Sx{mn"@oCTF{gnF:ez:\,(Aiؕ +]H gst=um<, g3-iIȑ[s֬:BsD CE ^QϘ2z܍&lT?@Y/ g<V+卦J:<_>ѦW\BE̠#Q퀦/<;_ &̗[/TqG&mb2 oi1'$\.Z, ;É"?!-g(q'[[êVν \XTG뙓Ƹ{܍aE])޽!*thŽg^Mb!C8߸߸xύtmaF 7xhVNпK*/qPj>~ˀDzxK=S3lV)<%nODڸ T3fS?a=_GɏkV:im3C:H~hUrZȧ!g|ЕDs cVZ2\cX2\UΖK fOh1)?6s T{#Դ1x@5^i(i+p/LÌE˨̋K5ĶÇ[*m?jF >/0>P$Jd0IҖWh/Y>^=(;P]_ާޓl茝;4AFTc%?=WF 5BYٍ̐Nb?aUǵ}ep04}0$+gq(Gx P2ezSqcC gP ^3-ֻh_cm /ذ.DfS>1-Ƅ#),N%`6ˣ_YNUN9ID鋇=ْeo?pQܓ  [!_S^$bQͨSRA`A- ][q i[./Q`M9A#(F2CL \ʳn.l!oBv\d'( ұb|#u'vY&3$ԇ3Gpȍ$\~ZV3ԗfaѽZ6#ʕ2]?8-zd"Toa-IqkmY#&Y1C>ߖ4U4J`߂kCD6^jSWdU s":Tyԫ1w³6'ŔD%o}iN-B4U9--f^Wt",rx~+>=5ۈ] narZn"^'t˕;"U b^ de繙;%~VrvV>.U4Z+l1ssZT-#7S߄5X_>Ī S, wn7`*b4yEY6N??f/R qL 1R=ٶk&Ö`=n 1"14Aϋ%,yѡ@unb?&!0{zqbp#d+5r^ K5j\3@Ft1^vpPK;۫9PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:;۫9 META-INF/CERT.RSAPKyc%./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7870000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-2048.apk0100644 0000000 0000000 00000010731 14763776540 027102 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg71q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s9G "`pg֐ J S?;`vӮ51~O2@k.{˙mu~z.֨m>q"zQIc+ete՞yrϼ~AT+6uxvw:T9*ylv[.~<K8_BUvg璌N*rMzzga0O;wbW*ݮ*tmŅ.kt-/owj_b<|: \y&PKhn9 PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:hn9  META-INF/CERT.RSAPKyJ./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7870000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-3072.apk0100644 0000000 0000000 00000011552 14763776540 027102 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7h>RP H0 *B@> RaA*;P~)_x7(D@ AA^$xCFH..?.HT r~.?:Ch[!ڕ+rO`.gԠ~s[SOn4jeO-NVR)eLѴTvt3*9\W: 8wj)uȕUL>;;9CA{.YQꍁ!Kb@;s%ۦ R}h'DƋA8Hs1oqV{p)da&b F[6$i@b2o(uNzkq~UaYůc*Z|Po9Iwž赕譞++;^fr([=5$;0?᪐1Rq@!7fuX ':*6Vd{J_<ۭ/mF)'c-$3uu6Jr#²#kO ,dKƔ3$2+µz L=`ƬW!Ac=) qsl˻3T|# OV/!Ekg7k/Td.[)VH t\o:# OMGVIߴ@>|hMrH(q^ k\j\]c%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:5D  META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7870000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-4096.apk0100644 0000000 0000000 00000012360 14763776540 027107 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7S:r*C:!F-3/yQFK^nZNOD}%͌j@?65iژ w82b s"l\z%X<Pn6:RgԸ(=XXsrcPkS/F sR .B򗙅T%TQ}e&VHA}i㪻=tx?Kzӹ90Re+D{VkF Lۄy/%Fي?LijoV‰\\ic6&J71UO9[RTdnMpI>BeAgPu+:h\nu}̚Vn *_X k@7@.  4[( dFvŰ Na~ԕHoծN uf]4嚠&KNCDiJGXO; tCưWw])͂ qu*aʢ,wxrCi3W$ /.ܬ#}MBX[x<6!zEœ."ĵ Dk%Σ'_ӗ(q`K5. aa7gS0vlS"_?|hGXjfM33dc\2KPPM^Oɖ  g^‹ [ P6b۲v}0cp̡z˄ocH[G*t67>`/\ :|n&BkZ߈z60נOQCX  G$7*GN|9D)'ua]SMa^,t^/SXrC?p\jcR(Co4[!mY7w_i$'Cv?ހMC >:Gl\#{I^hkBfr0.-/&lq 9e)whǣW*gQ3:9 -N^yHOgs{bZlM*|:UX1QƔuˤV$,x6=Wn W[/'E'q ڛ{i`ƌo˺5-դZmT:%2V$[/cz!OZn=qHS3\ 'j͡3*~d/hLì+6$=b(Aʶ6'?R6ϠщP/]0ʐX^ͤ6J^Fl">E>ϙKzICd})([MLPBnPKכ7PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:כ7 META-INF/CERT.RSAPKya./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7880000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-8192.apk0100644 0000000 0000000 00000015364 14763776540 027117 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}? 4>]vR&SPPG#D ƌ@a#_ w}~7i˵PCPK>%BPK!:META-INF/CERT.SF]Mo@; am bJiBme_q|dڅ__bڴ6dIhQc8+ &Kk+-W< qo=ci֥>̃~f,9 p DYw=a5%(O,K,Ȫ(o>--lpE&<=Krx0ur*f< ]vR<ͨ߻= s>>bg7u,{=<~h<" x|x D#x0aP$#@3Gh<!Ё  D ш@ğ  }1*rh^vV@+`T6y?`?Btx0gs !HBM<Erkɧc$XK/ODz5c?ݺ#8@cW-^~5Ү,oŤ )[?Wُ2x欹KwMj0SlؖFR7geCEe RUm=쮫mѴ{e/DEU ^e%U|Ñ׼&Pk>[v:eq=p8<Ն}_}OswI}}(PcaUhmM THOG#j' ``젪~e>15r0hgA+C5Tꊍ%|}{I+ T_}\`"nAI"*,zXe{(g*mp@~$zC|fT;3y=kˊ;}~wMk;L I MY!v2 BPQ2|#DݰXB07ËuFomfm(ՙi(wĥwY ^u|=&O"/r>ѝ}jSQMΦKklgy E3%6^/-'~`a%mhI膨3%\?=Ziy>KDhul(g0iL4gCqHS]{{ ; *8nc<3 Ou0,v=̏ sˍ̔ɂB|gl ΀!h: qYBt}@m*%5O4qM}<s9b(~5S*Kۑ4igm^V1a]hukddAIn~j>_fAqEb~kg03 `DjiOan:KDUH9.}O(0'7IKuyg1ƒ"I}0ײJz(k0_D5rj0iptxy9jRSf,^d͕гwy]mBˍHE)ξYyC 5?۱XJ;MԹLJ|PVte'<1gT^b!M0G>WTЬĆ0+hF1A5021JF+?1W`b .EuSYB_Ԯ}bbPh9U>Ҭ?<7c}4o~:@Ѷ*sLVzT\]'[1K#5a5äUS.ҀK ~[ )N!ohnU:ȑ_6b~ev/287Mm#A.i19E#gĝ 7nՆN~- mZZjL]:3d4ŵ.{`uɤ6 pRsкK ] dt9@ ]Sp"f֤[ s4SbP]<ךdzﲄ,'HCf= 2PHx*3elg$`qj$aբ,*9<{4 j^xEE'7@J!ĸI6"A`s EbƢx?G[͢_x0 0wR-.H'UNs꯿7KDfV%p5#ifC~7q߅3mlk_W NU YVTHOOyfRS2@v,ޒ>݅s {X=dtx ۑ=)m:#hnRCwuqQ5ܐ:JX-^)l eӄH|yj5&ܩbtMb2TyZ2%TdN.F4s1LE78T/vOWT(nv&\X=VCEY˲ϝ\#]ooF }K6Gd>8w:{Ғ7q-)Ii7ll$u OSf^l b!IMQqr jY蹾2yuXsV30>TW %M:ּZD؂ֆDH}e5ӣ=3{W9gE`l*|S r;q3FથmJOzDe'^ѾS4mږ' ynzxZI 3:@VU僧CoIOfFmNXEi/޽wcYk[ S1@y_\_tL|jR,4Ӫ!F\閗te-j\ +YWJ],R\^ 8""v̐JoU ^rZioz% 0ooy 27FrݱClsN& ɝue:xYV],Uq!ǀZ+I/=4/C)w( )sf.],YZ8$EQ|IZ ~]r՗x+V~2̤lȭ۳܆U2*h$<a!3id7z+8PK/m; PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:>%B META-INF/MANIFEST.MFPK!:yU< META-INF/CERT.SFPK!:/m;  META-INF/CERT.RSAPKye./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7880000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-1024.apk0100644 0000000 0000000 00000010026 14763776540 027074 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:AL$META-INF/CERT.RSA3hbƩiAS&Ll|LR E 41~LLL v<{{€OPЀ98Q@N8JŮq>ٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G LfOWnXۘo i^f/9YO{=Y#%ӂ %OךR݀;I5b'6կ4$-eRĺUf;J^WY|J&}6l?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:AL$ META-INF/CERT.RSAPK!:j META-INF/MANIFEST.MFPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7880000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-16384.apk0100644 0000000 0000000 00000023314 14763776540 027177 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:WAMETA-INF/CERT.RSAuUPFgaA{pww w''H!wުm>v %BBa" B]Ď  Ȋ"A@ B_OPv?3JA1b+F@Gqs7cq@)18aP(77>!7' { "! 7F0zM:gJO}ͿVҰ,pՇ|KǙzk=h2) /uL #鍥S>[iH2vxEẃ 5:3,rU{sIK(#x݈UGV4BP>"A9s72lNC6 >ľǧޤ6ƩuWp:52 dZz?c/8U[x3!ijoәO7#]L.P zyH 09 șߐs{^} Vb (J|ΏT7 2e#gW dE*}е JCg=b"(9o2ئ33Q~[`dɉR^~" ;M;t ŷ:u錸nD3$JfǰϽIhTf{G 'yTws+:9 &fϤ#UGK Gup6ĝۀ͒fBr[HX>260.l Q]7W#?1_tfXL~d-~jAt,01".F~ ѭM\nelq#\c\ ' ǽ\<*P6)ѕL] _H(8[:jp禺%G8XPbnN|] 3;u(!>*DL"[z1A"'ߴyzꤲZNVMyN1Um!7MpxRh|Qj׋2 Z-ȇqPI!Yy嗮$5 ?wSPl2ٝYI[*Đ.ekAbG.*nc%?Uxh8ZV Q&C(uW=kP['^dR' ƞ*fv+w;rCJZ؏zg/XUe$oShCsI㭃&y,E;;;_,$E7QGd5tam IAĖ9 ~ִr6|RKUwymXy*~ٟ[uwE QSWY8+4EЇʱp2!y& ؕV]i9_w޼(\{W?Es_!`'1,Fk"@r Y `RL &\0"|_Jo ' 2ϧ_VȬw]q& q)怑mm")ɥcALcn t`DbG8魩'D1Hjvі[3"`Y0 Ys8f:G}-]';|f T&(=C{IM}s%fރ+ar~dYv@,2G%ǣ6^:WukB$avnN'Z^Z3#BW9Z|`}{wBv{~L4a̸Go 8iVU"*2x)i'6fEX1Cް,>@s¯nZ9YAvڶlVƒNN(J@gW>1_|-IȬoNCo}\L2a-bti[vJєBz>'Mm^$:z󦙑W9 snvQr#g+mBq!yK|~"]x||5hi_+5ÑIkųlJ93ⳃG] NE :ilE68:8(zQe&P0gIs+*g?>¿̥9K]5 2_/mlVt{En]n1K]/Eͽu ]ЂA=C>U!i_-F~XaQBCdw%l2>$n/_1횾tXfi7a(q,VlgVNUS)n}Xrw9t// UJ|m)iQ~Qc 6eÌfH^flU:g{d1W:MՒ]eR}=g|9YMY^ )>YwؖӠuLO}r#} D,\kK;)awDXc7|^3HK1Z}r)[ ڃ;],uԟ" Qz, '{y6$pmJO rNE s=y,./3ONsN nOLj#D4F7@@yeEk݅m tþ3nJ6~B ix7i7fb/˟eʢ+%c㚠G r-l@S5gJ==an=]?lǹeȎGQeT2]9ljD u؆䦗cU);6h/ɳM`OUh[qq뉶I.8nYh7󰎂vK_][uDDLKaBXK!j.mʺ&m.ZbU>-6BC募4Le3Ɓ%>[`='> Wfn~1`y^}TX-.%IXTޛj(hλۛ&]8Gy<>~i F-F@%Dg2=Ruvk |lhT/g .|aTi [ opkv)csJ/W I:]c>!HֶFzKlO= \y#^nO-3{ ./Vw6$r`;)[TT=,Ulyטۉ1fz2Pȶ-syKhu$Zv6y֖.OEc*#"CjW dJv_|<1gFS "'6zt]^?%hnݽPX+Ռ:Tz?eY8M_q kHVVSv A@ͳMnEE+d#Q};%݀dټUp cmv*ϒd5vBٚT"er2'wC4rߝ8C!rPRO]RE?d.HB%A%y U+,\) ٹ oϔvk >_~ Cۇ9ytW삹NDGHK5*3#:8>fDG4%FhZlOS:&BRŧ*uҝ():~l"R4.bAW1g6]|z(|&c}dI3 [RIWRdg+=9(")VVt 煓Ș|B gHxeO' p#XpP-5&:9FC$k;rF^kSJIbqMMz4}EҥdJ4#M.EY:sywMn?CKx%mFAj(*) LUI#e; ޑhNjPpC,>w?Чz}£L#@n $Q!Ơ'VRPP^EW`,oh\E&zֳ=;0{+ U.=FSM){m|4j;ncڕ#f7>gF qr(dN?w2^NCVye^>]=؂\F,_~e T7.߇\!* Bt-1;Wd9gmxk M1sjO@kݧiB%ZeQg#Q.QW ު$.m,K)%h:媥4`K_Kנ| ̉{߄̗NXjCfRua(EA?f#Ӗya1FAĄy D(G+'lze5_ R úaC'P44gF"+L&S=YG~BtWW8ͦ i#RIP*2l5$:cA&k R!0,%U>}۱ug>u ogZ*p%ՈÙ$ٶ&d#^dJ_v)1fXS)[/U^!oB9-{Ι0g6/yU ĎdgF(g0#A#$?/?&V[X(`nQ!"1\; F h|pr0N(w"u%W'(և&c 9GjЗ 8s(*,-=/K̚ߙIY_*&2aIQ|R' edL[ KlCRX̺R6"6F?wx!۟cwCx֘vzHѺt'{'a)Mew9_0GCD p3HǶjpE~\u]i_:P=8lj|)y4 L +tX0%~ E<}ƌ'cO(<=l#Ziq"t?%,G4oBcP5wX{\*9r2-cFF8hQ;7@Ə&+z#L\i5'BÒ&'`jkX-ua]qwePK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:WA META-INF/CERT.RSAPK!:j5$META-INF/MANIFEST.MFPKy=%./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7890000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-2048.apk0100644 0000000 0000000 00000010664 14763776540 027113 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:䂅)META-INF/CERT.RSA3hbajhδ%נ%ѐۀUIqA_&M03121q2en W 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s* O9)!%++K#Cy+g_iM7ec[?x->h#k Sc _L\Gٮó7nM2mƬ'~[‘?#={Qy@ZKҙG]8:7Q#LݶROI퉺?Z\|3sRcvhnb+]=sy+pq:.?Rw\:}I\jwf.8ΦrPK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:䂅) META-INF/CERT.RSAPK!:jMETA-INF/MANIFEST.MFPKy%./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7890000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-3072.apk0100644 0000000 0000000 00000011502 14763776540 027101 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:rMETA-INF/CERT.RSAmy4 fƒq;#7,vKBb4%䤤4AN-J[c_FLftpZ2!ո]sO׹??< )IvHH` ѐ % q4TD ߂hO *xAwC@2"p".)v.&HRxc@FM}RFoo%P8i@&l ҀkA)BR$$|zΈ2;F^Sũ[ZlG}BMVM9glwW{֌ ^(Woc/DqScDaV [ 'b{HaM)~ UӚx8pQv ӶttwiSo~V1ϓ OTIQϬPꐴF`nY=YXu> \[`Ȓڝ}ȁ|j=ab!x*ՈEjyӮ"!1go+tUƉl_}\:Z &Bftk±sme=g4wV-M\PӀ)ހz ^=n=X/u"70 %!-V!,oyzz3:qjWjHXS H~P${(7VaZ^:̉<ݘϚ^2ycZ;wn`(/iQI$CncKt$6и|q4ɗER|֊N7$oȊ| 鎈#$C67-5@{KL< F_ 7vhb y 5)b Lw8>[!<*۫{R@'Pla٧ ,5p!Ǟ@:"r]g[6uD9RPUt!=..$STHkJ_X >6Vzs1qYlJYp6l jP-ՃT]M6.^/V.hv}'Co0H+} cW#232Kk.6AIn#snO8۾N He[Qݺ3v-HPm)3#dpr.Js W=ϡ^r)n:luC؍[{0=A(I*킽`Nep*Pݔ\9*8"+M* S` KGqD- 'Cme-Wh bQV=-)^G/U!k;[Jyj\n5цQecУ yD{?0DoeJ'[ 3E @8@tu(3G}cY;[qp)SE<붬xE]AϟeF2<1~(rPK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:r META-INF/CERT.RSAPK!:jMETA-INF/MANIFEST.MFPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7890000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-4096.apk0100644 0000000 0000000 00000012311 14763776540 027107 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:I3>META-INF/CERT.RSAmwT7bBSD(UP"0D+*+$(h *H TF(!  {h#R͇|{se(cTeAL ؉Z_  vJ<vF m:rxS@531U3!fx^C7t yaC-_^h4 |ph&?"&"9b`>_hԲnc̥XF&׻*z+N @]jԀEX/9\31 "mKʙ:}~Ny6AOzynq9yPKˠ&^Y;=Uyg#buY5}wG/i' WU;[\J{-2{Úr!-}I~w4ͷxv%RiY;tRf;mԈ5ZPzXM6{:.9cJU' <-vCW>h\n4cG#[S de` hZ)S1O}c_%moXs,=o_.zvA/ҙtnp#.boŵ&ڤr:*~X &sxB7AWP{=:X\eaeŭ;U]c],X~okj0e8Ak 4{Ԩeo.RxF?f3>S#dXS;&7 h[pj\8ɥhަ̆6> ,O5RjH+j3I>MvbxZҴo<<{%[nݛk],I{wpSېsҲA3/q6 tzOh_<9v,r95ϼF-fp2(Dnya?oQg C2 ӌ!}r2dotca^ݿ-v"/:0j#ўob a8_)&d<_iݒ[)mF` YB p:wίE6_UOYOoGc}C$evah&ז)?58*rJ5<+% fEҡ~KCΛ[1߾,]MΊGYOzV!~vN/نĸaʲٯA7O F5.l~H3ɵxw'[\?/)KlG: *6FJ;Ve]SGjGLVQCP˟ )i%Nsً&p << 8%ȳ9? vQf0Tg^Dz>0]4uEVyM&s2ĞEA8Єҕ4+ԶA!Qb*a֜%:{LiIBUhAD=bEVAZKB,'"[;|U2D ^*,YݨV[96p]oW{}%ܖ\ahplpDd&n7 ) U~TPK!:jMETA-INF/MANIFEST.MF]N@@= 0K]@E%qQVH4 ac^:#0ʹߋDM@+hE2R~: !Xx~6{H-*#5DQvy#h+Ҳ~Ԯ\p/1D[ݔwgq|R?0}?G` 㰑v>?tǛ4Z}"1 @F\wY8RJCMC|PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:@ META-INF/CERT.SFPK!:I3> META-INF/CERT.RSAPK!:j2META-INF/MANIFEST.MFPKy:./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.10100644 0000000 0000000 00000000034 14763776540 030730 xustar000000000 0000000 28 mtime=1741684064.7890000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-8192.apk0100644 0000000 0000000 00000015317 14763776540 027121 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:@META-INF/CERT.SF]Ko@= amĄąR/,vTd%M9y/K R7 d@ɀ@yg U^feeF|iG)d8z$oq]^ih0TD&9ϔ6< 61LUnv9M4ݽs B}5WVeoqTѯ'gˆŃ@>N/؟PK!:07D META-INF/CERT.RSAmgP HBB&%J JfH$1* H"4Ei"MJC)C=;~;Cb b`3 "<@@ D  HY#ҁ L%2!a{(f$BgpQA#8`(%"R dST@ȣ]%#A՗dj?%'ԕ]c!]u&U [J[L%t)IU?+5bF l'Q-;=OX>.E0rKl:1*G!eH/Ѥ{.-΅Uq7:}7_k[CF3BY,O$3#]3-Sd](Pqh _Gt[Blk!WDCϮJB{mF&w[QH.UL,Wo%40l%/_UnTҔ 1B\XޖxNu5AjLRC6/~%*IAJfRYT R"hX(?AlrqMz' Ô\S(k֟GpP"/tR5V_qHրЗ;D&TA <SԾ}_0Y»"IH2#C/BOW7GTx˹[_КR ddiRl@ږzhڹO{h\۝ ((EZ|_cMNSkۻ G#BVsN%EnuX7UŠݽY}]ZX? d_Z]#AheE-Sܔm-*:3OtI7jCZx_s7}޺SEY{3^Zh'Q:^[r2+XJ:qx" 7 A.42٤L6,Q.!2#_ ktB/GkȊn~<#ԤYȩ}LN9ZԜc6'([d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G LfOWnXۘo i^f/9YO{=Y#%ӂ %OךR݀;I5b'6կ4$-eRĺUf;J^WY|J&}6l:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWqrsC(08y!nN.n.N" n.{=T02Ϳ #cc Fjh`Q{mݦ&c0#ZE˦U/-U/g­,y@fܒߣuC]0NurY)oPA}V Di-JEGnUMSH4|Ksxb[En Je.L PA>OWnm#4Z~ CflZPw<>+kI~z+\=|{_z~MϳXɨoMp dg;r^ՠE]?%|_"Y-ȧ0"J}"t^ K cʲ): 0,.AImӛn!MKꁇKt?,et1J&"3.8՛~#'iҙ)F|U (go?i39 ϜݐirAD .z?Y a1GS+!&Ҿ60?uF!u9aK9H(_aAb1GLL4zCb[+^vwDfwiڭmB7Mr#ȵ;iy[ܛZ`nm~wvRj<DX>a62hvw;3}Ki`Xt}%ؘh`dMM:f^,RGƷZ4ѝfxe1#ZGE]g!XdŶBcz^4a)R+l̴o2G}ɵ#FxqQ.:o TIa& pfA7v0s0κL,j،bkkeƞ7(Hgi,\ 59񩒊EThd]#ibQR|&&TgլNQSI_I! sr#Q|?8D.Ϛw>x<&Db vT}EaLiq5|t䏜0x *uLOms: UEGI$1}&dvz ,,gSF !ge`=MnNL`AeЏ?3 y"Q! aUUj&|)jS~&hCPζ\&T*ꏩ>'!W~u k7ymG򌍒mݯȪdBi?* z% ]PJaB(7TsťDXx[[*OT0N꽃@7嗑m!i\'-vmE X+CV b%IMf$.K2!%J*82@mbl6>M]}ٙ)~uW4G> >U*|e3Q Ntz?s,LHG`qA.vV/޻qm_mT)\ X-ݓ!,I}PoįGEmU7F!XXh5xOE[.kEۺVp!b9RF01:G\UXl?sAx)Ojld _ pUg7c16W@1((}U\9R0dtUpjٱ̂)Y "^y Ǯ=%”%}Nm;N#ąDyt1c*omaQ$8mT"O!&qHmXņS70;6[5CBv][}xk 7_K~hn.^ܰ2;?'"\a7;\ K}[1:et4rydVQ}aw/du.ƬhUh103%֭tb@8@:DB@ "QdJ({є,@X!!@'BoQiY5  Q^QLuZs!$q-%"T*̢cFO Ua ta毅vbuP3ތ[ KbU!ЖXf܇BշaN.rz:8ni4wtB+NaW%,vIl_=Km23(.;\li#HtH9۳:V"K}k2zEJXjV##5BXJ ַwo׽xQ79Oz/ʐ qF[ʁ3N)hxWaecUomFmF?6'H(`5t>.qdg-M̩ LoߐF*n !~ FNʞ]PD= },}Mӫ-e\>$!&KƘaVRJ)ÓWvJM[O,-j.3msP K?YEŮHKͅܬ.)V,͛hC"जUG=>98O^1DijOk7gl5;0H"#6RdFO+zO3dXK[$=O]HǙXI+4hjטEx}t+83hĦ iXrm5}g+nؽQp I%wΒ]N!Ļ\L7PSR3m_nՇxʼn^| TĄ4BMF|?Uz~h{Ӡ0ݰM񁚺ĸLtmx# l Gi/!Fث hMZ[Ub㙢-4L'/"<|rvWx1!DžrDǠD6.!SwC:*蠝PFV Ճ󘘕7bhaU,k&/)yp rh8\ԡ`nW,'7a~?DJX]:BꄆEHӃsK])93m`౮^=m:09ոf#!wJha˔KkpMYM'yHDhɔ/u_x`.=uq {j=[L2)YM([h7C1sSN8)-X);2wtӝ:y@)qdSH'ٴTgGgc]gFn`BHmn)om U9dm(icǜaP HN.9l ET{] \'?}Ϧ5YݑaWsv|?lP9M9N[бƒ>^[Ss&CiiǗz)hJ!'вq)Mpen"kIXJuojM*?5&YIot FV#!E]_ F 64ۉƊ 2̘-\mwݙU,#M l( ;T1hi#F`ٷ ~砳 OW%'IRM>V0ŰF)׍7+MZ9vЅ{y=_&BrY~KFMmlK\R,BTZM춌Gz*ơ`sn:$M=v XJ#cY|0)I`j@|km`M,Qמ3O_xݷmh=:gZ;)+۫Ɵ5 ^%(2>oK<3N>ƣsf<־fQ+f0EsZ(}`Uua"+;`}w&U-ؽz>#gltawܭdz ;ϲC!Jvr)'a$ٹ)cKM(o" 9qP{Sa=5wwWK,F3O/5p6.@'ioū3ylȒCbQ\5 ,m˺C2AFM^1w e'Fj,P;N/fq(~.)pF$ID5Q\+A28G,͒ܦN3j124B"1q1?=ol,5)gM&PKC=PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:C=F META-INF/CERT.RSAPKy%./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030740 xustar000000000 0000000 28 mtime=1741684064.7900000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.1-2048.apk0100644 0000000 0000000 00000011070 14763776540 027246 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fo`s-G $4Pv޾̪O Ê2ϸ`KϾm;ӧ#^??9z޲+.-4mor_~I.PrʾP:[d醗/}͵]E%U. {=//]T OY_ʡGEU>޶Z@cmgw:n*LڷOyo㛧uZǕ"Fm:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW>T\s.c(YC@՜Fo џ,-4m^-.(8seTX 1PS*vbM*6 E|Ƕq#}ᥑ(~)qxZ2.zF:;E r{Ӧh\?A>ZQx ؕz^©* pX%$֛ wM"|$0"Dmm4w"3-2zn?cGtzo[1VٯK̏ˈ^"Vz$@%cSN(ڱ,jR R.=$o So> ^Zf"W4\-].tHd\ol>ÄBB!١J,HSS\ClYXx^Q'[W GjM~K(Qҟ=[vqՄRQ1,䋀@j*4&f{c wmI&V Pmfo&S3ّX*]y{")2B$Jڿ' WӢ̏A§5CTyNU_˧ 'GQWٿPKrPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:rF META-INF/CERT.RSAPKy9./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030740 xustar000000000 0000000 28 mtime=1741684064.7910000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.1-4096.apk0100644 0000000 0000000 00000012515 14763776540 027260 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW}^ ͫA"EBy' $P(ZDrVЫ08炀4|$Y HPKfJ} Bs(0( sG"n tA9 $Z`h-}m]7 6G~(s9 #9@wO er9$ҵ;? LD=%{p'r_TXҁZnvfwh#q;0Z2 [z9J3)\0 !f4F ?Nlc R'qWNκ~~z>.hSmr-kνAg*_#gF1BygcX(_Js3j8G%ϛ3DBܲrܸ>sm!Qk<HPBu7ԥ(|’[ Bj`;A!⋠({XY8\Vj8!Z6Nfw (8BUhط#v^-`}0eU+svOI,UF۬g] s gEZrǼdC[:vl0-Y٦fwM 5D =ʂJbJwcB}yK}"R1X"gڼZZ]9ɀK:#PӍN:<2{N̊W]S%vZ[hƼ>s`&+5|4ؖ!}Oz~q,JmrPJ052:ȯ];ۿYmVL5~6O̕&"/wTg)I 5Tp@΢O,\@,[OR&[0U3$$bS#=E6 ĆtXFvbB9w *r>'/hy0< S gRaskXMx䮲I4a*}%<2rWwfeZ]FWE/YC[ &CXTe'=`763f[[9JEWWOǮԐ߯ '"9AyhLا%ZVȳi$g3Jp1n,.z_馩LTYSiŦ>e{ŪCm ްS}ޔXBqUֹmYLH-o ~wg3X­ n{I!too +kUêFkhgѭJYI VaAO4ur/cC3܁حc'0'BIKt{EBٚ05Rjݩe{{wCITGa%KyjPN~l탎s"gl$T*(坫Sj %v/F5f*e}J t/]]=2g!xw:-dݭr6!rlEtY!/u\$G1Wi.r|q)VׅKR/PKV"9PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:V"9F META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030740 xustar000000000 0000000 28 mtime=1741684064.7910000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.1-8192.apk0100644 0000000 0000000 00000015520 14763776540 027260 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWMƯ7~^$,j0 8H H(fvM5ܠA tc:@@z djHRs?9N$; ѳ2aCܔ4Z^PG#TQu5*?p ~`ǁtg8 '4 ϩv$&ShA!7+ϹSA$a>S}~~4k7H/,[\p[(Јb՘W.,塚YT>|<ϐ`[|΢M@&ϯjٻ-<^'q⥗? Q7$D=:0GT񣆢]B›ZL^sϑ[5V0`wv[A̗Qᓛz^Kpcy>`c* F;; z`"$nrUfJs*JܴLl"L&6i%_[[>t_s ݊Kxe؝K(8]^ٹh)r˽NUg~rs,EPv[1GYTS[iĺv[HED_`KGI1l \,BLZq]u@"٦D'}ޤ} .sSbf;g}& @b V))e!(yLyL.{F#:\ȩHuUكAK12#FDZcX5dz3ځ؁lMYmM-Ǔ.Qi =Pf@a69m(䷤Z `>B'A_]5޻6 %$^ۂu{v7O,`kռnmkKD{ ȔӢP-2)mv9;h)970Q5f*<yT-V{ʾʚv*N9 @VcC3DAϮx2.DM {!tag_~XThp6LI iKP'RZ{vC(]$߃h)%?Oe2ie 2'm.FXڥi!\~ '{HrX9H3^5s903ű5a 'Wsg͓Я2%Ef:{,sL>(@|`}`qhk+y+CbHc4Zq6z( y^|G3OG[zo 0y#f*ޅzxw_{I<tUNר<7=OV`BT\88eOM[q)'J$ }OR|UC`17ݦ=1&D4!PNy'jh3V cuOF34nWǬ tȟٞ9z Zߔ4ַv%)t.܄4q *H0Ȕv{]ooa8T-C%Y/3 -L̐2} &l:_ ʬr6f*>ox5'x =zUZA~zbhNg'fٵER0UĢ7e8妾w]?? 2hv1\cG?kSEᐄZhYLfvk^1z^Wr2/\^|sǝ9˕zV(PPRmђ ,/ 4ih\zLCsd bx*u4\QU%Լ]ܭ{\ ~!?r-RF!q)!h+gDmR0 . [ O9ɼR - g9 /rEk&?q8/9sXkf*Er_"w2,S̃W'Y+D>[4 ac#P:a.1 Y%ʁgeu4'Znт)xM˓x.j>£(IU.C'c1BpM= m'Ǽ`)ne 3slC2Ou E_ȮtjR6%vi )\T`l-2%ʨ^:պ\&cP+lT(9tSEıd皍{UnJt2P #+hKqeY3|KFZ[*Ĝ<@s@ |"Mחۃ.(m[krpzᴡ:Ph4{";DNV'R(_0E/SX:+On9gL`R dL[{Ɣg)8A^z2Ǽr ^|1&/t~usCY rfj*^RSӭ1|hiثvm`ܔrخEa'vbY ]B{? ítR+Py&:φ+$ sgUSeEsQwSd~'E(X^ˈ^>8<ɢ%*bo {zbHh z^v%6Yg4P_ N [t˗txrRY\o⥔d+qLZ'֑uXz1aS*vdߛS5/O^V?}]qx,㬷U}q}W8sq"}SƐrvvK b%a[J5Ԁў*aN2.V3,!)ҹ2!< n_K\WnPK< PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:< F META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030740 xustar000000000 0000000 28 mtime=1741684064.7910000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.14-1024.apk0100644 0000000 0000000 00000010166 14763776540 027330 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:UבMETA-INF/CERT.RSA3hbƩiA[&Ll m,L,  41~LLL v<{{OPЀ98Q@N8JŮq>ٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G LfOWnXۘo i^f/9YO{=Y#%ӂ %OךR݀;I5b'6կ4$-eRĺUf;J^WY|J&}6l:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:! EMETA-INF/CERT.RSAuUP Fg`p]] hp݂,_Uu>_?BH#Xﰀ(!R/  <\hB!0Cp_BpBPwhϮm {Íυ BD$6$BPi= !ɦ"cݮ `8ݒwmQNǝ4Px& 5Q(w_q{WrOAA8.lL8OA _0krwB^S^6}["es Ԇ '|_>Ecl{E{#V\:PO08'Ĭh*g3Aj} S5-Ph;~q쎠&q]s󔒸sE:WyIPqgU؛.J~*PkE_A 2# }|9$E:a;FR_Y az,ɇn[: ZOTc-WOk #Jt%FxC5MTrrY|w4cЁ-/$MﳍHW$麂B(a u&)} ~$Ӭ*UTz>$+$$nSi~l}#Um Ōg()߯AVؖoG3$ZPc|F\f;E0`lN4P̉?Òt[+~8ݛDBuڣ."!M>Hc;4'?bWn]lMx'ONRZ;CBdbU/}&,@@I"ݝl}WZ?)Ez _if:a?=rߦf\ =BN&!+c6mlAEܻ,7ڃBΐGE=W#AK[yh5YUy˜'[ݨXrC9CuR]q[9Q.+#lZX.;c2<tqaǘgI8K>V`멹gG>.U1 & 9CZ#*ܳ["YHb7O 3)[Q5ѴBO4!pp458NyR^(g '#uHL. 9Byqսb^6?%ݒlE0}PؑťXQ|hLL۷}ۚ\^bS.P?͚lrx1̣ o7D}M.lكH"(*+^C <(jH\aJ狘[>̬;Ē.ZIjkehh?b4fm'pP,Е*08wE8d-ӗlQm;p-n_y_ĕc@)nƺ Ns1*J,};$!=665n~],8١jo lhEY\?kq|~+)$-j5Nf5pBb/Va ,3fZ@t_;Ns(W"[6tt=X؍#Yˮmtaqm# " .5.62L6V󂘶l 7X3(0@H\@RTTke=5i:罜=}7"r;*N-)DԾ;98Ʈ8^:vZx>}776`Cd3i6<{8][ׁC}=t{[s ?2NYπ냓Q6kK `1_:kQ9FecMJbruz5v8)/uU~@wHbX=N*G ri~!6k$Ypy-+ծD/Gv vL,+3 t7}1A$s^L _U75;;ϸelc%θ|ym׬4q4n7nِ'LQN܈%rZÜ5KY3A)](UȂmkǢQӊ'+t'QPrS|lr1]RoX3bʪQTH>y([ ̡s~n9Yj'nKDoAb'D% i|8*qɊs\%U~E3TNvo dkrgm=۵2glWSf`VLYϳչn\PTP`RҼIq$@UE-sY޵ Db> _<)f"T6EX+7Po,\)ڣ~#|kbQK/˚{M2}QxGoA -gFƖ- VO?l0rci 0e}baSyY~y*6RkANG0tk;zzPJ /L/yg۞ex)R/C>ō{{b̡#>W,h7ˍjd< Sɽk}W*Vh?)gF@d ^){2Lo7!ٍP>Q{U_%+ ^^ θmou ㇴ $[ԣ'gJ5-Zb^) ՙ2YeW'yz?1DoCtor6Ivr'^a.5/b2Leo,F'` I80b$SLEci+dG;<6 1vC#8~x̷{S X$t.݄@6Q:`xǠ3mkSM &%0 X6%qIEPhBs#dNvo |*|y|Vxd %.jҳsⴋ RB[ԾEwEew"5 TɏS7 ڮl}ٜte'(bW҃5նlrLJ'f]*q,5L݉^Rԧ#W.ɛ:ԮURse3Ogh} G:hNPq6^~mf>F޴پF?Wb.AL lCnTo[,$D61WpPnuVC LmbYxjqq+#5M_ N"() #nx1dzx}&Rꤦ{M(R2lw5ĉƭܯ9 :?Xߠ=/*xaR!Й(0b#leƫ . кZw&&lwq6G3"|" z>g_[gPn#K޲ՔsNB<~'SqʇO5R̷=`/8# f|+`:N_Tyͯ!;v+\4i`,cn`}M%tc8msǒqHBz ARB^UFKgeRiFD'n.~a_o ھ5‹̪8F]Z v>vI;_Q86hMAоq#X7-QEJZ&ao;RʩoclJ'OMAI?")4 pp* \4UW_)%HP*y܁ostXp/>&(~kt!x?9\vxٸl2mP(@xfx:YqnKso=vr۽%+ 1ԯhQS t$x(x(S`-^˯9WL߿-u@zIc~YiX@/(FNQ h!\KR!SM@&wsU<`Z> V$4N1F<(XgU@S~r7Us=\}}FAS z7]yx[)v?Lx7Z1]-K,lD~O,A=O7bYzPqt&8t>nl6۝+ߢ_pۜNd{>5隉[lAL䱆*7]pa)F U%gŷX _ ˴~J-o щ ?I;Ӄ^-HpxIׯY@^jG:!iZ<3F@MdzhxGϳo}.?:^*N¹I&;lkG6vA2),dUʣ&tE$Jᜁ>o.2ԍLCOo1#CB8RyOcp#Fe/BԵwFbJ RNsQ.HdlKڙb&r`A3nA`X`;Vĺ# }7l.1AUTT}ߘx2N̷8F>;#9h_;o$ wvЈRə65c#t;fkgf'7f:M!uy긹DcDwկAYCSѶO<ՁR,6l*HgV$2d0bb"! B^բCtm<ݔE)sNg *AujX,dSc^^$Y sfcjq爒7ń^E+*:Y!E6;J[̦W*ܘu<~6qi GhXyDL?WX6 @Ki:w "q'?m-2$g.ѶhրD򏶤/d{i}<_(xFfX=*Zm4*W6f /Et<:FPx@V NRR`k,.'Mը/IxVsł۠BR5G->J%$-,/g-Tk&_5A#q/0{1X_"ivV{MD,m噹HA"E4',s/>f=/ڏ!LO> Ocbܡ8D 5o%ɥ1Rŷ6Vtd`H]|N:i3Bъ6!Pzh|ƒMRvw@:?㶙B/E㍄Y&-WMo(Vɩ{~%]t:'[*f?0T0}u[yIF vu,Y]9qfr[dwГQ oп :{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:pr,META-INF/CERT.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fo`s* Oh99'!'+K#u+<=-J9&h֋Km5k4Yz`f:gM-o슫-wj%*2r߾/3 )^I?f^lγ0[LְY<)kܲk]2'+XsDNYݬw*gȬ=PzO7WNGpގ@C/&qpiҞ;VYӣ1xd/_emq9+aϯ/ol@e9gV{rqW$u|b۸^yVXPK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:U̅META-INF/CERT.RSAm{4ywfS5Xф1vRC[}X rP.tX:-+$%܍kJdS(ɝqb-tvN>cP>BqGAf F@0|qD!Qh$?d6C0%DH$b;KeOvuC0@A+.j$H !H%jjI i ""ٿ@#@HA0x/NNX%'_pGz--7VUMM LFuэr%b双c/g:g,oc}vl/u\ez&ldO֔Kp/m܋ULǪ@t֢ri%Kw󳇣_Z8IPRjBrӭu5'%<>s3[(fA =j!^lf<hFR> /yZhVBox J#D[5sDfMt?T`V0c+N`QmC2dz3d(f=+XzQhipMG5ƪz^+T̈́ ,)ѭM9IzlU'Q )$ s9۵w%{Z8f *.{cY]}+D=@@=W^=/7&Rt5.eLm)֢;$X1L&AvIPN= obWz@6%Ixs8[E"zvsFʑzf%|5s"t37_&<7]qeGF?A^ 3sx^mlKe(KdAJ߁gkG811~mP:<,$7gu(ReLwշ㼆 ҷ8|&zvsm Hn_ZEK7Iy@@J8.fDܕId5 8K،SevAĵ =kQGhBޟ,ҐBiu|d="\1yXQBhļaHenAcZ!G*7!'fW,K2&-[}djp'[=+x0GeZWR{P [?+s WBղ!^aϗo^TaǤcixyu8E2Y࠼<Հ%+';DX/o_87SC37Ndpf(Y@;ȩ7DF]ы~ L~h㡫LڶD.9z >"rTA#@9_PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:D (@qD80 UعQ` )aO̷?rP (rTu@q>5-TSTr@:j=9^eph (d݄+|m6e).49n;<Õ-(aPVn);?|ԷҀNK0;/{j/?fPnYg c{^ v&و0K\c:q&8~>QFf. q O/؈MDfJ&'|V=4g~|vGSn fӳ,y2^[4r=N Gv'u,HtړFh FɸTH.jAet{kO9fUYU*Y|gaAgG 첗 %`YnKPiZYMvG< p9^=waLGzcO~s"[ldUKQG.wDH .֡ob1a1 Q`I:niOߍ^ԯb*VR# ժ҇TkM򩨮pqpՎZf{(&7  k.y4vH|+\LD?Rc)Sȴ6Ct"xWSTCe;Ő<*̊OFe [y +k4H9+~. e.!\IF<Yh;˸@My,.I*$8Υ#Sr?J6O}gq#ݪ)">zyl~v[=yOkBkLSSӼȎU@xʿ/<x+ Zg45<^a1e,xm2U7ri\@kʰX7Jq3`V;֏+ Q+؅'Ti8x2Ʊ$+Un}٨Cv xZxu 6rp" .)e'm[: dV/;qrL] FJrcի#+t$EˬjlYC~Gey/ wWeKb^71u !>gSDy!su,pS`v7쀥}%8HINDlH;e8'Ʊ'4^c~+=p\Zi;LwRM(M\ 3Qwl3CǙw#S? PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:OD META-INF/CERT.RSAmWP ӈDB (=HP"HQ:*EAHQP騄&ň[@D:(ޙ;;xχ!!0B>xTBBbH_EbP} L A` Cb>Đ %`^TJ% rXv  6~rX5E (cjJ8kFUH>2$gD5th{'փt=㟻[ c?? 3QȀ["cavZˊ_]+@=J6}GOϗ!XUEm9^hө1}叓.:z/8c@?]WkCfnjhHEztj`T#qSSw~{m\mK+!yDK}9Y~*/վa;wSS!XW2SӶ(zp$UlpSae^U49)[6Rtp^oVIX_{aruw^nmpTd^t ZJۯќmJ0OG$a/(2Rk7m.Zwr-u  gs\ b9g1nAvJ K? F{;vc{ص.R{~t]SVʶ=1x"'o͑wXR(folkiDi}#ӛ,-]dZ7C.3/ Xrn<@;llZʏ,0"P-?䤂mXGCUOzYMLejN.+±oT3:)%n|njꑛeYžG^5=(-0%ISC* [O.3sP$I6B!"vﰷN>d4>9Q&"qצ7ۮ|Blnt*yK0G+DCѧ^z^˘Pz02,cM3*7gDNN <^$ /izNS:ŬD,{ nyGe_(('*sLF'B I#4336RW=mfq:^҂EwQ'M}iLN2뽸yB$;7Ma-*f[}e&c`pCn$^7C_ZP9gQ) w& ?j{LvR FvU =)쬋LTo5Scuᙊ(לWH"1~{wa+ӶǪވ)e*^|Õ}BzwWh3k2*(R!n8 m[5=e=(Z3~){6`]x.bO`{Ğ hJRPZ1}GKܺ~aK=c"`{}wѩZԡICR0i 2S|; ,zQePEp$(v|*G+锆:pώ@ɍ)J2 0lpʁ2hP$/#O>Iʰ)hkllJks$&Y8ey|< (iFڍâ1>wK`F; r =%~*>6cSP>Vy3G(*U_ zMx@>Zn/iԝ_\Cab :$Ka"c;j|*åg*G*6oDMox (Oz|[(SչZpv}Ko+ZTWcf~7T{7U.I u3 4 d[ۉFfٛ7r{Q/xO f8"(AbmP7ǒ?9 BnVd:RdtVmyb:s:oE-'Qh4w3>yKwؚ:ni"/Ko'Ў/ȇ'q\oxcIC)aa+1? !A,ײݕ u^mr93 ٙߡB~m}(5Pn,33׮M⽖$-@:(ߟWnb"gB 8ԧD`'X9̆wSVm_5 z>?Ƣ|6Yh-%k<2\;#6JCxガ^{'iERJ]RS-uT L%{IsYe[[1ұ陁 \8+Hko"ҥoԅ"TV<n2bˬ8F r22U3 wo^ȳ$K3& vMm#&䆀B[W[KNx[ɣ'\Гc C6T3Q۪%Qwvy)nʒ#kucp~\UD_!W5an0H+P)];ul)>F]KLJphdtw֍2в!Gϝ}\<ن}4_yޑG̹V~u,-c Eɖ2P+gėg#}CExW zJg3=~BO^E ӷvhO9̳&7caW'i)yO>v~iVht(!KWr.:@l]A ؜ûדxv3 6uBmhȆ4""SCbӴ'BњZpBfB:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW@71724v2562<_fCAhr>—ߩ]mra=oc:c6̗ryg{7?ZPdhΆ HJO /<_kJw'|rشSVWGӐPImO\W}Қul+yy_e^)aw>d2ٰA1`1PMhI9 Kc$5|=yOC`ú;Oa) 9+m3#*CSV> Owg1ڤ>~3skmqdc5eMYay w':H>2(kdnPK PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!: F META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030745 xustar000000000 0000000 28 mtime=1741684064.7930000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.1-16384.apk0100644 0000000 0000000 00000023523 14763776540 027351 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW(?]Wt`fз!8X]UDojcoSl,JV!2/T}~ӍTiyo6BSj|M/ù$A;ǓaIu]$ٹ1jG]+z2m4̲Iz+NgnFCpL14tm0@9:6| gtG*jzњL'̤p)@I[I!/ib@>qv?Iv@Vz+ڼO+,ƄP?&\ ePe0eF>cO&1㤽_X%hu+8IxiAK=ADEqۀ1{Xvokrbv`I{TEzl~o Bޑڣ-XxxcTn՝H+q9XPCdo̾m;l 1B_#9~yI'4؏KPs?IlVFZf- ('B.~Kcc&M\#~E<[6A~G'%g"AC"A+-yє͈usb /~/swiu FcEttIuű.GڔOTM:~7 t_߁nw":[nH7%^PhonP3c.OD6C'YX x!P⪇L# I|>k6)fE?Ą߿>ثzLGPAsr}QXC5)l+$ \P4w$OwKG$[Ra>v>u.V HbX9 |e`n^ !w)B>gq,SߵBESf[4ExG_Bt͋ӗmG;OWvOR"O4޺; UB\}TvqTGjâ//‡$#dM<~pۭ zt(%fST*O.V+u 2N%'`g5>8 D"-1Yi Ǵڑp;VO-^Cgw_/Jh^ ~Uj|^qذɱ 8T8ar,! "}~%l'D_cZa`KB49!lXE>pEZE2{^Ξ !kq;/ٶaByv*ݞ\(Ff"^|<[8n*VzD]'IS6x+i&"oY̚4uafo8pFoSƎ>}sp9Jner+Ng T!,@{P%Wc* + &; q(2er{~Sx-Z^5 .-D^J2RkWb7zTj;'WmdZ>˻`9O:0%qɳ&ezzS:"N-fUX)xkzMY- -Vdf';o[jvT/ehdt'V&V |mFO+^S,d^7SaLX, sƎUXQ9RHϗ'[Y;Ec7ɡYf\6,̟]%7K%"!Et'o7!$ hE@50(VbՇ'7sT]ͼt@mf|<E!#i)v.|n m9SF& Y󞵹ޕlPtpP͊BQqJ֘GYփPT<݋؍.p=mZŢ6oM#tbKǬ(efKR<ٮj|n<ˆu4"g|jB\FϓyqԮK@} $38|ŐlR$햮~ؽIh E`ފM ԭjy._rzQV]\m8W2$[ܸ,&>b2c9-ͰD:ufJW2Z dyۂUU:j3x2wѺR>/*zbσzƷ|r3ɍg_n__%\ 0\EL!{O=vï:^:5@^ro]To!lvҝx侧<\!IH%, WޮߊЭ1-yM^b<N5/¬R eo,S]Ga{̢P|~nGq:!n9ʉ9+G3qݶۼ2b,ALӨ7޾QS}t0څqUz.WG #j$׃L^$Xҿ FRD" $rB,k0u7,u(!8qoΡ.=-ݙO jednv4X -nrintn)cs'GƯ@̀(1bsLj6U&u!GZJbZ}";f!IR8Myje[]oƸ'#G a*VWybG{s]؝JLH/8ya2vAK_ !u DrrC?nUݿUu۴?d_htLw%Z6Jw]q0t µFnPdé+E/w݂1$CLi;zǶ-RW#D{d۬3W8T/^p H G m}c]OyJEA\\ “Ah qُJ?̛QX\JXC ;št7|?"9q_6|YeQ*~*]9[>J{#eǿ'PJuCzixTߡ\?5SB%y7-èU~ ACf庨}lnأeDzb\}BS47a)0<K'!u ޢbV4}(!_$u򞫕= zF/PߦR%MHbKƾz3/~(CTv֚FOϐg#6L{uq$;́AeR˸ mSuഥ4jT%?or$R1wZ^Pyd" E;C1~$38N\@zhRn^o2Zф}T`"^|; *Bb[ٞM"O}+/hҚ1+u [ETr&zIɓDwBQb~'fJSo#3U*,@ ΃_wE#c:MƋ|j1P.f+Nev#8a~NDͷBQxQ Jpdi%iŽ .4,Em>@.fu`Jǵ_;yU| N(cQ5ݖK)gף8t>|mK۪#j>ua(n=ŝ X)"@EzÒ]vkEy5WWطptʫ&@,ehXze\G%Y^򲃞%y(, ӆVLng[b)f %:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW-|{^r*e^Z)n)k]NA#6nwvkMSd^<|W*-O:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW q|1@ZLCl4 xqPL@+Pi]Ҡ0( CfRd8@>O / 0QPo-;x % <{%CO&рِ~C hܹA1yBj $Rus K >f6ǺkYv\X*;U?SbTkߞ<\ev}.99mjPJmӈS4>H[h/HvC_P!@$ݹ֙Iţ #Obr6#?-Jc0B^Z(a0JJ4iiBtUy]i&[pMK[W;VɜώAJK_Ѻʔ'۬P|x ^COmkLYN/fvCKd‘6Ƶ"NPEiT_\kp\Pd$d%+% =sʄX\tמ?Tڲ_sQ ڜe?kY9,f' Ǣ"c9aD8]b(,etp?Pӽ^Q =HەdhzFIA*K"4AsZA>5X33%D:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW Y*(A!B,t$ P(VD {,ap*E'2- d` I {$L %PX P\ 䭥 VXں2m (}Q02 d yw\͸Fw"\Δ2L][8p܇p4w8n̜`=F}#=8Oy0)Z}*saF"}_lO\A3F:zH}5yѳenU>8={áHT'fvx֫bd}ү d 61کy%;nz-ۤXƥ` Tvhc/9iyZu}?ݾ>,1-ukn]ҏI|y&iN|g%]#D/?~غXݥ['.:oה|}z9n%k%%?[56o9,%2"VҪl5G1TvA9xz.u6K= \@fGK7͌<kiWR`cregKt$DG18M_EcsĠ̈TCZldjOnA1e4M/w? *%aX?z☡ _QEfY7f[tgazZ'Ӎ?>*&T?3oWdvmID6)uϫ3!ubcz(.iQȶؐv30.d> 2]$x1RTs)A՜ekT;S{bSYͳHd/zZݿ%hh#T, r.)C??w?dzS)!.WY?ӎy|Fnḫ..u8yFfV JFZZc6!"&˗++Xye;x2/`D7} oOG Mӯ'j%rߡ6-2şEFX?ڢ^)3]TBʺǛ72MN)~.g=csJz)S)c' i7֟mT{|}MQkP0g* JVdiu2&6: ̓*[h|/ʭg2.8bʥhG=NaQ6NgLk_LP޺MW}ԥz{!_@BbK~P_]w[Vt 㞢 .DipI2be\.qsY\[0hYu\YIc8 1c_2&QUdLCʘQ\A`h"a顔-0P XPKvth<PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:vth<F META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030745 xustar000000000 0000000 28 mtime=1741684064.7940000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.1-8192.apk0100644 0000000 0000000 00000015522 14763776540 027267 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW-fg.-~aW_/'WˡA/.~J,R4[鈁O$V?9t=hӘN=VS|GK&^k].jU~yO,%iV|J(K~?Ӯ |p,-nsz;ڛKV5SgԛTWdA@pjN?Cs^k`aRcR eӎ6|'$]%TX&WɎ32SEuJ}D тm*}ƀ s0:Nѽ(q >8PN|r8_2K2nC$ר\O) `:hV(s]^'_>m16%b6#.mi,r7Oy漶 }x'j>2C}?<;\Eu҉Fw#͓tIxDp1x%Aˇf9lF*kCFީG,UWb!SԝM"V-,B NxeC Cpn= Ja?Aŧmyʘn3|yg%s):5A3~|Atw\q׼\4Yg?ȷSiTnʊJw/:x2A @:l`A=?S!.i xs "`S"@q?pvĐ%Ū/M`SK°];B\_F6o!3vK\wx3uL2 =Icz$hy`,oOH]6q?>Mݮ$J=:]R?$ ϻ,Xo?&4&)쵁T+h./ĞRuS?nPQ_Mju d0E'*K(FȇݣA6[ 'iH4<^ܨ.pmdfFmGm`I Vy2K*tHm;dX0 (Wx6{˲r9 Hӗ_* ۄP,J/_"rlN=uB6|`0.d;R'ͧ~3̖PUwf=Ԟٹ J_)rIܰh,rhv5 ;mMSLU(!j>Ʊ;L-Z{Ι^6lAV حT[VI_&DN dEJ^ydc\apMKD sUtn|*FE^/:ɶ쉗˂'Iy Vҥ4/off=gM;&d5pV <l'bQc5jG3FU72R/Cs?~tXzHc)i83%q LS}]MGvXd#P, aNc+ u:)ʞ!:trO[~ĪDFޭinhkcԎ_Fuw|+n\ȔuOp11q.sCi8ef 'Gؐ9W&c0@ #1gNuWiHc:Th0Fjq πLzd ؿc~S[^^9yPaDoI,%j.: ^l<)f}6VoB=PRm^zi#lܖU\QOރ_lͺ鍌k *7*nrmO PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:.Y META-INF/CERT.SFPK!:ϕ> F META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030745 xustar000000000 0000000 28 mtime=1741684064.7940000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.11-1024.apk0100644 0000000 0000000 00000010160 14763776540 027324 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:HMETA-INF/CERT.RSA3hbƩiA[&Ll m,L  41~LLL v<{{OPЀ98Q@N8JŮq>@71724v2562<_fCAhr>—ߩ]mra=oc:c6̗ryg{7?ZPdhΆ HJO /<_kJw'|rشSVWGӐPImO\W}Қul+yy_e^)aw>d2ٰA1`1P@&ax4%$Ɔ[?1.]|>~Yȩz޸ BDT4?MdJń]&LOm{t:+~_{|כJB?ug=)4;$%OwA.S{$:1'Nߕt5ӗ&B,sPK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:*uFMETA-INF/CERT.RSAuUP\Fqwwwwwkpww A; xTMLݺ쳾]q%DDaP  BI Po @DP80@@A(8Q~}t,W >.^[LIp rpsapsBi{?g"c3G"VۢY};抳oU? R`3rp^Am&(XG{TԠ&Oh-Ty%D a{'^7MnOT򅜥d *`GQ9@Q4$i*tgˇrO Z{~T9d\`gNL5LZ`vlj;PM-9V̏u{"˨?lBfh oX>^OXk-HkQ)&KG`) 9MLrypBOw]'4ampw'taC9s;@-nHG> J/fEC*R E|~gтh-y;ۦكfՂYߐ[DYJC>b"23W^$[aZg)S]CGssBo-cswwE6O]>pOdq`ᢻ!Plޣ8Qt& hl?B9 ޭ{$䛪)c EH1-wh93gqBc=9yoxIX8[v4?ӳI|b+fr=1iX!#b6%y "te>1ߒgKqd#bKKzk]".Ǧ|tL)Rw$UZ_{mڥٔ^HU% R4^ӊG"h'm.HL`4g 'X 6&2V(|KHE s} :f &0V~F# C0WH7T9R,UaED>S ڊpIr2hsaT21VŹ &uP_e"-:)'lNK !;Oeظ_r9k\r].3W?pϿ"SV E75qL ސ0c\ hi#ݿy;;6Zھ F/Vl-]_ u2y$;=O"dm$0mT.ڡ*.Uţ?r^aPb{#yED(ye4]U6[F2XEk()SUH9s 8%<:1CҜiq"#ybדkk~8(ثKOސ?ٖh}?Mqvu~%JYorC'42d;׳H`rީ?3~Z C9TW3Wv]?[iqI[Z(u7d҆Nq,{UrFj0xk&OOn._uID[fSD & x 0<_AAH"%D2ך wm@ߍν|WBo("'~oI.=+:4 )2ӭYQ@k Î/p1V 4IڤHc  ҕې b:?B7cР-r{Ev3uX[zLd^ ]S:֦|UqΨww{ J!?$$H }iP¬\^/-W\K)l*1K[Qq]SXkʌ{C[2^]0poUf7QwI9MDd9wOoU&&߶ tI)OoEܒ97[5X3A)= EU Hð#ȒmkVAY35auPƾ 3\_NynS77B2XRؒ5-`ȨKÐ>,>YyW\Z}P<'C]W QEe!nu] yPˬ_jŠ;u޻зvՋ fom&A^zeL8$ NUE-k 뷣P4\حna3ڻ|Kū8,-10Rrݩe-iޞ@wWdL_E'a5eX~KnG o.F:/u&_9$~6 _%~<)v4ly:x@j/p~'M[_))\)ϫ,d} Lc4OuDٯ@U\Q_9llc͆:Q7MV4ﵜTrz-DEuuĆ xr,h}iWW2By/Ͻw;f]¸=rz x:^A8=1֤]M-lZ.VJπ)旻FFEn?}{4y2'PQO5Gsz~^R5 0{>܅5CC:ɵp(2'-q|7u01 ]D Q{Xw1,ף;~Pk!8lٛ.h_6Y`xG3PIg>k.L(k/?urACϒ:\ت+.lڷw97Js"\5;>D#:$\ŋ0knݙyʎW52P>N2/8}]cmu^6q>~YsKѡv\̬\fω=ϣR|@q9FƶL \UYo;mhYc^D}K9QbUq';ЋizM-!ԣw!=&lwhl1^3 OHcD.@tFDcBƴ@0e$x!k[}E@[XtpIz4X1\%Ӵ~;3ߘҮ9V7*6$^"Hz˙40.#9^>>aWbS1z;߰_oT.uY6&rb :9 LVT=W]Z:Hx|a˜1ҠȷmXK>W]n{ OɯOHE[d.\$%GP MZ{dѤRPHCPzET7n_Pȿ-%^)mߧ=̤, (d+ʪ~׫E40:j{K2~CҤ5IGI7ZS{M6c /L>?ppRZ*xZْI%@IeSEG'hZF\BU @L_uUw_B Ec8o!(.Is{͓u'OlFx͖|%Bߋ|4}/&/wY}^;¾+âQc({ef)))"QX+-G4D+𹯕,BrlF' LO Db统\$i[O%>oHV"0Wo`8-HYlmj'% w3#eiK Dn']kdSG}o2fL5FO.L{D\X¦!5襲^ g8%LnkRF}b?/%'E&-A⠑@iwAf> w{6FzJȖe'P}m #VDYnDPÂ]6`ݙ"٫q+,#Dz2ʜNN{# -D1u($4Tǹx`V Ll4I}/,Emʋ2cu mjVo[e)J.eF#4B3mhڦWa+Wp5!h~sb p@=W(`8}ѝGJ7 -(Q"[|8M:Dk}\m';"rOO2;kFƆa1d'_utpʺIOgƏr߄d?12bx'O;&×.xS~gک`cgR GɈW$S禟ے;ZPχ4b}cO-La,6>A+sѮ& f*VpoM2{Қo*-%1+N4DBEDdBA@ ,m "zQ) A~>N˯b]ǯRͬdL>ZpѶ(PS P֢DTRCo=q5Sc;Yy248Ejq3C_#/TX ̷tb:Q)F.V^msuZ5Os#aI@.$\~3SU$٧#83adB;IEs̀\*MECE2Hj:8Im`\r*$]`Qf'hRÏy]s).䷄Y6~Ҿ1\W(%>$ Ѕ6n=-K\e=ssN{$tU{zx 2viW4>"mbf: j:/<& w1,)ɽ-qG{<~i7!,PL1r$C6R~3NF_=/K\\mu줺p0sr#|f]WkZ#١8U ^ʐ,^7:o~ձ'lj>(31> ٺ) |=yxQ@ $̒W*)'xƦp?ȁ)ew&!I[svNA66;zTl}n/KBLNr$o5i"iLrÝur#*`+"r]S-{dl"7w^)Hlo+>nY X2~FM[mS9CMrOU _PM󝫮$p Օ,b/E~A eM[{AԗM%l+[5X:5afs',iֲb_TLP:.8,p6j9{J+vu|qNN:.шD~ܯF1-sc;XgX Lr "Ȱ/Kj_ ns~9Zdqp8={SXF42 Ţjd|28< \3bkEB qbv]8=ZѫW;ÞlE:gՉ6KwlN۱lPK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:a(META-INF/CERT.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICX[G{Oz >rxyts|,~9k3'lssE4kFzԪ=h]YqջfNZp$Ǩ4F# Q/ }2s#{|Ol6^*Q*y˖lp3Y]zb-^~E}plsl=/J}"71* U͂Noo^86Z%nrPK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:GjΞMETA-INF/CERT.RSAmTy4 ز%cjuƒT[ -3b,QCN"-<x:F&[ɫ,Ҩ9z~=~"E%tҀ<Y!E8eAiQ J5@E  @uB|!W~ b^@@r'eE$$BJb>4 *+JA7sS6$&?ߓ G! '! Mt]y]$V6Qӹ47">tx`󫍩Oeo-%ԅ -ld@Vןkj=-m]nx[5x׭'tb|ȫYբRX|`Nhdm6} A]ɀ5ΞC#Cvգof8wxI L A:AXu!wS[ϽV\oPFmĮl0:NsWuID‰[D@9O+hݛƪ-ka :_Zoڧ_a26!IUѹV9[7N[Y-@i]WV m$[u/s_O$D%xc؞_+ XW wB1b)JJ],iUc`'sa^䦇x̖.nNbgLq Uɼ+Vmkfh=34 #,͔)o$4*AsD-~ZfwKgs{_NCx} v ;C!:#,3^YxɅC&l 8?7D8ٔI7Xd,eXQh$yoߌip[X)B8$܏}gWNU`D+76c?Jx[Ւ ,o~MPyII|L*D'O}3PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!:5L<META-INF/CERT.RSAmy4 Ɠ7Xk-_cXGl(A-b-$RUEТ-0ua}jG~s{s=D^2ObM "o4HP(XC( A"  >Hj> wBo!TЇD"+&x$򚯶6处P. cg!%  .D^~x@BFΈ~s;)lR1ɽi!GfIH̏LO=/ lUzH)I6$epC<^n6J]}Y-4:X{9 ף,oO+Hо8Wv //uc$xW;/AtL.ֿ'w3"%yi~v떘Md\ 5 dՈ{arb9KFǓbj8'E$άcؕߦw8dOBz2_M~+Vf1{sp5Y[q ߀nȋqv+e! _-yy.cZRiGp(u*(|>?  ^A/=qw('B+|Ա,^UΏSwJpf}m٩ .t[_kiZX ۻg άJ)VOݫ,c#(hiZo 7{aȓ%_R}AB6i%:4`r1mzuMgC 'bNXrcf "Ͷc$+*6'WyRWBŮ1tFY#}ˈhƊ%b^:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:}SMETA-INF/CERT.SFmn@= 06DHIP+Q"MLٜ==PԌ(aF&K#Pح4?41+(Eس,3EKτ Ň9=Sdv~®#m͛kc|aF9d^eI60#a&KoXhpXO>uq@kp.# ~:#R9*&_뇨 |񦰽ȩ® *;8>fG!GiC^,^P[VTIYܴZP0;PK!: ]G META-INF/CERT.RSAmy4gƘ1C{ k#Bd_#"K=4ƞ5nl 4n4a/)n9{>{>4Ӌۇρhh  H wޣA Mw@", >D,߹@$30;]VF!x9HEB"/hUG(!!4XEAh0#ARQkcjx9v?o%HqX[ $µsrox94ʊ7,tdidBn+99%aƋerK9EK{ 7zշ0w8ngW,]prt_7Y }~%5OR58$ma1XfnwX'7V2 5ݴa""mv\w%]VB5V vzE 6ndƃ BhT|͕LɁZK>kGGkwfb+FzQs5DHOh?.3łȸK*kP;]w*i8RAG <3zǏUz-qm)i߻oR`$Qq~pV@:*~Zjc4_=ڐim-x<+1זbq&+j\ٖ2nd]FIƆmcG-!MHo ݆{ΓOsY-V\\ʨ Ai iڅe||uI*E_]{h@4ic0!"Z{1X ?MI[{^(x)D.I 3l"'/viZY~lh }wC nen%0m_IHm/ꎈ7X*wk[ zz ߓZTߋJɩRZ}N/r|͐q:)kSJj(3jŚ̬@ZަЙWʭ=+xa!L|zj̎s ium&V<vr`"R\YVݐ|9E(9c;"wu6['k d;t/I3=sig#..eLCr>fDo[A~*wN;@4@aSD .vːpJ:k]e}kfdY^@nO6ay#ʂt? V<V/` g`dLW$C~#6[#\y;CfR!_^ rCDNR]U{ ׳b$>",$j`gX&ץ=Ԫ: ,?/G9i_Nk~ͻ(t`3% q`BByZ^!Iy6 ,r,F~,:ow''b][l4n5/̒xJ>|(趯Beku]tsEZvhLP{IZ;TX]Aʣ;ley/ =m2;q`gY0OUc## `_zXO&Qʰ!hW2d~5=KsP 8dxerWIl?*k$RjjuA(gR>4#?dKntswUb GȥGv9Lx:s;@Ҵ¥<KVzRw|Sk)Gp8OqiK+ƨޟ gb̙*] =YUwT۬ր!7XiKۍʹ' |e1>?ÊDrbXg/5&g5SVQۂSePPYi@C[yWe5_a!*]\!ek*;ѳN jEp7|Ym+; Wh8^2lӪ9gVZr^(YOwYI !ݝ!pR9VUNIjZM,<+Ha` ́JC [y\^!]ܨNn3$Z3M8:@ȤWZ-:͖%dNZ_ F8t,$T''_{h8aV&񺰙.cN#LT[oӌz،/dF ;Z߽d"6qVBoޯ'D3(Lꦱg;ĸ>Μџ :Z uNk^uc6>=)7] )sSWݷ;=S58_ ۭK|-:ȓZo.u@^kdKvmf\t|D2jZPWoyWr.Tc΄DiH6&6yjAH6`fo@^N˾J=`5dE8!cqyr>肊4붅Aq[_bXD^'2Kc .وBS&V״R1Kn ȇ~Y0L{9O.6>AEB?3R76,AXl8CmD(5uS);L~!<~1Ɂ"H}:ԅ'va$lvdX''請6x^ CM(oeuy|m}^*&ѝ9)P4ϭG̐&&hwAwyQBHә\|/+^PF#}39ݻE9F~Ĝ_~83&V>x@*[{mWf)kג㚭2/ zS늭jp>*O/ fG3ilr;d$P;ɧPK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G LfOWnXۘo i^f/9YO{=Y#%ӂ %OךR݀;I5b'6կ4$-eRĺUf;J^WY|J&}6l1{˹.6ODʲi\;PK#coPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!:#co META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030747 xustar000000000 0000000 28 mtime=1741684064.7960000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.1-16384.apk0100644 0000000 0000000 00000023721 14763776540 027353 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽcq Q}=!s4`\Hv VPwJTKޭmG^dM/O2f|=e@RP3^("[P{Q׌$DtPG­~b 2<9ًXƛ:wx6-Qcݻ)&W:0O 0K˯k┝VK 6f5! xsψYV<Q]aektB?lOBہc7xuo+2?r%V++>YkU޾^r Aq.Ў$ s$*;2,–Oϋ!䶀Mp(?BStےfQüQ: =4_LYWӆiVYVpfV C>$\qplsI򾱚6<4cZ)}%oH',KLEI_DaiGNvGqI'l, z?C[x+^-̃#3tڣy/#!E>opǶk~Ĕoz"ٚǛ ޟNw+$ KyPwaH>mA,Y_Ji w%e'Ia?.2|dӧhԊdrl ӡx/{5B3 yR_w!1&=@zҵ񘚓H^ _ ^S|}hJQ6M*g,cX[ZFtaGD6.'f6!԰ ueDzݎx0cʳ$]or!] PnkڳY*Kö@'_SM3JJQ.v3DV{,[7>O, ){_vuÈH*|%Շh?M&S~:8B)ʙup9x:%B vT9<ס H*!dY\u+x%BnjUi wMD0}p U >8*JJ&[>A>w/Va9`n~>Y1m2z^r< }Ӣ}UA<ꈪ)b|B?ƛ%DԂɝbꦫw>,lO;Ēkt^cgB,LѼmD $6D&܄E' 1hZ8;7mCYQpi"ktsi7 EA4cYr u߸Wj\Z'z@=;\RFL#l#Cw(:-}2ĥ;ŔQI6X |D^nqM zyYH!O6!+F.iuu׳Vl8o mk}ᴐP'> \?>Uj1JS \KgR@վҮrTno`C*nVYLzu~1Ğ,l\N sϕ,\^ d䶟;1U'ς{c>6Ps&[4e8G]%FjNEߗlQy&*JJH0@EwvUi#w}>]>(Չָ[ɫ-!0_wo.05"}5;#$+;YTo*uSW*J8B*ٌb_LSQl o3\No#KsO|Y[)1;zZnE4_{WN y=k1kz5Leuk?h8@&:7?%H@f.WqILWMoz 7Q0 4D !Ay 42յN{9{DnE\ -)ſuCDJ\4;E9 l 8 nt-<ήUlgoFOPݻ$N_jMȼfgdIgS5usp1fe t,NT|vMP-G}{.V#81ʪBF [gutFe6`ɹDDدҒdW8?fE5za=2[XcE{Yvo{Kp $4c>` Q9kTiuw8瑵~D7&e]ݤeCNwʯfV5 yUV [z 0+Zks,i2@_'Fήϊܐ4'_qIC > sU~㔍gv)O'LJuMlKH|n"< F4P2A?kc;1rrǹ7)l3J4{CE@t93^CMpV.2Ծbc/iw"‡Z4xk[rTӤ>XϘ絾G;pa,Yy[=>H(9iZp|[הYz.Q|qq@h ;:qͩIN.aeFw._+ǭʜKïbd)S}FXҦ.\z,|fij */a6юhGk;Gq9@C(6۶@7:cuq/bĊ085 (q mfVg% Œp&~}2N=JX:}+٫"*JH\̑H~wC]?`{x]& -a0P(UHˣK#k}_shWS]fA_)T`tӚ PgvWа–9z':&(A*@z݉?6 ͒ {Px5);O 8޴W2h&/tX%paBqUN*Wv/,!,w=c`O-FB+ƱU@LsŶƊ; #)Կc,sdCHmCO>LZt2p. +ᕵ۴Totf հ+ kbݸhdڇsBjz ;ء#qU]/>eeo(\!ړ{4cxƥ0hqfD1?^+jP#s&8f Ld ٢| 4]b3O0*w0"<MuH)WbQ@Cc}{ʑv)Poi׫R x:ީN!.a .jH`s66g]W;82n'1 sueIagjfCWY0 !xhr)m?͙\ `i@r]^Ln0qSݛt??u߈1Vk['iTYC+?J(iX%p,XTh-=;و#^N~¸F]]jK_v ߆c[g( !)xRe7MjOTYՓį GI"~ I_2li(W8,OdO%s3RB`^1Ym$)l;Y 1ݭJ6X]l">h02&M[ǥuF"($d㚿$i>;ht/-9 p.XRj(\Z, O~+Aͪ)Z." ?;w?6T1D@J|4|y.i"D^a|mҌ`":cj]mʸ"brވxRrؽ+r ЇD8i/aBCC\?:8?ԼBnЯefNlg5իmm$e(g$JRW,QH0T:ł!dۑ9< NmJ˜ea!h˞7]DJv*ݏw>w+]]_4um_ ] l0 )҂ǜ`9v~T>,;+^}q+]_'DUebƅ\qQ] 8Q'I>ie/1vMZ"tcFvvS i8-C- zǃAJ(‚%GEEe]ӸML hbnQjEUߐzhB gmL~WQ1yTG#7w3? `k&0w/HNi]H 6ڑ+mj4D/Q8*9wpn.sN:y01P m)'}ސ*~ԣ0$:Ɂi,e<1U>U1#"S:_¬m-1q?Y) Utrj&V9EԟC?DΠ]c!8r<Jc)u7(akKXG7}_s#D])Ҁ&/ƛqB&Z:^& mcvJ+Č+U#O׏ }t X0\hi}v(vlΨn+P>SP1Q粌)raj,ZfWxTN刜& qrR:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fo`s-G $40%YI>d(jl4|j#vI7<Y=ak3S OwUO횗gO&AO'{[D~vkߝ>?܃AɩRmN3 )nsr%)~?Dzm*}&qÔ!z*87GPʬ(ykW?]5q |(pkM*IR޶D켨{PK]$PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!:]$ META-INF/CERT.RSAPKy'./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030747 xustar000000000 0000000 28 mtime=1741684064.7970000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.1-3072.apk0100644 0000000 0000000 00000012111 14763776540 027250 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ)By  PDm$H* bK 龁ЮN D\0a(_ hK@ 6ExH`@"Pr#9~:$A*1adktʵ枓?,5jjF_#ⱪsmaIe /x˴z*o-YOwzCs4Eti4l]WGu#pߘB~&)[F x)KQkk󉖙Wڋ #ޒ.i9ͱ'–pc#g?SD)^*dX._X>㖻}fD˷V*B>׋|Mj !l޳Qۡuo/xSf\/;D :8ʍ:yzْ$=s(sb[kRmҍ}d -1vgr2PJN'@鐢pIxmUnTêzPao %bp;?<lJ.Ć,ؚg~/dݣ O*2r{2Ÿ\3bF=PVz4&>?2qԂ.aɜA'gT;^%|iWZHx##l*2IL#1d'sCNwQ,YKUd?ߤv K7KXQoCXo-S1M>v=ى`ug"1:[÷?P7L[awn| 0J(mQl'Se g)wviOv[٨KY7@B2VtIU.7e"-n1ƣ=.sY^B05 OC"|$0$$ F,5aOi*w,:(6&T`1̄= SFmC/EɺXXLtkm'tKіF_3s%b:&Մl"LDC'WyJL^?n۝`,{ STK܍oխK^^RM \г{2~&zl`»Z;7}! \_B*ѺB5d-/9Tvhy =PʲXT֯lt6Gql#%ظۭ֒htQW~0Nן;EǦOЈ2#-0Sőr^;*8]$py1%c_PK3&CPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!:3&C META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030747 xustar000000000 0000000 28 mtime=1741684064.7970000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.1-4096.apk0100644 0000000 0000000 00000012713 14763776540 027267 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ( hj_6FK9sw\ +[bJr WH P4?vB=apP  ĀP$UrSv?BG9A  qSQ5@)Q$Z`huU-5 QBkv{9#9@!=?B~}0$o,0phc1BJIqyEz!=+ ɪRd::"dE@ŇJ>n7X%@)ۻAmw'E$Ϗ}/^js3'1ZmxQV3jͼ֓cQ,vnYVj64GL:g0{1洔>"YzGKkU\]p9}.gfֱE#:?SG|_Xf0!̿p+%:>O#z4jM˓jJ'oEj*r|K5СGfqJd]dB"|_vWNl./IOeigo˟u'(ѧlՒ5z}pptg蚤eK=S8uT>{G|!y3*]gm ;s4'd~o2|GV6Fבdy";kx/7@v-ZGՑ䃋vWXB%{੯߲p1Pm+ aP Z5B(x;!):B!+ ,˛VퟪW@Ƌ?ں2\t ^~|Nݚ dPZ R3>0cUA>LX/Egy@ʆ'}(рk.suv6xh;aq=īܹuZ+\"}W+#Cu΁ܿ9wwН [oɹy;i^T2uAߘIѿYiI2G>I }$-aT+:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ0'z6UY"q K?yk RG~{]ff_E6/nj0`1j6u1Oo.g.=z6%4q}직6ٔLĒ蓆{}>y2?YK x1I$_9i?H AG{-m=d}3*J+yTl{KE!w8cRĽ{~/8뜣EDH9+m'r3ȯw"z[겪m7 O@eSlcl>X\@alp<( J,''OW C/}Q'令ؓFO2)9UÚ#&/W3;7\aITN9,`Y"gꆘZ{oMVí(hz8Fn( q( NVݟ_ky]:ה=PWN94{/|uIo`ΣAȡvdQ{ªQCIX97i+ W/`x nu0NämJ5 ni#R 4=';rPzG+'jΥ/&xМX^QU6rUwF$P,dj([אv* hoѭ&X^R.\No+\Ju_t"Is^nG Dde8$AEfq*lQh <4-:pF_H_̥S(~jV.SMxaxw䬲KRo-1L %?F&Ӭ?7+*s_Oי'V.S쇳I)<b*EiqRN/WzaV?짥}Pe[9<7>h͎,l'L|1T!C2Q@ SlXה@nh|-E5Gj/ 9,?`'qFU\G_P`or-GU0ʜ+1 (]5+ ZFuZ+׉*o!mBvktWyoPWU[Ŏ(T;OM;/WAbQ?1kɓjFYޛ}؝mn$;Jmh]U7uΫԅ;@nXQSRv_l>"81}ŦmEpjIq`3}>8)tN>rdw'd ?!0l%uc R~*,Md.a`ѽD<,tXӅXė@7c'] }/rmOAy | z% +MJKeFCionqZeu,WK2E{qc;Oxt5ZU䮖,2_fŗQL@lW͝#JX'VܨZ"%QcpN{ډlQ4Y-"&x]c!g䯽90䦪Kj%)ձݽ}/+,ֵ-,[] DS_mnf7wk*+xI4Q%ҹ?v M r")Hֹ*1߉z|.9,ڰG%c=SJ6V G0}o1aM:kWk*,PPAQQGFCŐDD:(y܄vc2J_J%B'UV?Blkka}>\`G֧Cˈ;/0HMꃳu v@U%L3_硤S+PK=35B PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:Z"Hs4 META-INF/MANIFEST.MFPK!:ile! META-INF/CERT.SFPK!:=35B  META-INF/CERT.RSAPKyE./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030747 xustar000000000 0000000 28 mtime=1741684064.7980000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-1024.apk0100644 0000000 0000000 00000010363 14763776540 027334 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:L.eMETA-INF/CERT.SFen@້ .`zVP`Eϊ+,*&m\f H\k$f p`(jP*L#A'iMq*D3+T?KTm,A!HnDQPsK5|Sd=yBϘ9b&$;M[**LGmYg;yIK989m9W'ŤέwcLڜT6hqR~ŤClT$Sߪ<z?0i|Ln[%|ԣ\*ėpjJ5bZ'܊j^{LZw&[)Y~+IPX#/xyC&[n9x OPK!:lMETA-INF/CERT.RSA3hbƩiA[&Ll m,LL  41~LLL v<{{OPЀ98Q@N8JŮq>ٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G LfOWnXۘo i^f/9YO{=Y#%ӂ %OךR݀;I5b'6կ4$-eRĺUf;J^WY|J&}6lrs?=6{ß9.}aSv|ץ3p/MnGFPK!:]:4META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ META-INF/CERT.RSAPK!:]:4 META-INF/MANIFEST.MFPKyd./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030747 xustar000000000 0000000 28 mtime=1741684064.7980000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-16384.apk0100644 0000000 0000000 00000023645 14763776540 027442 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:L.eMETA-INF/CERT.SFen@້ .`zVP`Eϊ+,*&m\f H\k$f p`(jP*L#A'iMq*D3+T?KTm,A!HnDQPsK5|Sd=yBϘ9b&$;M[**LGmYg;yIK989m9W'ŤέwcLڜT6hqR~ŤClT$Sߪ<z?0i|Ln[%|ԣ\*ėpjJ5bZ'܊j^{LZw&[)Y~+IPX#/xyC&[n9x OPK!:l9CMETA-INF/CERT.RSAucpwvlVc[mFXMAfǶwƶ٘s̜sν?Zϻ~?rE#1GG>yDD@ nr4N>NAnnNC4n.O*``1`#0u+h5&3v%tK]G^{^Δ{0d^إGdCWʛEASGÆ kt&Y7wHO@Z"x 4iۻ!,S:8 4DyudFB9u\!N1 ;lkSI5eg[N U xPGwM-JKJfšTU8BW. >aJSCj?@r@XL#kː"y/a`;lSl۲zq2~zR4Iz+NP01UT RrY<<'T0fw'4Y3; MnT1#Ug>sr/pARu|~g1(jJ9L2L3+3T4ZihDęa澈# Ygҗ_DO_B-IF2)[D]f;PJXDŝiЩz i>M;<?%[ѹ]prLҺ{19;BS{7,.Hd+J$usAG&"XQa"b)t 9 ]ė^ 5TŴ4}Ĺgaf{y,o]Y2pB#g+!ؔ_3UMb* )o"&w(5@?F]qM$Ս%}i*Oc}!¹;rI c/&qxM_P 3cZ\dW{+$oJ<5#E¹V_xpGϣ[;h{1ʁ^7Hws%eQ8]Q*/"TbP巑!vi<N0@7F*XWm$wK̒wѧ9PEt'rmmfbE#>E:pfj(?x4#.6tx RW_^02MU_}OۄW@aO1frUу{=K:aNQLf}a)BBl_F4IX@k/"E>y7L96JwLP-|LCm[!Yԗ0!6dN[7nF?ASp/*UK%fjWx9בĨ_M \+?e\ 82Cxxm K檎?96\ܺ(#D/[Z i?E9q?qO*wz0[`+[^~*hFdSľ'LG&cQsKxǁ/`"HydmWWTKs6Ϋf_+Yđm@..LeTXf)rDGzਛ,Dy[vfAjS7eW(>}'x8qaAEk[")n"Bij_k/wlp,R1ֳ2Yj=)dpU{L;lTG}$Zp_]Xزˤ ZN<{hÍt_<|TRX&t@r}-]WaijYxA5o42b=}O^x7PKrg`WiI WнP ۠į".1l:jۂzHql Au;g5O&]͚Cy|]R =i_D7|^9/n߾Yݏ^{u+~f5d"|cwoeSŴtž~٫*mTz/Q<ޫCi >ԫ%-V$}xz~u~)&?{W ЦIMxp RV ǻ\6tqvXwlS1!q$SPø_/D r~7\WˡИ$ᘡ,Fhv M^D{m7z#9XP>Z_ ^nj:ڰ#3sQԄMz;.ÝJ1*4`( ۷i2t\c.A+)rAp/(u,zqۉַS\8%=^]LzM[o4]}'f^1*-7M=܏EMG]V'֦tO fΈ|bEKچ4D=xzi={;+!}j 6b}X W5>)=ֶZ@ˏHmb.K9YI{&z}6lw'Y-(̲n&%3ENw,}`gd}ܯ at hax1̑|'p'*ۇ.s}:m:4kI_g$oFxqی[kpKUO+yL@lV! :jSd)c9|LmD{et=|;;ZiruYZ'jK /w=cTYr0['ټE4zRM/;:[kBFV ' %QۇH۔#؆RgV/,5ˁddRF0H]jzk8u۴W$j]Ś]ɖ+-b͏SiUn*>Խ,#/ F] v~vIeeP)/ SIHiF@寔BֹgG؝X?(;R%J/Not GTGTUW_)%p0pD3t1>#rNNY)Zt*V5+"$Am)W+\P*>1\}2\5Hq*^xUՎPhNH1fViF 7J7 oػ){!g[va}ϛH`ηJI>$c}"Ue Zm]D]0.I1WgIx2wNJ*0޻iq%[3{q=ܛN:/k[@.EКQ1 ǨWS=/G}TDe҆iJ>M#sئ|>O*(0ޘڋ0+ #P2{F{\ϙS9[S H4Eϊv8y5b#T]TAr|JZ9$U  ncw;$,zoD=KyhQ:͊.۰)W`C6&VΦo{z*P7WŪI]8t'I~Dfxewg+_ R$[%wǤj736sIx4qul4 OQWhntIzK_z13Vs)h1Xgŵ 3RO Ee{HRx/q%{2aԳ'>bXI9Ol:1DvAM(J $_{"*W]>Vb0ٶzyK-Gl쐙8k{$cĮ-CO;#FAl^Wb[k͒Ǡۘ\LD' Իp}v~H5nAy?Q?AƜG:_:%J4#q_ r9oNy\px!_ƛbzSlM_(v 1ԉ.|C>{ju~T>܉Tl⃕Z0ˇdNR0渻 ":U`Ҵ2GB-6L*lPvEd6+5ZL=ؘuFqTxu^l|_O#={(t(˒fzOG%U-`umĥXnDNvh2e3ёv׽L'~6 dQ;M>O{>}qy X&uݟ>.q.XGO1[w|b 2 IW~@^TؘI`.&10<U10d WIyqSbfp7W1)]GRkµEa%Ӭ7j6c>FUX}9YUr?V`hm H[]%L{S*ӜpRt ifǒYaXIҔjJCho&iElͭ`z*^)YW JcφXv\CV}UА@Ow󖟒o&"S:bn',DeC zHQPj7dЋTjѴD_GyW#33,-$3!Nhs^FV28q\U`HܐCm9·*wywqXLop7}}B`T:zؘ͏(iX{̤~٢KCv,hD^Mbgɂ}c+'q6jRyJq4ڷnNǯe#KdSq91m:ˠ5^nkC5+ nз NN>З%5&w$Rme~^yڱOjCM"H˼SOxЌDܕ_ҥ=OMŮVOrzDgHX=aŶ)gޭmYPK!:]:4META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ META-INF/CERT.RSAPK!:]:4$META-INF/MANIFEST.MFPKy&./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030747 xustar000000000 0000000 28 mtime=1741684064.7980000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-2048.apk0100644 0000000 0000000 00000011215 14763776540 027340 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:L.eMETA-INF/CERT.SFen@້ .`zVP`Eϊ+,*&m\f H\k$f p`(jP*L#A'iMq*D3+T?KTm,A!HnDQPsK5|Sd=yBϘ9b&$;M[**LGmYg;yIK989m9W'ŤέwcLڜT6hqR~ŤClT$Sߪ<z?0i|Ln[%|ԣ\*ėpjJ5bZ'܊j^{LZw&[)Y~+IPX#/xyC&[n9x OPK!:`b+META-INF/CERT.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fo`s* Oh99'!'+K#E?3%j~~7b#UUeA/02gGhhd̸TyKeÙēe }9\Xv5*]4-XNc n]NB U'n2/y$GdQV|6T[H罯jQ*S ekѴ{Rdyo7xD0>OA]>q&E[d׶'}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ META-INF/CERT.RSAPK!:]:4META-INF/MANIFEST.MFPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030747 xustar000000000 0000000 28 mtime=1741684064.7980000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-3072.apk0100644 0000000 0000000 00000012034 14763776540 027336 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:L.eMETA-INF/CERT.SFen@້ .`zVP`Eϊ+,*&m\f H\k$f p`(jP*L#A'iMq*D3+T?KTm,A!HnDQPsK5|Sd=yBϘ9b&$;M[**LGmYg;yIK989m9W'ŤέwcLڜT6hqR~ŤClT$Sߪ<z?0i|Ln[%|ԣ\*ėpjJ5bZ'܊j^{LZw&[)Y~+IPX#/xyC&[n9x OPK!:xL5META-INF/CERT.RSAmw4iAD-0J%eD(atADѥhY=6Ѣ.6Y%і$X$fvCy>Ʋ <B}!&< 79pC p r !!K9eˁ+p aDp rQ&?5H$4 g4UA";@ ۟'! Z[]"vP1< gYD\ Bg5<[o,m^5)Ve?DTՔ1o|~h[J[{aF*lf˹UTM0t]BsloB*U˲pv1֗2kCݪHiIZ2^:14ť;ܨ#pbykNwæ[qX:Ԗ<FQדtv2WO0 x\9ZQ=ӷ5 uѸ[+ z=_P|ilQV> h 5Yhxt sg/ShDU}K*K7] #vw$I-B4gEeo{ba8A3Pl:cH$}幹;;;LYljP[) D~:(_9(āR[$jdG[{sz4m> އDUMDWI⋻DijL.1g༖e]}c{>Aɋ?n &ȏFŽdlpS7Y\y4;q#~3ѵ,)g-OJ3վsd}6’t(&NuA=B -/ހfR\=cm?5@cxr53x;a~~K#o_1٥6iaInm[VxuJZgPe {8hz܇' z 0 eScTac ,CkRI, )u?ַHImtqqow0;~S_ZiMrO. ғ2m;x!4]9XooIr!;}$>1-BQ[JST5Hۿ_Нr\06"E-e;RɣF~R @rMY1S\Egj,ғNiSIO;{yxzd1=eCPK!:]:4META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ META-INF/CERT.RSAPK!:]:4'META-INF/MANIFEST.MFPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030747 xustar000000000 0000000 28 mtime=1741684064.7990000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-4096.apk0100644 0000000 0000000 00000012644 14763776540 027354 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:L.eMETA-INF/CERT.SFen@້ .`zVP`Eϊ+,*&m\f H\k$f p`(jP*L#A'iMq*D3+T?KTm,A!HnDQPsK5|Sd=yBϘ9b&$;M[**LGmYg;yIK989m9W'ŤέwcLڜT6hqR~ŤClT$Sߪ<z?0i|Ln[%|ԣ\*ėpjJ5bZ'܊j^{LZw&[)Y~+IPX#/xyC&[n9x OPK!:wxcxBMETA-INF/CERT.RSAm{<ǷfCk.(0RZ-D rKňaֲ3h%"uʏ&+B6\Ҩ$r9z`] OZ"Inq ۗi6hޣV2??U?W-Hx-01i$I]Sӹ2C:K*}3!y^ě^zW\ge6! Z'--ӇV m;>9EnSU~KPvdه3+~XJs/e/OnSwؤzžBW ?clD+gb;,1gwuC}⬌K'+dUւV54 KdWǞ:>Mif\z42}P{>S%諒FfS̅+-+k7kn5xx+8FW67ϲؿ|M][66]Psj2kkfʩL>Xbh]x >BAWPg::p5%TlO[5@P4\Ak(4{ȒhrӢ<(i[[iϐsGI@"\ʖCd'!B~|C g1k.qB(lr,dU]3~ʷ$p64AITZ&i Z_τʎ׹c6YU{|@ThvIW?w΋Y= /UKa-G" C޵ө<չ-fn)sx k89s92)'vX ҉CYn]F{ŹOugE0el*1%bfg`;]fqa%@Gk~rYۏﶵt(i& I ,_x\56Ka4A?]zН"zO,6t(iY7E #,T ƿ.[m( 5<+PןygW'-Mj4#Gg- I'vѬ˄ۋ0_JQ6#R yw1l+aF\X%Yt  {g)AlaMCP28\=,gr#Dt g`G_"˛UcU WA:T](I$MaB-H*Z{)H5f}v_hOnazc? G7J*B/[FQO6;3:C4j[:v!;FWMg>w9%)@8r@^$dl{所˳!5wt`Sd-ޭz{U{Z<kQ ۻ^M?y)v+5c $q:*0ȦTPRL$ݣ±?ĉW#W/~*ggGfavɵ XX旞ebAT+ڰeaDͨr Wǝc ^9jl80D{Һ{ҋWC7~ : tw/2xW{0)94b P~-ayѕoVo TCĔ>k}=_=]S^sPK!:]:4META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ META-INF/CERT.RSAPK!:]:4META-INF/MANIFEST.MFPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030747 xustar000000000 0000000 28 mtime=1741684064.7990000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-8192.apk0100644 0000000 0000000 00000015653 14763776540 027360 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:L.eMETA-INF/CERT.SFen@້ .`zVP`Eϊ+,*&m\f H\k$f p`(jP*L#A'iMq*D3+T?KTm,A!HnDQPsK5|Sd=yBϘ9b&$;M[**LGmYg;yIK989m9W'ŤέwcLڜT6hqR~ŤClT$Sߪ<z?0i|Ln[%|ԣ\*ėpjJ5bZ'܊j^{LZw&[)Y~+IPX#/xyC&[n9x OPK!:kI META-INF/CERT.RSAmi4gƐel}AvRH" 2 !àKeYle2eX^c&[d{kn9{y~xh, cǻP) F Ʌ=f@A z#i">T"߹@7bar3AX1*:H]u(?Dh"u55uƒ% `ƒ <J/ s^;Shqxܼ?&kc1mԜVLYk `oޗ$zh}>($P=|sՂ j |auuY!ҙJxm~$/uJx>ˋR,]|t\64k$QE4%>|kUijGE_ua-Du$s{Æ큝0v|0GX.]ѝ6!_u)ʔKFe+&եM\4}V"u2owyJAe6I)#F(deNp85UbԂDžԙvKzwr{8=rJdhm7LTVOK f8ZaU Ri1`Y[k?QdbnUl2<3Z۶}ZfX͌5Xi'/j'Cyr~ u&1udU&3LHu e"z' ԭL~}*!W+$KИRPs K6VWE,ܻD-iQQi|vݱ))ގg|/_ԘfV\nb&0|.`0P@^]O(=.6~!>(T\wӚV#E{{GMs)/ e uL~Jq>8<~񜢋 *N>tok(z";>.`2c@w;Ēoh 7e%?iiP`xy='AL@a]"`>0k< cPZX}lhZ y!Y |7O1]5^;̽L@zqr>ӧmsb];kli$~9Sc[_Z^t<]+O.R3QaV6iymIve3=N"W6y%m@Ҍ\J#tgJ8xn`2h,>/-L`48YO`GAQ]ΣzݖY5cr)?"-%k+r}{|gqO`t_eB>TpG;& V{H"oZ)1dB"\qLlU.LMa;ɼ`NЧ\W S' */{U}/3DxƬIv oxtՔiIz2[.>ވ=,?Yq(ʊR^vHǞbZ1ߗF ?V ߫vDq([k6ce<,GD! ՟gI-ozE0.I^c)jw9`!mJX)~S2V*S @Myh^NƊh9mZ5\!ygG|1t73.-|Kv8z;#ApY=e% S6Rf/m=-rqu LibmkX>'W2|}r%o ~O>f̋7"]ٱ$zxHa{B(&7swrh&@Vd}K_%AmVQr~m]x݉њRiDxY#͙IgHrGbs-L<723ōބHW굾ls~_6X=Ak1LImkV4#]yyxRM1Y8Ikd]}q 縡wܣ]˫p*h7GJBf=Z$7".%LXTFjmbzi.'SeO\OI=su5`]s~u;Syy,^aB"#'h˸AUj>WF$˺cX\rZ MZNOjgse1[zޥ[2QFxcCaIgh;;Ұi5F /x`*)Kҋ/gzCQ-}W|Ms>i :!Xh6!@rt$:vp)xНdNq)zQ*kof,sMkT}ϻ1CkxPK!:]:4META-INF/MANIFEST.MFen@= Cj+Z4Sfu;>}1iퟜyDjM1XA~T)*W "˚#Ϊɹ&v,2>(ь3+HzBWbٽ META-INF/CERT.RSAPK!:]:4META-INF/MANIFEST.MFPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030740 xustar000000000 0000000 28 mtime=1741684064.7990000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.1-1024.apk0100644 0000000 0000000 00000010635 14763776540 027245 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]ٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G LfOWnXۘo i^f/9YO{=Y#%ӂ %OךR݀;I5b'6կ4$-eRĺUf;J^WY|J&}6l:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]OC2#?kjt̕ք@vǑ+PUo Fcrzqf~ao-F*@ ߄kR{db N6L >?EPr@&D0=bStےZaO]*K: m4L(љj͏a vQSՍu8LMbWvsvц6#чd̀n(4xb}LpC j D b|Tjg0G2ά̈PQoAehڧgE( },Ëo ,HgQH_z 6'͐lAAqqˆk*fswd¦v_cN\ ox%6=|g jk?6z"mR"Dݢ9S'~R۸?ڄp#xFkZ 67!21wઇM#;Lq;#]IY>nn9t8 f>R8,:Mq |_ ;,t]eq\o4r:$B$m{.ݧŒ5 &c >sfwQicI K+k c\J#Lܵ_h\gS?u35,#t]ހ#:2嚓4ϒϸ+][Yc*G1 &.M?)]JUEN%,8bʻUMIHE?O:nTNy4ob%՛h M!S|88D-Ȟw>x8!BvR}IeU&EȲLFE9΅^Ou~K%7%ɂM {;s@SW И0)O]$Qښl^Bc@7͒t|p)ԭ , DmӴG*A ꨲPČb|'ʉyb1a*ขgU77@oևbq' DյE=iZ:hCp\"8T&Đ [ 4| (<g[{(k{ ~O>=gnlc-FR ~2L7,0wA+NA).E'Ɖ Ry;{U~ٖ<ҚfstbR4R5Ph#$S]b&+<'m`MJ !5:Ӳr1|TMp}eӀ8/;~[G<ܶ&]x},/.&B-,8Ѷ *Y֧Oᒀ b:|O(~M6B{dF ]C?*tQOfIUT7kq rvRPbbJdC'_F`bα˛VAJ,mu)L[=5[fES e~AWR(e+h܊^ ~$sYrJ = D U "lW5t% ` Mv0? 0Q0 TV J"R a\2RZǽ=]"  .>- a".؝lVLX<^:T!+2`-<ζU礿h3q.< HPݷ,J_bE:yqVڛeL׺ 5cѻ29_*QF%U}UrBRWWK<{qbn܀lI#HlXޣ:cE` nL"iznN'ZRKfYQ^XHW1,d Շ&ѳ]Ĝy30چ($NUĝV᱂It3"_ƫ^ˍ [6&ɨ|k&E]?rQZ697E@C3){|{I􍦷7Qج[S[ʼBD" kƖCDI%ZXǛ77줍Ջ췮),\P{ҏԗK%;S5]ؒ+99ͧ%ƬWu򚅊6Min/|e$\1ڭ~FiYb5!⼨^'1/-8r =Wi9504o}aK)`W:;i*W)(a7W9z<*2rk|:\}HYrJ3(1z떉 j~,]pz^R\m8OyHaoOE\^W%txs媇=ݰdÝ:mStd9+«Fg&]Y rm-gB Eۛ(]i至ƒgr">9wɘӀnMhVL&O~Ԗvzuyd N^- θMOe5Lj6Z-읽LLAj;1c-2YҰy<=G}U~K"HCZM'`c6̉Wf7].񣓼Mw8MHƘ R(:?wY1 8pM񃚺yy_n'l4 Z GyyCU3q[JBP 6) BF0 H-+\4;5sj;Kc#ZUC6<8Kxfd.ƖGas`]ruj[&QRGK#S"|L+M-g 9`FɌأ',aCXXG%*-psfV\6HY%-΀5b]ԓ<156-Am8TJ3Y2#䴉dtŭވcW J&`?Kɳ?FPQB#b&e N;@"bMqGYh]oK}C`uj|Χt ά+AÅ-N5uLVa,#1_lNg1$%Qֿ p>Sh{u  ?]v0\X&-tsa;)'+-_)$82CP/+'4:jdVmJATڧy/ klq:U(nz]6O6zS\!S[4neR DhkunX gI\Js ٷP&J-ʤ 79q*Q".S"=*0$ІVl# Q,0>S&cQhNwb puN]c*ף:YN h˥כ"!}+aK?f1/?U>SG.gh]X#,)7X>-fR6ggHWˍ^q~ 8qTxp1붯Pt:AcWOtOFQ[xZHfG'P4hMeAqk R2 _\EON—Z!BI&84 1k?^LX6 M$>?7X\M3ܥF_>VԤ/JQ9RT 1 K{?EnlJ+˾kN dQG; cS6LzDҌpxIu:Jjrx+Fd:*s,E蹆o YT/4E@5#XՒ-|J݄0njJ|%kq ^ؠE^E} FmզUG1pZq{y #;S7kTN=ų,HfϯУ=wV"*h+pu)IܢKr閞K770^|.l Ie'03ȐEq9+Sh^%wqk+18OKb~=?3ZFD m!D7!Du%/yN&6 OVVd Jڈγ%,0|%-8XШGb"hL,uvGcW@xgJEJGU8*h:mcN)jkQ3%efp~/cH!ɇ&k>!,|_ҩQs҆6icZx !Sˑ]\mx,kdg̓P.p^C UTfSjj5uUd,w*[S%CVՑ1RR,-lF^͍'i ϒPI=E=3|ˎ~`}pN[*x2)˙ovGN%\]Lӧޏ2qP3c~+~z(.`?PKh\?PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:l META-INF/MANIFEST.MFPK!:1j̬DY META-INF/CERT.SFPK!:h\?C META-INF/CERT.RSAPKy&./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030740 xustar000000000 0000000 28 mtime=1741684064.8000000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.1-2048.apk0100644 0000000 0000000 00000011467 14763776540 027260 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fo`s-G $4ȰzZW+miςJמOQ#~X Ҋd%V ^[%%uQy^ \^5o;5-e)G7S_q1\ȖPK4EwҸk}oGե5[-67[M= lhU(uuքUYp{C\+n8{J⃥/ʿ&`P?C~KnGd>xH_>~vwy} <7edi cyfQPKa&PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:l META-INF/MANIFEST.MFPK!:1j̬DY META-INF/CERT.SFPK!:a&C META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030740 xustar000000000 0000000 28 mtime=1741684064.8000000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.1-3072.apk0100644 0000000 0000000 00000012304 14763776540 027245 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]qQRH|-,.CvP*ɦ.NfpmK~4DHb"dž ٍ'"W͛CfVLERZOEjU`{[_ j/g?f7n)jWV 8yܦ<_ 4J=0D"jU5 5U=0F}~P$a/@^4~j70+Xn4o sZG9/,Id)@;hRXQ튕6 ^.cbTj)r N'ӷ9]WFeU[yS=izzhh> EHsEO̐ 8'XxT4 QFĜ=bXi 9Ib嘚 ro},B  {:0\*kc+zю-+@{`织Z~՞YXWz4G>w;/ykI=[tK*`LrBI_P)ֱؔ*J'l$|f7]w%2Ee7,Ϋ[!S?O;VZ._|UT\_U1r٢8E& ?l90FqkA^]Q̬m؆&T_@&72_w>i3<Jٻ.,4Zu-N9?$h\zRi1>*iX 0Ւ566YV累4K!폳;M+S#Jpx ,]"|$0 DFUMxHs;|)+qZҡ)쓺#:Z!}D_-6ǻ򰌛zN3å-^h[։)@Lc?K=8G59cu [/SY( OpE):STRⳟ C՝%dK|0LuBAv°LL bܘd!)}NňTh?gLZ!̖-茚=vM+jd>n%.M5S;nPtpVthH -Z5$аc6[1;fLyvȪ؇ Q ߾T aeSDMH>{1 @a'@Q(.`WPKPK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:l META-INF/MANIFEST.MFPK!:1j̬DY META-INF/CERT.SFPK!:C META-INF/CERT.RSAPKy5./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030740 xustar000000000 0000000 28 mtime=1741684064.8000000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.1-4096.apk0100644 0000000 0000000 00000013115 14763776540 027255 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]p裦fZ8k_i<"^E@`d٘d y[aŔS\%~×ߨ!=o e8rḏgmnR+s H) Yrqa(O٧B9<:rrJ/boG(Mr,E b/ڪum/DPSyɉV؈g$yrkĀdه dTH6*>ZwP~nާʣdFI<']6s1AFjNUi3o'O\+)4txT/xr!kܿ2 3'wYoqʴهi΋ F@6uZz|3 RBgƟ/-Ϥ).ΊkOem75~u6AA_mhn6|89}%imucr/9Tb"־ͤ 됫` $C;R :aW].ⲯ| L{j?r2܎Yt|]F9Tt$&tԧ(ENx bFTp8hڨ<\$\9'ҫIw<{抽M/zp5P%ǀw G0(t?}q"xC\(1 tQ](KF`-/ O]B [ #Gk6Oh_&ECyK+۶G2c Kn[:uWQPͧ΢B6K &䯧&2=̭w9DS>rص`xvd+<&!(ݙ-]Jy^0y'*Q=+AgmbfVmG%vN𨚪|A*!lBwкuOfXYvOǨW5 [XVi{v٘Zg/ Jrt4/ qy$6c.PIuO6tjТʖ\{|9S Ew OUU՝3u3Vfp\E*~rvwGf !-)qjy5w[4k|qݞN}LP كbI|IQVwL76aa;.V1MHBȗo7%*>"rLNEƣom6öyMj)Ee!8UMk60.YOL# }y-.ήu{YԪ[2S2Ͼ㊵kzuZ?4KnΑd;CUfMp%q=ˊ<&6V]uǼ$;8gR+LXqU2#Ѱ9Mf\3ə4 k,bCv V;ܚXQQZ:}ճ)Hb M0)e_8Yd>LD }eMShbu3P1Q!?xn# A0^yI/|J/~Isqy&~r32ZBit U/;X:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeAo@; n*D+eeb]Px,$ J AGuQ!p<)r1A<_AOI (UyD!d2Hi)+LF!/{J LGg(ITeqJx>W9QԞ5sjsáI`堤yۅ)$JElԚhI}9̥̗X/,=m/XK1lkt4>uzm9`@D]Y{xbܯ:]FNJA9;L\}D (I~3ViaB42#|srhe %KQVdp#VsUÆ;=ھrk*褁sZg?}c U< h؅whKqJ q hIL1qDow !AŚO ; jTk|V<vx=+[feVqC#!ֳ|} Jtj>TuQF5;͇g i^\{SMt3GyV vr7J9Qg'=5y(Е J++X榟bc!Oup҂YL6 7L~pH}xP# `; `S"@诪q7Wvm)EswŊEjL SGWJp?, 8 s&e]ް G,p(>ٙ]tJu8qѹnY;Tf#NR3N 6x표*y)"Tr|&/="/9> uo3,(&RF6&><%7j!%* Cשge mf4ezN>=g%3E]&m^񎀰{xjdrAc:as /ɃIں|-#(Hj^i/~xS8sO-O<5"~ᜅJ!kVvmaFL<阠Mڹr'Hu~g MuFc@\ߛRFwg2ŨIj.at.uZq*buAġe(t^GV2ܜė+>J?0nG:UC쌃́uFq^@-y`> M+6*∔b{cŷ,r - ۥbε45ZB@3e:R '^XnUdؠxͷ.V>^gǷ>Cf)P4w4 +2K` *J&yyG_9Jr]@2AzC)!xVψiR0`‘;1\熦Cqz\\ҕTfFzO~ =鋁 }9fyWk>bN Jbj$u2u[6lse_lH+~Kxe 1s=irg3z#SU:Wa]-J7B_{-SSw`3 #HkH3grx9#CV]%)F dt;XC5 %+nOi>!:<> DO42 GnOct+ voӾ5a]'HV gc6K1`@ɪ' 7w|ZnєݾN_WF w.nqsP]WuZ|򅊁QTYasX 6(xgQdۢ гLL~! ӽz_]< &c-|(5 梌Zu\KtHK}dd:ޜ Jhv9t' [޵(tGT(]b:૾Tb06WRSW[9v!m3VWETTϯ'^ kß.~]4Zp D,Xg֋N˟4C5{97'n cxsm^>zKW^+غDZ7axf-R UR3Z&kJD)_i=(˦6{'IbǙbgeuC2§m\v<}ljk+WX+t:#ܨq`3m|qg5Aj}6ᔮAЧqhS-Pnjt*Iq&,Ht1Q>Ln^}fhA[G78( :?7o{wX^lv'8;X0 p0%MH$4NPKND < PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:l META-INF/MANIFEST.MFPK!:1j̬DY META-INF/CERT.SFPK!:ND < C META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.10100644 0000000 0000000 00000000034 14763776540 030740 xustar000000000 0000000 28 mtime=1741684064.8010000 src/test/resources/com/android/apksig/v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.13-1024.apk0100644 0000000 0000000 00000010570 14763776540 027326 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:˪HMETA-INF/CERT.SFeO@߁c"W=""Fa*K6iywyyQ )C5 x2X qgFH߇Й4I-Pyk_fBaxOmzpl.44[Vp89iuГi| 8:ms\/7O|L-:Lw)$wMiOQ[O7B9OG/|,ED|kD {[[k[[ s_BEs-IKd v_,o R-7@Ůmm+9tOw'٥m/F hmEWՄMcnieW\/%\fq^l j%YU->-~ aDPdz~ʁ.iES]w0y% ~!~PK!:META-INF/CERT.RSA3hbƩiA[&Ll m,L̬  41~LLL v<{{OPЀ98Q@N8JŮq>ٌ ̍ L ׷PPj=\9Ƨ:ew*9V_=g7rL2Rx5v5nW/)kwN_5_okye Y>,D>* `P3Z/ Jqq,ٲ|,b,"_܉辔<+֑+- A,b 032G LfOWnXۘo i^f/9YO{=Y#%ӂ %OךR݀;I5b'6կ4$-eRĺUf;J^WY|J&}6lyj3v?^ɺK؋]mދ'{?V\0y)q}؜SYҫprʿ컣;v4gZOOtqcc PK!:NlMETA-INF/MANIFEST.MFeAo@; n*D+eeb]:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:˪HMETA-INF/CERT.SFeO@߁c"W=""Fa*K6iywyyQ )C5 x2X qgFH߇Й4I-Pyk_fBaxOmzpl.44[Vp89iuГi| 8:ms\/7O|L-:Lw)$wMiOQ[O7B9OG/|,ED|kD {[[k[[ s_BEs-IKd v_,o R-7@Ůmm+9tOw'٥m/F hmEWՄMcnieW\/%\fq^l j%YU->-~ aDPdz~ʁ.iES]w0y% ~!~PK!:1QDMETA-INF/CERT.RSAuUPڶEw'݂Kpi  %@p޸wNխzs=kCI#PPRp('c+`PWp(. E@4OVrXpqQu1=,8xTX`>00//!1/X_' dPst`(PXɮ*̻MLq9ZU۶U-MwҭM{"3&aJځ n5r>zH{u_6c) mG&ng0p R/joB0k#?:g3]d]d:sn,Hc $);eB]k(I\f ArMƷr0q*s1x{_zqM'Ŀ\l`Cx 渽{7F~gحO'''NK2:_ʾ#o|I)Y\ueHltbL!Th (jhl~Rdzʛ@_mbIg-t^#'B,>9[ H(Z>4gf o!rv, EߨUH; SڄBRh ⋟m)vM=_ 1v}zv6LTeʵ[ ngʊ/enBʙbm p!W#?7]K-”X[N)]︞Azg3spuQ_/E7CFUba? f,A׬uc@X L;Pb#!ar5˩V8'+hsG"?2x/AG5(H?Z0/SqZw!u-ؔRx R*b`Ä~HQEP_ ϥōu6XW 0HX Oa1E/&x CA3euAoSuw>je6)XȗFTtzIPlZjFZIbX #F"K }ѹDDȿܒdG^=%NS> YRf3}Mp z~L2iʼhY"%sҠzAQXw-k#6nUL%𯑪6r6uyB12*걣k1_CiˊUf1[Z= RRoC}{>s6aCepKT^E TgM)1>6.)w);:N1e(jQd?_Vo}}9sht&Pw,@ܟK%+#C-<>EbJD~h?**,1gEHmhXʇiX? .8/vDm~8@ k*}fŻY-A{Yzr,R>b/†keE.RTpP͒(DB^baT$l0A>IҼ*1u"LqbW{Z gd`j^r{ F(1ג|rNdHX0?t;}|jdlq>nC;h)d%`80韣qr <,2qox9XyvCi]z+ˠe ./Rt!S-Ej[$)~z]UI}MA!_O+tGF^5i3}0O2)PflsJQjjhͽbK(Yoe} Jɉ{=Ǿ6u)ތldCn{盗=.H~/e~#Ѹ fz +b`uoZVi"j nuтGQz.%Xz{j*gkEV=_Mv\ 0gT1No̐د7q).qCcq 8{Ȧ2Qfyr @Xn8w'F x$y'L_uFmM}u8a3 Ƹ+gPy zV`lR3_sZ]fđ־9#۞hb?hR|8uiߊx,9&?W}"PBw@YKp5Py 6/)3t!QHѢcIsr~aZQ AONA}%Z@?lA-J Mwb#{IL0 LZ40dJƏ0~K+p{#]%1w{lJgT&R\%&-^u38GDebC3QÌx*uLqe̔oAX?/חi϶uחEo ^0GXf(PZU8R<}-5ʀ2F~>h/5Ohtzs?naJւE:ۧwM +g\T_Gz :Vχ+zU:`l|m>v&>iM$ܡ}7Z5,|q.2=HNJ/Yd+죚 Ll]2]BC3w(H R`+]WW)e PBJ[B;gط\s DwͥQT5φۉJ3's㟰[@axp?fű t$ptLϷWƞ"7=삐BלO}>49pp!6/_:!dDdHxN}E۬D]X]ߚeHoTQFK#;ᆴ'3zpD /ϝ))iǬs:tqxj`Sn Mē:MיĤhcDTPxsw~v_]Yenx6Mm*ֹ07+$(7bW_S1 \QJj8M%4*q箋3*"4fYjŧlSGK&'bhjR,J G+SⒽ=RJ n2tUx$S-I@;s5,4 7k".Q!ad@|H0Kh?QqJ)HA*>`ٯDn6X5<:%BiW ڻ#6u" baaZ\26י~U [$:":KW Yr8#iY2xE3N,kwv8ZnĘctyt.ySFl\S$(eʑ_MFE+Xj\"|I ;0O]mHnrp.e*)qS0@(tzՑ_ekڬU>,.#c^ÅOkj~:L"W0%ΦPw̄$(%;~CZi>A`whZEAt Fa~3g g^.I#&6ny b.G-]ӊ_RU}&7 !8 lR_d섴ê؛} ='=ɩg哔ߜu>M-rnY x oenKR3R|C{KEjEWрC`E2@ 0GIv$GA۪_Y]WDϨc[ 2q5""C^ -^>')̰~s0Th[+q"PU5Sa_N;gN B!}Deh0Դ;0KO DUtmVJ? k3a^@Fe.aFm^ΝCƥKGgSWdꪑ @7ľaC R wLXhaű?yQXJiQo7/|)EOs-*MS3ut(5 CĄwj<1A|UL>ohjF~BG_ha}q0*9?vPK!:NlMETA-INF/MANIFEST.MFeAo@; n*D+eeb]:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:˪HMETA-INF/CERT.SFeO@߁c"W=""Fa*K6iywyyQ )C5 x2X qgFH߇Й4I-Pyk_fBaxOmzpl.44[Vp89iuГi| 8:ms\/7O|L-:Lw)$wMiOQ[O7B9OG/|,ED|kD {[[k[[ s_BEs-IKd v_,o R-7@Ůmm+9tOw'٥m/F hmEWՄMcnieW\/%\fq^l j%YU->-~ aDPdz~ʁ.iES]w0y% ~!~PK!:/^+META-INF/CERT.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fo`s* Oh99'!'/+K#C3{/V} { 5O(>񽐯gj4E%,ݾGōf}*˵cޣӦnt1{yusY*?/t_1w칀gb+nh֨~^Vizy7V}g)o(|j#-* >:ɭekت9O^IEy_O՗hilW:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:˪HMETA-INF/CERT.SFeO@߁c"W=""Fa*K6iywyyQ )C5 x2X qgFH߇Й4I-Pyk_fBaxOmzpl.44[Vp89iuГi| 8:ms\/7O|L-:Lw)$wMiOQ[O7B9OG/|,ED|kD {[[k[[ s_BEs-IKd v_,o R-7@Ůmm+9tOw'٥m/F hmEWՄMcnieW\/%\fq^l j%YU->-~ aDPdz~ʁ.iES]w0y% ~!~PK!:-&META-INF/CERT.RSAmy4{wei,9:Zd wd_.Y^1c⦛k_dS7dK%['ruN^ӟ|y(!By( ̇+^DQ rK4   ~`{>H!^OW;o$hEvk C(Q"DagQts \ ! $ !%c hSsC׏=jKq^`X4_Z4ٰ ζ,S͢kUTk9 c4m|痎ٮNvu^Y/m99|8jem͛ t!hIB.ZU|Hݛ7" ߚ؜i[>{BҁBS#Hcׇi܅6K 5Cȶt:FcntbA_[ xW5zW:@~#Y{O3ebka~۶)F~x&Y)ʬ=U̦Uq$Ir`1{;p57,\&%7Vddhnե.+e'H@Eg3n:/(iqyLz/]jޖ }% IH[?j%;{Zn ‚Zј'.rrM&Mow'>M kc8gZEpnzC x gf+).W'-IY':[LDo]uBQkݷE/^5`gRY ׮&/?B-]94gv-R|;:*䒍 ҕ7`R̒PՔ3Yxu+""ֿɩa ȯ~bx+{ީmtDD$U? /Jܠ4r_Tq0ϐO͓A Y\ytZ OaKfdkn36S[P1e"|Y kqklG ~1ZVrcDSԴ"MO[%w;wvU;dh b)L,Vp_6N[ȺC  7/2+ L8FCXN_,0\(OKtT1Y/7SXկ"#ATɌpmūe8VEK&sLwppvkP{BPAL{֘[{և˵9V+2^nVQ5xVr釤G LbbX<-l;4_^4~(eu-d#>9:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:˪HMETA-INF/CERT.SFeO@߁c"W=""Fa*K6iywyyQ )C5 x2X qgFH߇Й4I-Pyk_fBaxOmzpl.44[Vp89iuГi| 8:ms\/7O|L-:Lw)$wMiOQ[O7B9OG/|,ED|kD {[[k[[ s_BEs-IKd v_,o R-7@Ůmm+9tOw'٥m/F hmEWՄMcnieW\/%\fq^l j%YU->-~ aDPdz~ʁ.iES]w0y% ~!~PK!: ,=META-INF/CERT.RSAmg4igSDFQe'IDIт%%%:CD_De*Ѣ1hsoΞ?w.BBFDl,$xP( 6P! A(?]R`X!QesPp ǶJ («xY5%{(%EP CD@PFА9@?ńO G{ϿCu4E^$\qKMDa-&FKɈnv2ʥ;ȝa[!W,,x. .~ڪ+dh xAHv.h֋. [9[~༻LP. )ޮ DX~\yK` Q=$͠sG*FWl3$+K$~7LyrCbCxd]9&~!| >:)$ː\ &,Iܛkrhwfd)S=94cF_שgW7o7ʹ L ]YrL"a23"ׯڰQ.P#F`n}oˆE^µkM].;;]&JrHtVdx<¯6Ȟ펻ܹO`P4hbyvO$~)X59I- B`аU#м vhO3/hՇ+XSGнkP.@F.qӥq޲"^޼(j4b<9ĝǭŀ'DdoU5Ԭd%+%܌ҹ5 lpe`֢!g)c3ejoK@~lCkxB5j`G-sz~5ېA.n֘slq/'io?r`yؘ @*udduh?0.R8дė<{GUJDk5.Fd E`o5MmIS!]8`@'Lΰc2Ͷr< +.T kp" _f(jN1.װTt&()Qg t(^D/X=[rgFgVq|OW*X,X]Б.XY3`ޗZSw{7ypH̨ugUn6K6nkJ9jg O lJP[agG''( RM7%~f4wzYH+ܛR.)YiQ?M5}eNV8g?GOk:Pp/n=m݂VE?RL2.^zno$>ʆYj \+XnnOYxOu̢7]m )_u5-ODWyEchE81T](śJYUX]veTGw}b $NFseK)pdvO~ޔ6I52?{%jo6QU6[t@'\h VҘвEmYLo@E&ieS2Khzdums[V9t&P[;KJb_ʞ?q.fpkE C+nC~4 jVFLhd kퟲe=L]mZ]0wtH?V>PK!:NlMETA-INF/MANIFEST.MFeAo@; n*D+eeb]:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:˪HMETA-INF/CERT.SFeO@߁c"W=""Fa*K6iywyyQ )C5 x2X qgFH߇Й4I-Pyk_fBaxOmzpl.44[Vp89iuГi| 8:ms\/7O|L-:Lw)$wMiOQ[O7B9OG/|,ED|kD {[[k[[ s_BEs-IKd v_,o R-7@Ůmm+9tOw'٥m/F hmEWՄMcnieW\/%\fq^l j%YU->-~ aDPdz~ʁ.iES]w0y% ~!~PK!:9iD META-INF/CERT.RSAmi4g1f=;3A"[[}_;){al,we !cƚ%K}==y??8MC'a?8P8>Vx hS8a@@ R? &8L@ P_f p9BIVFi+  s 5 σ` DLEpy :Zi''bdZ*9Vҡ0ڜ`DV_tw` ]u߀B nJ~=p\22 l×/H刣m; v>B5z|kbg|Xٶ+ҀN_u6(}§AgN8צnwZtFH_*\uBo1R{ V DM-PqP֥yK7uV\y򛯾/&nU:PKIC*+j$;6舀:4lH_=\啜~F{yjB槝2-4f0cRcH$*a mr*g)=0>ʹ4X`M@3,_ٹe1j2Iu'hKKD =tjй!L_>h:_s!ky%Đc͜uP'n& y뙔E@A}HceLc-TFB^mOzsvf^,,rgp"[SdJ&8mdz桢:2ڲMBQoi`Fb3Ezc>49ղP^4gu+z ;b\&Ja L35Û#r?jaᱷ;LM{o(~E[(gĂ|_+^^-Ju_GF$rgW(yMN%qri7{=nƒH \CvT["DmrA:{k>Ge(93>o2(\ .dyA=33Ewd^g6P(#[ڢ~/Rlz6PFﺘf5K?N(w0fj)bvY'G+L&G|9\QRfӸo!A)4"W|4%pOup3,xmǂ\2Es5Ҡpp0\Sé)4eϜzN@@C V浢Lջ'#^"Z{dmL^) Gg@- q3? L[_)n=CMY8lWCnΆ]l60YQFD7zstYɬʳf6(2,(pZ:GPX(q 0 p:8shPhu^GرSo Bl#ۨ^HeU-Ϡ`eb{OYR!OVЩ[*Z [R j;={y sXc}re_Bޡiw^-/94TM'ud&gn;n;Q`E[a̰I̡g{Qhy^DG9}.Go4cJpʅ?MPJ395qSk 5cysU_t oK,x≩y52dLUpqMVeu+KTֱTԤt(x3u!HAƔ"ٵhIQw74ːcI;…aqHςQ5f` M0#wc^n !iѹ=mSE-aƿ1TVExFOuV$epz;^9)3k}l*˜z2zy >z2gb5-%5gn=f2ﲶ:G4T+CDϬы՜Uqf!Յ3L.+-5énL6?`-=Ȟ<]穀,lABMFݕZ<7N/ |`B'(u7SҠqhڠnoW9G~0]d >bОa 7!6,>UQ'd]Hlm*ͷLѶ' ~XH!l":8֢^ t}sEB'˩-*@KaNY|B#6W.^|s K 0G~בYLv@n aڬ6㏃3^m_җsޛôYy1Qs`4k)gqD~[TTo - %n Bz}:α(5!oޕ+p@Qwf{ 2meVwnKJ}ťY!sҧ~iϤ[f#"NmrC7@ց^-,ַ~lJߴ ܾ*~~z8v6r(5.cg MkxSG沩g/+{4"eiYqig^Qky[y@bӧzߣfG l%neGg}y)_MH03}hvS,#W*@69UPK!:NlMETA-INF/MANIFEST.MFeAo@; n*D+eeb]_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:JwSMETA-INF/RSA-2048.RSA3hbƩiA &%Ll m,L  414hb| .w>BFn>aCA~6PfD]# 9q^C3ccCCSs(q^c Cs(&F%dÁbnbgs15122\x0T`q*/4zx'j W)snY>~;Jb<.}|2_'SOk9Lx {O]WeIu[:==%;V92梩+Jrhd8V+pzKիë~|zT:WFn$y!m_60wuY1Ϟ38wAyeJU=]سɢk҅E:wh0*~X'zĽ  dA'"""Τ[,Po1RufƶH|y2Xv< ,̌"ԌS~nkz"{ƟNg?g?1فz~nw՟3ë^ [|OBՕ~K_|}sBMOx9<_RfM ֬7'aBe__#RcϫW2Hg_8N!s[ O20mټ3Kc?]Q%WsG71pn|V0q"%u]yԌZ_9AksM7}UTusumiZjXLwUb5@,J, K}q~"~:lb[_n݌YDԼZ)|Aqy/oߜ~<^K̭޷z'&*37~]flϼJp [zzSdefמpgc9grk^Y}4οʽ8"~ROfokj5M1!O*&8=uQtٺ9{'=R[v68~BZUFOsacɓẗ́& 3nZهOqN%x=STVPK!:MQMETA-INF/MANIFEST.MFeMO0; 6Gà,@E66w1]Zlc^4&&#Z!pQ1i1V$I/.1o3K$Fh>-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:JwS META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-missing-digest.apk0100644 0000000 0000000 00000000034 14763776540 032521 xustar000000000 0000000 28 mtime=1741684064.8030000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-missing-digest.apk0100644 0000000 0000000 00000011064 14763776540 027367 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:oPH2META-INF/RSA-2048.RSA3hbYƩiAD&^&FFC~^6΄6Tf&FVnBML ~41>\gjsQ;HT!#7P?s( 3GQq8e89]S@G1713Ź.>Q|FkqMJ<'+x뎤I-XTl։S|zLYpGs _%942gN8eU?>=*LHdx+#f{EW/B~?dgG]EJ}g.d5";4 U|q&fF~ccg-m):Kq3c[lZ۾}' F/}e&{ۆSkݛ°pFڑ]+tO3/ju9y҈E lޙ]yⱟڋV.ب+W987T+gmxκͮ}J۱kAwSVmE}iPztg55u^-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:oPH2 META-INF/RSA-2048.RSAPK!:MQcMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-multiple-good-digest0100644 0000000 0000000 00000000034 14763776540 033057 xustar000000000 0000000 28 mtime=1741684064.8030000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-multiple-good-digests.apk0100644 0000000 0000000 00000011137 14763776540 030663 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:鱒]META-INF/RSA-2048.RSA3hbebjhδA{&FFC~^6΄6Tf&FVnBML ~41>\gjsQ;HT!#7P?s( 3GQq8e89]S@G1713Ź.>Q|FkqMJ<'+x뎤I-XTl։S|zLYpGs _%942gN8eU?>=*LHdx+#f{EW/B~?dgG]EJ}g.d5";4 U|q&fF~ccg-m):Kq3c[lZ۾}' F/}e&{ۆSkݛ°pFڑ]+tO3/ju9y҈E lޙ]yⱟڋV.ب+W987T+gmxκͮBPEaɢ?:ۯ_pϢQ 6\]ly[˭I׀Xtv965OvnƬJŇ$9Fpwr`Ij[ib);.V :qsg ~*{>d帡ǜ'z;.[7F"\x ]qϋX5oK~B9v%&(-T&[bC~8R=TOsgy2J% ,en>\ϺdF:&4 Gs27,&4mePK!:MQMETA-INF/MANIFEST.MFeMO0; 6Gà,@E66w1]Zlc^4&&#Z!pQ1i1V$I/.1o3K$Fh>-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:鱒] META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-signerInfo1-good-sig0100644 0000000 0000000 00000000213 14763776540 032712 xustar000000000 0000000 111 path=src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-good-signerInfo2-good.apk 28 mtime=1741684064.8030000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-good-signerInfo2-good.ap0100644 0000000 0000000 00000011565 14763776540 032517 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:yHs META-INF/RSA-2048.RSA3hbajhδנѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICڟ3Yhޣ3 oe|a/(Ɖ@f};8|Xj. Bs *cLܴrYyL/>>¸y&|ӊjUkt1DmՃI{|>ǙP|Vi}}PK!:MQMETA-INF/MANIFEST.MFeMO0; 6Gà,@E66w1]Zlc^4&&#Z!pQ1i1V$I/.1o3K$Fh>-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:yHs  META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-signerInfo1-missing-0100644 0000000 0000000 00000000233 14763776540 032732 xustar000000000 0000000 127 path=src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-missing-content-type-signerInfo2-good.apk 28 mtime=1741684064.8030000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-missing-content-type-sig0100644 0000000 0000000 00000011555 14763776540 032736 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:TkMETA-INF/RSA-2048.RSA3hbcbjhδA{&FFC~^6΄6Tf&FVnBML ~41>\gjsQ;HT!#7P?s( 3GQq8e89]S@G1713Ź.>Q|FkqMJ<'+x뎤I-XTl։S|zLYpGs _%942gN8eU?>=*LHdx+#f{EW/B~?dgG]EJ}g.d5";4 U|q&fF~ccg-m):Kq3c[lZ۾}' F/}e&{ۆSkݛ°pFڑ]+tO3/ju9y҈E lޙ]yⱟڋV.ب+W987T+gmxκͮ-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:Tk META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-signerInfo1-missing-0100644 0000000 0000000 00000000225 14763776540 032733 xustar000000000 0000000 121 path=src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-missing-digest-signerInfo2-good.apk 28 mtime=1741684064.8040000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-missing-digest-signerInf0100644 0000000 0000000 00000011513 14763776540 032720 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:WIMETA-INF/RSA-2048.RSA3hb}ƩiA&Ll m,L  414hb| .w>BFn>aCA~6PfD]# 9q^C3ccCCSs(q^c Cs(&F%dÁbnbgs15122\x0T`q*/4zx'j W)snY>~;Jb<.}|2_'SOk9Lx {O]WeIu[:==%;V92梩+Jrhd8V+pzKիë~|zT:WFn$y!m_60wuY1Ϟ38wAyeJU=]سɢk҅E:wh0*~X'zĽ  dA'"""Τ[,Po1RufƶH|y2Xv< ,̌"ԌS~nkz"{ƟNg?g?1فz~nw՟3ë^ [|OBՕ~K_|}sBMOx9<_RfM ֬7'aBe__#RcϫW2Hg_8N!s[ O20mټ3Kc?]Q%WsG71pn|V0q"%u]yԌZ_9AksM7}UTusumiZjĴ 9@kjEH9imȵ/d4{nTЭHʫVwySX.zR*RRwtů4^-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:WI META-INF/RSA-2048.RSAPK!:MQzMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-signerInfo1-multiple0100644 0000000 0000000 00000000234 14763776540 033040 xustar000000000 0000000 128 path=src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-multiple-good-digests-signerInfo2-good.apk 28 mtime=1741684064.8040000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-multiple-good-digests-si0100644 0000000 0000000 00000011566 14763776540 032712 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:^mtQMETA-INF/RSA-2048.RSA3hbejhδΠ͊ѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICoVz\\]/o}ēZW?3qV*ހ7u͝.5-z0i8_2Vu=2oPK!:MQMETA-INF/MANIFEST.MFeMO0; 6Gà,@E66w1]Zlc^4&&#Z!pQ1i1V$I/.1o3K$Fh>-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:^mtQ META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-signerInfo1-wrong-co0100644 0000000 0000000 00000000231 14763776540 032735 xustar000000000 0000000 125 path=src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-wrong-content-type-signerInfo2-good.apk 28 mtime=1741684064.8040000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-wrong-content-type-signe0100644 0000000 0000000 00000011564 14763776540 032744 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:P)r META-INF/RSA-2048.RSA{4i}1fduHc0B׆Fe!4.fZ o2%B$EfY:K.]V1nd&s:k||`08so(#U&(Dy0B 8T5|dКY}+}5XECؘ%le &)60JXYӬ6'୨-鸼 ~|S@P3+/O5%!O('B"yEUl莼ohȚeZ=9֎epre#LZ0cTgZ5Fʬ_4zJbVKm v!_ج@[Vs i0ܵQ}h$mGza i(u攺g-&{>=S̺eur_NM=[RyJa`^mp'B (=aUhMH]ɵݤ. 7x$h`il&I h -E ̄o"Rܨd9}6wB6qL= x@Xa6s$ZxsQ8ƿ`B1٭0aU.16z`eÍLYc0KB'Oj8ioux I:k#s}\P{"k*&t49vBE)(yEp\L]Vkۮ n1ҎA/ub:hST;0dahY8eR=b;]Dq;mGwA&-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:P)r  META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-signerInfo1-wrong-di0100644 0000000 0000000 00000000223 14763776540 032731 xustar000000000 0000000 119 path=src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-wrong-digest-signerInfo2-good.apk 28 mtime=1741684064.8040000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-wrong-digest-signerInfo20100644 0000000 0000000 00000011566 14763776540 032654 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:t META-INF/RSA-2048.RSA{Tg*x JmfvҼ\LYBs*-]TlbD ]AKgTjvUNNǿ~=yd*oPFnəA* 2 d*#@QȨ JP`BA&T2g0("!v`;(TW|-PCSCF[ۀED֎dx%&&Od~ ~iŖ`SiuHDa^{6UE)vޭ;3FS=_zEvYtbyEu[ ;`y#w7}+7Ϧ5#6QKid::i?cEVbdJXKz1σ~2;rLro1O,=k)3+O79*o̽sRu`Akdʛ* W3Tu0PTLRGT  &ƫcS8Z[*І+0XPjA5{p/P{|p `T\{,7QXk}َ&Qnjߟsj̙ ۋB)ƒoϑ~i8pd'cVѕ+^!ܸ~Ag$UEn4pogG!4)R:GU"/3HvGs>*c9#RueJo87ؗP=Yn3Kl[*z1Gj+EſDFhwJUu>fV3w*&!nTٝ?5͒W)1. co!E微5_H:#wᮥ&r;lP[/;V5^zhƏ* HyH^#(_+fdl8w,?h=~nrIX9rYX4΋"2tŋOzY#JQ;s ciwuB+a(3-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:t  META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-signerInfo1-wrong-or0100644 0000000 0000000 00000000222 14763776540 032754 xustar000000000 0000000 118 path=src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-wrong-order-signerInfo2-good.apk 28 mtime=1741684064.8040000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-wrong-order-signerInfo2-0100644 0000000 0000000 00000011563 14763776540 032562 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:h#Yq META-INF/RSA-2048.RSAyPi*(xhRjPQkv2 tҼ+kְ_%Pf뙑WXxdҖeeiX:vlL5}wyM6ٓX@^cAH UGg$PZ߄@9! 8a` `8*}=PGD`P)T4#`) Fvv4БBz\t`sM8'uK>b@" 2܈u)eWD>`ȏ><`O!;*[8N+M_e I֬5g_0o8-c)UCբΨq9f^MZ3|nk>VDo,^1 UY`+1ã#}FtpSFc^GKO ޣk]% IMد;;y{N ތT?~`y$GGFm.DZ 7cd2Z\Y#@P(b!j`r*4B8#$PL+4NG<32 7#P)֯Ei2vFpΈz-V8}wS?%dW2nVz G±Բ1ucyf"8{ r0뉣ypё%ujcY+T}/l9CZggiiY^jle YQx$5,7ck硋0tØUS#D5bh̵ö́xfq{ {o8NSE%J ]<)E"#\_h$\Z!<ұN+Yc@U;}睞}}3 (ߓHe iC;:j&;Z)oQ6!Tv.?bL?;J4-i{UW#fEԅ$GGv}ORNv=7z?e<*{X!eT!pw [mZ8Q0m}ݷldhXi(,mzBS:%"nFC"*B:]և—TR:a{G!O`ԸzY$YSA[5F>i=](HDnת #n}; *I92F6U*wRV]v.f$$HI0`yfV7u=Y`u. .?msA9M6v[I[J%!m3㍤Ӊpa;1Cg.U2-a7}Cm.Oc8l@R/PK!:MQMETA-INF/MANIFEST.MFeMO0; 6Gà,@E66w1]Zlc^4&&#Z!pQ1i1V$I/.1o3K$Fh>-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:h#Yq  META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-signerInfo1-wrong-si0100644 0000000 0000000 00000000226 14763776540 032753 xustar000000000 0000000 122 path=src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-wrong-signature-signerInfo2-good.apk 28 mtime=1741684064.8040000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-signerInfo1-wrong-signature-signerIn0100644 0000000 0000000 00000011561 14763776540 032762 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:+R%o META-INF/RSA-2048.RSASk4 1f0F2"F}cq܆PȽc5 5JçKK ӺmhքBN[Ln;T|yyFoBcMҝpBk5'LP4y*7Sˑ%ڲ~T i~V|,h BBmx+9yvYWq҃iPӣKVO>]m&(@,Wz&5JM\}jx'c jn;. fyK2_Z~|i&^V(n6J6g hxDg$(?PK!:MQMETA-INF/MANIFEST.MFeMO0; 6Gà,@E66w1]Zlc^4&&#Z!pQ1i1V$I/.1o3K$Fh>-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:+R%o  META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-wrong-content-type.a0100644 0000000 0000000 00000000034 14763776540 033023 xustar000000000 0000000 28 mtime=1741684064.8050000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-wrong-content-type.apk0100644 0000000 0000000 00000011140 14763776540 030217 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:G,^META-INF/RSA-2048.RSA3hbƩiA!&}Ll m,L  414hb| .w>BFn>aCA~6PfD]# 9q^C3ccCCSs(q^c Cs(&F%dÁbnbgs15122\x0T`q*/4zx'j W)snY>~;Jb<.}|2_'SOk9Lx {O]WeIu[:==%;V92梩+Jrhd8V+pzKիë~|zT:WFn$y!m_60wuY1Ϟ38wAyeJU=]سɢk҅E:wh0*~X'zĽ  dA'"""Τ[,Po1RufƶH|y2Xv< ,̌"ԌS~nkz"{ƟNg?g?1فz~nw՟3ë^ [|OBՕ~K_|}sBMOx9<_RfM ֬7'aBe__#RcϫW2Hg_8N!s[ O20mټ3Kc?]Q%WsG71pn|V0q"%u]yԌZ_9AksM7}UTusumiZj8sԊ#ro xr2"e,&} ’Et?_ᦟE?4lַ[7e&F$3n?vzKZ~z̕B.^2tʚ35k?8ĪJ=^=oNR5޶T7+"ˮ&&H+Jr<>+kZ||KKT7M&yI/߂'Jޭ7͝DKu}S*3z^'[ot -hV1XܾI{ɼ*IڱgSL()y6vX囓7PK!:MQMETA-INF/MANIFEST.MFeMO0; 6Gà,@E66w1]Zlc^4&&#Z!pQ1i1V$I/.1o3K$Fh>-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:G,^ META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-wrong-digest.apk0100644 0000000 0000000 00000000034 14763776540 032204 xustar000000000 0000000 28 mtime=1741684064.8050000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-wrong-digest.apk0100644 0000000 0000000 00000011136 14763776540 027052 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:%10\META-INF/RSA-2048.RSA3hbƩiA!&}Ll m,L  414hb| .w>BFn>aCA~6PfD]# 9q^C3ccCCSs(q^c Cs(&F%dÁbnbgs15122\x0T`q*/4zx'j W)snY>~;Jb<.}|2_'SOk9Lx {O]WeIu[:==%;V92梩+Jrhd8V+pzKիë~|zT:WFn$y!m_60wuY1Ϟ38wAyeJU=]سɢk҅E:wh0*~X'zĽ  dA'"""Τ[,Po1RufƶH|y2Xv< ,̌"ԌS~nkz"{ƟNg?g?1فz~nw՟3ë^ [|OBՕ~K_|}sBMOx9<_RfM ֬7'aBe__#RcϫW2Hg_8N!s[ O20mټ3Kc?]Q%WsG71pn|V0q"%u]yԌZ_9AksM7}UTusumiZj8sԊ#ro xr2"g,} ’Et?_ᦟE?4lַ[7e&FU_]wz䥭t-@xvG5/ٱ@Fvs91nvfxQ';TX2Q #_lϗ|K"~pm*v:^Q'!.aOmPt3c{Le:(g5yGJ*V`U7|]R/*uܓ.W%\,]6!c¯oguHN]O -v%>WJSPK!:MQMETA-INF/MANIFEST.MFeMO0; 6Gà,@E66w1]Zlc^4&&#Z!pQ1i1V$I/.1o3K$Fh>-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:%10\ META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-wrong-order.apk0100644 0000000 0000000 00000000034 14763776540 032040 xustar000000000 0000000 28 mtime=1741684064.8050000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-wrong-order.apk0100644 0000000 0000000 00000011137 14763776540 026707 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:I 5]META-INF/RSA-2048.RSA3hbƩiA!&}Ll m,L  414hb| .w>BFn>aCA~6PfD]# 9q^C3ccCCSs(q^c Cs(&F%dÁbnbgs15122\x0T`q*/4zx'j W)snY>~;Jb<.}|2_'SOk9Lx {O]WeIu[:==%;V92梩+Jrhd8V+pzKիë~|zT:WFn$y!m_60wuY1Ϟ38wAyeJU=]سɢk҅E:wh0*~X'zĽ  dA'"""Τ[,Po1RufƶH|y2Xv< ,̌"ԌS~nkz"{ƟNg?g?1فz~nw՟3ë^ [|OBՕ~K_|}sBMOx9<_RfM ֬7'aBe__#RcϫW2Hg_8N!s[ O20mټ3Kc?]Q%WsG71pn|V0q"%u]yԌZ_9AksM7}UTusumiZj8sԊ#ro}xr*(,YG!cu~nY3@-o}uB!rND,M kr\>{uCšw'Խ?ȡkT~2KGe܈s?'elR9dSW*{AS]9E+9w~ElE!iK KX.Yd&'3҈?>{ruE\{֨{#L;y瞍;ҝ*=z̸ffS8q\*Ϧ>;'[6K F o]-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:I 5] META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs-wrong-signature.apk0100644 0000000 0000000 00000000034 14763776540 032726 xustar000000000 0000000 28 mtime=1741684064.8050000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs-wrong-signature.apk0100644 0000000 0000000 00000011140 14763776540 027567 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:^META-INF/RSA-2048.RSA3hbƩiA!&}Ll m,L  414hb| .w>BFn>aCA~6PfD]# 9q^C3ccCCSs(q^c Cs(&F%dÁbnbgs15122\x0T`q*/4zx'j W)snY>~;Jb<.}|2_'SOk9Lx {O]WeIu[:==%;V92梩+Jrhd8V+pzKիë~|zT:WFn$y!m_60wuY1Ϟ38wAyeJU=]سɢk҅E:wh0*~X'zĽ  dA'"""Τ[,Po1RufƶH|y2Xv< ,̌"ԌS~nkz"{ƟNg?g?1فz~nw՟3ë^ [|OBՕ~K_|}sBMOx9<_RfM ֬7'aBe__#RcϫW2Hg_8N!s[ O20mټ3Kc?]Q%WsG71pn|V0q"%u]yԌZ_9AksM7}UTusumiZj8sԊ#ro xr2"g,} ’Et?_ᦟE?4lַ[7e&F)R-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:^ META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-signed-attrs.apk0100644 0000000 0000000 00000000034 14763776540 027575 xustar000000000 0000000 28 mtime=1741684064.8050000 src/test/resources/com/android/apksig/v1-only-with-signed-attrs.apk0100644 0000000 0000000 00000011137 14763776540 024444 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlT=oAKCcȇc)6  @"E:c.8} *~ ߑn7ެmYݛvv۵#d% \s>_7N$~'D.]߈4ctEha߯ Gw os&0}ɘI4log-07I=TN&f~_a*6w23: ݭMμȈ{Pz\䚝15fKȭ "5Gc1=cDX'{7qKOqC5s; qS.c]=%NԭkEIJ'y?4.Tx>:#ȜՕ3ٲ&+h+*ئKm1CA!D C.'}'wxyf8g֥IJL=o8\ӚLv2f 'R._0ܴSǶ;qcpkw9.8%G11#us##!uւѲYrrG.8u޲/+=:˗,x}ww/PKmzPK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(a:vduɽ,^8)$hPK!:&}]META-INF/RSA-2048.RSA3hbƩiA!&}Ll m,L  414hb| .w>BFn>aCA~6PfD]# 9q^C3ccCCSs(q^c Cs(&F%dÁbnbgs15122\x0T`q*/4zx'j W)snY>~;Jb<.}|2_'SOk9Lx {O]WeIu[:==%;V92梩+Jrhd8V+pzKիë~|zT:WFn$y!m_60wuY1Ϟ38wAyeJU=]سɢk҅E:wh0*~X'zĽ  dA'"""Τ[,Po1RufƶH|y2Xv< ,̌"ԌS~nkz"{ƟNg?g?1فz~nw՟3ë^ [|OBՕ~K_|}sBMOx9<_RfM ֬7'aBe__#RcϫW2Hg_8N!s[ O20mټ3Kc?]Q%WsG71pn|V0q"%u]yԌZ_9AksM7}UTusumiZj8sԊ#ro xr2"g,} ’Et?_ᦟE?4lַ[7e&F3u3C/6J`gXt.\S{݅-Xz;\bcl%%³3W%\giǼ-&·y"9T{lg[Yc6`紜0\6/޲c_YH;"4G꧖l]~W2aû'ܿ}aI{TU!d\Jk:+>Ԅgb+_oy&zMy{˅Wr]}RDoGdZYw]w PK!:MQMETA-INF/MANIFEST.MFeMO0; 6Gà,@E66w1]Zlc^4&&#Z!pQ1i1V$I/.1o3K$Fh>-H߭JN-8W6S*'\C+ mw@nG8)|nx Ez[+h !0 _1 w 9&̑tvp_О[44Gr"X".v~-5eY(iz9sNGPK !:!O resources.arsc5PK!:mzAndroidManifest.xmlPK!:T  sclasses.dexPK!:C` META-INF/RSA-2048.SFPK!:&}] META-INF/RSA-2048.RSAPK!:MQMETA-INF/MANIFEST.MFPK./PaxHeaders.X/src_test_resources_com_android_apksig_v1-only-with-stamp.apk0100644 0000000 0000000 00000000034 14763776540 026315 xustar000000000 0000000 28 mtime=1741684064.8050000 src/test/resources/com/android/apksig/v1-only-with-stamp.apk0100644 0000000 0000000 00000030733 14763776540 023167 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlTMn1}44i4IK `M@HE!iD3C`X qqX~ߏo&@_%py{kHmCbD'?ZDI#7^nwg+q=Ȭ/5N8sT8xF.BL9)fH8,w]Ƙ6k2E:xęjNgءGvؗz15t|\1DY?s?KrU]<)Wץ]f}#UpاX#ƦxV$m>1}1gJk Gu>})[8{N~EX#%>{ mo.Dc/yzs*b-BP[:7& \"ĄLʷfoJ 8nYVmjZ,Y])[p5]bep@\[p3s/l8]8k Gڼɴ5ޟGh5\XHȜ&䠢IՔP]&U-PsAٖ~CMetZ9^WBCS@ELyegR[HJ.SÒ5Tv]KkO a |M YS !0h9'+BF~ُVsꖊv܈;Vv?~GE-d,B, C #!s079<γplM1ZT؟9%(i_"e-8sKԍR_oF5%6 84ץdC˸H5//YlHID2rGZ$||E+D5 ?d·mΕ^{He^s{pyIz2-rĠ̅tϾ utz~j=ҮǵnS4߻knŨ|V]1UiQ WL'3w5~Եe7k:d&n>Š6,)-%ubVj-KKX1|efJƊAERR/HDMVJE:SPKy<&PK!:S% stamp-cert-sha256 ]@Zc/]z 1+;Xtkc ylA1 :VwW&ue>iZzT~)zru_ tit1֜VzL۳wx^3p <5PK!:{&META-INF/MANIFEST.MFen0; qAVL@ajۍ*hQG%3CFl.KKN`M >E(O;QFe˂úeI0'se0/(83Y :z6\^lD^Zvr.yZ77VQٺre_iA!0iInL7˕e f8 FݑQB.УHڸBol Lr$Bࢪpی&쭗|nq~cs|ʙQ _% m00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*_7(m|_wP L\S[յ6y?<Ў^Y$k/}‹sxϨa_Wc·\Rïڧ+ߎWZ1ó- f{Ȥ?7ü gz1=֨ߡ1*v!ܠiEoh VcȜx@uWN|->`c1{.Ƿ.y-jvhOY&ܤ+N;n4~EWǖ۫czu^"3A5erz#r7ims^e?GRspi:I\|iw6.[!z%Wq)_2sJ󙹧r>PKO~PK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(}Gw779ro S䤩8#~>|-AH(p-^Hbj~IQt~3/I&kd(>4¦BvU#=w472jd%^aM̿شϳG/+%U'bh-w^CxsB8j[a! sG1" cM{p tj\cAP#}{qz 6n N1+Y n,D $Ebʛ;Y1Ky+|W[Yn.uc]HW:'|PK!:fK&META-INF/CERT.RSA3hbajhδ%נ%ѐۀUIqA_&M03121q2en W 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s* O9)_&F Z&qSgO+ril^/py0(g׺Vb6eUU6mQ:o*H پ&2&KRߎw1`s]tYMA<&E_,V%n g ŜzdwJ=q}u޿g$Lʱ3Z1sy,tw+U^vuܛ,LO|vQi+ff!{9q9?ܘmffJkY.:~䞼moPK!:o1KMETA-INF/MANIFEST.MFen@E&6 (bM\ R^":A60c(Я5MM=ǁEeL˔SF~OV8a0בĦEi3eQ  +{.Q4q6`4fM\Z86i6̧P GF\-Jir3֙sA&zfV %.`9~[AG_zjyYTd+u9 #ikg oꇴD+&u_(˃7%z ;9\zlp{QQ_T=H 0>PK !:;k薸 resources.arsc5PK!:O~AndroidManifest.xmlPK!:T  classes.dexPK!:Aq|5 META-INF/CERT.SFPK!:fK& META-INF/CERT.RSAPK!:o1KMETA-INF/MANIFEST.MFPKn./PaxHeaders.X/src_test_resources_com_android_apksig_v1-sha1-sha256-manifest-and-sf-with-sha1-wrong0100644 0000000 0000000 00000000034 14763776540 032240 xustar000000000 0000000 28 mtime=1741684064.8060000 src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk0100644 0000000 0000000 00000011405 14763776540 030671 0ustar000000000 0000000 PK !:;k薸 resources.arsc5 ($hTiny App for CTS [b U b` one two three]  . Tiny,   . App,   . for,   . CTS,   android.appsecurity.cts.tinyapp `@$ attrstring4 app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlnA&I@B(b"  @"  X"x@7dm :;s=gƎQN T֧HZTy <]g>_7(m|_wP L\S[յ6y?<Ў^Y$k/}‹sxϨa_Wc·\Rïڧ+ߎWZ1ó- f{Ȥ?7ü gz1=֨ߡ1*v!ܠiEoh VcȜx@uWN|->`c1{.Ƿ.y-jvhOY&ܤ+N;n4~EWǖ۫czu^"3A5erz#r7ims^e?GRspi:I\|iw6.[!z%Wq)_2sJ󙹧r>PKO~PK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL("^ZxCd Gvvv范&=BBQ5Fޔk揎#R-tV)ΐV&(㥫sb I 7ApQQz::waT>;OG^x0Y{߳j_̿ذɳGݞ^=dz!8 0 ڌ ݹg@:k/_0 (ty=s}u-[q#дr"ٺNmדqȷr M.&u/H V-O[9]'<\pIҗMTu4V\=w3:sd[[?/PK!:ۣ&META-INF/CERT.RSA3hbajhδ%נ%ѐۀUIqA_&M03121q2en W 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s* O9)_&FS䞩_^z%"iK/L*䫣]]/m2oUEtՙK=Izƞ<ɟDT*VnBcA$U6ygy>LDnwDm4Wؖ[mf |!7AwU,|Lv#oS>Oص\auB"Yi ~p}WGoO2,W69zٟs.XY7kg`}n?<[{fniPK!: KMETA-INF/MANIFEST.MFen@E$6 ȣ@ 40* @4mS790-SRYzN1pMa BaV'B(c1.Պ4q6zLq2fẍ\Y9;6m@6Ć4REU4#+.ɍP- )u.gSA6=M~+P|{Ѱ4fk9苲*YUO(@KR[ YD8@/?vKRStCZ"&u_7-zt;5fѣz.:EFhM PK !:;k薸 resources.arsc5PK!:O~AndroidManifest.xmlPK!:T  classes.dexPK!:y1R`|5 META-INF/CERT.SFPK!:ۣ& META-INF/CERT.RSAPK!: KMETA-INF/MANIFEST.MFPKn./PaxHeaders.X/src_test_resources_com_android_apksig_v1-sha1-sha256-manifest-and-sf-with-sha256-wro0100644 0000000 0000000 00000000214 14763776540 032067 xustar000000000 0000000 112 path=src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk 28 mtime=1741684064.8060000 src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.a0100644 0000000 0000000 00000011405 14763776540 031710 0ustar000000000 0000000 PK !:;k薸 resources.arsc5 ($hTiny App for CTS [b U b` one two three]  . Tiny,   . App,   . for,   . CTS,   android.appsecurity.cts.tinyapp `@$ attrstring4 app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlnA&I@B(b"  @"  X"x@7dm :;s=gƎQN T֧HZTy <]g>_7(m|_wP L\S[յ6y?<Ў^Y$k/}‹sxϨa_Wc·\Rïڧ+ߎWZ1ó- f{Ȥ?7ü gz1=֨ߡ1*v!ܠiEoh VcȜx@uWN|->`c1{.Ƿ.y-jvhOY&ܤ+N;n4~EWǖ۫czu^"3A5erz#r7ims^e?GRspi:I\|iw6.[!z%Wq)_2sJ󙹧r>PKO~PK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(hPFDvXPT[-ǘGw779|!*"H('_Ek2Qk< m"gҖsCeyQXK˜I'?"_ ^3X5gqI ܢA-e2㑗|&~tL*`+[:NEv帠"gm8һ`/4Zl~$s[V7لuJ) QW˖rɣk(z5Ex] 9S~UoYq}M+JM1Jxc :wSB3@ AZDJ5vи}\,0/t<\G@{~DYzjeΚ PK!:s8&META-INF/CERT.RSA3hbajhδ%נ%ѐۀUIqA_&M03121q2en W 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s* O9)_&FsOz gէ`)9ȥ[4/eIf]4ٻ~ܸ-Wf G?9F.>[a}c69$sX0mj9[|]?4jdۊV)WT8g {XpUm陼x'el -a7uLLowu`#O<}qZ_vۤOh8:g87T;PK!:/JMETA-INF/MANIFEST.MFen@E&6 (bM\ E^":A60"c(Я5MM=DžEeLˌVQ +rkP沢´9Srj^Օ{dzLσgb'K&XoSmAD]hܭ`'0]-s0V!ՎңP5-OPK !:;k薸 resources.arsc5PK!:O~AndroidManifest.xmlPK!:T  classes.dexPK!:'-,<}5 META-INF/CERT.SFPK!:s8& META-INF/CERT.RSAPK!:/JMETA-INF/MANIFEST.MFPKn./PaxHeaders.X/src_test_resources_com_android_apksig_v1-sha1-sha256-manifest-and-sf-with-sha256-wro0100644 0000000 0000000 00000000034 14763776540 032067 xustar000000000 0000000 28 mtime=1741684064.8070000 src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk0100644 0000000 0000000 00000011402 14763776540 031042 0ustar000000000 0000000 PK !:;k薸 resources.arsc5 ($hTiny App for CTS [b U b` one two three]  . Tiny,   . App,   . for,   . CTS,   android.appsecurity.cts.tinyapp `@$ attrstring4 app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlnA&I@B(b"  @"  X"x@7dm :;s=gƎQN T֧HZTy <]g>_7(m|_wP L\S[յ6y?<Ў^Y$k/}‹sxϨa_Wc·\Rïڧ+ߎWZ1ó- f{Ȥ?7ü gz1=֨ߡ1*v!ܠiEoh VcȜx@uWN|->`c1{.Ƿ.y-jvhOY&ܤ+N;n4~EWǖ۫czu^"3A5erz#r7ims^e?GRspi:I\|iw6.[!z%Wq)_2sJ󙹧r>PKO~PK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s* O9)_&F[a#=^U;yZީ\\آh91]v%S28ج p2[7uaimK-S}xbV^ǰ2<\GCgSӲד-}eNJ5o`-fcl~Zpӯ[6r|=`cLnքnye>^h|7hTEq>|wE"{K)D'-nX/ /4{}xh36닽*M5?wLgn[PK!: KMETA-INF/MANIFEST.MFen@E$6 ȣ@ 40* @4mS790-SRYzN1pMa BaV'B(c1.Պ4q6zLq2fẍ\Y9;6m@6Ć4REU4#+.ɍP- )u.gSA6=M~+P|{Ѱ4fk9苲*YUO(@KR[ YD8@/?vKRStCZ"&u_7-zt;5fѣz.:EFhM PK !:;k薸 resources.arsc5PK!:O~AndroidManifest.xmlPK!:T  classes.dexPK!:d@|5 META-INF/CERT.SFPK!:W# META-INF/CERT.RSAPK!: KMETA-INF/MANIFEST.MFPKk./PaxHeaders.X/src_test_resources_com_android_apksig_v1-sha1-sha256-manifest-and-sf.apk0100644 0000000 0000000 00000000034 14763776540 030055 xustar000000000 0000000 28 mtime=1741684064.8070000 src/test/resources/com/android/apksig/v1-sha1-sha256-manifest-and-sf.apk0100644 0000000 0000000 00000011404 14763776540 024721 0ustar000000000 0000000 PK !:;k薸 resources.arsc5 ($hTiny App for CTS [b U b` one two three]  . Tiny,   . App,   . for,   . CTS,   android.appsecurity.cts.tinyapp `@$ attrstring4 app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlnA&I@B(b"  @"  X"x@7dm :;s=gƎQN T֧HZTy <]g>_7(m|_wP L\S[յ6y?<Ў^Y$k/}‹sxϨa_Wc·\Rïڧ+ߎWZ1ó- f{Ȥ?7ü gz1=֨ߡ1*v!ܠiEoh VcȜx@uWN|->`c1{.Ƿ.y-jvhOY&ܤ+N;n4~EWǖ۫czu^"3A5erz#r7ims^e?GRspi:I\|iw6.[!z%Wq)_2sJ󙹧r>PKO~PK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(2UFo&܋f'L>,C"Qb?GccOw/n /EB:a{|Lqy([*p+дj"ٺLq\TqC{]H\l_Ҳ!}?$4zHuǍSTϸ{ϥx**Xxn{mA{ PK!:Y'O%META-INF/CERT.RSA3hbajhδ%נ%ѐۀUIqA_&M03121q2en W 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s* O9)_&F897 )]|T>;_mIWq [Jx_7(m|_wP L\S[յ6y?<Ў^Y$k/}‹sxϨa_Wc·\Rïڧ+ߎWZ1ó- f{Ȥ?7ü gz1=֨ߡ1*v!ܠiEoh VcȜx@uWN|->`c1{.Ƿ.y-jvhOY&ܤ+N;n4~EWǖ۫czu^"3A5erz#r7ims^e?GRspi:I\|iw6.[!z%Wq)_2sJ󙹧r>PKO~PK!: classes.dexkA&i4֚CڭAJDP/L7cm: ihQPM@[/*੠SIJM7wξ̼[ pn]]d.DJ09'/)/MhmhWQ!,H,#w/".2!KE56r<@"/Z\ ׁ˖Q$jqi4wܥǯL5 T>:7hZ巴hkkuUhU-Njky1Qi1cמu_ImC'Q'+D=:fxIT ]uy0RCZ+X'pokd8 8td)ϕ~Ρs~.͗x"pZfT0Hr<{좐*1`WhaYBaśyˋ5Ҷ3sTu6fJ#üu,b< woH3;#8z8CԹ- ͺ~hDhC7F#QvL(2UFo&܋f'L>,C"Qb?GccOw/n /EB:a{|Lqy([*p+дj"ٺLq\TqC{]H\l_Ҳ!}?$4zHuǍSTϸ{ϥx**Xxn{mA{ PK!:Y'O%META-INF/CERT.RSA3hbajhδ%נ%ѐۀUIqA_&M03121q2en W 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8#+s#?PƒIs3tVyid?\TcHr/NviC1P;4qg}#Y͗]:?q^lľ^'aڤS{z~/_HݿNE)߱j1ŗqǔw4M0XUC#yJ^^֫_^m^ӣ„D։2bWt{%1 l"䷁JNQqĹKO.[}VžM].ll/ɿCQ_:^gbfd`\`g :Y>1q&bzٖB73EȃY$ +)fa5`fd̠fs[5t=9Sodwc(l^ROb{@\⣭+j}Ґk4җZ6k'm7fݽ89, 7ȾoH/ٕ^AD:ӾYw 9J-(mp_$xqnٕw^*h_*rU|?sK;}ֆ)ʣf\: r?^{8-m*oKR&Fw`s* O9)_&F897 )]|T>;_mIWq [Jx:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW-|{^r*e^Z)n)k]NA#6nwvkMSd^<|W*-O$.% wK;3gsf ,Pǝ4NNX#vĀ8#߉/.qL'>_F h3'ЗE">Y%c%=>^  x3?dXF~C9Kl6[9=l[>΍۱c2Νl5#Oδ9{lLUvt1=5ipSے5lCF2L9r:(z>s˾>Ӿ*U茻ڧ3&Nr$MBCT&)(ŔRDJ53JDD@|.H|QU;yjrw'yå4u~ :q)npͼOs6UOkW054NjX75Rnf|/FYuՇ|SN˧^ˎAQxmbcv)嬧L{ _gV:ǯb*_}w]VsPKx/PK!: classes.dex]HQ|U~mJ䃄:A}X u溎.eHK=R`kPASoA/=t޻"939]=>ɧ/|+쪟Qg]-Qȝ3y4GˆD~Ȝs BA3ͩC;O̬P4/qu4T#V}fRGPe\a$C×,Ul$GIDst$||E+ Oف 3Y?(fڝ =jͧ.{nw< 83>&Y 41zz{wz[Zԫ}f[ZKk5Ms0FjbZz`jo>9ܨ)IwJͤm [YW%b6)eAyNiIPk_Z\Ɗ',32Vj-;zF\ jr-ԙbPKr"PK!:S% stamp-cert-sha256 ]6㭢vrT#|jHJXQStE!e^u̕5ZBYz!Ba)DvR rmxTV`{xa}D54T-2֖䬴,?n=}tPK!:ܗ[;META-INF/EC-P256.EC3hb2gjhδIàIѐ߀3̓1qAcAc .`fbdbd8ſw".6s,LB|l̡,<ɺFfr⼆fƆ&fQ&P.V=l`S333;1,U:)<};/+פ][tn¥-NsOl~rr~Ps˖cc"l3@HUh3' A,b 032GfO7&E, r}".]8?9$Yqk@%?-i>@Zc/]z 1+;Xtkc ylA1 :VwW& >1CJ'~Ҿ={w՝,ݱ;9sOj,ȣ` 8r˖DPK!:bÙ'META-INF/MANIFEST.MFeMo0; :e$; 1Z j[-JݯY749^{xRPK1,M5}oC[32L V *K91ŬH04gjm:ɹmi:0ma\g"]5?(a"v16ɾ )MD#w8X>WQ׿nq'[?PBѳulP!==$G|kQUKiz?y"jǰE?iQ,aԹVSV{k1F;6 q{w,( u|DO,+~).$UiAbtp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,U  TPH0F! :wYw2r*\Mp!#bi iYoJjKh[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDhS ,( u|DO,+~).$UiAbuq0m0 UҲ%0 *H=010U ec-p2560 180713174151Z 280710174151Z010U ec-p256_20Y0*H=*H=BLr< =bPx3nuSJkPa 'b-b vzD"P0N0Uy+D(^_0U#05h[02 qC[0 U00 *H=H0E 'i4yĮcR, +C0!`l` $1I*kgo;xp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,Uyq0m0 UҲ%0 *H=010U ec-p2560 180713174151Z 280710174151Z010U ec-p256_20Y0*H=*H=BLr< =bPx3nuSJkPa 'b-b vzD"P0N0Uy+D(^_0U#05h[02 qC[0 U00 *H=H0E 'i4yĮcR, +C0!`l` $1I*F0D `:z,y+RI^"*X X=͎$и\3PduDkTPH0F!WM/p 9,0xE*x,'!!3ZŠ3crgdV\]b[0Y0*H=*H=BLr< =bPx3nuSJkPa 'b-b vzD"m me00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*v\T @N⃧/$Z!Hik+|ii=)T_Yca 1jOj+ (՘k{PLPN||Ꮚ o*[ӒBh@.@jɌ3`Aǟ8d B`.(!MRnb?8-_>v\T @N⃧/$Z!Hik+|ii=)T_Yca 1jOj+ (՘k{PLPN||Ꮚ ')sٷ|u3a*+-;$3\3vqL7zuS?~ב N wM'k/dH&9 )P,H$Mת}L4M]_b D{$ĺH2CNLF. ;DhcN JjKΫN$rK"c-SXj Lpx-r$ʓd%jLovwerBAPK Sig Block 42PK !:9gGÄ resources.arsc5PK!:x/AndroidManifest.xmlPK!:r" classes.dexPK!:S% R stamp-cert-sha256PK!:m] META-INF/EC-P256.SFPK!:ܗ[;4 META-INF/EC-P256.ECPK!:bÙ'2META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v1v2v3-rsa-2048-negmod-in-cert.apk0100644 0000000 0000000 00000000034 14763776540 027750 xustar000000000 0000000 28 mtime=1741684064.8080000 src/test/resources/com/android/apksig/v1v2v3-rsa-2048-negmod-in-cert.apk0100644 0000000 0000000 00000030627 14763776540 024624 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:NK$META-INF/RSA-2048.RSA3hbifjhδĠѐ߀3̓1qA&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM| @qN&F & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z|hۚ/>OLv`}{D$3G]gx𪰗j=~3S_humm\Pg퓆^yϗԲY=m5aaXAEPF}WȮX 'ҙ5κSVjiDik"S?s[6̮ROE+zlTɕcM _L6HIgfW5WNګiiglgU9hݜG}[61z;0(`Mh9#&a`e[lNWQ[wtg6tf̷]Xo zBT]|=HF&8EGYַp6?'z0I%{iO\EJ씗nl2ޟk6L}Ok_yhִ nyߔzzurOS}\;Gfc{]Qkxu3^jRu"6~~#CHŗy}DbZW(dxe٫bPK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcyHݿ 9%Y6U2 /șǫIrrW;ٿ]M&HtLvA?Iڽ{‘ı*1lk;;ҟZCI{Xˈ^ޝǚk̐g+9y4 8IʵTz$i.K`3Fq?Bjqwu\=&e/珟cj[ONW εTD2&asޭެB-8f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:NK$ META-INF/RSA-2048.RSAPK!:DmQ^META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v1v2v3-with-rsa-2048-lineage-3-signers-invalid0100644 0000000 0000000 00000000213 14763776540 032176 xustar000000000 0000000 111 path=src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-invalid-lineage-attr.apk 28 mtime=1741684064.8080000 src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-invalid-lineage-attr.ap0100644 0000000 0000000 00000040627 14763776540 032025 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&0!(g/DRgX'(![G_1+M:U z _% !00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ!nC?Z7@~f5jy$WN[[mF>|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[4hS,(`( E~m̍M(C-# 1X>&0!(g/DRgX'(![G_1+M:U z _% !00 Г}=׽0  *H  010U rsa-2048_20 180619164943Z 280616164943Z010U rsa-2048_30"0  *H 0 {3j?[3+2 b1Φ2RY~sw*^u?iw}ER mb3KXTUßUgn~'[\^cvG#aL5=Zɖ3cM$IO*o?8spPDL oZFQkE&g$>/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."_ 2fڃ8MkQ % ƿ'a} >)3:hR0knbi*Sť5XiT0j wҐfbHaň=l̡LĞMe@ Qǣz#٤c r.F΍ 1~ r[-&zLQq%x [Pf6<7EJ/c#ceH FO,',(̑ ݝsvP?Y]Mfs}Z}wb'$0Xgpo*G όAu Kaw ,ObOxuYh:kJ;OT Ӥ`z%z[Uh\kmWW^b(y?g!8(!D|/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v1v2v3-with-rsa-2048-lineage-3-signers-invalid0100644 0000000 0000000 00000000034 14763776540 032177 xustar000000000 0000000 28 mtime=1741684064.8090000 src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk0100644 0000000 0000000 00000040627 14763776540 030426 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&0!(g/DRgX'(![G_1+M:U z _% !00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ!nC?Z7@~f5jy$WN[[mF>|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[4hS,(`( E~m̍M(C-# 1X>&0!(g/DRgX'(![G_1+M:U z _% !00 Г}=׽0  *H  010U rsa-2048_20 180619164943Z 280616164943Z010U rsa-2048_30"0  *H 0 {3j?[3+2 b1Φ2RY~sw*^u?iw}ER mb3KXTUßUgn~'[\^cvG#aL5=Zɖ3cM$IO*o?8spPDL oZFQkE&g$>/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."_ 2fڃ8MkQ % ƿ'a} >)3:hR0knbi*Sť5XiT0j wҐfbHaň=l̡LĞMe@ Qǣz#٤c r.F΍ 1~ r[-&zLQq%x [Pf6<7EJ/c#ceH FO,',(̑ ݝsvP?Y]Mfs}Z}wb'$0Xgpo*G όAu Kaw ,ObOxuYh:kJ;OT Ӥ`z%z[Uh\kmWW^b(y?g!8(!D|/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v1v2v3-with-rsa-2048-lineage-3-signers-no-sig-0100644 0000000 0000000 00000000034 14763776540 032022 xustar000000000 0000000 28 mtime=1741684064.8090000 src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers-no-sig-block.apk0100644 0000000 0000000 00000040627 14763776540 030464 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&0!(g/DRgX'(![G_1+M:U z _% !00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ!nC?Z7@~f5jy$WN[[mF>|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[4hS,(`( E~m̍M(C-# 1X>&0!(g/DRgX'(![G_1+M:U z _% !00 Г}=׽0  *H  010U rsa-2048_20 180619164943Z 280616164943Z010U rsa-2048_30"0  *H 0 {3j?[3+2 b1Φ2RY~sw*^u?iw}ER mb3KXTUßUgn~'[\^cvG#aL5=Zɖ3cM$IO*o?8spPDL oZFQkE&g$>/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."_ 2fڃ8MkQ % ƿ'a} >)3:hR0knbi*Sť5XiT0j wҐfbHaň=l̡LĞMe@ Qǣz#٤c r.F΍ 1~ r[-&zLQq%x [Pf6<7EJ/c#ceH FO,',(̑ ݝsvP?Y]Mfs}Z}wb'$0Xgpo*G όAu Kaw ,ObOxuYh:kJ;OT Ӥ`z%z[Uh\kmWW^b(y?g!8(!D|/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#werBAPK Sig Block 43PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v1v2v3-with-rsa-2048-lineage-3-signers.apk0100644 0000000 0000000 00000000034 14763776540 031325 xustar000000000 0000000 28 mtime=1741684064.8090000 src/test/resources/com/android/apksig/v1v2v3-with-rsa-2048-lineage-3-signers.apk0100644 0000000 0000000 00000040627 14763776540 026202 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&0!(g/DRgX'(![G_1+M:U z _% !00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ!nC?Z7@~f5jy$WN[[mF>|&NyMhF ]6ʃu=wrO&b]GTh3[ʇ{+.1ImqRqI4\u `0]usTcL eP h/W 2 )9?p9X+2] وtP7 aB-r IǷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[4hS,(`( E~m̍M(C-# 1X>&0!(g/DRgX'(![G_1+M:U z _% !00 Г}=׽0  *H  010U rsa-2048_20 180619164943Z 280616164943Z010U rsa-2048_30"0  *H 0 {3j?[3+2 b1Φ2RY~sw*^u?iw}ER mb3KXTUßUgn~'[\^cvG#aL5=Zɖ3cM$IO*o?8spPDL oZFQkE&g$>/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#P0N0UZrrM@mx 0U#0?V2I6-a%L?\0 U00  *H  :Sv1uPh܈ T|${%82"Dc l} }?Ki ɑmcEy Zg¥M()Q/X$t 'M."_ 2fڃ8MkQ % ƿ'a} >)3:hR0knbi*Sť5XiT0j wҐfbHaň=l̡LĞMe@ Qǣz#٤c r.F΍ 1~ r[-&zLQq%x [Pf6<7EJ/c#ceH FO,',(̑ ݝsvP?Y]Mfs}Z}wb'$0Xgpo*G όAu Kaw ,ObOxuYh:kJ;OT Ӥ`z%z[Uh\kmWW^b(y?g!8(!D|/Jֳ|Ȱ/8YkQVzdͪ) \Ϫ qz__uO$F~z\ I#werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v2-ec-p256-targetSdk-30.apk0100644 0000000 0000000 00000000034 14763776540 026471 xustar000000000 0000000 28 mtime=1741684064.8100000 src/test/resources/com/android/apksig/v2-ec-p256-targetSdk-30.apk0100644 0000000 0000000 00000030330 14763776540 023334 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlSn1=$MȣiR胖+DS*X +4IFIhf@TB`Q!>o p|cgӈ{m4 (N'3'V)'Έw"{KCO;I%nDh@vI㘳g@}ghBDdL1{_y2Q,#8!g r5e5웴>}\oGv951gN~GLVvdxeM6!m s>1^E+bKD'N&_AǒK,zIT:]}[4m?pGdb6G2Hږc6t KSo#H%c9ԥUBGܑxk?>;"86 }ST@SJkĈVQ*$"̽~]f_77튼*:rpi9M᲎2\ w(ߢYiƳL yI&Cf:Zv/=>O8>Zt/k75h--۴͎Ѵ6%c[k/8ޒWgU^,`yN"jPK`zPK!: classes.dexMhA~d5mmlH\ iZjRPDNm71 -ŪAzqЦ !+|9F~ɏVҐs v܈;Vv?J7 >˦4B\(2/i3 JC #!s.179<plc?MssJ@QҚY$%eEFih7%"6 lR'Peb$GǗ,Շl@IDsH, V"Aë~Pδ/z=:Wڽ}ux1ypk|G=:0khc$ SC}~薖0:z]]ZhDM4LQ:cx]nQ WM]'4w5AԵe72d&f>Š:,)-)ubVj-KX1|efRƊAEǒR/؈DMVRE:SPKu qmi,( R/qbt&''$imq/9tp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,URNF0D d]z6a^u CCFr 8b 1uGRv1~أbߙ6[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD[ werBAPK Sig Block 42PK !:9gGÄ resources.arsc5PK!:`zAndroidManifest.xmlPK!: classes.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-10-signers.apk0100644 0000000 0000000 00000000034 14763776540 026111 xustar000000000 0000000 28 mtime=1741684064.8100000 src/test/resources/com/android/apksig/v2-only-10-signers.apk0100644 0000000 0000000 00000050320 14763776540 022755 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ ?: q:,( (Lc=j^]&X.Nʹ00q J(0 *H8010U dsa-10240 160331152710Z 430817152710Z010U dsa-102400+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#QbƣP0N0U:Ui.%0U#0:Ui.%0 U00 *H8/0,Qri /sP[D-w}:6.0,kwu2U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#QbH,( (Lc=j^]&X.Nc_0[0 .bj>0 *H8010U dsa-20480 160331183001Z 430817183001Z010U dsa-20480G09*H80,oRG"٦Y92+lS[2Z1MZn=Zq !_2h"2~At~zYc{6->rx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBdrx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBdnDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6ZnDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6ZU.cXKۜ' Ʒ8?\ P0N0ULŎ+m8<9i0U#0LŎ+m8<9i0 U00 *H=i0f1+]=.re>>$N)\ܾ&, ;|1UBu&3a_2YqML۰L0xarnf0d0-+4 ٙThCěu 85xnP߻ݏ:X0 W%"L(沦Ge2a>`i\iC~7]LX&c:x0v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ WLH@w#SGG E!O{%ã&Z!*Tm;U7Rnx뚷 aŅ00T Wt[/v"0 *H=010U ec-p5210 160331153122Z 430817153122Z010U ec-p52100*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzP0N0UEϭ CXve-0U#0Eϭ CXve-0 U00 *H=0BZMɨa{jx=HfZ)*Dh|Jnb^\CIe_4!DB-BqmbW1s2 ~FzUy{d̚8D8WZ<̠ӚW0Bh͸jdIb'[%M~ ;9p-MyģMKJ4buWYB>KuמB CHV҇ j4\Drda+.P3_{_%aR00*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPz{=,( (Lc=j^]&X.N00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=D-wZGbm!X~R-3fjX`,"|If߬X>S_HF?o"myU^jiu;9첋ma+lik10\<$A']^bÇ.WZ/7u/a%U r!&ňH@ 3&t39DW:R2%﫩*?塲M<h6_Ux^rΒ|qOV秮+)mzVowHDK/86AL OoG*xX%䬸% +&0"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=(qDV_I]1TyώUQ|ʗ i}9HЍQo݃^n񀃯iǽM@P'mclpWX _C"֓cXQ)G@($'hL0* ƵnҾJ݂*uoj$gSyyҾ{wc޺yHec@Y{vk&kIq9g\9gp"T_,DG((Fl+-^XO.Xv®FUC'v~ ̃Es1=l5=﫼`|(TCH\y?*OwB :/5_ņ91r֌.i@ΙT6ԡ|uEdJoִ'DN293k;:9]B wbY&cK|BȬdeC+]3eSǪpW|M%%C-2U0_~TVο$Ka2.qӾbaGj=)t("InO˘-?@[LX{Lߤr5<} ѫ+&k09.R sчدl (%)l/;],p:ɦ00  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~werB?APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKP./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-11-signers.apk0100644 0000000 0000000 00000000034 14763776540 026112 xustar000000000 0000000 28 mtime=1741684064.8110000 src/test/resources/com/android/apksig/v2-only-11-signers.apk0100644 0000000 0000000 00000060320 14763776540 022757 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ OVL qNL,( (Lc=j^]&X.Nʹ00q J(0 *H8010U dsa-10240 160331152710Z 430817152710Z010U dsa-102400+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#QbƣP0N0U:Ui.%0U#0:Ui.%0 U00 *H8/0,Qri /sP[D-w}:6.0,6̬W]""$S!T$S13e-B߿3jź00+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#QbH,( (Lc=j^]&X.Nc_0[0 .bj>0 *H8010U dsa-20480 160331183001Z 430817183001Z010U dsa-20480G09*H80,oRG"٦Y92+lS[2Z1MZn=Zq !_2h"2~At~zYc{6->rx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBdrx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBdnDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6ZnDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6ZgMtr[0Y0*H=*H=BLr< =bPx3nuSJkPa 'b-b vzD" LH@w#SGG E!O{%ã&Z!*Tm;U7Rnx뚷 aŅ00. VK"j0 *H=010U ec-p3840 160331153057Z 430817153057Z010U ec-p3840v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ P0N0ULŎ+m8<9i0U#0LŎ+m8<9i0 U00 *H=i0f1+]=.re>>$N)\ܾ&, ;|1UBu&3a_2YqML۰L0xasog0e0#D )']5wdL-j:5.uGd{1j驯KE3_|k"%L5JHu柦&3|6x0v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ WLH@w#SGG E!O{%ã&Z!*Tm;U7Rnx뚷 aŅ00T Wt[/v"0 *H=010U ec-p5210 160331153122Z 430817153122Z010U ec-p52100*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzP0N0UEϭ CXve-0U#0Eϭ CXve-0 U00 *H=0BZMɨa{jx=HfZ)*Dh|Jnb^\CIe_4!DB-BqmbW1s2 ~FzUy{d̚8D8WZ<̠ӚW0BLf)mwaeD]+Rs,EnsDl韐$O]褨+v1MA&3%Z$ѢhUeD/wIL07Θp!Š Σ'1fPh\Oq߾͋00*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPz{=,( (Lc=j^]&X.N00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=D-wZGbm!X~R-3fjX`,"|If߬X>S_HF?o"myU^jiu;9첋ma+lik10\<$A']^bÇ.WZ/7u/a%U r!&ňH@ 3&t39DW:R2%﫩*?塲M<h6_Ux^rΒ|qOV秮+)mzVowHDK/86AL OoG*xX%䬸% +&0"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=(qDV_I]1TyώUQ|ʗ i}9HЍQo݃^n񀃯iǽM@P'mclpWX _C"֓cXQ)G@($'hL0* ƵnҾJ݂*uoj$gSyyҾ{wc޺yHec@Y{vk&kIq9g\9gp"T_,DG((Fl+-^XO.Xv®FUC'v~ ̃Es1=l5=﫼`|(TCH\y?*OwB :/5_ņ91r֌.i@ΙT6ԡ|uEdJoִ'DN293k;:9]B wbY&cK|BȬdeC+]3eSǪpW|M%%C-2U0_~TVο$Ka2.qӾbaGj=)t("InO˘-?@[LX{Lߤr5<} ѫ+&k09.R sчدl (%)l/;],p:ɦ00  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~] LH@w#SGG E!O{%ã&Z!*Tm;U7Rnx뚷 aŅ 00 7%0  *H  010U rsa-81920 160404193246Z 430821193246Z010U rsa-81920"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnP0N0U]FsR$2/~?0U#0]FsR$2/~?0 U00  *H  hU_##B)q q;)1eG+)9RN&~W$`LYR8n \WmӔCzMaOiڒnR FSfv1$>3{ >\~Z{ {J~nt |)`tڝs3)1 tZ֝d:J蕯WSЃ(Ѝ$#&Pt\X2DvGê9ǢcS6w-aDfBHjAn"1_M+%w|]/O߂D*%'Y T=1' L}eI odw g8NR)dlo6"h9 r\h/[5,dGzNS4]sr #F7ԯ7@'Zb8.;:!憌Q]3:+8>6~^ 1V̋4L"rzE1r.7Lv1x =(+1 Kc ~=]#pփ,sy MI3V7up3Hi׈ˊGJpY{ASm!ԍ7YlC{fo;[cG(Rk33eRۃuRߨcvH- i=\hC,*#n'cY"d46S pfy s݉'H?b;Pؠ.  )U)avggVo;d85qڸ /WHNNa4_?oC~ިmU8aKv'˾ɴ9ʸ$sO¾C{`P:l7CEcv*$fibxswڥ-%۽:M]\7!U笑ݖ/vJ6?C$:Bv,2 M,ȥa!b! c*6`4rwX R2U E80Fj g{ (f·vk ly=</A Nd[g[rp>p6ȧj uhWzd;%56|$,o%tP/ ƫ8@Y$~~[)L$ Y{˝۰lM0h:q銜aq28c6jp]ƟG°U @̣f88Y$zܛr"\0u+DkkCO׊L|r2bHv{uFH2r67)ER+ЉU5M`M*lY-bͱ7>ZOfcO_'7mw- ,DpqUн@ |@xDܦ 9kv`[Q}'JWaX(DqOCgKn{3uW`U23K`Eoa8p+ _{Zo,9hE_bkw[?1rW|+nh;T~hG'Oƅ37 P<}q1ݞ2#G\-v?'\_ŽO^w,[*5P?A8XѮ./J] (=,lXmAujxD,ʟhje_xD8jF}mpj98bI/ݴd!$}QƤע&}xFvtH^{bUH&:ݕx- ^pOrDB oOh Q@_ J_G*YݜW@Ż)f;CKVƕ^UIBVXjOzӀqQDuBdKWx ~ImN&0"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnzwerBOAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK`./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-apk-sig-block-size-mismatch.apk0100644 0000000 0000000 00000000034 14763776540 031417 xustar000000000 0000000 28 mtime=1741684064.8120000 src/test/resources/com/android/apksig/v2-only-apk-sig-block-size-mismatch.apk0100644 0000000 0000000 00000007103 14763776540 026264 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qSLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00T Wt[/v"0 *H=010U ec-p5210 160331153122Z 430817153122Z010U ec-p52100*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzP0N0UEϭ CXve-0U#0Eϭ CXve-0 U00 *H=0BZMɨa{jx=HfZ)*Dh|Jnb^\CIe_4!DB-BqmbW1s2 ~FzUy{d̚8D8WZ<̠ӚW0B @YVzǶ>LǀRvf+"&HsHG]yUxX^肋A7B>W/U}X o$[.i"})aכЀzayT; 'p2,gVKpn泞00*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKs ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-cert-and-public-key-mismatch.apk0100644 0000000 0000000 00000000034 14763776540 031563 xustar000000000 0000000 28 mtime=1741684064.8120000 src/test/resources/com/android/apksig/v2-only-cert-and-public-key-mismatch.apk0100644 0000000 0000000 00000010046 14763776540 026430 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*kOojki"@\X2"G1p92]UȽ||Vqrk6k)yVll ͩkcqgɻ2G uв}U5E rB^Ӻ𐺎ʳ7&#AU[J?ԺL(sIV~@? 'N>O֭`p䉱y? Sb=QpŠ,`rFq6)l7y_ X]*mJ2:Oɖ &0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[APK Sig Block 42PK./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-garbage-between-cd-and-eocd.apk0100644 0000000 0000000 00000000034 14763776540 031274 xustar000000000 0000000 28 mtime=1741684064.8120000 src/test/resources/com/android/apksig/v2-only-garbage-between-cd-and-eocd.apk0100644 0000000 0000000 00000010055 14763776540 026141 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( 0% gަhRL|t5(J}00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( Wg볂D֣M<00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*<:8Ae4x ^Z:J'C$ &&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKV./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-missing-classes.dex.apk0100644 0000000 0000000 00000000034 14763776540 030104 xustar000000000 0000000 28 mtime=1741684064.8130000 src/test/resources/com/android/apksig/v2-only-missing-classes.dex.apk0100644 0000000 0000000 00000005062 14763776540 024753 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^av,  q LH@Bh2U.cXKۜ' Ʒ8?\ P0N0ULŎ+m8<9i0U#0LŎ+m8<9i0 U00 *H=i0f1+]=.re>>$N)\ܾ&, ;|1UBu&3a_2YqML۰L0xasog0e1*spBGi"؂?/\|1ڂLjqc鋢Dq{ϲ0G٣,%}*bN@|ۢ 'H_(6UEx0v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ ,APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-no-certs-in-sig.apk0100644 0000000 0000000 00000000034 14763776540 027137 xustar000000000 0000000 28 mtime=1741684064.8130000 src/test/resources/com/android/apksig/v2-only-no-certs-in-sig.apk0100644 0000000 0000000 00000006445 14763776540 024014 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qzv8,( zd@GҦY}$5H{IJm\ *lM|ߤV6Y rϐAqzd$ess~|E%PuUkwi6o%_]^#J_yO2O=*Aś^GQP6U=s#5h(e"-t*&>d9BiB"qNR]on;[1-~'85 eĕnߟr-gL1S/:+R|J\rtgBM] .Gq&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKU ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-signatures-and-digests-block-mismatch.0100644 0000000 0000000 00000000034 14763776540 033004 xustar000000000 0000000 28 mtime=1741684064.8130000 src/test/resources/com/android/apksig/v2-only-signatures-and-digests-block-mismatch.apk0100644 0000000 0000000 00000010122 14763776540 030340 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qeX( zd@GҦY}$5H{IJm\(xV4 zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*=@n6MҨl!9xTH_~mI~e!jv=cqƈѳ%t{1S~uK9ETtzIET$IW6Ms>V8-44?ͶAVL5 VYS,Eb1˛a`[fK3ʜmcOB~͖\ D#+ ڗ2MzWb7Ҙrc~9ʄ֑<5KƦ3&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-targetSandboxVersion-2.apk0100644 0000000 0000000 00000000034 14763776540 030533 xustar000000000 0000000 28 mtime=1741684064.8130000 src/test/resources/com/android/apksig/v2-only-targetSandboxVersion-2.apk0100644 0000000 0000000 00000010112 14763776540 025372 0ustar000000000 0000000 PK !:!O resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=㵝q'8ֆ(PhB$CAD*  MD(OB|AEuvf{cf ķ"Pb88눸F<"^O;qQDB| >|([CH ~8B} zs=4LTrcG]Z= `>itd))K]]<&L>-;<Si;'BGPK  q{w9,( d7%ٵ w@έjb+`VA00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*Ν#̞:@oE@PpqqxD$/w.7@|&~P"""&6 -A$&q6}.ѝ{hީ 序|Ku>;zc>}:\mnSgS@6;d|˓yL}Zv8#xB n-2m7=ܫOΝ6#-5u"--Tzt}BWgP#K-"5$m3S]{UWq]wzM3Rer2r'2.Z {Jr1cvOAMſz~4RJ^Ym["/CѠm`qU՜R5QP9! S-1aɿsxSlf=˧ 3:z]`b?m 7ib]dYc7krkpsr;mr9N̚wAlLX<Si;'BGPK   q YLH@AR_AY;NhDuW˜[SW1Rh2ggwsQm00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=RA&@Iut`r"Ҙ h9(z#>Ltz٤mѦ+Txi&dZuež?g SȦ/z#S1W2ԿGCfA iŨ/4a֙3q5a̺ {.?ˠ̝Kp4oP֔%=["r#>߹!D;=H㒢!;&0"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( @ɷzGDފez-m\%00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ n qfw9,( zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qw9,( zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ . qw9,( zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qqX( (Lc=j^]&X.N( ͞w,p-"'jb9rŏv"00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*27gNO6)(Ǩ b39qQy$ 08pvHҁUvpr EЏPIu [(oSDupFC|2c Z ØkhDRˊS,5+qvb%!AU#WIa:d˶$ne UAs-;c /`*!Tpv&}8>27gNO6)(Ǩ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-unknown-pair-in-apk-sig-block.apk0100644 0000000 0000000 00000000034 14763776540 031676 xustar000000000 0000000 28 mtime=1741684064.8140000 src/test/resources/com/android/apksig/v2-only-unknown-pair-in-apk-sig-block.apk0100644 0000000 0000000 00000012137 14763776540 026546 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  xV4UNKNOWN BLOCK  q YLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=XIdF[M %:$K`At W{Ӗ&0"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q,( zd@GҦY}$5H{IJm\00q J(0 *H8010U dsa-10250 160331152710Z 430817152710Z010U dsa-102400+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#QbƣP0N0U:Ui.%0U#0:Ui.%0 U00 *H8/0,Qri /sP[D-w}:6.0, 4Mn5: LU\5\U`b;+ź00+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#QbAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-dsa-sha256-1024.apk0100644 0000000 0000000 00000000034 14763776540 027333 xustar000000000 0000000 28 mtime=1741684064.8140000 src/test/resources/com/android/apksig/v2-only-with-dsa-sha256-1024.apk0100644 0000000 0000000 00000007640 14763776540 024206 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q,( zd@GҦY}$5H{IJm\00q J(0 *H8010U dsa-10240 160331152710Z 430817152710Z010U dsa-102400+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#QbƣP0N0U:Ui.%0U#0:Ui.%0 U00 *H8/0,Qri /sP[D-w}:6.0, 4Mn5: LU\5\U`b;+ź00+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#QbAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-dsa-sha256-2048.apk0100644 0000000 0000000 00000000034 14763776540 027342 xustar000000000 0000000 28 mtime=1741684064.8150000 src/test/resources/com/android/apksig/v2-only-with-dsa-sha256-2048.apk0100644 0000000 0000000 00000011364 14763776540 024213 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ qQ qIE,( zd@GҦY}$5H{IJm\c_0[0 .bj>0 *H8010U dsa-20480 160331183001Z 430817183001Z010U dsa-20480G09*H80,oRG"٦Y92+lS[2Z1MZn=Zq !_2h"2~At~zYc{6->rx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBdrx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBd:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ q Q  qI E ,( zd@GҦY}$5H{IJm\00 <60 *H8010U dsa-30720 160331183256Z 430817183256Z010U dsa-3072009*H80,(2< u^OO6?qX1t"Q@N|%, p۳$}0.E1.=u,D੝  s-amTKmܣ jY>nDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6ZnDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6Z:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ r qjf,( d@GҦY}$5H{IJm\tp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,USOG0E!6Q٫( 5g=o_R,# ķJq< sz09#lziY)[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKE ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-ecdsa-sha256-p256-sig-does-not-ve0100644 0000000 0000000 00000000034 14763776540 032115 xustar000000000 0000000 28 mtime=1741684064.8150000 src/test/resources/com/android/apksig/v2-only-with-ecdsa-sha256-p256-sig-does-not-verify.apk0100644 0000000 0000000 00000006425 14763776540 030434 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ r qjf,( zd@GҦY}$5H{IJm\tp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,USOG0E *a~^l̜}4/R! <2:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ r qjf,( zd@GҦY}$5H{IJm\tp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,USOG0E +a~^l̜}4/R! <2:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ   q,( zd@GҦY}$5H{IJm\00. VK"j0 *H=010U ec-p3840 160331153057Z 430817153057Z010U ec-p3840v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ P0N0ULŎ+m8<9i0U#0LŎ+m8<9i0 U00 *H=i0f1+]=.re>>$N)\ܾ&, ;|1UBu&3a_2YqML۰L0xarnf0d0 nGcM^W P] gڊT0..Lu[o6T{D\TH"έVx8" G"sx0v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-ecdsa-sha256-p521.apk0100644 0000000 0000000 00000000034 14763776540 027744 xustar000000000 0000000 28 mtime=1741684064.8160000 src/test/resources/com/android/apksig/v2-only-with-ecdsa-sha256-p521.apk0100644 0000000 0000000 00000007043 14763776540 024614 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qxt3,( zd@GҦY}$5H{IJm\00T Wt[/v"0 *H=010U ec-p5210 160331153122Z 430817153122Z010U ec-p52100*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzP0N0UEϭ CXve-0U#0Eϭ CXve-0 U00 *H=0BZMɨa{jx=HfZ)*Dh|Jnb^\CIe_4!DB-BqmbW1s2 ~FzUy{d̚8D8WZ<̠ӚW0BG %ĕkD4([$y~,ёVᩋ L볒B\^XIiTBD%xR܁'&#ao6rGD%1_ .,ihAR;!6s2,00*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKS ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-ecdsa-sha512-p256.apk0100644 0000000 0000000 00000000034 14763776540 027744 xustar000000000 0000000 28 mtime=1741684064.8160000 src/test/resources/com/android/apksig/v2-only-with-ecdsa-sha512-p256.apk0100644 0000000 0000000 00000006465 14763776540 024623 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@tp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,USOG0E!ݞk/C on2&,Ԍ9Ď ~% >$oE d}0O/fy-[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKe ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-ecdsa-sha512-p384.apk0100644 0000000 0000000 00000000034 14763776540 027746 xustar000000000 0000000 28 mtime=1741684064.8160000 src/test/resources/com/android/apksig/v2-only-with-ecdsa-sha512-p384.apk0100644 0000000 0000000 00000006656 14763776540 024627 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ +  q LH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00. VK"j0 *H=010U ec-p3840 160331153057Z 430817153057Z010U ec-p3840v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ P0N0ULŎ+m8<9i0U#0LŎ+m8<9i0 U00 *H=i0f1+]=.re>>$N)\ܾ&, ;|1UBu&3a_2YqML۰L0xarnf0d07UE8I$id^sG3 wbz$]ϷP|Y0 E&Bf'"7ͯ]f ̂H9Y #Ux0v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ +APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-ecdsa-sha512-p521.apk0100644 0000000 0000000 00000000034 14763776540 027737 xustar000000000 0000000 28 mtime=1741684064.8160000 src/test/resources/com/android/apksig/v2-only-with-ecdsa-sha512-p521.apk0100644 0000000 0000000 00000007103 14763776540 024604 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qSLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00T Wt[/v"0 *H=010U ec-p5210 160331153122Z 430817153122Z010U ec-p52100*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzP0N0UEϭ CXve-0U#0Eϭ CXve-0 U00 *H=0BZMɨa{jx=HfZ)*Dh|Jnb^\CIe_4!DB-BqmbW1s2 ~FzUy{d̚8D8WZ<̠ӚW0B @YVzǶ>LǀRvf+"&HsHG]yUxX^肋A7B>W/U}X o$[.i"})aכЀzayT; 'p2,gVKpn泞00*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKs ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-ignorable-unsupported-sig-algs.ap0100644 0000000 0000000 00000000034 14763776540 033053 xustar000000000 0000000 28 mtime=1741684064.8160000 src/test/resources/com/android/apksig/v2-only-with-ignorable-unsupported-sig-algs.apk0100644 0000000 0000000 00000011226 14763776540 030074 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q(xV4 zd@GҦY}$5H{IJm\( zd@GҦY}$5H{IJm\(!Ce zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ { qso5,( <*o1Vyɕ=6Q00] 0  *H  010U rsa-10240 160331161443Z 430817161443Z010U rsa-102400  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbP0N0UDYxgSoFĿy90U#0DYxgSoFĿy90 U00  *H  4ۀ o=Ms<lkIÍvU)?RON^c*r0 \*B\ڏ. HTfUDa~%j>_@ 8vJdV2.ҝ*:{^6+N솲[xpCSC%F;rBTH_M|ԏ)G|=) YJ2]RStPR3of(}K=CNA5wi "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbAPK Sig Block 42PK !:!O resources.arsc5PK!:^avAndroidManifest.xmlPK!:Շ  oclasses.dexPKV ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pkcs1-sha256-1024.apk0100644 0000000 0000000 00000000034 14763776540 030370 xustar000000000 0000000 28 mtime=1741684064.8160000 src/test/resources/com/android/apksig/v2-only-with-rsa-pkcs1-sha256-1024.apk0100644 0000000 0000000 00000007035 14763776540 025241 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ z qrn4,( zd@GҦY}$5H{IJm\00] 0  *H  010U rsa-10240 160331161443Z 430817161443Z010U rsa-102400  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbP0N0UDYxgSoFĿy90U#0DYxgSoFĿy90 U00  *H  4ۀ o=Ms<lkIÍvU)?RON^c*r0 \*B\ڏ. HTfUDa~%j>_@ 8vJdV2.Z@c-zw &m`5bR_Z<4 nGpVBD2t^nQ'Iq#;u,VeD &_eEȑ]|kY,"$;"]AJ&*q@000  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKM ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pkcs1-sha256-16384.apk0100644 0000000 0000000 00000000034 14763776540 030467 xustar000000000 0000000 28 mtime=1741684064.8170000 src/test/resources/com/android/apksig/v2-only-with-rsa-pkcs1-sha256-16384.apk0100644 0000000 0000000 00000026050 14763776540 025336 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ !! q}!y!;,( zd@GҦY}$5H{IJm\00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$Sժ(8W,UȾ5Xvm#W(oggyϸvbʅHxllf9·#pI!O|XŁP ދ)T+ 9zBl ,p,]`&bE%$E4VYMI2`ȺX&W;7MW@/#y'>ϥ;$},$ʢnfs9ؿ'EcɁg]Ѕ8f# 1{㳇Y\-}!5-k'ԓcLq*qr}Sjl*䴮`%V 0DrM e8.MGkכ㒎ID~2!aٖզ!e-ri'5-xѪEuFhpzN}@mj{kaBF8tjWBG;n*&WYWK}`v/x.Am%3{'wJ~k k&qo%?<;].)mӺ1[F&4bvt,Cޑ7d~6 CMl G8cZ(ANQ´ `(xKXAO?dُ7aAW.;wR*JZU29{.~P(3Bi5-_x|i<ώmW~@zвolhzCoh^⣑[l *\<~Ķ;܆j"|LF_PDD.;0pM*py-wt'_Qd'=HEWTd"MUNY\OE2)%Xp}ke|%S#lUWd80i>L]RmQIJ\KeO&c?ꘌ2ǔ*@L; ԨIe(+G?:pVzo LZ `ܣcGBVY6 淽&0"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0!APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKX+./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pkcs1-sha256-2048-sig-does-no0100644 0000000 0000000 00000000034 14763776540 031747 xustar000000000 0000000 28 mtime=1741684064.8170000 src/test/resources/com/android/apksig/v2-only-with-rsa-pkcs1-sha256-2048-sig-does-not-verify.apk0100644 0000000 0000000 00000010046 14763776540 031054 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( zd@GҦY}$5H{IJm\00a ga0  *H  010U rsa-30720 160331191458Z 430817191458Z010U rsa-307200  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~P0N0UOfql5ާS0U#0Ofql5ާS0 U00  *H  "UVkKefڻY,ZcS.Bê͐ŬpÅ_/6Nq `6u'KZz{ҍ*7B>(qDV_I]1TyώUQ|ʗ i}9HЍQo݃^n񀃯iǽM@P'mclpWX _C"֓cXQ)G@($'hL0* ƵnҾJ݂*uoj$gSyyҾ{wc޺yHec@Y{vk&kIq9g\9gp"T_,DG((Fl+-^XO.Xv®FUC'v~ 8VG8iJp,?&0'5FO8CZR8'zXЁ$VHd{zqfZp2kum[dAvH_Ntv\Gm*o:m@SG-z̤bX R$ M0urkHE}a7`Tbw_én2}ٺmY0tF:BlT..$:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ   q{ w 9,( zd@GҦY}$5H{IJm\00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=Ǚa#N Y)Ib0|2o|g'$}:8C#C1YIH45rd wc⨎.Hk]VSw?P?6N-)*)7Z}.T/kJ@LJhܠ\0i^^ptěeөӒ[;#$D&kX͗h) _z6#q߫!v+D\ꠁ /)$Bͳ"X$Kp[ A&0"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9 ,( zd@GҦY}$5H{IJm\ 00 7%0  *H  010U rsa-81920 160404193246Z 430821193246Z010U rsa-81920"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnP0N0U]FsR$2/~?0U#0]FsR$2/~?0 U00  *H  hU_##B)q q;)1eG+)9RN&~W$`LYR8n \WmӔCzMaOiڒnR FSfv1$>3{ >\~Z{ {J~nt |)`tڝs3)1 tZ֝d:J蕯WSЃ(Ѝ$#&Pt\X2DvGê9ǢcS6w-aDfBHjAn"1_M+%w|]/O߂D*%'Y T=1' L}eI odw g8NR)dlo6"h9 r\h/[5,dGzNS4]sr #F7ԯ7@'Zb8.;:!憌Q]3:+8>6~^ 1V̋4L"rzE1r.7Lv1x =(+1 Kc ~=]#pփ,sy MI3V7up3Hi׈ˊGJpY{ASm!ԍ7YlC{fo;[cG(Rk33eRۃuRߨcvH- i=\hC,*#n'cY"d46S pfy s݉'H?b;Pؠ.  )U)avggVo;d85qڸ /WHNNa4_?oC~ިmU8aKv'˾ɴ9ʸ$sO¾C{`P:l7CEcv*$fibxswڥ-%۽:M]\7!U笑ݖ/vJ6?C$:Bv,2 M,ȥa!b! c*6`4rwX w`ag F"%朂K_&ḭ{(spQ (MMc3À⒲އIܼ庩 ;%I=S3@a_pҰk7n('/^Vڝk;xuOw .hBo %.daaIG%Hmf^+ⶹ3  RJL yInk8=^mN޹1ɯ-W"?+ l|aӡ0 Wc0„0 {yjT]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKV./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pkcs1-sha512-1024.apk0100644 0000000 0000000 00000000034 14763776540 030363 xustar000000000 0000000 28 mtime=1741684064.8180000 src/test/resources/com/android/apksig/v2-only-with-rsa-pkcs1-sha512-1024.apk0100644 0000000 0000000 00000007075 14763776540 025240 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qTLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00] 0  *H  010U rsa-10240 160331161443Z 430817161443Z010U rsa-102400  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbP0N0UDYxgSoFĿy90U#0DYxgSoFĿy90 U00  *H  4ۀ o=Ms<lkIÍvU)?RON^c*r0 \*B\ڏ. HTfUDa~%j>_@ 8vJdV2.` ŖEaj4)p0?1/Gk1ro¾@ydp8qWJ%\qdmB2np4vɑG$@U'8 ǓX 00  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKm ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pkcs1-sha512-16384.apk0100644 0000000 0000000 00000000034 14763776540 030462 xustar000000000 0000000 28 mtime=1741684064.8180000 src/test/resources/com/android/apksig/v2-only-with-rsa-pkcs1-sha512-16384.apk0100644 0000000 0000000 00000026110 14763776540 025326 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ !! q!![LH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$3}v?īu`EydP*.i|VٖF(IoAs c4?\oh+ϺT;M '{ھXG_ͱK{ MY4%B_BPt=Mk;P ;I;iW-\Oy.׶(] sW,+nL>D߬9Z;{J"cbJvGJ]:ceC8q(>Ž@"IWڦxi' N_ڤ?zj>.ח9pvi1C=XЪP+%0Me$`cX-bZ_MUQK138! R[z/ 3';,r#n`%4@Rfp>-#Q~O%S۲/}Ծ t-;tX<$#7ѽ*qHȈY;e’8K'5KH21톶cڝrz`$Lrh8X9aP ymO)Ep]ȸc\BXC{S*`;NC8,hyj=!w5FYgWn1{\1`:fm,q _{ʘ8Ik8$9?HT=^4G4W%j#(}r<iL vjse4M ?t dTebF4ޭ+ZƾCES +UzL߀qfᇍc%({N@ɸmAGEOYXbЯxo$ qY|スr?5aR`yPcURt0$\obnJPfh6FE|^!I :27p#@)]"D3@wA\fgq+k~a^)ͨqϝҪXmET~v?8i/C=ٟN$ZwUu^uy, eD!.GU*ơ,=俬 1pNDჸŔtC ꞈ]C޲]p9V=tՙ`rG`iQj"Ev2jTYĐ5PjY V"n}:`}92n 284GUu$o]C 5Jpi'[kVDY-L?1"Yxl ge; 8KH.Og c"#YK mga xıOQYfCd*A.4/.z6K'Q}E&0"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0!APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKx+./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pkcs1-sha512-2048.apk0100644 0000000 0000000 00000000034 14763776540 030372 xustar000000000 0000000 28 mtime=1741684064.8190000 src/test/resources/com/android/apksig/v2-only-with-rsa-pkcs1-sha512-2048.apk0100644 0000000 0000000 00000010106 14763776540 025234 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qYLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*kC l .:waӻZ*Pcz9߱2VŦGEƳqy2LiPJ fsb嚹f:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qYLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00a ga0  *H  010U rsa-30720 160331191458Z 430817191458Z010U rsa-307200  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~P0N0UOfql5ާS0U#0Ofql5ާS0 U00  *H  "UVkKefڻY,ZcS.Bê͐ŬpÅ_/6Nq `6u'KZz{ҍ*7B>(qDV_I]1TyώUQ|ʗ i}9HЍQo݃^n񀃯iǽM@P'mclpWX _C"֓cXQ)G@($'hL0* ƵnҾJ݂*uoj$gSyyҾ{wc޺yHec@Y{vk&kIq9g\9gp"T_,DG((Fl+-^XO.Xv®FUC'v~ Ha+B,/4{݌ۯ׶F( Z䦷JrD$!}u k/\)2 ތ-k*x#/1?jcrj0,73ec4<& Զ (5wNKqqF6g^Vg7~ʚ_aqr?RdXG՚0XLQF %n)6BSw_Fyk(0Y \SߠmRvRQC#G ѻ-Yz`"$T[B<&ѓ-]* *1#8_}z`);?s*!6,dHrU&a00  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKv./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pkcs1-sha512-4096-digest-mism0100644 0000000 0000000 00000000034 14763776540 032045 xustar000000000 0000000 28 mtime=1741684064.8190000 src/test/resources/com/android/apksig/v2-only-with-rsa-pkcs1-sha512-4096-digest-mismatch.apk0100644 0000000 0000000 00000012106 14763776540 030323 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ   q YLH@^V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=}㽑oϒẒlj}V[JI#nS02i=Qy3Ɂ؆er PuI?sNΣ+!_vʷPA:7s# tܯ˘DL"A=C<d9s  }r"^}yjU[wZ&0"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ   q YLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=XIdF[M %:$K`At W{Ӗ&0"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qY LH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@ 00 7%0  *H  010U rsa-81920 160404193246Z 430821193246Z010U rsa-81920"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnP0N0U]FsR$2/~?0U#0]FsR$2/~?0 U00  *H  hU_##B)q q;)1eG+)9RN&~W$`LYR8n \WmӔCzMaOiڒnR FSfv1$>3{ >\~Z{ {J~nt |)`tڝs3)1 tZ֝d:J蕯WSЃ(Ѝ$#&Pt\X2DvGê9ǢcS6w-aDfBHjAn"1_M+%w|]/O߂D*%'Y T=1' L}eI odw g8NR)dlo6"h9 r\h/[5,dGzNS4]sr #F7ԯ7@'Zb8.;:!憌Q]3:+8>6~^ 1V̋4L"rzE1r.7Lv1x =(+1 Kc ~=]#pփ,sy MI3V7up3Hi׈ˊGJpY{ASm!ԍ7YlC{fo;[cG(Rk33eRۃuRߨcvH- i=\hC,*#n'cY"d46S pfy s݉'H?b;Pؠ.  )U)avggVo;d85qڸ /WHNNa4_?oC~ިmU8aKv'˾ɴ9ʸ$sO¾C{`P:l7CEcv*$fibxswڥ-%۽:M]\7!U笑ݖ/vJ6?C$:Bv,2 M,ȥa!b! c*6`4rwX 8dyfc(|I6nF.P/xW[E fqq81bWCL/>|54Q[ x&_\ؒEww- u_*s оg{vwy(JzURXOuӽ4Ei|ND~1#"wjA%i_Ry- BV.}vm`kG4 }M$ wu2l+:bm bLJ$bȇg0Hf@VT @sD\LY=tNtGwk(a.6nmДym`cSC/>v TOOmکQ>6B5k|*p~'d3VҦtXPAʈM;9%!HMM;9ِE2b?V a8]yσ:޺l3";WGtk s]-VM6OдQZka)n}+')g0#cIhh!ʛ.|}cgZYLuBnRt6L䚭 K 6J/ !@HA@).WEiI' h|G^`s-6 -@Ohy",CS{iodBl'TU3T$.DC ~B@́H.-oѰU2AS'kw+V ,Er hbdH#s⻝+񀧘6Sdy-҉/C|=Z4 ɡ#~{]t@8fIF g~xRV<&0"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKv./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pss-sha256-1024.apk0100644 0000000 0000000 00000000034 14763776540 030154 xustar000000000 0000000 28 mtime=1741684064.8190000 src/test/resources/com/android/apksig/v2-only-with-rsa-pss-sha256-1024.apk0100644 0000000 0000000 00000007035 14763776540 025025 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ z qrn4,( zd@GҦY}$5H{IJm\00] 0  *H  010U rsa-10240 160331161443Z 430817161443Z010U rsa-102400  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbP0N0UDYxgSoFĿy90U#0DYxgSoFĿy90 U00  *H  4ۀ o=Ms<lkIÍvU)?RON^c*r0 \*B\ڏ. HTfUDa~%j>_@ 8vJdV2.zsV Pn#5|qԇi$5W Iڄ}x,}R%B`nKYw[E ʞ9Ð'J`>kg] >Uu~a!ZzRH1N[00  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKM ./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pss-sha256-16384.apk0100644 0000000 0000000 00000000034 14763776540 030253 xustar000000000 0000000 28 mtime=1741684064.8200000 src/test/resources/com/android/apksig/v2-only-with-rsa-pss-sha256-16384.apk0100644 0000000 0000000 00000026050 14763776540 025122 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ !! q}!y!;,( zd@GҦY}$5H{IJm\00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$y߽G) sJ~F 14oE^0EܚVH,u Y.`&%ɕ^w=AjieÇw^6]S@ v%xwR,g݋z9k${$e,ˈ,}?t_&>g#"DXwBuQ*M ?. O*j~VzYM0e\b`A VMHd4)-nTȭ@ ՛+,,z4rDA1ARͤIz^_w7.5JN2@ $Sqt4ǰ]S'_8Q~#`Lj891.{;Aݭpm&zA%O hD:"sM申r]:vQj|#SIҌtᜩ ExmQxn=SWm 2VaBqo|zKN&cp$˿—H/Q[*nRܓ$̩Mʥ *oiSzݳd&57 =;UmO@(Œށ=ȶ:%^rM N$bk d;9+ RzYdQkMb13oX/ی^y^< FDnX*2D0`A)dUްA*Չ./07˴dYVϦ/Q8K֑A"N^ODZuk_, jM|& Xk uw䈕-3Φ RD GQYy&n4"7%NaVYNSBgq>˨R]lXW,T8a h D*wzœFJqIJ ̦H(׿U{PK`PUxo|Yi]4zC4He>1M" FgT)D}j$=nF6/xltB?ӺWSB}@_AIafV%jѩbHh&&3H )JAl):5p7KCH Ș[(Գtl0>(8BF/Z> %Šh[!( _ ?a/|}=<Vud]\ijuJ_9iݎƲ)HOf0/oEHbQYZƒGBZﶀŸoF!FZK@2)x$bB`44^u}tϨ`{`-Cn5S_J^DvHQ&] >`kwIp _X:_ kЅ6C6Qa[婤hBaeϞja &bZ>"4**LW+ÙFc_u}s<0z!AZV2jf4CB >~${KD浖YSjxgc$#>iuMZ a)MKN~1S[odžd#kv8[,.u vRH.Z.Je~~;@` !;<*bQ?5X7FiW~wfo*,IHXg_R,r—>1@M3V9rE,lqSp pA&JQ;-Mt\λO'M]ta^`Clg^Q;NgJ)$d\sZؾp]G$19.78fS'1L _o{xRo PCk-A/-5ZxC}}H8ɠMOgk2mts:Q& \kic2ZW$eb. )$Ύeۇ~,Zi&0"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0!APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKX+./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pss-sha256-2048-sig-does-not-0100644 0000000 0000000 00000000034 14763776540 031774 xustar000000000 0000000 28 mtime=1741684064.8200000 src/test/resources/com/android/apksig/v2-only-with-rsa-pss-sha256-2048-sig-does-not-verify.apk0100644 0000000 0000000 00000010046 14763776540 030640 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( zd@GҦY}$5H{IJm\00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*{)z$p9]͇Ҏ.z3 [vjj)E^ӄ "y฻kYRM+p3Gn݆Vt̷9 N <=3 66#yQ:*مC%09`_l&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKV./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pss-sha256-3072.apk0100644 0000000 0000000 00000000034 14763776540 030161 xustar000000000 0000000 28 mtime=1741684064.8200000 src/test/resources/com/android/apksig/v2-only-with-rsa-pss-sha256-3072.apk0100644 0000000 0000000 00000011046 14763776540 025027 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9,( zd@GҦY}$5H{IJm\00a ga0  *H  010U rsa-30720 160331191458Z 430817191458Z010U rsa-307200  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~P0N0UOfql5ާS0U#0Ofql5ާS0 U00  *H  "UVkKefڻY,ZcS.Bê͐ŬpÅ_/6Nq `6u'KZz{ҍ*7B>(qDV_I]1TyώUQ|ʗ i}9HЍQo݃^n񀃯iǽM@P'mclpWX _C"֓cXQ)G@($'hL0* ƵnҾJ݂*uoj$gSyyҾ{wc޺yHec@Y{vk&kIq9g\9gp"T_,DG((Fl+-^XO.Xv®FUC'v~ ~ npyR(O +u Mvo>HA #c9]$D(n֩EEH!,.c%buڹ/~+\J{Ѯb1gw=1ɞ&iڙ @~ ?ZhQ.U\ ʑCWs_sl]q١jd"* 3`&FmO3Dh}^d2XkE0Cm:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ   q{ w 9,( zd@GҦY}$5H{IJm\00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=߱QAa&v7Q뽣dp<zlA~cij쬞W%7P P< ᭂ{bli 2FPG#4͒Jp3SDrEO_0Z{ZXePْ! ;/{FQ>%=Ѽ1R<ʞ$Ԟ" ^gJ% c<컛܊$Wӯyd?~8`rMV*eT%pCS\ߖBWxI$\]I56P;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q{w9 ,( zd@GҦY}$5H{IJm\ 00 7%0  *H  010U rsa-81920 160404193246Z 430821193246Z010U rsa-81920"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnP0N0U]FsR$2/~?0U#0]FsR$2/~?0 U00  *H  hU_##B)q q;)1eG+)9RN&~W$`LYR8n \WmӔCzMaOiڒnR FSfv1$>3{ >\~Z{ {J~nt |)`tڝs3)1 tZ֝d:J蕯WSЃ(Ѝ$#&Pt\X2DvGê9ǢcS6w-aDfBHjAn"1_M+%w|]/O߂D*%'Y T=1' L}eI odw g8NR)dlo6"h9 r\h/[5,dGzNS4]sr #F7ԯ7@'Zb8.;:!憌Q]3:+8>6~^ 1V̋4L"rzE1r.7Lv1x =(+1 Kc ~=]#pփ,sy MI3V7up3Hi׈ˊGJpY{ASm!ԍ7YlC{fo;[cG(Rk33eRۃuRߨcvH- i=\hC,*#n'cY"d46S pfy s݉'H?b;Pؠ.  )U)avggVo;d85qڸ /WHNNa4_?oC~ިmU8aKv'˾ɴ9ʸ$sO¾C{`P:l7CEcv*$fibxswڥ-%۽:M]\7!U笑ݖ/vJ6?C$:Bv,2 M,ȥa!b! c*6`4rwX @֮#+E~ppυ5WFx)E+w>r?&hY)b% sgoG8%v*>lq0@+,Ô{RZRmfj ]nni"iwow:RR -Oā|@v^PS/2z?/Dn]V$ [߁@n{I/C? mZwHÍcjcPQaV>ʆ%j/DiWHw|IS< =Iꌘ< C;]*d3uMnElstv*m<4*au2Lqհ3zjpK[9g!ߴ(bvV$L!=щy8gNU' jy)XbS85a]vfG?@>sZQh٩|#(6KPs]Ǒoz큢3\pp=4S3p >N}"~d( 6جMU2#i`[<.__K7{!@֘>QCcly42#`~*=!wB#B>J*]ŧ[]ĉq?nJV<;w(Of"޵5«;ʻF%.woz?1Il?”Et=Ʀ+#Qy*N:h= ʨ9q-sVUN~QGT٢ J_K :PvO 5?WMXx|KJn-[\C>LA>wûE[/1OҭolQNe}wKڬܶf>wݎ%uaPר *Mc$]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKV./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pss-sha512-16384.apk0100644 0000000 0000000 00000000034 14763776540 030246 xustar000000000 0000000 28 mtime=1741684064.8210000 src/test/resources/com/android/apksig/v2-only-with-rsa-pss-sha512-16384.apk0100644 0000000 0000000 00000026110 14763776540 025112 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ !! q!![LH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$m䟩5qu&@; {:@?od V2m ,sP1ˋJnb ֱ I2f'<]y| Ȑй6o`\_b/a(D|L5?ѿ)g مvۡmUx9C-?ZA oVr+mjOUie ) /gMlk ̼Mj+0`<֟xaNY$WXKf%2JKv4 q`*9̑̚p`0ac@0$[輻m,f$m ˤFYɷ>@ -BD&5/>bWZt_ϜDEs,?!ћ_M~PP5(WZPITlS@M-R~zC&ZTcԴ޳]ktjT!0zE(-Bӫw $/6mG2.hfcm#C3,Q-}:箿.G~{3oȬr~RgѮ0YQ (KaLpvA'75aps1/ r5vgD~TMF.ҮZŭETM| Yit|HAC6{{8H!觶n,kHKY.d^uҴ\pQ]~ʗٓ /)؈]4(5'S!&B.գw|†Ғ4+y }l֭Ǥ1& "K,eӄ^"<^_OŊ.췔r A):7oBAL7rp0nu[I KKWTǖg8-&;ùb` v3к}s+to_1 G\l?l ͿY(hdYi2ͮD{s}^K_|8WaQ<ڸxK"9I{URw“*{Y̓ŧ0żRnu1x}4fVCd~pR (}w޴F Lh?'bƿσefFKt/ڴ~]urQYI>1we`HѲG[;N{?QRY&Sޣnm1bR/&R[ %)x'kv 7.pqo~RxݞqMqwPk $cq'zu`|,|zQm<58$WFF`֤aRTkq)~t1z`# u$}6E2E=+%Ȟhڬ< ,vVl*&0"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0!APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKx+./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pss-sha512-2048.apk0100644 0000000 0000000 00000000034 14763776540 030156 xustar000000000 0000000 28 mtime=1741684064.8220000 src/test/resources/com/android/apksig/v2-only-with-rsa-pss-sha512-2048.apk0100644 0000000 0000000 00000010106 14763776540 025020 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qYLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qYLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00a ga0  *H  010U rsa-30720 160331191458Z 430817191458Z010U rsa-307200  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~P0N0UOfql5ާS0U#0Ofql5ާS0 U00  *H  "UVkKefڻY,ZcS.Bê͐ŬpÅ_/6Nq `6u'KZz{ҍ*7B>(qDV_I]1TyώUQ|ʗ i}9HЍQo݃^n񀃯iǽM@P'mclpWX _C"֓cXQ)G@($'hL0* ƵnҾJ݂*uoj$gSyyҾ{wc޺yHec@Y{vk&kIq9g\9gp"T_,DG((Fl+-^XO.Xv®FUC'v~ 6!]k2`].M;  :. lŘ$0(BG7bHyTbxf1f(h~?t'kk,‚;hj+krP&~Gy;.~m'70= c3QCG/]11| 4սL}$MoGmG4l1x?9]@^p~*(I?vH3F1z5:[uۂ .S.-[<$ Ƒ{2t JBY+ܐ +B~gqĂ3׹ b7%ѼߦUk䜰*u*ԉJ#&H7- Yj~wTdrx%JRٖuq .eHS:cria슁>̦00  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKv./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-rsa-pss-sha512-4096.apk0100644 0000000 0000000 00000000034 14763776540 030163 xustar000000000 0000000 28 mtime=1741684064.8220000 src/test/resources/com/android/apksig/v2-only-with-rsa-pss-sha512-4096.apk0100644 0000000 0000000 00000012106 14763776540 025027 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ   q YLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=($g]Օl{E5K±H;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  qY LH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@ 00 7%0  *H  010U rsa-81920 160404193246Z 430821193246Z010U rsa-81920"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnP0N0U]FsR$2/~?0U#0]FsR$2/~?0 U00  *H  hU_##B)q q;)1eG+)9RN&~W$`LYR8n \WmӔCzMaOiڒnR FSfv1$>3{ >\~Z{ {J~nt |)`tڝs3)1 tZ֝d:J蕯WSЃ(Ѝ$#&Pt\X2DvGê9ǢcS6w-aDfBHjAn"1_M+%w|]/O߂D*%'Y T=1' L}eI odw g8NR)dlo6"h9 r\h/[5,dGzNS4]sr #F7ԯ7@'Zb8.;:!憌Q]3:+8>6~^ 1V̋4L"rzE1r.7Lv1x =(+1 Kc ~=]#pփ,sy MI3V7up3Hi׈ˊGJpY{ASm!ԍ7YlC{fo;[cG(Rk33eRۃuRߨcvH- i=\hC,*#n'cY"d46S pfy s݉'H?b;Pؠ.  )U)avggVo;d85qڸ /WHNNa4_?oC~ިmU8aKv'˾ɴ9ʸ$sO¾C{`P:l7CEcv*$fibxswڥ-%۽:M]\7!U笑ݖ/vJ6?C$:Bv,2 M,ȥa!b! c*6`4rwX A-4 %Jq'6" b!t)>%Ww;4<^3 Iym; O{42"o*כە-J/R@'07H~\ݟ,Vv[-ؔ@Nc4v+T4nvy/kk` c4oc.B_ik/)>\-.FLGmDL|`\rW?* uC%٤8V\Yz9S?Jܗ5l}dɀjI(+O C[gJRfLJ~掋 %Q8-pg{(hLBr-e]^X0Z+]x"lk`KMlBG;7PEURH;~-qQ)"1)ߛ-(~edYvЫр*Ms5vӦH[Im2)p8',Z )c捜5[JAt񳶿.C"Pf>$vUB,|I -cAgpDU & k/F6_) (,18͢]umf!'(].V;e}J-혿0B~oym`36\.֧%ҰF+w>ż-:5=ӹ,$^ݻ|ƅPwWgAY𵩬8( L S.3-deDŽcBY f!d%}}Ye^XO=?=Ii-b*&.?J/u{3RM-C~8u]F pH, D|X`d_}=?2YذNp̹!Sr=w"33$mH $G s z( 34&0"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKv./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-with-stamp.apk0100644 0000000 0000000 00000000034 14763776540 026316 xustar000000000 0000000 28 mtime=1741684064.8220000 src/test/resources/com/android/apksig/v2-only-with-stamp.apk0100644 0000000 0000000 00000030427 14763776540 023170 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlTnRA=-D]21Mwmc S{ %ܫiWt飸ra|p >rIuafΜ7?wfuӾI'>_Dq@mJ&eDF|'~D q3dqހ=ȺKkr#Ddb8ıNًgٿ&k9* ԊIC2}\z U:'kd.Kݺoak i2k8QyrggDM5pcCoɏc5K;xk/JVM?⚺'-zDm=,Nes+ 4K^c=Kz5]d|8OkMTD&!`B<BƘub?ƌrDL7a*gcUv\O^CJ-;ۖ Ǻzry^uͷ_p7qjy\`J8|945O8 x-9^kz9Gs'^+)7,Xq3Ż8 +;~~\kHx?ɽ53_PK?8PK!: classes.dexOHQgUVMKdnokw}tד/V6?zm7"hIDk=.vюpBE!3 yl#OKC>#_|E<L,dNHr\ѠjjR]&T-PsA沕~CMektZ9^WBCSCE6LymVgwROJGu) a CYs;ښư&|MMXc>qЦ 9MW}!sɍgϩ[,q#D>[⳪n|hܹ\d^@3`O{$(a,H̹ xD@8@!)hA=dezufWe>֜,8H//Yl@IDsH, V"A+#~P4 =j],f^{LU^q{t9I޷Gz-pĠt߾%MN?avjZSk}2ʟUG [#Sp}WE&M]͢ݪ47fVf^3ḑTg9N*CEi +惞LX?XJE JɵsPgyPKץKPK!:S% stamp-cert-sha256 ]jDݷ)4[5u]uEYj͉%s2t*!`!Om;I (9X3ڦݓvgNkk$Po/ŕ|ʒDφ%p)*e6rE9P ۃa!ܡii, G?7yTF32H 7 *@;wWD8w^l;=͛roHo/[- werBAPK Sig Block 42PK !:9gGÄ resources.arsc5PK!:?8AndroidManifest.xmlPK!:ץK classes.dexPK!:S% stamp-cert-sha256PK0./PaxHeaders.X/src_test_resources_com_android_apksig_v2-only-wrong-apk-sig-block-magic.apk0100644 0000000 0000000 00000000034 14763776540 031054 xustar000000000 0000000 28 mtime=1741684064.8230000 src/test/resources/com/android/apksig/v2-only-wrong-apk-sig-block-magic.apk0100644 0000000 0000000 00000012106 14763776540 025720 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ   q YLH@V8t6U ڇpag栓dA\^۰1 T Q5Wxr⻶y@00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=XIdF[M %:$K`At W{Ӗ&0"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=/fm&?mV>xK^O$DҨt>xsï-9`F;xrw&}ۦ6{ߟ7Gd&1mh1e,3٦g6g"@WHuu$Ca,Pjݐ,t]w{<}әsO'}%yZon'*onR!Q Z#։1+T+TDT7fľN9nEոNU H=u9vwp-ul<+z:45C8Z e=.4k=tH>O9>ZrΩ &Ѻ.ֲѲMlMkS66Os[vѫ:z,ZwY͹PKa|PK!: classes.dexMhI_utLD4laY2 f .UDV nMWMҦzqqOYPX<{"dQ^DÞ< ^<쫮{UzzoN4r<7wlzy[k!0'`yXJ0u#Gi@^ߣӮK>aiZVwb?o,w#؏ڦVוZZ}d ,.b tOpC&, ̧:Hf\.d^QG$OBqYhE'dRz ٷbix+FpӧB MXgE/ET|e!xؿ<GKIu%$%wR%Ykmhp)Et]B7Un?'}RӄÐMΖLjtDiҨW&[& (-=_O :6<ؔ*4|«"Ok0SOc|0_ N2Vas^,")TNrY.q3 ,LozWrȨcuY#V79FFu&9C^qvg6' iWw[1t̸ YGrO;۬%Yf{ytZ͈LNY`gu '7jVɩfPK*4@PK!:Ȋ(META-INF/RSA-2048.SFeKo@= e3V@d@v72bx D6uwsN}QeaJr# 6 G YaeJEyP.{+"M)zK5v[p aVd4t sNy+9 Y+,ZQGkY3|0r!e;_ՑPTb2Ɓ Qױw#,#ECPK!:ˣ%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC)~J3)"snMc()Wdi)cdiF8X%IggilVvY &9WonMJ,Q_$k1>55)-x} 78#EJkīC:K+"cwmZp  W~ q{=,( 1Jr'"CA@spS?T܈9$nz:>p.In? ڙ{l^g YC%t ~9"|Tmb`&qz4t ~-?z4dH[? 4:.LO}Xܔ{\J9M4iG v Vttn13-OWm*F! Pw#@9`w&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[5 werBAPK Sig Block 42PK !:9gGÄ resources.arsc5PK!:a|AndroidManifest.xmlPK!:*4@ classes.dexPK!:Ȋ(META-INF/RSA-2048.SFPK!:ˣ%META-INF/RSA-2048.RSAPK!:DbR4QMETA-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v2-stripped-with-ignorable-signing-schemes.apk0100644 0000000 0000000 00000000034 14763776540 033066 xustar000000000 0000000 28 mtime=1741684064.8240000 src/test/resources/com/android/apksig/v2-stripped-with-ignorable-signing-schemes.apk0100644 0000000 0000000 00000011115 14763776540 027731 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!:Շ  classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWNM1ݞY$f݋+BEA/Ju~r\VDm s=o+w,B^PKW2PK!:X)%META-INF/CERT.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_ICqϦDo.z=Qy~n/gOJۂk睺?S2(-d[%r>PKX)%PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!: Q META-INF/MANIFEST.MFPK!:W2 META-INF/CERT.SFPK!:X)%Z META-INF/CERT.RSAPKy./PaxHeaders.X/src_test_resources_com_android_apksig_v2-stripped.apk0100644 0000000 0000000 00000000034 14763776540 025074 xustar000000000 0000000 28 mtime=1741684064.8240000 src/test/resources/com/android/apksig/v2-stripped.apk0100644 0000000 0000000 00000011111 14763776540 021733 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:^avAndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!:Շ  classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!: QMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jWԬDk k}`V޴YFl+PK-PK!:$zp&META-INF/CERT.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ O! q!!kLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$̷l:H;x:MmE;e,OdR'WM:X? :cw/a$a\B-=ۣ-Kr~Rx`+$xY\=H_Ӭu&m"VVۙdߊXi/Qm4U Eevu9Vۑ,4!U%q>)|cYC1O-e߽Jn2*PFS[%'X㘊1=WxDiݐt%z6<1߉ e/;u5'}T8ˊ>M] Ң\)wkyiS> +~~oc]mƿORfY S%C̃HKY 7˼GÞl -2E0,S]pJvs$ q'to{$Z Ж14J`՜ɩ-쑁*_KkZz`!䜹|ؼKIap0.?%H1ELp(⭇D}`?IWqᕣ綈%`7P/,*?qkbf8&Z~+`[8{sS8s 24i:T~:JjBc:4"j-2ie_oi6`Cm71va)֍wx ށj=6']L+]vVel8ЍKzk.,d>qPbɱ iYҩc4uaY#Zf4+c_gwyҭ.E&<(etAÍ1yH_l4fhn%&0"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0!werB!!cLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$bW&%`TeCza(f]#/ZOI]$+Auw 9ݼ2ԧ^bJp$+_b<;:O ZrvrKJPެBVZ׳&xjq֝c:BhQ`U o:+1fa(`}l+ҖsK$Vmo6`nyvEH2d=ĕ> @TӉ_bZ?K;YEWJ׆tsPib*i3PHU0۸E/>y ‚QS;~+-ۍR=hea)!!9?(palSX6/Xb%c--3]\{GZFoӥ'Skة/DF../աT?֥`m8„> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0^ werBOAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKp./PaxHeaders.X/src_test_resources_com_android_apksig_v2v3-unknown-additional-attr.apk0100644 0000000 0000000 00000000034 14763776540 030270 xustar000000000 0000000 28 mtime=1741684064.8240000 src/test/resources/com/android/apksig/v2v3-unknown-additional-attr.apk0100644 0000000 0000000 00000040320 14763776540 025133 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  q`( Bn:I] jc ղ0/0!(VA>_@y&qcw/ʶ 00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*%D&?rv}^C%e/'CN$5d @=XZ!"ݘ|=}XnQH4U/!I5=j(ڞҎ*x7!m,x"(@\pU'#k BPC-C-jLFwOkʵ0IB~-ڣ! /(3tX",1cf( d ]}.݌Jt%D&?rv}^C%e/'CN$5d @=XZ!"ݘ|=}XnQH4U/&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ hS ,( Bn:I] jc ղ0/00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3tXD3"wfU<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v3-ec-p256-targetSdk-30.apk0100644 0000000 0000000 00000000034 14763776540 026472 xustar000000000 0000000 28 mtime=1741684064.8250000 src/test/resources/com/android/apksig/v3-ec-p256-targetSdk-30.apk0100644 0000000 0000000 00000030330 14763776540 023335 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlSn1=$MȣiR胖+DS*X +4IFIhf@TB`Q!>o p|cgӈ{m4 (N'3'V)'Έw"{KCO;I%nDh@vI㘳g@}ghBDdL1{_y2Q,#8!g r5e5웴>}\oGv951gN~GLVvdxeM6!m s>1^E+bKD'N&_AǒK,zIT:]}[4m?pGdb6G2Hږc6t KSo#H%c9ԥUBGܑxk?>;"86 }ST@SJkĈVQ*$"̽~]f_77튼*:rpi9M᲎2\ w(ߢYiƳL yI&Cf:Zv/=>O8>Zt/k75h--۴͎Ѵ6%c[k/8ޒWgU^,`yN"jPK`zPK!: classes.dexMhA~d5mmlH\ iZjRPDNm71 -ŪAzqЦ !+|9F~ɏVҐs v܈;Vv?J7 >˦4B\(2/i3 JC #!s.179<plc?MssJ@QҚY$%eEFih7%"6 lR'Peb$GǗ,Շl@IDsH, V"Aë~Pδ/z=:Wڽ}ux1ypk|G=:0khc$ SC}~薖0:z]]ZhDM4LQ:cx]nQ WM]'4w5AԵe72d&f>Š:,)-)ubVj-KX1|efRƊAEǒR/؈DMVRE:SPKhSyu,( R/qbt&''$imq/9tp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,URNF0D 0}uWx= apw5F]O 9s@_q}'>lg^f [0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDO werBAPK Sig Block 42PK !:9gGÄ resources.arsc5PK!:`zAndroidManifest.xmlPK!: classes.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-cert-and-public-key-mismatch.apk0100644 0000000 0000000 00000000034 14763776540 031564 xustar000000000 0000000 28 mtime=1741684064.8250000 src/test/resources/com/android/apksig/v3-only-cert-and-public-key-mismatch.apk0100644 0000000 0000000 00000030320 14763776540 026426 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSA,( Bn:I] jc ղ0/00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*)lpjXBL6Ԭ6)% Zq̂[s5َY14+lTK'|ݱ,n\ykV< h/PsBl>}666g~߹C:~V^دI&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[= werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-empty.apk0100644 0000000 0000000 00000000034 14763776540 025360 xustar000000000 0000000 28 mtime=1741684064.8260000 src/test/resources/com/android/apksig/v3-only-empty.apk0100644 0000000 0000000 00000000026 14763776540 022222 0ustar000000000 0000000 PK./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-no-certs-in-sig.apk0100644 0000000 0000000 00000000034 14763776540 027140 xustar000000000 0000000 28 mtime=1741684064.8260000 src/test/resources/com/android/apksig/v3-only-no-certs-in-sig.apk0100644 0000000 0000000 00000030320 14763776540 024002 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hS@,( Bn:I] jc ղ0/ ΃J:C8avdw T5F_ՠ;dl&]˚DV unԳ\o{"3P:|+/ȉY g4b3?˹1D%Oj怢 pD{X?7$#J;2EmPİG'A-)= 3M{?F0*GzLᦅ/l:6WđMEUvzn$[ype>,7sP &0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[> werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-no-supported-sig-algs.apk0100644 0000000 0000000 00000000034 14763776540 030365 xustar000000000 0000000 28 mtime=1741684064.8260000 src/test/resources/com/android/apksig/v3-only-no-supported-sig-algs.apk0100644 0000000 0000000 00000030320 14763776540 025227 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSA,(D3" Bn:I] jc ղ0/00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ixѣ`fllLnT3ܡ[SC~|^Q8? [Pv ޜa\$zCݢULo1xș0&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[= werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-signatures-and-digests-block-mismatch.0100644 0000000 0000000 00000000034 14763776540 033005 xustar000000000 0000000 28 mtime=1741684064.8260000 src/test/resources/com/android/apksig/v3-only-signatures-and-digests-block-mismatch.apk0100644 0000000 0000000 00000030320 14763776540 030343 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSmX( Bn:I] jc ղ0/(D3" Bn:I] jc ղ0/00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  hS ,( Bn:I] jc ղ0/00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3tXD3"wfU<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-unknown-pair-in-apk-sig-block.apk0100644 0000000 0000000 00000000034 14763776540 031677 xustar000000000 0000000 28 mtime=1741684064.8270000 src/test/resources/com/android/apksig/v3-only-unknown-pair-in-apk-sig-block.apk0100644 0000000 0000000 00000030320 14763776540 026541 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ UNKNOWNhS~<,( Bn:I] jc ղ0/00] 0  *H  010U rsa-10240 160331161443Z 430817161443Z010U rsa-102400  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbP0N0UDYxgSoFĿy90U#0DYxgSoFĿy90 U00  *H  4ۀ o=Ms<lkIÍvU)?RON^c*r0 \*B\ڏ. HTfUDa~%j>_@ 8vJdV2.49["7:4GaÎ~z#ޔg~PFX%QɌO.Gї `ij _#E4aB}ʠ\,'+|_9nwOLmA\c5oui>xΩE-t 1x00  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) Ċb/ werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-dsa-sha256-1024.apk0100644 0000000 0000000 00000000034 14763776540 027334 xustar000000000 0000000 28 mtime=1741684064.8270000 src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-1024.apk0100644 0000000 0000000 00000030320 14763776540 024176 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  hS,( Bn:I] jc ղ0/00q J(0 *H8010U dsa-10240 160331152710Z 430817152710Z010U dsa-102400+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#QbƣP0N0U:Ui.%0U#0:Ui.%0 U00 *H8/0,Qri /sP[D-w}:6.0, k?m똧[jOIfK@ ?z00+*H80.XHeYY?`·7`ߞV>U vGjᡵ/ȩX0m܀|;oQ}05זjNwb(#c `~AfpqrknRPmNs. XF-~,9r%q ) A]M^^ ɕEUQ:UץXX=;4ڐ8ݐB@Znѷm2@,sĤICJ]G(y^ަ,hH:<=񋂃{a+n6cmVϐڨfL0T ;}li? le3[d״`lp#Qb werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-dsa-sha256-2048-sig-does-not-veri0100644 0000000 0000000 00000000034 14763776540 032042 xustar000000000 0000000 28 mtime=1741684064.8270000 src/test/resources/com/android/apksig/v3-only-with-dsa-sha256-2048-sig-does-not-verify.apk0100644 0000000 0000000 00000030320 14763776540 030015 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ ahSYU,( Bn:I] jc ղ0/c_0[0 .bj>0 *H8010U dsa-20480 160331183001Z 430817183001Z010U dsa-20480G09*H80,oRG"٦Y92+lS[2Z1MZn=Zq !_2h"2~At~zYc{6->rx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBdrx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBd:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ ahSYU,( Bn:I] jc ղ0/c_0[0 .bj>0 *H8010U dsa-20480 160331183001Z 430817183001Z010U dsa-20480G09*H80,oRG"٦Y92+lS[2Z1MZn=Zq !_2h"2~At~zYc{6->rx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBdrx%z}.B_4 @5HŻP~ן7N\;ϛۄ> Qh%e$Ög) Y*B$ np^,rvSFP>1)?Q;|"-=?+(Ojb뚘l\#U pFi !+)nLyg.Yaj}9,7{ZIȾG1N$J2[f!̵H8P[x^qpq{} )"vGEà)o3JD7̨j䣍>?b|nع^Cɤx5yez ^< x4o86;bI1/|[ڀ, P<0ϳUcExf%Ȥ*:Nh 0MH0@o~ c :-Z鑰BT˟i_ڭMs2"%>V'DAP LUMZDٝlB' #EP<M̢F&u.TY"`Ӡ%:DFЯ{OcqCa9h|7k:"SZ Xsh`B-;uL\})tO%4DnO 2L3Q]uBd:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ a hSY U $,( Bn:I] jc ղ0/00 <60 *H8010U dsa-30720 160331183256Z 430817183256Z010U dsa-3072009*H80,(2< u^OO6?qX1t"Q@N|%, p۳$}0.E1.=u,D੝  s-amTKmܣ jY>nDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6ZVU=, "4堵U25нg8n҉<۵009*H80,(2< u^OO6?qX1t"Q@N|%, p۳$}0.E1.=u,D੝  s-amTKmܣ jY>nDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6Z:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ a hSY U $,( Bn:I] jc ղ0/00 <60 *H8010U dsa-30720 160331183256Z 430817183256Z010U dsa-3072009*H80,(2< u^OO6?qX1t"Q@N|%, p۳$}0.E1.=u,D੝  s-amTKmܣ jY>nDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6ZnDfi%'KR;;\YViaP;n$t.}BFsJL=v6^ǼВX5Nj$P3zGn S PȺ{ǯwW6@f&ߵ/rm04Z#l"$hmW & 1f@as/0 n%p ݱ)'߭6a:4/+3KwY.ШǍd@]8{q3PE@G5.΁YR wd%bR˚2x)O)6=%§?#C;7 < bޠ{C@ψ)[!Czv_elt]Gu7wdƇz7AZ0Kõ/9›*ʽ):] ֨ǽEf4.!H l@nPoP= N}x;wGnoH00P=Ah*GdN/Awe;0Ο jq' -T۰q 7 p|S\-Q*h_kiuH0io;xvS.lnU@UAǰ5?+ ϸJq2 $6=4j~*u2*i~'8 )ǓQz #᠄6<*vC.|^҃M:vSs׮bF(@(ul0t9݂8(s3Jq4+d,2znN(35mJ6Z:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSyu,( Bn:I] jc ղ0/tp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,URNF0D 5>luW5uIvz95p:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hS,( Bn:I] jc ղ0/00. VK"j0 *H=010U ec-p3840 160331153057Z 430817153057Z010U ec-p3840v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ P0N0ULŎ+m8<9i0U#0LŎ+m8<9i0 U00 *H=i0f1+]=.re>>$N)\ܾ&, ;|1UBu&3a_2YqML۰L0xasog0e0sR*7~b\#5! ~r-ꄙ m9= 4+I1ue;Q%'WoQijF?D;%RĐeCߎ+x0v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-ecdsa-sha256-p521.apk0100644 0000000 0000000 00000000034 14763776540 027745 xustar000000000 0000000 28 mtime=1741684064.8290000 src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha256-p521.apk0100644 0000000 0000000 00000030320 14763776540 024607 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hS;,( Bn:I] jc ղ0/00T Wt[/v"0 *H=010U ec-p5210 160331153122Z 430817153122Z010U ec-p52100*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzP0N0UEϭ CXve-0U#0Eϭ CXve-0 U00 *H=0BZMɨa{jx=HfZ)*Dh|Jnb^\CIe_4!DB-BqmbW1s2 ~FzUy{d̚8D8WZ<̠ӚW0Bƺ(,1B)w,R/JL{F/ iZ@xFD EҘXA!hG$GE!~ED[@r[1-gmjm"Iƞ00*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzA werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-ecdsa-sha512-p256.apk0100644 0000000 0000000 00000000034 14763776540 027745 xustar000000000 0000000 28 mtime=1741684064.8290000 src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p256.apk0100644 0000000 0000000 00000030320 14763776540 024607 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSLH@- AyӀoUĂ zBk5Tl0'(Ȏ)Stp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,USOG0E!E ky7;'jB恢(>Q /L 7"R nTׂquc&16[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD. werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-b0100644 0000000 0000000 00000000034 14763776540 032077 xustar000000000 0000000 28 mtime=1741684064.8290000 src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk0100644 0000000 0000000 00000030320 14763776540 031442 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00. VK"j0 *H=010U ec-p3840 160331153057Z 430817153057Z010U ec-p3840v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ P0N0ULŎ+m8<9i0U#0LŎ+m8<9i0 U00 *H=i0f1+]=.re>>$N)\ܾ&, ;|1UBu&3a_2YqML۰L0xarnf0d0 {&$%ؐ;OJu2w'َ' tWDH8p0. j#AMTݸE5!ZiW7tdH x0v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ werBAPK Sig Block 43PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-ecdsa-sha512-p384.apk0100644 0000000 0000000 00000000034 14763776540 027747 xustar000000000 0000000 28 mtime=1741684064.8290000 src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p384.apk0100644 0000000 0000000 00000030320 14763776540 024611 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00. VK"j0 *H=010U ec-p3840 160331153057Z 430817153057Z010U ec-p3840v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ P0N0ULŎ+m8<9i0U#0LŎ+m8<9i0 U00 *H=i0f1+]=.re>>$N)\ܾ&, ;|1UBu&3a_2YqML۰L0xarnf0d0 {&$%ؐ;OJu2w'َ' tWDH8p0. j#AMTݸE5!ZiW7tdH x0v0*H=+"bcu?q4ģ= 8A(bo#(%=5C~>U.cXKۜ' Ʒ8?\ werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-ecdsa-sha512-p521-sig-does-not-ve0100644 0000000 0000000 00000000034 14763776540 032104 xustar000000000 0000000 28 mtime=1741684064.8300000 src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk0100644 0000000 0000000 00000030320 14763776540 030412 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hS[LH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00T Wt[/v"0 *H=010U ec-p5210 160331153122Z 430817153122Z010U ec-p52100*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzP0N0UEϭ CXve-0U#0Eϭ CXve-0 U00 *H=0BZMɨa{jx=HfZ)*Dh|Jnb^\CIe_4!DB-BqmbW1s2 ~FzUy{d̚8D8WZ<̠ӚW0AHVS?v;ILGa ⚾# ay&#EūP(h36CB<2טb *7T.ib:}k)*eoך/hn00*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPz! werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-ecdsa-sha512-p521.apk0100644 0000000 0000000 00000000034 14763776540 027740 xustar000000000 0000000 28 mtime=1741684064.8300000 src/test/resources/com/android/apksig/v3-only-with-ecdsa-sha512-p521.apk0100644 0000000 0000000 00000030320 14763776540 024602 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hS[LH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00T Wt[/v"0 *H=010U ec-p5210 160331153122Z 430817153122Z010U ec-p52100*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPzP0N0UEϭ CXve-0U#0Eϭ CXve-0 U00 *H=0BZMɨa{jx=HfZ)*Dh|Jnb^\CIe_4!DB-BqmbW1s2 ~FzUy{d̚8D8WZ<̠ӚW0AHVS?v;ILGa ⚾# ay&#EūP(h36CB<2טb *7T.ib:}k)*eoך/hn00*H=+#ay( ȰTʯr,yN ~ua7*F 6 0YV&,S֕'a(P6ӵ)kشӘ J~|En6/eBusHPz! werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-ignorable-unsupported-sig-algs.ap0100644 0000000 0000000 00000000034 14763776540 033054 xustar000000000 0000000 28 mtime=1741684064.8300000 src/test/resources/com/android/apksig/v3-only-with-ignorable-unsupported-sig-algs.apk0100644 0000000 0000000 00000030320 14763776540 030071 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ #hS(D3" Bn:I] jc ղ0/( Bn:I] jc ղ0/HwfU@- AyӀoUĂ zBk5Tl0'(Ȏ)S00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ZV]LT[eO";lB,zW|:QnX6ZG.-BXL/ċ1LN}%5C(xQG.v6+:-1۔ o|M/lg bD+g].&m$D5U=xI\Ӑ9:5%Sݐs[6}Sȡur$M(jHUP7X涧j*S dex>ZV]LT[eO";lB,zW|:QnX6ZG.-BXL/ċ1LN}%5C(xQG.v6+:-1۔ o|M/lg bD+wfU]jYhDfN֣+Zpם`;1j=JLS ;U;4tNILH<nͧasSgCj).puݧ3b5\gN$mxS$1n8 p%Y s`bM\Tbn{{>m\_(BW7.qJxl1Q [MZ}eJ hn߫Q5Rcp3n͋y+ p ś&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha256-1024.apk0100644 0000000 0000000 00000000034 14763776540 030371 xustar000000000 0000000 28 mtime=1741684064.8300000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-1024.apk0100644 0000000 0000000 00000030320 14763776540 025233 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hS~<,( Bn:I] jc ղ0/00] 0  *H  010U rsa-10240 160331161443Z 430817161443Z010U rsa-102400  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbP0N0UDYxgSoFĿy90U#0DYxgSoFĿy90 U00  *H  4ۀ o=Ms<lkIÍvU)?RON^c*r0 \*B\ڏ. HTfUDa~%j>_@ 8vJdV2.49["7:4GaÎ~z#ޔg~PFX%QɌO.Gї `ij _#E4aB}ʠ\,'+|_9nwOLmA\c5oui>xΩE-t 1x00  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbF werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha256-16384.apk0100644 0000000 0000000 00000000034 14763776540 030470 xustar000000000 0000000 28 mtime=1741684064.8300000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-16384.apk0100644 0000000 0000000 00000050320 14763776540 025334 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ /!hS!!C,( Bn:I] jc ղ0/00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$9&S H`+s%@[k-#-j b¹6ê.-aYdkMkS.^٫c߭=E<ڎT\ Λ3Y=!<~3uG*#=Dp0TYA]= O"|Xe4F}e6:zCT5Gso !q2>Pp_[=jξI+>ΖY-v@GÞN7޸Bd &F`N )h(ΏB1abz{ k2J0=ń,חKp0Ԇי.\dȸD(/ʯؒ>JP)o-5{Gawٺ.+de4)\#&!icsi4U~c.byyk!bc|X%wl\slzMɆF-VߵkI \$A-/n# h1K/ a.Tl2& :ږUX.?%Ff]|@Et oJD|# Xj\:6ڝY5E3 5y}'+&/B B z?l#gtxi.}<@E}tK! ߄bٲvfuf=UlBrM\sdX ;Y[׶ꃆ_H s#qn3e -c"ym?u*d~MF"szDYumYً[0}!̀qxF |]S ɕ?%Zi4ſFF?* ,qHx̳{FExtZwmm%+tjnҡ2xCٱkWlU*Ɠcj^w|^2**gQ@z0j0Uq׍/lȫ"7> b<. (u`APXѨ%:pߔ>CUWZ Cy gn{s1B}˟a .IK/؉iBZ^$(6}H"9kBVf,ZYds:xK-%ifқvpY-E[wJ$6#!U=q/x7be)%Ս l) {JqK?"`Sɵ; ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0;werB/APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKP./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha256-2048.apk0100644 0000000 0000000 00000000034 14763776540 030400 xustar000000000 0000000 28 mtime=1741684064.8310000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-2048.apk0100644 0000000 0000000 00000030320 14763776540 025242 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSA,( Bn:I] jc ղ0/00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@* ~W5z?ώS3yB玚XB_vp˲KBIkI-iu "T6iƑ,υ.`'qa@X*aYi@>Ur Tј3sFII9?,ƤHl>&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[= werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha256-3072-sig-does-no0100644 0000000 0000000 00000000034 14763776540 031746 xustar000000000 0000000 28 mtime=1741684064.8310000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk0100644 0000000 0000000 00000030320 14763776540 031050 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSA,( Bn:I] jc ղ0/00a ga0  *H  010U rsa-20720 160331191458Z 430817191458Z010U rsa-307200  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~P0N0UOfql5ާS0U#0Ofql5ާS0 U00  *H  "UVkKefڻY,ZcS.Bê͐ŬpÅ_/6Nq `6u'KZz{ҍ*7B>(qDV_I]1TyώUQ|ʗ i}9HЍQo݃^n񀃯iǽM@P'mclpWX _C"֓cXQ)G@($'hL0* ƵnҾJ݂*uoj$gSyyҾ{wc޺yHec@Y{vk&kIq9g\9gp"T_,DG((Fl+-^XO.Xv®FUC'v~ l8~|nUq\v77#2[59(g֗1`VέـH6N`Y %y@?3eth1D3}"v6;]yޡJJxL@Sujŷ=/ (4ݶX0&`d)%5V`]݀jl6HZqDeokq ,yL]RWYW܄"!SvgZ]݋*iI:^icu'dHs%"TfS9w*pӇpZY!w[yBnZ85"ޏq8i!Ǹ#aof@d TioSg+v O2a4¦00  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~=werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha256-3072.apk0100644 0000000 0000000 00000000034 14763776540 030376 xustar000000000 0000000 28 mtime=1741684064.8310000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-3072.apk0100644 0000000 0000000 00000030320 14763776540 025240 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSA,( Bn:I] jc ղ0/00a ga0  *H  010U rsa-30720 160331191458Z 430817191458Z010U rsa-307200  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~P0N0UOfql5ާS0U#0Ofql5ާS0 U00  *H  "UVkKefڻY,ZcS.Bê͐ŬpÅ_/6Nq `6u'KZz{ҍ*7B>(qDV_I]1TyώUQ|ʗ i}9HЍQo݃^n񀃯iǽM@P'mclpWX _C"֓cXQ)G@($'hL0* ƵnҾJ݂*uoj$gSyyҾ{wc޺yHec@Y{vk&kIq9g\9gp"T_,DG((Fl+-^XO.Xv®FUC'v~ l8~|nUq\v77#2[59(g֗1`VέـH6N`Y %y@?3eth1D3}"v6;]yޡJJxL@Sujŷ=/ (4ݶX0&`d)%5V`]݀jl6HZqDeokq ,yL]RWYW܄"!SvgZ]݋*iI:^icu'dHs%"TfS9w*pӇpZY!w[yBnZ85"ޏq8i!Ǹ#aof@d TioSg+v O2a4¦00  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~=werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha256-4096.apk0100644 0000000 0000000 00000000034 14763776540 030405 xustar000000000 0000000 28 mtime=1741684064.8320000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha256-4096.apk0100644 0000000 0000000 00000030320 14763776540 025247 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  hS A,( Bn:I] jc ղ0/00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSA ,( Bn:I] jc ղ0/ 00 7%0  *H  010U rsa-81920 160404193246Z 430821193246Z010U rsa-81920"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnP0N0U]FsR$2/~?0U#0]FsR$2/~?0 U00  *H  hU_##B)q q;)1eG+)9RN&~W$`LYR8n \WmӔCzMaOiڒnR FSfv1$>3{ >\~Z{ {J~nt |)`tڝs3)1 tZ֝d:J蕯WSЃ(Ѝ$#&Pt\X2DvGê9ǢcS6w-aDfBHjAn"1_M+%w|]/O߂D*%'Y T=1' L}eI odw g8NR)dlo6"h9 r\h/[5,dGzNS4]sr #F7ԯ7@'Zb8.;:!憌Q]3:+8>6~^ 1V̋4L"rzE1r.7Lv1x =(+1 Kc ~=]#pփ,sy MI3V7up3Hi׈ˊGJpY{ASm!ԍ7YlC{fo;[cG(Rk33eRۃuRߨcvH- i=\hC,*#n'cY"d46S pfy s݉'H?b;Pؠ.  )U)avggVo;d85qڸ /WHNNa4_?oC~ިmU8aKv'˾ɴ9ʸ$sO¾C{`P:l7CEcv*$fibxswڥ-%۽:M]\7!U笑ݖ/vJ6?C$:Bv,2 M,ȥa!b! c*6`4rwX kSRj4 &(BQIo˃!"bn^L<j\͞%(s}}KE(Ks5YuZ#18Ilpz  xl^ j+-޳i"*[sp)􋊙Ζ1A jvľˊlY"B*WV[8ٳtVHyq02*ڄ &C.m{XϪڦy1+m}'3WXP\Ioh]h?G"* ~03<~RK'4 ț+DR~2Ş4!hU507`5V .p TRIAHÇ('zQ™@[v⩅nG 0_"f :KR(6w/En0 1`cα{T:*;^TX5vV@_2pNĂy_fx,m.y펌vS;gi#wZiQX.-z0Z·>܄,v?JoW1+~] sVOHMAb5l16Exf]z'BɘKP=JShb|*0 ']r|K9T[?*}B}xW z=C}йp;NYӉ.zݒt bb-udZj_IfN%CE0G/OPqh8qBĖ'+CQJб150gNֈ ?徫2c,{d%j5G&0"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[Wn=werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha512-1024.apk0100644 0000000 0000000 00000000034 14763776540 030364 xustar000000000 0000000 28 mtime=1741684064.8320000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-1024.apk0100644 0000000 0000000 00000030320 14763776540 025226 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hS\LH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00] 0  *H  010U rsa-10240 160331161443Z 430817161443Z010U rsa-102400  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) ĊbP0N0UDYxgSoFĿy90U#0DYxgSoFĿy90 U00  *H  4ۀ o=Ms<lkIÍvU)?RON^c*r0 \*B\ڏ. HTfUDa~%j>_@ 8vJdV2.W?.Ӹd}$9)VdO4؛ݸX+ m1Ge3˵vdpf|}w/3ox>O|c7" KdD+OShMŐDK‰Mؼ00  *H 0㏯> "c{/ ~WU_yAq{{j2'&QW=FS oꊻlp|_f!@o=1 ԷjL8l}  , 5)[.WG) Ċb& werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha512-16384.apk0100644 0000000 0000000 00000000034 14763776540 030463 xustar000000000 0000000 28 mtime=1741684064.8330000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-16384.apk0100644 0000000 0000000 00000050320 14763776540 025327 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ /!hS!!cLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$bW&%`TeCza(f]#/ZOI]$+Auw 9ݼ2ԧ^bJp$+_b<;:O ZrvrKJPެBVZ׳&xjq֝c:BhQ`U o:+1fa(`}l+ҖsK$Vmo6`nyvEH2d=ĕ> @TӉ_bZ?K;YEWJ׆tsPib*i3PHU0۸E/>y ‚QS;~+-ۍR=hea)!!9?(palSX6/Xb%c--3]\{GZFoӥ'Skة/DF../աT?֥`m8„> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0werB/APK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKP./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha512-2048.apk0100644 0000000 0000000 00000000034 14763776540 030373 xustar000000000 0000000 28 mtime=1741684064.8330000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-2048.apk0100644 0000000 0000000 00000030320 14763776540 025235 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSaLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ℱoeHv{ ٍfx_dh4+ܧi.0]ȘҊD6saN{ނtoӼy帴 ߋl3q3)&qWG8 ל:)U~85ȮɌ g bF`rj*uPhZ3+lR3ߵЊjwU&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha512-3072.apk0100644 0000000 0000000 00000000034 14763776540 030371 xustar000000000 0000000 28 mtime=1741684064.8330000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-3072.apk0100644 0000000 0000000 00000030320 14763776540 025233 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSaLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00a ga0  *H  010U rsa-30720 160331191458Z 430817191458Z010U rsa-307200  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~P0N0UOfql5ާS0U#0Ofql5ާS0 U00  *H  "UVkKefڻY,ZcS.Bê͐ŬpÅ_/6Nq `6u'KZz{ҍ*7B>(qDV_I]1TyώUQ|ʗ i}9HЍQo݃^n񀃯iǽM@P'mclpWX _C"֓cXQ)G@($'hL0* ƵnҾJ݂*uoj$gSyyҾ{wc޺yHec@Y{vk&kIq9g\9gp"T_,DG((Fl+-^XO.Xv®FUC'v~ GJP+GІg/j@b%y@5}ED_m X>NbITb4epc#WSehD캵W%F1u/]Y!'b}FoF+VP ,\͝ !rk{5nئ00  *H 0"Y3o%VRcs=|’Z,KxEK%eG~%&/'ŭ&S(URX6Wkǻ&es4eA˟pav_k漟ԉ [.%{ϟy(AHF%SN,V^U #w1 C;Ah ]CMue_MYc`4C7D$ `djO4c0\֤*'lOhs37nqej'ʦ-\M!gvDm*TٽC3Lr mEV$*x&3_fH,d#w[ Vȇ`)+l[oI~werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-blo0100644 0000000 0000000 00000000216 14763776540 031733 xustar000000000 0000000 114 path=src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk 28 mtime=1741684064.8340000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch0100644 0000000 0000000 00000030320 14763776540 031664 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  hS aLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=OL̕ܤ9g͗û`gS-$bG{Cn>@W~Ԕ_ˢ5£{2olQQ#Zy qU"I G#gh߉sgTe R>OP'Pwf7rԱ;V0c@RV졍Œ10aƁXq s?`]XDXK!hqTV=Gk~#i¸9X}$O踰J V%L2W|`X)c~qJ7} yC_K<6>K&:f3#'CVT 7QVAF}rk䛐VڛϹP'a8* C߼T[CFS.En+qvDt&>; QK:UHx+KPG pSԅT;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ  hS aLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=OL̕ܤ9g͗û`gS-$bG{Cn>@W~Ԕ_ˢ5£{2olQQ#Zy qU"I G#gh߉sgTe R>OP'Pwf7rԱ;V0c@RV졍Œ10aƁXq s?`]XDXK!hqTV=Gk~#i¸9X}$O踰J V%L2W|`X)c~qJ7} yC_K<6>K&:f3#'CVT 7QVAF}rk䛐VڛϹP'a8* C߼T[CFS.En+qvDt&>; QK:UHx+KPG pSԅT;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSa LH@- AyӀoUĂ zBk5Tl0'(Ȏ)S 00 7%0  *H  010U rsa-81920 160404193246Z 430821193246Z010U rsa-81920"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnP0N0U]FsR$2/~?0U#0]FsR$2/~?0 U00  *H  hU_##B)q q;)1eG+)9RN&~W$`LYR8n \WmӔCzMaOiڒnR FSfv1$>3{ >\~Z{ {J~nt |)`tڝs3)1 tZ֝d:J蕯WSЃ(Ѝ$#&Pt\X2DvGê9ǢcS6w-aDfBHjAn"1_M+%w|]/O߂D*%'Y T=1' L}eI odw g8NR)dlo6"h9 r\h/[5,dGzNS4]sr #F7ԯ7@'Zb8.;:!憌Q]3:+8>6~^ 1V̋4L"rzE1r.7Lv1x =(+1 Kc ~=]#pփ,sy MI3V7up3Hi׈ˊGJpY{ASm!ԍ7YlC{fo;[cG(Rk33eRۃuRߨcvH- i=\hC,*#n'cY"d46S pfy s݉'H?b;Pؠ.  )U)avggVo;d85qڸ /WHNNa4_?oC~ިmU8aKv'˾ɴ9ʸ$sO¾C{`P:l7CEcv*$fibxswڥ-%۽:M]\7!U笑ݖ/vJ6?C$:Bv,2 M,ȥa!b! c*6`4rwX P#T፱~,. YcC;Yh-enٻٴpW^\5CEf ;egq \W8bq\zf}6E2п(p:PׇPۧtۊ P黪?xjR H8۷`\*łۭic}8I9Y$*3FG,4bJP Ppel"._u{=zj(3 1Q|kY'JB5 b[#n<𨬚ta%#EO$^74&0"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-rsa-pkcs1-sha512-8192.apk0100644 0000000 0000000 00000000034 14763776540 030401 xustar000000000 0000000 28 mtime=1741684064.8340000 src/test/resources/com/android/apksig/v3-only-with-rsa-pkcs1-sha512-8192.apk0100644 0000000 0000000 00000040320 14763776540 025244 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ hSa LH@- AyӀoUĂ zBk5Tl0'(Ȏ)S 00 7%0  *H  010U rsa-81920 160404193246Z 430821193246Z010U rsa-81920"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnP0N0U]FsR$2/~?0U#0]FsR$2/~?0 U00  *H  hU_##B)q q;)1eG+)9RN&~W$`LYR8n \WmӔCzMaOiڒnR FSfv1$>3{ >\~Z{ {J~nt |)`tڝs3)1 tZ֝d:J蕯WSЃ(Ѝ$#&Pt\X2DvGê9ǢcS6w-aDfBHjAn"1_M+%w|]/O߂D*%'Y T=1' L}eI odw g8NR)dlo6"h9 r\h/[5,dGzNS4]sr #F7ԯ7@'Zb8.;:!憌Q]3:+8>6~^ 1V̋4L"rzE1r.7Lv1x =(+1 Kc ~=]#pփ,sy MI3V7up3Hi׈ˊGJpY{ASm!ԍ7YlC{fo;[cG(Rk33eRۃuRߨcvH- i=\hC,*#n'cY"d46S pfy s݉'H?b;Pؠ.  )U)avggVo;d85qڸ /WHNNa4_?oC~ިmU8aKv'˾ɴ9ʸ$sO¾C{`P:l7CEcv*$fibxswڥ-%۽:M]\7!U笑ݖ/vJ6?C$:Bv,2 M,ȥa!b! c*6`4rwX P#T፱~,. YcC;Yh-enٻٴpW^\5CEf ;egq \W8bq\zf}6E2п(p:PׇPۧtۊ P黪?xjR H8۷`\*łۭic}8I9Y$*3FG,4bJP Ppel"._u{=zj(3 1Q|kY'JB5 b[#n<𨬚ta%#EO$^74&0"0  *H 0 =$֑Zpsq@`DG*Dz =u}tgk"мz_y=Atw2{įTE/NǝpU22U.+]㘿HoNlγbPcs6*~&tG!4Dy+ͩ7,X߬`w.ՍMdjRjCr O6v=||a `U.YAhCD%%sq>]\̖^)cvaw#jAǨtl\PQY̷8Wl"B=@t- \>wD’ouz;ub@z%ڹ2B.Fcڟr =3N!6ȨHv}}96RLZӭIN u~ (&DV_ c$C.̜,nd^' ARҶ `hwՔy Z^(^dT.hd,G A / BTZ=E%wqCJi_єSq䒙]M1 h8=@ʳrC(/\zצC8lڣwx<"ԱϾquN5՞|!˩|m#m .<1>7q:> (hL /P+lK0yڶMۅDO H瞻4<Ulxk;{Ꮿ>bOSS"}kٛm%{+TF)ku--' t6*Zx2I?*\BC|<Xl[S@m*#ѪRqJcP!cZ_7F r^Rth 6rِf(:89[WnwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v3-only-with-stamp.apk0100644 0000000 0000000 00000000034 14763776540 026317 xustar000000000 0000000 28 mtime=1741684064.8350000 src/test/resources/com/android/apksig/v3-only-with-stamp.apk0100644 0000000 0000000 00000030427 14763776540 023171 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlTnRA=-?D]21Mwmc S{ %ܫiWt飸ra|p >rIuafΜ7?w5di$։]/bmb8 6qA|%~2 E|#CC88coPde%Z5"2M1oi?|XB3|_ptDeDcjEդ~!>GZ*]wؓk2{nǷʰ45Ө<331uE̡DZuXǚѥd<ֵf%+S&pqM95A{'x`3iO(YGLLP12Lږc=62D[9L%/PS=U]d|8OkMTD&!`B<BƘ F YcF9c"yP[HKYѱ*W;.'!y Am˅zc]bhE˭z^sͷ_p7qjy\`J8|945O8 x-9^kz9Gs'^+)7,Xq3Ż8 +;~~\kHx?ɽ53_PK;8PK!: classes.dex]HQ|U~mJ䃄:A}X u溎.eHK=R`kPASoA/=t޻"939]=>ɧ/|+쪟Qg]-Qȝ3y4GˆD~Ȝs BA3ͩC;O̬P4/qu4T#V}fRGPe\a$C×,Ul$GIDst$||E+ Oف 3Y?(fڝ =jͧ.{nw< 83>&Y 41zz{wz[Zԫ}f[ZKk5Ms0FjbZz`jo>9ܨ)IwJͤm [YW%b6)eAyNiIPk_Z\Ɗ',32Vj-;zF\ jr-ԙbPKr"PK!:S% stamp-cert-sha256 ]P0o V2oIO<[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtD% m00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*%4_|>}s?XN>j}NrsC썵KC;/,-*ή[}'a<-ew }Q}3I[XJ??lHG4Hs>٣АMzяP5d 9B 4 21ji[3t! werBAPK Sig Block 42PK !:9gGÄ resources.arsc5PK!:;8AndroidManifest.xmlPK!:r" classes.dexPK!:S% stamp-cert-sha256PK0./PaxHeaders.X/src_test_resources_com_android_apksig_v3-rsa-2048_2-tgt-dev-release.apk0100644 0000000 0000000 00000000034 14763776540 027632 xustar000000000 0000000 28 mtime=1741684064.8350000 src/test/resources/com/android/apksig/v3-rsa-2048_2-tgt-dev-release.apk0100644 0000000 0000000 00000040627 14763776540 024507 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ hS ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3tH<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqX werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v3-stripped.apk0100644 0000000 0000000 00000000034 14763776540 025075 xustar000000000 0000000 28 mtime=1741684064.8350000 src/test/resources/com/android/apksig/v3-stripped.apk0100644 0000000 0000000 00000070320 14763776540 021743 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ O! q!!kLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$̷l:H;x:MmE;e,OdR'WM:X? :cw/a$a\B-=ۣ-Kr~Rx`+$xY\=H_Ӭu&m"VVۙdߊXi/Qm4U Eevu9Vۑ,4!U%q>)|cYC1O-e߽Jn2*PFS[%'X㘊1=WxDiݐt%z6<1߉ e/;u5'}T8ˊ>M] Ң\)wkyiS> +~~oc]mƿORfY S%C̃HKY 7˼GÞl -2E0,S]pJvs$ q'to{$Z Ж14J`՜ɩ-쑁*_KkZz`!䜹|ؼKIap0.?%H1ELp(⭇D}`?IWqᕣ綈%`7P/,*?qkbf8&Z~+`[8{sS8s 24i:T~:JjBc:4"j-2ie_oi6`Cm71va)֍wx ށj=6']L+]vVel8ЍKzk.,d>qPbɱ iYҩc4uaY#Zf4+c_gwyҭ.E&<(etAÍ1yH_l4fhn%&0"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0!werB!!cLH@- AyӀoUĂ zBk5Tl0'(Ȏ)S00 wn0  *H  010U rsa-163840 160404193431Z 430821193431Z010U rsa-163840"0  *H 0 س+O)6_vKE_IϷMSgw 1f_FsĒ~8H*ƾ{1Lr/}xV沅0H%]qV'u | v҄k5te: DҜ̒ Z y\ӑ4IT\lpA[*:Y1t/;drwH7xEnP"85k''YfR8,-pN> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0P0N0U$p_DLs"0S,0U#0$p_DLs"0S,0 U00  *H  G;b$ ENBlWogp/6@HI/ z⾤$* !XxIP<O܆"hmYOg}]ߑ"&$=#e}' hYfy(P{f//s~%ydbqm 74*~N jKmdppdqGF>K*4tORvcm:S[{b>m}+L݈9On;K&fY#,rst^'%hL0@BMQ1Ce9."pnuJIi;9>d"ZZ4L-Y$IQ0{Dky!,Sv>?S)҄ܧ xktHAi(Wc* 5sҿhdf;nUaGJ^TCnLNswGn>ӕWݭ5a=S6գf)_Wr-0͛`RC5/.x$5["=r7P;E3[\bce !pY =iC`yΟK${L%di/]rBqBid.C$L&vW?|ךr|pzs62}PƓ6@GyM?P'O;]ۻykQ3#_Un3;̓G}5 +9f̸2n,sɽ)WJV{.Rnx~zBћ*J-׶<ʆO6&%T nrӘ@>u, " z$ ɻk"hVߜXnM*3f"[*=u(/ Mʹ 7!]`Hjx ༫$xx'<Na? 9A+^BH,x:1$bW&%`TeCza(f]#/ZOI]$+Auw 9ݼ2ԧ^bJp$+_b<;:O ZrvrKJPެBVZ׳&xjq֝c:BhQ`U o:+1fa(`}l+ҖsK$Vmo6`nyvEH2d=ĕ> @TӉ_bZ?K;YEWJ׆tsPib*i3PHU0۸E/>y ‚QS;~+-ۍR=hea)!!9?(palSX6/Xb%c--3]\{GZFoӥ'Skة/DF../աT?֥`m8„> ݊B?'7IԫceP";0B7{AH >WF,I#mg&yzxgq9/4P$WdFUY &1P48ʲiQ^V.$`K_ĎM.!_#Ü}R3x>[we6`:^}LB6y9 OK@9&]^9MQ$#׊Sq_7zaY2J\ɒ%%o_sU& pѕD<^ɻ9,&`]2{&bEjۆsJfsQ`4 ,=` G}2YBJpXM"Fo7eyC+KJM@+ RNiPȕs}ڕyq qJ88jny -nKSg~B(p/NϽBoc P~wX9j)mM=;BzFIW+d+Ϊv\&r曽Wr`FDE=:Lpc:{ZЬ˵>;MLȞA/T&mS.kT&J\I[M=iJh*Aߡ0 q3=JU"¨9ezi֚a@>DEX\8Wx V$h`)%ZT :ljxbD~:? @٥e17Io(E}L^b1j RM3֦_OpJtH ]\j9FԗLVHc v l}QS_t2#Z7̨0Ө:ĜW pm&q9䑢7k7_yێ'fE~۩E_g J  /;%-DafAi1{}Ah̪Ϧ X 6#O5OhxAk0Y#V/@q9 l![g1<^trnh ~7sVMhY7f&ZK}v qKIq8K%<|]gn%9֞z(ٸ9 iEqUQ6EH z{/VEU:e#M# ǛnAeU 0^ werBOAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPKp./PaxHeaders.X/src_test_resources_com_android_apksig_v31-2elem-incorrect-lineage.apk0100644 0000000 0000000 00000000034 14763776540 027720 xustar000000000 0000000 28 mtime=1741684064.8360000 src/test/resources/com/android/apksig/v31-2elem-incorrect-lineage.apk0100644 0000000 0000000 00000040627 14763776540 024575 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ a ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >Duu.[c&2ʎ$BcNs8(_o ү uy\!Ize#EpYqQK^et[.| C_4q+3c'|Y%[^ VU~So߃l$0ד-JfHN2Ȏ[&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq hS ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >Duu.[c&2ʎ$BcNs8(_o ү uy\!Ize#EpYqQK^et[.| C_4q+3c'|Y%[^ VU~So߃l$0ד-JfHN2Ȏ[&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v31-2elem-lineage-incorrect-digest.apk0100644 0000000 0000000 00000000034 14763776540 031175 xustar000000000 0000000 28 mtime=1741684064.8370000 src/test/resources/com/android/apksig/v31-2elem-lineage-incorrect-digest.apk0100644 0000000 0000000 00000040627 14763776540 026052 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ a ,( E~m̍M(C- 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >=O"*{+JwI7^]LGAu [rK2:JHsVkϢEўOnbRW:GNrz/Ej du)+>)GW0Ӱ A E!r]tsXRܑބl͋S "PZE]"IkXh@M&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq hS ,( E~m̍M(C- 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >=O"*{+JwI7^]LGAu [rK2:JHsVkϢEўOnbRW:GNrz/Ej du)+>)GW0Ӱ A E!r]tsXRܑބl͋S "PZE]"IkXh@M&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqwerBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v31-block-stripped-v3-attr-value-33.apk0100644 0000000 0000000 00000000034 14763776540 031101 xustar000000000 0000000 28 mtime=1741684064.8370000 src/test/resources/com/android/apksig/v31-block-stripped-v3-attr-value-33.apk0100644 0000000 0000000 00000030627 14763776540 025755 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSM,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*],od0L̏#2] 4|W:U?r}m Wc0U9dվ ) IV; j/ҦtWp Sz5aR&YD2b~r:X'3`Yբxmmd6 A\&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v31-ec-p256-2-tgt-33-1-tgt-28-targetSdk-30.apk0100644 0000000 0000000 00000000034 14763776540 031112 xustar000000000 0000000 28 mtime=1741684064.8370000 src/test/resources/com/android/apksig/v31-ec-p256-2-tgt-33-1-tgt-28-targetSdk-30.apk0100644 0000000 0000000 00000030634 14763776540 025764 0ustar000000000 0000000 PK !:9gGÄ resources.arsc5 (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp P0 string, app_nameThX@ThX@enXAThX@arXBPK!:AndroidManifest.xmlSn1=$MȣiR胖+DS*X +4IFIhf@TB`Q!>o p|cgӈ{m4 (N'3'V)'Έw"{KCO;I%nDh@vI㘳g@}ghBDdL1{_y2Q,#8!g r5e5웴>}\oGv951gN~GLVvdxeM6!m s>1^E+bKD'N&_AǒK,zIT:]}[4m?pGdb6G2Hږc6t KSo#H%c9ԥUBGܑxk?>;"86 }ST@SJkĈVQ*$"̽~]f_77튼*:rpi9M᲎2\ w(ߢYiƳL yI&Cf:Zv/=>O8>Zt/k75h--۴͎Ѵ6%c[k/8ޒWgU^,`yN"jPK`zPK!: classes.dexMhA~d5mmlH\ iZjRPDNm71 -ŪAzqЦ !+|9F~ɏVҐs v܈;Vv?J7 >˦4B\(2/i3 JC #!s.179<plc?MssJ@QҚY$%eEFih7%"6 lR'Peb$GǗ,Շl@IDsH, V"Aë~Pδ/z=:Wڽ}ux1ypk|G=:0khc$ SC}~薖0:z]]ZhDM4LQ:cx]nQ WM]'4w5AԵe72d&f>Š:,)-)ubVj-KX1|efRƊAEǒR/؈DMVRE:SPKPK!:r!*META-INF/EC-P256.SFen@= (֒QAn`e@g__cl͹ 5-# $ueHJo FPC0@3+ߋtLC`[*·! 5)psVXKݜwj/,{1 ؐIQX!J uoH]E%QOӜ"wI gi3儵0uN4]oPK!:Kb:META-INF/EC-P256.EC3hb2cjhδIݠIѐ߀3̓1qAcAc .`fbdbd8ſw".6s,LB|l̡,<ɺFfr⼆fƆ&fQ&P.V=l`S333;1,U:)<};/+פ][tn¥-NsOl~rr~Ps˖cc"l3@HUh3' A,b 032GfO7&E, r}".]8?9$Yqk@%?-i>@Zc/]z 1+;XtkmylA1 :V7&.Yo|'8&%WU;I)0bR^;ۺxzv/nϓUQūR _PK!:i@FMETA-INF/MANIFEST.MFeN@= Rp ZjPZfr^M4ϟ/$ur 󦶤ˁdgTtw oNiQؓ ,ɮorrPyX&L͒]Bsyq^2XI]Q5joL7gMK0nBp3ΖT7t}W~` |eOGLlON'p*S4;:a<Ġ9ڴ+Z_yQ qzv,( ?LלCᐯPnR.QΐEtp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,U  SOG0E U@* 6.V7m05sІ!SдPohifLP/]뇓[0Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDa(,( ?LלCᐯPnR.QΐEuq0m0 UҲ%0 *H=010U ec-p2560 180713174151Z 280710174151Z010U ec-p256_20Y0*H=*H=BLr< =bPx3nuSJkPa 'b-b vzD"P0N0Uy+D(^_0U#05h[02 qC[0 U00 *H=H0E 'i4yĮcR, +C0!`l` $1I* sgo;xp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,Uyq0m0 UҲ%0 *H=010U ec-p2560 180713174151Z 280710174151Z010U ec-p256_20Y0*H=*H=BLr< =bPx3nuSJkPa 'b-b vzD"P0N0Uy+D(^_0U#05h[02 qC[0 U00 *H=H0E 'i4yĮcR, +C0!`l` $1I*F0D `:z,y+RI^"*X X=͎$и\3PduDk RNF0D I~)\lZ&!=XVnI i j`u!$L(1@ָ:az2%zS [0Y0*H=*H=BLr< =bPx3nuSJkPa 'b-b vzD"hS,( ?LלCᐯPnR.QΐEtp0l0 Mfr0 *H=010U ec-p2560 160331145806Z 430817145806Z010U ec-p2560Y0*H=*H=B_="I1+x_e6NΐҴᔵ ܎T:v2%ǂtDP0N0U5h[02 qC[0U#05h[02 qC[0 U00 *H=I0F!l),\Tǟ@ax!lWATG۸,U U  TPH0F!A x)ޠ`aE{:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[aA,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*#_nxI0zB=/r'٬XW|zk<֌CPُjkw ގS:+@И=Y~yG42b| ,+0y@ = CbӁx%#TČֶ`}'MʶܝW0LY#K/R[ZtOg}Ͷ&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK0./PaxHeaders.X/src_test_resources_com_android_apksig_v31-rsa-2048_2-tgt-10000-1-tgt-28.apk0100644 0000000 0000000 00000000034 14763776540 027340 xustar000000000 0000000 28 mtime=1741684064.8380000 src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-10000-1-tgt-28.apk0100644 0000000 0000000 00000040627 14763776540 024215 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ a ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t'@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >Y /06M͔|!!,$ aKG1檎:)Сjhua h1! 5[_Dc&kQOMd][M |h%ը6V?L` h8BP|U[ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqhSM,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*]: (ĭ}3&O¢eE_&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v31-rsa-2048_2-tgt-10000-dev-release.apk0100644 0000000 0000000 00000000034 14763776540 030351 xustar000000000 0000000 28 mtime=1741684064.8380000 src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-10000-dev-release.apk0100644 0000000 0000000 00000040627 14763776540 025226 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ a ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t'H<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? > b -yu'ߎES5TR&{uL~18#\,mHg}7Sekk60 2+XSrmn4#o3L";l JK,9ϮccWH{?ػҁ܍) t=C= ~qxM< [j<~N.E7i&&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqhSM,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*T9 N >JKqpY6Āhf٣J.%-0'Gs maofBZpȦ277 mT+YΗ8gaw(!peK~G~.s\C 7R+:˷'U ?&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v31-rsa-2048_2-tgt-10000-dev-release.apk.idsig0100644 0000000 0000000 00000000034 14763776540 031447 xustar000000000 0000000 28 mtime=1741684064.8390000 src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-10000-dev-release.apk.idsig0100644 0000000 0000000 00000015375 14763776540 026326 0ustar000000000 0000000 - .?\Vy5tޗPN= E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@* XRHfwD.[e Z?88TRCɏ}[ w/'ov8ꩄxpCig(< Fɔ[(FÚ ]BfNa] E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t&0"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqXbp;x]6 4QL1Pq9uYr:۟W5|NJrCݒ.~!>/ª 1M4;1B ̆)rYWIȤT$6p%ډ,xs =%<3t1ǫ祋pp=Zig%~X<܆0)K#j7߭ ruzwiGn.Kܽri EEf@:lxWI2!r # \b8Rc~h B)(r0A%BZ͵5qSM4Xr f:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ a ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t H<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqhSM,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*|x[r?qGNrb9iTB$ʥs̝I7%=ª&kk9svCF sH.$g{j3rkӱ( V}@~x] n6~,^5 \]k(5 nx=W ${$yc>QG!~V 4\0E[&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v31-rsa-2048_2-tgt-34-1-tgt-28.apk0100644 0000000 0000000 00000000034 14763776540 027126 xustar000000000 0000000 28 mtime=1741684064.8390000 src/test/resources/com/android/apksig/v31-rsa-2048_2-tgt-34-1-tgt-28.apk0100644 0000000 0000000 00000040627 14763776540 024003 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ a ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t"@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >*IO%r]=pڠ1ExI]b\q:`#&1m 4ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqhSM,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*wĠW Pb^m-jl vڽ9 !l|y$Cl(ԴI=E'02޻hQl Vn! 513G,?%DO淼ԍhԝ sYNHmi6tT7}fu@b^'e m9zQo\ $St ]tvo= ,DLh8)uu,LcC&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v31-tgt-33-no-v3-attr.apk0100644 0000000 0000000 00000000034 14763776540 026255 xustar000000000 0000000 28 mtime=1741684064.8390000 src/test/resources/com/android/apksig/v31-tgt-33-no-v3-attr.apk0100644 0000000 0000000 00000040627 14763776540 023132 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ a ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t!@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqhSA,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*}B,JMU̓=6G9QIyet/*k~KP $0s oJnʊGW&2rQ5|>>P$R UW3ne).&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v31-tgt-33-no-v3-block.apk0100644 0000000 0000000 00000000034 14763776540 026375 xustar000000000 0000000 28 mtime=1741684064.8400000 src/test/resources/com/android/apksig/v31-tgt-33-no-v3-block.apk0100644 0000000 0000000 00000040627 14763776540 023252 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ a ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t!@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAq` werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v31-tgt-34-v3-attr-value-33.apk0100644 0000000 0000000 00000000034 14763776540 027201 xustar000000000 0000000 28 mtime=1741684064.8400000 src/test/resources/com/android/apksig/v31-tgt-34-v3-attr-value-33.apk0100644 0000000 0000000 00000040627 14763776540 024056 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[ a ,( E~m̍M(C-# 1X>&00 {F *O0  *H  010U rsa-20480 180619000500Z 280616000500Z010U rsa-2048_20"0  *H 0 0 Wpi/ \zkEIԌmwtk>ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t"@<o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqP0N0U?V2I6-a%L?\0U#0-s89j!Y,o0 U00  *H  ")ѼVPj=^ `F{-O#*9ԒɆP0]K0B$6EA ]&L^XBbcj_7]ưHrr᷀X0aA2D#Ѵ6i`_ހ)K#JiH‹-gĺkTg f+C7\ojYGMU3;gC#-hQwY gLW,(Wr^3t,Lʂ/0GĊi!lw8B^\fc t_@GmbR}&}kTEg7 ]&R:yQ"雪sx;7;!ej]eNn.AݱH%f_D0ޟר5M!2Yވ"U,LNIvXL%#4G@_0|bS? >*IO%r]=pڠ1ExI]b\q:`#&1m 4ct%\OGw]ZDޤqU|}O('wUb a*;9&1uJÛ|?'M㞆ѯ pq,ֵ$~QYG#v6j}&^2{$󃢕^{W-`F7t=.Ӳ# .EꞩW(|H"1!DtFiL R4sAqhSM,( E~m̍M(C-# 1X>&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@* 8{f!:&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v41-digest-mismatched-with-v31.apk0100644 0000000 0000000 00000000034 14763776540 030300 xustar000000000 0000000 28 mtime=1741684064.8400000 src/test/resources/com/android/apksig/v41-digest-mismatched-with-v31.apk0100644 0000000 0000000 00000040627 14763776540 025155 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:9*META-INF/RSA-2048.SFmKo@= e;tC*).'̨Cy_54mw7'799SFB}Y,!-ђgQP*3\X%:*֛7;vVcy( 2Mbp'-Ap݁.)/HDa sKGEɳi88G?MVD^׹yJ{7I写>$aGK"eu.?n=+Fs5DVv?m1hI1+k\QHYХ2I7=g>OĔɮUz*r^zIHnPK!:N,%META-INF/RSA-2048.RSA3hbiajhδԠѐ߀3̓1qA_&M03121q2eU 'l(hQTkd`ba 'khf`llhhbjnb%kbl`ahbĨl8QM @q.&FF & 5`Y契FodpQ] ʽ8w:˧1pT<@IҐ%pTf5_v@yy{m`:iOawv,^v#w:ns7[Ǫu;_ƽS\4`WI *yxY~pizyxՏO 2Y'2^ʈ^ /d틐7?.+Y;9Fs.=(oW?lR_Y {6YtM}H' FoDox}qq,0dXXDęt-f[ R#o Vf0k@Հ?Z2{mMOė{O_W'&;0_OR"Oލuf<txUKt?aVHo{q鋏ֶop_IC۷̠3fzk׻n$D22e^ǹ/:66zXpvƶt59փI-) kO}**T5%W֖gtc[W-?\Ӱ gdx\ͻEF=\pgt Ы۵?px4E:MZīU}DO[qB* ӺB +^+PK!:DmQMETA-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ7t Z.9?^-$bcy&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*vGj {|)S~]pDuT u;3jrG'M5p%Wx^PDcڈ#8Pϕ~8>ݤu4'Ԇ+Xsdu􀋂$B=h~Zx9̷Xhi\ f&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[aLH@-nE:@9LPwn ][Ը2=wcP`]k00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((= : o;00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=N&Rz;\!xC>$3r݌G8T- x0aTVqM>XY;u[3ФA&;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*],od0L̏#2] 4|W:U?r}m Wc0U9dվ ) IV; j/ҦtWp Sz5aR&YD2b~r:X'3`Yբxmmd6 A\&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[werBAPK Sig Block 42PK !:!Oresources.arscPK!:^avAndroidManifest.xmlPK!:Շ  gclasses.dexPK!:9* META-INF/RSA-2048.SFPK!:N,% META-INF/RSA-2048.RSAPK!:DmQ_META-INF/MANIFEST.MFPK@./PaxHeaders.X/src_test_resources_com_android_apksig_v41-digest-mismatched-with-v31.apk.idsig0100644 0000000 0000000 00000000034 14763776540 031376 xustar000000000 0000000 28 mtime=1741684064.8400000 src/test/resources/com/android/apksig/v41-digest-mismatched-with-v31.apk.idsig0100644 0000000 0000000 00000017373 14763776540 026255 0ustar000000000 0000000 - n})t&00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9i nK!;\ѽe L7+I=t[goR0mw*7؛~"K(L 5-._ѥ,Rc3sW`=-dHd0O}]˜O^%\`BBHmIgdVK6aa[ E~m̍M(C-# 1X>&00 q 0  *H  010U rsa-40960 160331152846Z 430817152846Z010U rsa-40960"0  *H 0 >;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=;o'= YOAcCZ3r<\m6P 甚'o:JX`p7Ahذb0^tFp y^i^Y³803W ~ 8^]((=FcZ437[0 !eS;481UȓMP%4ONL#Fn*=4=-Fvv WZ-ee4vRfD-QaR 63ٽv]ba(MQGmFU~6SO A./PaxHeaders.X/src_test_resources_com_android_apksig_valid-stamp.apk0100644 0000000 0000000 00000000034 14763776540 025136 xustar000000000 0000000 28 mtime=1741684064.8410000 src/test/resources/com/android/apksig/valid-stamp.apk0100644 0000000 0000000 00000040726 14763776540 022013 0ustar000000000 0000000 PK !:!Oresources.arsc (CTiny App for CTS -[Ţîñý Åþþ ƒöŕ ÇŢŠ one two three] @‏‮Tiny‬‏ ‏‮App‬‏ ‏‮for‬‏ ‏‮CTS‬‏ android.appsecurity.cts.tinyapp T4$attrstring, app_nameL`P8L`P8enXAL`P8arXBPK!:AndroidManifest.xmlTnA=8$@c<)k( DHD :c`a |_PQP)sē-x{cf!APŗ`>:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:S% stamp-cert-sha256 ][Aif3m$wGwZkM-UbWY`aMTwlf18sN_[1f+KX|ʪr4x˻T&0F#,f32 hvU_5[TDm?\&:aE7{]zc1Mķ#~Uⶸe¦?\Gz%xf_  w0 0PK!:,{ 2META-INF/MANIFEST.MFmMo0; ߁SEx9# lTi}eL?y~y8jN,n{8WY %E}bs?rdgU "3ǰCw^v n(Wӥ<^$!h!,%t3dKQ0%4LƱϜJ_!K(28Yb<ڔ5Fvݵ 2W 0pvei(/pё-_Zqa~P˅5&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[hSA,( V|8Qّn^QyZbGh] !)5pj/K}"nTA."yvڄ4Sg:z@Lt&0"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[U mM00 50l0  *H  010U rsa-20480 160331145749Z 430817145749Z010U rsa-20480"0  *H 0 5^0#YY2aD%],E sdM,T75P"¥TtImyjDn%X'J5" qrNsߧgBF_3.."/:GMH)x0(hC$nqJ;ߥ_'{7Wzq`haJh\xJb0N vt\%3Xw'ɦ"[z塌8б(!!,[P0N0U-s89j!Y,o0U#0-s89j!Y,o0 U00  *H  dKnBȓ@dyYNFsK'6 , $u*\I&}{y?0}xG)L` 2@*9RzMQe,;}VgT! A ;r>p&WV+МRoOz5wjT}2= B)קf yjvH/JOֹҹy6(Dҵqߞ' C Ӟ؛diqm8w9d@UkP=MD$28^ !_gnr~A(@M"h(g~ST7&ȕU@T Ff:{oψ}cL|%D1';EJ#^ĀF fD=6̨)xy>[d-Ox"b|:gm")rV{cl \Hp5~zhiҿ}ǴV#0s&j#l Z/DFjKC.ψ6c'Lc3k;Z_JH/CBk5ɷw={phkdsn)+kܯB*rxgJm6#{by7U=԰1*| >e"edcNSld?[{AMyaT9"!Ayyrہ?\9M;˚~[-zp|]QОN2 'g2X2ܬ)~ESǖGYSG:u y\dz$L15#-:q?GO\05;8Z4Zv:ˎυ1uvh}qVKFq_z|՛/zs%o_PK^avPK!: classes.dexMhAnf7i4֚PDŪЦ(ZQ54Ih񠢇lIxQE`UPă JO7;& o}y9g>?p V>g'wn/}xwJbq4T9 &@槎&A} ] 1dX!-Gڗ(\A#ߑ=!B oHE22!ud C"\X ցa$rQ+Z;nS纜2VN􇔟(k(mk bQRϫېe7yw1o"4aǨ?#>"Or5,5HKTj^ϋ?ok@C>{-_ahY,ܜEeݪ2۲oq-uY]߻~re(},[k3}]T+:?Ū;kQJrĬ3.|LM+xVU,o.86@{ceDIi)ЧơڶE!q9($9sJ={xq0/ tHy.|,>`TiY ǡKKÃxXM\⢇Gڱ3Y{ Íuw_J {W$ؙ]&0y`C/Bz"z_4MPB{_KK]&0/οyDO򹢟>8iSi#-'BGPKՇ PK!:META-INF/MANIFEST.MFeMK@X,~B?6jY5 -L6323ۯ"( jǔ1jxfڎ@^hzD~ײ$K)2a+k]nvm?!PV|Xx% ޤeՒ74CNA\7~"HL1-NݜQG;%9 jW-|{^r*e^Z)n)k]NA#6nwvkMSd^<|W*-O