Initial commit
This commit is contained in:
commit
9f113b4952
8 changed files with 4109 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
target
|
||||
out
|
774
Cargo.lock
generated
Normal file
774
Cargo.lock
generated
Normal file
|
@ -0,0 +1,774 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[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 = "block-modes"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e2211b0817f061502a8dd9f11a37e879e79763e3c698d2418cf824d8cb2f21e"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[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 = "ctr"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[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 = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-build"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"itertools",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
"petgraph",
|
||||
"prettyplease",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
version = "7.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rtoolbox",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtoolbox"
|
||||
version = "0.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.32.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[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 = "signal-decrypt-backup-rust"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"base64",
|
||||
"block-modes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"prost",
|
||||
"prost-build",
|
||||
"prost-types",
|
||||
"rpassword",
|
||||
"rusqlite",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[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.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"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",
|
||||
]
|
24
Cargo.toml
Normal file
24
Cargo.toml
Normal file
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "signal-decrypt-backup-rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
prost-build = "0.13.4"
|
||||
|
||||
[dependencies]
|
||||
aes = "0.8"
|
||||
block-modes = "0.9"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
serde_json = "1.0"
|
||||
hkdf = "0.12.4"
|
||||
base64 = "0.22.1"
|
||||
rusqlite = "0.32.1"
|
||||
rpassword = "7.3.1"
|
||||
prost = "0.13.4"
|
||||
prost-types = "0.13.4"
|
||||
ctr = "0.9.2"
|
||||
cipher = "0.4.4"
|
29
build.rs
Normal file
29
build.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// use protobuf_codegen::Codegen;
|
||||
|
||||
// fn main() {
|
||||
// // Specify the path to your .proto file
|
||||
// let proto_file = "./src/Backups.proto"; // Adjust the path accordingly
|
||||
|
||||
// // Generate Rust code from the .proto file
|
||||
// Codegen::new()
|
||||
// .out_dir("./src/proto") // Output directory for generated code
|
||||
// .inputs(&[proto_file])
|
||||
// .include("./src") // Include path for imports
|
||||
// .run()
|
||||
// .expect("protoc failed.");
|
||||
// }
|
||||
|
||||
use std::io::Result;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Specify the path to your .proto files
|
||||
let proto_files = &["src/Backups.proto"]; // Adjust the path as necessary
|
||||
|
||||
// Specify the output directory
|
||||
let _out_dir = [std::env::var("OUT_DIR").unwrap()];
|
||||
|
||||
// Compile the proto files
|
||||
prost_build::compile_protos(proto_files, &["src"]).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
82
src/Backups.proto
Normal file
82
src/Backups.proto
Normal file
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* Copyright (C) 2018 Open Whisper Systems
|
||||
*
|
||||
* Licensed according to the LICENSE file in this repository.
|
||||
*/
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package signal;
|
||||
|
||||
option java_package = "org.thoughtcrime.securesms.backup.proto";
|
||||
|
||||
message SqlStatement {
|
||||
message SqlParameter {
|
||||
optional string stringParamter = 1;
|
||||
optional uint64 integerParameter = 2;
|
||||
optional double doubleParameter = 3;
|
||||
optional bytes blobParameter = 4;
|
||||
optional bool nullparameter = 5;
|
||||
}
|
||||
|
||||
optional string statement = 1;
|
||||
repeated SqlParameter parameters = 2;
|
||||
}
|
||||
|
||||
message SharedPreference {
|
||||
optional string file = 1;
|
||||
optional string key = 2;
|
||||
optional string value = 3;
|
||||
optional bool booleanValue = 4;
|
||||
repeated string stringSetValue = 5;
|
||||
optional bool isStringSetValue = 6;
|
||||
}
|
||||
|
||||
message Attachment {
|
||||
optional uint64 rowId = 1;
|
||||
optional uint64 attachmentId = 2;
|
||||
optional uint32 length = 3;
|
||||
}
|
||||
|
||||
message Sticker {
|
||||
optional uint64 rowId = 1;
|
||||
optional uint32 length = 2;
|
||||
}
|
||||
|
||||
message Avatar {
|
||||
optional string name = 1;
|
||||
optional string recipientId = 3;
|
||||
optional uint32 length = 2;
|
||||
}
|
||||
|
||||
message DatabaseVersion {
|
||||
optional uint32 version = 1;
|
||||
}
|
||||
|
||||
message Header {
|
||||
optional bytes iv = 1;
|
||||
optional bytes salt = 2;
|
||||
optional uint32 version = 3;
|
||||
}
|
||||
|
||||
message KeyValue {
|
||||
optional string key = 1;
|
||||
optional bytes blobValue = 2;
|
||||
optional bool booleanValue = 3;
|
||||
optional float floatValue = 4;
|
||||
optional int32 integerValue = 5;
|
||||
optional int64 longValue = 6;
|
||||
optional string stringValue = 7;
|
||||
}
|
||||
|
||||
message BackupFrame {
|
||||
optional Header header = 1;
|
||||
optional SqlStatement statement = 2;
|
||||
optional SharedPreference preference = 3;
|
||||
optional Attachment attachment = 4;
|
||||
optional DatabaseVersion version = 5;
|
||||
optional bool end = 6;
|
||||
optional Avatar avatar = 7;
|
||||
optional Sticker sticker = 8;
|
||||
optional KeyValue keyValue = 9;
|
||||
}
|
490
src/main.rs
Normal file
490
src/main.rs
Normal file
|
@ -0,0 +1,490 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, BufReader, Read, Seek, SeekFrom, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use aes::Aes256;
|
||||
use base64::prelude::*;
|
||||
use ctr::cipher::{KeyIvInit, StreamCipher};
|
||||
use ctr::Ctr32BE;
|
||||
use hkdf::Hkdf;
|
||||
use hmac::{Hmac, Mac};
|
||||
use prost::Message;
|
||||
use rusqlite::{Connection, TransactionBehavior};
|
||||
use sha2::{Digest, Sha256, Sha512};
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
pub mod signal {
|
||||
include!(concat!(env!("OUT_DIR"), "/signal.rs"));
|
||||
}
|
||||
|
||||
use signal::BackupFrame;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HeaderData {
|
||||
initialisation_vector: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
version: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Keys {
|
||||
cipher_key: Vec<u8>,
|
||||
hmac_key: Vec<u8>,
|
||||
}
|
||||
|
||||
fn to_io_error(e: rusqlite::Error) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::Other, e.to_string())
|
||||
}
|
||||
|
||||
fn read_backup_header<R: Read>(backup_file: &mut R) -> io::Result<HeaderData> {
|
||||
let mut length_bytes = [0u8; 4];
|
||||
backup_file.read_exact(&mut length_bytes)?;
|
||||
let length = u32::from_be_bytes(length_bytes);
|
||||
|
||||
let mut backup_frame_bytes = vec![0u8; length as usize];
|
||||
backup_file.read_exact(&mut backup_frame_bytes)?;
|
||||
|
||||
let backup_frame: BackupFrame = BackupFrame::decode(&backup_frame_bytes[..])?;
|
||||
|
||||
let header = backup_frame
|
||||
.header
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Missing header"))?;
|
||||
|
||||
Ok(HeaderData {
|
||||
initialisation_vector: header.iv.unwrap(),
|
||||
salt: header.salt.unwrap(),
|
||||
version: header.version,
|
||||
})
|
||||
}
|
||||
|
||||
fn derive_keys(passphrase: &str, salt: &[u8]) -> io::Result<Keys> {
|
||||
let passphrase_bytes = passphrase.replace(" ", "").as_bytes().to_vec();
|
||||
|
||||
let mut hash = passphrase_bytes.clone();
|
||||
let mut sha512 = Sha512::new();
|
||||
|
||||
Digest::update(&mut sha512, salt);
|
||||
|
||||
for _ in 0..250000 {
|
||||
Digest::update(&mut sha512, &hash);
|
||||
Digest::update(&mut sha512, &passphrase_bytes);
|
||||
hash = sha512.finalize_reset().to_vec();
|
||||
}
|
||||
|
||||
let hkdf = Hkdf::<Sha256>::new(Some(b""), &hash[..32]);
|
||||
let mut keys = vec![0u8; 64];
|
||||
hkdf.expand(b"Backup Export", &mut keys)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "HKDF expand failed"))?;
|
||||
|
||||
Ok(Keys {
|
||||
cipher_key: keys[..32].to_vec(),
|
||||
hmac_key: keys[32..].to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
fn increment_initialisation_vector(initialisation_vector: &[u8]) -> Vec<u8> {
|
||||
let mut counter = u32::from_be_bytes(initialisation_vector[..4].try_into().unwrap());
|
||||
counter = (counter + 1) & 0xFFFFFFFF;
|
||||
let mut new_iv = counter.to_be_bytes().to_vec();
|
||||
new_iv.extend_from_slice(&initialisation_vector[4..]);
|
||||
new_iv
|
||||
}
|
||||
|
||||
fn parameter_to_native_type(
|
||||
parameter: &signal::sql_statement::SqlParameter,
|
||||
) -> rusqlite::Result<Option<Box<dyn rusqlite::ToSql>>> {
|
||||
if let Some(s) = ¶meter.string_paramter {
|
||||
Ok(Some(Box::new(s.clone())))
|
||||
} else if let Some(i) = parameter.integer_parameter {
|
||||
let signed_i = if i & (1 << 63) != 0 {
|
||||
i | (-1_i64 << 63) as u64
|
||||
} else {
|
||||
i
|
||||
};
|
||||
Ok(Some(Box::new(signed_i as i64)))
|
||||
} else if let Some(d) = parameter.double_parameter {
|
||||
Ok(Some(Box::new(d)))
|
||||
} else if let Some(b) = ¶meter.blob_parameter {
|
||||
Ok(Some(Box::new(b.clone())))
|
||||
} else if parameter.nullparameter.is_some() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
fn decrypt_frame<R: Read>(
|
||||
backup_file: &mut R,
|
||||
hmac_key: &[u8],
|
||||
cipher_key: &[u8],
|
||||
initialisation_vector: &[u8],
|
||||
header_version: Option<u32>,
|
||||
ciphertext_buf: &mut Vec<u8>,
|
||||
plaintext_buf: &mut Vec<u8>,
|
||||
) -> io::Result<BackupFrame> {
|
||||
let mut hmac = <HmacSha256 as Mac>::new_from_slice(hmac_key)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid HMAC key"))?;
|
||||
|
||||
let mut ctr =
|
||||
<Ctr32BE<Aes256> as KeyIvInit>::new_from_slices(cipher_key, initialisation_vector)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid CTR parameters"))?;
|
||||
|
||||
let length = match header_version {
|
||||
None => {
|
||||
let mut length_bytes = [0u8; 4];
|
||||
backup_file.read_exact(&mut length_bytes)?;
|
||||
u32::from_be_bytes(length_bytes)
|
||||
}
|
||||
Some(1) => {
|
||||
let mut encrypted_length = [0u8; 4];
|
||||
backup_file.read_exact(&mut encrypted_length)?;
|
||||
Mac::update(&mut hmac, &encrypted_length);
|
||||
|
||||
let mut decrypted_length = encrypted_length;
|
||||
ctr.apply_keystream(&mut decrypted_length);
|
||||
u32::from_be_bytes(decrypted_length)
|
||||
}
|
||||
Some(v) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Unsupported version: {}", v),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if length < 10 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Frame too short",
|
||||
));
|
||||
}
|
||||
|
||||
ciphertext_buf.clear();
|
||||
ciphertext_buf.resize((length - 10) as usize, 0);
|
||||
backup_file.read_exact(ciphertext_buf)?;
|
||||
|
||||
let mut their_mac = [0u8; 10];
|
||||
backup_file.read_exact(&mut their_mac)?;
|
||||
|
||||
Mac::update(&mut hmac, ciphertext_buf);
|
||||
let our_mac = hmac.finalize().into_bytes();
|
||||
|
||||
if their_mac != our_mac[..10] {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"MAC verification failed",
|
||||
));
|
||||
}
|
||||
|
||||
plaintext_buf.clear();
|
||||
plaintext_buf.extend_from_slice(ciphertext_buf);
|
||||
ctr.apply_keystream(plaintext_buf);
|
||||
|
||||
BackupFrame::decode(&plaintext_buf[..])
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||
}
|
||||
|
||||
fn decrypt_frame_payload<R: Read>(
|
||||
backup_file: &mut R,
|
||||
length: usize,
|
||||
hmac_key: &[u8],
|
||||
cipher_key: &[u8],
|
||||
initialisation_vector: &[u8],
|
||||
chunk_size: usize,
|
||||
) -> io::Result<Vec<u8>> {
|
||||
let mut hmac = <HmacSha256 as Mac>::new_from_slice(hmac_key)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid HMAC key"))?;
|
||||
Mac::update(&mut hmac, initialisation_vector);
|
||||
|
||||
let mut ctr =
|
||||
<Ctr32BE<Aes256> as KeyIvInit>::new_from_slices(cipher_key, initialisation_vector)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid CTR parameters"))?;
|
||||
|
||||
let mut decrypted_data = Vec::new();
|
||||
let mut remaining_length = length;
|
||||
|
||||
while remaining_length > 0 {
|
||||
let this_chunk_length = remaining_length.min(chunk_size);
|
||||
remaining_length -= this_chunk_length;
|
||||
|
||||
let mut ciphertext = vec![0u8; this_chunk_length];
|
||||
backup_file.read_exact(&mut ciphertext)?;
|
||||
Mac::update(&mut hmac, &ciphertext);
|
||||
|
||||
let mut decrypted_chunk = ciphertext;
|
||||
ctr.apply_keystream(&mut decrypted_chunk);
|
||||
decrypted_data.extend(decrypted_chunk);
|
||||
}
|
||||
|
||||
let mut their_mac = [0u8; 10];
|
||||
backup_file.read_exact(&mut their_mac)?;
|
||||
let our_mac = hmac.finalize().into_bytes();
|
||||
|
||||
if &their_mac != &our_mac[..10] {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Bad MAC found. Passphrase may be incorrect or file corrupted or incompatible.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(decrypted_data)
|
||||
}
|
||||
|
||||
fn decrypt_backup<R>(
|
||||
backup_file: &mut R,
|
||||
passphrase: &str,
|
||||
output_directory: &Path,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
let mut backup_file = BufReader::with_capacity(32 * 1024, backup_file);
|
||||
let total_size = backup_file.seek(SeekFrom::End(0))?;
|
||||
backup_file.seek(SeekFrom::Start(0))?;
|
||||
let mut last_percentage = 0;
|
||||
|
||||
let database_filename = output_directory.join("database.sqlite");
|
||||
let preferences_filename = output_directory.join("preferences.json");
|
||||
let key_value_filename = output_directory.join("key_value.json");
|
||||
let attachments_directory = output_directory.join("attachments");
|
||||
let stickers_directory = output_directory.join("stickers");
|
||||
let avatars_directory = output_directory.join("avatars");
|
||||
|
||||
for directory in [
|
||||
output_directory,
|
||||
&attachments_directory,
|
||||
&stickers_directory,
|
||||
&avatars_directory,
|
||||
] {
|
||||
fs::create_dir_all(directory)?;
|
||||
}
|
||||
|
||||
if database_filename.exists() {
|
||||
fs::remove_file(&database_filename)?;
|
||||
}
|
||||
|
||||
let mut db_connection = Connection::open(&database_filename).map_err(to_io_error)?;
|
||||
|
||||
db_connection
|
||||
.execute_batch(
|
||||
"PRAGMA journal_mode = WAL;
|
||||
PRAGMA synchronous = NORMAL;
|
||||
PRAGMA temp_store = MEMORY;
|
||||
PRAGMA mmap_size = 30000000000;
|
||||
PRAGMA page_size = 4096;",
|
||||
)
|
||||
.map_err(to_io_error)?;
|
||||
|
||||
let tx = db_connection
|
||||
.transaction_with_behavior(TransactionBehavior::Immediate)
|
||||
.map_err(to_io_error)?;
|
||||
|
||||
let mut preferences: HashMap<String, HashMap<String, HashMap<String, serde_json::Value>>> =
|
||||
HashMap::new();
|
||||
let mut key_values: HashMap<String, HashMap<String, serde_json::Value>> = HashMap::new();
|
||||
|
||||
let header_data = read_backup_header(&mut backup_file)?;
|
||||
let keys = derive_keys(passphrase, &header_data.salt)?;
|
||||
let mut initialisation_vector = header_data.initialisation_vector.clone();
|
||||
|
||||
let mut ciphertext: Vec<u8> = Vec::with_capacity(1024 * 1024);
|
||||
let mut plaintext: Vec<u8> = Vec::with_capacity(1024 * 1024);
|
||||
|
||||
loop {
|
||||
let current_position = backup_file.stream_position()?;
|
||||
let percentage = ((current_position as f64 / total_size as f64) * 100.0) as u32;
|
||||
if percentage != last_percentage {
|
||||
eprintln!("Progress: {}%", percentage);
|
||||
last_percentage = percentage;
|
||||
}
|
||||
|
||||
let backup_frame = decrypt_frame(
|
||||
&mut backup_file,
|
||||
&keys.hmac_key,
|
||||
&keys.cipher_key,
|
||||
&initialisation_vector,
|
||||
header_data.version,
|
||||
&mut ciphertext,
|
||||
&mut plaintext,
|
||||
)?;
|
||||
|
||||
initialisation_vector = increment_initialisation_vector(&initialisation_vector);
|
||||
|
||||
if backup_frame.end.unwrap_or(false) {
|
||||
break;
|
||||
} else if let Some(version) = backup_frame.version {
|
||||
if let Some(ver_num) = version.version {
|
||||
let pragma_sql = format!("PRAGMA user_version = {}", ver_num);
|
||||
tx.execute_batch(&pragma_sql).map_err(to_io_error)?;
|
||||
}
|
||||
} else if let Some(statement) = backup_frame.statement {
|
||||
if let Some(sql) = statement.statement {
|
||||
if !sql.to_lowercase().starts_with("create table sqlite_")
|
||||
&& !sql.contains("sms_fts_")
|
||||
&& !sql.contains("mms_fts_")
|
||||
{
|
||||
let params: Vec<Option<Box<dyn rusqlite::ToSql>>> = statement
|
||||
.parameters
|
||||
.iter()
|
||||
.map(parameter_to_native_type)
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(to_io_error)?;
|
||||
|
||||
tx.execute(
|
||||
&sql,
|
||||
rusqlite::params_from_iter(params.iter().map(|p| p.as_deref())),
|
||||
)
|
||||
.map_err(to_io_error)?;
|
||||
}
|
||||
}
|
||||
} else if let Some(preference) = backup_frame.preference {
|
||||
let value_dict = preferences
|
||||
.entry(preference.file.unwrap_or_default())
|
||||
.or_default()
|
||||
.entry(preference.key.unwrap_or_default())
|
||||
.or_default();
|
||||
|
||||
if let Some(value) = preference.value {
|
||||
value_dict.insert("value".to_string(), serde_json::Value::String(value));
|
||||
}
|
||||
if let Some(boolean_value) = preference.boolean_value {
|
||||
value_dict.insert(
|
||||
"booleanValue".to_string(),
|
||||
serde_json::Value::Bool(boolean_value),
|
||||
);
|
||||
}
|
||||
if preference.is_string_set_value.unwrap_or(false) {
|
||||
value_dict.insert(
|
||||
"stringSetValue".to_string(),
|
||||
serde_json::Value::Array(
|
||||
preference
|
||||
.string_set_value
|
||||
.into_iter()
|
||||
.map(serde_json::Value::String)
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if let Some(key_value) = backup_frame.key_value {
|
||||
let value_dict = key_values
|
||||
.entry(key_value.key.unwrap_or_default())
|
||||
.or_default();
|
||||
|
||||
if let Some(boolean_value) = key_value.boolean_value {
|
||||
value_dict.insert(
|
||||
"booleanValue".to_string(),
|
||||
serde_json::Value::Bool(boolean_value),
|
||||
);
|
||||
}
|
||||
if let Some(float_value) = key_value.float_value {
|
||||
value_dict.insert(
|
||||
"floatValue".to_string(),
|
||||
serde_json::Value::Number(
|
||||
serde_json::Number::from_f64(float_value.into()).unwrap(),
|
||||
),
|
||||
);
|
||||
}
|
||||
if let Some(integer_value) = key_value.integer_value {
|
||||
value_dict.insert(
|
||||
"integerValue".to_string(),
|
||||
serde_json::Value::Number(integer_value.into()),
|
||||
);
|
||||
}
|
||||
if let Some(long_value) = key_value.long_value {
|
||||
value_dict.insert(
|
||||
"longValue".to_string(),
|
||||
serde_json::Value::Number(long_value.into()),
|
||||
);
|
||||
}
|
||||
if let Some(string_value) = key_value.string_value {
|
||||
value_dict.insert(
|
||||
"stringValue".to_string(),
|
||||
serde_json::Value::String(string_value),
|
||||
);
|
||||
}
|
||||
if let Some(blob_value) = key_value.blob_value {
|
||||
value_dict.insert(
|
||||
"blobValueBase64".to_string(),
|
||||
serde_json::Value::String(BASE64_STANDARD.encode(&blob_value)),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let (filename, length) = if let Some(attachment) = backup_frame.attachment {
|
||||
(
|
||||
attachments_directory.join(format!("{}.bin", attachment.row_id.unwrap_or(0))),
|
||||
attachment.length.unwrap_or(0),
|
||||
)
|
||||
} else if let Some(sticker) = backup_frame.sticker {
|
||||
(
|
||||
stickers_directory.join(format!("{}.bin", sticker.row_id.unwrap_or(0))),
|
||||
sticker.length.unwrap_or(0),
|
||||
)
|
||||
} else if let Some(avatar) = backup_frame.avatar {
|
||||
(
|
||||
avatars_directory
|
||||
.join(format!("{}.bin", avatar.recipient_id.unwrap_or_default())),
|
||||
avatar.length.unwrap_or(0),
|
||||
)
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Invalid field type found",
|
||||
));
|
||||
};
|
||||
|
||||
let mut file = File::create(&filename)?;
|
||||
let payload = decrypt_frame_payload(
|
||||
&mut backup_file,
|
||||
length as usize,
|
||||
&keys.hmac_key,
|
||||
&keys.cipher_key,
|
||||
&initialisation_vector,
|
||||
8 * 1024,
|
||||
)?;
|
||||
file.write_all(&payload)?;
|
||||
initialisation_vector = increment_initialisation_vector(&initialisation_vector);
|
||||
}
|
||||
}
|
||||
|
||||
tx.commit().map_err(to_io_error)?;
|
||||
|
||||
let mut preferences_file = File::create(preferences_filename)?;
|
||||
serde_json::to_writer_pretty(&mut preferences_file, &preferences)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
let mut key_values_file = File::create(key_value_filename)?;
|
||||
serde_json::to_writer_pretty(&mut key_values_file, &key_values)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
if args.len() < 2 {
|
||||
eprintln!(
|
||||
"Usage: {} <backup_file> [output_directory] [-p PASSPHRASE]",
|
||||
args[0]
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let backup_file_path = &args[1];
|
||||
let output_directory = if args.len() > 2 {
|
||||
Path::new(&args[2]).to_path_buf()
|
||||
} else {
|
||||
Path::new("./out").to_path_buf()
|
||||
};
|
||||
|
||||
let passphrase = if let Some(pos) = args.iter().position(|arg| arg == "-p") {
|
||||
args.get(pos + 1).expect("Passphrase not provided").clone()
|
||||
} else {
|
||||
rpassword::prompt_password("Backup passphrase: ").expect("Failed to read passphrase")
|
||||
};
|
||||
|
||||
let mut backup_file = File::open(backup_file_path)?;
|
||||
decrypt_backup(&mut backup_file, &passphrase, &output_directory)?;
|
||||
|
||||
Ok(())
|
||||
}
|
2705
src/proto/Backups.rs
Normal file
2705
src/proto/Backups.rs
Normal file
File diff suppressed because it is too large
Load diff
3
src/proto/mod.rs
Normal file
3
src/proto/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
// @generated
|
||||
|
||||
pub mod Backups;
|
Loading…
Add table
Add a link
Reference in a new issue