feat: encrypt seed and encode as URI

This commit is contained in:
nazeh
2023-12-10 12:10:53 +03:00
parent 68bdfa93a4
commit 70061dffe6
12 changed files with 639 additions and 96 deletions

353
Cargo.lock generated
View File

@@ -2,6 +2,66 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstream"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3a318f1f38d2418400f8209655bfd825785afd25aa30bb7ba6cc792e4596748"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "argon2"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9"
dependencies = [
"base64ct",
"blake2",
"cpufeatures",
"password-hash",
]
[[package]]
name = "arrayref"
version = "0.3.7"
@@ -14,6 +74,31 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bessie"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30407622e75e71bbc4f53708a3016c75eb960ed417ec11abeae034bd78e1a196"
dependencies = [
"blake3",
"rand",
]
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "blake3"
version = "1.5.0"
@@ -27,6 +112,15 @@ dependencies = [
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bytes"
version = "1.5.0"
@@ -48,12 +142,98 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "constant_time_eq"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
[[package]]
name = "cpufeatures"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.11"
@@ -65,13 +245,22 @@ dependencies = [
"wasi",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "kytes"
version = "0.1.0"
dependencies = [
"blake3",
"argon2",
"bessie",
"bytes",
"clap",
"rand",
"thiserror",
"z32",
]
@@ -81,12 +270,41 @@ version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
@@ -117,12 +335,145 @@ dependencies = [
"getrandom",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "z32"
version = "1.0.2"

18
design/seed.md Normal file
View File

@@ -0,0 +1,18 @@
# Seed
Kytes seed is an encrypted seed encoded as URI as follows:
```
kytes:seed:<suffix>
```
The `suffix` is a `z-base32` encoded bytes as follows:
```
<VERSION><Encrypted seed ciphertext>
```
## Version 0
For version 0 the encrypted seed is using [`bessie`](https://github.com/oconnor663/bessie/blob/44f9500ebeb0f28efc9689184ff5b1d79e2308e0/design.md) version 0.0.1

View File

@@ -5,10 +5,21 @@ edition = "2021"
description = "Soaring in the Cloud, but you pull the strings."
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
blake3 = "1.5.0"
bytes = "1.5.0"
rand = "0.8.5"
z32 = "1.0.2"
# cli
clap = { version = "4.4.11", optional = true, features = ["derive"] }
argon2 = "0.5.2"
thiserror = "1.0.50"
bessie = "0.0.1"
[features]
default = ["cli"]
cli = ["clap"]
[[bin]]
name = "kytes"
required-features = ["cli"]

View File

@@ -1,86 +0,0 @@
use bytes::{BufMut, Bytes, BytesMut};
use rand::Rng;
const SEED_FILE_PREFIX: &str = "kytes seed";
const VERSION: u8 = 0;
const PASSPHRASE_HASHING_ITERATIONS: i32 = 100_000;
/// Takes an encrypted seed and format it into a seed file as follows:
/// `kytes encrypted-seed v<version> <zbase32 encoded encrypted_seed>`
pub fn format_encrypted_seed_file(encrypted_seed: &[u8; 32]) -> Bytes {
let mut seed_file = BytesMut::with_capacity(SEED_FILE_PREFIX.len() + 33);
seed_file.extend_from_slice(SEED_FILE_PREFIX.as_bytes());
seed_file.extend_from_slice(b" v");
seed_file.put_u8(VERSION + 48);
seed_file.extend_from_slice(b" ");
seed_file.extend_from_slice(z32::encode(encrypted_seed).as_bytes());
seed_file.freeze()
}
pub fn encrypted_seed_file_version(seed_file: &Bytes) -> Option<u8> {
let version_start = SEED_FILE_PREFIX.len() + 2;
let version_end = version_start + 2;
seed_file
.get(version_start..version_end)
.map(|version| version[0] - 48_u8)
}
pub fn encrypt_seed_file() {
unimplemented!()
}
pub fn generate_seed() -> [u8; 32] {
let mut rng = rand::thread_rng();
rng.gen()
}
pub fn seed_encryption_key(passphrase: &str) -> [u8; 32] {
let mut hash: [u8; 32] = blake3::hash(passphrase.as_bytes()).into();
for i in 0..PASSPHRASE_HASHING_ITERATIONS {
hash = blake3::hash(&hash).into();
}
hash.to_owned()
}
#[cfg(test)]
mod test {
use std::time::Instant;
use crate::passphrase::generate_4words_passphrase;
use super::*;
#[test]
fn test_format_encrypted_seed_file() {
let seed = generate_seed();
let seed_file = format_encrypted_seed_file(&seed);
dbg!(&seed_file);
assert_eq!(seed_file.len(), 52 + 4 + SEED_FILE_PREFIX.len());
assert!(seed_file.starts_with(SEED_FILE_PREFIX.as_bytes()));
assert!(seed_file.starts_with(SEED_FILE_PREFIX.as_bytes()));
assert_eq!(encrypted_seed_file_version(&seed_file).unwrap(), 0);
assert!(seed_file.ends_with(&z32::encode(&seed).as_bytes()));
}
#[test]
fn hash() {
let passphrase = generate_4words_passphrase();
let start = Instant::now();
println!("start hashing...");
let hash = seed_encryption_key(&passphrase);
println!("final hash: {:?}", hash);
println!("{} ms", start.elapsed().as_millis());
}
}

View File

@@ -0,0 +1,33 @@
//! Encryption functions.
use crate::{Error, Result};
use rand::{Rng, RngCore};
use crate::crypto::{Key, Nonce};
/// Compute the length of a ciphertext, given the length of a plaintext.
///
/// This function returns `None` if the resulting ciphertext length would overflow a `u64`.
pub fn ciphertext_len(plaintext_len: u64) -> Option<u64> {
bessie::ciphertext_len(plaintext_len)
}
/// Encrypt a message and write the ciphertext to an existing slice.
///
/// This function does not allocate memory. However, `ciphertext.len()` must be exactly equal to
/// [`ciphertext_len(plaintext.len())`](ciphertext_len), or else this function will panic.
pub fn encrypt_to_slice(key: &Key, plaintext: &[u8], ciphertext: &mut [u8]) {
bessie::encrypt_to_slice(key, plaintext, ciphertext)
}
/// Encrypt a message and return the ciphertext as a `Vec<u8>`.
pub fn encrypt(key: &Key, plaintext: &[u8]) -> Vec<u8> {
bessie::encrypt(key, plaintext)
}
/// Decrypt a message and return the plaintext as `Result` of `Vec<u8>`.
///
/// If the ciphertext or key has been changed, decryption will return `Err`.
pub fn decrypt(key: &Key, ciphertext: &[u8]) -> Result<Vec<u8>> {
bessie::decrypt(key, ciphertext).map_err(|err| Error::Generic(err.to_string()))
}

8
kytes/src/crypto/keys.rs Normal file
View File

@@ -0,0 +1,8 @@
//! Keypairs.
use crate::crypto::Key;
/// Generate a random secret seed.
pub fn generate_seed() -> Key {
rand::random()
}

9
kytes/src/crypto/mod.rs Normal file
View File

@@ -0,0 +1,9 @@
mod encryption;
mod keys;
mod passphrase;
mod seed;
/// A 32 bytes key (encryption key or public key or shared_secret key).
pub type Key = [u8; bessie::KEY_LEN];
/// A 24 bytes Nonce or salt.
pub type Nonce = [u8; bessie::NONCE_LEN];

View File

@@ -1,3 +1,5 @@
//! EFF large wordlist passphrase generator.
use rand::seq::SliceRandom;
/// Generate a 4 words passphrase with spaces, from the [EFF large wordlist](https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt).

124
kytes/src/crypto/seed.rs Normal file
View File

@@ -0,0 +1,124 @@
//! Manage Kytes seed files.
//!
//! Seed file contains a seed encrypted with a strong passphrase.
use argon2::PasswordHasher;
use bytes::{BufMut, Bytes, BytesMut};
use crate::{
crypto::{
encryption::{ciphertext_len, decrypt, encrypt_to_slice},
keys::generate_seed,
passphrase::generate_4words_passphrase,
Key, Nonce,
},
Error, Result,
};
const SEED_SCHEME: &[u8] = b"kytes:seed:";
const VERSION: u8 = 0;
const KNOWN_VERSIONS: [u8; 1] = [0];
/// Encrypt the seed with a strong passphrase, and return an [encrypted seed
/// file](../../../design/seed.md).
pub fn encrypt_seed(seed: &Key, passphrase: &str) -> Bytes {
let encryption_key = derive_encrypiton_key(passphrase);
let mut seed_file = BytesMut::with_capacity(SEED_SCHEME.len() + 33);
seed_file.extend_from_slice(SEED_SCHEME);
let suffix_len = 1 + ciphertext_len(seed.len() as u64).unwrap() as usize;
let mut suffix = vec![0_u8; suffix_len];
suffix[0] = VERSION;
encrypt_to_slice(&encryption_key, seed, &mut suffix[1..]);
seed_file.extend_from_slice(z32::encode(&suffix).as_bytes());
seed_file.freeze()
}
pub fn decrypt_seed(seed_file: Bytes, passphrase: &str) -> Result<Vec<u8>> {
if !seed_file.starts_with(SEED_SCHEME) {
return Err(Error::Generic("Not a Kytes seed".to_string()));
}
let suffix = z32::decode(&seed_file[SEED_SCHEME.len()..])
.map_err(|_| Error::Generic("Invalid seed encoding".to_string()))?;
let version = suffix[0];
match version {
0 => decrypted_seed_v0(&suffix, passphrase),
_ => Err(Error::Generic(
"Unknown kytes seed file version".to_string(),
)),
}
}
fn decrypted_seed_v0(suffix: &[u8], passphrase: &str) -> Result<Vec<u8>> {
let encryption_key = derive_encrypiton_key(passphrase);
let encrypted_seed = &suffix[1..];
decrypt(&encryption_key, encrypted_seed)
}
fn parse_version(byte_string: &[u8]) -> Result<u8> {
// Convert byte array to string slice
let str_slice = std::str::from_utf8(byte_string)
.map_err(|_| Error::Generic("Invalid version number".to_string()))?;
str_slice
.parse::<u8>()
.map(Ok)
.map_err(|_| Error::Generic("Invalid version number".to_string()))?
}
/// Derive a secret key from a strong passphrase for encrypting/decrypting the seed.
fn derive_encrypiton_key(passphrase: &str) -> Key {
// Argon2 with default params (Argon2id v19)
let hasher = argon2::Argon2::default();
let mut encryption_key: Key = [0; 32];
hasher
.hash_password_into(
passphrase.as_bytes(),
// While this is technically a Nonce reuse, it should not be a problem
// since the encryption key is never shared or stored anywhere.
SEED_SCHEME,
&mut encryption_key,
)
// There shouldn't be any error, as we use the default params.
.unwrap();
encryption_key
}
#[cfg(test)]
mod test {
use std::time::Instant;
use super::*;
#[test]
fn test_encrypt_decrypt_seed() {
let seed = generate_seed();
let passphrase = generate_4words_passphrase();
let encrypted_seed_file = encrypt_seed(&seed, &passphrase);
dbg!(&encrypted_seed_file);
let start = Instant::now();
let decrypted_seed = decrypt_seed(encrypted_seed_file, &passphrase)
.expect("Failde to decrypt the seed file");
assert!(
start.elapsed().as_millis() > 300,
"decrypting the seed shouldn't be too fast"
);
assert_eq!(decrypted_seed, seed);
}
}

9
kytes/src/error.rs Normal file
View File

@@ -0,0 +1,9 @@
//! Main Crate Error
#[derive(thiserror::Error, Debug)]
/// Kytes crate error enum.
pub enum Error {
/// For starter, to remove as code matures.
#[error("Generic error: {0}")]
Generic(String),
}

View File

@@ -1,3 +1,9 @@
#[allow(unused)]
#![allow(unused)]
mod crypto;
mod passphrase;
mod error;
// Exports
pub use crate::error::Error;
// Alias Result to be the crate Result.
pub type Result<T, E = Error> = core::result::Result<T, E>;

View File

@@ -1,8 +1,66 @@
mod passphrase;
use clap::{Parser, Subcommand};
use passphrase::generate_4words_passphrase;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Optional name to operate on
name: Option<String>,
/// Sets a custom config file
// #[arg(short, long, value_name = "FILE")]
// config: Option<PathBuf>,
/// Turn debugging information on
#[arg(short, long, action = clap::ArgAction::Count)]
debug: u8,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
/// does testing things
Test {
/// lists test values
#[arg(short, long)]
list: bool,
},
}
fn main() {
println!("Hello, world!");
println!("{}", generate_4words_passphrase());
let cli = Cli::parse();
// You can check the value provided by positional arguments, or option arguments
if let Some(name) = cli.name.as_deref() {
println!("Value for name: {name}");
}
// if let Some(config_path) = cli.config.as_deref() {
// println!("Value for config: {}", config_path.display());
// }
// You can see how many times a particular flag or argument occurred
// Note, only flags can have multiple occurrences
match cli.debug {
0 => println!("Debug mode is off"),
1 => println!("Debug mode is kind of on"),
2 => println!("Debug mode is on"),
_ => println!("Don't be crazy"),
}
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level cmd
match &cli.command {
Some(Commands::Test { list }) => {
if *list {
println!("Printing testing lists...");
} else {
println!("Not printing testing lists...");
}
}
None => {}
}
// Continued program logic goes here...
}