refactor: apk-parser crate
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
||||
/target
|
||||
**/target
|
||||
.idea/
|
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -124,6 +124,17 @@ dependencies = [
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "apk-parser"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"apk",
|
||||
"byteorder",
|
||||
"hex",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arraydeque"
|
||||
version = "0.5.1"
|
||||
@ -978,6 +989,12 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hex-conservative"
|
||||
version = "0.1.2"
|
||||
@ -1463,9 +1480,8 @@ name = "nap"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"apk",
|
||||
"apk-parser",
|
||||
"async-trait",
|
||||
"byteorder",
|
||||
"clap",
|
||||
"config",
|
||||
"dialoguer",
|
||||
|
@ -20,10 +20,9 @@ reqwest = { version = "0.12.12", features = ["json", "stream"] }
|
||||
tokio = { version = "1.43.0", features = ["fs", "rt", "macros", "rt-multi-thread"] }
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
async-trait = "0.1.86"
|
||||
apk = "0.4.0"
|
||||
semver = "1.0.25"
|
||||
indicatif = "0.17.11"
|
||||
dialoguer = "0.11.0"
|
||||
env_logger = "0.11.6"
|
||||
sha2 = "0.10.8"
|
||||
byteorder = "1.5.0"
|
||||
apk-parser = { path = "./apk-parser" }
|
||||
|
878
apk-parser/Cargo.lock
generated
Normal file
878
apk-parser/Cargo.lock
generated
Normal file
@ -0,0 +1,878 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
|
||||
[[package]]
|
||||
name = "apk"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cb37b529daddddf129612580831db21a538d1aa2798b367039e9316762c81a7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"quick-xml",
|
||||
"rasn",
|
||||
"rasn-pkix",
|
||||
"roxmltree",
|
||||
"rsa",
|
||||
"serde",
|
||||
"sha2",
|
||||
"tracing",
|
||||
"xcommon",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "apk-parser"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"apk",
|
||||
"byteorder",
|
||||
"hex",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"num-traits",
|
||||
"png",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "konst"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330f0e13e6483b8c34885f7e6c9f19b1a7bd449c673fbb948a51c99d66ef74f4"
|
||||
dependencies = [
|
||||
"konst_macro_rules",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "konst_macro_rules"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"libm",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs1"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719"
|
||||
dependencies = [
|
||||
"der",
|
||||
"pkcs8",
|
||||
"spki",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rasn"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "417c93c4470c2637fd27307f046653c70a957f374caae89cb5c9fb35a5fdf147"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"konst",
|
||||
"nom",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"rasn-derive",
|
||||
"snafu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rasn-derive"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e1c39006ee96ffe7c83f656a5d20269366098a3feb35ad4a7c022e3784643cb"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rasn-pkix"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88a7f32e03743ae0f691408246c7e1ce9065a345b6589e05df3b6c57138d4655"
|
||||
dependencies = [
|
||||
"rasn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb6d47b59770b0ae88c7f270c68502832ec14d8c7ab5f7a584f204bb76dbfd8e"
|
||||
dependencies = [
|
||||
"xmlparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "094052d5470cbcef561cb848a7209968c9f12dfa6d668f4bca048ac5de51099c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"signature",
|
||||
"smallvec",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "snafu"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6"
|
||||
dependencies = [
|
||||
"doc-comment",
|
||||
"snafu-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu-derive"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xcommon"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cd8830f4fbeb328f04d29d48af5f2b968395542d6046c9bf55f5b59ebbc342f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"dunce",
|
||||
"image",
|
||||
"pem",
|
||||
"rasn",
|
||||
"rasn-pkix",
|
||||
"rsa",
|
||||
"sha2",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xmlparser"
|
||||
version = "0.13.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
"flate2",
|
||||
]
|
11
apk-parser/Cargo.toml
Normal file
11
apk-parser/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "apk-parser"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.95"
|
||||
log = "0.4.25"
|
||||
hex = "0.4.3"
|
||||
byteorder = "1.5.0"
|
||||
apk = "0.4.0"
|
6
apk-parser/src/lib.rs
Normal file
6
apk-parser/src/lib.rs
Normal file
@ -0,0 +1,6 @@
|
||||
mod manifest;
|
||||
mod signing_block;
|
||||
|
||||
pub use apk::*;
|
||||
pub use manifest::*;
|
||||
pub use signing_block::*;
|
99
apk-parser/src/manifest.rs
Normal file
99
apk-parser/src/manifest.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use anyhow::{bail, Result};
|
||||
use apk::res::Chunk;
|
||||
use apk::AndroidManifest;
|
||||
use log::debug;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
|
||||
/// Parse android manifest from AndroidManifest.xml file data
|
||||
pub fn parse_android_manifest(data: &[u8]) -> Result<AndroidManifest> {
|
||||
let chunks = if let Chunk::Xml(chunks) = Chunk::parse(&mut Cursor::new(data))? {
|
||||
chunks
|
||||
} else {
|
||||
bail!("Invalid AndroidManifest file");
|
||||
};
|
||||
|
||||
let strings = if let Chunk::StringPool(strings, _) = &chunks[0] {
|
||||
HashMap::from_iter(
|
||||
strings
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| (s.to_string(), i as i32)),
|
||||
)
|
||||
} else {
|
||||
bail!("invalid manifest 1");
|
||||
};
|
||||
|
||||
let mut res = AndroidManifest::default();
|
||||
res.package = find_value_in(&strings, &chunks, "manifest", "package");
|
||||
res.version_code =
|
||||
find_value_in(&strings, &chunks, "manifest", "versionCode").and_then(|v| v.parse().ok());
|
||||
res.version_name = find_value_in(&strings, &chunks, "manifest", "versionName");
|
||||
res.compile_sdk_version = find_value_in(&strings, &chunks, "manifest", "compileSdkVersion")
|
||||
.and_then(|v| v.parse().ok());
|
||||
res.compile_sdk_version_codename =
|
||||
find_value_in(&strings, &chunks, "manifest", "compileSdkVersionCodename")
|
||||
.and_then(|v| v.parse().ok());
|
||||
res.platform_build_version_code =
|
||||
find_value_in(&strings, &chunks, "manifest", "platformBuildVersionCode")
|
||||
.and_then(|v| v.parse().ok());
|
||||
res.platform_build_version_name =
|
||||
find_value_in(&strings, &chunks, "manifest", "platformBuildVersionName")
|
||||
.and_then(|v| v.parse().ok());
|
||||
|
||||
res.sdk.min_sdk_version =
|
||||
find_value_in(&strings, &chunks, "uses-sdk", "minSdkVersion").and_then(|v| v.parse().ok());
|
||||
res.sdk.target_sdk_version = find_value_in(&strings, &chunks, "uses-sdk", "targetSdkVersion")
|
||||
.and_then(|v| v.parse().ok());
|
||||
res.sdk.max_sdk_version =
|
||||
find_value_in(&strings, &chunks, "uses-sdk", "maxSdkVersion").and_then(|v| v.parse().ok());
|
||||
|
||||
res.application.theme = find_value_in(&strings, &chunks, "application", "theme");
|
||||
res.application.label = find_value_in(&strings, &chunks, "application", "label");
|
||||
res.application.icon = find_value_in(&strings, &chunks, "application", "icon");
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn find_value_in(
|
||||
strings: &HashMap<String, i32>,
|
||||
chunks: &Vec<Chunk>,
|
||||
node: &str,
|
||||
attr: &str,
|
||||
) -> Option<String> {
|
||||
let idx_node = if let Some(i) = strings.get(node) {
|
||||
*i
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let idx_attr = if let Some(i) = strings.get(attr) {
|
||||
*i
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
chunks.iter().find_map(|chunk| {
|
||||
if let Chunk::XmlStartElement(_, el, attrs) = chunk {
|
||||
match el.name {
|
||||
x if x == idx_node => attrs.iter().find(|e| e.name == idx_attr).and_then(|e| {
|
||||
debug!("{}, {}, {:?}", node, attr, e);
|
||||
match e.typed_value.data_type {
|
||||
3 => strings
|
||||
.iter()
|
||||
.find(|(_, v)| **v == e.raw_value)
|
||||
.map(|(k, _)| k.clone()),
|
||||
16 => Some(e.typed_value.data.to_string()),
|
||||
_ => {
|
||||
debug!("unknown data type {},{},{:?}", node, attr, e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
358
apk-parser/src/signing_block.rs
Normal file
358
apk-parser/src/signing_block.rs
Normal file
@ -0,0 +1,358 @@
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use log::{debug, warn};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::path::Path;
|
||||
|
||||
/// APK Signing block storage type
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ApkSigningBlock {
|
||||
pub data: Vec<(u32, Vec<u8>)>,
|
||||
}
|
||||
|
||||
impl ApkSigningBlock {
|
||||
/// Load the signing block from and APK file
|
||||
pub fn from_path(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let mut file = File::open(path)?;
|
||||
ApkSigningBlock::from_reader(&mut file)
|
||||
}
|
||||
|
||||
/// Load the signing block from an APK file
|
||||
pub fn from_reader<R: Read + Seek>(reader: &mut R) -> Result<Self> {
|
||||
load_signing_block(reader)
|
||||
}
|
||||
|
||||
/// Parse signatures from signing block
|
||||
pub fn get_signatures(&self) -> Result<Vec<ApkSignatureBlock>> {
|
||||
const V2_SIG_BLOCK_ID: u32 = 0x7109871a;
|
||||
const V3_SIG_BLOCK_ID: u32 = 0xf05368c0;
|
||||
|
||||
let mut sigs = vec![];
|
||||
for (k, v) in &self.data {
|
||||
match *k {
|
||||
V2_SIG_BLOCK_ID => {
|
||||
let v2 = get_length_prefixed_u32_sequence(v)?;
|
||||
ensure!(
|
||||
v2.len() == 1,
|
||||
"Expected 1 element in signing block got {}",
|
||||
v2.len()
|
||||
);
|
||||
|
||||
let v2 = get_length_prefixed_u32_sequence(v2[0])?;
|
||||
let signed_data = get_sequence(v2[0])?;
|
||||
let digests = get_sequence_kv(signed_data[0])?;
|
||||
let certificates = get_sequence(signed_data[1])?;
|
||||
let attributes = get_sequence_kv(signed_data[2])?;
|
||||
let signatures = get_sequence_kv(v2[1])?;
|
||||
let public_key = v2[2];
|
||||
let digests: HashMap<u32, &[u8]> = HashMap::from_iter(digests);
|
||||
sigs.push(ApkSignatureBlock::V2 {
|
||||
attributes: HashMap::from_iter(
|
||||
attributes.into_iter().map(|(k, v)| (k, v.to_vec())),
|
||||
),
|
||||
certificates: certificates.into_iter().map(|v| v.to_vec()).collect(),
|
||||
signatures: parse_sigs(&signatures, &digests),
|
||||
public_key: public_key.to_vec(),
|
||||
});
|
||||
}
|
||||
V3_SIG_BLOCK_ID => {
|
||||
let v3 = get_length_prefixed_u32_sequence(v)?;
|
||||
ensure!(
|
||||
v3.len() == 1,
|
||||
"Expected 1 element in signing block got {}",
|
||||
v3.len()
|
||||
);
|
||||
|
||||
let v3 = get_length_prefixed_u32_sequence(v3[0])?;
|
||||
let signed_data = get_sequence(v3[0])?;
|
||||
let digests = get_sequence_kv(signed_data[0])?;
|
||||
let certificates = get_sequence(signed_data[1])?;
|
||||
let min_sdk_signed = u32::from_le_bytes(signed_data[2].try_into()?);
|
||||
let max_sdk_signed = u32::from_le_bytes(signed_data[3].try_into()?);
|
||||
let attributes = get_sequence_kv(signed_data[4])?;
|
||||
|
||||
let min_sdk = u32::from_le_bytes(v3[1].try_into()?);
|
||||
let max_sdk = u32::from_le_bytes(v3[2].try_into()?);
|
||||
|
||||
ensure!(
|
||||
min_sdk_signed == min_sdk,
|
||||
"Invalid min_sdk in signing block V3 {} != {}",
|
||||
min_sdk_signed,
|
||||
min_sdk
|
||||
);
|
||||
ensure!(
|
||||
max_sdk_signed == max_sdk,
|
||||
"Invalid max_sdk in signing block V3 {} != {}",
|
||||
max_sdk_signed,
|
||||
max_sdk
|
||||
);
|
||||
|
||||
let signatures = get_sequence_kv(v3[3])?;
|
||||
let public_key = v3[4];
|
||||
let digests: HashMap<u32, &[u8]> = HashMap::from_iter(digests);
|
||||
|
||||
sigs.push(ApkSignatureBlock::V3 {
|
||||
min_sdk,
|
||||
max_sdk,
|
||||
attributes: HashMap::from_iter(
|
||||
attributes.into_iter().map(|(k, v)| (k, v.to_vec())),
|
||||
),
|
||||
certificates: certificates.into_iter().map(|v| v.to_vec()).collect(),
|
||||
signatures: parse_sigs(&signatures, &digests),
|
||||
public_key: public_key.to_vec(),
|
||||
});
|
||||
}
|
||||
v => debug!("Unknown block id {}", v),
|
||||
}
|
||||
}
|
||||
Ok(sigs)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_sigs(signatures: &Vec<(u32, &[u8])>, digests: &HashMap<u32, &[u8]>) -> Vec<ApkSignature> {
|
||||
signatures
|
||||
.into_iter()
|
||||
.filter_map(|(k, v)| {
|
||||
let sig_len = u32::from_le_bytes(v[..4].try_into().ok()?) as usize;
|
||||
if sig_len > v.len() - 4 {
|
||||
warn!("Invalid signature length: {} > {}", sig_len, v.len());
|
||||
return None;
|
||||
}
|
||||
if let Ok(a) = ApkSignatureAlgo::try_from(*k) {
|
||||
Some(ApkSignature {
|
||||
algo: a,
|
||||
digest: digests.get(&k).map(|v| v[4..].to_vec())?,
|
||||
signature: v[4..sig_len + 4].to_vec(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ApkSignatureBlock {
|
||||
/// Unknown block
|
||||
Unknown { data: Vec<u8> },
|
||||
|
||||
/// Android V2 Signature Block
|
||||
///
|
||||
/// https://source.android.com/docs/security/features/apksigning/v2#apk-signature-scheme-v2-block-format
|
||||
V2 {
|
||||
signatures: Vec<ApkSignature>,
|
||||
public_key: Vec<u8>,
|
||||
certificates: Vec<Vec<u8>>,
|
||||
attributes: HashMap<u32, Vec<u8>>,
|
||||
},
|
||||
|
||||
/// Android V3 Signature Block
|
||||
///
|
||||
/// https://source.android.com/docs/security/features/apksigning/v3#format
|
||||
V3 {
|
||||
signatures: Vec<ApkSignature>,
|
||||
certificates: Vec<Vec<u8>>,
|
||||
public_key: Vec<u8>,
|
||||
attributes: HashMap<u32, Vec<u8>>,
|
||||
min_sdk: u32,
|
||||
max_sdk: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for ApkSignatureBlock {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ApkSignatureBlock::Unknown { .. } => write!(f, "unknown"),
|
||||
ApkSignatureBlock::V2 { signatures, .. } => {
|
||||
write!(f, "v2: ")?;
|
||||
for sig in signatures {
|
||||
write!(
|
||||
f,
|
||||
"algo={}, digest={}, sig={}",
|
||||
sig.algo,
|
||||
hex::encode(&sig.digest),
|
||||
hex::encode(&sig.signature)
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ApkSignatureBlock::V3 { signatures, .. } => {
|
||||
write!(f, "V3: ")?;
|
||||
for sig in signatures {
|
||||
write!(
|
||||
f,
|
||||
"algo={}, digest={}, sig={}",
|
||||
sig.algo,
|
||||
hex::encode(&sig.digest),
|
||||
hex::encode(&sig.signature)
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ApkSignature {
|
||||
pub algo: ApkSignatureAlgo,
|
||||
pub signature: Vec<u8>,
|
||||
pub digest: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ApkSignatureAlgo {
|
||||
RsaSsaPssSha256,
|
||||
RsaSsaPssSha512,
|
||||
RsaSsaPkcs1Sha256,
|
||||
RsaSsaPkcs1Sha512,
|
||||
EcdsaSha256,
|
||||
EcdsaSha512,
|
||||
DsaSha256,
|
||||
}
|
||||
|
||||
impl Display for ApkSignatureAlgo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ApkSignatureAlgo::RsaSsaPssSha256 => write!(f, "RSASSA-PSS-SHA256"),
|
||||
ApkSignatureAlgo::RsaSsaPssSha512 => write!(f, "RSASSA-PSS-SHA512"),
|
||||
ApkSignatureAlgo::RsaSsaPkcs1Sha256 => write!(f, "RSASSA-PKCS1-SHA256"),
|
||||
ApkSignatureAlgo::RsaSsaPkcs1Sha512 => write!(f, "RSASSA-PKCS1-SHA512"),
|
||||
ApkSignatureAlgo::EcdsaSha256 => write!(f, "ECDSA-SHA256"),
|
||||
ApkSignatureAlgo::EcdsaSha512 => write!(f, "ECDSA-SHA512"),
|
||||
ApkSignatureAlgo::DsaSha256 => write!(f, "DSA-SHA256"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for ApkSignatureAlgo {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
0x0101 => Ok(ApkSignatureAlgo::RsaSsaPssSha256),
|
||||
0x0102 => Ok(ApkSignatureAlgo::RsaSsaPssSha512),
|
||||
0x0103 => Ok(ApkSignatureAlgo::RsaSsaPkcs1Sha256),
|
||||
0x0104 => Ok(ApkSignatureAlgo::RsaSsaPkcs1Sha512),
|
||||
0x0201 => Ok(ApkSignatureAlgo::EcdsaSha256),
|
||||
0x0202 => Ok(ApkSignatureAlgo::EcdsaSha512),
|
||||
0x0301 => Ok(ApkSignatureAlgo::DsaSha256),
|
||||
_ => bail!("Unknown signature algo"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_signing_block<R>(zip: &mut R) -> Result<ApkSigningBlock>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
const SIG_BLOCK_MAGIC: &[u8] = b"APK Sig Block 42";
|
||||
|
||||
// scan backwards until we find the singing block
|
||||
let flen = zip.seek(SeekFrom::End(0))?;
|
||||
let mut magic_buf = [0u8; 16];
|
||||
loop {
|
||||
let magic_pos = zip.seek(SeekFrom::Current(-17))?;
|
||||
if magic_pos <= 4 {
|
||||
bail!("Failed to find signing block");
|
||||
}
|
||||
|
||||
zip.read_exact(&mut magic_buf)?;
|
||||
if magic_buf == SIG_BLOCK_MAGIC {
|
||||
zip.seek(SeekFrom::Current(-(16 + 8)))?;
|
||||
let size1 = zip.read_u64::<LittleEndian>()?;
|
||||
ensure!(size1 <= flen, "Signing block is larger than entire file");
|
||||
|
||||
zip.seek(SeekFrom::Current(-(size1 as i64 - 8)))?;
|
||||
let size2 = zip.read_u64::<LittleEndian>()?;
|
||||
ensure!(
|
||||
size2 == size1,
|
||||
"Invalid block sizes, {} != {}",
|
||||
size1,
|
||||
size2
|
||||
);
|
||||
|
||||
let mut data_bytes = size1 - 8 - 16;
|
||||
let mut sigs = Vec::new();
|
||||
loop {
|
||||
let (k, v) = read_u64_length_prefixed_kv(zip)?;
|
||||
data_bytes -= (v.len() + 4 + 8) as u64;
|
||||
sigs.push((k, v));
|
||||
if data_bytes == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
zip.seek(SeekFrom::Start(0))?;
|
||||
return Ok(ApkSigningBlock { data: sigs });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_u64_length_prefixed_kv<T>(file: &mut T) -> Result<(u32, Vec<u8>)>
|
||||
where
|
||||
T: Read + Seek,
|
||||
{
|
||||
let kv_len = file.read_u64::<LittleEndian>()?;
|
||||
let k = file.read_u32::<LittleEndian>()?;
|
||||
let v_len = kv_len as usize - 4;
|
||||
let mut v = vec![0; v_len];
|
||||
file.read_exact(v.as_mut_slice())?;
|
||||
Ok((k, v))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u64_length_prefixed_kv(slice: &[u8]) -> Result<(u32, &[u8])> {
|
||||
let kv_len = u64::from_le_bytes(slice[..8].try_into()?);
|
||||
let k = u32::from_le_bytes(slice[8..12].try_into()?);
|
||||
Ok((k, &slice[12..(kv_len as usize - 12)]))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u32_length_prefixed_kv(slice: &[u8]) -> Result<(u32, &[u8])> {
|
||||
let kv_len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
let k = u32::from_le_bytes(slice[4..8].try_into()?);
|
||||
Ok((k, &slice[8..(kv_len as usize - 8)]))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_length_prefixed_u32(slice: &[u8]) -> Result<&[u8]> {
|
||||
let len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
Ok(&slice[4..4 + len as usize])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_length_prefixed_u32_sequence(slice: &[u8]) -> Result<Vec<&[u8]>> {
|
||||
let sequence_len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
get_sequence(&slice[4..4 + sequence_len as usize])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_sequence(mut slice: &[u8]) -> Result<Vec<&[u8]>> {
|
||||
let mut ret = Vec::new();
|
||||
while slice.len() >= 4 {
|
||||
let data = get_length_prefixed_u32(slice)?;
|
||||
let r_len = data.len() + 4;
|
||||
slice = &slice[r_len..];
|
||||
ret.push(data);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_sequence_kv(slice: &[u8]) -> Result<Vec<(u32, &[u8])>> {
|
||||
let seq = get_sequence(slice)?;
|
||||
Ok(seq
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
let k = u32::from_le_bytes(s[..4].try_into().unwrap());
|
||||
let v = &s[4..];
|
||||
(k, v)
|
||||
})
|
||||
.collect())
|
||||
}
|
415
src/repo/mod.rs
415
src/repo/mod.rs
@ -1,22 +1,19 @@
|
||||
use crate::manifest::Manifest;
|
||||
use crate::repo::github::GithubRepo;
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use apk::res::Chunk;
|
||||
use apk::zip::ZipArchive;
|
||||
use apk::AndroidManifest;
|
||||
use byteorder::LittleEndian;
|
||||
use byteorder::ReadBytesExt;
|
||||
use log::{debug, info, warn};
|
||||
use apk_parser::zip::ZipArchive;
|
||||
use apk_parser::{parse_android_manifest, AndroidManifest, ApkSignatureBlock, ApkSigningBlock};
|
||||
use log::{info, warn};
|
||||
use nostr_sdk::prelude::{hex, Coordinate, StreamExt};
|
||||
use nostr_sdk::{Event, EventBuilder, Kind, NostrSigner, Tag};
|
||||
use reqwest::Url;
|
||||
use semver::Version;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashSet;
|
||||
use std::env::temp_dir;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
use std::io::{Read, Seek};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
@ -66,7 +63,7 @@ impl TryInto<EventBuilder> for RepoArtifact {
|
||||
Tag::parse(["f", self.platform.to_string().as_str()])?,
|
||||
Tag::parse(["m", self.content_type.as_str()])?,
|
||||
Tag::parse(["size", self.size.to_string().as_str()])?,
|
||||
Tag::parse(["x", &hex::encode(self.hash)])?
|
||||
Tag::parse(["x", &hex::encode(self.hash)])?,
|
||||
]);
|
||||
if let RepoResource::Remote(u) = self.location {
|
||||
b = b.tag(Tag::parse(["url", u.as_str()])?);
|
||||
@ -74,10 +71,11 @@ impl TryInto<EventBuilder> for RepoArtifact {
|
||||
match self.metadata {
|
||||
ArtifactMetadata::APK {
|
||||
manifest,
|
||||
signature,
|
||||
signatures,
|
||||
} => {
|
||||
for signature in signatures {
|
||||
match signature {
|
||||
ApkSignatureBlock::None => {
|
||||
ApkSignatureBlock::Unknown { .. } => {
|
||||
warn!("No signature found in metadata");
|
||||
}
|
||||
ApkSignatureBlock::V2 { signatures, .. } => {
|
||||
@ -97,6 +95,7 @@ impl TryInto<EventBuilder> for RepoArtifact {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(vn) = manifest.version_name {
|
||||
b = b.tag(Tag::parse(["version", vn.as_str()])?);
|
||||
}
|
||||
@ -115,7 +114,6 @@ impl TryInto<EventBuilder> for RepoArtifact {
|
||||
target_sdk.to_string().as_str(),
|
||||
])?);
|
||||
}
|
||||
//TODO: apk sig
|
||||
}
|
||||
}
|
||||
Ok(b)
|
||||
@ -126,117 +124,16 @@ impl TryInto<EventBuilder> for RepoArtifact {
|
||||
pub enum ArtifactMetadata {
|
||||
APK {
|
||||
manifest: AndroidManifest,
|
||||
signature: ApkSignatureBlock,
|
||||
signatures: Vec<ApkSignatureBlock>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ApkSignatureBlock {
|
||||
None,
|
||||
/// Android V2 Signature Block
|
||||
///
|
||||
/// https://source.android.com/docs/security/features/apksigning/v2#apk-signature-scheme-v2-block-format
|
||||
V2 {
|
||||
signatures: Vec<ApkSignature>,
|
||||
public_key: Vec<u8>,
|
||||
certificates: Vec<Vec<u8>>,
|
||||
attributes: HashMap<u32, Vec<u8>>,
|
||||
},
|
||||
V3 {
|
||||
signatures: Vec<ApkSignature>,
|
||||
public_key: Vec<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for ApkSignatureBlock {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ApkSignatureBlock::None => write!(f, "none"),
|
||||
ApkSignatureBlock::V2 { signatures, .. } => {
|
||||
write!(f, "v2: ")?;
|
||||
for sig in signatures {
|
||||
write!(
|
||||
f,
|
||||
"algo={}, digest={}, sig={}",
|
||||
sig.algo,
|
||||
hex::encode(&sig.digest),
|
||||
hex::encode(&sig.signature)
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ApkSignatureBlock::V3 { signatures, .. } => {
|
||||
write!(f, "V3: ")?;
|
||||
for sig in signatures {
|
||||
write!(
|
||||
f,
|
||||
"algo={}, digest={}, sig={}",
|
||||
sig.algo,
|
||||
hex::encode(&sig.digest),
|
||||
hex::encode(&sig.signature)
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ApkSignature {
|
||||
pub algo: ApkSignatureAlgo,
|
||||
pub signature: Vec<u8>,
|
||||
pub digest: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ApkSignatureAlgo {
|
||||
RsaSsaPssSha256,
|
||||
RsaSsaPssSha512,
|
||||
RsaSsaPkcs1Sha256,
|
||||
RsaSsaPkcs1Sha512,
|
||||
EcdsaSha256,
|
||||
EcdsaSha512,
|
||||
DsaSha256,
|
||||
}
|
||||
|
||||
impl Display for ApkSignatureAlgo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ApkSignatureAlgo::RsaSsaPssSha256 => write!(f, "RSASSA-PSS-SHA256"),
|
||||
ApkSignatureAlgo::RsaSsaPssSha512 => write!(f, "RSASSA-PSS-SHA512"),
|
||||
ApkSignatureAlgo::RsaSsaPkcs1Sha256 => write!(f, "RSASSA-PKCS1-SHA256"),
|
||||
ApkSignatureAlgo::RsaSsaPkcs1Sha512 => write!(f, "RSASSA-PKCS1-SHA512"),
|
||||
ApkSignatureAlgo::EcdsaSha256 => write!(f, "ECDSA-SHA256"),
|
||||
ApkSignatureAlgo::EcdsaSha512 => write!(f, "ECDSA-SHA512"),
|
||||
ApkSignatureAlgo::DsaSha256 => write!(f, "DSA-SHA256"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for ApkSignatureAlgo {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
0x0101 => Ok(ApkSignatureAlgo::RsaSsaPssSha256),
|
||||
0x0102 => Ok(ApkSignatureAlgo::RsaSsaPssSha512),
|
||||
0x0103 => Ok(ApkSignatureAlgo::RsaSsaPkcs1Sha256),
|
||||
0x0104 => Ok(ApkSignatureAlgo::RsaSsaPkcs1Sha512),
|
||||
0x0201 => Ok(ApkSignatureAlgo::EcdsaSha256),
|
||||
0x0202 => Ok(ApkSignatureAlgo::EcdsaSha512),
|
||||
0x0301 => Ok(ApkSignatureAlgo::DsaSha256),
|
||||
_ => bail!("Unknown signature algo"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ArtifactMetadata {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ArtifactMetadata::APK {
|
||||
manifest,
|
||||
signature,
|
||||
signatures,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
@ -244,7 +141,11 @@ impl Display for ArtifactMetadata {
|
||||
manifest.package.as_ref().unwrap_or(&"missing".to_string()),
|
||||
manifest.version_name.as_ref().unwrap_or(&String::new()),
|
||||
manifest.version_code.as_ref().unwrap_or(&0),
|
||||
signature
|
||||
signatures
|
||||
.iter()
|
||||
.map(|b| b.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -441,7 +342,7 @@ async fn load_artifact_url(url: &str) -> Result<RepoArtifact> {
|
||||
info!("Downloading artifact {}", url);
|
||||
let u = Url::parse(url)?;
|
||||
let rsp = reqwest::get(u.clone()).await?;
|
||||
let id = hex::encode(sha2::Sha256::digest(url.as_bytes()));
|
||||
let id = hex::encode(Sha256::digest(url.as_bytes()));
|
||||
let mut tmp = temp_dir().join(id);
|
||||
tmp.set_extension(
|
||||
PathBuf::from(u.path())
|
||||
@ -480,7 +381,7 @@ fn load_artifact(path: &Path) -> Result<RepoArtifact> {
|
||||
fn load_apk_artifact(path: &Path) -> Result<RepoArtifact> {
|
||||
let file = File::open(path)?;
|
||||
let mut file = std::io::BufReader::new(file);
|
||||
let sig_block = load_signing_block(&mut file)?;
|
||||
let sig_block = ApkSigningBlock::from_reader(&mut file)?;
|
||||
|
||||
let mut zip = ZipArchive::new(file)?;
|
||||
let manifest = load_manifest(&mut zip)?;
|
||||
@ -514,7 +415,7 @@ fn load_apk_artifact(path: &Path) -> Result<RepoArtifact> {
|
||||
},
|
||||
metadata: ArtifactMetadata::APK {
|
||||
manifest,
|
||||
signature: sig_block.try_into()?,
|
||||
signatures: sig_block.get_signatures()?,
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -545,180 +446,6 @@ where
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ApkSigningBlock {
|
||||
pub data: Vec<(u32, Vec<u8>)>,
|
||||
}
|
||||
|
||||
impl TryInto<ApkSignatureBlock> for ApkSigningBlock {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_into(self) -> std::result::Result<ApkSignatureBlock, Self::Error> {
|
||||
const V2_SIG_BLOCK_ID: u32 = 0x7109871a;
|
||||
const V3_SIG_BLOCK_ID: u32 = 0xf05368c0;
|
||||
|
||||
if let Some(v3) =
|
||||
self.data
|
||||
.iter()
|
||||
.find_map(|(k, v)| if *k == V3_SIG_BLOCK_ID { Some(v) } else { None })
|
||||
{
|
||||
todo!("Not done yet")
|
||||
}
|
||||
if let Some(v2) =
|
||||
self.data
|
||||
.iter()
|
||||
.find_map(|(k, v)| if *k == V2_SIG_BLOCK_ID { Some(v) } else { None })
|
||||
{
|
||||
let v2 = get_length_prefixed_u32_sequence(&v2[4..])?;
|
||||
let signed_data = get_sequence(v2[0])?;
|
||||
let digests = get_sequence_kv(signed_data[0])?;
|
||||
let certificates = get_sequence(signed_data[1])?;
|
||||
let attributes = get_sequence_kv(signed_data[2])?;
|
||||
let signatures = get_sequence_kv(v2[1])?;
|
||||
let public_key = v2[2];
|
||||
let digests: HashMap<u32, &[u8]> = HashMap::from_iter(digests);
|
||||
return Ok(ApkSignatureBlock::V2 {
|
||||
attributes: HashMap::from_iter(
|
||||
attributes.into_iter().map(|(k, v)| (k, v.to_vec())),
|
||||
),
|
||||
certificates: certificates.into_iter().map(|v| v.to_vec()).collect(),
|
||||
signatures: signatures
|
||||
.into_iter()
|
||||
.filter_map(|(k, v)| {
|
||||
let sig_len = u32::from_le_bytes(v[..4].try_into().ok()?) as usize;
|
||||
if sig_len > v.len() - 4 {
|
||||
warn!("Invalid signature length: {} > {}", sig_len, v.len());
|
||||
return None;
|
||||
}
|
||||
if let Ok(a) = ApkSignatureAlgo::try_from(k) {
|
||||
Some(ApkSignature {
|
||||
algo: a,
|
||||
digest: digests.get(&k).map(|v| v[4..].to_vec())?,
|
||||
signature: v[4..sig_len + 4].to_vec(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
public_key: public_key.to_vec(),
|
||||
});
|
||||
}
|
||||
Ok(ApkSignatureBlock::None)
|
||||
}
|
||||
}
|
||||
|
||||
fn load_signing_block<R>(zip: &mut R) -> Result<ApkSigningBlock>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
const SIG_BLOCK_MAGIC: &[u8] = b"APK Sig Block 42";
|
||||
|
||||
// scan backwards until we find the singing block
|
||||
let flen = zip.seek(SeekFrom::End(0))?;
|
||||
let mut magic_buf = [0u8; 16];
|
||||
loop {
|
||||
let magic_pos = zip.seek(SeekFrom::Current(-17))?;
|
||||
if magic_pos <= 4 {
|
||||
bail!("Failed to find signing block");
|
||||
}
|
||||
|
||||
zip.read_exact(&mut magic_buf)?;
|
||||
if magic_buf == SIG_BLOCK_MAGIC {
|
||||
zip.seek(SeekFrom::Current(-(16 + 8)))?;
|
||||
let size1 = zip.read_u64::<LittleEndian>()?;
|
||||
ensure!(size1 <= flen, "Signing block is larger than entire file");
|
||||
|
||||
zip.seek(SeekFrom::Current(-(size1 as i64 - 8)))?;
|
||||
let size2 = zip.read_u64::<LittleEndian>()?;
|
||||
ensure!(
|
||||
size2 == size1,
|
||||
"Invalid block sizes, {} != {}",
|
||||
size1,
|
||||
size2
|
||||
);
|
||||
|
||||
let mut data_bytes = size1 - 8 - 16;
|
||||
let mut sigs = Vec::new();
|
||||
loop {
|
||||
let (k, v) = read_u64_length_prefixed_kv(zip)?;
|
||||
data_bytes -= (v.len() + 4 + 8) as u64;
|
||||
sigs.push((k, v));
|
||||
if data_bytes == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
zip.seek(SeekFrom::Start(0))?;
|
||||
return Ok(ApkSigningBlock { data: sigs });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_u64_length_prefixed_kv<T>(file: &mut T) -> Result<(u32, Vec<u8>)>
|
||||
where
|
||||
T: Read + Seek,
|
||||
{
|
||||
let kv_len = file.read_u64::<LittleEndian>()?;
|
||||
let k = file.read_u32::<LittleEndian>()?;
|
||||
let v_len = kv_len as usize - 4;
|
||||
let mut v = vec![0; v_len];
|
||||
file.read_exact(v.as_mut_slice())?;
|
||||
Ok((k, v))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u64_length_prefixed_kv(slice: &[u8]) -> Result<(u32, &[u8])> {
|
||||
let kv_len = u64::from_le_bytes(slice[..8].try_into()?);
|
||||
let k = u32::from_le_bytes(slice[8..12].try_into()?);
|
||||
Ok((k, &slice[12..(kv_len as usize - 12)]))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u32_length_prefixed_kv(slice: &[u8]) -> Result<(u32, &[u8])> {
|
||||
let kv_len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
let k = u32::from_le_bytes(slice[4..8].try_into()?);
|
||||
Ok((k, &slice[8..(kv_len as usize - 8)]))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_length_prefixed_u32(slice: &[u8]) -> Result<&[u8]> {
|
||||
let len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
Ok(&slice[4..4 + len as usize])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_length_prefixed_u32_sequence(slice: &[u8]) -> Result<Vec<&[u8]>> {
|
||||
let sequence_len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
get_sequence(&slice[4..4 + sequence_len as usize])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_sequence(mut slice: &[u8]) -> Result<Vec<&[u8]>> {
|
||||
let mut ret = Vec::new();
|
||||
while slice.len() >= 4 {
|
||||
let data = get_length_prefixed_u32(slice)?;
|
||||
let r_len = data.len() + 4;
|
||||
slice = &slice[r_len..];
|
||||
ret.push(data);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_sequence_kv(slice: &[u8]) -> Result<Vec<(u32, &[u8])>> {
|
||||
let seq = get_sequence(slice)?;
|
||||
Ok(seq
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
let k = u32::from_le_bytes(s[..4].try_into().unwrap());
|
||||
let v = &s[4..];
|
||||
(k, v)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn list_libs<T>(zip: &mut ZipArchive<T>) -> Vec<String>
|
||||
where
|
||||
T: Read + Seek,
|
||||
@ -734,98 +461,6 @@ where
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_android_manifest(data: &[u8]) -> Result<AndroidManifest> {
|
||||
let chunks = if let Chunk::Xml(chunks) = Chunk::parse(&mut Cursor::new(data))? {
|
||||
chunks
|
||||
} else {
|
||||
bail!("Invalid AndroidManifest file");
|
||||
};
|
||||
|
||||
let strings = if let Chunk::StringPool(strings, _) = &chunks[0] {
|
||||
HashMap::from_iter(
|
||||
strings
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| (s.to_string(), i as i32)),
|
||||
)
|
||||
} else {
|
||||
bail!("invalid manifest 1");
|
||||
};
|
||||
|
||||
let mut res = AndroidManifest::default();
|
||||
res.package = find_value_in(&strings, &chunks, "manifest", "package");
|
||||
res.version_code =
|
||||
find_value_in(&strings, &chunks, "manifest", "versionCode").and_then(|v| v.parse().ok());
|
||||
res.version_name = find_value_in(&strings, &chunks, "manifest", "versionName");
|
||||
res.compile_sdk_version = find_value_in(&strings, &chunks, "manifest", "compileSdkVersion")
|
||||
.and_then(|v| v.parse().ok());
|
||||
res.compile_sdk_version_codename =
|
||||
find_value_in(&strings, &chunks, "manifest", "compileSdkVersionCodename")
|
||||
.and_then(|v| v.parse().ok());
|
||||
res.platform_build_version_code =
|
||||
find_value_in(&strings, &chunks, "manifest", "platformBuildVersionCode")
|
||||
.and_then(|v| v.parse().ok());
|
||||
res.platform_build_version_name =
|
||||
find_value_in(&strings, &chunks, "manifest", "platformBuildVersionName")
|
||||
.and_then(|v| v.parse().ok());
|
||||
|
||||
res.sdk.min_sdk_version =
|
||||
find_value_in(&strings, &chunks, "uses-sdk", "minSdkVersion").and_then(|v| v.parse().ok());
|
||||
res.sdk.target_sdk_version = find_value_in(&strings, &chunks, "uses-sdk", "targetSdkVersion")
|
||||
.and_then(|v| v.parse().ok());
|
||||
res.sdk.max_sdk_version =
|
||||
find_value_in(&strings, &chunks, "uses-sdk", "maxSdkVersion").and_then(|v| v.parse().ok());
|
||||
|
||||
res.application.theme = find_value_in(&strings, &chunks, "application", "theme");
|
||||
res.application.label = find_value_in(&strings, &chunks, "application", "label");
|
||||
res.application.icon = find_value_in(&strings, &chunks, "application", "icon");
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn find_value_in(
|
||||
strings: &HashMap<String, i32>,
|
||||
chunks: &Vec<Chunk>,
|
||||
node: &str,
|
||||
attr: &str,
|
||||
) -> Option<String> {
|
||||
let idx_node = if let Some(i) = strings.get(node) {
|
||||
*i
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let idx_attr = if let Some(i) = strings.get(attr) {
|
||||
*i
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
chunks.iter().find_map(|chunk| {
|
||||
if let Chunk::XmlStartElement(_, el, attrs) = chunk {
|
||||
match el.name {
|
||||
x if x == idx_node => attrs.iter().find(|e| e.name == idx_attr).and_then(|e| {
|
||||
debug!("{}, {}, {:?}", node, attr, e);
|
||||
match e.typed_value.data_type {
|
||||
3 => strings
|
||||
.iter()
|
||||
.find(|(_, v)| **v == e.raw_value)
|
||||
.map(|(k, _)| k.clone()),
|
||||
16 => Some(e.typed_value.data.to_string()),
|
||||
_ => {
|
||||
debug!("unknown data type {},{},{:?}", node, attr, e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -833,17 +468,9 @@ mod tests {
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn read_apk() -> Result<()> {
|
||||
let path = "/home/kieran/Downloads/app-arm64-v8a-release.apk";
|
||||
let path = "/home/kieran/Downloads/snort-arm64-v8a-v0.3.0.apk";
|
||||
|
||||
let apk = load_apk_artifact(&PathBuf::from(path))?;
|
||||
assert!(
|
||||
matches!(&apk.platform, Platform::Android { arch } if matches!(arch, Architecture::ARM64 { .. }))
|
||||
);
|
||||
assert!(matches!(&apk.metadata,
|
||||
ArtifactMetadata::APK { signature, .. } if matches!(signature,
|
||||
ApkSignatureBlock::V2 { signatures, .. } if signatures.len() == 1 &&
|
||||
matches!(signatures[0].algo, ApkSignatureAlgo::RsaSsaPkcs1Sha256))));
|
||||
|
||||
eprint!("{}", apk);
|
||||
Ok(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user