Initial commit

This commit is contained in:
Samuel 2024-12-21 15:12:38 +01:00
commit c7072159fd
No known key found for this signature in database
6 changed files with 1284 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
/pkg

707
Cargo.lock generated Normal file
View file

@ -0,0 +1,707 @@
# 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 = "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.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[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 = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[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",
]
[[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.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[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 = "home"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
dependencies = [
"windows-sys",
]
[[package]]
name = "indexmap"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [
"equivalent",
"hashbrown",
]
[[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.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "js-sys"
version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[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.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
[[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 = "prettyplease"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
dependencies = [
"proc-macro2",
"syn 1.0.109",
]
[[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.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-build"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
dependencies = [
"bytes",
"heck",
"itertools",
"lazy_static",
"log",
"multimap",
"petgraph",
"prettyplease",
"prost",
"prost-types",
"regex",
"syn 1.0.109",
"tempfile",
"which",
]
[[package]]
name = "prost-derive"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "prost-types"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
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 = "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",
]
[[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_bytes"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a"
dependencies = [
"serde",
]
[[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 2.0.90",
]
[[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-wasm"
version = "0.1.0"
dependencies = [
"aes",
"base64",
"ctr",
"hkdf",
"hmac",
"js-sys",
"prost",
"prost-build",
"prost-types",
"serde",
"serde_bytes",
"serde_json",
"sha2",
"wasm-bindgen",
"web-sys",
]
[[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.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",
]
[[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 = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasm-bindgen"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
dependencies = [
"cfg-if",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn 2.0.90",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
[[package]]
name = "web-sys"
version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[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.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[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.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[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.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

26
Cargo.toml Normal file
View file

@ -0,0 +1,26 @@
[package]
name = "signal-decrypt-backup-wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["console"] }
aes = "0.8"
ctr = "0.9"
hkdf = "0.12"
hmac = "0.12"
sha2 = "0.10"
base64 = "0.21"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
prost = "0.11"
prost-types = "0.11"
serde_bytes = "0.11.15"
[build-dependencies]
prost-build = "0.11"

27
build.rs Normal file
View file

@ -0,0 +1,27 @@
fn main() {
let mut config = prost_build::Config::new();
config.bytes(&["."]);
config.type_attribute(".", "#[derive(::serde::Serialize, ::serde::Deserialize)]");
// For bytes fields, use with = "crate::bytes_serde"
config.field_attribute(
".signal.SqlStatement.SqlParameter.blobParameter",
"#[serde(with = \"crate::bytes_serde\")]",
);
config.field_attribute(
".signal.Header.iv",
"#[serde(with = \"crate::bytes_serde\")]",
);
config.field_attribute(
".signal.Header.salt",
"#[serde(with = \"crate::bytes_serde\")]",
);
config.field_attribute(
".signal.KeyValue.blobValue",
"#[serde(with = \"crate::bytes_serde\")]",
);
config
.compile_protos(&["src/signal.proto"], &["src/"])
.unwrap();
}

440
src/lib.rs Normal file
View file

@ -0,0 +1,440 @@
pub(crate) mod bytes_serde {
use prost::bytes::Bytes;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(bytes: &Option<Bytes>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match bytes {
Some(b) => serializer.serialize_bytes(b),
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Bytes>, D::Error>
where
D: Deserializer<'de>,
{
Option::<Vec<u8>>::deserialize(deserializer).map(|opt| opt.map(|vec| Bytes::from(vec)))
}
}
use aes::Aes256;
use ctr::cipher::{KeyIvInit, StreamCipher};
use ctr::Ctr32BE;
use hkdf::Hkdf;
use hmac::{Hmac, Mac};
use js_sys::Function;
use prost::Message;
use sha2::{Digest, Sha256, Sha512};
use std::io::{self, Read};
use wasm_bindgen::prelude::*;
type HmacSha256 = Hmac<Sha256>;
// Keep the original protobuf module
pub mod signal {
include!(concat!(env!("OUT_DIR"), "/signal.rs"));
}
#[wasm_bindgen]
pub struct DecryptionResult {
database_bytes: Vec<u8>,
preferences: String,
key_values: String,
}
#[wasm_bindgen]
impl DecryptionResult {
#[wasm_bindgen(getter)]
pub fn database_bytes(&self) -> Vec<u8> {
self.database_bytes.clone()
}
#[wasm_bindgen(getter)]
pub fn preferences(&self) -> String {
self.preferences.clone()
}
#[wasm_bindgen(getter)]
pub fn key_values(&self) -> String {
self.key_values.clone()
}
}
// Helper struct to read from byte slice
struct ByteReader<'a> {
data: &'a [u8],
position: usize,
}
impl<'a> ByteReader<'a> {
fn new(data: &'a [u8]) -> Self {
ByteReader { data, position: 0 }
}
}
impl<'a> Read for ByteReader<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let available = self.data.len() - self.position;
let amount = buf.len().min(available);
if amount == 0 {
return Ok(0);
}
buf[..amount].copy_from_slice(&self.data[self.position..self.position + amount]);
self.position += amount;
Ok(amount)
}
}
// Keep the original structs but without Debug derive
struct HeaderData {
initialisation_vector: Vec<u8>,
salt: Vec<u8>,
version: Option<u32>,
}
struct Keys {
cipher_key: Vec<u8>,
hmac_key: Vec<u8>,
}
fn derive_keys(passphrase: &str, salt: &[u8]) -> Result<Keys, JsValue> {
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(|_| JsValue::from_str("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 read_backup_header(reader: &mut ByteReader) -> Result<HeaderData, JsValue> {
let mut length_bytes = [0u8; 4];
reader
.read_exact(&mut length_bytes)
.map_err(|e| JsValue::from_str(&format!("Failed to read header length: {}", e)))?;
let length = u32::from_be_bytes(length_bytes);
let mut backup_frame_bytes = vec![0u8; length as usize];
reader
.read_exact(&mut backup_frame_bytes)
.map_err(|e| JsValue::from_str(&format!("Failed to read backup frame: {}", e)))?;
let backup_frame = signal::BackupFrame::decode(&backup_frame_bytes[..])
.map_err(|e| JsValue::from_str(&format!("Failed to decode backup frame: {}", e)))?;
let header = backup_frame
.header
.ok_or_else(|| JsValue::from_str("Missing header"))?;
Ok(HeaderData {
initialisation_vector: header.iv.unwrap().to_vec(),
salt: header.salt.unwrap().to_vec(),
version: header.version,
})
}
fn decrypt_frame(
reader: &mut ByteReader,
hmac_key: &[u8],
cipher_key: &[u8],
initialisation_vector: &[u8],
header_version: Option<u32>,
ciphertext_buf: &mut Vec<u8>,
plaintext_buf: &mut Vec<u8>,
) -> Result<signal::BackupFrame, JsValue> {
let mut hmac = <HmacSha256 as Mac>::new_from_slice(hmac_key)
.map_err(|_| JsValue::from_str("Invalid HMAC key"))?;
let mut ctr =
<Ctr32BE<Aes256> as KeyIvInit>::new_from_slices(cipher_key, initialisation_vector)
.map_err(|_| JsValue::from_str("Invalid CTR parameters"))?;
let length = match header_version {
None => {
let mut length_bytes = [0u8; 4];
reader
.read_exact(&mut length_bytes)
.map_err(|e| JsValue::from_str(&format!("Failed to read length: {}", e)))?;
u32::from_be_bytes(length_bytes)
}
Some(1) => {
let mut encrypted_length = [0u8; 4];
reader.read_exact(&mut encrypted_length).map_err(|e| {
JsValue::from_str(&format!("Failed to read encrypted length: {}", e))
})?;
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(JsValue::from_str(&format!("Unsupported version: {}", v))),
};
if length < 10 {
return Err(JsValue::from_str("Frame too short"));
}
ciphertext_buf.clear();
ciphertext_buf.resize((length - 10) as usize, 0);
reader
.read_exact(ciphertext_buf)
.map_err(|e| JsValue::from_str(&format!("Failed to read ciphertext: {}", e)))?;
let mut their_mac = [0u8; 10];
reader
.read_exact(&mut their_mac)
.map_err(|e| JsValue::from_str(&format!("Failed to read MAC: {}", e)))?;
Mac::update(&mut hmac, ciphertext_buf);
let our_mac = hmac.finalize().into_bytes();
if their_mac != our_mac[..10] {
return Err(JsValue::from_str("MAC verification failed"));
}
plaintext_buf.clear();
plaintext_buf.extend_from_slice(ciphertext_buf);
ctr.apply_keystream(plaintext_buf);
signal::BackupFrame::decode(&plaintext_buf[..])
.map_err(|e| JsValue::from_str(&format!("Failed to decode frame: {}", e)))
}
// this would be used for attachments, stickers, avatars, which we might need later
fn decrypt_frame_payload(
reader: &mut ByteReader,
length: usize,
hmac_key: &[u8],
cipher_key: &[u8],
initialisation_vector: &[u8],
chunk_size: usize,
) -> Result<Vec<u8>, JsValue> {
let mut hmac = <HmacSha256 as Mac>::new_from_slice(hmac_key)
.map_err(|_| JsValue::from_str("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(|_| JsValue::from_str("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];
reader
.read_exact(&mut ciphertext)
.map_err(|e| JsValue::from_str(&format!("Failed to read chunk: {}", e)))?;
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];
reader
.read_exact(&mut their_mac)
.map_err(|e| JsValue::from_str(&format!("Failed to read MAC: {}", e)))?;
let our_mac = hmac.finalize().into_bytes();
if &their_mac != &our_mac[..10] {
return Err(JsValue::from_str(
"Bad MAC found. Passphrase may be incorrect or file corrupted or incompatible.",
));
}
Ok(decrypted_data)
}
#[wasm_bindgen]
pub fn decrypt_backup(
backup_data: &[u8],
passphrase: &str,
progress_callback: &Function,
) -> Result<DecryptionResult, JsValue> {
let mut reader = ByteReader::new(backup_data);
let total_size = backup_data.len();
let mut last_percentage = 0;
// Set up collections for results
let mut database_bytes = Vec::new();
let mut preferences: std::collections::HashMap<
String,
std::collections::HashMap<String, std::collections::HashMap<String, serde_json::Value>>,
> = std::collections::HashMap::new();
let mut key_values: std::collections::HashMap<
String,
std::collections::HashMap<String, serde_json::Value>,
> = std::collections::HashMap::new();
// Read header and derive keys
let header_data = read_backup_header(&mut reader)?;
let keys = derive_keys(passphrase, &header_data.salt)?;
let mut initialisation_vector = header_data.initialisation_vector.clone();
// Pre-allocate buffers for frame decryption
let mut ciphertext: Vec<u8> = Vec::with_capacity(1024 * 1024);
let mut plaintext: Vec<u8> = Vec::with_capacity(1024 * 1024);
// Main decryption loop
loop {
// Update progress
let current_position = reader.position;
let percentage = ((current_position as f64 / total_size as f64) * 100.0) as u32;
if percentage != last_percentage {
progress_callback
.call1(&JsValue::NULL, &JsValue::from_f64(percentage as f64))
.map_err(|e| JsValue::from_str(&format!("Failed to report progress: {:?}", e)))?;
last_percentage = percentage;
}
let backup_frame = decrypt_frame(
&mut reader,
&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(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_")
{
// Store SQL statements and parameters for database reconstruction
database_bytes.extend_from_slice(sql.as_bytes());
database_bytes.push(b';');
}
}
} 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::Engine::encode(
&base64::engine::general_purpose::STANDARD,
&blob_value,
)),
);
}
}
// Note: We're skipping attachments, stickers, and avatars for now
}
// Final progress update
progress_callback
.call1(&JsValue::NULL, &JsValue::from_f64(100.0))
.map_err(|e| JsValue::from_str(&format!("Failed to report final progress: {:?}", e)))?;
Ok(DecryptionResult {
database_bytes,
preferences: serde_json::to_string(&preferences)
.map_err(|e| JsValue::from_str(&format!("Failed to serialize preferences: {}", e)))?,
key_values: serde_json::to_string(&key_values)
.map_err(|e| JsValue::from_str(&format!("Failed to serialize key_values: {}", e)))?,
})
}

82
src/signal.proto Normal file
View 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;
}