diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ecbd5bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +.DS_Store diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ec6e916 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,748 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aes" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aes-soft" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aesni" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "block-cipher-trait" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bstr" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bumpalo" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cast" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "criterion" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion-plot 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", + "tinytemplate 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "criterion-plot" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bstr 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hermit-abi" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex-literal" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex-literal-impl" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "js-sys" +version = "0.3.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wasm-bindgen 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memoffset" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "oorandom" +version = "11.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "plotters" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "js-sys 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro-hack" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "1.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tinytemplate" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasm-bindgen" +version = "0.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "web-sys" +version = "0.3.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "js-sys 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "xts-mode" +version = "0.1.0" +dependencies = [ + "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" +"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" +"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" +"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +"checksum bstr 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41" +"checksum bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum criterion 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63f696897c88b57f4ffe3c69d8e1a0613c7d0e6c4833363c8560fbde9c47b966" +"checksum criterion-plot 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ddeaf7989f00f2e1d871a26a110f3ed713632feac17f65f03ca938c542618b60" +"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +"checksum csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" +"checksum csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +"checksum hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +"checksum hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" +"checksum hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d" +"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +"checksum js-sys 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "0b823ebafcee1632403f2782d28728aab353f7881547a700043ef455c078326f" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +"checksum memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +"checksum oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" +"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +"checksum regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" +"checksum regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +"checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +"checksum serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)" = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" +"checksum syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum tinytemplate 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "45e4bc5ac99433e0dcb8b9f309dd271a165ae37dde129b9e0ce1bfdd8bfe4891" +"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +"checksum wasm-bindgen 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)" = "f56e97dbea16d5f56549d6c8ea7f36efb6be98507308650c1a5970574b3941b9" +"checksum wasm-bindgen-backend 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75d4f3f9b81dfc7d66b955876b325b20e8affd4ce8d93e51162626fc5faadb" +"checksum wasm-bindgen-macro 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)" = "9dcde4b19e863521c1e78ecf100935132396291b09ae0ae2e155ff84ccbe9736" +"checksum wasm-bindgen-macro-support 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)" = "13d87d2b117af2b86472402d70f7eb173bbe166beb5e727f3c0bebecdf356504" +"checksum wasm-bindgen-shared 0.2.61 (registry+https://github.com/rust-lang/crates.io-index)" = "71f77b681efd0bca6f8ea356cdc2e497538b41d3e2a02afed18ce8f022231d29" +"checksum web-sys 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "07c5819dc39222a788ca169a81aef7d02739019256300534f493b5747d5469c2" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c130e90 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "xts-mode" +version = "0.1.0" +authors = ["Aphek "] +edition = "2018" +license = "MIT" + +readme = "README.md" +description = "XTS block mode implementation in rust" +repository = "https://github.com/pheki/xts-mode" +documentation = "https://docs.rs/xts-mode" +keywords = ["encryption", "xts"] + +[dependencies] +block-cipher-trait = "0.6" +byteorder = "1" + +[dev-dependencies] +# For tests +hex-literal = "0.2" +aes = "0.3" + +# For benchmarks +criterion = "0.3" +rand = "0.7" + +[[bench]] +name = "encryption" +harness = false diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..4de92ce --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2020 Aphek Brito + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fd87558 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +# xts-mode + +XTS implementation in rust. Currently only 128-bit (16-byte) algorithms are supported, if you +require other sizes, please open an issue. + +For better AES performance, it is recommended to use the `aes` crate and enable the aes feature in +the compiler (see [reference](https://doc.rust-lang.org/reference/attributes/codegen.html#the-target_feature-attribute) +and [aesni](https://docs.rs/aesni/)). + +## Examples: + +Encrypting and decrypting multiple sectors at a time: +```rust +use aes::Aes128; +use aes::block_cipher_trait::BlockCipher; +use xts_mode::{Xts128, get_tweak_default}; + +// Load the encryption key +let key = [1; 32]; +let plaintext = [5; 0x400]; +// Load the data to be encrypted +let mut buffer = plaintext.to_owned(); + +let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); +let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + +let xts = Xts128::::new(cipher_1, cipher_2); + +// Encrypt data in the buffer +xts.encrypt_area(&mut buffer, 0x200, 0, get_tweak_default); + +// Decrypt data in the buffer +xts.decrypt_area(&mut buffer, 0x200, 0, get_tweak_default); + +assert_eq!(&buffer[..], &plaintext[..]); +``` + +Encrypting and decrypting a single sector: +```rust +use aes::Aes128; +use aes::block_cipher_trait::BlockCipher; +use xts_mode::{Xts128, get_tweak_default}; + +// Load the encryption key +let key = [1; 32]; +let plaintext = [5; 0x200]; +// Load the data to be encrypted +let mut buffer = plaintext.to_owned(); + +let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); +let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + +let xts = Xts128::::new(cipher_1, cipher_2); + +let tweak = get_tweak_default(0); // 0 is the sector index + +// Encrypt data in the buffer +xts.encrypt_sector(&mut buffer, tweak); + +// Decrypt data in the buffer +xts.decrypt_sector(&mut buffer, tweak); + +assert_eq!(&buffer[..], &plaintext[..]); +``` + +Decrypting a [NCA](https://switchbrew.org/wiki/NCA_Format) (nintendo content archive) header: +```rust +use aes::Aes128; +use aes::block_cipher_trait::BlockCipher; +use xts_mode::Xts128; + +pub fn get_nintendo_tweak(sector_index: u128) -> [u8; 0x10] { + sector_index.to_be_bytes() +} + +// Load the header key +let header_key = &[0; 0x20]; + +// Read into buffer header to be decrypted +let mut buffer = vec![0; 0xC00]; + +let cipher_1 = Aes128::new_varkey(&header_key[..0x10]).unwrap(); +let cipher_2 = Aes128::new_varkey(&header_key[0x10..]).unwrap(); + +let mut xts = Xts128::new(cipher_1, cipher_2); + +// Decrypt the first 0x400 bytes of the header in 0x200 sections +xts.decrypt_area(&mut buffer[0..0x400], 0x200, 0, get_nintendo_tweak); + +let magic = &buffer[0x200..0x204]; +assert_eq!(magic, b"NCA3"); // In older NCA versions the section index used in header encryption was different + +// Decrypt the rest of the header +xts.decrypt_area(&mut buffer[0x400..0xC00], 0x200, 2, get_nintendo_tweak); +``` diff --git a/benches/encryption.rs b/benches/encryption.rs new file mode 100644 index 0000000..3ad5df7 --- /dev/null +++ b/benches/encryption.rs @@ -0,0 +1,95 @@ +#[macro_use] +extern crate criterion; + +use xts_mode::{get_tweak_default, Xts128}; + +use aes::Aes128; +use block_cipher_trait::BlockCipher; +use criterion::Criterion; +use rand::RngCore; + +fn encryption_128(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("xts 128"); + + let mut rng = rand::thread_rng(); + + let mut key = [0; 32]; + rng.fill_bytes(&mut key); + + let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); + let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + + let xts = Xts128::::new(cipher_1, cipher_2); + + let mut buffer = Vec::new(); + + buffer.resize(16, 0); + rng.fill_bytes(&mut buffer); + assert_eq!(buffer.len(), 16); + group.bench_function("sector size 16 B", |benchmark| { + let mut i = 0; + benchmark.iter(|| { + let tweak = get_tweak_default(i); + xts.encrypt_sector(&mut buffer, tweak); + i = i.wrapping_add(1); + }) + }); + + buffer.resize(64, 0); + rng.fill_bytes(&mut buffer); + assert_eq!(buffer.len(), 64); + group.bench_function("sector size 64 B", |benchmark| { + let mut i = 0; + benchmark.iter(|| { + let tweak = get_tweak_default(i); + xts.encrypt_sector(&mut buffer, tweak); + i = i.wrapping_add(1); + }) + }); + + buffer.resize(256, 0); + rng.fill_bytes(&mut buffer); + assert_eq!(buffer.len(), 256); + group.bench_function("sector size 256 B", |benchmark| { + let mut i = 0; + benchmark.iter(|| { + let tweak = get_tweak_default(i); + xts.encrypt_sector(&mut buffer, tweak); + i = i.wrapping_add(1); + }) + }); + + buffer.resize(1024, 0); + rng.fill_bytes(&mut buffer); + assert_eq!(buffer.len(), 1024); + group.bench_function("sector size 1024 B", |benchmark| { + benchmark.iter(|| { + xts.encrypt_area(&mut buffer, 0x200, 0, get_tweak_default); + }) + }); + + buffer.resize(8192, 0); + rng.fill_bytes(&mut buffer); + assert_eq!(buffer.len(), 8192); + group.bench_function("sector size 8192 B", |benchmark| { + benchmark.iter(|| { + xts.encrypt_area(&mut buffer, 0x200, 0, get_tweak_default); + }) + }); + + buffer.resize(16384, 0); + rng.fill_bytes(&mut buffer); + assert_eq!(buffer.len(), 16384); + group.bench_function("sector size 16384 B", |benchmark| { + // let mut i = 0; + benchmark.iter(|| { + // let tweak = get_tweak_default(i); + xts.encrypt_area(&mut buffer, 0x200, 0, get_tweak_default); + // xts.encrypt_sector(&mut buffer, tweak); + // i = i.wrapping_add(1); + }) + }); +} + +criterion_group!(benches, encryption_128); +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..c2d6102 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,366 @@ +/*! +XTS implementation in rust. Currently only 128-bit (16-byte) algorithms are supported, if you +require other sizes, please open an issue. + +For better AES performance, it is recommended to use the `aes` crate and enable the aes feature in +the compiler (see [reference](https://doc.rust-lang.org/reference/attributes/codegen.html#the-target_feature-attribute) +and [aesni](https://docs.rs/aesni/)). + +# Examples: + +Encrypting and decrypting multiple sectors at a time: +``` +use aes::Aes128; +use aes::block_cipher_trait::BlockCipher; +use xts_mode::{Xts128, get_tweak_default}; + +// Load the encryption key +let key = [1; 32]; +let plaintext = [5; 0x400]; +// Load the data to be encrypted +let mut buffer = plaintext.to_owned(); + +let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); +let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + +let xts = Xts128::::new(cipher_1, cipher_2); + +// Encrypt data in the buffer +xts.encrypt_area(&mut buffer, 0x200, 0, get_tweak_default); + +// Decrypt data in the buffer +xts.decrypt_area(&mut buffer, 0x200, 0, get_tweak_default); + +assert_eq!(&buffer[..], &plaintext[..]); +``` + +Encrypting and decrypting a single sector: +``` +use aes::Aes128; +use aes::block_cipher_trait::BlockCipher; +use xts_mode::{Xts128, get_tweak_default}; + +// Load the encryption key +let key = [1; 32]; +let plaintext = [5; 0x200]; +// Load the data to be encrypted +let mut buffer = plaintext.to_owned(); + +let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); +let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + +let xts = Xts128::::new(cipher_1, cipher_2); + +let tweak = get_tweak_default(0); // 0 is the sector index + +// Encrypt data in the buffer +xts.encrypt_sector(&mut buffer, tweak); + +// Decrypt data in the buffer +xts.decrypt_sector(&mut buffer, tweak); + +assert_eq!(&buffer[..], &plaintext[..]); +``` + +Decrypting a [NCA](https://switchbrew.org/wiki/NCA_Format) (nintendo content archive) header: +``` +use aes::Aes128; +use aes::block_cipher_trait::BlockCipher; +use xts_mode::Xts128; + +pub fn get_nintendo_tweak(sector_index: u128) -> [u8; 0x10] { + sector_index.to_be_bytes() +} + +// Load the header key +let header_key = &[0; 0x20]; + +// Read into buffer header to be decrypted +let mut buffer = vec![0; 0xC00]; + +let cipher_1 = Aes128::new_varkey(&header_key[..0x10]).unwrap(); +let cipher_2 = Aes128::new_varkey(&header_key[0x10..]).unwrap(); + +let mut xts = Xts128::new(cipher_1, cipher_2); + +// Decrypt the first 0x400 bytes of the header in 0x200 sections +xts.decrypt_area(&mut buffer[0..0x400], 0x200, 0, get_nintendo_tweak); + +let magic = &buffer[0x200..0x204]; +# if false { // Removed from tests as we're not decrypting an actual NCA +assert_eq!(magic, b"NCA3"); // In older NCA versions the section index used in header encryption was different +# } + +// Decrypt the rest of the header +xts.decrypt_area(&mut buffer[0x400..0xC00], 0x200, 2, get_nintendo_tweak); +``` +*/ + +use std::convert::TryFrom; +use std::convert::TryInto; + +use block_cipher_trait::generic_array::typenum::Unsigned; +use block_cipher_trait::generic_array::GenericArray; +use block_cipher_trait::BlockCipher; + +use byteorder::{ByteOrder, LittleEndian}; + +/// Xts128 block cipher. Does not implement implement BlockMode due to XTS differences detailed +/// [here](https://github.com/RustCrypto/block-ciphers/issues/48#issuecomment-574440662). +pub struct Xts128 { + /// This cipher is actually used to encrypt the blocks. + cipher_1: C, + /// This cipher is used only to compute the tweak at each sector start. + cipher_2: C, +} + +impl Xts128 { + /// Creates a new Xts128 using two cipher instances: the first one's used to encrypt the blocks + /// and the second one to compute the tweak at the start of each sector. + /// + /// Usually both cipher's are the same algorithm and the key is stored as double sized + /// (256 bits for Aes128), and the key is split in half, the first half used for cipher_1 and + /// the other for cipher_2. + /// + /// If you require support for different cipher types, or block sizes different than 16 bytes, + /// open an issue. + pub fn new(cipher_1: C, cipher_2: C) -> Xts128 { + Xts128 { cipher_1, cipher_2 } + } + + /// Encrypts a single sector in place using the given tweak. + /// + /// # Panics + /// - If the block size is not 16 bytes. + /// - If there's less than a single block in the sector. + pub fn encrypt_sector(&self, sector: &mut [u8], mut tweak: [u8; 16]) { + assert_eq!( + ::BlockSize::to_usize(), + 128 / 8, + "Wrong block size" + ); + + assert!( + sector.len() >= 16, + "AES-XTS needs at least two blocks to perform stealing, or a single complete block" + ); + + let block_count = sector.len() / 16; + let need_stealing = sector.len() % 16 != 0; + + // Compute tweak + self.cipher_2 + .encrypt_block(GenericArray::from_mut_slice(&mut tweak)); + + let nosteal_block_count = if need_stealing { + block_count - 1 + } else { + block_count + }; + + for i in (0..sector.len()).step_by(16).take(nosteal_block_count) { + let block = &mut sector[i..i + 16]; + + xor(block, &tweak); + self.cipher_1 + .encrypt_block(GenericArray::from_mut_slice(block)); + xor(block, &tweak); + + tweak = galois_field_128_mul_le(tweak); + } + + if need_stealing { + let next_to_last_tweak = tweak; + let last_tweak = galois_field_128_mul_le(tweak); + + let remaining = sector.len() % 16; + let mut block: [u8; 16] = sector[16 * (block_count - 1)..16 * block_count] + .try_into() + .unwrap(); + + xor(&mut block, &next_to_last_tweak); + self.cipher_1 + .encrypt_block(GenericArray::from_mut_slice(&mut block)); + xor(&mut block, &next_to_last_tweak); + + let mut last_block = [0u8; 16]; + last_block[..remaining].copy_from_slice(§or[16 * block_count..]); + last_block[remaining..].copy_from_slice(&block[remaining..]); + + xor(&mut last_block, &last_tweak); + self.cipher_1 + .encrypt_block(GenericArray::from_mut_slice(&mut last_block)); + xor(&mut last_block, &last_tweak); + + sector[16 * (block_count - 1)..16 * block_count].copy_from_slice(&last_block); + sector[16 * block_count..].copy_from_slice(&block[..remaining]); + } + } + + /// Decrypts a single sector in place using the given tweak. + /// + /// # Panics + /// - If the block size is not 16 bytes. + /// - If there's less than a single block in the sector. + pub fn decrypt_sector(&self, sector: &mut [u8], mut tweak: [u8; 16]) { + assert_eq!( + ::BlockSize::to_usize(), + 128 / 8, + "Wrong block size" + ); + + assert!( + sector.len() >= 16, + "AES-XTS needs at least two blocks to perform stealing, or a single complete block" + ); + + let block_count = sector.len() / 16; + let need_stealing = sector.len() % 16 != 0; + + // Compute tweak + self.cipher_2 + .encrypt_block(GenericArray::from_mut_slice(&mut tweak)); + + let nosteal_block_count = if need_stealing { + block_count - 1 + } else { + block_count + }; + + for i in (0..sector.len()).step_by(16).take(nosteal_block_count) { + let block = &mut sector[i..i + 16]; + + xor(block, &tweak); + self.cipher_1 + .decrypt_block(GenericArray::from_mut_slice(block)); + xor(block, &tweak); + + tweak = galois_field_128_mul_le(tweak); + } + + if need_stealing { + let next_to_last_tweak = tweak; + let last_tweak = galois_field_128_mul_le(tweak); + + let remaining = sector.len() % 16; + let mut block: [u8; 16] = sector[16 * (block_count - 1)..16 * block_count] + .try_into() + .unwrap(); + + xor(&mut block, &last_tweak); + self.cipher_1 + .decrypt_block(GenericArray::from_mut_slice(&mut block)); + xor(&mut block, &last_tweak); + + let mut last_block = [0u8; 16]; + last_block[..remaining].copy_from_slice(§or[16 * block_count..]); + last_block[remaining..].copy_from_slice(&block[remaining..]); + + xor(&mut last_block, &next_to_last_tweak); + self.cipher_1 + .decrypt_block(GenericArray::from_mut_slice(&mut last_block)); + xor(&mut last_block, &next_to_last_tweak); + + sector[16 * (block_count - 1)..16 * block_count].copy_from_slice(&last_block); + sector[16 * block_count..].copy_from_slice(&block[..remaining]); + } + } + + /// Encrypts a whole area in place, usually consisting of multiple sectors. + /// + /// The tweak is computed at the start of every sector using get_tweak_fn(sector_index). + /// `get_tweak_fn` is usually `get_tweak_default`. + /// + /// # Panics + /// - If the block size is not 16 bytes. + /// - If there's less than a single block in the last sector. + pub fn encrypt_area( + &self, + area: &mut [u8], + sector_size: usize, + first_sector_index: u128, + get_tweak_fn: impl Fn(u128) -> [u8; 16], + ) { + let area_len = area.len(); + let mut chunks = area.chunks_exact_mut(sector_size); + for (i, chunk) in (&mut chunks).enumerate() { + let tweak = get_tweak_fn( + u128::try_from(i).expect("usize cannot be bigger than u128") + first_sector_index, + ); + self.encrypt_sector(chunk, tweak); + } + let remainder = chunks.into_remainder(); + + if !remainder.is_empty() { + let i = area_len / sector_size; + let tweak = get_tweak_fn( + u128::try_from(i).expect("usize cannot be bigger than u128") + first_sector_index, + ); + self.encrypt_sector(remainder, tweak); + } + } + + /// Decrypts a whole area in place, usually consisting of multiple sectors. + /// + /// The tweak is computed at the start of every sector using get_tweak_fn(sector_index). + /// `get_tweak_fn` is usually `get_tweak_default`. + /// + /// # Panics + /// - If the block size is not 16 bytes. + /// - If there's less than a single block in the last sector. + pub fn decrypt_area( + &self, + area: &mut [u8], + sector_size: usize, + first_sector_index: u128, + get_tweak_fn: impl Fn(u128) -> [u8; 16], + ) { + let area_len = area.len(); + let mut chunks = area.chunks_exact_mut(sector_size); + for (i, chunk) in (&mut chunks).enumerate() { + let tweak = get_tweak_fn( + u128::try_from(i).expect("usize cannot be bigger than u128") + first_sector_index, + ); + self.decrypt_sector(chunk, tweak); + } + let remainder = chunks.into_remainder(); + + if !remainder.is_empty() { + let i = area_len / sector_size; + let tweak = get_tweak_fn( + u128::try_from(i).expect("usize cannot be bigger than u128") + first_sector_index, + ); + self.decrypt_sector(remainder, tweak); + } + } +} + +/// This is the default way to get the tweak, which just consists of separating the sector_index +/// in an array of 16 bytes with little endian. May be called to get the tweak for every sector +/// or passed directly to `(en/de)crypt_area`, which will basically do that. +pub fn get_tweak_default(sector_index: u128) -> [u8; 16] { + sector_index.to_le_bytes() +} + +#[inline(always)] +fn xor(buf: &mut [u8], key: &[u8]) { + debug_assert_eq!(buf.len(), key.len()); + for (a, b) in buf.iter_mut().zip(key) { + *a ^= *b; + } +} + +fn galois_field_128_mul_le(tweak_source: [u8; 16]) -> [u8; 16] { + let low_bytes = u64::from_le_bytes(tweak_source[0..8].try_into().unwrap()); + let high_bytes = u64::from_le_bytes(tweak_source[8..16].try_into().unwrap()); + let new_low_bytes = (low_bytes << 1) ^ if (high_bytes >> 63) != 0 { 0x87 } else { 0x00 }; + let new_high_bytes = (low_bytes >> 63) | (high_bytes << 1); + + let mut tweak = [0; 16]; + + // byteorder used for performance, as it uses std::ptr::copy_nonoverlapping + LittleEndian::write_u64(&mut tweak[0..8], new_low_bytes); + LittleEndian::write_u64(&mut tweak[8..16], new_high_bytes); + + tweak +} diff --git a/test_files/random_no_remainder b/test_files/random_no_remainder new file mode 100644 index 0000000..44c59ec Binary files /dev/null and b/test_files/random_no_remainder differ diff --git a/test_files/random_no_remainder.enc b/test_files/random_no_remainder.enc new file mode 100644 index 0000000..496b48d Binary files /dev/null and b/test_files/random_no_remainder.enc differ diff --git a/test_files/random_with_remainder b/test_files/random_with_remainder new file mode 100644 index 0000000..1172876 Binary files /dev/null and b/test_files/random_with_remainder differ diff --git a/test_files/random_with_remainder.enc b/test_files/random_with_remainder.enc new file mode 100644 index 0000000..e732554 Binary files /dev/null and b/test_files/random_with_remainder.enc differ diff --git a/tests/encryption.rs b/tests/encryption.rs new file mode 100644 index 0000000..4d1aaed --- /dev/null +++ b/tests/encryption.rs @@ -0,0 +1,125 @@ +use std::fs; + +#[macro_use] +extern crate hex_literal; + +use aes::Aes128; + +use block_cipher_trait::BlockCipher; +use xts_mode::{get_tweak_default, Xts128}; + +#[test] +fn recrypt() { + let key = hex!("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"); + let plaintext = b"Yu9b5QgBck wBogw5ATwAHLEV YWDPK2mS"; + assert_eq!(plaintext.len(), 34); + let mut buffer = plaintext.to_owned(); + + let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); + let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + + let xts = Xts128::::new(cipher_1, cipher_2); + + let tweak = get_tweak_default(0); + xts.encrypt_sector(&mut buffer, tweak); + let _encrypted = buffer.clone(); + xts.decrypt_sector(&mut buffer, tweak); + + assert_eq!(&buffer[..], &plaintext[..]); +} + +#[test] +fn recrypt_no_remainder() { + let key = hex!("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"); + let plaintext = b"ATwAHLEVk WDPK2m5D1ZY9QpLyW 3aK9"; + assert_eq!(plaintext.len(), 32); + let mut buffer = plaintext.to_owned(); + + let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); + let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + + let xts = Xts128::::new(cipher_1, cipher_2); + + let tweak = get_tweak_default(0); + xts.encrypt_sector(&mut buffer, tweak); + let _encrypted = buffer.clone(); + xts.decrypt_sector(&mut buffer, tweak); + + assert_eq!(&buffer[..], &plaintext[..]); +} + +// Seems like OpenSSL resets the tweak every 0x1000 bytes +fn get_tweak_openssl(_sector_index: u128) -> [u8; 0x10] { + [0; 0x10] +} + +#[test] +fn encrypt_file_no_remainder() { + let key = hex!("f1e4acd1ca1258b2751c538f512cd8d2d26d7867a3e1245c5c4462cd398d443e"); + let mut buffer = fs::read("test_files/random_no_remainder").expect("could not read input"); + assert_eq!(buffer.len(), 0x3000); + + let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); + let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + + let xts = Xts128::::new(cipher_1, cipher_2); + + xts.encrypt_area(&mut buffer, 0x1000, 0, get_tweak_openssl); + + let reference = + fs::read("test_files/random_no_remainder.enc").expect("could not read reference"); + assert_eq!(&buffer[..], &reference[..]); +} + +#[test] +fn decrypt_file_no_remainder() { + let key = hex!("f1e4acd1ca1258b2751c538f512cd8d2d26d7867a3e1245c5c4462cd398d443e"); + let mut buffer = fs::read("test_files/random_no_remainder.enc").expect("could not read input"); + assert_eq!(buffer.len(), 0x3000); + + let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); + let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + + let xts = Xts128::::new(cipher_1, cipher_2); + + xts.decrypt_area(&mut buffer, 0x1000, 0, get_tweak_openssl); + + let reference = fs::read("test_files/random_no_remainder").expect("could not read reference"); + assert_eq!(&buffer[..], &reference[..]); +} + +#[test] +fn encrypt_file_with_remainder() { + let key = hex!("a5f85b18e5d06f13aa3a2dca389d776ab195a6feb1827980eb00abb0f75ea609"); + let mut buffer = fs::read("test_files/random_with_remainder").expect("could not read input"); + assert_eq!(buffer.len(), 20001); + + let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); + let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + + let xts = Xts128::::new(cipher_1, cipher_2); + + xts.encrypt_area(&mut buffer, 0x1000, 0, get_tweak_openssl); + + let reference = + fs::read("test_files/random_with_remainder.enc").expect("could not read reference"); + assert_eq!(&buffer[..], &reference[..]); +} + +#[test] +fn decrypt_file_with_remainder() { + let key = hex!("a5f85b18e5d06f13aa3a2dca389d776ab195a6feb1827980eb00abb0f75ea609"); + let mut buffer = + fs::read("test_files/random_with_remainder.enc").expect("could not read input"); + assert_eq!(buffer.len(), 20001); + + let cipher_1 = Aes128::new_varkey(&key[..16]).unwrap(); + let cipher_2 = Aes128::new_varkey(&key[16..]).unwrap(); + + let xts = Xts128::::new(cipher_1, cipher_2); + + xts.decrypt_area(&mut buffer, 0x1000, 0, get_tweak_openssl); + + let reference = fs::read("test_files/random_with_remainder").expect("could not read reference"); + assert_eq!(&buffer[..], &reference[..]); +}