From 0c97a5fcc83b1e1bfd7587c1ae37cc1837c66df6 Mon Sep 17 00:00:00 2001 From: Avril Date: Mon, 21 Sep 2020 16:20:57 +0100 Subject: [PATCH] initial commit --- .gitignore | 2 + Cargo.lock | 1062 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 24 + build.rs | 24 + src/args/error.rs | 55 +++ src/args/mod.rs | 65 +++ src/args/parse.rs | 301 ++++++++++++ src/config.rs | 94 ++++ src/ext.rs | 98 ++++ src/main.rs | 86 ++++ src/params/data.rs | 261 ++++++++++ src/params/error.rs | 64 +++ src/params/mod.rs | 80 +++ src/params/parse.rs | 432 ++++++++++++++++ src/params/spec.rs | 215 ++++++++ src/params/validate.rs | 77 +++ src/version.rs | 203 ++++++++ 17 files changed, 3143 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 src/args/error.rs create mode 100644 src/args/mod.rs create mode 100644 src/args/parse.rs create mode 100644 src/config.rs create mode 100644 src/ext.rs create mode 100644 src/main.rs create mode 100644 src/params/data.rs create mode 100644 src/params/error.rs create mode 100644 src/params/mod.rs create mode 100644 src/params/parse.rs create mode 100644 src/params/spec.rs create mode 100644 src/params/validate.rs create mode 100644 src/version.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2a3069 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +*~ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7c7e178 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1062 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "arc-swap" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "cc" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "color-eyre" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ba9b5e817f1bc1f2219b5a3474b69f838321b2a2ba8860d6a71c3bfe3d0fc1" +dependencies = [ + "backtrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", +] + +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +dependencies = [ + "build_const", +] + +[[package]] +name = "crypto-mac" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "cryptohelpers" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825b8339215ceae0288ed0384ebe63fe717f5b0c77b102ff3c9f59aefeed9106" +dependencies = [ + "crc", + "getrandom", + "hex-literal", + "hmac", + "libc", + "openssl", + "pbkdf2", + "serde", + "serde_derive", + "sha2", + "tokio", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "eyre" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f9683839e579a53258d377fcc0073ca0bf2042ac5e6c60a598069e64403a6d" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures-core" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" + +[[package]] +name = "hermit-abi" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +dependencies = [ + "libc", +] + +[[package]] +name = "hex-literal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5af1f635ef1bc545d78392b136bfe1c9809e029023c84a3638a864a10b8819c8" + +[[package]] +name = "hmac" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "indenter" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0bd112d44d9d870a6819eb505d04dd92b5e4d94bb8c304924a0872ae7016fb5" + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "miniz_oxide" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.1", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" +dependencies = [ + "log", + "mio", + "miow 0.3.5", + "winapi 0.3.9", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +dependencies = [ + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "net2" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" + +[[package]] +name = "once_cell" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-sys" +version = "0.9.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "owo-colors" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1250cdd103eef6bd542b5ae82989f931fc00a41a27f60377338241594410f3" + +[[package]] +name = "pbkdf2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170d73bf11f39b4ce1809aabc95bf5c33564cdc16fc3200ddda17a5f6e5e48b" +dependencies = [ + "base64", + "crypto-mac", + "hmac", + "rand", + "rand_core", + "sha2", + "subtle", +] + +[[package]] +name = "pin-project" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" + +[[package]] +name = "pkg-config" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" + +[[package]] +name = "ppv-lite86" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" + +[[package]] +name = "proc-macro2" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rae" +version = "0.1.0" +dependencies = [ + "cfg-if", + "color-eyre", + "cryptohelpers", + "lazy_static", + "rustc_version", + "serde", + "smallmap", + "tokio", + "toml", + "tracing", + "tracing-error", + "tracing-futures", + "tracing-subscriber", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" +dependencies = [ + "block-buffer", + "cfg-if", + "cpuid-bool", + "digest", + "opaque-debug", +] + +[[package]] +name = "sharded-slab" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallmap" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2adda73259bbc3ff84f711425ebfb8c90e9dd32a12b05c6528dd49244ea8230f" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "smallvec" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" + +[[package]] +name = "socket2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + +[[package]] +name = "subtle" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd" + +[[package]] +name = "syn" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + +[[package]] +name = "tokio" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "slab", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c" +dependencies = [ + "cfg-if", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bcf46c1f1f06aeea2d6b81f3c863d0930a596c86ad1920d4e5bad6dd1d7119a" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-error" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0f8c7178e13481ff6765bd169b33e8d554c5d2bbede5e32c356194be02b9b9" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82bb5079aa76438620837198db8a5c529fb9878c730bc2b28179b0241cf04c10" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..36c3e0d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "rae" +version = "0.1.0" +authors = ["Avril "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +color-eyre = {version = "0.5.3", default-features =false} +lazy_static = "1.4.0" +cryptohelpers = {version = "1.1.2", features=["full", "async", "serialise"]} +cfg-if = "0.1.10" +tokio = {version = "0.2", features=["full"]} +serde = {version ="1.0.116", features=["derive"]} +toml = "0.5.6" +tracing = "0.1.19" +tracing-subscriber = "0.2.12" +tracing-futures = "0.2.4" +tracing-error = "0.1.2" +smallmap = "1.1.2" + +[build-dependencies] +rustc_version = "0.2" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6399463 --- /dev/null +++ b/build.rs @@ -0,0 +1,24 @@ + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + match version_meta().unwrap().channel { + Channel::Stable => { + println!("cargo:rustc-cfg=stable"); + } + Channel::Beta => { + println!("cargo:rustc-cfg=beta"); + } + Channel::Nightly => { + println!("cargo:rustc-cfg=nightly"); + } + Channel::Dev => { + println!("cargo:rustc-cfg=dev"); + } + } +} diff --git a/src/args/error.rs b/src/args/error.rs new file mode 100644 index 0000000..8474b1d --- /dev/null +++ b/src/args/error.rs @@ -0,0 +1,55 @@ +//! Argument related errors +use super::*; +use std::{ + error, + fmt, +}; + +#[derive(Debug)] +#[non_exhaustive] +pub enum ParseErrorKind +{ + ExpectedArg(String), + UnknownOption(char), + UnknownOptionFor(char, char), + CannotCombine(char, char), + BadTail(char, String), + NotUnique(char), +} + +impl From<(String, ParseErrorKind)> for Error +{ + #[inline] fn from((froms, fromk): (String, ParseErrorKind)) -> Self + { + Self::Parse(froms, fromk) + } +} + + +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + Parse(String, ParseErrorKind), + + Unknown, + AtomInternal, +} +impl error::Error for Error{} +impl fmt::Display for Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + _ => write!(f, "unknown error"), + } + } +} + + +impl From> for Error +{ + #[inline] fn from(_: tokio::sync::mpsc::error::SendError) -> Self + { + Self::AtomInternal + } +} diff --git a/src/args/mod.rs b/src/args/mod.rs new file mode 100644 index 0000000..58a3cd9 --- /dev/null +++ b/src/args/mod.rs @@ -0,0 +1,65 @@ +//! Argument stuffs +use super::*; + + +mod error; +mod parse; + +pub fn program_name() -> &'static str +{ + lazy_static!{ + static ref NAME: String = std::env::args().next().unwrap(); + } + + &NAME[..] +} + +pub fn usage() -> ! +{ + println!(r#"signing encryptor version {pkg_version} (format version {real_version}) + by {authour} with <3 + licensed with GPL v3 or later + +Usage: {program} [OPTIONS...] [-e|-d|-a] [-] [] +Usage: {program} --generate RSA [--input ] [--password[=]] [--format BIN|TEXT|PEM] [] +Usage: {program} --generate AES [--input ] [--password[=]] [--format BIN|TEXT] +Usage: {program} --stat +Usage: {program} --help + + - Stop reading argument flags: + -e Encryption mode + -d Decryption mode + -a Autodetect mode (default) +OPTIONS: + -t Process to (autodetect mode) + -te Process to (encryption mode) + -td Process to (decryption mode) + -i Process in-place for non-specific files (i.e. do not add or remove `.rae` file extension) + + -k Use this key (autodetect type) + -kR Use this key (rsa private) + -kr Use this key (rsa public) + -ka Use this key (aes) +Other options for `-k` + -p Read a password from stdin for this key + -P The next argument after the key file is a password for the key + -s This key is also a signing key + -S This key is only a signing key + +EXAMPLE: + {program} -kRP key.rsa "super secret password" -ka key.aes -te input_file output_encrypted file +"#, + pkg_version = env!("CARGO_PKG_VERSION"), + real_version = CURRENT_VERSION, + authour = env!("CARGO_PKG_AUTHORS"), + program = program_name()); + std::process::exit(1) +} + +#[instrument] +/// Parse the program args into `Operation`. +pub async fn parse_args() -> Result +{ + parse::parse(std::env::args().skip(1)) + .with_suggestion(|| "Try passing `--help`") +} diff --git a/src/args/parse.rs b/src/args/parse.rs new file mode 100644 index 0000000..ab748f8 --- /dev/null +++ b/src/args/parse.rs @@ -0,0 +1,301 @@ +//! Arg parsing +#![allow(unused_macros)] +use super::*; +use tokio::{ + sync::{ + mpsc, + }, +}; +use std::{ + iter, +}; +use eyre::eyre; +use config::op::Password; +use error::ParseErrorKind; + +#[derive(Debug)] +enum IsSigning +{ + No, + Yes, + Only, +} + +impl Default for IsSigning +{ + #[inline] + fn default() -> Self + { + Self::No + } +} + +#[derive(Debug)] +enum KeyKind +{ + Autodetect, + RsaPublic, + RsaPrivate(IsSigning), // is signing key? + Aes, +} + +#[derive(Debug)] +enum TranslationMode +{ + Autodetect, + Encrypt, + Decrypt, +} + +impl Default for TranslationMode +{ + #[inline] + fn default() -> Self + { + Self::Autodetect + } +} + + +#[derive(Debug)] +enum Atom +{ + Key(KeyKind, String, Password), + + NonSpecific, + FileAuto(String), + FileSpecific(String, String, TranslationMode), + //TODO: +} + + +#[instrument] +pub async fn parse + std::fmt::Debug>(args: I) -> Result +{ + let (mut tx, mut rx) = mpsc::channel(1); + + macro_rules! unwrap_handler { + ($handler:expr) => ($handler.await + .wrap_err_with(|| eyre!("Atom parser panicked") + .with_warning(|| "This usually indicates a bug in the program, not invalid input."))); + } + + let mut args = args.into_iter(); + let handler = if let Some(arg) = args.next() + { + match arg.to_lowercase().trim() { + "--generate" => todo!(), + "--help" => todo!(), + "--stat" => todo!(), + _ => { + let handler = tokio::spawn(async move { + //TODO: What container type do we make for this? + while let Some(atom) = rx.recv().await { + debug!("[Normal] recv atom: {:?}", atom); + // Construct `Normal` with atoms + } + todo!() + }); + match parse_normal(std::iter::once(arg) + .chain(args.map(Into::into)), &mut tx).await + { + // The `handler` has dropped `rx` and returned an error, let unwrap_handler down there handle this. + Err(error::Error::AtomInternal) => Ok(()), + other => other, + } + .with_note(|| "In parsing of normal operation spec")?; + + + handler + }, + } + } else { + return Err(eyre!("No arguments specified")); + }; + + match unwrap_handler!(handler)? { + Err(error) => { + return Err(error) + .wrap_err_with(|| eyre!("Failed to construct operation from arguments")) + .with_note(|| "In constructing of operation from spec"); + }, + Ok(result) => { + + }, + } + + todo!() +} + +async fn parse_normal>(args: I, atoms: &mut mpsc::Sender) -> Result<(), error::Error> +{ + let mut args = args.into_iter(); + let mut reading=true; + + + while let Some(arg) = args.next() + { + + macro_rules! take_one { + ($msg:literal $($tt:tt)*) => { + match args.next() { + Some(v) => v, + None => return Err((arg, ParseErrorKind::ExpectedArg(format!($msg $($tt)*))).into()), + } + } + } + if reading && arg.starts_with("-") { + let mut opt = arg.chars().skip(1); + match opt.next() { + Some('i') => match opt.next() { + None => atoms.send(Atom::NonSpecific).await?, + Some(r) => return Err((ParseErrorKind::BadTail('i', iter::once(r).chain(opt).collect()), arg).swap().into()), + }, + Some('t') => { + let input = take_one!("t: Expected input file"); + let output = take_one!("t: Expected output file"); + let mut trans = TranslationMode::default(); + + let mut had = smallmap::Map::new(); + while let Some(opt) = opt.next() { + if (had.contains_key(&opt), had.insert(opt, ())).0 { + // arg has been repeated, not allowed + return Err((arg, error::ParseErrorKind::NotUnique(opt)).into()); + } + macro_rules! check_combine { + ($($chars:expr),+) => { + $(if had.contains_key(&$chars) { + return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars)))); + })* + }; + } + + match opt { + 'e' => trans= TranslationMode::Encrypt, + 'd' => trans= TranslationMode::Decrypt, + _ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('t', opt)).into()), + } + } + atoms.send(Atom::FileSpecific(input, output, trans)).await?; + }, + Some('k') => { + let key_file = take_one!("k: Expected a key file"); + enum KeySpec { + Autodetect, + RsaPrivate, + RsaPublic, + Aes, + } + let mut kind = KeySpec::Autodetect; + let mut password = Password::default(); + let mut signing = IsSigning::default(); + + let mut had = smallmap::Map::new(); + while let Some(opt) = opt.next() { + if (had.contains_key(&opt), had.insert(opt, ())).0 { + // arg has been repeated, not allowed + return Err((arg, error::ParseErrorKind::NotUnique(opt)).into()); + } + macro_rules! check_combine { + ($($chars:expr),+) => { + $(if had.contains_key(&$chars) { + return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars)))); + })* + }; + } + + match opt { + 'R' => { + check_combine!['r','a']; + kind = KeySpec::RsaPrivate; + }, + 'r' => { + check_combine!['R','a']; + kind = KeySpec::RsaPublic; + }, + 'a' => { + check_combine!['r','R']; + kind = KeySpec::Aes; + }, + 'P' => { + check_combine!['p']; + password= Password::Yes; + }, + 'p' => { + check_combine!['P']; + password = Password::Specific(take_one!("P: Expected a password")); + }, + 'S' => { + check_combine!['s', 'a']; + signing = IsSigning::Only; + }, + 's' => { + check_combine!['S', 'a']; + signing = IsSigning::Yes; + }, + _ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('k', opt)).into()), + } + } + + let kind = match kind { + KeySpec::Autodetect => KeyKind::Autodetect, + KeySpec::RsaPublic => KeyKind::RsaPublic, + KeySpec::RsaPrivate => KeyKind::RsaPrivate(signing), + KeySpec::Aes => KeyKind::Aes, + }; + atoms.send(Atom::Key(kind, key_file, password)).await?; + }, + Some(unknown) => return Err((arg, error::ParseErrorKind::UnknownOption(unknown)).into()), + None => reading=false, + } + } else { + reading=false; + atoms.send(Atom::FileAuto(arg)).await?; + } + } + todo!() + +} + + + +/* +#[instrument] +pub fn parse, I: IntoIterator + std::fmt::Debug>(args: I) -> Result +{ +let mut args = args.into_iter(); +if let Some(arg) = args.next() +{ +let arg =arg.into(); +match arg.to_lowercase().trim() { +"--generate" => Ok(Spec::Concrete(config::Operation::GenerateKey(parse_keygen(args.map(Into::into)) +.with_note(|| "Mode was `--generate`")?))), +"--help" => Ok(Spec::Concrete(config::Operation::Help)), +"--stat" => Ok(Spec::Concrete(config::Operation::KeyInfo(parse_stat(args.map(Into::into)) +.with_note(|| "Mode was `--stat")?))), +_ => Ok(parse_normal(std::iter::once(arg) +.chain(args.map(Into::into))) +.with_note(|| "Mode was crypt")?.into()), + } + } else { + Err(eyre!("No arguments specified")) + } +} + +fn parse_normal>(args: I) -> Result +{ + todo!() +} + + +fn parse_keygen>(args: I) -> Result +{ + todo!() +} + + +fn parse_stat>(args: I) -> Result, eyre::Report> +{ + todo!() +} +*/ diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..2fefc80 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,94 @@ +//! Parsed config +use super::*; + +pub mod op { + + /// The crypt mode + #[derive(Debug, PartialEq, Eq, Clone, Hash)] + pub enum Mode { + /// Detect based on file-type + Autodetect, + Encrypt, + Decrypt, + } + impl Default for Mode + { + #[inline] + fn default() -> Self + { + Mode::Autodetect + } + } + + #[derive(Debug, Clone,PartialEq, Eq, Default)] + pub struct Normal { + pub rsa: Vec<(String, Password)>, + pub sign: Vec<(String, Password)>, + pub aes: Vec<(String, Password)>, + pub mode: Mode, + + pub files: Vec<(String, Option)>, + } + + #[derive(Debug, Clone,PartialEq, Eq)] + pub enum GenerateKey { + Rsa(Rsa), + Aes(Aes), + } + + #[derive(Debug, PartialEq, Eq, Clone, Hash)] + pub enum Password + { + No, + Yes, + Specific(String), + } + impl Default for Password + { + #[inline] + fn default() -> Self + { + Self::No + } + } + + #[derive(Debug ,PartialEq, Eq, Clone, Hash, Default)] + pub struct KeyDescription + { + pub name: String, + pub email: String, + pub description: String, + + other: String, + } + + #[derive(Debug, Clone,PartialEq, Eq)] + pub struct Rsa + { + input: Option<(String, Password)>, + + output_priv: String, + output_pub: Option, + password: Password, + + description: KeyDescription, + } + #[derive(Debug,Clone, PartialEq, Eq)] + pub struct Aes + { + input: Option<(String, Password)>, + + output: String, + password: Password, + + description: KeyDescription, + } +} +#[derive(Debug, Clone,PartialEq, Eq)] +pub enum Operation +{ + Normal(op::Normal), + GenerateKey(op::GenerateKey), + KeyInfo(Vec<(String, op::Password)>), + Help, +} diff --git a/src/ext.rs b/src/ext.rs new file mode 100644 index 0000000..bf5d776 --- /dev/null +++ b/src/ext.rs @@ -0,0 +1,98 @@ +//! Extensions + +pub trait JoinStrsExt: Sized +{ + /// Join an iterator of `str` with a seperator + fn join(self, with: &str) -> String; +} + +impl JoinStrsExt for I +where I: Iterator, + T: AsRef +{ + fn join(self, with: &str) -> String + { + let mut output = String::new(); + let mut first=true; + for string in self + { + if !first { + output.push_str(with); + } + let string = string.as_ref(); + output.push_str(string); + first=false; + } + output + } +} + +/*macro_rules! typed_swap { + (@ [] $($reversed:tt)*) => { + fn swap(self) -> ($($reversed)*); + }; + (@ [$first:tt $($rest:tt)*] $($reversed:tt)*) => { + typed_swap!{@ [$($rest)*] $first $($reversed)*} + }; + (@impl {$($body:tt)*} [] $($reversed:tt)*) => { + fn swap(self) -> ($($reversed)*) + { + $($body)* + } + }; + (@impl {$($body:tt)*} [$first:tt $($rest:tt)*] $($reversed:tt)*) => { + typed_swap!{@impl {$($body)*} [$($rest)*] $first $($reversed)*} + }; + () => {}; + ({$($params:tt)*} $($rest:tt)*) => { + mod swap { + pub trait SwapTupleExt<$($params)*>: Sized + { + typed_swap!(@ [$($params)*]); + } + + impl<$($params)*> SwapTupleExt<$($params)*> for ($($params)*) + { + typed_swap!(@impl { + todo!() + } [$($params)*]); + } + + typed_swap!($($rest)*); + } + pub use swap::*; + }; + + (all $first:tt $($params:tt)+) => { + typed_swap!({$first, $($params),+}); + mod nswap { + typed_swap!(all $($params)+); + } + }; + (all $($one:tt)?) => {}; +} + +typed_swap!(all A B C D E F G H I J K L M N O P Q R S T U V W X Y Z); +pub use swap::*; + +fn test() +{ + let sw = (1, 2).swap(); + +}*/ +// ^ unfortunately not lol + +pub trait SwapTupleExt: Sized +{ + fn swap(self) -> (U,T); +} +impl SwapTupleExt for (T,U) +{ + #[inline(always)] fn swap(self) -> (U,T) { + (self.1, self.0) + } +} + +/*typed_swap!({A, B} + {A, U, V} + {T, U, V, W});*/ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7cd6ef8 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,86 @@ +#![cfg_attr(nightly, feature(label_break_value))] + +#![allow(dead_code)] + +#[macro_use] extern crate tracing; +use std::{ + convert::{TryFrom, TryInto}, +}; +use color_eyre::{ + eyre::{ + self, + WrapErr, + }, + Help, SectionExt, +}; +use lazy_static::lazy_static; +use serde::{Serialize, Deserialize}; + +pub const CURRENT_VERSION: version::Version = version::Version::new(0,0,0,version::Tag::Prerelease); + +mod ext; +use ext::*; +mod version; +mod config; +mod args; + +/*/// Dispatch params operations that can be handled at top level (i.e. `Help`) +async fn dispatch_args() -> Result +{ +match args::parse_args().await +.with_section(move || std::env::args().skip(1).join("\n").header("Input args were:"))? { + + } +}*/ + +macro_rules! cfg_debug { + (if {$($if:tt)*} else {$($else:tt)*}) => { + { + cfg_if::cfg_if!{ + if #[cfg(debug_assertions)] { + $($if)* + } else { + $($else)* + } + } + } + }; +} + +fn install_tracing() { + use tracing_error::ErrorLayer; + use tracing_subscriber::prelude::*; + use tracing_subscriber::{fmt, EnvFilter}; + + let fmt_layer = fmt::layer().with_target(false); + let filter_layer = EnvFilter::try_from_default_env() + .or_else(|_| EnvFilter::try_new("info")) + .unwrap(); + + tracing_subscriber::registry() + .with(filter_layer) + .with(fmt_layer) + .with(ErrorLayer::default()) + .init(); +} + +async fn work(op: config::Operation) -> Result<(), eyre::Report> +{ + todo!() +} + +#[instrument] +#[tokio::main] +async fn main() -> Result<(), eyre::Report> { + install_tracing(); + color_eyre::install()?; + + trace!("Parsing args"); + let args = args::parse_args().await?; + + work(args).await + .with_suggestion(|| "Run with `RUST_LOG=\"debug\"` environment variable for more detailed logging") + .with_suggestion(|| "Run with `RUST_LOG=\"trace\"` environment variable for extremely detailed logging")?; + + Ok(()) +} diff --git a/src/params/data.rs b/src/params/data.rs new file mode 100644 index 0000000..7904fb0 --- /dev/null +++ b/src/params/data.rs @@ -0,0 +1,261 @@ +use super::*; +use std::{ + path::{ + PathBuf, + }, +}; + +/// The type of key to generate +#[derive(Debug,Eq,PartialEq)] +pub enum GenerateKey +{ + Rsa, + Aes, +} + +/// The key serialise format +#[derive(Debug,Eq,PartialEq)] +pub enum KeyOutputFormat +{ + Binary, + Text, + /// PEM format only valid for RSA keys + PEM, +} + +impl FromStr for KeyOutputFormat +{ + type Err = Error; + fn from_str(value: &str) -> Result { + Ok(match value.to_uppercase().as_str() { + "TEXT" => KeyOutputFormat::Text, + "BIN" => KeyOutputFormat::Binary, + "PEM" => KeyOutputFormat::PEM, + d => return Err(format!("Unknown key output format `{}`. Expected `TEXT`, `BIN` or `PEM`.", d))?, + }) + } +} + +impl KeyOutputFormat +{ + pub fn is_aes_okay(&self) -> bool + { + if let KeyOutputFormat::PEM = self { + false + } else { + true + } + } +} + +/// Operation mode for `Operation::Normal` +#[derive(Debug,Eq,PartialEq)] +pub enum OperationMode +{ + Autodetect, + Encrypt, + Decrypt, + Verify, +} + +/// The operation for this run +#[derive(Debug,Eq,PartialEq)] +pub enum Operation +{ + /// Crypto + Normal{ + mode: OperationMode, + rsa: Vec, + sign: Vec, + auto_sign: bool, + aes: Vec, + + translate: Vec<(PathBuf, Option)>, + in_place: bool, + verbose: bool, + + #[cfg(feature="threads")] + sync: bool, + }, + /// Generate key + KeyGen{ + key_type: GenerateKey, + format: KeyOutputFormat, + cycle: Option, + use_password: bool, + password: Option, + output: Option, + private_only: bool, + public_only: bool, + output_public: Option, + verbose: bool, + + }, + /// Print help + Help, +} + +impl Operation +{ + /// Is verbose logging enabled + pub fn verbose(&self) -> bool + { + match self { + Self::Normal { verbose, .. } | + Self::KeyGen { verbose, .. } => *verbose, + _ => false, + } + } +} + +macro_rules! transcribe { + ($cond:expr => $if:expr; $else:expr) => { + if $cond {$if} else {$else} + }; +} + +use std::fmt; +impl fmt::Display for Operation +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match &self { + Self::Help => writeln!(f, "Display help"), + Self::Normal{ + mode, + rsa, + sign, + auto_sign, + aes, + in_place, + verbose, + #[cfg(feature="threads")] + sync, + + .. + } => { + write!(f, "Mode: ")?; + match mode { + OperationMode::Encrypt => writeln!(f, "Encrypt")?, + OperationMode::Decrypt => writeln!(f, "Decrypt")?, + OperationMode::Verify => writeln!(f, "Verify")?, + _ => writeln!(f, "Autodetected")?, + } + if rsa.len() > 0 { + writeln!(f, "RSA encryption enabled for {} keys.", rsa.len())?; + } + if sign.len() > 0 || *auto_sign { + writeln!(f, "Signing with {} keys.", sign.len() + transcribe!(*auto_sign => rsa.len(); 0))?; + } + writeln!(f, "AES encrypting with {} keys.", aes.len())?; + writeln!(f, " (will generate {} session keys.)", + transcribe!(aes.len() > 0 => + aes.len() * transcribe!(rsa.len()>0 => rsa.len(); 1); + rsa.len()))?; + if *in_place { + writeln!(f, "In-place")?; + } + + #[cfg(feature="threads")] + if *sync { + writeln!(f, "Single-threaded mode")?; + } + #[cfg(not(feature="threads"))] + writeln!(f, "Single-threaded mode")?; + + if *verbose { + writeln!(f, "Verbose mode")?; + } + + Ok(()) + }, + Self::KeyGen { + key_type: GenerateKey::Rsa, + format, + cycle, + password, + use_password, + output, + verbose, + + private_only, + output_public, + public_only, + } => { + writeln!(f, "Generate key: RSA")?; + writeln!(f, "Output format: {}", format)?; + if let Some(cycle) = cycle { + if output.is_some() || output_public.is_some() { + writeln!(f, "Input: {:?}", cycle)?; + } else { + writeln!(f, "In-place: {:?}", cycle)?; + } + } + if password.is_some() || *use_password { + writeln!(f, "Using password.")?; + } + if let Some(output) = output { + writeln!(f, "Outputting to: {:?}", output)?; + } + if let Some(output_public) = output_public { + if *public_only { + writeln!(f, "Outputting public only to {:?}", output_public)?; + } else { + writeln!(f, "Outputting public to {:?}", output_public)?; + } + } + if *private_only { + writeln!(f, "No public output")?; + } + if *verbose { + writeln!(f, "Verbose logging")?; + } + + Ok(()) + }, + Self::KeyGen { + key_type: GenerateKey::Aes, + format, + cycle, + password, + use_password, + output, + verbose, + .. + } => { + writeln!(f, "Generate key: AES")?; + writeln!(f, "Output format: {}", format)?; + if let Some(cycle) = cycle { + if output.is_some() { + writeln!(f, "Input: {:?}", cycle)?; + } else { + writeln!(f, "In-place: {:?}", cycle)?; + } + } + if password.is_some() || *use_password { + writeln!(f, "Using password.")?; + } + if let Some(output) = output { + writeln!(f, "Outputting to: {:?}", output)?; + } + if *verbose { + writeln!(f, "Verbose logging.")?; + } + Ok(()) + }, + + } + } +} + +impl fmt::Display for KeyOutputFormat +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}", match self { + Self::PEM => "PEM", + Self::Text => "TEXT", + Self::Binary => "BIN", + }) + } +} diff --git a/src/params/error.rs b/src/params/error.rs new file mode 100644 index 0000000..9881309 --- /dev/null +++ b/src/params/error.rs @@ -0,0 +1,64 @@ +use std::{ + error, + fmt, +}; + +#[derive(Debug)] +pub enum Error +{ + Unknown, + Message(String), + MessageStatic(&'static str), + FileNotFound(std::path::PathBuf), + BadPath(std::path::PathBuf), + ExpectedFile(std::path::PathBuf), +} + +impl error::Error for Error{} +impl fmt::Display for Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "failed to parse args: ")?; + match self { + Error::Message(message) => write!(f, "{}", message), + Error::MessageStatic(message) => write!(f, "{}", message), + Error::FileNotFound(file) => write!(f, "file not found: `{:?}`", file), + Error::BadPath(file) => write!(f, "bad path: `{:?}`", file), + Error::ExpectedFile(file) => write!(f, "expected a file: `{:?}`", file), + _ => write!(f, "unknown error"), + } + } +} + +impl From for Error +{ + fn from(string: String) -> Self + { + Self::Message(string) + } +} + +impl From<&'static str> for Error +{ + fn from(string: &'static str) -> Self + { + Self::MessageStatic(string) + } +} + +use std::path::PathBuf; +impl From for Error +{ + fn from(path: PathBuf) -> Self + { + + if !path.exists() { + Self::FileNotFound(path) + } else if path.is_dir() { + Self::ExpectedFile(path) + } else { + Self::BadPath(path) + } + } +} diff --git a/src/params/mod.rs b/src/params/mod.rs new file mode 100644 index 0000000..90c4838 --- /dev/null +++ b/src/params/mod.rs @@ -0,0 +1,80 @@ +//! Argument and option hanlding +use super::*; +use std::{ + env::args, + str::FromStr, +}; +use lazy_static::lazy_static; + +/// Program executable name +#[inline] +pub fn program() -> &'static str +{ + lazy_static! { + static ref NAME: &'static str = Box::leak(args().next().unwrap().into_boxed_str()); + } + + &NAME +} + +/// Print usage and exit with exit code 1. +pub fn usage() -> ! +{ + println!("naes: native file encryptor (prog. ver: {}, format ver: {})", env!("CARGO_PKG_VERSION"), crate::CURRENT_VERSION); + println!(" made by {} with <3", env!("CARGO_PKG_AUTHORS")); + println!(" licensed with GPL v3"); + #[cfg(feature="threads")] println!(" (compiled with threaded support)"); + println!(); + + println!("Usage: {} [OPTIONS...] [-e|-d|-v] [--] ", program()); + println!("Usage: {} --keygen RSA [--verbose] [--cycle ] [--format TEXT|BIN|PEM] [--private-only|--public-only] [--password] []", program()); + println!("Usage: {} --keygen AES [--verbose] [--cycle ] [--format TEXT|BIN] [--password] ", program()); + println!("Usage: {} --help", program()); + println!(); + println!("Options:"); + println!(" --rsa[=keys,...] []\tEncrypt with this (or these) RSA keys."); + println!(" --sign[=keys,...] []\tSign with this (or these) RSA keys."); + println!(" --auto-sign\t\t\tWhen RSA encryption keys are provided, also sign with them."); + println!(" --aes[=keys,...] []\tEncrypt with this (or these) AES keys."); + println!(" --translate=[,] [ ]\tSpecify input and output filenames."); + println!(" --in-place\t\t\tDo not add/remove `.nenc` extension to/from output file."); + println!(" --verbose\t\t\tVerbose logging."); + #[cfg(feature="threads")] + println!(" --sync\t\t\tDo not process files concurrently."); + println!(); + println!("Additional options:"); + println!(" -e\t\tForce encryption mode. Default is autodetect."); + println!(" -d\t\tForce decryption mode. Default is autodetect."); + println!(" -v\t\tVerification mode. Checks integrity of encrypted files and validates signatures with provided signing keys."); + println!(" --\t\tStop reading args."); + println!(); + println!("Keygen only options:"); + println!(" --cycle \t\tRead and write out to output instead of generating new key."); + println!(" --format TEXT|BIN|PEM\t\tOutput key format. PEM is only valid for RSA outputs. Default is BIN."); + println!(" --private-only\t\tOnly valid for RSA outputs. Do not re-output public key."); + println!(" --public-only\t\t\tOnly valid for RSA outputs. Only output public key. Only useful with `--cycle`."); + println!(" --password[=text]\t\tPassword-protect this key."); + + std::process::exit(1) +} + +pub mod error; +use error::*; + +mod data; +pub use data::*; + +mod parse; + +mod validate; + +mod spec; + +/// Parse the command line arguments passed. +pub async fn parse_args() -> Result +{ + let mut args = std::env::args(); + let _ = args.next(); + + parse::parse(&mut args).await?.try_into_validated() +} diff --git a/src/params/parse.rs b/src/params/parse.rs new file mode 100644 index 0000000..f3962ad --- /dev/null +++ b/src/params/parse.rs @@ -0,0 +1,432 @@ +use super::*; +use std::{ + convert::{ + TryFrom,TryInto, + }, +}; + +mod builder { + use super::*; + + #[derive(PartialEq,Eq,Debug)] + pub enum OperationMode + { + Normal, + KeyGen(GenerateKey), + Help, + } + + #[derive(PartialEq,Eq,Debug)] + pub struct KeyGenOps + { + pub format: KeyOutputFormat, + pub cycle: Option, + pub use_password: bool, + pub password: Option, + pub output: Option, + pub private_only: bool, + pub output_public: Option, + pub public_only: bool, + } + + impl Default for KeyGenOps + { + fn default() -> Self { + Self { + format: KeyOutputFormat::Binary, + cycle: None, + use_password: false, + password: None, + output: None, + private_only: false, + output_public: None, + public_only: false, + } + } + } + + #[derive(PartialEq,Eq,Debug)] + pub struct NormalOps + { + pub mode: super::super::OperationMode, + pub rsa: Vec, + pub sign: Vec, + pub auto_sign: bool, + pub aes: Vec, + + pub translate: Vec<(String, Option)>, + pub in_place: bool, + pub sync: bool, + } + + impl Default for NormalOps + { + fn default() -> Self + { + Self { + mode: super::super::OperationMode::Autodetect, + rsa: Vec::new(), + sign: Vec::new(), + auto_sign: false, + aes: Vec::new(), + translate: Vec::new(), + in_place: false, + sync: false, + } + } + } + + #[derive(PartialEq,Eq,Debug)] + pub struct Operation + { + pub mode: OperationMode, + + pub verbose: bool, + pub keygen: KeyGenOps, + pub normal: NormalOps, + } + + impl Default for Operation + { + fn default() -> Self + { + Self { + mode: OperationMode::Help, + keygen: KeyGenOps::default(), + normal: Default::default(), + verbose: false, + } + } + } +} + +pub fn split<'a>(input: &'a str) -> (&'a str, Option<&'a str>) +{ + if let Some(index) = input.find('=') { + (&input[..index], Some(&input[(index+1)..])) + } else { + (input, None) + } +} + +pub async fn parse(input: &mut I) -> Result +where I: Iterator, + T: AsRef +{ + let mut i=0; + + let mut op = builder::Operation::default(); + let mut reading = true; + let mut rest = Vec::new(); + let mut cfg_files = Vec::new(); + + macro_rules! take_one + { + ($message:expr) => {(i+=1, input.next().ok_or_else(|| $message)?).1.as_ref()} + } + + while let Some(arg) = input.next() + { + let arg = arg.as_ref(); + 'next: { + if i == 0 { + // First arg + match arg { + "--help" => return Ok(Operation::Help), + "--keygen" => { + op.keygen = builder::KeyGenOps::default(); + + op.mode = builder::OperationMode::KeyGen(match take_one!("Expected a key type").to_uppercase().as_str() { + "AES" => GenerateKey::Aes, + "RSA" => GenerateKey::Rsa, + d => return Err(format!("Unknown key type `{}`. Expected `RSA` or `AES`.",d))?, + }); + break 'next; + }, + _ => { + op.mode = builder::OperationMode::Normal; + }, + } + } + + match op.mode { + builder::OperationMode::KeyGen(GenerateKey::Rsa) => { + let (name, value) = split(arg); + + 'cont: { + match name.to_lowercase().as_str() { + "-c" => { + cfg_files.push(take_one!("Expected a filename").to_owned()); + }, + "--verbose" if value.is_some() => op.verbose = value.unwrap().to_lowercase().trim() == "true", + "--verbose" => op.verbose = true, + "--cycle" if value.is_some() => op.keygen.cycle = Some(value.unwrap().to_owned()), + "--cycle" => op.keygen.cycle = Some(take_one!("Expected a filename").to_owned()), + "--format" if value.is_some() => op.keygen.format = value.unwrap().parse()?, + "--format" => op.keygen.format = take_one!("Expected an output format").parse()?, + "--password" if value.is_some() => { + op.keygen.use_password = true; + op.keygen.password = Some(value.unwrap().to_owned()); + }, + "--password" => op.keygen.use_password = true, + "--private-only" => op.keygen.private_only = true, + "--public-only" => op.keygen.public_only = true, + _ => break 'cont, + } + break 'next; + }; + }, + builder::OperationMode::KeyGen(GenerateKey::Aes) => { + let (name, value) = split(arg); + 'cont: { + match name.to_lowercase().as_str() { + "--verbose" if value.is_some() => op.verbose = value.unwrap().to_lowercase().trim() == "true", + "--verbose" => op.verbose = true, + "--cycle" if value.is_some() => op.keygen.cycle = Some(value.unwrap().to_owned()), + "--cycle" => op.keygen.cycle = Some(take_one!("Expected a filename").to_owned()), + "--format" if value.is_some() => op.keygen.format = value.unwrap().parse()?, + "--format" => op.keygen.format = take_one!("Expected an output format").parse()?, + "--password" if value.is_some() => { + op.keygen.use_password = true; + op.keygen.password = Some(value.unwrap().to_owned()); + }, + "--password" => op.keygen.use_password = true, + _ => break 'cont, + } + break 'next; + } + }, + builder::OperationMode::Normal => { + let (name, value) = split(arg); + if reading { + 'cont: { + match name.to_lowercase().as_str() { + "--rsa" if value.is_some() => op.normal.rsa.extend(value.unwrap().split(',').map(|x| x.to_owned())), + "--rsa" => op.normal.rsa.push(take_one!("Expected an RSA key filename").to_owned()), + "--sign" if value.is_some() => op.normal.sign.extend(value.unwrap().split(',').map(|x| x.to_owned())), + "--sign" => op.normal.sign.push(take_one!("Expected an RSA signing key filename").to_owned()), + "--auto-sign" => op.normal.auto_sign = true, + "--aes" if value.is_some() => op.normal.aes.extend(value.unwrap().split(',').map(|x| x.to_owned())), + "--aes" => op.normal.aes.push(take_one!("Expected an AES key filename").to_owned()), + "--translate" if value.is_some() => { + let mut value = value.unwrap().split(','); + let input = value.next().ok_or("Invalid --translate singleton")?.to_owned(); + let output = value.next().ok_or("Invalid --translate singleton (expected an output)")?.to_owned(); + op.normal.translate.push((input, Some(output))); + }, + "--translate" => { + let input = take_one!("No --translate input").to_owned(); + let output = take_one!("No --translate output").to_owned(); + op.normal.translate.push((input.to_owned(), Some(output.to_owned()))); + }, + "--in-place" => op.normal.in_place = true, + "--verbose" => op.verbose = true, + "--sync" => op.normal.sync = true, + + "-e" => { + op.normal.mode = super::OperationMode::Encrypt; + }, + "-d" => { + op.normal.mode = super::OperationMode::Decrypt; + }, + "-v" => { + op.normal.mode = super::OperationMode::Verify; + }, + + "--" => reading = false, + _ => { + reading = false; + break 'cont; + }, + } + break 'next; + }; + } + }, + _ => return Err("Try `--help`")?, + } + rest.push(arg.to_owned()); + }; + i+=1; + + } + + // Add `rest` + match op.mode { + builder::OperationMode::Normal => { + if rest.len() < 1 { + return Err("Expected at least one input file")?; + } + op.normal.translate.extend(rest.into_iter().map(|x| (x, None))); + }, + builder::OperationMode::KeyGen(GenerateKey::Aes) => { + if rest.len() > 1 { + return Err("Expected only one output file")?; + } else if rest.len() < 1 && op.keygen.cycle.is_none() { + return Err("Expected one output file or `--cycle`")?; + } else if rest.len()>0 { + op.keygen.output = Some(rest.into_iter().next().unwrap()); + } + }, + builder::OperationMode::KeyGen(GenerateKey::Rsa) => { + if rest.len() > 2 { + return Err("Expected one or 2 output files")?; + } else if rest.len() < 1 && op.keygen.cycle.is_none() { + return Err("Expected at least one output or `--cycle`")?; + } else if rest.len() == 2 { + let mut iter = rest.into_iter(); + + if op.keygen.public_only { + return Err("Expected one public output")?; + } else { + op.keygen.output = Some(iter.next().unwrap()); + op.keygen.output_public = Some(iter.next().unwrap()); + } + } else { + let mut iter = rest.into_iter(); + if op.keygen.public_only { + op.keygen.output_public = Some(iter.next().ok_or("`--public-only` requires one output.")?); + } else { + op.keygen.output = iter.next(); + } + } + }, + _ => return Err("No params specified. Try `--help`.")?, + }; + + // Ensure valid + let mut op = op.try_into()?; + + // Add spec + for spec in cfg_files.into_iter() + { + spec::Spec::re //TODO: Fuck this whole module. Just rewrite all of this crap shit FUCK + } + + Ok(op) +} + + +impl TryFrom for Operation +{ + type Error = Error; + + fn try_from(op: builder::Operation) -> Result + { + match op.mode { + builder::OperationMode::Normal => { + + if op.keygen != builder::KeyGenOps::default() { + return Err("Invalid mode for params.")?; + } + + if op.normal.rsa.len() < 1 && op.normal.auto_sign { + warn!("`--auto-sign` here does nothing."); + } + if op.normal.aes.len() < 1 && op.normal.rsa.len() < 1 { + return Err("No keys specified.")?; + } + if op.normal.translate.len() < 1 { + return Err("No files specified.")?; + } + #[cfg(not(feature="threads"))] + if op.normal.sync { + warn!("`--sync` here does nothing, binary not compiled with `threads` feature."); + } + + // Number of inputs without specified outputs + let autodetect_inputs = op.normal.translate.iter().filter(|x| x.1.is_none()).count(); + if op.normal.in_place && autodetect_inputs < 1 { + warn!("`--in-place` here does nothing, no autodetected outputs."); + } + + Ok(Operation::Normal{ + mode: op.normal.mode, + rsa: op.normal.rsa.into_iter().map(|x| x.into()).collect(), + sign: op.normal.sign.into_iter().map(|x| x.into()).collect(), + auto_sign: op.normal.auto_sign, + aes: op.normal.aes.into_iter().map(|x| x.into()).collect(), + translate: op.normal.translate.into_iter().map(|x| (x.0.into(), x.1.and_then(|y| Some(y.into())))).collect(), + in_place: op.normal.in_place, + verbose: op.verbose, + #[cfg(feature="threads")] + sync: op.normal.sync, + }) + }, + builder::OperationMode::KeyGen(GenerateKey::Rsa) => { + if op.normal != builder::NormalOps::default() { + return Err("Invalid mode for params.")?; + } + + if op.keygen.public_only && op.keygen.private_only { + return Err("`--private-only` and `--public-only` will generate nothing.")?; + } + if op.keygen.public_only && op.keygen.cycle.is_none() { + warn!("`--public-only` is only useful with `--cycle`."); + } + if op.keygen.output.is_none() + && op.keygen.output_public.is_none() + && op.keygen.cycle.is_none() { + return Err("No output found.")?; + } + if op.keygen.public_only && op.keygen.cycle.is_none() && op.keygen.output_public.is_none() + { + return Err("`--public-only` requires one output or `--cycle`.")?; + } + if op.keygen.private_only && op.keygen.output_public.is_some() + { + warn!("Unused public key output."); + } + + Ok(Operation::KeyGen{ + key_type: GenerateKey::Rsa, + format: op.keygen.format, + cycle: op.keygen.cycle.and_then(|x| Some(x.into())), + use_password: op.keygen.use_password, + output: op.keygen.output.and_then(|x| Some(x.into())), + private_only: op.keygen.private_only, + public_only: op.keygen.public_only, + output_public: op.keygen.output_public.and_then(|x| Some(x.into())), + password: op.keygen.password, + verbose: op.verbose, + }) + }, + builder::OperationMode::KeyGen(GenerateKey::Aes) => { + if op.normal != builder::NormalOps::default() { + return Err("Invalid mode for params.")?; + } + + if op.keygen.private_only || + op.keygen.public_only || + op.keygen.output_public.is_some() + { + return Err("Invalid params for mode.")?; + } + if !op.keygen.format.is_aes_okay() + { + return Err("This output format is not valid for AES key.")?; + } + + + if op.keygen.cycle.is_none() && op.keygen.output.is_none() { + return Err("No output set.")?; + } + + Ok(Operation::KeyGen{ + key_type: GenerateKey::Aes, + format: op.keygen.format, + cycle: op.keygen.cycle.and_then(|x| Some(x.into())), + use_password: op.keygen.use_password, + output: op.keygen.output.and_then(|x| Some(x.into())), + password: op.keygen.password, + verbose: op.verbose, + + private_only: false, + public_only: false, + output_public: None, + }) + }, + builder::OperationMode::Help => Ok(Operation::Help), + } + } +} diff --git a/src/params/spec.rs b/src/params/spec.rs new file mode 100644 index 0000000..983ea71 --- /dev/null +++ b/src/params/spec.rs @@ -0,0 +1,215 @@ +//! Way of serialising multiple keys in file +use super::*; +use std::{ + io, + path::{ + PathBuf, + Path, + }, + marker::Unpin, + fmt, + error, +}; +use tokio::{ + prelude::*, + io::{ + AsyncRead, + AsyncWrite, + }, + fs::{ + OpenOptions, + }, +}; + +/// An error type for serialisation or deserialisation +#[derive(Debug)] +pub enum SerdeError +{ + Serialise(toml::ser::Error), + Deserialise(toml::de::Error), + IO(io::Error), +} + +impl From for SerdeError +{ + fn from(from: toml::ser::Error) -> Self + { + Self::Serialise(from) + } +} + +impl From for SerdeError +{ + fn from(from: toml::de::Error) -> Self + { + Self::Deserialise(from) + } +} + +impl From for SerdeError +{ + fn from(from: io::Error) -> Self + { + Self::IO(from) + } +} + +impl error::Error for SerdeError +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match &self { + Self::Serialise(s) => Some(s), + Self::Deserialise(d) => Some(d), + Self::IO(io) => Some(io), + } + } +} + +impl fmt::Display for SerdeError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Self::Serialise(_) => write!(f, "writing failed"), + Self::Deserialise(_) => write!(f, "reading failed"), + Self::IO(_) => write!(f, "io error"), + } + } +} + + +/// Serialisable struct containing key file information +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Spec +{ + rsa: Vec, + rsa_sign: Vec, + sign_all: Option, + aes: Vec, +} + +pub struct Signers<'a>(&'a Spec, usize); + +impl<'a> Iterator for Signers<'a> +{ + type Item = &'a String; + fn next(&mut self) -> Option { + (if self.1 < self.0.rsa_sign.len() { + Some(&self.0.rsa_sign[self.1]) + } else if self.0.is_sign_all() { + let rest = self.1 -self.0.rsa_sign.len(); + if rest < self.0.rsa.len() { + Some(&self.0.rsa[rest]) + } else { + None + } + } else { + None + }, self.1+=1).0 + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.0.rsa_sign.len() + if self.0.is_sign_all() { + self.0.rsa.len() + } else { + 0 + }; + (len, Some(len)) + } +} +impl<'a> std::iter::ExactSizeIterator for Signers<'a>{} + +impl Spec +{ + #[inline] fn is_sign_all(&self) -> bool + { + self.sign_all.unwrap_or(false) + } + pub fn new() -> Self + { + Self { + rsa: Vec::new(), + rsa_sign: Vec::new(), + sign_all: Some(false), + aes: Vec::new(), + } + } + + /// An iterator over all signers + pub fn signers(&self) -> Signers<'_> + { + Signers(self, 0) + } + + /// An iterator over all RSA encryptors + pub fn rsa(&self) -> std::slice::Iter<'_, String> + { + self.rsa.iter() + } + + /// An iterator over all AES encryptors + pub fn aes(&self) -> std::slice::Iter<'_, String> + { + self.aes.iter() + } + + /// Consume this instance into a `Normal` operation. + /// + /// # Panics + /// If `op` is not `Normal` mode. + pub fn into_operation(self, op: &mut Operation) + { + match op { + Operation::Normal { + rsa, + aes, + sign, + auto_sign, + .. + } => { + let sign_all = self.is_sign_all(); + if *auto_sign == sign_all { + // Both are true or both are false, no extra conversion needed + sign.extend(self.rsa_sign.into_iter().map(PathBuf::from)); + } else if sign_all { + // Need to add `rsa` and `rsa_sign` to `sign`. + sign.extend(self.rsa_sign.iter() + .chain(self.rsa.iter()) + .cloned() + .map(PathBuf::from)); + } + rsa.extend(self.rsa.into_iter().map(PathBuf::from)); + aes.extend(self.aes.into_iter().map(PathBuf::from)); + }, + _ => panic!("Tried to consume into an operation of invalid type"), + } + } + + /// Read an instance from a stream + pub async fn read_from_stream(from: &mut S) -> eyre::Result + { + let mut output = Vec::with_capacity(4096); + async fn internal(output: &mut Vec, from: &mut S) -> Result { + let mut buffer = [0u8; 4096]; + let mut read; + while {read = from.read(&mut buffer[..]).await?; read>0} { + output.extend_from_slice(&buffer[..read]); + } + + Ok(toml::from_slice(&output[..])?) + } + + internal(&mut output, from).await + .with_section(move || String::from_utf8_lossy(&output[..]).to_string().header("Read contents was")) + } + + /// Read an instance from a file + pub async fn read_from_file>(from: P) -> eyre::Result + { + let mut file = OpenOptions::new() + .read(true) + .open(from.as_ref()).await?; + Ok(Self::read_from_stream(&mut file).await + .with_section(move || format!("{:?}", from.as_ref()).header("Filename was"))?) + } +} diff --git a/src/params/validate.rs b/src/params/validate.rs new file mode 100644 index 0000000..3d1238b --- /dev/null +++ b/src/params/validate.rs @@ -0,0 +1,77 @@ +use super::*; + +impl Operation +{ + /// Validate the options i/o. + pub fn try_into_validated(self) -> Result + { + match &self { + Self::KeyGen{ + key_type: GenerateKey::Aes, + + output, + cycle, + .. + } => { + if output == cycle { + warn!("Cycle output is the same as input."); + } + + if let Some(output) = output { + if output.is_dir() { + return Err(output.clone())?; + } + if output.exists() { + warn!("Output will be overwritten."); + } + } + + if let Some(cycle) = cycle { + if !cycle.exists() || cycle.is_dir() { + return Err(cycle.clone())?; + } + } + }, + Self::KeyGen{ + key_type: GenerateKey::Rsa, + + output, + output_public, + cycle, + .. + } => { + if output == output_public && output.is_some() { + return Err("Output and public key output are the same.")?; + } + if cycle == output && output.is_some() { + warn!("Cycle output is the same as input."); + } + if let Some(output) = output { + if output.is_dir() { + return Err(output.clone())?; + } + if output.exists() { + warn!("Output will be overwritten."); + } + } + if let Some(output_public) = output_public { + if output_public.is_dir() { + return Err(output_public.clone())?; + } + if output_public.exists() { + warn!("Output for public will be overwritten."); + } + } + if let Some(cycle) = cycle { + if !cycle.exists() || cycle.is_dir() { + return Err(cycle.clone())?; + } + } + + }, + _ => (), + } + + Ok(self) + } +} diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 0000000..717e132 --- /dev/null +++ b/src/version.rs @@ -0,0 +1,203 @@ +//! Version information for the file container format, semver-like internal version format. + +use super::*; +use std::{ + fmt, + mem::size_of, + error, +}; + +/// Represents other states of this container format version +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Copy)] +#[repr(u8)] +pub enum Tag +{ + None =0, + Prerelease =1, + Unstable =2, +} + +/// The error used by `Tag` and `Version` when they parse invalid input. +#[derive(Debug)] +pub struct ParsingError; +impl error::Error for ParsingError{} +impl fmt::Display for ParsingError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "An attempt was made to parse invalid Version input") + } +} + + +impl Tag +{ + /// Try to parse a `u8` as `Tag`. + fn try_from_u8(from: u8) -> Result + { + macro_rules! branches { + ($($num:path),*) => { + match from { + $( + x if x == $num as u8 => $num, + )* + _ => return Err(ParsingError), + } + } + } + + Ok(branches! { + Self::Prerelease, + Self::Unstable, + Self::None + }) + } +} + +impl fmt::Display for Tag +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Self::Prerelease => write!(f, "*"), + Self::Unstable => write!(f, "~"), + _ => Ok(()), + } + } +} + +impl Default for Tag +{ + #[inline] + fn default() -> Self + { + Self::None + } +} + +/// Represents a container version number in this format: `Major.Minor.Bugfix.Tag` +/// +/// Should adhear to this principle +/// * Major - If different, fail parsing. +/// * Minor - If higher that current, fail parsing. +/// * Bugfix - If higher than current, warn user. +/// * Tag - Unused, but may represent things like prerelease or unstable +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Copy, Default)] +#[repr(packed, C)] +pub struct Version(u8,u8,u8,Tag); + +impl fmt::Display for Version +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}.{}.{}{}", self.0, self.1, self.2, self.3) + } +} + +impl Version +{ + /// Is this read version compatable with a system of version `other`? + pub fn is_compat(&self, other: &Self) -> bool + { + self.0 == other.0 && + self.1 <= other.1 + } + /// Should we warn about parsing with this version when system version is `other`? + pub fn should_warn(&self, other: &Self) -> bool + { + self.2 > other.2 || self.3 == Tag::Unstable + } + + /// Create a new instance of `Version`. + pub const fn new(major: u8, minor: u8, bugfix: u8, tag: Tag) -> Self + { + Self(major,minor,bugfix,tag) + } + + /// Encode as u32 + pub fn as_u32(&self) -> u32 + { + debug_assert_eq!(size_of::(), size_of::()); + unsafe{*(self as *const Self as *const u32)} + } + + /// Decode u32 as self, assuming `Tag` is valid. + pub unsafe fn from_u32_unchecked(from: u32) -> Self + { + debug_assert_eq!(size_of::(), size_of::()); + *(&from as *const u32 as *const Self) + } + + /// Try to parse a `u32` encoded `Version`. + pub fn try_from_u32(from: u32) -> Result + { + debug_assert_eq!(size_of::(), size_of::()); + debug_assert_eq!(size_of::(), 4); + let parts = &from as *const u32 as *const u8; + + macro_rules! index { + ($array:ident, $index:expr) => { + unsafe{*($array.offset($index))} + } + } + + Ok(Self::new(index!(parts, 0), + index!(parts, 1), + index!(parts, 2), + Tag::try_from_u8(index!(parts, 3))?)) + } +} + +impl TryFrom for Tag +{ + type Error = ParsingError; + + #[inline] fn try_from(from: u8) -> Result + { + Self::try_from_u8(from) + } +} + +impl From for u8 +{ + #[inline] fn from(from: Tag) -> Self + { + from as u8 + } +} + +impl TryFrom for Version +{ + type Error = ParsingError; + + #[inline] fn try_from(from: u32) -> Result + { + Self::try_from_u32(from) + } +} + +impl From for u32 +{ + #[inline] fn from(from: Version) -> Self + { + from.as_u32() + } +} + +#[cfg(test)] +mod tests +{ + use super::*; + #[test] + fn vers_encode() + { + let version1 = Version::new(1,0,0,Tag::Unstable); + let version2 = Version::try_from_u32(version1.as_u32()).unwrap(); + let version3 = unsafe{Version::from_u32_unchecked(version1.as_u32())}; + assert_eq!(format!("{}", version1), "1.0.0~"); + assert_eq!(version1, version2); + assert_eq!(version2, version3); + } +} + +