From c7072159fda7fa9052c53bda8eb03ed84e81a904 Mon Sep 17 00:00:00 2001 From: Samuel Date: Sat, 21 Dec 2024 15:12:38 +0100 Subject: [PATCH] Initial commit --- .gitignore | 2 + Cargo.lock | 707 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 26 ++ build.rs | 27 ++ src/lib.rs | 440 +++++++++++++++++++++++++++++ src/signal.proto | 82 ++++++ 6 files changed, 1284 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 src/lib.rs create mode 100644 src/signal.proto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4113ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/pkg \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..24146bf --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7aca7d7 --- /dev/null +++ b/Cargo.toml @@ -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" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..0abc775 --- /dev/null +++ b/build.rs @@ -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(); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a825de3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,440 @@ +pub(crate) mod bytes_serde { + use prost::bytes::Bytes; + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn serialize(bytes: &Option, serializer: S) -> Result + where + S: Serializer, + { + match bytes { + Some(b) => serializer.serialize_bytes(b), + None => serializer.serialize_none(), + } + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + Option::>::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; + +// Keep the original protobuf module +pub mod signal { + include!(concat!(env!("OUT_DIR"), "/signal.rs")); +} + +#[wasm_bindgen] +pub struct DecryptionResult { + database_bytes: Vec, + preferences: String, + key_values: String, +} + +#[wasm_bindgen] +impl DecryptionResult { + #[wasm_bindgen(getter)] + pub fn database_bytes(&self) -> Vec { + 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 { + 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, + salt: Vec, + version: Option, +} + +struct Keys { + cipher_key: Vec, + hmac_key: Vec, +} + +fn derive_keys(passphrase: &str, salt: &[u8]) -> Result { + 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::::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 { + 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 { + 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, + ciphertext_buf: &mut Vec, + plaintext_buf: &mut Vec, +) -> Result { + let mut hmac = ::new_from_slice(hmac_key) + .map_err(|_| JsValue::from_str("Invalid HMAC key"))?; + + let mut ctr = + 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, JsValue> { + let mut hmac = ::new_from_slice(hmac_key) + .map_err(|_| JsValue::from_str("Invalid HMAC key"))?; + Mac::update(&mut hmac, initialisation_vector); + + let mut ctr = + 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 { + 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>, + > = std::collections::HashMap::new(); + let mut key_values: std::collections::HashMap< + String, + std::collections::HashMap, + > = 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 = Vec::with_capacity(1024 * 1024); + let mut plaintext: Vec = 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)))?, + }) +} diff --git a/src/signal.proto b/src/signal.proto new file mode 100644 index 0000000..492c9d6 --- /dev/null +++ b/src/signal.proto @@ -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; +}