1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
|
package apkverifier_test
import (
"bytes"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"github.com/avast/apkverifier"
"io/ioutil"
"math"
"os"
"path/filepath"
"strings"
"testing"
)
func Example() {
res, err := apkverifier.Verify(os.Args[1], nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Verification failed: %s\n", err.Error())
}
fmt.Printf("Verification scheme used: v%d\n", res.SigningSchemeId)
cert, _ := apkverifier.PickBestApkCert(res.SignerCerts)
if cert == nil {
fmt.Printf("No certificate found.\n")
} else {
fmt.Println(cert)
}
}
// From https://android.googlesource.com/platform/tools/apksig 5941d7112b28d5dada825aad757781f0b3ecf23b
var (
DSA_KEY_NAMES = []string{"1024", "2048", "3072"}
DSA_KEY_NAMES_1024_AND_SMALLER = []string{"1024"}
DSA_KEY_NAMES_2048_AND_LARGER = []string{"2048", "3072"}
EC_KEY_NAMES = []string{"p256", "p384", "p521"}
RSA_KEY_NAMES = []string{"1024", "2048", "3072", "4096", "8192", "16384"}
RSA_KEY_NAMES_2048_AND_LARGER = []string{"2048", "3072", "4096", "8192", "16384"}
)
const anyErrorString = "LiterallyAnything"
func TestOriginalAccepted(t *testing.T) {
assertVerified(t, "original.apk")
}
func TestV1OneSignerMD5withRSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-%s.apk", RSA_KEY_NAMES)
}
func TestV1OneSignerSHA1withRSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-%s.apk", RSA_KEY_NAMES)
}
func TestV1OneSignerSHA224withRSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.14-%s.apk", RSA_KEY_NAMES)
}
func TestV1OneSignerSHA256withRSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.11-%s.apk", RSA_KEY_NAMES)
}
func TestV1OneSignerSHA384withRSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-%s.apk", RSA_KEY_NAMES)
}
func TestV1OneSignerSHA512withRSAVerifies(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.13-%s.apk", RSA_KEY_NAMES)
}
func TestV1OneSignerSHA1withECDSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha1-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-%s.apk", EC_KEY_NAMES)
}
func TestV1OneSignerSHA224withECDSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha224-1.2.840.10045.4.3.1-%s.apk", EC_KEY_NAMES)
}
func TestV1OneSignerSHA256withECDSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha256-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-%s.apk", EC_KEY_NAMES)
}
func TestV1OneSignerSHA384withECDSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-%s.apk", EC_KEY_NAMES)
}
func TestV1OneSignerSHA512withECDSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-%s.apk", EC_KEY_NAMES)
}
func TestV1OneSignerSHA1withDSAAccepted(t *testing.T) {
// 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(t, "v1-only-with-dsa-sha1-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER)
assertVerifiedForEach(t, "v1-only-with-dsa-sha1-1.2.840.10040.4.3-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER)
}
func TestV1OneSignerSHA1withDSAAcceptedWithKeysTooLongForDigest(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-dsa-sha1-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER)
assertVerifiedForEach(t, "v1-only-with-dsa-sha1-1.2.840.10040.4.3-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER)
}
func TestV1OneSignerSHA224withDSAAccepted(t *testing.T) {
// 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(t, "v1-only-with-dsa-sha224-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER)
assertVerifiedForEach(t, "v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-%s.apk",
DSA_KEY_NAMES_1024_AND_SMALLER)
}
func TestV1OneSignerSHA224withDSAAcceptedWithKeysTooLongForDigest(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-dsa-sha224-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER)
assertVerifiedForEach(t, "v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER)
}
func TestV1OneSignerSHA256withDSAAccepted(t *testing.T) {
// APK signed with v1 scheme only, one signer
assertVerifiedForEach(t, "v1-only-with-dsa-sha256-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES)
assertVerifiedForEach(t, "v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.2-%s.apk", DSA_KEY_NAMES)
}
func TestV2StrippedRejected(t *testing.T) {
// 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(t, "v2-stripped.apk", "cannot be verified using v1 scheme, downgrade attack?")
// 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(t, "v2-stripped-with-ignorable-signing-schemes.apk", "cannot be verified using v1 scheme, downgrade attack?")
}
func TestV3StrippedRejected(t *testing.T) {
// 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(t, "v3-stripped.apk", "was stripped, downgrade attack")
}
func TestSignaturesIgnoredForMaxSDK(t *testing.T) {
// 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.
assertVerifiedSdk(t, "v1v2v3-with-rsa-2048-lineage-3-signers.apk", -1, 26)
assertVerifiedSdk(t, "v1v2v3-with-rsa-2048-lineage-3-signers.apk", -1, 23)
}
func TestV2OneSignerOneSignatureAccepted(t *testing.T) {
// APK signed with v2 scheme only, one signer, one signature
assertVerifiedForEachSdk(t, "v2-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES, 25, math.MaxInt32)
assertVerifiedForEachSdk(t, "v2-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES, 25, math.MaxInt32)
assertVerifiedForEachSdk(t, "v2-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES, 25, math.MaxInt32)
// 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)
assertVerifiedForEachSdk(t, "v2-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES, 25, math.MaxInt32)
assertVerifiedForEachSdk(t, "v2-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES, 25, math.MaxInt32)
}
func TestV3OneSignerOneSignatureAccepted(t *testing.T) {
// APK signed with v3 scheme only, one signer, one signature
assertVerifiedForEachSdk(t, "v3-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES, 28, math.MaxInt32)
assertVerifiedForEachSdk(t, "v3-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES, 28, math.MaxInt32)
assertVerifiedForEachSdk(t, "v3-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES, 28, math.MaxInt32)
assertVerifiedForEachSdk(t, "v3-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES, 28, math.MaxInt32)
assertVerifiedForEachSdk(t, "v3-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES, 28, math.MaxInt32)
}
func TestV2OneSignerOneRsaPssSignatureAccepted(t *testing.T) {
// APK signed with v2 scheme only, one signer, one signature
assertVerifiedForEachSdk(t, "v2-only-with-rsa-pss-sha256-%s.apk", RSA_KEY_NAMES, 25, math.MaxInt32)
assertVerifiedForEachSdk(t, "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
25, math.MaxInt32)
}
func TestV2SignatureDoesNotMatchSignedDataRejected(t *testing.T) {
// 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(t, "v2-only-with-dsa-sha256-1024-sig-does-not-verify.apk", "verification failed")
// 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.
assertVerificationFailure(t, "v2-only-with-rsa-pkcs1-sha256-2048-sig-does-not-verify.apk", "verification error")
// Bitflip in the ECDSA signature. Based on v2-only-with-ecdsa-sha256-p256.apk.
assertVerificationFailure(t, "v2-only-with-ecdsa-sha256-p256-sig-does-not-verify.apk", "verification failed")
}
func TestV3SignatureDoesNotMatchSignedDataRejected(t *testing.T) {
// 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(t, "v3-only-with-dsa-sha256-2048-sig-does-not-verify.apk", "failed to verify signature")
// Bitflip in signed data. Based on v3-only-with-rsa-pkcs1-sha256-3072.apk
assertVerificationFailure(t, "v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk", "failed to verify signature")
// Based on v3-only-with-ecdsa-sha512-p521 with the signature ID changed to be ECDSA with
// SHA-256.
assertVerificationFailure(t, "v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk", "failed to verify signature")
}
func TestV2RsaPssSignatureDoesNotMatchSignedDataRejected(t *testing.T) {
// 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(t, "v2-only-with-rsa-pss-sha256-2048-sig-does-not-verify.apk", "verification error")
}
func TestV2ContentDigestMismatchRejected(t *testing.T) {
// APK signed with v2 scheme only, but the digest of contents does not match the digest
// stored in signed-data
// 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(t, "v2-only-with-rsa-pkcs1-sha512-4096-digest-mismatch.apk", "digest of contents did not verify")
// 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(t, "v2-only-with-ecdsa-sha256-p256-digest-mismatch.apk", "digest of contents did not verify")
}
func TestV3ContentDigestMismatchRejected(t *testing.T) {
// 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(t, "v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", "digest of contents 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(t, "v3-only-with-dsa-sha256-3072-digest-mismatch.apk", "digest of contents did not verify")
}
func TestNoApkSignatureSchemeBlockRejected(t *testing.T) {
// 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(t, "v2-only-wrong-apk-sig-block-magic.apk", "No valid MANIFEST.SF")
// 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(t, "v2-only-garbage-between-cd-and-eocd.apk", "No valid MANIFEST.SF")
// Obtained by modifying the size in APK Signature Block header. Based on
// v2-only-with-ecdsa-sha512-p521.apk.
assertVerificationFailure(t, "v2-only-apk-sig-block-size-mismatch.apk", "No valid MANIFEST.SF")
// 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(t, "v1-with-apk-sig-block-but-without-apk-sig-scheme-v2-block.apk")
}
func TestNoV3ApkSignatureSchemeBlockRejected(t *testing.T) {
// 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(t, "v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", "No valid MANIFEST.SF")
// Obtained from v3-only-with-rsa-pkcs1-sha512-4096.apk by modifying the size in the APK
// Signature Block header and footer.
assertVerificationFailure(t, "v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk", "No valid MANIFEST.SF")
}
func TestTruncatedZipCentralDirectoryRejected(t *testing.T) {
// 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
assertVerificationFailure(t, "v2-only-truncated-cd.apk", "No valid MANIFEST.SF")
}
func TestV2UnknownPairIgnoredInApkSigningBlock(t *testing.T) {
// 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.
assertVerifiedSdk(t, "v2-only-unknown-pair-in-apk-sig-block.apk", 25, math.MaxInt32)
}
func TestV3UnknownPairIgnoredInApkSigningBlock(t *testing.T) {
// 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.
assertVerifiedSdk(t, "v3-only-unknown-pair-in-apk-sig-block.apk", 28, math.MaxInt32)
}
func TestV2UnknownSignatureAlgorithmsIgnored(t *testing.T) {
// 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.
assertVerifiedSdk(t, "v2-only-with-ignorable-unsupported-sig-algs.apk", 25, math.MaxInt32)
}
func TestV3UnknownSignatureAlgorithmsIgnored(t *testing.T) {
// 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.
assertVerifiedSdk(t, "v3-only-with-ignorable-unsupported-sig-algs.apk", 28, math.MaxInt32)
}
func TestV3WithOnlyUnknownSignatureAlgorithmsRejected(t *testing.T) {
// APK is only signed with an unknown signature algorithm. Obtained by modifying APK
// signer's ID for a known signature algorithm.
assertVerificationFailure(t, "v3-only-no-supported-sig-algs.apk", "no supported signatures")
}
func TestV2UnknownAdditionalAttributeIgnored(t *testing.T) {
// 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.
assertVerifiedSdk(t, "v2-only-unknown-additional-attr.apk", 25, math.MaxInt32)
}
func TestV3UnknownAdditionalAttributeIgnored(t *testing.T) {
// 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
assertVerifiedSdk(t, "v3-only-unknown-additional-attr.apk", 28, math.MaxInt32)
// APK's v2 and v3 signatures contain unknown additional attributes before and after the
// anti-stripping and lineage attributes.
assertVerifiedSdk(t, "v2v3-unknown-additional-attr.apk", 28, math.MaxInt32)
}
func TestV2MismatchBetweenSignaturesAndDigestsBlockRejected(t *testing.T) {
// 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(t, "v2-only-signatures-and-digests-block-mismatch.apk", "signature algorithms don't match")
}
func TestV3MismatchBetweenSignaturesAndDigestsBlockRejected(t *testing.T) {
// 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(t, "v3-only-signatures-and-digests-block-mismatch.apk", "signature algorithms don't match")
}
func TestV2MismatchBetweenPublicKeyAndCertificateRejected(t *testing.T) {
// 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(t, "v2-only-cert-and-public-key-mismatch.apk", "Public key mismatch between certificate and signature")
}
func TestV3MismatchBetweenPublicKeyAndCertificateRejected(t *testing.T) {
// 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(t, "v3-only-cert-and-public-key-mismatch.apk", "Public key mismatch")
}
func TestV2SignerBlockWithNoCertificatesRejected(t *testing.T) {
// 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(t, "v2-only-no-certs-in-sig.apk", "No certificates listed")
}
func TestV3SignerBlockWithNoCertificatesRejected(t *testing.T) {
// 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(t, "v3-only-no-certs-in-sig.apk", "No certificates listed")
}
func TestTwoSignersAccepted(t *testing.T) {
// APK signed by two different signers
assertVerified(t, "two-signers.apk")
assertVerified(t, "v1-only-two-signers.apk")
assertVerifiedSdk(t, "v2-only-two-signers.apk", 25, math.MaxInt32)
}
func TestV2TwoSignersRejectedWhenOneBroken(t *testing.T) {
// 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(t, "two-signers-second-signer-v2-broken.apk", "verification failed")
}
func TestV2TwoSignersRejectedWhenOneWithoutSignatures(t *testing.T) {
// APK v2-signed by two different signers. However, there are no signatures for the second
// signer.
assertVerificationFailure(t, "v2-only-two-signers-second-signer-no-sig.apk", "no signatures found")
}
func TestV2TwoSignersRejectedWhenOneWithoutSupportedSignatures(t *testing.T) {
// APK v2-signed by two different signers. However, there are no supported signatures for
// the second signer.
assertVerificationFailure(t, "v2-only-two-signers-second-signer-no-supported-sig.apk", "no supported signatures found")
}
func TestCorrectCertUsedFromPkcs7SignedDataCertsSet(t *testing.T) {
// 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.
res := assertVerified(t, "v1-only-pkcs7-cert-bag-first-cert-not-used.apk")
if len(res.SignerCerts) != 1 || len(res.SignerCerts[0]) != 1 {
t.Fatalf("Wrong cert count")
}
hash := sha256.Sum256(res.SignerCerts[0][0].Raw)
if hex.EncodeToString(hash[:]) != "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8" {
t.Fatalf("Invalid cert hash")
}
}
func TestV1SchemeSignatureCertNotReencoded(t *testing.T) {
// 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.
res := assertVerified(t, "v1-only-with-rsa-1024-cert-not-der.apk")
if len(res.SignerCerts) != 1 || len(res.SignerCerts[0]) != 1 {
t.Fatalf("Wrong cert count")
}
// apkverifier is not expected to pass this, we are not extracting the certs, so whatever
t.Skip("test not relevant for apkverifier")
hash := sha256.Sum256(res.SignerCerts[0][0].Raw)
if hex.EncodeToString(hash[:]) != "c5d4535a7e1c8111687a8374b2198da6f5ff8d811a7a25aa99ef060669342fa9" {
t.Fatalf("Invalid cert hash")
}
}
func TestV1SchemeSignatureCertNotReencoded2(t *testing.T) {
// 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.
res := assertVerified(t, "v1-only-with-rsa-1024-cert-not-der2.apk")
if len(res.SignerCerts) != 1 || len(res.SignerCerts[0]) != 1 {
t.Fatalf("Wrong cert count")
}
// apkverifier is not expected to pass this, we are not extracting the certs, so whatever
t.Skip("test not relevant for apkverifier")
hash := sha256.Sum256(res.SignerCerts[0][0].Raw)
if hex.EncodeToString(hash[:]) != "da3da398de674541313deed77218ce94798531ea5131bb9b1bb4063ba4548cfb" {
t.Fatalf("Invalid cert hash")
}
}
func TestMaxSizedZipEocdCommentAccepted(t *testing.T) {
// 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(t, "v1-only-max-sized-eocd-comment.apk")
assertVerifiedSdk(t, "v2-only-max-sized-eocd-comment.apk", 25, math.MaxInt32)
}
func TestEmptyApk(t *testing.T) {
// Unsigned empty ZIP archive
assertVerificationFailureSdk(t, "empty-unsigned.apk", 1, math.MaxInt32, "No valid MANIFEST.SF")
// JAR-signed empty ZIP archive
assertVerificationFailureSdk(t, "v1-only-empty.apk", 18, math.MaxInt32, "No manifest entry")
// APK Signature Scheme v2 signed empty ZIP archive
assertVerificationFailureSdk(t, "v2-only-empty.apk", 25, math.MaxInt32, "No valid MANIFEST.SF")
// APK Signature Scheme v3 signed empty ZIP archive
assertVerificationFailureSdk(t, "v3-only-empty.apk", 28, math.MaxInt32, "No valid MANIFEST.SF")
}
func TestTargetSandboxVersion2AndHigher(t *testing.T) {
// 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(t, "targetSandboxVersion-2.apk")
// v1 signature is needed only if minSdkVersion is lower than 24
assertVerificationFailure(t, "v2-only-targetSandboxVersion-2.apk", "No valid MANIFEST.SF")
assertVerifiedSdk(t, "v2-only-targetSandboxVersion-2.apk", 24, math.MaxInt32)
// v2 signature is required
assertVerificationFailure(t, "v1-only-targetSandboxVersion-2.apk", "no valid signature for sandbox version")
assertVerificationFailure(t, "unsigned-targetSandboxVersion-2.apk", "no valid signature for sandbox version")
// minSdkVersion 28, meaning v1 signature not needed
assertVerified(t, "v2-only-targetSandboxVersion-3.apk")
}
func TestV1MultipleDigestAlgsInManifestAndSignatureFile(t *testing.T) {
// 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 ... <file> ...
// jarsigner -sigalg SHA1withRSA -digestalg SHA1 ... <same file> ...
assertVerified(t, "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(t, "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(t, "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", "No matching hash for")
assertVerificationFailureSdk(t, "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", -1, 17, "No matching hash for")
assertVerifiedSdk(t, "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", 18, math.MaxInt32)
// 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(t, "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", "Invalid hash of manifest entry")
assertVerificationFailureSdk(t, "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", -1, 17, "Invalid hash of manifest entry")
assertVerifiedSdk(t, "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", 18, math.MaxInt32)
// 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(t, "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", "No matching hash for")
assertVerificationFailureSdk(t, "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", 18, math.MaxInt32, "No matching hash for")
assertVerifiedSdk(t, "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", -1, 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(t, "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", "Invalid hash of manifest entry")
assertVerificationFailureSdk(t, "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", 18, math.MaxInt32, "Invalid hash of manifest entry")
assertVerifiedSdk(t, "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", -1, 17)
}
func TestV1WithUnsupportedCharacterInZipEntryName(t *testing.T) {
// Android Package Manager does not support ZIP entry names containing CR or LF
assertVerificationFailure(t, "v1-only-with-cr-in-entry-name.apk", anyErrorString)
assertVerificationFailure(t, "v1-only-with-lf-in-entry-name.apk", anyErrorString)
}
func TestWeirdZipCompressionMethod(t *testing.T) {
// 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(t, "weird-compression-method.apk")
}
func TestZipCompressionMethodMismatchBetweenLfhAndCd(t *testing.T) {
// 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(t, "mismatched-compression-method.apk")
}
func TestV1SignedAttrs(t *testing.T) {
apk := "v1-only-with-signed-attrs.apk"
assertVerificationFailureSdk(t, apk, 18, math.MaxInt32, "APKs with Signed Attributes broken on platforms")
assertVerifiedSdk(t, apk, 19, math.MaxInt32)
apk = "v1-only-with-signed-attrs-signerInfo1-good-signerInfo2-good.apk"
assertVerificationFailureSdk(t, apk, 18, math.MaxInt32, "APKs with Signed Attributes broken on platforms")
assertVerifiedSdk(t, apk, 19, math.MaxInt32)
}
func TestV1SignedAttrsNotInDerOrder(t *testing.T) {
// 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(t, "v1-only-with-signed-attrs-wrong-order.apk")
assertVerified(t, "v1-only-with-signed-attrs-signerInfo1-wrong-order-signerInfo2-good.apk")
}
func TestV1SignedAttrsMissingContentType(t *testing.T) {
// SignedAttributes must contain ContentType. Pre-N, Android ignores this requirement.
// Android N onwards rejects such APKs.
apk := "v1-only-with-signed-attrs-missing-content-type.apk"
assertVerifiedSdk(t, apk, -1, 23)
assertVerificationFailure(t, apk, "failed to parse signed content type")
// 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"
assertVerifiedSdk(t, apk, -1, 23)
assertVerificationFailure(t, apk, "failed to parse signed content type")
}
func TestV1SignedAttrsWrongContentType(t *testing.T) {
// ContentType of SignedAttributes must equal SignedData.encapContentInfo.eContentType.
// Pre-N, Android ignores this requirement.
// From N onwards, Android rejects such SignerInfos.
apk := "v1-only-with-signed-attrs-wrong-content-type.apk"
assertVerifiedSdk(t, apk, -1, 23)
assertVerificationFailure(t, apk, "PKCS7 content type does not match")
// 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"
assertVerifiedSdk(t, apk, -1, 23)
assertVerifiedSdk(t, apk, 24, math.MaxInt32)
// 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(t, apk, "PKCS7 content type does not match")
}
func TestV1SignedAttrsMissingDigest(t *testing.T) {
// Content digest must be present in SignedAttributes
apk := "v1-only-with-signed-attrs-missing-digest.apk"
assertVerificationFailureSdk(t, apk, -1, 23, "failed to parse signed message digest")
assertVerificationFailureSdk(t, apk, 24, math.MaxInt32, "failed to parse signed message digest")
// 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"
assertVerificationFailureSdk(t, apk, -1, 23, "failed to parse signed message digest")
assertVerificationFailureSdk(t, apk, 24, math.MaxInt32, "failed to parse signed message digest")
}
func TestV1SignedAttrsMultipleGoodDigests(t *testing.T) {
// Only one content digest must be present in SignedAttributes
apk := "v1-only-with-signed-attrs-multiple-good-digests.apk"
assertVerificationFailureSdk(t, apk, -1, 23, "failed to parse signed message digest")
assertVerificationFailureSdk(t, apk, 24, math.MaxInt32, "failed to parse signed message digest")
// 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"
assertVerificationFailureSdk(t, apk, -1, 23, "failed to parse signed message digest")
assertVerificationFailureSdk(t, apk, 24, math.MaxInt32, "failed to parse signed message digest")
}
func TestV1SignedAttrsWrongDigest(t *testing.T) {
// Content digest in SignedAttributes does not match the contents
apk := "v1-only-with-signed-attrs-wrong-digest.apk"
assertVerificationFailureSdk(t, apk, -1, 23, "signedAttributes hash mismatch")
assertVerificationFailureSdk(t, apk, 24, math.MaxInt32, "signedAttributes hash mismatch")
// 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"
assertVerificationFailureSdk(t, apk, -1, 23, "signedAttributes hash mismatch")
assertVerifiedSdk(t, apk, 24, math.MaxInt32)
}
func TestV1SignedAttrsWrongSignature(t *testing.T) {
// Signature over SignedAttributes does not verify
apk := "v1-only-with-signed-attrs-wrong-signature.apk"
assertVerificationFailureSdk(t, apk, -1, 23, "verification error")
assertVerificationFailureSdk(t, apk, 24, math.MaxInt32, "verification error")
// 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"
assertVerificationFailureSdk(t, apk, -1, 23, "verification error")
assertVerifiedSdk(t, apk, 24, math.MaxInt32)
}
// Lineage tests
func TestLineageFromAPKContainsExpectedSigners(t *testing.T) {
res := assertVerifiedSdk(t, "v1v2v3-with-rsa-2048-lineage-3-signers.apk", 24, math.MaxInt32)
if res.SigningBlockResult == nil {
t.Fatalf("no signing block found")
} else if res.SigningBlockResult.SigningLineage == nil {
t.Fatalf("no signing lineage found")
}
lin := res.SigningBlockResult.SigningLineage
certNames := []string{"rsa-2048.x509.pem", "rsa-2048_2.x509.pem", "rsa-2048_3.x509.pem"}
if len(certNames) != len(lin.Nodes) {
t.Fatalf("invalid number of certs in lineage, expected %d got %d", len(certNames), len(lin.Nodes))
}
rsc := filepath.Join(os.Getenv("APKSIG_PATH"), "src/test/resources/com/android/apksig")
for _, cn := range certNames {
data, err := ioutil.ReadFile(filepath.Join(rsc, cn))
if err != nil {
t.Fatalf("failed to read cert file %s: %s", cn, err.Error())
}
block, _ := pem.Decode(data)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("failed to parse cert %s: %s", cn, err.Error())
}
found := false
for _, n := range lin.Nodes {
if n.SigningCert.Equal(cert) {
found = true
break
}
}
if !found {
ci := apkverifier.NewCertInfo(cert)
t.Fatalf("certificate %s was not found in lineage", ci.String())
}
}
}
func TestLineageFromAPKWithInvalidZipCDSizeFails(t *testing.T) {
// 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.
assertVerificationFailureSdk(t, "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk",
24, math.MaxInt32, anyErrorString)
}
func TestLineageFromAPKWithNoLineageFails(t *testing.T) {
// 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.
assertNoLineage(t, "golden-aligned-v1v2-out.apk", true)
// This is a valid APK signed with the V1, V2, and V3 signature schemes, but there is no
// lineage in the V3 signature block.
assertNoLineage(t, "golden-aligned-v1v2v3-out.apk", true)
// 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.
assertNoLineage(t, "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-lineage-attr.apk", false)
}
func assertVerifiedForEach(t *testing.T, format string, names []string) {
assertVerifiedForEachSdk(t, format, names, -1, math.MaxInt32)
}
func assertVerifiedForEachSdk(t *testing.T, format string, names []string, minSdkVersion, maxSdkVersion int32) {
for _, n := range names {
assertVerifiedSdk(t, fmt.Sprintf(format, n), minSdkVersion, maxSdkVersion)
}
}
func assertVerified(t *testing.T, name string) apkverifier.Result {
return assertVerifiedSdk(t, name, -1, math.MaxInt32)
}
func assertVerifiedSdk(t *testing.T, name string, minSdkVersion, maxSdkVersion int32) apkverifier.Result {
res, err := verify(t, name, minSdkVersion, maxSdkVersion)
if err == nil {
return res
}
t.Fatalf("%s did not verify: %s\n%s", name, err.Error(), formatResult(t, res))
return apkverifier.Result{}
}
func assertVerificationFailure(t *testing.T, name string, expectedError string) {
assertVerificationFailureSdk(t, name, -1, math.MaxInt32, expectedError)
}
func assertVerificationFailureSdk(t *testing.T, name string, minSdkVersion, maxSdkVersion int32, expectedError string) {
res, err := verify(t, name, minSdkVersion, maxSdkVersion)
if err == nil || expectedError == "" {
goto fail
}
if expectedError == anyErrorString {
return
}
if strings.Contains(err.Error(), expectedError) {
return
}
if res.SigningBlockResult != nil {
for _, err := range res.SigningBlockResult.Errors {
if strings.Contains(err.Error(), expectedError) {
return
}
}
}
fail:
t.Fatalf("%s was supposed to fail verification with '%s', but returned error %v instead\n%s",
name, expectedError, err, formatResult(t, res))
}
func assertNoLineage(t *testing.T, name string, mustVerify bool) {
res, err := verify(t, name, -1, math.MaxInt32)
if mustVerify != (err == nil) {
t.Fatalf("%s has wrong verification result %v, expected %v", name, err, mustVerify)
} else if res.SigningBlockResult == nil {
t.Fatalf("missing signing block in %s", name)
} else if res.SigningBlockResult.SigningLineage != nil {
t.Fatalf("extra signing lineage in %s", name)
}
}
func verify(t *testing.T, name string, minSdkVersion, maxSdkVersion int32) (apkverifier.Result, error) {
apksigPath, prs := os.LookupEnv("APKSIG_PATH")
if !prs || apksigPath == "" {
t.Skip("This test requires APKSIG_PATH set.")
}
path := filepath.Join(apksigPath, "src/test/resources/com/android/apksig", name)
return apkverifier.VerifyWithSdkVersion(path, nil, minSdkVersion, maxSdkVersion)
}
func formatResult(t *testing.T, res apkverifier.Result) string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "Detected scheme: v%d\n\n", res.SigningSchemeId)
if blk := res.SigningBlockResult; blk != nil {
if len(blk.Errors) > 1 {
fmt.Fprintln(&buf, "Additional errors:")
for i := 0; i < len(blk.Errors)-1; i++ {
fmt.Fprintf(&buf, "%s\n", blk.Errors[i].Error())
}
fmt.Fprintln(&buf)
}
if len(blk.Warnings) != 0 {
fmt.Fprintln(&buf, "Warnings:")
for _, w := range blk.Warnings {
fmt.Fprintf(&buf, "%s\n", w)
}
fmt.Fprintln(&buf)
}
}
_, picked := apkverifier.PickBestApkCert(res.SignerCerts)
var cinfo apkverifier.CertInfo
for i, ca := range res.SignerCerts {
for x, cert := range ca {
cinfo.Fill(cert)
if picked == cert {
fmt.Fprintf(&buf, "Chain %d, cert %d [PICKED AS BEST]:\n", i, x)
} else {
fmt.Fprintf(&buf, "Chain %d, cert %d:\n", i, x)
}
fmt.Fprintln(&buf, "algo:", cert.SignatureAlgorithm)
fmt.Fprintln(&buf, "validfrom:", cinfo.ValidFrom)
fmt.Fprintln(&buf, "validto:", cinfo.ValidTo)
fmt.Fprintln(&buf, "serialnumber:", cert.SerialNumber.Text(16))
fmt.Fprintln(&buf, "thumbprint-md5:", cinfo.Md5)
fmt.Fprintln(&buf, "thumbprint-sha1:", cinfo.Sha1)
fmt.Fprintln(&buf, "thumbprint-sha256:", cinfo.Sha256)
fmt.Fprintf(&buf, "Subject:\n %s\n", cinfo.Subject)
fmt.Fprintf(&buf, "Issuer:\n %s\n", cinfo.Issuer)
fmt.Fprintln(&buf)
}
}
return buf.String()
}
|