├── .gitignore ├── .rustfmt.toml ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── docs └── Usage.md └── src ├── app.rs ├── main.rs ├── modules.rs └── modules ├── aes.rs ├── base.rs ├── base58.rs ├── base64.rs ├── case.rs ├── completion.rs ├── ecdsa.rs ├── ecdsa ├── p256.rs ├── p384.rs ├── secp256k1.rs └── sm2.rs ├── eddsa.rs ├── eddsa └── ed25519.rs ├── hash.rs ├── hex.rs ├── html.rs ├── number_codec.rs ├── number_system.rs ├── pbkdf2.rs ├── re.rs ├── sm4.rs ├── srdsa.rs ├── srdsa └── sr25519.rs ├── time.rs ├── unicode.rs ├── url.rs └── usage.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # These are backup files generated by rustfmt 6 | **/*.rs.bk 7 | .idea 8 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs=true 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dtool" 3 | version = "0.12.0" 4 | authors = ["GB "] 5 | description = "A command-line tool collection to assist development" 6 | categories = ["command-line-utilities"] 7 | homepage = "https://github.com/guoxbin/dtool" 8 | repository = "https://github.com/guoxbin/dtool" 9 | license = "GPL-3.0" 10 | readme = "README.md" 11 | keywords = [ "tools", "conversion", "codec", "utilities", "develop" ] 12 | edition = "2018" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | clap = "2.33.0" 18 | hex = "0.4.0" 19 | chrono = "0.4.10" 20 | bs58 = { version = "0.3.0", features = ["check"] } 21 | base64 = "0.11.0" 22 | urlencoding = "1.0.0" 23 | parity-codec = "3.2" 24 | md5 = "0.7.0" 25 | lazy_static = "1.4.0" 26 | ring = "0.16.20" 27 | sha2 = "0.8.0" 28 | sha3 = "0.8.2" 29 | ripemd160 = "0.8.0" 30 | escaper = "0.1.0" 31 | regex = "1.3.1" 32 | linked-hash-map = "0.5.6" 33 | prettytable-rs = "^0.8" 34 | madato = "0.5.3" 35 | crc = "1.8.1" 36 | heck = "0.3.1" 37 | rust-crypto = "0.2.36" 38 | yogcrypt = "0.0.0" 39 | secp256k1 = { version = "0.20", features = ["rand-std", "bitcoin_hashes"] } 40 | untrusted = "0.7.1" 41 | schnorrkel = "0.10.1" 42 | twox-hash = "1.6.1" 43 | byteorder = "1.3.1" 44 | rand = "0.8.4" 45 | p256 = "0.9.0" 46 | p384 = "0.8.0" 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dtool 2 | 3 | [![Build Status](https://travis-ci.org/guoxbin/dtool.svg?branch=master)](https://travis-ci.org/guoxbin/dtool) 4 | [![Crates.io](https://img.shields.io/crates/v/dtool)](https://crates.io/crates/dtool) 5 | 6 | `dtool` is a command-line tool collection to assist development 7 | 8 | ## Table of Contents 9 | 10 | - [Description](#description) 11 | - [Usage](#usage) 12 | - [Tips](#tips) 13 | - [Installation](#installation) 14 | 15 | ## Description 16 | 17 | Now `dtool` supports: 18 | 19 | - [Hex / UTF-8 string / binary / byte array conversion](./docs/Usage.md#hex--utf-8-string--binary--byte-array-conversion) 20 | - [Timestamp / date conversion](./docs/Usage.md#timestamp--date-conversion) 21 | - [Number 10/2/8/16 base conversion](./docs/Usage.md#number-102816-base-conversion) 22 | - [Hex / base58 conversion](./docs/Usage.md#hex--base58-conversion) 23 | - [Hex / base64 conversion](./docs/Usage.md#hex--base64-conversion) 24 | - [URL encode / decode](./docs/Usage.md#url-encode--decode) 25 | - [Number codec](./docs/Usage.md#number-codec) 26 | - [Hash (MD5, SHA-1, SHA-2, SHA-3, RIPEMD, CRC, Blake2b, SM3, Twox)](./docs/Usage.md#hash-md5-sha-1-sha-2-sha-3-ripemd-crc-blake2b-sm3-twox) 27 | - [UTF-8 string / unicode conversion](./docs/Usage.md#utf-8-string--unicode-conversion) 28 | - [HTML entity encode / decode](./docs/Usage.md#html-entity-encode--decode) 29 | - [Regex match](./docs/Usage.md#regex-match) 30 | - [Pbkdf2](./docs/Usage.md#pbkdf2) 31 | - [Case conversion (upper, lower, title, camel, pascal, snake, shouty snake, kebab, sarcasm)](./docs/Usage.md#case-conversion-upper-lower-title-camel-pascal-snake-shouty-snake-kebab-sarcasm) 32 | - [AES encrypt / decrypt](./docs/Usage.md#aes-encrypt--decrypt) 33 | - [ECDSA (Secp256k1, NIST P-256, NIST P-384, SM2)](./docs/Usage.md#ecdsa-secp256k1-nist-p-256-nist-p-384-sm2) 34 | - [SM4 encrypt / decrypt](./docs/Usage.md#sm4-encrypt--decrypt) 35 | - [EdDSA (Ed25519)](./docs/Usage.md#eddsa-ed25519) 36 | - [sr25519 signature](./docs/Usage.md#sr25519-signature) 37 | 38 | ## Usage 39 | 40 | `dtool` does different works by different sub commands: 41 | 42 | |Sub command| Desc | Example | 43 | |-----------|-------------------------------------|------------------------------| 44 | | h2s |Convert hex to UTF-8 string
v0.1.0|$ dtool h2s 0x61626364
abcd| 45 | | s2h |Convert UTF-8 string to hex
v0.1.0|$ dtool s2h abcd
0x61626364| 46 | | h2b | Convert hex to binary
v0.1.0 |$ dtool h2b 0x61626364
abcd| 47 | | b2h | Convert binary to hex
v0.1.0 |$ dtool b2h abcd
0x61626364| 48 | | ...| 49 | 50 | [View full usage document](./docs/Usage.md) 51 | 52 | * Besides the sub command `help`, `dtool` provides a sub command `usage` to show examples: 53 | 54 | ```bash 55 | $ dtool usage 56 | Usage 57 | ---------------------------------------------------------- 58 | h2s Convert hex to UTF-8 string $ dtool h2s 0x61626364 59 | v0.1.0 abcd 60 | ---------------------------------------------------------- 61 | ... 62 | ``` 63 | 64 | * You can search usage with a keyword: 65 | ```bash 66 | $ dtool usage -s md5 67 | Usage 68 | ------------------------------------------------------- 69 | hash Hex to hash $ dtool hash -a md5 0x616263 70 | MD5 0x900150983cd24fb0d6963f7d28e17f72 71 | v0.2.0 72 | ------------------------------------------------------- 73 | ``` 74 | 75 | ## Tips 76 | ### pipe 77 | convert a string to base64 78 | ``` 79 | $ echo -n abc | dtool s2h | dtool h2b64 80 | YWJj 81 | ``` 82 | 83 | convert a encoded timestamp to date 84 | ``` 85 | $ echo -n 2c28e75d | dtool nd -tu32 | dtool ts2d 86 | 2019-12-04 11:29:48 87 | ``` 88 | 89 | convert a jpeg to base64 90 | ``` 91 | $ cat pic.jpg | dtool b2h | dtool h2b64 92 | /9j/4AAQSkZJR... 93 | ``` 94 | 95 | calculate file md5 96 | ``` 97 | $ cat pic.jpg | dtool b2h | dtool hash -a md5 98 | 0x1884b72e23b0c93320bac6b050478ff4 99 | ``` 100 | 101 | ## Installation 102 | ### Homebrew 103 | ```bash 104 | $ brew install guoxbin/guoxbin/dtool 105 | ``` 106 | Recommend! Homebrew will install shell completion for bash, fish, and zsh along with `dtool` 107 | 108 | ### Arch Linux 109 | 110 | There is [an AUR package for dtool](https://aur.archlinux.org/packages/dtool/) that includes shell completion for bash, fish, and zsh. 111 | 112 | ```bash 113 | git clone https://aur.archlinux.org/dtool.git 114 | cd dtool 115 | makepkg -si 116 | ``` 117 | 118 | ### Cargo 119 | ```bash 120 | $ cargo install dtool 121 | ``` 122 | 123 | -------------------------------------------------------------------------------- /src/app.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::ModuleManager; 2 | use clap::App; 3 | 4 | pub fn build_app<'a, 'b>() -> (App<'a, 'b>, ModuleManager<'a, 'b>) { 5 | let mut app = App::new(env!("CARGO_PKG_NAME")) 6 | .version(env!("CARGO_PKG_VERSION")) 7 | .author(env!("CARGO_PKG_AUTHORS")) 8 | .about(env!("CARGO_PKG_DESCRIPTION")); 9 | 10 | let module_manager = ModuleManager::new(); 11 | let subcommands = module_manager.apps(); 12 | 13 | for subcommand in subcommands { 14 | app = app.subcommand(subcommand); 15 | } 16 | 17 | (app, module_manager) 18 | } 19 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod app; 2 | mod modules; 3 | 4 | fn main() { 5 | let (app, module_manager) = app::build_app(); 6 | 7 | let mut app_clone = app.clone(); 8 | 9 | let matches = app.get_matches(); 10 | 11 | let (name, matches) = matches.subcommand(); 12 | 13 | if let Some(matches) = matches { 14 | module_manager.run(name, matches); 15 | } else { 16 | app_clone.print_help().unwrap_or(()); 17 | println!(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/modules.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::let_and_return)] 2 | #![allow(clippy::unit_arg)] 3 | #![allow(clippy::type_complexity)] 4 | #![allow(clippy::len_zero)] 5 | #![allow(clippy::unnecessary_wraps)] 6 | #![allow(clippy::match_like_matches_macro)] 7 | #![allow(clippy::needless_range_loop)] 8 | #![allow(clippy::useless_format)] 9 | #![allow(clippy::upper_case_acronyms)] 10 | 11 | use clap::{App, ArgMatches}; 12 | use linked_hash_map::LinkedHashMap; 13 | use std::iter; 14 | mod aes; 15 | mod base; 16 | mod base58; 17 | mod base64; 18 | mod case; 19 | mod completion; 20 | mod ecdsa; 21 | mod eddsa; 22 | mod hash; 23 | mod hex; 24 | mod html; 25 | mod number_codec; 26 | mod number_system; 27 | mod pbkdf2; 28 | mod re; 29 | mod sm4; 30 | mod srdsa; 31 | mod time; 32 | mod unicode; 33 | mod url; 34 | mod usage; 35 | 36 | #[derive(Clone)] 37 | pub struct Module<'a, 'b> { 38 | pub desc: String, 39 | pub commands: Vec>, 40 | pub get_cases: fn() -> LinkedHashMap<&'static str, Vec>, //lazy 41 | } 42 | 43 | #[derive(Clone)] 44 | pub struct Command<'a, 'b> { 45 | pub app: App<'a, 'b>, 46 | pub f: fn(&ArgMatches<'a>) -> Result, String>, 47 | } 48 | 49 | #[derive(Clone)] 50 | pub struct Case { 51 | pub desc: String, 52 | pub input: Vec, 53 | pub output: Vec, 54 | pub is_example: bool, 55 | pub is_test: bool, 56 | pub since: String, 57 | } 58 | 59 | pub struct ModuleManager<'a, 'b> { 60 | modules: Vec>, 61 | commands: LinkedHashMap>, 62 | } 63 | 64 | impl<'a, 'b> ModuleManager<'a, 'b> { 65 | pub fn new() -> Self { 66 | let mut mm = Self { 67 | modules: Vec::new(), 68 | commands: LinkedHashMap::new(), 69 | }; 70 | mm.register(hex::module()); 71 | mm.register(time::module()); 72 | mm.register(number_system::module()); 73 | mm.register(base58::module()); 74 | mm.register(base64::module()); 75 | mm.register(url::module()); 76 | mm.register(number_codec::module()); 77 | mm.register(hash::module()); 78 | mm.register(unicode::module()); 79 | mm.register(html::module()); 80 | mm.register(re::module()); 81 | mm.register(pbkdf2::module()); 82 | mm.register(case::module()); 83 | mm.register(aes::module()); 84 | mm.register(ecdsa::module()); 85 | mm.register(sm4::module()); 86 | mm.register(eddsa::module()); 87 | mm.register(srdsa::module()); 88 | mm 89 | } 90 | 91 | pub fn apps(&self) -> Vec> { 92 | self.commands 93 | .iter() 94 | .map(|(_, command)| command.app.to_owned()) 95 | .chain(iter::once(usage::app())) 96 | .chain(iter::once(completion::app())) 97 | .collect() 98 | } 99 | 100 | pub fn run(&self, name: &str, matches: &ArgMatches<'a>) { 101 | let result = match name { 102 | "usage" => usage::run(matches, &self.modules), 103 | "completion" => completion::run(matches), 104 | _ => (self.commands.get(name).expect("subcommand must exist").f)(matches), 105 | }; 106 | 107 | match result { 108 | Ok(result) => result.iter().for_each(|x| println!("{}", x)), 109 | Err(e) => eprintln!("{}", e), 110 | } 111 | } 112 | 113 | fn register(&mut self, module: Module<'a, 'b>) { 114 | self.modules.push(module.clone()); 115 | for command in module.commands { 116 | self.commands 117 | .insert(command.app.get_name().to_string(), command); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/modules/aes.rs: -------------------------------------------------------------------------------- 1 | use self::Mode::{CBC, CTR, ECB}; 2 | use crate::modules::base::Hex; 3 | use crate::modules::{base, Command, Module}; 4 | use clap::{Arg, ArgMatches, SubCommand}; 5 | use crypto::aes::{cbc_decryptor, cbc_encryptor, ctr, ecb_decryptor, ecb_encryptor, KeySize}; 6 | use crypto::blockmodes::PkcsPadding; 7 | use crypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer}; 8 | use crypto::symmetriccipher::{Decryptor, Encryptor}; 9 | 10 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 11 | Module { 12 | desc: "AES encrypt / decrypt".to_string(), 13 | commands: commands(), 14 | get_cases: cases::cases, 15 | } 16 | } 17 | 18 | pub fn commands<'a, 'b>() -> Vec> { 19 | vec![ 20 | Command { 21 | app: SubCommand::with_name("aes_enc") 22 | .about("AES encrypt") 23 | .arg( 24 | Arg::with_name("INPUT") 25 | .help("Plain (Hex)") 26 | .required(false) 27 | .index(1), 28 | ) 29 | .arg( 30 | Arg::with_name("MODE") 31 | .long("mode") 32 | .short("m") 33 | .help("Mode\necb: ECB\ncbc: CBC\nctr: CTR\n") 34 | .takes_value(true) 35 | .possible_values(&["ecb", "cbc", "ctr"]) 36 | .required(true), 37 | ) 38 | .arg( 39 | Arg::with_name("KEY") 40 | .long("key") 41 | .short("k") 42 | .help("Key (Hex)") 43 | .takes_value(true) 44 | .required(true), 45 | ) 46 | .arg( 47 | Arg::with_name("IV") 48 | .long("iv") 49 | .short("i") 50 | .help("IV (Hex)") 51 | .takes_value(true) 52 | .required(false), 53 | ), 54 | f: aes_enc, 55 | }, 56 | Command { 57 | app: SubCommand::with_name("aes_dec") 58 | .about("AES decrypt") 59 | .arg( 60 | Arg::with_name("INPUT") 61 | .help("Cipher (Hex)") 62 | .required(false) 63 | .index(1), 64 | ) 65 | .arg( 66 | Arg::with_name("MODE") 67 | .long("mode") 68 | .short("m") 69 | .help("Mode\necb: ECB\ncbc: CBC\nctr: CTR\n") 70 | .takes_value(true) 71 | .possible_values(&["ecb", "cbc", "ctr"]) 72 | .required(true), 73 | ) 74 | .arg( 75 | Arg::with_name("KEY") 76 | .long("key") 77 | .short("k") 78 | .help("Key (Hex)") 79 | .takes_value(true) 80 | .required(true), 81 | ) 82 | .arg( 83 | Arg::with_name("IV") 84 | .long("iv") 85 | .short("i") 86 | .help("IV (Hex)") 87 | .takes_value(true) 88 | .required(false), 89 | ), 90 | f: aes_dec, 91 | }, 92 | ] 93 | } 94 | 95 | enum Mode { 96 | ECB, 97 | CBC { iv: Vec }, 98 | CTR { iv: Vec }, 99 | } 100 | 101 | fn aes_enc(matches: &ArgMatches) -> Result, String> { 102 | let (key_size, key, mode, input) = get_common_arg(matches)?; 103 | 104 | // cipher 105 | let result = match mode { 106 | ECB => aes_enc_ecb(key_size, &key, &input), 107 | CBC { iv } => aes_enc_cbc(key_size, &key, &input, &iv), 108 | CTR { iv } => aes_enc_ctr(key_size, &key, &input, &iv), 109 | }?; 110 | let result = Hex::from(result).into(); 111 | 112 | Ok(vec![result]) 113 | } 114 | 115 | fn aes_dec(matches: &ArgMatches) -> Result, String> { 116 | let (key_size, key, mode, input) = get_common_arg(matches)?; 117 | 118 | // plain 119 | let result = match mode { 120 | ECB => aes_dec_ecb(key_size, &key, &input), 121 | CBC { iv } => aes_dec_cbc(key_size, &key, &input, &iv), 122 | CTR { iv } => aes_dec_ctr(key_size, &key, &input, &iv), 123 | }?; 124 | let result = Hex::from(result).into(); 125 | 126 | Ok(vec![result]) 127 | } 128 | 129 | fn get_common_arg(matches: &ArgMatches) -> Result<(KeySize, Vec, Mode, Vec), String> { 130 | let input = base::input_string(matches)?; 131 | 132 | // key and key_size 133 | let key = matches 134 | .value_of("KEY") 135 | .ok_or_else(|| "Invalid key".to_string())?; 136 | let key: Vec = key.parse::().map_err(|_| "Invalid key")?.into(); 137 | let key_size = match key.len() { 138 | 16 => KeySize::KeySize128, 139 | 24 => KeySize::KeySize192, 140 | 32 => KeySize::KeySize256, 141 | _ => return Err("Invalid key size (should be 128/192/256)".to_string()), 142 | }; 143 | 144 | let get_iv = || -> Result, String> { 145 | let iv = matches 146 | .value_of("IV") 147 | .ok_or_else(|| "Invalid IV".to_string())?; 148 | let iv: Vec = iv.parse::().map_err(|_| "Invalid IV")?.into(); 149 | Ok(iv) 150 | }; 151 | 152 | // mode 153 | let mode = matches 154 | .value_of("MODE") 155 | .ok_or_else(|| "Invalid mode".to_string())?; 156 | let mode = match mode { 157 | "ecb" => ECB, 158 | "cbc" => CBC { iv: get_iv()? }, 159 | "ctr" => CTR { iv: get_iv()? }, 160 | _ => unreachable!(), 161 | }; 162 | 163 | // input 164 | let input = input.parse::().map_err(|_| "Invalid input")?.into(); 165 | 166 | Ok((key_size, key, mode, input)) 167 | } 168 | 169 | fn aes_enc_ecb(key_size: KeySize, key: &[u8], input: &[u8]) -> Result, String> { 170 | let mut a = ecb_encryptor(key_size, key, PkcsPadding); 171 | let cipher_len = cipher_length(input.len()); 172 | let mut result = vec![0u8; cipher_len]; 173 | a.encrypt( 174 | &mut RefReadBuffer::new(&input), 175 | &mut RefWriteBuffer::new(&mut result), 176 | true, 177 | ) 178 | .map_err(|_| "Enc failed")?; 179 | Ok(result) 180 | } 181 | 182 | fn aes_enc_cbc(key_size: KeySize, key: &[u8], input: &[u8], iv: &[u8]) -> Result, String> { 183 | let mut a = cbc_encryptor(key_size, key, iv, PkcsPadding); 184 | let cipher_len = cipher_length(input.len()); 185 | let mut result = vec![0u8; cipher_len]; 186 | a.encrypt( 187 | &mut RefReadBuffer::new(&input), 188 | &mut RefWriteBuffer::new(&mut result), 189 | true, 190 | ) 191 | .map_err(|_| "Enc failed")?; 192 | Ok(result) 193 | } 194 | 195 | fn aes_enc_ctr(key_size: KeySize, key: &[u8], input: &[u8], iv: &[u8]) -> Result, String> { 196 | let mut a = ctr(key_size, key, iv); 197 | let mut result = vec![0u8; input.len()]; 198 | a.encrypt( 199 | &mut RefReadBuffer::new(&input), 200 | &mut RefWriteBuffer::new(&mut result), 201 | true, 202 | ) 203 | .map_err(|_| "Enc failed")?; 204 | Ok(result) 205 | } 206 | 207 | fn aes_dec_ecb(key_size: KeySize, key: &[u8], input: &[u8]) -> Result, String> { 208 | let mut a = ecb_decryptor(key_size, key, PkcsPadding); 209 | let mut result = vec![0u8; input.len()]; 210 | let mut buffer = RefWriteBuffer::new(&mut result); 211 | a.decrypt(&mut RefReadBuffer::new(&input), &mut buffer, true) 212 | .map_err(|_| "Dec failed")?; 213 | let len = buffer.capacity() - buffer.remaining(); 214 | let mut result = result.clone(); 215 | result.truncate(len); 216 | Ok(result) 217 | } 218 | 219 | fn aes_dec_cbc(key_size: KeySize, key: &[u8], input: &[u8], iv: &[u8]) -> Result, String> { 220 | let mut a = cbc_decryptor(key_size, key, iv, PkcsPadding); 221 | let mut result = vec![0u8; input.len()]; 222 | let mut buffer = RefWriteBuffer::new(&mut result); 223 | a.decrypt(&mut RefReadBuffer::new(&input), &mut buffer, true) 224 | .map_err(|_| "Dec failed")?; 225 | let len = buffer.capacity() - buffer.remaining(); 226 | let mut result = result.clone(); 227 | result.truncate(len); 228 | Ok(result) 229 | } 230 | 231 | fn aes_dec_ctr(key_size: KeySize, key: &[u8], input: &[u8], iv: &[u8]) -> Result, String> { 232 | let mut a = ctr(key_size, key, iv); 233 | let mut result = vec![0u8; input.len()]; 234 | let mut buffer = RefWriteBuffer::new(&mut result); 235 | a.decrypt(&mut RefReadBuffer::new(&input), &mut buffer, true) 236 | .map_err(|_| "Dec failed")?; 237 | Ok(result) 238 | } 239 | 240 | const BLOCK_SIZE: usize = 16; 241 | 242 | fn cipher_length(input_len: usize) -> usize { 243 | ((input_len / BLOCK_SIZE) + 1) * BLOCK_SIZE 244 | } 245 | 246 | mod cases { 247 | use crate::modules::Case; 248 | use linked_hash_map::LinkedHashMap; 249 | 250 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 251 | vec![ 252 | ( 253 | "aes_enc", 254 | vec![ 255 | Case { 256 | desc: "KeySize 128 ECB".to_string(), 257 | input: vec![ 258 | "-k", 259 | "01010101010101010101010101010101", 260 | "-m", 261 | "ecb", 262 | "0x616263646162636461626364616263", 263 | ] 264 | .into_iter() 265 | .map(Into::into) 266 | .collect(), 267 | output: vec!["0xe89c98329f3e8b6da3e714fbba2be6d1"] 268 | .into_iter() 269 | .map(Into::into) 270 | .collect(), 271 | is_example: true, 272 | is_test: true, 273 | since: "0.6.0".to_string(), 274 | }, 275 | Case { 276 | desc: "KeySize 128 ECB".to_string(), 277 | input: vec![ 278 | "-k", 279 | "01010101010101010101010101010101", 280 | "-m", 281 | "ecb", 282 | "0x61626364616263646162636461626364", 283 | ] 284 | .into_iter() 285 | .map(Into::into) 286 | .collect(), 287 | output: vec![ 288 | "0xd7f480b25ee881f4b14d9893e6d76e7d68434d37d7f69f0e03b75bb15bc94b6c", 289 | ] 290 | .into_iter() 291 | .map(Into::into) 292 | .collect(), 293 | is_example: false, 294 | is_test: true, 295 | since: "0.6.0".to_string(), 296 | }, 297 | Case { 298 | desc: "KeySize 192 ECB".to_string(), 299 | input: vec![ 300 | "-k", 301 | "010101010101010101010101010101010101010101010101", 302 | "-m", 303 | "ecb", 304 | "0x616263646162636461626364616263", 305 | ] 306 | .into_iter() 307 | .map(Into::into) 308 | .collect(), 309 | output: vec!["0x88fe17738e31914c9166f9b101d1b028"] 310 | .into_iter() 311 | .map(Into::into) 312 | .collect(), 313 | is_example: true, 314 | is_test: true, 315 | since: "0.6.0".to_string(), 316 | }, 317 | Case { 318 | desc: "KeySize 192 ECB".to_string(), 319 | input: vec![ 320 | "-k", 321 | "010101010101010101010101010101010101010101010101", 322 | "-m", 323 | "ecb", 324 | "0x61626364616263646162636461626364", 325 | ] 326 | .into_iter() 327 | .map(Into::into) 328 | .collect(), 329 | output: vec![ 330 | "0x163d5ef52036845917bd95ef5f4adc1bc5e91d8668d614923d5e38a3f5fb895d", 331 | ] 332 | .into_iter() 333 | .map(Into::into) 334 | .collect(), 335 | is_example: false, 336 | is_test: true, 337 | since: "0.6.0".to_string(), 338 | }, 339 | Case { 340 | desc: "KeySize 256 ECB".to_string(), 341 | input: vec![ 342 | "-k", 343 | "0101010101010101010101010101010101010101010101010101010101010101", 344 | "-m", 345 | "ecb", 346 | "0x616263646162636461626364616263", 347 | ] 348 | .into_iter() 349 | .map(Into::into) 350 | .collect(), 351 | output: vec!["0x3e6bcc9d26c494b1c6971316020acd3a"] 352 | .into_iter() 353 | .map(Into::into) 354 | .collect(), 355 | is_example: true, 356 | is_test: true, 357 | since: "0.6.0".to_string(), 358 | }, 359 | Case { 360 | desc: "KeySize 256 ECB".to_string(), 361 | input: vec![ 362 | "-k", 363 | "0101010101010101010101010101010101010101010101010101010101010101", 364 | "-m", 365 | "ecb", 366 | "0x61626364616263646162636461626364", 367 | ] 368 | .into_iter() 369 | .map(Into::into) 370 | .collect(), 371 | output: vec![ 372 | "0x5bd66672cb9f17f96a2684a815efa024cce1a41155667587123071beb30ed5c8", 373 | ] 374 | .into_iter() 375 | .map(Into::into) 376 | .collect(), 377 | is_example: false, 378 | is_test: true, 379 | since: "0.6.0".to_string(), 380 | }, 381 | Case { 382 | desc: "KeySize 128 CBC".to_string(), 383 | input: vec![ 384 | "-k", 385 | "01010101010101010101010101010101", 386 | "-i", 387 | "03030303030303030303030303030303", 388 | "-m", 389 | "cbc", 390 | "0x616263646162636461626364616263", 391 | ] 392 | .into_iter() 393 | .map(Into::into) 394 | .collect(), 395 | output: vec!["0x350678b99c37ab5f68f560551e960572"] 396 | .into_iter() 397 | .map(Into::into) 398 | .collect(), 399 | is_example: true, 400 | is_test: true, 401 | since: "0.6.0".to_string(), 402 | }, 403 | Case { 404 | desc: "KeySize 128 CBC".to_string(), 405 | input: vec![ 406 | "-k", 407 | "01010101010101010101010101010101", 408 | "-i", 409 | "03030303030303030303030303030303", 410 | "-m", 411 | "cbc", 412 | "0x61626364616263646162636461626364", 413 | ] 414 | .into_iter() 415 | .map(Into::into) 416 | .collect(), 417 | output: vec![ 418 | "0x292d1fba6bf1c22fa8487591b71ac04415a5e65a5a17ada18718df37025abd1f", 419 | ] 420 | .into_iter() 421 | .map(Into::into) 422 | .collect(), 423 | is_example: false, 424 | is_test: true, 425 | since: "0.6.0".to_string(), 426 | }, 427 | Case { 428 | desc: "KeySize 192 CBC".to_string(), 429 | input: vec![ 430 | "-k", 431 | "010101010101010101010101010101010101010101010101", 432 | "-i", 433 | "03030303030303030303030303030303", 434 | "-m", 435 | "cbc", 436 | "0x616263646162636461626364616263", 437 | ] 438 | .into_iter() 439 | .map(Into::into) 440 | .collect(), 441 | output: vec!["0xbbc8ff4de1a197e67a5f8f4d7a35f9a0"] 442 | .into_iter() 443 | .map(Into::into) 444 | .collect(), 445 | is_example: true, 446 | is_test: true, 447 | since: "0.6.0".to_string(), 448 | }, 449 | Case { 450 | desc: "KeySize 192 CBC".to_string(), 451 | input: vec![ 452 | "-k", 453 | "010101010101010101010101010101010101010101010101", 454 | "-i", 455 | "03030303030303030303030303030303", 456 | "-m", 457 | "cbc", 458 | "0x61626364616263646162636461626364", 459 | ] 460 | .into_iter() 461 | .map(Into::into) 462 | .collect(), 463 | output: vec![ 464 | "0xee6e039c879dcd303599f26f992ef00f0ee4d0e9aee9d65c751be72510368a51", 465 | ] 466 | .into_iter() 467 | .map(Into::into) 468 | .collect(), 469 | is_example: false, 470 | is_test: true, 471 | since: "0.6.0".to_string(), 472 | }, 473 | Case { 474 | desc: "KeySize 256 CBC".to_string(), 475 | input: vec![ 476 | "-k", 477 | "0101010101010101010101010101010101010101010101010101010101010101", 478 | "-i", 479 | "03030303030303030303030303030303", 480 | "-m", 481 | "cbc", 482 | "0x616263646162636461626364616263", 483 | ] 484 | .into_iter() 485 | .map(Into::into) 486 | .collect(), 487 | output: vec!["0x3309a7511f007e993676a90a06391d28"] 488 | .into_iter() 489 | .map(Into::into) 490 | .collect(), 491 | is_example: true, 492 | is_test: true, 493 | since: "0.6.0".to_string(), 494 | }, 495 | Case { 496 | desc: "KeySize 256 CBC".to_string(), 497 | input: vec![ 498 | "-k", 499 | "0101010101010101010101010101010101010101010101010101010101010101", 500 | "-i", 501 | "03030303030303030303030303030303", 502 | "-m", 503 | "cbc", 504 | "0x61626364616263646162636461626364", 505 | ] 506 | .into_iter() 507 | .map(Into::into) 508 | .collect(), 509 | output: vec![ 510 | "0xe81f4279192c2eca7a1bfcf171c352bf512f1379831e41c71a83cdda50b84205", 511 | ] 512 | .into_iter() 513 | .map(Into::into) 514 | .collect(), 515 | is_example: false, 516 | is_test: true, 517 | since: "0.6.0".to_string(), 518 | }, 519 | Case { 520 | desc: "KeySize 128 CTR".to_string(), 521 | input: vec![ 522 | "-k", 523 | "01010101010101010101010101010101", 524 | "-i", 525 | "03030303030303030303030303030303", 526 | "-m", 527 | "ctr", 528 | "0x616263", 529 | ] 530 | .into_iter() 531 | .map(Into::into) 532 | .collect(), 533 | output: vec!["0x075e64"].into_iter().map(Into::into).collect(), 534 | is_example: true, 535 | is_test: true, 536 | since: "0.6.0".to_string(), 537 | }, 538 | Case { 539 | desc: "KeySize 192 CTR".to_string(), 540 | input: vec![ 541 | "-k", 542 | "010101010101010101010101010101010101010101010101", 543 | "-i", 544 | "03030303030303030303030303030303", 545 | "-m", 546 | "ctr", 547 | "0x616263", 548 | ] 549 | .into_iter() 550 | .map(Into::into) 551 | .collect(), 552 | output: vec!["0xbad37a"].into_iter().map(Into::into).collect(), 553 | is_example: true, 554 | is_test: true, 555 | since: "0.6.0".to_string(), 556 | }, 557 | Case { 558 | desc: "KeySize 256 CTR".to_string(), 559 | input: vec![ 560 | "-k", 561 | "0101010101010101010101010101010101010101010101010101010101010101", 562 | "-i", 563 | "03030303030303030303030303030303", 564 | "-m", 565 | "ctr", 566 | "0x616263", 567 | ] 568 | .into_iter() 569 | .map(Into::into) 570 | .collect(), 571 | output: vec!["0x9e5062"].into_iter().map(Into::into).collect(), 572 | is_example: true, 573 | is_test: true, 574 | since: "0.6.0".to_string(), 575 | }, 576 | ], 577 | ), 578 | ( 579 | "aes_dec", 580 | vec![ 581 | Case { 582 | desc: "KeySize 128 ECB".to_string(), 583 | input: vec![ 584 | "-k", 585 | "01010101010101010101010101010101", 586 | "-m", 587 | "ecb", 588 | "0xe89c98329f3e8b6da3e714fbba2be6d1", 589 | ] 590 | .into_iter() 591 | .map(Into::into) 592 | .collect(), 593 | output: vec!["0x616263646162636461626364616263"] 594 | .into_iter() 595 | .map(Into::into) 596 | .collect(), 597 | is_example: true, 598 | is_test: true, 599 | since: "0.6.0".to_string(), 600 | }, 601 | Case { 602 | desc: "KeySize 128 ECB".to_string(), 603 | input: vec![ 604 | "-k", 605 | "01010101010101010101010101010101", 606 | "-m", 607 | "ecb", 608 | "0x9628c4db5e8f782545876a9e1f676bdb9feda21b5557c81308f95a50e1a16232", 609 | ] 610 | .into_iter() 611 | .map(Into::into) 612 | .collect(), 613 | output: vec!["0x6162636461626364616263646162630000"] 614 | .into_iter() 615 | .map(Into::into) 616 | .collect(), 617 | is_example: false, 618 | is_test: true, 619 | since: "0.6.0".to_string(), 620 | }, 621 | Case { 622 | desc: "KeySize 128 ECB".to_string(), 623 | input: vec![ 624 | "-k", 625 | "01010101010101010101010101010101", 626 | "-m", 627 | "ecb", 628 | "0xd7f480b25ee881f4b14d9893e6d76e7d68434d37d7f69f0e03b75bb15bc94b6c", 629 | ] 630 | .into_iter() 631 | .map(Into::into) 632 | .collect(), 633 | output: vec!["0x61626364616263646162636461626364"] 634 | .into_iter() 635 | .map(Into::into) 636 | .collect(), 637 | is_example: false, 638 | is_test: true, 639 | since: "0.6.0".to_string(), 640 | }, 641 | Case { 642 | desc: "KeySize 192 ECB".to_string(), 643 | input: vec![ 644 | "-k", 645 | "010101010101010101010101010101010101010101010101", 646 | "-m", 647 | "ecb", 648 | "0x88fe17738e31914c9166f9b101d1b028", 649 | ] 650 | .into_iter() 651 | .map(Into::into) 652 | .collect(), 653 | output: vec!["0x616263646162636461626364616263"] 654 | .into_iter() 655 | .map(Into::into) 656 | .collect(), 657 | is_example: true, 658 | is_test: true, 659 | since: "0.6.0".to_string(), 660 | }, 661 | Case { 662 | desc: "KeySize 192 ECB".to_string(), 663 | input: vec![ 664 | "-k", 665 | "010101010101010101010101010101010101010101010101", 666 | "-m", 667 | "ecb", 668 | "0x163d5ef52036845917bd95ef5f4adc1bc5e91d8668d614923d5e38a3f5fb895d", 669 | ] 670 | .into_iter() 671 | .map(Into::into) 672 | .collect(), 673 | output: vec!["0x61626364616263646162636461626364"] 674 | .into_iter() 675 | .map(Into::into) 676 | .collect(), 677 | is_example: false, 678 | is_test: true, 679 | since: "0.6.0".to_string(), 680 | }, 681 | Case { 682 | desc: "KeySize 256 ECB".to_string(), 683 | input: vec![ 684 | "-k", 685 | "0101010101010101010101010101010101010101010101010101010101010101", 686 | "-m", 687 | "ecb", 688 | "0x3e6bcc9d26c494b1c6971316020acd3a", 689 | ] 690 | .into_iter() 691 | .map(Into::into) 692 | .collect(), 693 | output: vec!["0x616263646162636461626364616263"] 694 | .into_iter() 695 | .map(Into::into) 696 | .collect(), 697 | is_example: true, 698 | is_test: true, 699 | since: "0.6.0".to_string(), 700 | }, 701 | Case { 702 | desc: "KeySize 256 ECB".to_string(), 703 | input: vec![ 704 | "-k", 705 | "0101010101010101010101010101010101010101010101010101010101010101", 706 | "-m", 707 | "ecb", 708 | "0x5bd66672cb9f17f96a2684a815efa024cce1a41155667587123071beb30ed5c8", 709 | ] 710 | .into_iter() 711 | .map(Into::into) 712 | .collect(), 713 | output: vec!["0x61626364616263646162636461626364"] 714 | .into_iter() 715 | .map(Into::into) 716 | .collect(), 717 | is_example: false, 718 | is_test: true, 719 | since: "0.6.0".to_string(), 720 | }, 721 | Case { 722 | desc: "KeySize 128 CBC".to_string(), 723 | input: vec![ 724 | "-k", 725 | "01010101010101010101010101010101", 726 | "-i", 727 | "03030303030303030303030303030303", 728 | "-m", 729 | "cbc", 730 | "0x350678b99c37ab5f68f560551e960572", 731 | ] 732 | .into_iter() 733 | .map(Into::into) 734 | .collect(), 735 | output: vec!["0x616263646162636461626364616263"] 736 | .into_iter() 737 | .map(Into::into) 738 | .collect(), 739 | is_example: true, 740 | is_test: true, 741 | since: "0.6.0".to_string(), 742 | }, 743 | Case { 744 | desc: "KeySize 128 CBC".to_string(), 745 | input: vec![ 746 | "-k", 747 | "01010101010101010101010101010101", 748 | "-i", 749 | "03030303030303030303030303030303", 750 | "-m", 751 | "cbc", 752 | "0x292d1fba6bf1c22fa8487591b71ac04415a5e65a5a17ada18718df37025abd1f", 753 | ] 754 | .into_iter() 755 | .map(Into::into) 756 | .collect(), 757 | output: vec!["0x61626364616263646162636461626364"] 758 | .into_iter() 759 | .map(Into::into) 760 | .collect(), 761 | is_example: false, 762 | is_test: true, 763 | since: "0.6.0".to_string(), 764 | }, 765 | Case { 766 | desc: "KeySize 192 CBC".to_string(), 767 | input: vec![ 768 | "-k", 769 | "010101010101010101010101010101010101010101010101", 770 | "-i", 771 | "03030303030303030303030303030303", 772 | "-m", 773 | "cbc", 774 | "0xbbc8ff4de1a197e67a5f8f4d7a35f9a0", 775 | ] 776 | .into_iter() 777 | .map(Into::into) 778 | .collect(), 779 | output: vec!["0x616263646162636461626364616263"] 780 | .into_iter() 781 | .map(Into::into) 782 | .collect(), 783 | is_example: true, 784 | is_test: true, 785 | since: "0.6.0".to_string(), 786 | }, 787 | Case { 788 | desc: "KeySize 192 CBC".to_string(), 789 | input: vec![ 790 | "-k", 791 | "010101010101010101010101010101010101010101010101", 792 | "-i", 793 | "03030303030303030303030303030303", 794 | "-m", 795 | "cbc", 796 | "0xee6e039c879dcd303599f26f992ef00f0ee4d0e9aee9d65c751be72510368a51", 797 | ] 798 | .into_iter() 799 | .map(Into::into) 800 | .collect(), 801 | output: vec!["0x61626364616263646162636461626364"] 802 | .into_iter() 803 | .map(Into::into) 804 | .collect(), 805 | is_example: false, 806 | is_test: true, 807 | since: "0.6.0".to_string(), 808 | }, 809 | Case { 810 | desc: "KeySize 256 CBC".to_string(), 811 | input: vec![ 812 | "-k", 813 | "0101010101010101010101010101010101010101010101010101010101010101", 814 | "-i", 815 | "03030303030303030303030303030303", 816 | "-m", 817 | "cbc", 818 | "0x3309a7511f007e993676a90a06391d28", 819 | ] 820 | .into_iter() 821 | .map(Into::into) 822 | .collect(), 823 | output: vec!["0x616263646162636461626364616263"] 824 | .into_iter() 825 | .map(Into::into) 826 | .collect(), 827 | is_example: true, 828 | is_test: true, 829 | since: "0.6.0".to_string(), 830 | }, 831 | Case { 832 | desc: "KeySize 256 CBC".to_string(), 833 | input: vec![ 834 | "-k", 835 | "0101010101010101010101010101010101010101010101010101010101010101", 836 | "-i", 837 | "03030303030303030303030303030303", 838 | "-m", 839 | "cbc", 840 | "0xe81f4279192c2eca7a1bfcf171c352bf512f1379831e41c71a83cdda50b84205", 841 | ] 842 | .into_iter() 843 | .map(Into::into) 844 | .collect(), 845 | output: vec!["0x61626364616263646162636461626364"] 846 | .into_iter() 847 | .map(Into::into) 848 | .collect(), 849 | is_example: false, 850 | is_test: true, 851 | since: "0.6.0".to_string(), 852 | }, 853 | Case { 854 | desc: "KeySize 128 CTR".to_string(), 855 | input: vec![ 856 | "-k", 857 | "01010101010101010101010101010101", 858 | "-i", 859 | "03030303030303030303030303030303", 860 | "-m", 861 | "ctr", 862 | "0x075e64", 863 | ] 864 | .into_iter() 865 | .map(Into::into) 866 | .collect(), 867 | output: vec!["0x616263"].into_iter().map(Into::into).collect(), 868 | is_example: true, 869 | is_test: true, 870 | since: "0.6.0".to_string(), 871 | }, 872 | Case { 873 | desc: "KeySize 192 CTR".to_string(), 874 | input: vec![ 875 | "-k", 876 | "010101010101010101010101010101010101010101010101", 877 | "-i", 878 | "03030303030303030303030303030303", 879 | "-m", 880 | "ctr", 881 | "0xbad37a", 882 | ] 883 | .into_iter() 884 | .map(Into::into) 885 | .collect(), 886 | output: vec!["0x616263"].into_iter().map(Into::into).collect(), 887 | is_example: true, 888 | is_test: true, 889 | since: "0.6.0".to_string(), 890 | }, 891 | Case { 892 | desc: "KeySize 256 CTR".to_string(), 893 | input: vec![ 894 | "-k", 895 | "0101010101010101010101010101010101010101010101010101010101010101", 896 | "-i", 897 | "03030303030303030303030303030303", 898 | "-m", 899 | "ctr", 900 | "0x9e5062", 901 | ] 902 | .into_iter() 903 | .map(Into::into) 904 | .collect(), 905 | output: vec!["0x616263"].into_iter().map(Into::into).collect(), 906 | is_example: true, 907 | is_test: true, 908 | since: "0.6.0".to_string(), 909 | }, 910 | ], 911 | ), 912 | ] 913 | .into_iter() 914 | .collect() 915 | } 916 | } 917 | 918 | #[cfg(test)] 919 | mod tests { 920 | use super::*; 921 | use crate::modules::base::test::test_module; 922 | 923 | #[test] 924 | fn test_cases() { 925 | test_module(module()); 926 | } 927 | } 928 | -------------------------------------------------------------------------------- /src/modules/base.rs: -------------------------------------------------------------------------------- 1 | use clap::ArgMatches; 2 | use std::io; 3 | use std::io::{BufRead, Read}; 4 | use std::str::FromStr; 5 | 6 | pub fn input_string(matches: &ArgMatches) -> Result { 7 | match matches.value_of("INPUT") { 8 | Some(input) => Ok(input.to_string()), 9 | None => io::stdin() 10 | .lock() 11 | .lines() 12 | .collect::, io::Error>>() 13 | .map(|x| x.join("\n")) 14 | .map_err(|_| "Invalid input".to_string()), 15 | } 16 | } 17 | 18 | pub fn input_bytes(matches: &ArgMatches) -> Result, String> { 19 | match matches.value_of("INPUT") { 20 | Some(input) => Ok(input.bytes().collect::>()), 21 | None => io::stdin() 22 | .bytes() 23 | .collect::, io::Error>>() 24 | .map_err(|_| "Invalid input".to_string()), 25 | } 26 | } 27 | 28 | pub struct Hex(Vec); 29 | 30 | impl FromStr for Hex { 31 | type Err = String; 32 | fn from_str(s: &str) -> Result { 33 | let s = hex::decode(s.trim_start_matches("0x")).map_err(|_| "Invalid hex".to_string())?; 34 | Ok(Self(s)) 35 | } 36 | } 37 | 38 | impl From> for Hex { 39 | fn from(f: Vec) -> Self { 40 | Self(f) 41 | } 42 | } 43 | 44 | impl From for String { 45 | fn from(v: Hex) -> Self { 46 | format!("0x{}", hex::encode(v.0)) 47 | } 48 | } 49 | 50 | impl From for Vec { 51 | fn from(v: Hex) -> Self { 52 | v.0 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | pub mod test { 58 | use crate::modules::Module; 59 | 60 | pub fn test_module(module: Module) { 61 | let commands = module.commands; 62 | let cases = (module.get_cases)(); 63 | for command in commands { 64 | let app = &command.app; 65 | let cases = cases.get(app.get_name()); 66 | 67 | assert!(cases.is_some(), "{} should have cases", app.get_name()); 68 | if let Some(cases) = cases { 69 | assert!(cases.len() > 0, "{} should have cases", app.get_name()); 70 | 71 | let f = &command.f.clone(); 72 | for case in cases { 73 | if case.is_test { 74 | let mut ori_input = case 75 | .input 76 | .clone() 77 | .into_iter() 78 | .map(|x| { 79 | let x = x.trim_start_matches("'"); 80 | let x = x.trim_end_matches("'"); 81 | x.to_string() 82 | }) 83 | .collect(); 84 | let mut input = vec![app.get_name().to_string()]; 85 | input.append(&mut ori_input); 86 | let expected_output = Ok((&case.output).clone()); 87 | let matches = app.clone().get_matches_from(input.clone()); 88 | let output = f(&matches); 89 | assert_eq!(output, expected_output, "Test: {}", input.join(" ")); 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/modules/base58.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::base::Hex; 2 | use crate::modules::{base, Command, Module}; 3 | use clap::{Arg, ArgMatches, SubCommand}; 4 | 5 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 6 | Module { 7 | desc: "Hex / base58 conversion".to_string(), 8 | commands: commands(), 9 | get_cases: cases::cases, 10 | } 11 | } 12 | 13 | pub fn commands<'a, 'b>() -> Vec> { 14 | vec![ 15 | Command { 16 | app: SubCommand::with_name("h2b58") 17 | .about("Convert hex to base58") 18 | .arg(Arg::with_name("INPUT").required(false).index(1)), 19 | f: h2b58, 20 | }, 21 | Command { 22 | app: SubCommand::with_name("h2b58c") 23 | .about("Convert hex to base58 check") 24 | .arg(Arg::with_name("INPUT").required(false).index(1)), 25 | f: h2b58c, 26 | }, 27 | Command { 28 | app: SubCommand::with_name("b582h") 29 | .about("Convert base58 to hex") 30 | .arg(Arg::with_name("INPUT").required(false).index(1)), 31 | f: b582h, 32 | }, 33 | Command { 34 | app: SubCommand::with_name("b58c2h") 35 | .about("Convert base58 check to hex") 36 | .arg(Arg::with_name("INPUT").required(false).index(1)), 37 | f: b58c2h, 38 | }, 39 | ] 40 | } 41 | 42 | fn h2b58(matches: &ArgMatches) -> Result, String> { 43 | let input = base::input_string(matches)?; 44 | 45 | let input: Vec = input.parse::().map_err(|_| "Convert failed")?.into(); 46 | 47 | let result = bs58::encode(input).into_string(); 48 | 49 | Ok(vec![result]) 50 | } 51 | 52 | fn h2b58c(matches: &ArgMatches) -> Result, String> { 53 | let input = base::input_string(matches)?; 54 | 55 | let input: Vec = input.parse::().map_err(|_| "Convert failed")?.into(); 56 | 57 | let result = bs58::encode(input).with_check().into_string(); 58 | 59 | Ok(vec![result]) 60 | } 61 | 62 | fn b582h(matches: &ArgMatches) -> Result, String> { 63 | let input = base::input_string(matches)?; 64 | 65 | let input = bs58::decode(&input) 66 | .into_vec() 67 | .map_err(|_| "Convert failed")?; 68 | let result = Hex::from(input).into(); 69 | 70 | Ok(vec![result]) 71 | } 72 | 73 | fn b58c2h(matches: &ArgMatches) -> Result, String> { 74 | let input = base::input_string(matches)?; 75 | 76 | let input = bs58::decode(&input) 77 | .with_check(None) 78 | .into_vec() 79 | .map_err(|_| "Convert failed")?; 80 | let result = Hex::from(input).into(); 81 | 82 | Ok(vec![result]) 83 | } 84 | 85 | mod cases { 86 | use crate::modules::Case; 87 | use linked_hash_map::LinkedHashMap; 88 | 89 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 90 | vec![ 91 | ( 92 | "h2b58", 93 | vec![ 94 | Case { 95 | desc: "".to_string(), 96 | input: vec!["0x0075774f5d9963c021009a58d7d2d8e83771dd6c7a"] 97 | .into_iter() 98 | .map(Into::into) 99 | .collect(), 100 | output: vec!["12dvBhvPEPniQmBmgvj4qpJEodT7P"] 101 | .into_iter() 102 | .map(Into::into) 103 | .collect(), 104 | is_example: true, 105 | is_test: true, 106 | since: "0.1.0".to_string(), 107 | }, 108 | Case { 109 | desc: "".to_string(), 110 | input: vec!["0x41e1df5ec0fec39b5cf51d959118819ce5e64643df"] 111 | .into_iter() 112 | .map(Into::into) 113 | .collect(), 114 | output: vec!["53yFvKp7qazhe1YV6rE5ESr1iofv6"] 115 | .into_iter() 116 | .map(Into::into) 117 | .collect(), 118 | is_example: false, 119 | is_test: true, 120 | since: "0.1.0".to_string(), 121 | }, 122 | ], 123 | ), 124 | ( 125 | "h2b58c", 126 | vec![ 127 | Case { 128 | desc: "".to_string(), 129 | input: vec!["0x0075774f5d9963c021009a58d7d2d8e83771dd6c7a"] 130 | .into_iter() 131 | .map(Into::into) 132 | .collect(), 133 | output: vec!["1Bi6zFVNtntP5MtDraNrAD7e469ifsQMwF"] 134 | .into_iter() 135 | .map(Into::into) 136 | .collect(), 137 | is_example: true, 138 | is_test: true, 139 | since: "0.1.0".to_string(), 140 | }, 141 | Case { 142 | desc: "".to_string(), 143 | input: vec!["0x41e1df5ec0fec39b5cf51d959118819ce5e64643df"] 144 | .into_iter() 145 | .map(Into::into) 146 | .collect(), 147 | output: vec!["TWZWdFSL6Kcn7hfAg6SHiGixUP5efEaBtW"] 148 | .into_iter() 149 | .map(Into::into) 150 | .collect(), 151 | is_example: false, 152 | is_test: true, 153 | since: "0.1.0".to_string(), 154 | }, 155 | ], 156 | ), 157 | ( 158 | "b582h", 159 | vec![Case { 160 | desc: "".to_string(), 161 | input: vec!["12dvBhvPEPniQmBmgvj4qpJEodT7P"] 162 | .into_iter() 163 | .map(Into::into) 164 | .collect(), 165 | output: vec!["0x0075774f5d9963c021009a58d7d2d8e83771dd6c7a"] 166 | .into_iter() 167 | .map(Into::into) 168 | .collect(), 169 | is_example: true, 170 | is_test: true, 171 | since: "0.1.0".to_string(), 172 | }], 173 | ), 174 | ( 175 | "b58c2h", 176 | vec![Case { 177 | desc: "".to_string(), 178 | input: vec!["1Bi6zFVNtntP5MtDraNrAD7e469ifsQMwF"] 179 | .into_iter() 180 | .map(Into::into) 181 | .collect(), 182 | output: vec!["0x0075774f5d9963c021009a58d7d2d8e83771dd6c7a"] 183 | .into_iter() 184 | .map(Into::into) 185 | .collect(), 186 | is_example: true, 187 | is_test: true, 188 | since: "0.1.0".to_string(), 189 | }], 190 | ), 191 | ] 192 | .into_iter() 193 | .collect() 194 | } 195 | } 196 | 197 | #[cfg(test)] 198 | mod tests { 199 | use super::*; 200 | use crate::modules::base::test::test_module; 201 | 202 | #[test] 203 | fn test_cases() { 204 | test_module(module()); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/modules/base64.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::base::Hex; 2 | use crate::modules::{base, Command, Module}; 3 | use clap::{Arg, ArgMatches, SubCommand}; 4 | 5 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 6 | Module { 7 | desc: "Hex / base64 conversion".to_string(), 8 | commands: commands(), 9 | get_cases: cases::cases, 10 | } 11 | } 12 | 13 | pub fn commands<'a, 'b>() -> Vec> { 14 | vec![ 15 | Command { 16 | app: SubCommand::with_name("h2b64") 17 | .about("Convert hex to base64") 18 | .arg(Arg::with_name("INPUT").required(false).index(1)), 19 | f: h2b64, 20 | }, 21 | Command { 22 | app: SubCommand::with_name("b642h") 23 | .about("Convert base64 to hex") 24 | .arg(Arg::with_name("INPUT").required(false).index(1)), 25 | f: b642h, 26 | }, 27 | ] 28 | } 29 | 30 | fn h2b64(matches: &ArgMatches) -> Result, String> { 31 | let input = base::input_string(matches)?; 32 | 33 | let input: Vec = input.parse::().map_err(|_| "Convert failed")?.into(); 34 | 35 | let result = base64::encode(&input); 36 | 37 | Ok(vec![result]) 38 | } 39 | 40 | fn b642h(matches: &ArgMatches) -> Result, String> { 41 | let input = base::input_string(matches)?; 42 | 43 | let result = base64::decode(&input).map_err(|_| "Convert failed")?; 44 | let result = Hex::from(result).into(); 45 | 46 | Ok(vec![result]) 47 | } 48 | 49 | mod cases { 50 | use crate::modules::Case; 51 | use linked_hash_map::LinkedHashMap; 52 | 53 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 54 | vec![ 55 | ( 56 | "h2b64", 57 | vec![Case { 58 | desc: "".to_string(), 59 | input: vec!["0x616263"].into_iter().map(Into::into).collect(), 60 | output: vec!["YWJj"].into_iter().map(Into::into).collect(), 61 | is_example: true, 62 | is_test: true, 63 | since: "0.1.0".to_string(), 64 | }], 65 | ), 66 | ( 67 | "b642h", 68 | vec![Case { 69 | desc: "".to_string(), 70 | input: vec!["YWJj"].into_iter().map(Into::into).collect(), 71 | output: vec!["0x616263"].into_iter().map(Into::into).collect(), 72 | is_example: true, 73 | is_test: true, 74 | since: "0.1.0".to_string(), 75 | }], 76 | ), 77 | ] 78 | .into_iter() 79 | .collect() 80 | } 81 | } 82 | 83 | #[cfg(test)] 84 | mod tests { 85 | use super::*; 86 | use crate::modules::base::test::test_module; 87 | 88 | #[test] 89 | fn test_cases() { 90 | test_module(module()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/modules/case.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::{base, Command, Module}; 2 | use clap::{Arg, ArgMatches, SubCommand}; 3 | use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase, TitleCase}; 4 | 5 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 6 | Module { 7 | desc: "Case conversion (upper, lower, title, camel, pascal, snake, shouty snake, kebab, sarcasm)" 8 | .to_string(), 9 | commands: commands(), 10 | get_cases: cases::cases, 11 | } 12 | } 13 | 14 | pub fn commands<'a, 'b>() -> Vec> { 15 | vec![Command { 16 | app: SubCommand::with_name("case") 17 | .about("Case conversion") 18 | .arg( 19 | Arg::with_name("TYPE") 20 | .long("type") 21 | .short("t") 22 | .help( 23 | "Case type\nupper: GOOD TOOL\nlower: good tool\ntitle: Good Tool\n\ 24 | camel: goodTool\npascal: GoodTool\nsnake: good_tool\nshouty_snake: GOOD_TOOL\n\ 25 | kebab: good-tool\nsarcasm: gOoD tOoL", 26 | ) 27 | .takes_value(true) 28 | .required(false), 29 | ) 30 | .arg(Arg::with_name("INPUT").required(false).index(1)), 31 | f: case, 32 | }] 33 | } 34 | 35 | fn case(matches: &ArgMatches) -> Result, String> { 36 | let input = base::input_string(matches)?; 37 | 38 | let t = matches.value_of("TYPE").unwrap_or_default(); 39 | 40 | let result = match t { 41 | "upper" => input.to_uppercase(), 42 | "lower" => input.to_lowercase(), 43 | "title" => input.to_title_case(), 44 | "camel" => input.to_mixed_case(), 45 | "pascal" => input.to_camel_case(), //heck camel casing is pascal casing 46 | "snake" => input.to_snake_case(), 47 | "shouty_snake" => input.to_shouty_snake_case(), 48 | "kebab" => input.to_kebab_case(), 49 | "sarcasm" => to_sarcasm_case(&input), 50 | _ => { 51 | format!( 52 | "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n", 53 | input.to_uppercase(), 54 | input.to_lowercase(), 55 | input.to_title_case(), 56 | input.to_mixed_case(), 57 | input.to_camel_case(), 58 | input.to_snake_case(), 59 | input.to_shouty_snake_case(), 60 | input.to_kebab_case(), 61 | to_sarcasm_case(&input), 62 | ) 63 | } 64 | }; 65 | 66 | Ok(vec![result]) 67 | } 68 | 69 | fn to_sarcasm_case(input: &str) -> String { 70 | let lowercased = input.to_lowercase(); 71 | 72 | #[derive(PartialEq)] 73 | enum Case { 74 | Upper, 75 | Lower, 76 | } 77 | let mut case = Case::Lower; 78 | let result = lowercased 79 | .chars() 80 | .map(|c| { 81 | let result = { 82 | if c == ' ' { 83 | case = Case::Lower; 84 | " ".to_string() 85 | } else if case == Case::Upper { 86 | case = Case::Lower; 87 | c.to_uppercase().to_string() 88 | } else { 89 | case = Case::Upper; 90 | c.to_lowercase().to_string() 91 | } 92 | }; 93 | result 94 | }) 95 | .collect(); 96 | result 97 | } 98 | 99 | mod cases { 100 | use crate::modules::Case; 101 | use linked_hash_map::LinkedHashMap; 102 | 103 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 104 | vec![( 105 | "case", 106 | vec![ 107 | Case { 108 | desc: "Upper case".to_string(), 109 | input: vec!["-t", "upper", "'good tool'"] 110 | .into_iter() 111 | .map(Into::into) 112 | .collect(), 113 | output: vec!["GOOD TOOL"].into_iter().map(Into::into).collect(), 114 | is_example: true, 115 | is_test: true, 116 | since: "0.5.0".to_string(), 117 | }, 118 | Case { 119 | desc: "Lower case".to_string(), 120 | input: vec!["-t", "lower", "'GOOD TOOL'"] 121 | .into_iter() 122 | .map(Into::into) 123 | .collect(), 124 | output: vec!["good tool"].into_iter().map(Into::into).collect(), 125 | is_example: true, 126 | is_test: true, 127 | since: "0.5.0".to_string(), 128 | }, 129 | Case { 130 | desc: "Title case".to_string(), 131 | input: vec!["-t", "title", "'GOOD TOOL'"] 132 | .into_iter() 133 | .map(Into::into) 134 | .collect(), 135 | output: vec!["Good Tool"].into_iter().map(Into::into).collect(), 136 | is_example: true, 137 | is_test: true, 138 | since: "0.5.0".to_string(), 139 | }, 140 | Case { 141 | desc: "Camel case".to_string(), 142 | input: vec!["-t", "camel", "'GOOD TOOL'"] 143 | .into_iter() 144 | .map(Into::into) 145 | .collect(), 146 | output: vec!["goodTool"].into_iter().map(Into::into).collect(), 147 | is_example: true, 148 | is_test: true, 149 | since: "0.5.0".to_string(), 150 | }, 151 | Case { 152 | desc: "Pascal case".to_string(), 153 | input: vec!["-t", "pascal", "'GOOD TOOL'"] 154 | .into_iter() 155 | .map(Into::into) 156 | .collect(), 157 | output: vec!["GoodTool"].into_iter().map(Into::into).collect(), 158 | is_example: true, 159 | is_test: true, 160 | since: "0.5.0".to_string(), 161 | }, 162 | Case { 163 | desc: "Snake case".to_string(), 164 | input: vec!["-t", "snake", "GoodTool"] 165 | .into_iter() 166 | .map(Into::into) 167 | .collect(), 168 | output: vec!["good_tool"].into_iter().map(Into::into).collect(), 169 | is_example: true, 170 | is_test: true, 171 | since: "0.5.0".to_string(), 172 | }, 173 | Case { 174 | desc: "Shouty snake case".to_string(), 175 | input: vec!["-t", "shouty_snake", "GoodTool"] 176 | .into_iter() 177 | .map(Into::into) 178 | .collect(), 179 | output: vec!["GOOD_TOOL"].into_iter().map(Into::into).collect(), 180 | is_example: true, 181 | is_test: true, 182 | since: "0.5.0".to_string(), 183 | }, 184 | Case { 185 | desc: "Kebab case".to_string(), 186 | input: vec!["-t", "kebab", "GoodTool"] 187 | .into_iter() 188 | .map(Into::into) 189 | .collect(), 190 | output: vec!["good-tool"].into_iter().map(Into::into).collect(), 191 | is_example: true, 192 | is_test: true, 193 | since: "0.5.0".to_string(), 194 | }, 195 | Case { 196 | desc: "Sarcasm case".to_string(), 197 | input: vec!["-t", "sarcasm", "good tool"] 198 | .into_iter() 199 | .map(Into::into) 200 | .collect(), 201 | output: vec!["gOoD tOoL"].into_iter().map(Into::into).collect(), 202 | is_example: true, 203 | is_test: true, 204 | since: "0.9.0".to_string(), 205 | }, 206 | Case { 207 | desc: "All cases".to_string(), 208 | input: vec!["good tool"] 209 | .into_iter() 210 | .map(Into::into) 211 | .collect(), 212 | output: vec!["GOOD TOOL\ngood tool\nGood Tool\ngoodTool\nGoodTool\ngood_tool\nGOOD_TOOL\ngood-tool\ngOoD tOoL\n"].into_iter().map(Into::into).collect(), 213 | is_example: true, 214 | is_test: true, 215 | since: "0.11.0".to_string(), 216 | }, 217 | ], 218 | )] 219 | .into_iter() 220 | .collect() 221 | } 222 | } 223 | 224 | #[cfg(test)] 225 | mod tests { 226 | use super::*; 227 | use crate::modules::base::test::test_module; 228 | 229 | #[test] 230 | fn test_cases() { 231 | test_module(module()); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/modules/completion.rs: -------------------------------------------------------------------------------- 1 | use crate::app; 2 | use clap::{App, Arg, ArgMatches, Shell, SubCommand}; 3 | use std::io::stdout; 4 | use std::str::FromStr; 5 | 6 | pub fn app<'a, 'b>() -> App<'a, 'b> { 7 | SubCommand::with_name("completion") 8 | .about("Generate completion") 9 | .arg( 10 | Arg::with_name("SHELL") 11 | .long("shell") 12 | .short("s") 13 | .help("Shell") 14 | .takes_value(true) 15 | .possible_values(&Shell::variants()) 16 | .required(true), 17 | ) 18 | } 19 | 20 | pub fn run(matches: &ArgMatches) -> Result, String> { 21 | let shell = matches 22 | .value_of("SHELL") 23 | .ok_or_else(|| "Invalid shell".to_string())?; 24 | let shell = Shell::from_str(shell)?; 25 | 26 | let (mut app, _) = app::build_app(); 27 | app.gen_completions_to("dtool", shell, &mut stdout()); 28 | 29 | Ok(vec![]) 30 | } 31 | -------------------------------------------------------------------------------- /src/modules/ecdsa.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::base::Hex; 2 | use crate::modules::{base, Command, Module}; 3 | use clap::{Arg, ArgMatches, SubCommand}; 4 | use lazy_static::lazy_static; 5 | use std::collections::HashMap; 6 | 7 | mod p256; 8 | mod p384; 9 | mod secp256k1; 10 | mod sm2; 11 | 12 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 13 | Module { 14 | desc: "ECDSA (Secp256k1, NIST P-256, NIST P-384, SM2)".to_string(), 15 | commands: commands(), 16 | get_cases: cases::cases, 17 | } 18 | } 19 | 20 | struct Curve { 21 | name: &'static str, 22 | help: &'static str, 23 | gk_f: fn(compress: bool) -> Result<(Vec, Vec), String>, 24 | sign_f: fn( 25 | secret_key: Vec, 26 | message: Vec, 27 | sig_form: SignatureFormEnum, 28 | ) -> Result, String>, 29 | verify_f: fn( 30 | public_key: Vec, 31 | sig: Vec, 32 | message: Vec, 33 | sig_form: SignatureFormEnum, 34 | ) -> Result<(), String>, 35 | pk_f: fn(secret_key: Vec, compress: bool) -> Result, String>, 36 | } 37 | 38 | #[derive(Clone)] 39 | pub enum SignatureFormEnum { 40 | Der, 41 | Fixed, 42 | } 43 | 44 | struct SignatureForm { 45 | name: &'static str, 46 | help: &'static str, 47 | e: SignatureFormEnum, 48 | } 49 | 50 | lazy_static! { 51 | static ref RAW_CURVES: Vec = vec![ 52 | Curve { 53 | name: "secp256k1", 54 | help: "Secp256k1", 55 | gk_f: secp256k1::ec_gk_secp256k1, 56 | sign_f: secp256k1::ec_sign_secp256k1, 57 | verify_f: secp256k1::ec_verify_secp256k1, 58 | pk_f: secp256k1::ec_pk_secp256k1, 59 | }, 60 | Curve { 61 | name: "p256", 62 | help: "NIST P-256", 63 | gk_f: p256::ec_gk_p256, 64 | sign_f: p256::ec_sign_p256, 65 | verify_f: p256::ec_verify_p256, 66 | pk_f: p256::ec_pk_p256, 67 | }, 68 | Curve { 69 | name: "p384", 70 | help: "NIST P-384", 71 | gk_f: p384::ec_gk_p384, 72 | sign_f: p384::ec_sign_p384, 73 | verify_f: p384::ec_verify_p384, 74 | pk_f: p384::ec_pk_p384, 75 | }, 76 | Curve { 77 | name: "sm2", 78 | help: "Chinese National Standard SM2", 79 | gk_f: sm2::ec_gk_sm2, 80 | sign_f: sm2::ec_sign_sm2, 81 | verify_f: sm2::ec_verify_sm2, 82 | pk_f: sm2::ec_pk_sm2, 83 | }, 84 | ]; 85 | static ref RAW_SIGNATURE_FORMS: Vec = vec![ 86 | SignatureForm { 87 | name: "der", 88 | help: "ASN1 DER", 89 | e: SignatureFormEnum::Der, 90 | }, 91 | SignatureForm { 92 | name: "fixed", 93 | help: "Fixed", 94 | e: SignatureFormEnum::Fixed, 95 | }, 96 | ]; 97 | static ref CURVES: HashMap<&'static str, &'static Curve> = 98 | RAW_CURVES.iter().map(|x| (x.name, x)).collect(); 99 | static ref CURVE_NAMES: Vec<&'static str> = RAW_CURVES.iter().map(|x| x.name).collect(); 100 | static ref CURVE_HELP: String = "Curve\n".to_string() 101 | + &RAW_CURVES 102 | .iter() 103 | .map(|a| { format!("{}: {}", a.name, a.help) }) 104 | .collect::>() 105 | .join("\n") 106 | + "\n"; 107 | static ref SIGNATURE_FORMS: HashMap<&'static str, &'static SignatureForm> = 108 | RAW_SIGNATURE_FORMS.iter().map(|x| (x.name, x)).collect(); 109 | static ref SIGNATURE_FORM_NAMES: Vec<&'static str> = 110 | RAW_SIGNATURE_FORMS.iter().map(|x| x.name).collect(); 111 | static ref SIGNATURE_FORM_HELP: String = "Signature form\n".to_string() 112 | + &RAW_SIGNATURE_FORMS 113 | .iter() 114 | .map(|a| { format!("{}: {}", a.name, a.help) }) 115 | .collect::>() 116 | .join("\n") 117 | + "\n"; 118 | } 119 | 120 | pub fn commands<'a, 'b>() -> Vec> { 121 | vec![ 122 | Command { 123 | app: SubCommand::with_name("ec_gk") 124 | .about("Elliptic-curve generate key pair (Secret key, Public key)") 125 | .arg( 126 | Arg::with_name("CURVE") 127 | .long("curve") 128 | .short("c") 129 | .help(&CURVE_HELP) 130 | .takes_value(true) 131 | .possible_values(&CURVE_NAMES) 132 | .required(true), 133 | ) 134 | .arg( 135 | Arg::with_name("COMPRESS") 136 | .long("compress") 137 | .short("C") 138 | .help("Compress") 139 | .required(false), 140 | ), 141 | f: ec_gk, 142 | }, 143 | Command { 144 | app: SubCommand::with_name("ec_sign") 145 | .about("Elliptic-curve sign") 146 | .arg( 147 | Arg::with_name("INPUT") 148 | .help("Message (Hex)") 149 | .required(false) 150 | .index(1), 151 | ) 152 | .arg( 153 | Arg::with_name("CURVE") 154 | .long("curve") 155 | .short("c") 156 | .help(&CURVE_HELP) 157 | .takes_value(true) 158 | .possible_values(&CURVE_NAMES) 159 | .required(true), 160 | ) 161 | .arg( 162 | Arg::with_name("SECRET_KEY") 163 | .long("secret-key") 164 | .short("s") 165 | .help("Secret key (Private key, Hex)") 166 | .takes_value(true) 167 | .required(true), 168 | ) 169 | .arg( 170 | Arg::with_name("SIGNATURE_FORM") 171 | .long("sig-form") 172 | .short("f") 173 | .help(&SIGNATURE_FORM_HELP) 174 | .takes_value(true) 175 | .possible_values(&SIGNATURE_FORM_NAMES) 176 | .default_value("fixed") 177 | .required(false), 178 | ), 179 | f: ec_sign, 180 | }, 181 | Command { 182 | app: SubCommand::with_name("ec_verify") 183 | .about("Elliptic-curve verify") 184 | .arg( 185 | Arg::with_name("INPUT") 186 | .help("Message (Hex)") 187 | .required(false) 188 | .index(1), 189 | ) 190 | .arg( 191 | Arg::with_name("CURVE") 192 | .long("curve") 193 | .short("c") 194 | .help(&CURVE_HELP) 195 | .takes_value(true) 196 | .possible_values(&CURVE_NAMES) 197 | .required(true), 198 | ) 199 | .arg( 200 | Arg::with_name("PUBLIC_KEY") 201 | .long("public-key") 202 | .short("p") 203 | .help("Public key (Hex)") 204 | .takes_value(true) 205 | .required(true), 206 | ) 207 | .arg( 208 | Arg::with_name("SIGNATURE") 209 | .long("sig") 210 | .short("S") 211 | .help("Signature (Hex)") 212 | .takes_value(true) 213 | .required(true), 214 | ) 215 | .arg( 216 | Arg::with_name("SIGNATURE_FORM") 217 | .long("sig-form") 218 | .short("f") 219 | .help(&SIGNATURE_FORM_HELP) 220 | .takes_value(true) 221 | .possible_values(&SIGNATURE_FORM_NAMES) 222 | .default_value("fixed") 223 | .required(false), 224 | ), 225 | f: ec_verify, 226 | }, 227 | Command { 228 | app: SubCommand::with_name("ec_pk") 229 | .about("Elliptic-curve calculate public key") 230 | .arg( 231 | Arg::with_name("CURVE") 232 | .long("curve") 233 | .short("c") 234 | .help(&CURVE_HELP) 235 | .takes_value(true) 236 | .possible_values(&CURVE_NAMES) 237 | .required(true), 238 | ) 239 | .arg( 240 | Arg::with_name("SECRET_KEY") 241 | .long("secret-key") 242 | .short("s") 243 | .help("Secret key (Private key, Hex)") 244 | .takes_value(true) 245 | .required(false), 246 | ) 247 | .arg( 248 | Arg::with_name("COMPRESS") 249 | .long("compress") 250 | .short("C") 251 | .help("Compress") 252 | .required(false), 253 | ), 254 | f: ec_pk, 255 | }, 256 | ] 257 | } 258 | 259 | fn ec_gk(matches: &ArgMatches) -> Result, String> { 260 | let curve = matches.value_of("CURVE").ok_or("Invalid curve")?; 261 | 262 | let curve = CURVES.get(curve).ok_or("Invalid curve")?; 263 | 264 | let compress = matches.is_present("COMPRESS"); 265 | 266 | let (private_key, public_key) = (curve.gk_f)(compress)?; 267 | 268 | let (private_key, public_key): (String, String) = 269 | (Hex::from(private_key).into(), Hex::from(public_key).into()); 270 | 271 | let result = format!("({}, {})", private_key, public_key); 272 | 273 | Ok(vec![result]) 274 | } 275 | 276 | fn ec_sign(matches: &ArgMatches) -> Result, String> { 277 | let curve = matches.value_of("CURVE").ok_or("Invalid curve")?; 278 | 279 | let curve = CURVES.get(curve).ok_or("Invalid curve")?; 280 | 281 | let secret_key = matches.value_of("SECRET_KEY").ok_or("Invalid secret key")?; 282 | let secret_key: Vec = secret_key 283 | .parse::() 284 | .map_err(|_| "Invalid secret key")? 285 | .into(); 286 | 287 | let input = base::input_string(matches)?; 288 | let input: Vec = input.parse::().map_err(|_| "Invalid input")?.into(); 289 | 290 | let sig_form = matches 291 | .value_of("SIGNATURE_FORM") 292 | .ok_or("Invalid signature form")?; 293 | let sig_form = SIGNATURE_FORMS 294 | .get(sig_form) 295 | .ok_or("Invalid signature form")? 296 | .e 297 | .clone(); 298 | 299 | let sig = (curve.sign_f)(secret_key, input, sig_form)?; 300 | 301 | let result = Hex::from(sig).into(); 302 | 303 | Ok(vec![result]) 304 | } 305 | 306 | fn ec_verify(matches: &ArgMatches) -> Result, String> { 307 | let curve = matches.value_of("CURVE").ok_or("Invalid curve")?; 308 | 309 | let curve = CURVES.get(curve).ok_or("Invalid curve")?; 310 | 311 | let public_key = matches.value_of("PUBLIC_KEY").ok_or("Invalid public key")?; 312 | let public_key: Vec = public_key 313 | .parse::() 314 | .map_err(|_| "Invalid secret key")? 315 | .into(); 316 | 317 | let sig_form = matches 318 | .value_of("SIGNATURE_FORM") 319 | .ok_or("Invalid signature form")?; 320 | let sig_form = SIGNATURE_FORMS 321 | .get(sig_form) 322 | .ok_or("Invalid signature form")? 323 | .e 324 | .clone(); 325 | 326 | let sig = matches.value_of("SIGNATURE").ok_or("Invalid signature")?; 327 | let sig: Vec = sig.parse::().map_err(|_| "Invalid signature")?.into(); 328 | 329 | let input = base::input_string(matches)?; 330 | let input: Vec = input.parse::().map_err(|_| "Invalid input")?.into(); 331 | 332 | (curve.verify_f)(public_key, sig, input, sig_form)?; 333 | 334 | let result = "true".to_string(); 335 | 336 | Ok(vec![result]) 337 | } 338 | 339 | fn ec_pk(matches: &ArgMatches) -> Result, String> { 340 | let curve = matches.value_of("CURVE").ok_or("Invalid curve")?; 341 | 342 | let curve = CURVES.get(curve).ok_or("Invalid curve")?; 343 | 344 | let secret_key = matches.value_of("SECRET_KEY").ok_or("Invalid secret key")?; 345 | let secret_key: Vec = secret_key 346 | .parse::() 347 | .map_err(|_| "Invalid secret key")? 348 | .into(); 349 | 350 | let compress = matches.is_present("COMPRESS"); 351 | 352 | let public_key = (curve.pk_f)(secret_key, compress)?; 353 | 354 | let result = Hex::from(public_key).into(); 355 | 356 | Ok(vec![result]) 357 | } 358 | 359 | mod cases { 360 | use super::p256; 361 | use super::p384; 362 | use super::secp256k1; 363 | use super::sm2; 364 | use crate::modules::Case; 365 | use linked_hash_map::LinkedHashMap; 366 | use std::iter::empty; 367 | 368 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 369 | empty() 370 | .chain(secp256k1::cases()) 371 | .chain(p256::cases()) 372 | .chain(p384::cases()) 373 | .chain(sm2::cases()) 374 | .fold(LinkedHashMap::new(), |mut map, (name, mut cases)| { 375 | let list = map.entry(name).or_insert(vec![]); 376 | list.append(&mut cases); 377 | map 378 | }) 379 | } 380 | } 381 | 382 | #[cfg(test)] 383 | mod tests { 384 | use super::*; 385 | use crate::modules::base::test::test_module; 386 | 387 | #[test] 388 | fn test_cases() { 389 | test_module(module()); 390 | } 391 | } 392 | -------------------------------------------------------------------------------- /src/modules/ecdsa/p256.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::ecdsa::SignatureFormEnum; 2 | use crate::modules::Case; 3 | use linked_hash_map::LinkedHashMap; 4 | use p256::{elliptic_curve::sec1::ToEncodedPoint, SecretKey}; 5 | use rand::thread_rng; 6 | use ring::{ 7 | rand::SystemRandom, 8 | signature::{ 9 | EcdsaKeyPair, VerificationAlgorithm, ECDSA_P256_SHA256_ASN1, 10 | ECDSA_P256_SHA256_ASN1_SIGNING, ECDSA_P256_SHA256_FIXED, ECDSA_P256_SHA256_FIXED_SIGNING, 11 | }, 12 | }; 13 | use untrusted::Input; 14 | 15 | pub fn ec_gk_p256(compress: bool) -> Result<(Vec, Vec), String> { 16 | let secret_key = SecretKey::random(&mut thread_rng()); 17 | let public_key = secret_key.public_key(); 18 | 19 | let secret_key = secret_key.as_scalar_bytes().as_bytes().as_slice().to_vec(); 20 | let public_key = public_key.to_encoded_point(compress).as_bytes().to_vec(); 21 | 22 | Ok((secret_key, public_key)) 23 | } 24 | 25 | pub fn ec_sign_p256( 26 | secret_key: Vec, 27 | message: Vec, 28 | sig_form: SignatureFormEnum, 29 | ) -> Result, String> { 30 | let secret_key_obj = SecretKey::from_bytes(&secret_key).map_err(|_| "Invalid secret key")?; 31 | let public_key = secret_key_obj.public_key(); 32 | let public_key = public_key.to_encoded_point(false); 33 | let public_key = public_key.as_bytes(); 34 | 35 | let algo = match sig_form { 36 | SignatureFormEnum::Fixed => &ECDSA_P256_SHA256_FIXED_SIGNING, 37 | SignatureFormEnum::Der => &ECDSA_P256_SHA256_ASN1_SIGNING, 38 | }; 39 | 40 | let pair = EcdsaKeyPair::from_private_key_and_public_key(&algo, &secret_key, public_key) 41 | .map_err(|_| "Invalid secret key")?; 42 | let sig = pair 43 | .sign(&SystemRandom::new(), &message) 44 | .map_err(|_| "Failed to sign")?; 45 | 46 | Ok(sig.as_ref().to_vec()) 47 | } 48 | 49 | pub fn ec_verify_p256( 50 | public_key: Vec, 51 | sig: Vec, 52 | message: Vec, 53 | sig_form: SignatureFormEnum, 54 | ) -> Result<(), String> { 55 | let algo = match sig_form { 56 | SignatureFormEnum::Fixed => &ECDSA_P256_SHA256_FIXED, 57 | SignatureFormEnum::Der => &ECDSA_P256_SHA256_ASN1, 58 | }; 59 | 60 | let result = algo 61 | .verify( 62 | Input::from(&public_key), 63 | Input::from(&message), 64 | Input::from(&sig), 65 | ) 66 | .map_err(|e| format!("Invalid signature: {}", e))?; 67 | 68 | Ok(result) 69 | } 70 | 71 | pub fn ec_pk_p256(secret_key: Vec, compress: bool) -> Result, String> { 72 | let secret_key_obj = SecretKey::from_bytes(&secret_key).map_err(|_| "Invalid secret key")?; 73 | let public_key = secret_key_obj.public_key(); 74 | let public_key = public_key.to_encoded_point(compress); 75 | let public_key = public_key.as_bytes().to_vec(); 76 | Ok(public_key) 77 | } 78 | 79 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 80 | vec![ 81 | ("ec_gk", 82 | vec![ 83 | Case { 84 | desc: "P-256".to_string(), 85 | input: vec!["-c", "p256"].into_iter().map(Into::into).collect(), 86 | output: vec!["(0xf0b3b41add2d79932cdf2a4ba083c16e72647ddcd8718e2187d1567ed5a611c9, 0x045c79019e39199effa07576de6e3745fa1dba402854314aef05790e9e827cf7782ac5feb26e28039f94d73078c57b5f29be14ef9da57cb53e16e2839bdbbee630)"].into_iter().map(Into::into).collect(), 87 | is_example: true, 88 | is_test: false, 89 | since: "0.7.0".to_string(), 90 | }, 91 | ]), 92 | ("ec_sign", 93 | vec![ 94 | Case { 95 | desc: "P-256".to_string(), 96 | input: vec!["-c", "p256", "-s", "0xf0b3b41add2d79932cdf2a4ba083c16e72647ddcd8718e2187d1567ed5a611c9", "0x616263"].into_iter().map(Into::into).collect(), 97 | output: vec!["0x495f62f272440bd0621d27e97d60c57a0cdaef1cc2434c454eae833bb2111cabb91a79328ee766f720a888b14e0f6037eb8a397dcd9bc9f4c18b9b923a81cc69"].into_iter().map(Into::into).collect(), 98 | is_example: true, 99 | is_test: false, 100 | since: "0.7.0".to_string(), 101 | }, 102 | Case { 103 | desc: "P-256 DER signature form".to_string(), 104 | input: vec!["-c", "p256", "-s", "0xf0b3b41add2d79932cdf2a4ba083c16e72647ddcd8718e2187d1567ed5a611c9", "-f", "der", "0x616263"].into_iter().map(Into::into).collect(), 105 | output: vec!["0x3045022100ed94d4f7022cc2335ef5e34432fed541932e2c2b0c1430e2d51c06e66320302b022002cc2e13e6f5bde7f079a026399e2a6012c5ce4ad2babbe8e1e3444010b72d78"].into_iter().map(Into::into).collect(), 106 | is_example: false, 107 | is_test: false, 108 | since: "0.7.0".to_string(), 109 | }, 110 | ]), 111 | ("ec_verify", 112 | vec![ 113 | Case { 114 | desc: "P-256".to_string(), 115 | input: vec!["-c", "p256", "-p", "0x045c79019e39199effa07576de6e3745fa1dba402854314aef05790e9e827cf7782ac5feb26e28039f94d73078c57b5f29be14ef9da57cb53e16e2839bdbbee630", "-S", 116 | "0x495f62f272440bd0621d27e97d60c57a0cdaef1cc2434c454eae833bb2111cabb91a79328ee766f720a888b14e0f6037eb8a397dcd9bc9f4c18b9b923a81cc69", 117 | "0x616263"].into_iter().map(Into::into).collect(), 118 | output: vec!["true"].into_iter().map(Into::into).collect(), 119 | is_example: true, 120 | is_test: true, 121 | since: "0.7.0".to_string(), 122 | }, 123 | Case { 124 | desc: "P-256 DER signature form".to_string(), 125 | input: vec!["-c", "p256", "-p", "0x045c79019e39199effa07576de6e3745fa1dba402854314aef05790e9e827cf7782ac5feb26e28039f94d73078c57b5f29be14ef9da57cb53e16e2839bdbbee630", "-f", "der", "-S", 126 | "0x3045022100ed94d4f7022cc2335ef5e34432fed541932e2c2b0c1430e2d51c06e66320302b022002cc2e13e6f5bde7f079a026399e2a6012c5ce4ad2babbe8e1e3444010b72d78", 127 | "0x616263"].into_iter().map(Into::into).collect(), 128 | output: vec!["true"].into_iter().map(Into::into).collect(), 129 | is_example: false, 130 | is_test: true, 131 | since: "0.7.0".to_string(), 132 | }, 133 | ]), 134 | ("ec_pk", 135 | vec![ 136 | Case { 137 | desc: "P-256".to_string(), 138 | input: vec!["-c", "p256", "-s", "0xf0b3b41add2d79932cdf2a4ba083c16e72647ddcd8718e2187d1567ed5a611c9"].into_iter().map(Into::into).collect(), 139 | output: vec!["0x045c79019e39199effa07576de6e3745fa1dba402854314aef05790e9e827cf7782ac5feb26e28039f94d73078c57b5f29be14ef9da57cb53e16e2839bdbbee630"].into_iter().map(Into::into).collect(), 140 | is_example: true, 141 | is_test: true, 142 | since: "0.7.0".to_string(), 143 | }, 144 | ]), 145 | ].into_iter().collect() 146 | } 147 | -------------------------------------------------------------------------------- /src/modules/ecdsa/p384.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::ecdsa::SignatureFormEnum; 2 | use crate::modules::Case; 3 | use linked_hash_map::LinkedHashMap; 4 | use ring::signature::{VerificationAlgorithm, ECDSA_P384_SHA384_ASN1, ECDSA_P384_SHA384_FIXED}; 5 | use untrusted::Input; 6 | 7 | pub fn ec_gk_p384(_compress: bool) -> Result<(Vec, Vec), String> { 8 | unimplemented!() 9 | } 10 | 11 | pub fn ec_sign_p384( 12 | _secret_key: Vec, 13 | _message: Vec, 14 | _sig_form: SignatureFormEnum, 15 | ) -> Result, String> { 16 | unimplemented!() 17 | } 18 | 19 | pub fn ec_verify_p384( 20 | public_key: Vec, 21 | sig: Vec, 22 | message: Vec, 23 | sig_form: SignatureFormEnum, 24 | ) -> Result<(), String> { 25 | let algo = match sig_form { 26 | SignatureFormEnum::Fixed => &ECDSA_P384_SHA384_FIXED, 27 | SignatureFormEnum::Der => &ECDSA_P384_SHA384_ASN1, 28 | }; 29 | 30 | let result = algo 31 | .verify( 32 | Input::from(&public_key), 33 | Input::from(&message), 34 | Input::from(&sig), 35 | ) 36 | .map_err(|e| format!("Invalid signature: {}", e))?; 37 | 38 | Ok(result) 39 | } 40 | 41 | pub fn ec_pk_p384(_secret_key: Vec, _compress: bool) -> Result, String> { 42 | unimplemented!() 43 | } 44 | 45 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 46 | vec![ 47 | ("ec_gk", 48 | vec![ 49 | Case { 50 | desc: "P-384".to_string(), 51 | input: vec!["-c", "p384"].into_iter().map(Into::into).collect(), 52 | output: vec!["(0xfbc89e8fae9340747f162330345f7cfac7387a2049f6bedb55f7a30faf8b1d24da9b1e618db7b215daa1c7b0fd54858f, 0x044978c6c7be1a5c5194983a945d2d8c81ae4b421dd89d12c6dd1756d2387fa2601993657eeb93d289a57625a70c2830db5f06f988a3e4549e26e8b6d27c7f1e6e8949d6ce5bf3f88a0f5eebaa14499d4379bc81cca6e9ff17d18b8efb370fffe3)"].into_iter().map(Into::into).collect(), 53 | is_example: false, 54 | is_test: false, 55 | since: "0.7.0".to_string(), 56 | }, 57 | ]), 58 | ("ec_sign", 59 | vec![ 60 | Case { 61 | desc: "P-384".to_string(), 62 | input: vec!["-c", "p384", "-s", "0xfbc89e8fae9340747f162330345f7cfac7387a2049f6bedb55f7a30faf8b1d24da9b1e618db7b215daa1c7b0fd54858f", "0x616263"].into_iter().map(Into::into).collect(), 63 | output: vec!["0xa0d387bc5d5de4979750f531f337fd1d04384ab4a9d251a18852c1ce1a16e2e46a2778764d0b3ee090babbc5092ea57a108ddabf9a9fcf8efaad7c0862da2beddde806745c0c3972d738c416d55cfde19b85e39ab54151c87b537c4df7d177ff"].into_iter().map(Into::into).collect(), 64 | is_example: false, 65 | is_test: false, 66 | since: "0.7.0".to_string(), 67 | }, 68 | Case { 69 | desc: "P-384 DER signature form".to_string(), 70 | input: vec!["-c", "p384", "-s", "0xfbc89e8fae9340747f162330345f7cfac7387a2049f6bedb55f7a30faf8b1d24da9b1e618db7b215daa1c7b0fd54858f", "-f", "der", "0x616263"].into_iter().map(Into::into).collect(), 71 | output: vec!["0x3065023100e48b9cd154ecd8dfd138f2e3c5d79af62b3cdc413e52565822edcc96786b03d8e996f132cf793b17c267dc177a5e6525023043dd0485f762b48e3a4a9daeeef57ceff2cf84da6a00b6a65293ee7233efe392ba4514a475476815dddfbbb7ea9e269c"].into_iter().map(Into::into).collect(), 72 | is_example: false, 73 | is_test: false, 74 | since: "0.7.0".to_string(), 75 | }, 76 | ]), 77 | ("ec_verify", 78 | vec![ 79 | Case { 80 | desc: "P-384".to_string(), 81 | input: vec!["-c", "p384", "-p", "0x044978c6c7be1a5c5194983a945d2d8c81ae4b421dd89d12c6dd1756d2387fa2601993657eeb93d289a57625a70c2830db5f06f988a3e4549e26e8b6d27c7f1e6e8949d6ce5bf3f88a0f5eebaa14499d4379bc81cca6e9ff17d18b8efb370fffe3", "-S", 82 | "0xa0d387bc5d5de4979750f531f337fd1d04384ab4a9d251a18852c1ce1a16e2e46a2778764d0b3ee090babbc5092ea57a108ddabf9a9fcf8efaad7c0862da2beddde806745c0c3972d738c416d55cfde19b85e39ab54151c87b537c4df7d177ff", 83 | "0x616263"].into_iter().map(Into::into).collect(), 84 | output: vec!["true"].into_iter().map(Into::into).collect(), 85 | is_example: true, 86 | is_test: true, 87 | since: "0.7.0".to_string(), 88 | }, 89 | Case { 90 | desc: "P-384 DER signature form".to_string(), 91 | input: vec!["-c", "p384", "-p", "0x044978c6c7be1a5c5194983a945d2d8c81ae4b421dd89d12c6dd1756d2387fa2601993657eeb93d289a57625a70c2830db5f06f988a3e4549e26e8b6d27c7f1e6e8949d6ce5bf3f88a0f5eebaa14499d4379bc81cca6e9ff17d18b8efb370fffe3", "-f", "der", "-S", 92 | "0x3065023100e48b9cd154ecd8dfd138f2e3c5d79af62b3cdc413e52565822edcc96786b03d8e996f132cf793b17c267dc177a5e6525023043dd0485f762b48e3a4a9daeeef57ceff2cf84da6a00b6a65293ee7233efe392ba4514a475476815dddfbbb7ea9e269c", 93 | "0x616263"].into_iter().map(Into::into).collect(), 94 | output: vec!["true"].into_iter().map(Into::into).collect(), 95 | is_example: false, 96 | is_test: true, 97 | since: "0.7.0".to_string(), 98 | }, 99 | ]), 100 | ("ec_pk", 101 | vec![ 102 | Case { 103 | desc: "P-384".to_string(), 104 | input: vec!["-c", "p384", "-s", "0xfbc89e8fae9340747f162330345f7cfac7387a2049f6bedb55f7a30faf8b1d24da9b1e618db7b215daa1c7b0fd54858f"].into_iter().map(Into::into).collect(), 105 | output: vec!["0x044978c6c7be1a5c5194983a945d2d8c81ae4b421dd89d12c6dd1756d2387fa2601993657eeb93d289a57625a70c2830db5f06f988a3e4549e26e8b6d27c7f1e6e8949d6ce5bf3f88a0f5eebaa14499d4379bc81cca6e9ff17d18b8efb370fffe3"].into_iter().map(Into::into).collect(), 106 | is_example: false, 107 | is_test: false, 108 | since: "0.7.0".to_string(), 109 | }, 110 | ]), 111 | ].into_iter().collect() 112 | } 113 | -------------------------------------------------------------------------------- /src/modules/ecdsa/secp256k1.rs: -------------------------------------------------------------------------------- 1 | use linked_hash_map::LinkedHashMap; 2 | use secp256k1::bitcoin_hashes::sha256; 3 | use secp256k1::rand::thread_rng; 4 | use secp256k1::{Message, PublicKey, SecretKey, Signature}; 5 | 6 | use crate::modules::base::Hex; 7 | use crate::modules::ecdsa::SignatureFormEnum; 8 | use crate::modules::Case; 9 | 10 | pub fn ec_gk_secp256k1(compress: bool) -> Result<(Vec, Vec), String> { 11 | let (secret_key, public_key) = secp256k1::Secp256k1::new().generate_keypair(&mut thread_rng()); 12 | 13 | let secret_key: Vec = secret_key 14 | .to_string() 15 | .parse::() 16 | .map_err(|_| "Invalid secret key")? 17 | .into(); 18 | 19 | let public_key = match compress { 20 | true => public_key.serialize().to_vec(), 21 | false => public_key.serialize_uncompressed().to_vec(), 22 | }; 23 | Ok((secret_key, public_key)) 24 | } 25 | 26 | pub fn ec_sign_secp256k1( 27 | secret_key: Vec, 28 | message: Vec, 29 | sig_form: SignatureFormEnum, 30 | ) -> Result, String> { 31 | let message = Message::from_hashed_data::(&message); 32 | let secret_key = 33 | SecretKey::from_slice(&secret_key).map_err(|e| format!("Invalid secret key: {}", e))?; 34 | let signature = secp256k1::Secp256k1::signing_only().sign(&message, &secret_key); 35 | 36 | let signature = match sig_form { 37 | SignatureFormEnum::Fixed => signature.serialize_compact().to_vec(), 38 | SignatureFormEnum::Der => signature.serialize_der().as_ref().to_vec(), 39 | }; 40 | 41 | Ok(signature) 42 | } 43 | 44 | pub fn ec_verify_secp256k1( 45 | public_key: Vec, 46 | sig: Vec, 47 | message: Vec, 48 | sig_form: SignatureFormEnum, 49 | ) -> Result<(), String> { 50 | let message = Message::from_hashed_data::(&message); 51 | let sig = match sig_form { 52 | SignatureFormEnum::Fixed => Signature::from_compact(&sig), 53 | SignatureFormEnum::Der => Signature::from_der(&sig), 54 | } 55 | .map_err(|e| format!("Invalid signature: {}", e))?; 56 | 57 | let public_key = 58 | PublicKey::from_slice(&public_key).map_err(|e| format!("Invalid secret key: {}", e))?; 59 | let result = secp256k1::Secp256k1::verification_only() 60 | .verify(&message, &sig, &public_key) 61 | .map_err(|e| format!("{}", e)); 62 | result 63 | } 64 | 65 | pub fn ec_pk_secp256k1(secret_key: Vec, compress: bool) -> Result, String> { 66 | let secret_key = 67 | SecretKey::from_slice(&secret_key).map_err(|e| format!("Invalid secret key: {}", e))?; 68 | 69 | let secp = secp256k1::Secp256k1::new(); 70 | let public_key = PublicKey::from_secret_key(&secp, &secret_key); 71 | 72 | let public_key = match compress { 73 | true => public_key.serialize().to_vec(), 74 | false => public_key.serialize_uncompressed().to_vec(), 75 | }; 76 | 77 | Ok(public_key) 78 | } 79 | 80 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 81 | vec![ 82 | ("ec_gk", 83 | vec![ 84 | Case { 85 | desc: "Secp256k1".to_string(), 86 | input: vec!["-c", "secp256k1", "-C"].into_iter().map(Into::into).collect(), 87 | output: vec!["(0x9cbe9cd5d7759ca46296f64e3e8211ef5ccaf86b5cb7169711554d1ed2ed68ca, 0x0379ce37925295f3103855da38ee2bf0e06a60ec9d86806d0efd2de3649a74b40d)"].into_iter().map(Into::into).collect(), 88 | is_example: true, 89 | is_test: false, 90 | since: "0.7.0".to_string(), 91 | }, 92 | ]), 93 | ("ec_sign", 94 | vec![ 95 | Case { 96 | desc: "Secp256k1".to_string(), 97 | input: vec!["-c", "secp256k1", "-s", "0x9cb4f775e9b67118242cea15285555c287a7e3d2f86ba238c1fe87284b898e9a", "0x616263"].into_iter().map(Into::into).collect(), 98 | output: vec!["0x7c77b65a27984b0e124a0ae2eec6bbf2b338a5c999b943abda576108f92e95364b0b983da055493c87fd138fe5673992b2a48ef85d9ad30c98fc1afcc5fc7bc0"].into_iter().map(Into::into).collect(), 99 | is_example: true, 100 | is_test: true, 101 | since: "0.7.0".to_string(), 102 | }, 103 | Case { 104 | desc: "Secp256k1 DER signature form".to_string(), 105 | input: vec!["-c", "secp256k1", "-s", "0x9cb4f775e9b67118242cea15285555c287a7e3d2f86ba238c1fe87284b898e9a", "-f", "der", "0x616263"].into_iter().map(Into::into).collect(), 106 | output: vec!["0x304402207c77b65a27984b0e124a0ae2eec6bbf2b338a5c999b943abda576108f92e953602204b0b983da055493c87fd138fe5673992b2a48ef85d9ad30c98fc1afcc5fc7bc0"].into_iter().map(Into::into).collect(), 107 | is_example: false, 108 | is_test: true, 109 | since: "0.7.0".to_string(), 110 | }, 111 | ]), 112 | ("ec_verify", 113 | vec![ 114 | Case { 115 | desc: "Secp256k1".to_string(), 116 | input: vec!["-c", "secp256k1", "-p", "0x03391aa7238b79e1aad1e038c95306171a8ac7499357dc99586f96c5f3b9618d60", "-S", 117 | "0x7c77b65a27984b0e124a0ae2eec6bbf2b338a5c999b943abda576108f92e95364b0b983da055493c87fd138fe5673992b2a48ef85d9ad30c98fc1afcc5fc7bc0", 118 | "0x616263"].into_iter().map(Into::into).collect(), 119 | output: vec!["true"].into_iter().map(Into::into).collect(), 120 | is_example: true, 121 | is_test: true, 122 | since: "0.7.0".to_string(), 123 | }, 124 | Case { 125 | desc: "Secp256k1 DER signature form".to_string(), 126 | input: vec!["-c", "secp256k1", "-p", "0x03391aa7238b79e1aad1e038c95306171a8ac7499357dc99586f96c5f3b9618d60", "-f", "der", "-S", 127 | "0x304402207c77b65a27984b0e124a0ae2eec6bbf2b338a5c999b943abda576108f92e953602204b0b983da055493c87fd138fe5673992b2a48ef85d9ad30c98fc1afcc5fc7bc0", 128 | "0x616263"].into_iter().map(Into::into).collect(), 129 | output: vec!["true"].into_iter().map(Into::into).collect(), 130 | is_example: false, 131 | is_test: true, 132 | since: "0.7.0".to_string(), 133 | }, 134 | ]), 135 | ("ec_pk", 136 | vec![ 137 | Case { 138 | desc: "Secp256k1".to_string(), 139 | input: vec!["-c", "secp256k1", "-s", "0x9cb4f775e9b67118242cea15285555c287a7e3d2f86ba238c1fe87284b898e9a"].into_iter().map(Into::into).collect(), 140 | output: vec!["0x04391aa7238b79e1aad1e038c95306171a8ac7499357dc99586f96c5f3b9618d6035af9529d80a85ebecb1120d1cfaf1591b7c686907b0a3d18858a95e86976747"].into_iter().map(Into::into).collect(), 141 | is_example: true, 142 | is_test: true, 143 | since: "0.7.0".to_string(), 144 | }, 145 | Case { 146 | desc: "Secp256k1 Compressed public key".to_string(), 147 | input: vec!["-c", "secp256k1", "-s", "0x9cb4f775e9b67118242cea15285555c287a7e3d2f86ba238c1fe87284b898e9a", "-C"].into_iter().map(Into::into).collect(), 148 | output: vec!["0x03391aa7238b79e1aad1e038c95306171a8ac7499357dc99586f96c5f3b9618d60"].into_iter().map(Into::into).collect(), 149 | is_example: true, 150 | is_test: true, 151 | since: "0.7.0".to_string(), 152 | }, 153 | ]), 154 | ].into_iter().collect() 155 | } 156 | -------------------------------------------------------------------------------- /src/modules/ecdsa/sm2.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::ecdsa::SignatureFormEnum; 2 | use crate::modules::Case; 3 | use linked_hash_map::LinkedHashMap; 4 | use std::iter::empty; 5 | use std::iter::once; 6 | use yogcrypt::basic::cell::u64x4::U64x4; 7 | use yogcrypt::basic::field::field_p::FieldElement; 8 | use yogcrypt::sm2; 9 | use yogcrypt::sm2::{PubKey, SecKey, Signature}; 10 | 11 | pub fn ec_gk_sm2(compress: bool) -> Result<(Vec, Vec), String> { 12 | if compress { 13 | return Err("Compress is not supported".to_string()); 14 | } 15 | 16 | let secret_key = sm2::get_sec_key(); 17 | let public_key = sm2::get_pub_key(secret_key); 18 | 19 | let secret_key = secret_key_to_vec(secret_key); 20 | let public_key = public_key_to_vec(public_key); 21 | 22 | Ok((secret_key, public_key)) 23 | } 24 | 25 | pub fn ec_sign_sm2( 26 | secret_key: Vec, 27 | message: Vec, 28 | sig_form: SignatureFormEnum, 29 | ) -> Result, String> { 30 | if let SignatureFormEnum::Der = sig_form { 31 | return Err("DER form is not supported".to_string()); 32 | } 33 | 34 | let secret_key = vec_to_secret_key(secret_key)?; 35 | let public_key = sm2::get_pub_key(secret_key); 36 | 37 | let signature = sm2::sm2_gen_sign(&message, secret_key, public_key); 38 | 39 | let signature = signature_to_vec(signature); 40 | 41 | Ok(signature) 42 | } 43 | 44 | pub fn ec_verify_sm2( 45 | public_key: Vec, 46 | sig: Vec, 47 | message: Vec, 48 | sig_form: SignatureFormEnum, 49 | ) -> Result<(), String> { 50 | if let SignatureFormEnum::Der = sig_form { 51 | return Err("DER form is not supported".to_string()); 52 | } 53 | 54 | let public_key = vec_to_public_key(public_key)?; 55 | let signature = vec_to_signature(sig)?; 56 | 57 | let verified = sm2::sm2_ver_sign(&message, public_key, &signature); 58 | 59 | match verified { 60 | true => Ok(()), 61 | false => Err("Invalid signature".to_string()), 62 | } 63 | } 64 | 65 | pub fn ec_pk_sm2(secret_key: Vec, compress: bool) -> Result, String> { 66 | if compress { 67 | return Err("Compress is not supported".to_string()); 68 | } 69 | 70 | let secret_key = vec_to_secret_key(secret_key)?; 71 | let public_key = sm2::get_pub_key(secret_key); 72 | let public_key = public_key_to_vec(public_key); 73 | Ok(public_key) 74 | } 75 | 76 | // Referece: http://www.jonllen.com/upload/jonllen/case/jsrsasign-master/sample-sm2_crypt.html 77 | fn secret_key_to_vec(secret_key: SecKey) -> Vec { 78 | let result: Vec = secret_key 79 | .value 80 | .iter() 81 | .rev() 82 | .map(|x| x.to_be_bytes().to_vec()) 83 | .flatten() 84 | .collect(); 85 | result 86 | } 87 | 88 | fn public_key_to_vec(public_key: PubKey) -> Vec { 89 | let result = once(4u8) // uncompressed 90 | .chain( 91 | public_key 92 | .x 93 | .num 94 | .value 95 | .iter() 96 | .rev() 97 | .map(|i| i.to_be_bytes().to_vec()) 98 | .flatten(), 99 | ) 100 | .chain( 101 | public_key 102 | .y 103 | .num 104 | .value 105 | .iter() 106 | .rev() 107 | .map(|i| i.to_be_bytes().to_vec()) 108 | .flatten(), 109 | ) 110 | .collect::>(); 111 | result 112 | } 113 | 114 | fn vec_to_secret_key(vec: Vec) -> Result { 115 | if vec.len() != 32 { 116 | return Err("Invalid secret key".to_string()); 117 | } 118 | 119 | let secret_key = slice_to_u64x4(&vec); 120 | 121 | Ok(secret_key) 122 | } 123 | 124 | fn vec_to_public_key(vec: Vec) -> Result { 125 | if vec.len() != 65 || vec[0] != 4 { 126 | return Err("Invalid public key".to_string()); 127 | } 128 | 129 | let x_slice = &vec[1..33]; 130 | let y_slice = &vec[33..65]; 131 | let public_key = PubKey { 132 | x: FieldElement { 133 | num: slice_to_u64x4(x_slice), 134 | }, 135 | y: FieldElement { 136 | num: slice_to_u64x4(y_slice), 137 | }, 138 | }; 139 | 140 | Ok(public_key) 141 | } 142 | 143 | fn signature_to_vec(sig: Signature) -> Vec { 144 | let result = empty() 145 | .chain( 146 | sig.r 147 | .value 148 | .iter() 149 | .rev() 150 | .map(|i| i.to_be_bytes().to_vec()) 151 | .flatten(), 152 | ) 153 | .chain( 154 | sig.s 155 | .value 156 | .iter() 157 | .rev() 158 | .map(|i| i.to_be_bytes().to_vec()) 159 | .flatten(), 160 | ) 161 | .collect::>(); 162 | result 163 | } 164 | 165 | fn vec_to_signature(vec: Vec) -> Result { 166 | if vec.len() != 64 { 167 | return Err("Invalid signature".to_string()); 168 | } 169 | 170 | let r_slice = &vec[0..32]; 171 | let s_slice = &vec[32..64]; 172 | let signature = Signature { 173 | r: slice_to_u64x4(r_slice), 174 | s: slice_to_u64x4(s_slice), 175 | }; 176 | 177 | Ok(signature) 178 | } 179 | 180 | fn slice_to_u64x4(slice: &[u8]) -> U64x4 { 181 | U64x4 { 182 | value: [ 183 | u64::from_be_bytes(slice_to_arr(&slice[24..32])), 184 | u64::from_be_bytes(slice_to_arr(&slice[16..24])), 185 | u64::from_be_bytes(slice_to_arr(&slice[8..16])), 186 | u64::from_be_bytes(slice_to_arr(&slice[0..8])), 187 | ], 188 | } 189 | } 190 | 191 | fn slice_to_arr(slice: &[u8]) -> [u8; 8] { 192 | let mut a = [0u8; 8]; 193 | a.copy_from_slice(slice); 194 | a 195 | } 196 | 197 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 198 | vec![ 199 | ("ec_gk", 200 | vec![ 201 | Case { 202 | desc: "SM2".to_string(), 203 | input: vec!["-c", "sm2"].into_iter().map(Into::into).collect(), 204 | output: vec!["(0x80a61373e34f7215feceb8dd06bb3731ea362ff5355a7226d4e12d076a7eb588, 0x044b2dd8bf6dbbfb14db3e4d17bd7a3e8758eb4232049bec931d1038f4afaae46ac3c771f929bbf35a28b0363789fb19127cea3318f4c8902a0034ca5f1b7667d1)"].into_iter().map(Into::into).collect(), 205 | is_example: true, 206 | is_test: false, 207 | since: "0.7.0".to_string(), 208 | }, 209 | ]), 210 | ("ec_sign", 211 | vec![ 212 | Case { 213 | desc: "SM2".to_string(), 214 | input: vec!["-c", "sm2", "-s", "0x80a61373e34f7215feceb8dd06bb3731ea362ff5355a7226d4e12d076a7eb588", "0x616263"].into_iter().map(Into::into).collect(), 215 | output: vec!["0x0a4d089d3177234ed34aa7f30c6a7a7954539f68825bedbe82be65aefdb733c921207be31b8071bbfd5c99044ebde49d3c38e9972063b844f65f4acfc7d6dff2"].into_iter().map(Into::into).collect(), 216 | is_example: true, 217 | is_test: false, 218 | since: "0.7.0".to_string(), 219 | }, 220 | ]), 221 | ("ec_verify", 222 | vec![ 223 | Case { 224 | desc: "SM2".to_string(), 225 | input: vec!["-c", "sm2", "-p", "0x044b2dd8bf6dbbfb14db3e4d17bd7a3e8758eb4232049bec931d1038f4afaae46ac3c771f929bbf35a28b0363789fb19127cea3318f4c8902a0034ca5f1b7667d1", "-S", 226 | "0x0a4d089d3177234ed34aa7f30c6a7a7954539f68825bedbe82be65aefdb733c921207be31b8071bbfd5c99044ebde49d3c38e9972063b844f65f4acfc7d6dff2", 227 | "0x616263"].into_iter().map(Into::into).collect(), 228 | output: vec!["true"].into_iter().map(Into::into).collect(), 229 | is_example: true, 230 | is_test: true, 231 | since: "0.7.0".to_string(), 232 | }, 233 | ]), 234 | ("ec_pk", 235 | vec![ 236 | Case { 237 | desc: "SM2".to_string(), 238 | input: vec!["-c", "sm2", "-s", "0x80a61373e34f7215feceb8dd06bb3731ea362ff5355a7226d4e12d076a7eb588"].into_iter().map(Into::into).collect(), 239 | output: vec!["0x044b2dd8bf6dbbfb14db3e4d17bd7a3e8758eb4232049bec931d1038f4afaae46ac3c771f929bbf35a28b0363789fb19127cea3318f4c8902a0034ca5f1b7667d1"].into_iter().map(Into::into).collect(), 240 | is_example: true, 241 | is_test: true, 242 | since: "0.7.0".to_string(), 243 | }, 244 | ]), 245 | ].into_iter().collect() 246 | } 247 | -------------------------------------------------------------------------------- /src/modules/eddsa.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::base::Hex; 2 | use crate::modules::{base, Command, Module}; 3 | use clap::{Arg, ArgMatches, SubCommand}; 4 | 5 | mod ed25519; 6 | 7 | pub enum AltSecretKey { 8 | MiniSecretKey(Vec), 9 | } 10 | 11 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 12 | Module { 13 | desc: "EdDSA (Ed25519)".to_string(), 14 | commands: commands(), 15 | get_cases: cases::cases, 16 | } 17 | } 18 | 19 | pub fn commands<'a, 'b>() -> Vec> { 20 | vec![ 21 | Command { 22 | app: SubCommand::with_name("ed_gk") 23 | .about("EdDSA generate key pair (Mini secret key, Public key)"), 24 | f: ed_gk, 25 | }, 26 | Command { 27 | app: SubCommand::with_name("ed_sign") 28 | .about("EdDSA sign") 29 | .arg( 30 | Arg::with_name("INPUT") 31 | .help("Message (Hex)") 32 | .required(false) 33 | .index(1), 34 | ) 35 | .arg( 36 | Arg::with_name("MINI_SECRET_KEY") 37 | .long("mini-secret-key") 38 | .short("m") 39 | .help("Mini secret key (Mini private key, Hex)") 40 | .takes_value(true) 41 | .required(false), 42 | ), 43 | f: ed_sign, 44 | }, 45 | Command { 46 | app: SubCommand::with_name("ed_verify") 47 | .about("EdDSA verify") 48 | .arg( 49 | Arg::with_name("INPUT") 50 | .help("Message (Hex)") 51 | .required(false) 52 | .index(1), 53 | ) 54 | .arg( 55 | Arg::with_name("PUBLIC_KEY") 56 | .long("public-key") 57 | .short("p") 58 | .help("Public key (Hex)") 59 | .takes_value(true) 60 | .required(true), 61 | ) 62 | .arg( 63 | Arg::with_name("SIGNATURE") 64 | .long("sig") 65 | .short("S") 66 | .help("Signature (Hex)") 67 | .takes_value(true) 68 | .required(true), 69 | ), 70 | f: ed_verify, 71 | }, 72 | Command { 73 | app: SubCommand::with_name("ed_pk") 74 | .about("EdDSA calculate public key") 75 | .arg( 76 | Arg::with_name("MINI_SECRET_KEY") 77 | .long("mini-secret-key") 78 | .short("m") 79 | .help("Mini secret key (Mini private key, Hex)") 80 | .takes_value(true) 81 | .required(false), 82 | ), 83 | f: ed_pk, 84 | }, 85 | ] 86 | } 87 | 88 | fn ed_gk(_matches: &ArgMatches) -> Result, String> { 89 | let (private_key, public_key) = ed25519::ed_gk_ed25519()?; 90 | 91 | let (private_key, public_key): (String, String) = 92 | (Hex::from(private_key).into(), Hex::from(public_key).into()); 93 | 94 | let result = format!("({}, {})", private_key, public_key); 95 | 96 | Ok(vec![result]) 97 | } 98 | 99 | fn ed_sign(matches: &ArgMatches) -> Result, String> { 100 | let secret_key = get_alt_secret_key(matches)?; 101 | 102 | let input = base::input_string(matches)?; 103 | let input: Vec = input.parse::().map_err(|_| "Invalid input")?.into(); 104 | 105 | let sig = ed25519::ed_sign_ed25519(secret_key, input)?; 106 | 107 | let result = Hex::from(sig).into(); 108 | 109 | Ok(vec![result]) 110 | } 111 | 112 | fn ed_verify(matches: &ArgMatches) -> Result, String> { 113 | let public_key = matches.value_of("PUBLIC_KEY").ok_or("Invalid public key")?; 114 | let public_key: Vec = public_key 115 | .parse::() 116 | .map_err(|_| "Invalid secret key")? 117 | .into(); 118 | 119 | let sig = matches.value_of("SIGNATURE").ok_or("Invalid signature")?; 120 | let sig: Vec = sig.parse::().map_err(|_| "Invalid signature")?.into(); 121 | 122 | let input = base::input_string(matches)?; 123 | let input: Vec = input.parse::().map_err(|_| "Invalid input")?.into(); 124 | 125 | ed25519::ed_verify_ed25519(public_key, sig, input)?; 126 | 127 | let result = "true".to_string(); 128 | 129 | Ok(vec![result]) 130 | } 131 | 132 | fn ed_pk(matches: &ArgMatches) -> Result, String> { 133 | let secret_key = get_alt_secret_key(matches)?; 134 | 135 | let public_key = ed25519::ed_pk_ed25519(secret_key)?; 136 | 137 | let result = Hex::from(public_key).into(); 138 | 139 | Ok(vec![result]) 140 | } 141 | 142 | fn get_alt_secret_key(matches: &ArgMatches) -> Result { 143 | if matches.is_present("MINI_SECRET_KEY") { 144 | let secret_key = matches 145 | .value_of("MINI_SECRET_KEY") 146 | .ok_or("Invalid mini secret key")?; 147 | let secret_key: Vec = secret_key 148 | .parse::() 149 | .map_err(|_| "Invalid mini secret key")? 150 | .into(); 151 | Ok(AltSecretKey::MiniSecretKey(secret_key)) 152 | } else { 153 | Err("Mini secret key should be provided".to_string()) 154 | } 155 | } 156 | 157 | mod cases { 158 | use super::ed25519; 159 | use crate::modules::Case; 160 | use linked_hash_map::LinkedHashMap; 161 | use std::iter::empty; 162 | 163 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 164 | empty() 165 | .chain(ed25519::cases()) 166 | .fold(LinkedHashMap::new(), |mut map, (name, mut cases)| { 167 | let list = map.entry(name).or_insert(vec![]); 168 | list.append(&mut cases); 169 | map 170 | }) 171 | } 172 | } 173 | 174 | #[cfg(test)] 175 | mod tests { 176 | use super::*; 177 | use crate::modules::base::test::test_module; 178 | 179 | #[test] 180 | fn test_cases() { 181 | test_module(module()); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/modules/eddsa/ed25519.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::eddsa::AltSecretKey; 2 | use crate::modules::Case; 3 | use linked_hash_map::LinkedHashMap; 4 | use rand::{thread_rng, Rng}; 5 | use ring::signature::{Ed25519KeyPair, KeyPair, VerificationAlgorithm, ED25519}; 6 | use untrusted::Input; 7 | 8 | pub fn ed_gk_ed25519() -> Result<(Vec, Vec), String> { 9 | let seed = random_32_bytes(&mut thread_rng()); 10 | 11 | let key_pair = Ed25519KeyPair::from_seed_unchecked(&seed).map_err(|_| "Invalid secret key")?; 12 | 13 | let secret_key = seed.to_vec(); 14 | 15 | let public_key = key_pair.public_key().as_ref().to_vec(); 16 | 17 | Ok((secret_key, public_key)) 18 | } 19 | 20 | pub fn ed_sign_ed25519(secret_key: AltSecretKey, message: Vec) -> Result, String> { 21 | let key_pair = get_key_pair(secret_key)?; 22 | 23 | let signature = key_pair.sign(&message); 24 | 25 | let signature = signature.as_ref().to_vec(); 26 | 27 | Ok(signature) 28 | } 29 | 30 | pub fn ed_verify_ed25519( 31 | public_key: Vec, 32 | sig: Vec, 33 | message: Vec, 34 | ) -> Result<(), String> { 35 | let result = ED25519 36 | .verify( 37 | Input::from(&public_key), 38 | Input::from(&message), 39 | Input::from(&sig), 40 | ) 41 | .map_err(|e| format!("Invalid signature: {}", e))?; 42 | 43 | Ok(result) 44 | } 45 | 46 | pub fn ed_pk_ed25519(secret_key: AltSecretKey) -> Result, String> { 47 | let key_pair = get_key_pair(secret_key)?; 48 | 49 | let public_key = key_pair.public_key(); 50 | let public_key = public_key.as_ref().to_vec(); 51 | 52 | Ok(public_key) 53 | } 54 | 55 | fn random_32_bytes(rng: &mut R) -> [u8; 32] { 56 | let mut ret = [0u8; 32]; 57 | rng.fill_bytes(&mut ret); 58 | ret 59 | } 60 | 61 | fn get_key_pair(secret_key: AltSecretKey) -> Result { 62 | let key_pair = match secret_key { 63 | AltSecretKey::MiniSecretKey(key) => { 64 | let key_pair = 65 | Ed25519KeyPair::from_seed_unchecked(&key).map_err(|_| "Invalid mini secret key")?; 66 | key_pair 67 | } 68 | }; 69 | Ok(key_pair) 70 | } 71 | 72 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 73 | vec![ 74 | ("ed_gk", 75 | vec![ 76 | Case { 77 | desc: "".to_string(), 78 | input: Vec::::new().into_iter().map(Into::into).collect(), 79 | output: vec!["(0xb850164d1feec8698acca329947c9885bd1d94034d2fbbe6080598adbe15b298, 0x892c89a4cd631d08da314607223814775604535a05f50e959d21209d01740eba)"].into_iter().map(Into::into).collect(), 80 | is_example: true, 81 | is_test: false, 82 | since: "0.8.0".to_string(), 83 | }, 84 | ]), 85 | ("ed_sign", 86 | vec![ 87 | Case { 88 | desc: "Use mini secret key".to_string(), 89 | input: vec!["-m", "0xb850164d1feec8698acca329947c9885bd1d94034d2fbbe6080598adbe15b298", "0x616263"].into_iter().map(Into::into).collect(), 90 | output: vec!["0x52131a69ebb236703de0c3589689202eebd1d16c40990c3ad8b3582631a7a267db745dbb9156d8626187e40f42f6cfe884b6d3ce0cdc04603afeed089703ac0e"].into_iter().map(Into::into).collect(), 91 | is_example: true, 92 | is_test: true, 93 | since: "0.8.0".to_string(), 94 | }, 95 | ]), 96 | ("ed_verify", 97 | vec![ 98 | Case { 99 | desc: "".to_string(), 100 | input: vec!["-p", "0x892c89a4cd631d08da314607223814775604535a05f50e959d21209d01740eba", "-S", 101 | "0x52131a69ebb236703de0c3589689202eebd1d16c40990c3ad8b3582631a7a267db745dbb9156d8626187e40f42f6cfe884b6d3ce0cdc04603afeed089703ac0e", 102 | "0x616263"].into_iter().map(Into::into).collect(), 103 | output: vec!["true"].into_iter().map(Into::into).collect(), 104 | is_example: true, 105 | is_test: true, 106 | since: "0.8.0".to_string(), 107 | }, 108 | ]), 109 | ("ed_pk", 110 | vec![ 111 | Case { 112 | desc: "Use mini secret key".to_string(), 113 | input: vec!["-m", "0xb850164d1feec8698acca329947c9885bd1d94034d2fbbe6080598adbe15b298"].into_iter().map(Into::into).collect(), 114 | output: vec!["0x892c89a4cd631d08da314607223814775604535a05f50e959d21209d01740eba"].into_iter().map(Into::into).collect(), 115 | is_example: true, 116 | is_test: true, 117 | since: "0.8.0".to_string(), 118 | }, 119 | ]), 120 | ].into_iter().collect() 121 | } 122 | -------------------------------------------------------------------------------- /src/modules/hash.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::base::Hex; 2 | use crate::modules::{base, Command, Module}; 3 | use clap::{Arg, ArgMatches, SubCommand}; 4 | use crc::crc32; 5 | use crypto::blake2b::Blake2b; 6 | use lazy_static::lazy_static; 7 | use ring::digest::{Context, SHA1_FOR_LEGACY_USE_ONLY}; 8 | use sha2::{Digest, Sha224, Sha256, Sha384, Sha512, Sha512Trunc224, Sha512Trunc256}; 9 | use std::collections::HashMap; 10 | use yogcrypt::sm3::sm3_enc; 11 | 12 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 13 | Module { 14 | desc: "Hash (MD5, SHA-1, SHA-2, SHA-3, RIPEMD, CRC, Blake2b, SM3, Twox)".to_string(), 15 | commands: commands(), 16 | get_cases: cases::cases, 17 | } 18 | } 19 | 20 | struct Algorithm { 21 | name: &'static str, 22 | help: &'static str, 23 | f: AlgorithmF, 24 | } 25 | 26 | enum AlgorithmF { 27 | Normal(fn(data: Vec) -> Result, String>), 28 | WithKey(fn(data: Vec, key: Vec) -> Result, String>), 29 | WithSeed(fn(data: Vec, seed: u64) -> Result, String>), 30 | } 31 | 32 | lazy_static! { 33 | static ref RAW_ALGORITHMS: Vec = vec![ 34 | Algorithm { 35 | name: "md5", 36 | help: "MD5", 37 | f: AlgorithmF::Normal(md5), 38 | }, 39 | Algorithm { 40 | name: "sha1", 41 | help: "SHA-1", 42 | f: AlgorithmF::Normal(sha1), 43 | }, 44 | Algorithm { 45 | name: "sha2_224", 46 | help: "SHA-2 224", 47 | f: AlgorithmF::Normal(sha2_224), 48 | }, 49 | Algorithm { 50 | name: "sha2_256", 51 | help: "SHA-2 256", 52 | f: AlgorithmF::Normal(sha2_256), 53 | }, 54 | Algorithm { 55 | name: "sha2_384", 56 | help: "SHA-2 384", 57 | f: AlgorithmF::Normal(sha2_384), 58 | }, 59 | Algorithm { 60 | name: "sha2_512", 61 | help: "SHA-2 512", 62 | f: AlgorithmF::Normal(sha2_512), 63 | }, 64 | Algorithm { 65 | name: "sha2_512_224", 66 | help: "SHA-2 512 truncate 224", 67 | f: AlgorithmF::Normal(sha2_512_224), 68 | }, 69 | Algorithm { 70 | name: "sha2_512_256", 71 | help: "SHA-2 512 truncate 256", 72 | f: AlgorithmF::Normal(sha2_512_256), 73 | }, 74 | Algorithm { 75 | name: "sha3_224", 76 | help: "SHA-3 224", 77 | f: AlgorithmF::Normal(sha3_224), 78 | }, 79 | Algorithm { 80 | name: "sha3_256", 81 | help: "SHA-3 256", 82 | f: AlgorithmF::Normal(sha3_256), 83 | }, 84 | Algorithm { 85 | name: "sha3_384", 86 | help: "SHA-3 384", 87 | f: AlgorithmF::Normal(sha3_384), 88 | }, 89 | Algorithm { 90 | name: "sha3_512", 91 | help: "SHA-3 512", 92 | f: AlgorithmF::Normal(sha3_512), 93 | }, 94 | Algorithm { 95 | name: "sha3_k_224", 96 | help: "SHA-3 keccak 224", 97 | f: AlgorithmF::Normal(sha3_k_224), 98 | }, 99 | Algorithm { 100 | name: "sha3_k_256", 101 | help: "SHA-3 keccak 256", 102 | f: AlgorithmF::Normal(sha3_k_256), 103 | }, 104 | Algorithm { 105 | name: "sha3_k_384", 106 | help: "SHA-3 keccak 384", 107 | f: AlgorithmF::Normal(sha3_k_384), 108 | }, 109 | Algorithm { 110 | name: "sha3_k_512", 111 | help: "SHA-3 keccak 512", 112 | f: AlgorithmF::Normal(sha3_k_512), 113 | }, 114 | Algorithm { 115 | name: "ripemd_160", 116 | help: "RIPEMD-160", 117 | f: AlgorithmF::Normal(ripemd_160), 118 | }, 119 | Algorithm { 120 | name: "crc_32", 121 | help: "CRC32", 122 | f: AlgorithmF::Normal(crc_32), 123 | }, 124 | Algorithm { 125 | name: "blake2b_160", 126 | help: "Blake2b 160", 127 | f: AlgorithmF::WithKey(blake2b_160), 128 | }, 129 | Algorithm { 130 | name: "blake2b_256", 131 | help: "Blake2b 256", 132 | f: AlgorithmF::WithKey(blake2b_256), 133 | }, 134 | Algorithm { 135 | name: "blake2b_384", 136 | help: "Blake2b 384", 137 | f: AlgorithmF::WithKey(blake2b_384), 138 | }, 139 | Algorithm { 140 | name: "blake2b_512", 141 | help: "Blake2b 512", 142 | f: AlgorithmF::WithKey(blake2b_512), 143 | }, 144 | Algorithm { 145 | name: "sm3", 146 | help: "Chinese National Standard SM3", 147 | f: AlgorithmF::Normal(sm3), 148 | }, 149 | Algorithm { 150 | name: "twox", 151 | help: "TwoX", 152 | f: AlgorithmF::WithSeed(twox), 153 | }, 154 | ]; 155 | static ref ALGORITHMS: HashMap<&'static str, &'static Algorithm> = 156 | RAW_ALGORITHMS.iter().map(|x| (x.name, x)).collect(); 157 | static ref ALGORITHM_HELP: String = "Hash algorithm\n".to_string() 158 | + &RAW_ALGORITHMS 159 | .iter() 160 | .map(|a| { format!("{}: {}", a.name, a.help) }) 161 | .collect::>() 162 | .join("\n"); 163 | } 164 | 165 | pub fn commands<'a, 'b>() -> Vec> { 166 | vec![Command { 167 | app: SubCommand::with_name("hash") 168 | .about("Hex to hash") 169 | .arg( 170 | Arg::with_name("ALGORITHM") 171 | .long("algo") 172 | .short("a") 173 | .help(&ALGORITHM_HELP) 174 | .takes_value(true) 175 | .required(true), 176 | ) 177 | .arg( 178 | Arg::with_name("KEY") 179 | .long("key") 180 | .short("k") 181 | .help("Key for Blake2b") 182 | .takes_value(true) 183 | .required(false), 184 | ) 185 | .arg( 186 | Arg::with_name("SEED") 187 | .long("seed") 188 | .short("s") 189 | .help("Seed for twox") 190 | .takes_value(true) 191 | .required(false), 192 | ) 193 | .arg(Arg::with_name("INPUT").required(false).index(1)), 194 | f: hash, 195 | }] 196 | } 197 | 198 | fn hash(matches: &ArgMatches) -> Result, String> { 199 | let input = base::input_string(matches)?; 200 | 201 | let input: Vec = input.parse::().map_err(|_| "Convert failed")?.into(); 202 | 203 | let a_name = matches.value_of("ALGORITHM").ok_or("Invalid algorithm")?; 204 | 205 | let result = match ALGORITHMS.get(a_name) { 206 | Some(a) => match a.f { 207 | AlgorithmF::Normal(f) => (f)(input)?, 208 | AlgorithmF::WithKey(f) => { 209 | let key = match matches.value_of("KEY") { 210 | Some(key) => key.parse::().map_err(|_| "Invalid key")?.into(), 211 | None => vec![], 212 | }; 213 | (f)(input, key)? 214 | } 215 | AlgorithmF::WithSeed(f) => { 216 | let seed = match matches.value_of("SEED") { 217 | Some(seed) => seed.parse::().map_err(|_| "Invalid seed")?, 218 | None => 0, 219 | }; 220 | (f)(input, seed)? 221 | } 222 | }, 223 | None => return Err("Invalid algorithm".to_string()), 224 | }; 225 | 226 | let result = Hex::from(result).into(); 227 | 228 | Ok(vec![result]) 229 | } 230 | 231 | fn md5(data: Vec) -> Result, String> { 232 | Ok(md5::compute(data).0.to_vec()) 233 | } 234 | 235 | fn sha1(data: Vec) -> Result, String> { 236 | let mut context = Context::new(&SHA1_FOR_LEGACY_USE_ONLY); 237 | context.update(&data); 238 | let result = context.finish().as_ref().to_vec(); 239 | Ok(result) 240 | } 241 | 242 | fn sha2_224(data: Vec) -> Result, String> { 243 | let mut hasher = Sha224::new(); 244 | hasher.input(data); 245 | let result = hasher.result().to_vec(); 246 | Ok(result) 247 | } 248 | 249 | fn sha2_256(data: Vec) -> Result, String> { 250 | let mut hasher = Sha256::new(); 251 | hasher.input(data); 252 | let result = hasher.result().to_vec(); 253 | Ok(result) 254 | } 255 | 256 | fn sha2_384(data: Vec) -> Result, String> { 257 | let mut hasher = Sha384::new(); 258 | hasher.input(data); 259 | let result = hasher.result().to_vec(); 260 | Ok(result) 261 | } 262 | 263 | fn sha2_512(data: Vec) -> Result, String> { 264 | let mut hasher = Sha512::new(); 265 | hasher.input(data); 266 | let result = hasher.result().to_vec(); 267 | Ok(result) 268 | } 269 | 270 | fn sha2_512_224(data: Vec) -> Result, String> { 271 | let mut hasher = Sha512Trunc224::new(); 272 | hasher.input(data); 273 | let result = hasher.result().to_vec(); 274 | Ok(result) 275 | } 276 | 277 | fn sha2_512_256(data: Vec) -> Result, String> { 278 | let mut hasher = Sha512Trunc256::new(); 279 | hasher.input(data); 280 | let result = hasher.result().to_vec(); 281 | Ok(result) 282 | } 283 | 284 | fn sha3_224(data: Vec) -> Result, String> { 285 | let mut hasher = sha3::Sha3_224::default(); 286 | hasher.input(data); 287 | let result = hasher.result().to_vec(); 288 | Ok(result) 289 | } 290 | 291 | fn sha3_256(data: Vec) -> Result, String> { 292 | let mut hasher = sha3::Sha3_256::default(); 293 | hasher.input(data); 294 | let result = hasher.result().to_vec(); 295 | Ok(result) 296 | } 297 | 298 | fn sha3_384(data: Vec) -> Result, String> { 299 | let mut hasher = sha3::Sha3_384::default(); 300 | hasher.input(data); 301 | let result = hasher.result().to_vec(); 302 | Ok(result) 303 | } 304 | 305 | fn sha3_512(data: Vec) -> Result, String> { 306 | let mut hasher = sha3::Sha3_512::default(); 307 | hasher.input(data); 308 | let result = hasher.result().to_vec(); 309 | Ok(result) 310 | } 311 | 312 | fn sha3_k_224(data: Vec) -> Result, String> { 313 | let mut hasher = sha3::Keccak224::default(); 314 | hasher.input(data); 315 | let result = hasher.result().to_vec(); 316 | Ok(result) 317 | } 318 | 319 | fn sha3_k_256(data: Vec) -> Result, String> { 320 | let mut hasher = sha3::Keccak256::default(); 321 | hasher.input(data); 322 | let result = hasher.result().to_vec(); 323 | Ok(result) 324 | } 325 | 326 | fn sha3_k_384(data: Vec) -> Result, String> { 327 | let mut hasher = sha3::Keccak384::default(); 328 | hasher.input(data); 329 | let result = hasher.result().to_vec(); 330 | Ok(result) 331 | } 332 | 333 | fn sha3_k_512(data: Vec) -> Result, String> { 334 | let mut hasher = sha3::Keccak512::default(); 335 | hasher.input(data); 336 | let result = hasher.result().to_vec(); 337 | Ok(result) 338 | } 339 | 340 | fn ripemd_160(data: Vec) -> Result, String> { 341 | let mut hasher = ripemd160::Ripemd160::default(); 342 | hasher.input(data); 343 | let result = hasher.result().to_vec(); 344 | Ok(result) 345 | } 346 | 347 | fn crc_32(data: Vec) -> Result, String> { 348 | let result = crc32::checksum_ieee(&data); 349 | let result = result.to_be_bytes().to_vec(); 350 | 351 | Ok(result) 352 | } 353 | 354 | fn blake2b_160(data: Vec, key: Vec) -> Result, String> { 355 | blake2b(data, 20, key) 356 | } 357 | 358 | fn blake2b_256(data: Vec, key: Vec) -> Result, String> { 359 | blake2b(data, 32, key) 360 | } 361 | 362 | fn blake2b_384(data: Vec, key: Vec) -> Result, String> { 363 | blake2b(data, 48, key) 364 | } 365 | 366 | fn blake2b_512(data: Vec, key: Vec) -> Result, String> { 367 | blake2b(data, 64, key) 368 | } 369 | 370 | fn blake2b(data: Vec, size: usize, key: Vec) -> Result, String> { 371 | let mut result = vec![0u8; size]; 372 | 373 | Blake2b::blake2b(&mut result, &data, &key); 374 | 375 | Ok(result) 376 | } 377 | 378 | fn sm3(data: Vec) -> Result, String> { 379 | let result = sm3_enc(&data); 380 | 381 | let result: Vec = result 382 | .iter() 383 | .map(|x| x.to_be_bytes().to_vec()) 384 | .flatten() 385 | .collect(); 386 | 387 | Ok(result) 388 | } 389 | 390 | fn twox(data: Vec, seed: u64) -> Result, String> { 391 | use ::core::hash::Hasher; 392 | let mut h = twox_hash::XxHash::with_seed(seed); 393 | h.write(&data); 394 | let r = h.finish(); 395 | use byteorder::{ByteOrder, LittleEndian}; 396 | let mut dest = vec![0u8; 8]; 397 | LittleEndian::write_u64(&mut dest[0..8], r); 398 | Ok(dest) 399 | } 400 | 401 | mod cases { 402 | use crate::modules::Case; 403 | use linked_hash_map::LinkedHashMap; 404 | 405 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 406 | vec![ 407 | ("hash", 408 | vec![ 409 | Case { 410 | desc: "MD5".to_string(), 411 | input: vec!["-a", "md5", "0x616263"].into_iter().map(Into::into).collect(), 412 | output: vec!["0x900150983cd24fb0d6963f7d28e17f72"].into_iter().map(Into::into).collect(), 413 | is_example: true, 414 | is_test: true, 415 | since: "0.2.0".to_string(), 416 | }, 417 | Case { 418 | desc: "SHA-1".to_string(), 419 | input: vec!["-a", "sha1", "0x616263"].into_iter().map(Into::into).collect(), 420 | output: vec!["0xa9993e364706816aba3e25717850c26c9cd0d89d"].into_iter().map(Into::into).collect(), 421 | is_example: true, 422 | is_test: true, 423 | since: "0.2.0".to_string(), 424 | }, 425 | Case { 426 | desc: "SHA-2 224".to_string(), 427 | input: vec!["-a", "sha2_224", "0x616263"].into_iter().map(Into::into).collect(), 428 | output: vec!["0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"].into_iter().map(Into::into).collect(), 429 | is_example: true, 430 | is_test: true, 431 | since: "0.2.0".to_string(), 432 | }, 433 | Case { 434 | desc: "SHA-2 256".to_string(), 435 | input: vec!["-a", "sha2_256", "0x616263"].into_iter().map(Into::into).collect(), 436 | output: vec!["0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"].into_iter().map(Into::into).collect(), 437 | is_example: true, 438 | is_test: true, 439 | since: "0.2.0".to_string(), 440 | }, 441 | Case { 442 | desc: "SHA-2 384".to_string(), 443 | input: vec!["-a", "sha2_384", "0x616263"].into_iter().map(Into::into).collect(), 444 | output: vec!["0xcb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"].into_iter().map(Into::into).collect(), 445 | is_example: true, 446 | is_test: true, 447 | since: "0.2.0".to_string(), 448 | }, 449 | Case { 450 | desc: "SHA-2 512".to_string(), 451 | input: vec!["-a", "sha2_512", "0x616263"].into_iter().map(Into::into).collect(), 452 | output: vec!["0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"].into_iter().map(Into::into).collect(), 453 | is_example: true, 454 | is_test: true, 455 | since: "0.2.0".to_string(), 456 | }, 457 | Case { 458 | desc: "SHA-2 512 truncate 224".to_string(), 459 | input: vec!["-a", "sha2_512_224", "0x616263"].into_iter().map(Into::into).collect(), 460 | output: vec!["0x4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"].into_iter().map(Into::into).collect(), 461 | is_example: true, 462 | is_test: true, 463 | since: "0.2.0".to_string(), 464 | }, 465 | Case { 466 | desc: "SHA-2 512 truncate 256".to_string(), 467 | input: vec!["-a", "sha2_512_256", "0x616263"].into_iter().map(Into::into).collect(), 468 | output: vec!["0x53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"].into_iter().map(Into::into).collect(), 469 | is_example: true, 470 | is_test: true, 471 | since: "0.2.0".to_string(), 472 | }, 473 | Case { 474 | desc: "SHA-3 224".to_string(), 475 | input: vec!["-a", "sha3_224", "0x616263"].into_iter().map(Into::into).collect(), 476 | output: vec!["0xe642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf"].into_iter().map(Into::into).collect(), 477 | is_example: true, 478 | is_test: true, 479 | since: "0.2.0".to_string(), 480 | }, 481 | Case { 482 | desc: "SHA-3 256".to_string(), 483 | input: vec!["-a", "sha3_256", "0x616263"].into_iter().map(Into::into).collect(), 484 | output: vec!["0x3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"].into_iter().map(Into::into).collect(), 485 | is_example: true, 486 | is_test: true, 487 | since: "0.2.0".to_string(), 488 | }, 489 | Case { 490 | desc: "SHA-3 384".to_string(), 491 | input: vec!["-a", "sha3_384", "0x616263"].into_iter().map(Into::into).collect(), 492 | output: vec!["0xec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25"].into_iter().map(Into::into).collect(), 493 | is_example: true, 494 | is_test: true, 495 | since: "0.2.0".to_string(), 496 | }, 497 | Case { 498 | desc: "SHA-3 512".to_string(), 499 | input: vec!["-a", "sha3_512", "0x616263"].into_iter().map(Into::into).collect(), 500 | output: vec!["0xb751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"].into_iter().map(Into::into).collect(), 501 | is_example: true, 502 | is_test: true, 503 | since: "0.2.0".to_string(), 504 | }, 505 | Case { 506 | desc: "SHA-3 keccak 224".to_string(), 507 | input: vec!["-a", "sha3_k_224", "0x616263"].into_iter().map(Into::into).collect(), 508 | output: vec!["0xc30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8"].into_iter().map(Into::into).collect(), 509 | is_example: true, 510 | is_test: true, 511 | since: "0.2.0".to_string(), 512 | }, 513 | Case { 514 | desc: "SHA-3 keccak 256".to_string(), 515 | input: vec!["-a", "sha3_k_256", "0x616263"].into_iter().map(Into::into).collect(), 516 | output: vec!["0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"].into_iter().map(Into::into).collect(), 517 | is_example: true, 518 | is_test: true, 519 | since: "0.2.0".to_string(), 520 | }, 521 | Case { 522 | desc: "SHA-3 keccak 384".to_string(), 523 | input: vec!["-a", "sha3_k_384", "0x616263"].into_iter().map(Into::into).collect(), 524 | output: vec!["0xf7df1165f033337be098e7d288ad6a2f74409d7a60b49c36642218de161b1f99f8c681e4afaf31a34db29fb763e3c28e"].into_iter().map(Into::into).collect(), 525 | is_example: true, 526 | is_test: true, 527 | since: "0.2.0".to_string(), 528 | }, 529 | Case { 530 | desc: "SHA-3 keccak 512".to_string(), 531 | input: vec!["-a", "sha3_k_512", "0x616263"].into_iter().map(Into::into).collect(), 532 | output: vec!["0x18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96"].into_iter().map(Into::into).collect(), 533 | is_example: true, 534 | is_test: true, 535 | since: "0.2.0".to_string(), 536 | }, 537 | Case { 538 | desc: "RIPEMD-160".to_string(), 539 | input: vec!["-a", "ripemd_160", "0x616263"].into_iter().map(Into::into).collect(), 540 | output: vec!["0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"].into_iter().map(Into::into).collect(), 541 | is_example: true, 542 | is_test: true, 543 | since: "0.2.0".to_string(), 544 | }, 545 | Case { 546 | desc: "CRC32".to_string(), 547 | input: vec!["-a", "crc_32", "0x616263"].into_iter().map(Into::into).collect(), 548 | output: vec!["0x352441c2"].into_iter().map(Into::into).collect(), 549 | is_example: true, 550 | is_test: true, 551 | since: "0.5.0".to_string(), 552 | }, 553 | Case { 554 | desc: "Blake2b 160".to_string(), 555 | input: vec!["-a", "blake2b_160", "0x616263"].into_iter().map(Into::into).collect(), 556 | output: vec!["0x384264f676f39536840523f284921cdc68b6846b"].into_iter().map(Into::into).collect(), 557 | is_example: true, 558 | is_test: true, 559 | since: "0.5.0".to_string(), 560 | }, 561 | Case { 562 | desc: "Blake2b 160".to_string(), 563 | input: vec!["-a", "blake2b_160", "-k", "0x646566", "0x616263"].into_iter().map(Into::into).collect(), 564 | output: vec!["0x6f558b35e06631b03446e7ab87802058d7cd265c"].into_iter().map(Into::into).collect(), 565 | is_example: false, 566 | is_test: true, 567 | since: "0.5.0".to_string(), 568 | }, 569 | Case { 570 | desc: "Blake2b 256".to_string(), 571 | input: vec!["-a", "blake2b_256", "0x616263"].into_iter().map(Into::into).collect(), 572 | output: vec!["0xbddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319"].into_iter().map(Into::into).collect(), 573 | is_example: true, 574 | is_test: true, 575 | since: "0.5.0".to_string(), 576 | }, 577 | Case { 578 | desc: "Blake2b 256".to_string(), 579 | input: vec!["-a", "blake2b_256", "-k", "0x646566", "0x616263"].into_iter().map(Into::into).collect(), 580 | output: vec!["0xc3e58ca5dfa559fe4809eeb2f19ee8694a311c5b98f0ebfc241ea2bbbe577d75"].into_iter().map(Into::into).collect(), 581 | is_example: false, 582 | is_test: true, 583 | since: "0.5.0".to_string(), 584 | }, 585 | Case { 586 | desc: "Blake2b 384".to_string(), 587 | input: vec!["-a", "blake2b_384", "0x616263"].into_iter().map(Into::into).collect(), 588 | output: vec!["0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4"].into_iter().map(Into::into).collect(), 589 | is_example: true, 590 | is_test: true, 591 | since: "0.5.0".to_string(), 592 | }, 593 | Case { 594 | desc: "Blake2b 384".to_string(), 595 | input: vec!["-a", "blake2b_384", "-k", "0x646566", "0x616263"].into_iter().map(Into::into).collect(), 596 | output: vec!["0x3ddbbbe27b5a850bff56e0f5041f0e792730c17d648199e353f46bec666c314fa4ecbe31df150595169fc8c521643902"].into_iter().map(Into::into).collect(), 597 | is_example: false, 598 | is_test: true, 599 | since: "0.5.0".to_string(), 600 | }, 601 | Case { 602 | desc: "Blake2b 512".to_string(), 603 | input: vec!["-a", "blake2b_512", "0x616263"].into_iter().map(Into::into).collect(), 604 | output: vec!["0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"].into_iter().map(Into::into).collect(), 605 | is_example: true, 606 | is_test: true, 607 | since: "0.5.0".to_string(), 608 | }, 609 | Case { 610 | desc: "Blake2b 512".to_string(), 611 | input: vec!["-a", "blake2b_512", "-k", "0x646566", "0x616263"].into_iter().map(Into::into).collect(), 612 | output: vec!["0x956f2f56e2308b97120bb9f50eefaa5c6a5ae4238a372e308aeb824d3166d869c9a9ba32226d33ba081b235fc45c03852b262d97ce13018c55ed304d302c86b5"].into_iter().map(Into::into).collect(), 613 | is_example: false, 614 | is_test: true, 615 | since: "0.5.0".to_string(), 616 | }, 617 | Case { 618 | desc: "SM3".to_string(), 619 | input: vec!["-a", "sm3", "0x616263"].into_iter().map(Into::into).collect(), 620 | output: vec!["0x66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0"].into_iter().map(Into::into).collect(), 621 | is_example: true, 622 | is_test: true, 623 | since: "0.7.0".to_string(), 624 | }, 625 | Case { 626 | desc: "TwoX".to_string(), 627 | input: vec!["-a", "twox", "-s", "1", "0x616263"].into_iter().map(Into::into).collect(), 628 | output: vec!["0x0889329981caa9be"].into_iter().map(Into::into).collect(), 629 | is_example: true, 630 | is_test: true, 631 | since: "0.10.0".to_string(), 632 | }, 633 | ]), 634 | ].into_iter().collect() 635 | } 636 | } 637 | 638 | #[cfg(test)] 639 | mod tests { 640 | use super::*; 641 | use crate::modules::base::test::test_module; 642 | 643 | #[test] 644 | fn test_cases() { 645 | test_module(module()); 646 | } 647 | } 648 | -------------------------------------------------------------------------------- /src/modules/hex.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::base::Hex; 2 | use crate::modules::{base, Command, Module}; 3 | use clap::{Arg, ArgMatches, SubCommand}; 4 | use regex::Regex; 5 | use std::io; 6 | use std::io::Write; 7 | 8 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 9 | Module { 10 | desc: "Hex / UTF-8 string / binary / byte array conversion".to_string(), 11 | commands: commands(), 12 | get_cases: cases::cases, 13 | } 14 | } 15 | 16 | pub fn commands<'a, 'b>() -> Vec> { 17 | vec![ 18 | Command { 19 | app: SubCommand::with_name("h2s") 20 | .about("Convert hex to UTF-8 string") 21 | .arg(Arg::with_name("INPUT").required(false).index(1)), 22 | f: h2s, 23 | }, 24 | Command { 25 | app: SubCommand::with_name("s2h") 26 | .about("Convert UTF-8 string to hex") 27 | .arg(Arg::with_name("INPUT").required(false).index(1)), 28 | f: s2h, 29 | }, 30 | Command { 31 | app: SubCommand::with_name("h2b") 32 | .about("Convert hex to binary") 33 | .arg(Arg::with_name("INPUT").required(false).index(1)), 34 | f: h2b, 35 | }, 36 | Command { 37 | app: SubCommand::with_name("b2h") 38 | .about("Convert binary to hex") 39 | .arg(Arg::with_name("INPUT").required(false).index(1)), 40 | f: b2h, 41 | }, 42 | Command { 43 | app: SubCommand::with_name("h2a") 44 | .about("Convert hex to byte array") 45 | .arg(Arg::with_name("INPUT").required(false).index(1)), 46 | f: h2a, 47 | }, 48 | Command { 49 | app: SubCommand::with_name("a2h") 50 | .about("Convert byte array to hex") 51 | .arg(Arg::with_name("INPUT").required(false).index(1)), 52 | f: a2h, 53 | }, 54 | ] 55 | } 56 | 57 | fn h2s(matches: &ArgMatches) -> Result, String> { 58 | let input = base::input_string(matches)?; 59 | let input: Vec = input.parse::().map_err(|_| "Convert failed")?.into(); 60 | 61 | let result = String::from_utf8(input).map_err(|_| "Not UTF-8")?; 62 | 63 | Ok(vec![result]) 64 | } 65 | 66 | fn s2h(matches: &ArgMatches) -> Result, String> { 67 | let input = base::input_string(matches).map_err(|_| "Not UTF-8")?; 68 | 69 | let input = input.as_bytes().to_vec(); 70 | let result: String = Hex::from(input).into(); 71 | 72 | Ok(vec![result]) 73 | } 74 | 75 | fn h2b_inner(matches: &ArgMatches) -> Result, String> { 76 | let input = base::input_string(matches)?; 77 | let result: Vec = input.parse::().map_err(|_| "Convert failed")?.into(); 78 | 79 | Ok(result) 80 | } 81 | 82 | fn h2b(matches: &ArgMatches) -> Result, String> { 83 | let result = h2b_inner(matches)?; 84 | 85 | io::stdout() 86 | .write_all(&result) 87 | .map_err(|_| "Convert failed")?; 88 | 89 | Ok(vec![]) 90 | } 91 | 92 | fn b2h(matches: &ArgMatches) -> Result, String> { 93 | let input = base::input_bytes(matches)?; 94 | 95 | let result: String = Hex::from(input).into(); 96 | 97 | Ok(vec![result]) 98 | } 99 | 100 | fn h2a(matches: &ArgMatches) -> Result, String> { 101 | let input = base::input_string(matches)?; 102 | let input: Vec = input.parse::().map_err(|_| "Convert failed")?.into(); 103 | 104 | let result = input 105 | .into_iter() 106 | .map(|x| format!("{}", x)) 107 | .collect::>() 108 | .join(", "); 109 | let result = format!("[{}]", result); 110 | 111 | Ok(vec![result]) 112 | } 113 | 114 | fn a2h(matches: &ArgMatches) -> Result, String> { 115 | let input = base::input_string(matches).map_err(|_| "Not UTF-8")?; 116 | 117 | let input = input.trim_start_matches('[').trim_end_matches(']'); 118 | let input = Regex::new(", *") 119 | .expect("qed") 120 | .split(input) 121 | .collect::>(); 122 | let input = input 123 | .into_iter() 124 | .map(|x| x.parse::()) 125 | .collect::, _>>() 126 | .map_err(|_| "Invalid byte array")?; 127 | 128 | let result: String = Hex::from(input).into(); 129 | Ok(vec![result]) 130 | } 131 | 132 | mod cases { 133 | use crate::modules::Case; 134 | use linked_hash_map::LinkedHashMap; 135 | 136 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 137 | vec![ 138 | ( 139 | "h2s", 140 | vec![Case { 141 | desc: "".to_string(), 142 | input: vec!["0x61626364"].into_iter().map(Into::into).collect(), 143 | output: vec!["abcd"].into_iter().map(Into::into).collect(), 144 | is_example: true, 145 | is_test: true, 146 | since: "0.1.0".to_string(), 147 | }], 148 | ), 149 | ( 150 | "s2h", 151 | vec![Case { 152 | desc: "".to_string(), 153 | input: vec!["abcd"].into_iter().map(Into::into).collect(), 154 | output: vec!["0x61626364"].into_iter().map(Into::into).collect(), 155 | is_example: true, 156 | is_test: true, 157 | since: "0.1.0".to_string(), 158 | }], 159 | ), 160 | ( 161 | "h2b", 162 | vec![Case { 163 | desc: "".to_string(), 164 | input: vec!["0x61626364"].into_iter().map(Into::into).collect(), 165 | output: vec!["abcd"].into_iter().map(Into::into).collect(), 166 | is_example: true, 167 | is_test: false, 168 | since: "0.1.0".to_string(), 169 | }], 170 | ), 171 | ( 172 | "b2h", 173 | vec![Case { 174 | desc: "".to_string(), 175 | input: vec!["abcd"].into_iter().map(Into::into).collect(), 176 | output: vec!["0x61626364"].into_iter().map(Into::into).collect(), 177 | is_example: true, 178 | is_test: true, 179 | since: "0.1.0".to_string(), 180 | }], 181 | ), 182 | ( 183 | "h2a", 184 | vec![Case { 185 | desc: "".to_string(), 186 | input: vec!["0x61626364"].into_iter().map(Into::into).collect(), 187 | output: vec!["[97, 98, 99, 100]"] 188 | .into_iter() 189 | .map(Into::into) 190 | .collect(), 191 | is_example: true, 192 | is_test: true, 193 | since: "0.7.0".to_string(), 194 | }], 195 | ), 196 | ( 197 | "a2h", 198 | vec![Case { 199 | desc: "".to_string(), 200 | input: vec!["'[97, 98, 99, 100]'"] 201 | .into_iter() 202 | .map(Into::into) 203 | .collect(), 204 | output: vec!["0x61626364"].into_iter().map(Into::into).collect(), 205 | is_example: true, 206 | is_test: true, 207 | since: "0.7.0".to_string(), 208 | }], 209 | ), 210 | ] 211 | .into_iter() 212 | .collect() 213 | } 214 | } 215 | 216 | #[cfg(test)] 217 | mod tests { 218 | use super::*; 219 | use crate::modules::base::test::test_module; 220 | 221 | #[test] 222 | fn test_cases() { 223 | test_module(module()); 224 | } 225 | 226 | #[test] 227 | fn test_h2b() { 228 | let app = &commands()[1].app; 229 | 230 | let matches = app.clone().get_matches_from(vec!["h2b", "0x61626364"]); 231 | assert_eq!(h2b_inner(&matches), Ok(vec![0x61, 0x62, 0x63, 0x64])); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/modules/html.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::{base, Command, Module}; 2 | use clap::{Arg, ArgMatches, SubCommand}; 3 | 4 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 5 | Module { 6 | desc: "HTML entity encode / decode".to_string(), 7 | commands: commands(), 8 | get_cases: cases::cases, 9 | } 10 | } 11 | 12 | pub fn commands<'a, 'b>() -> Vec> { 13 | vec![ 14 | Command { 15 | app: SubCommand::with_name("he") 16 | .about("HTML entity encode") 17 | .arg(Arg::with_name("INPUT").required(false).index(1)), 18 | f: he, 19 | }, 20 | Command { 21 | app: SubCommand::with_name("hd") 22 | .about("HTML entity decode") 23 | .arg(Arg::with_name("INPUT").required(false).index(1)), 24 | f: hd, 25 | }, 26 | ] 27 | } 28 | 29 | fn he(matches: &ArgMatches) -> Result, String> { 30 | let input = base::input_string(matches)?; 31 | 32 | let result = escaper::encode_minimal(&input); 33 | 34 | Ok(vec![result]) 35 | } 36 | 37 | fn hd(matches: &ArgMatches) -> Result, String> { 38 | let input = base::input_string(matches)?; 39 | 40 | let result = escaper::decode_html(&input).map_err(|_| "Decode failed")?; 41 | 42 | Ok(vec![result]) 43 | } 44 | 45 | mod cases { 46 | use crate::modules::Case; 47 | use linked_hash_map::LinkedHashMap; 48 | 49 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 50 | vec![ 51 | ( 52 | "he", 53 | vec![Case { 54 | desc: "".to_string(), 55 | input: vec!["''"].into_iter().map(Into::into).collect(), 56 | output: vec!["<b>"].into_iter().map(Into::into).collect(), 57 | is_example: true, 58 | is_test: true, 59 | since: "0.4.0".to_string(), 60 | }], 61 | ), 62 | ( 63 | "hd", 64 | vec![Case { 65 | desc: "".to_string(), 66 | input: vec!["'<b>'"].into_iter().map(Into::into).collect(), 67 | output: vec![""].into_iter().map(Into::into).collect(), 68 | is_example: true, 69 | is_test: true, 70 | since: "0.4.0".to_string(), 71 | }], 72 | ), 73 | ] 74 | .into_iter() 75 | .collect() 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use super::*; 82 | use crate::modules::base::test::test_module; 83 | 84 | #[test] 85 | fn test_cases() { 86 | test_module(module()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/modules/number_codec.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::base::Hex; 2 | use crate::modules::{base, Command, Module}; 3 | use clap::{Arg, ArgMatches, SubCommand}; 4 | use parity_codec::{Compact, Decode, Encode}; 5 | 6 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 7 | Module { 8 | desc: "Number codec".to_string(), 9 | commands: commands(), 10 | get_cases: cases::cases, 11 | } 12 | } 13 | 14 | pub fn commands<'a, 'b>() -> Vec> { 15 | vec![ 16 | Command { 17 | app: SubCommand::with_name("ne") 18 | .about("Number encode") 19 | .arg( 20 | Arg::with_name("TYPE") 21 | .long("type") 22 | .short("t") 23 | .help("Number type\nu8\nu16\nu32\nu64\nu128\nc: Compact") 24 | .takes_value(true) 25 | .required(true), 26 | ) 27 | .arg(Arg::with_name("INPUT").required(false).index(1)), 28 | f: ne, 29 | }, 30 | Command { 31 | app: SubCommand::with_name("nd") 32 | .about("Number decode") 33 | .arg( 34 | Arg::with_name("TYPE") 35 | .long("type") 36 | .short("t") 37 | .help("Number type: u8, u16, u32, u64, u128, c(Compact)") 38 | .takes_value(true) 39 | .required(true), 40 | ) 41 | .arg(Arg::with_name("INPUT").required(false).index(1)), 42 | f: nd, 43 | }, 44 | ] 45 | } 46 | 47 | fn ne(matches: &ArgMatches) -> Result, String> { 48 | let input = base::input_string(matches)?; 49 | 50 | let t = matches.value_of("TYPE").ok_or("Invalid number type")?; 51 | 52 | let result = match t { 53 | "u8" => { 54 | let input = input.parse::().map_err(|_| "Invalid input")?; 55 | vec![input] 56 | } 57 | "u16" => { 58 | let input = input.parse::().map_err(|_| "Invalid input")?; 59 | input.encode() 60 | } 61 | "u32" => { 62 | let input = input.parse::().map_err(|_| "Invalid input")?; 63 | input.encode() 64 | } 65 | "u64" => { 66 | let input = input.parse::().map_err(|_| "Invalid input")?; 67 | input.encode() 68 | } 69 | "u128" => { 70 | let input = input.parse::().map_err(|_| "Invalid input")?; 71 | input.encode() 72 | } 73 | "c" => { 74 | let input = Compact(input.parse::().map_err(|_| "Invalid input")?); 75 | input.encode() 76 | } 77 | _ => return Err("Invalid input".to_string()), 78 | }; 79 | 80 | let result = Hex::from(result).into(); 81 | 82 | Ok(vec![result]) 83 | } 84 | 85 | fn nd(matches: &ArgMatches) -> Result, String> { 86 | let input = base::input_string(matches)?; 87 | 88 | let t = matches.value_of("TYPE").ok_or("Invalid number type")?; 89 | 90 | let input: Vec = input.parse::().map_err(|_| "Invalid input")?.into(); 91 | 92 | let mut input = &input[..]; 93 | 94 | let result = match t { 95 | "u8" => { 96 | let input: u8 = if input.len() > 0 { 97 | input[0] 98 | } else { 99 | return Err("Invalid input".to_string()); 100 | }; 101 | format!("{}", input) 102 | } 103 | "u16" => { 104 | let input: u16 = Decode::decode(&mut input).ok_or("Invalid input")?; 105 | format!("{}", input) 106 | } 107 | "u32" => { 108 | let input: u32 = Decode::decode(&mut input).ok_or("Invalid input")?; 109 | format!("{}", input) 110 | } 111 | "u64" => { 112 | let input: u64 = Decode::decode(&mut input).ok_or("Invalid input")?; 113 | format!("{}", input) 114 | } 115 | "u128" => { 116 | let input: u128 = Decode::decode(&mut input).ok_or("Invalid input")?; 117 | format!("{}", input) 118 | } 119 | "c" => { 120 | let input: Compact = Decode::decode(&mut input).ok_or("Invalid input")?; 121 | format!("{}", input.0) 122 | } 123 | _ => return Err("Invalid input".to_string()), 124 | }; 125 | 126 | Ok(vec![result]) 127 | } 128 | 129 | mod cases { 130 | use crate::modules::Case; 131 | use linked_hash_map::LinkedHashMap; 132 | 133 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 134 | vec![ 135 | ( 136 | "ne", 137 | vec![ 138 | Case { 139 | desc: "u8".to_string(), 140 | input: vec!["-tu8", "1"].into_iter().map(Into::into).collect(), 141 | output: vec!["0x01"].into_iter().map(Into::into).collect(), 142 | is_example: true, 143 | is_test: true, 144 | since: "0.1.0".to_string(), 145 | }, 146 | Case { 147 | desc: "u16".to_string(), 148 | input: vec!["-tu16", "1"].into_iter().map(Into::into).collect(), 149 | output: vec!["0x0100"].into_iter().map(Into::into).collect(), 150 | is_example: true, 151 | is_test: true, 152 | since: "0.1.0".to_string(), 153 | }, 154 | Case { 155 | desc: "u32".to_string(), 156 | input: vec!["-tu32", "1"].into_iter().map(Into::into).collect(), 157 | output: vec!["0x01000000"].into_iter().map(Into::into).collect(), 158 | is_example: true, 159 | is_test: true, 160 | since: "0.1.0".to_string(), 161 | }, 162 | Case { 163 | desc: "u64".to_string(), 164 | input: vec!["-tu64", "1"].into_iter().map(Into::into).collect(), 165 | output: vec!["0x0100000000000000"] 166 | .into_iter() 167 | .map(Into::into) 168 | .collect(), 169 | is_example: true, 170 | is_test: true, 171 | since: "0.1.0".to_string(), 172 | }, 173 | Case { 174 | desc: "u128".to_string(), 175 | input: vec!["-tu128", "1"].into_iter().map(Into::into).collect(), 176 | output: vec!["0x01000000000000000000000000000000"] 177 | .into_iter() 178 | .map(Into::into) 179 | .collect(), 180 | is_example: true, 181 | is_test: true, 182 | since: "0.1.0".to_string(), 183 | }, 184 | Case { 185 | desc: "Compact".to_string(), 186 | input: vec!["-tc", "6"].into_iter().map(Into::into).collect(), 187 | output: vec!["0x18"].into_iter().map(Into::into).collect(), 188 | is_example: true, 189 | is_test: true, 190 | since: "0.1.0".to_string(), 191 | }, 192 | Case { 193 | desc: "Compact".to_string(), 194 | input: vec!["-tc", "251"].into_iter().map(Into::into).collect(), 195 | output: vec!["0xed03"].into_iter().map(Into::into).collect(), 196 | is_example: true, 197 | is_test: true, 198 | since: "0.1.0".to_string(), 199 | }, 200 | ], 201 | ), 202 | ( 203 | "nd", 204 | vec![ 205 | Case { 206 | desc: "u8".to_string(), 207 | input: vec!["-tu8", "0x01"].into_iter().map(Into::into).collect(), 208 | output: vec!["1"].into_iter().map(Into::into).collect(), 209 | is_example: true, 210 | is_test: true, 211 | since: "0.1.0".to_string(), 212 | }, 213 | Case { 214 | desc: "u16".to_string(), 215 | input: vec!["-tu16", "0x0100"] 216 | .into_iter() 217 | .map(Into::into) 218 | .collect(), 219 | output: vec!["1"].into_iter().map(Into::into).collect(), 220 | is_example: true, 221 | is_test: true, 222 | since: "0.1.0".to_string(), 223 | }, 224 | Case { 225 | desc: "u32".to_string(), 226 | input: vec!["-tu32", "0x01000000"] 227 | .into_iter() 228 | .map(Into::into) 229 | .collect(), 230 | output: vec!["1"].into_iter().map(Into::into).collect(), 231 | is_example: true, 232 | is_test: true, 233 | since: "0.1.0".to_string(), 234 | }, 235 | Case { 236 | desc: "u64".to_string(), 237 | input: vec!["-tu64", "0x0100000000000000"] 238 | .into_iter() 239 | .map(Into::into) 240 | .collect(), 241 | output: vec!["1"].into_iter().map(Into::into).collect(), 242 | is_example: true, 243 | is_test: true, 244 | since: "0.1.0".to_string(), 245 | }, 246 | Case { 247 | desc: "u128".to_string(), 248 | input: vec!["-tu128", "0x01000000000000000000000000000000"] 249 | .into_iter() 250 | .map(Into::into) 251 | .collect(), 252 | output: vec!["1"].into_iter().map(Into::into).collect(), 253 | is_example: true, 254 | is_test: true, 255 | since: "0.1.0".to_string(), 256 | }, 257 | Case { 258 | desc: "Compact".to_string(), 259 | input: vec!["-tc", "0x18"].into_iter().map(Into::into).collect(), 260 | output: vec!["6"].into_iter().map(Into::into).collect(), 261 | is_example: true, 262 | is_test: true, 263 | since: "0.1.0".to_string(), 264 | }, 265 | Case { 266 | desc: "Compact".to_string(), 267 | input: vec!["-tc", "0xed03"].into_iter().map(Into::into).collect(), 268 | output: vec!["251"].into_iter().map(Into::into).collect(), 269 | is_example: true, 270 | is_test: true, 271 | since: "0.1.0".to_string(), 272 | }, 273 | ], 274 | ), 275 | ] 276 | .into_iter() 277 | .collect() 278 | } 279 | } 280 | 281 | #[cfg(test)] 282 | mod tests { 283 | use super::*; 284 | use crate::modules::base::test::test_module; 285 | 286 | #[test] 287 | fn test_cases() { 288 | test_module(module()); 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/modules/number_system.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::{base, Command, Module}; 2 | use clap::{Arg, ArgMatches, SubCommand}; 3 | 4 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 5 | Module { 6 | desc: "Number 10/2/8/16 base conversion".to_string(), 7 | commands: commands(), 8 | get_cases: cases::cases, 9 | } 10 | } 11 | 12 | pub fn commands<'a, 'b>() -> Vec> { 13 | vec![Command { 14 | app: SubCommand::with_name("ns") 15 | .about("Number system") 16 | .arg( 17 | Arg::with_name("DECIMAL") 18 | .long("decimal") 19 | .short("d") 20 | .help("Output decimal result") 21 | .required(false), 22 | ) 23 | .arg( 24 | Arg::with_name("BINARY") 25 | .long("binary") 26 | .short("b") 27 | .help("Output binary result") 28 | .required(false), 29 | ) 30 | .arg( 31 | Arg::with_name("OCTAL") 32 | .long("octal") 33 | .short("o") 34 | .help("Output octal result") 35 | .required(false), 36 | ) 37 | .arg( 38 | Arg::with_name("HEXADECIMAL") 39 | .long("hexadecimal") 40 | .short("x") 41 | .help("Output hexadecimal result") 42 | .required(false), 43 | ) 44 | .arg(Arg::with_name("INPUT").required(false).index(1)), 45 | f: ns, 46 | }] 47 | } 48 | 49 | fn ns(matches: &ArgMatches) -> Result, String> { 50 | let input = base::input_string(matches)?; 51 | 52 | let (radix, number) = match input { 53 | _ if input.starts_with("0b") => (2, &input[2..]), 54 | _ if input.starts_with("0o") => (8, &input[2..]), 55 | _ if input.starts_with("0x") => (16, &input[2..]), 56 | _ => (10, &input[..]), 57 | }; 58 | 59 | let number = u64::from_str_radix(number, radix).map_err(|_| "Invalid input")?; 60 | 61 | let mut results = Vec::new(); 62 | 63 | if matches.is_present("DECIMAL") { 64 | results.push(format!("{}", number)); 65 | } 66 | if matches.is_present("BINARY") { 67 | results.push(format!("0b{:b}", number)); 68 | } 69 | if matches.is_present("OCTAL") { 70 | results.push(format!("0o{:o}", number)); 71 | } 72 | if matches.is_present("HEXADECIMAL") { 73 | results.push(format!("0x{:x}", number)); 74 | } 75 | if results.len() == 0 { 76 | results = vec![ 77 | format!("{}", number), 78 | format!("0b{:b}", number), 79 | format!("0o{:o}", number), 80 | format!("0x{:x}", number), 81 | ]; 82 | } 83 | 84 | Ok(results) 85 | } 86 | 87 | mod cases { 88 | use crate::modules::Case; 89 | use linked_hash_map::LinkedHashMap; 90 | 91 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 92 | vec![( 93 | "ns", 94 | vec![ 95 | Case { 96 | desc: "Input decimal".to_string(), 97 | input: vec!["256"].into_iter().map(Into::into).collect(), 98 | output: vec!["256", "0b100000000", "0o400", "0x100"] 99 | .into_iter() 100 | .map(Into::into) 101 | .collect(), 102 | is_example: true, 103 | is_test: true, 104 | since: "0.1.0".to_string(), 105 | }, 106 | Case { 107 | desc: "Input octal".to_string(), 108 | input: vec!["0o400"].into_iter().map(Into::into).collect(), 109 | output: vec!["256", "0b100000000", "0o400", "0x100"] 110 | .into_iter() 111 | .map(Into::into) 112 | .collect(), 113 | is_example: true, 114 | is_test: true, 115 | since: "0.1.0".to_string(), 116 | }, 117 | Case { 118 | desc: "Output decimal".to_string(), 119 | input: vec!["-d", "256"].into_iter().map(Into::into).collect(), 120 | output: vec!["256"].into_iter().map(Into::into).collect(), 121 | is_example: true, 122 | is_test: true, 123 | since: "0.1.0".to_string(), 124 | }, 125 | Case { 126 | desc: "Output binary".to_string(), 127 | input: vec!["-b", "256"].into_iter().map(Into::into).collect(), 128 | output: vec!["0b100000000"].into_iter().map(Into::into).collect(), 129 | is_example: true, 130 | is_test: true, 131 | since: "0.1.0".to_string(), 132 | }, 133 | Case { 134 | desc: "Output octal".to_string(), 135 | input: vec!["-o", "256"].into_iter().map(Into::into).collect(), 136 | output: vec!["0o400"].into_iter().map(Into::into).collect(), 137 | is_example: true, 138 | is_test: true, 139 | since: "0.1.0".to_string(), 140 | }, 141 | Case { 142 | desc: "Output hexadecimal".to_string(), 143 | input: vec!["-x", "256"].into_iter().map(Into::into).collect(), 144 | output: vec!["0x100"].into_iter().map(Into::into).collect(), 145 | is_example: true, 146 | is_test: true, 147 | since: "0.1.0".to_string(), 148 | }, 149 | ], 150 | )] 151 | .into_iter() 152 | .collect() 153 | } 154 | } 155 | 156 | #[cfg(test)] 157 | mod tests { 158 | 159 | use super::*; 160 | use crate::modules::base::test::test_module; 161 | 162 | #[test] 163 | fn test_cases() { 164 | test_module(module()); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/modules/pbkdf2.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::base::Hex; 2 | use crate::modules::{base, Command, Module}; 3 | use clap::{Arg, ArgMatches, SubCommand}; 4 | use ring::pbkdf2::{ 5 | derive, PBKDF2_HMAC_SHA1, PBKDF2_HMAC_SHA256, PBKDF2_HMAC_SHA384, PBKDF2_HMAC_SHA512, 6 | }; 7 | use std::num::NonZeroU32; 8 | 9 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 10 | Module { 11 | desc: "Pbkdf2".to_string(), 12 | commands: commands(), 13 | get_cases: cases::cases, 14 | } 15 | } 16 | 17 | pub fn commands<'a, 'b>() -> Vec> { 18 | vec![ 19 | Command { 20 | app: SubCommand::with_name("pbkdf2").about("Pbkdf2") 21 | .arg( 22 | Arg::with_name("ITERATIONS") 23 | .long("iterations") 24 | .short("i").help("Iterations") 25 | .takes_value(true) 26 | .default_value("1") 27 | .required(false)) 28 | .arg( 29 | Arg::with_name("ALGORITHM") 30 | .long("algorithm") 31 | .short("a").help("Algorithm\nsha1: SHA-1\nsha2_256: SHA-2 256\nsha2_384: SHA-2 384\nsha2_512: SHA-2 512") 32 | .takes_value(true) 33 | .default_value("sha1") 34 | .required(false)) 35 | .arg( 36 | Arg::with_name("SALT") 37 | .long("salt") 38 | .short("s").help("Salt (Hex)") 39 | .takes_value(true) 40 | .default_value("0x") 41 | .required(false)) 42 | .arg( 43 | Arg::with_name("KEY_LENGTH") 44 | .long("key-length") 45 | .short("l").help("Key length") 46 | .help("Key length: must be a multiple of 8") 47 | .takes_value(true) 48 | .default_value("128") 49 | .required(false)) 50 | .arg( 51 | Arg::with_name("INPUT") 52 | .help("Secret (Hex)") 53 | .required(false) 54 | .index(1)), 55 | 56 | f: pbkdf2, 57 | } 58 | ] 59 | } 60 | 61 | fn pbkdf2(matches: &ArgMatches) -> Result, String> { 62 | let algo = match matches.value_of("ALGORITHM") { 63 | Some("sha1") => PBKDF2_HMAC_SHA1, 64 | Some("sha2_256") => PBKDF2_HMAC_SHA256, 65 | Some("sha2_384") => PBKDF2_HMAC_SHA384, 66 | Some("sha2_512") => PBKDF2_HMAC_SHA512, 67 | _ => return Err("Invalid algorithm".to_string()), 68 | }; 69 | 70 | let iterations = match matches.value_of("ITERATIONS") { 71 | Some(iterations) => iterations 72 | .parse::() 73 | .map_err(|_| "Invalid Iterations".to_string()), 74 | _ => Err("Invalid Iterations".to_string()), 75 | } 76 | .and_then(|x| { 77 | if x > 0 { 78 | NonZeroU32::new(x).ok_or_else(|| "Invalid Iterations".to_string()) 79 | } else { 80 | Err("Invalid Iterations".to_string()) 81 | } 82 | })?; 83 | 84 | let salt: Vec = match matches.value_of("SALT") { 85 | Some(salt) => salt.parse::().map_err(|_| "Invalid salt".to_string()), 86 | _ => Err("Invalid salt".to_string()), 87 | }? 88 | .into(); 89 | 90 | let key_length = match matches.value_of("KEY_LENGTH") { 91 | Some(key_length) => key_length 92 | .parse::() 93 | .map_err(|_| "Invalid key length".to_string()), 94 | _ => Err("Invalid key length".to_string()), 95 | } 96 | .and_then(|x| { 97 | if x > 0 { 98 | Ok(x) 99 | } else { 100 | Err("Invalid key length".to_string()) 101 | } 102 | })?; 103 | 104 | let key_byte_length = if key_length % 8 == 0 { 105 | Ok(key_length / 8) 106 | } else { 107 | Err("Invalid key length (must be a multiple of 8)".to_string()) 108 | }?; 109 | 110 | let secret = base::input_string(matches)?; 111 | let secret: Vec = secret.parse::().map_err(|_| "Invalid secret")?.into(); 112 | 113 | let mut result = vec![0u8; key_byte_length as usize]; 114 | 115 | derive(algo, iterations, &salt, &secret, &mut result); 116 | 117 | let result = Hex::from(result).into(); 118 | 119 | Ok(vec![result]) 120 | } 121 | 122 | mod cases { 123 | use crate::modules::Case; 124 | use linked_hash_map::LinkedHashMap; 125 | 126 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 127 | vec![( 128 | "pbkdf2", 129 | vec![Case { 130 | desc: "".to_string(), 131 | input: vec![ 132 | "-a", "sha2_256", "-s", "0x646566", "-i", "2", "-l", "256", "0x616263", 133 | ] 134 | .into_iter() 135 | .map(Into::into) 136 | .collect(), 137 | output: vec!["0x51a30556d0d133d859d3f3da86f861b7b12546c4f9a193ebb374397467872514"] 138 | .into_iter() 139 | .map(Into::into) 140 | .collect(), 141 | is_example: true, 142 | is_test: true, 143 | since: "0.5.0".to_string(), 144 | }], 145 | )] 146 | .into_iter() 147 | .collect() 148 | } 149 | } 150 | 151 | #[cfg(test)] 152 | mod tests { 153 | use super::*; 154 | use crate::modules::base::test::test_module; 155 | 156 | #[test] 157 | fn test_cases() { 158 | test_module(module()); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/modules/re.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::{base, Command, Module}; 2 | use clap::{Arg, ArgMatches, SubCommand}; 3 | use regex::Regex; 4 | 5 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 6 | Module { 7 | desc: "Regex match".to_string(), 8 | commands: commands(), 9 | get_cases: cases::cases, 10 | } 11 | } 12 | 13 | pub fn commands<'a, 'b>() -> Vec> { 14 | vec![Command { 15 | app: SubCommand::with_name("re") 16 | .about("Regex match") 17 | .arg( 18 | Arg::with_name("PATTERN") 19 | .long("pattern") 20 | .short("p") 21 | .help("Regex pattern") 22 | .takes_value(true) 23 | .required(true), 24 | ) 25 | .arg(Arg::with_name("INPUT").required(false).index(1)), 26 | f: re, 27 | }] 28 | } 29 | 30 | fn re(matches: &ArgMatches) -> Result, String> { 31 | let input = base::input_string(matches)?; 32 | 33 | let pattern = matches.value_of("PATTERN").ok_or("Invalid pattern")?; 34 | 35 | let pattern = Regex::new(pattern).map_err(|_| "Invalid pattern")?; 36 | 37 | let mut result = vec![]; 38 | 39 | for (_i, c) in pattern.captures_iter(&input).enumerate() { 40 | for (j, x) in c.iter().enumerate() { 41 | if j == 0 { 42 | result.push(format!("{}", x.unwrap().as_str())); 43 | } else { 44 | result.push(format!(" group#{}: {}", j, x.unwrap().as_str())); 45 | } 46 | } 47 | } 48 | 49 | Ok(result) 50 | } 51 | 52 | mod cases { 53 | use crate::modules::Case; 54 | use linked_hash_map::LinkedHashMap; 55 | 56 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 57 | vec![( 58 | "re", 59 | vec![Case { 60 | desc: "".to_string(), 61 | input: vec!["-p", "'a(.)c'", "abcadc"] 62 | .into_iter() 63 | .map(Into::into) 64 | .collect(), 65 | output: vec!["abc", " group#1: b", "adc", " group#1: d"] 66 | .into_iter() 67 | .map(Into::into) 68 | .collect(), 69 | is_example: true, 70 | is_test: true, 71 | since: "0.4.0".to_string(), 72 | }], 73 | )] 74 | .into_iter() 75 | .collect() 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use super::*; 82 | use crate::modules::base::test::test_module; 83 | 84 | #[test] 85 | fn test_cases() { 86 | test_module(module()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/modules/sm4.rs: -------------------------------------------------------------------------------- 1 | use self::Mode::CTR; 2 | use crate::modules::base::Hex; 3 | use crate::modules::{base, Command, Module}; 4 | use clap::{Arg, ArgMatches, SubCommand}; 5 | use yogcrypt::sm4; 6 | 7 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 8 | Module { 9 | desc: "SM4 encrypt / decrypt".to_string(), 10 | commands: commands(), 11 | get_cases: cases::cases, 12 | } 13 | } 14 | 15 | pub fn commands<'a, 'b>() -> Vec> { 16 | vec![ 17 | Command { 18 | app: SubCommand::with_name("sm4_enc") 19 | .about("SM4 encrypt") 20 | .arg( 21 | Arg::with_name("INPUT") 22 | .help("Plain (Hex)") 23 | .required(false) 24 | .index(1), 25 | ) 26 | .arg( 27 | Arg::with_name("MODE") 28 | .long("mode") 29 | .short("m") 30 | .help("Mode\nctr: CTR\n") 31 | .takes_value(true) 32 | .possible_values(&["ctr"]) 33 | .required(true), 34 | ) 35 | .arg( 36 | Arg::with_name("KEY") 37 | .long("key") 38 | .short("k") 39 | .help("Key (Hex)") 40 | .takes_value(true) 41 | .required(true), 42 | ) 43 | .arg( 44 | Arg::with_name("IV") 45 | .long("iv") 46 | .short("i") 47 | .help("IV (Hex)") 48 | .takes_value(true) 49 | .required(false), 50 | ), 51 | f: sm4_enc, 52 | }, 53 | Command { 54 | app: SubCommand::with_name("sm4_dec") 55 | .about("SM4 decrypt") 56 | .arg( 57 | Arg::with_name("INPUT") 58 | .help("Cipher (Hex)") 59 | .required(false) 60 | .index(1), 61 | ) 62 | .arg( 63 | Arg::with_name("MODE") 64 | .long("mode") 65 | .short("m") 66 | .help("Mode\nctr: CTR\n") 67 | .takes_value(true) 68 | .possible_values(&["ctr"]) 69 | .required(true), 70 | ) 71 | .arg( 72 | Arg::with_name("KEY") 73 | .long("key") 74 | .short("k") 75 | .help("Key (Hex)") 76 | .takes_value(true) 77 | .required(true), 78 | ) 79 | .arg( 80 | Arg::with_name("IV") 81 | .long("iv") 82 | .short("i") 83 | .help("IV (Hex)") 84 | .takes_value(true) 85 | .required(false), 86 | ), 87 | f: sm4_dec, 88 | }, 89 | ] 90 | } 91 | 92 | enum Mode { 93 | CTR { iv: Vec }, 94 | } 95 | 96 | enum KeySize { 97 | KeySize128, 98 | } 99 | 100 | fn sm4_enc(matches: &ArgMatches) -> Result, String> { 101 | let (key_size, key, mode, input) = get_common_arg(matches)?; 102 | 103 | // cipher 104 | let result = match mode { 105 | CTR { iv } => sm4_enc_ctr(key_size, &key, &input, &iv), 106 | }?; 107 | let result = Hex::from(result).into(); 108 | 109 | Ok(vec![result]) 110 | } 111 | 112 | fn sm4_dec(matches: &ArgMatches) -> Result, String> { 113 | let (key_size, key, mode, input) = get_common_arg(matches)?; 114 | 115 | // plain 116 | let result = match mode { 117 | CTR { iv } => sm4_dec_ctr(key_size, &key, &input, &iv), 118 | }?; 119 | let result = Hex::from(result).into(); 120 | 121 | Ok(vec![result]) 122 | } 123 | 124 | fn get_common_arg(matches: &ArgMatches) -> Result<(KeySize, Vec, Mode, Vec), String> { 125 | let input = base::input_string(matches)?; 126 | 127 | // key and key_size 128 | let key = matches 129 | .value_of("KEY") 130 | .ok_or_else(|| "Invalid key".to_string())?; 131 | let key: Vec = key.parse::().map_err(|_| "Invalid key")?.into(); 132 | let key_size = match key.len() { 133 | 16 => KeySize::KeySize128, 134 | _ => return Err("Invalid key size (should be 128)".to_string()), 135 | }; 136 | 137 | let get_iv = || -> Result, String> { 138 | let iv = matches 139 | .value_of("IV") 140 | .ok_or_else(|| "Invalid IV".to_string())?; 141 | let iv: Vec = iv.parse::().map_err(|_| "Invalid IV")?.into(); 142 | Ok(iv) 143 | }; 144 | 145 | // mode 146 | let mode = matches 147 | .value_of("MODE") 148 | .ok_or_else(|| "Invalid mode".to_string())?; 149 | let mode = match mode { 150 | "ctr" => CTR { iv: get_iv()? }, 151 | _ => unreachable!(), 152 | }; 153 | 154 | // input 155 | let input = input.parse::().map_err(|_| "Invalid input")?.into(); 156 | 157 | Ok((key_size, key, mode, input)) 158 | } 159 | 160 | const BLOCK_SIZE: usize = 16; 161 | 162 | fn sm4_enc_ctr(key_size: KeySize, key: &[u8], input: &[u8], iv: &[u8]) -> Result, String> { 163 | sm4_ctr_process(key_size, key, input, iv) 164 | } 165 | 166 | fn sm4_dec_ctr(key_size: KeySize, key: &[u8], input: &[u8], iv: &[u8]) -> Result, String> { 167 | sm4_ctr_process(key_size, key, input, iv) 168 | } 169 | 170 | fn sm4_ctr_process( 171 | _key_size: KeySize, 172 | key: &[u8], 173 | input: &[u8], 174 | iv: &[u8], 175 | ) -> Result, String> { 176 | let mut buff = [0u8; BLOCK_SIZE]; 177 | buff.copy_from_slice(iv); 178 | 179 | let block_count = input.len() / BLOCK_SIZE; 180 | let tail_len = input.len() % BLOCK_SIZE; 181 | 182 | let key = { 183 | let mut key_arr = [0u8; BLOCK_SIZE]; 184 | key_arr.copy_from_slice(key); 185 | key_arr 186 | }; 187 | 188 | let mut result = vec![0u8; input.len()]; 189 | 190 | for i in 0..block_count { 191 | let enc = sm4::sm4_enc(&key, &buff); 192 | let ct = block_xor(&enc, &input[i * BLOCK_SIZE..i * BLOCK_SIZE + BLOCK_SIZE]); 193 | 194 | let to_write = &mut result[i * BLOCK_SIZE..i * BLOCK_SIZE + BLOCK_SIZE]; 195 | to_write.copy_from_slice(&ct); 196 | block_add_one(&mut buff); 197 | } 198 | 199 | let enc = sm4::sm4_enc(&key, &buff); 200 | for i in 0..tail_len { 201 | let ii = block_count * 16 + i; 202 | let b = input[ii] ^ enc[i]; 203 | result[ii] = b; 204 | } 205 | 206 | let result = result.to_vec(); 207 | 208 | Ok(result) 209 | } 210 | 211 | fn block_xor(a: &[u8], b: &[u8]) -> [u8; BLOCK_SIZE] { 212 | let mut out: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; 213 | for i in 0..BLOCK_SIZE { 214 | out[i] = a[i] ^ b[i]; 215 | } 216 | out 217 | } 218 | 219 | fn block_add_one(a: &mut [u8]) { 220 | let mut t; 221 | let mut carry = 1; 222 | 223 | for i in 0..16 { 224 | t = i32::from(a[15 - i]) + carry; 225 | if t == 256 { 226 | t = 0; 227 | carry = 1; 228 | } else { 229 | carry = 0 230 | } 231 | a[15 - i] = t as u8; 232 | } 233 | } 234 | 235 | mod cases { 236 | use crate::modules::Case; 237 | use linked_hash_map::LinkedHashMap; 238 | 239 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 240 | vec![ 241 | ( 242 | "sm4_enc", 243 | vec![Case { 244 | desc: "CTR".to_string(), 245 | input: vec![ 246 | "-k", 247 | "01010101010101010101010101010101", 248 | "-i", 249 | "03030303030303030303030303030303", 250 | "-m", 251 | "ctr", 252 | "0x616263", 253 | ] 254 | .into_iter() 255 | .map(Into::into) 256 | .collect(), 257 | output: vec!["0x8cd7ea"].into_iter().map(Into::into).collect(), 258 | is_example: true, 259 | is_test: true, 260 | since: "0.6.0".to_string(), 261 | }], 262 | ), 263 | ( 264 | "sm4_dec", 265 | vec![Case { 266 | desc: "CTR".to_string(), 267 | input: vec![ 268 | "-k", 269 | "01010101010101010101010101010101", 270 | "-i", 271 | "03030303030303030303030303030303", 272 | "-m", 273 | "ctr", 274 | "0x8cd7ea", 275 | ] 276 | .into_iter() 277 | .map(Into::into) 278 | .collect(), 279 | output: vec!["0x616263"].into_iter().map(Into::into).collect(), 280 | is_example: true, 281 | is_test: true, 282 | since: "0.7.0".to_string(), 283 | }], 284 | ), 285 | ] 286 | .into_iter() 287 | .collect() 288 | } 289 | } 290 | 291 | #[cfg(test)] 292 | mod tests { 293 | use super::*; 294 | use crate::modules::base::test::test_module; 295 | 296 | #[test] 297 | fn test_cases() { 298 | test_module(module()); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/modules/srdsa.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::base::Hex; 2 | use crate::modules::{base, Command, Module}; 3 | use clap::{Arg, ArgMatches, SubCommand}; 4 | 5 | mod sr25519; 6 | 7 | pub enum AltSecretKey { 8 | MiniSecretKey(Vec), 9 | SecretKey(Vec), 10 | } 11 | 12 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 13 | Module { 14 | desc: "sr25519 signature".to_string(), 15 | commands: commands(), 16 | get_cases: cases::cases, 17 | } 18 | } 19 | 20 | pub fn commands<'a, 'b>() -> Vec> { 21 | vec![ 22 | Command { 23 | app: SubCommand::with_name("sr_gk") 24 | .about("sr25519 generate key pair (Mini secret key, Public key)"), 25 | f: sr_gk, 26 | }, 27 | Command { 28 | app: SubCommand::with_name("sr_sign") 29 | .about("sr25519 sign") 30 | .arg( 31 | Arg::with_name("INPUT") 32 | .help("Message (Hex)") 33 | .required(false) 34 | .index(1), 35 | ) 36 | .arg( 37 | Arg::with_name("MINI_SECRET_KEY") 38 | .long("mini-secret-key") 39 | .short("m") 40 | .help("Mini secret key (Mini private key, Hex)") 41 | .takes_value(true) 42 | .required(false), 43 | ) 44 | .arg( 45 | Arg::with_name("SECRET_KEY") 46 | .long("secret-key") 47 | .short("s") 48 | .help("Secret key (Private key, Hex)") 49 | .takes_value(true) 50 | .required(false), 51 | ), 52 | f: sr_sign, 53 | }, 54 | Command { 55 | app: SubCommand::with_name("sr_verify") 56 | .about("sr25519 verify") 57 | .arg( 58 | Arg::with_name("INPUT") 59 | .help("Message (Hex)") 60 | .required(false) 61 | .index(1), 62 | ) 63 | .arg( 64 | Arg::with_name("PUBLIC_KEY") 65 | .long("public-key") 66 | .short("p") 67 | .help("Public key (Hex)") 68 | .takes_value(true) 69 | .required(true), 70 | ) 71 | .arg( 72 | Arg::with_name("SIGNATURE") 73 | .long("sig") 74 | .short("S") 75 | .help("Signature (Hex)") 76 | .takes_value(true) 77 | .required(true), 78 | ), 79 | f: sr_verify, 80 | }, 81 | Command { 82 | app: SubCommand::with_name("sr_sk") 83 | .about("sr25519 calculate secret key from mini secret key") 84 | .arg( 85 | Arg::with_name("MINI_SECRET_KEY") 86 | .long("mini-secret-key") 87 | .short("m") 88 | .help("Mini secret key (Mini private key, Hex)") 89 | .takes_value(true) 90 | .required(true), 91 | ), 92 | 93 | f: sr_sk, 94 | }, 95 | Command { 96 | app: SubCommand::with_name("sr_pk") 97 | .about("sr25519 calculate public key") 98 | .arg( 99 | Arg::with_name("MINI_SECRET_KEY") 100 | .long("mini-secret-key") 101 | .short("m") 102 | .help("Mini secret key (Mini private key, Hex)") 103 | .takes_value(true) 104 | .required(false), 105 | ) 106 | .arg( 107 | Arg::with_name("SECRET_KEY") 108 | .long("secret-key") 109 | .short("s") 110 | .help("Secret key (Private key, Hex)") 111 | .takes_value(true) 112 | .required(false), 113 | ), 114 | 115 | f: sr_pk, 116 | }, 117 | ] 118 | } 119 | 120 | fn sr_gk(_matches: &ArgMatches) -> Result, String> { 121 | let (private_key, public_key) = sr25519::sr_gk_sr25519()?; 122 | 123 | let (private_key, public_key): (String, String) = 124 | (Hex::from(private_key).into(), Hex::from(public_key).into()); 125 | 126 | let result = format!("({}, {})", private_key, public_key); 127 | 128 | Ok(vec![result]) 129 | } 130 | 131 | fn sr_sign(matches: &ArgMatches) -> Result, String> { 132 | let secret_key = get_alt_secret_key(matches)?; 133 | 134 | let input = base::input_string(matches)?; 135 | let input: Vec = input.parse::().map_err(|_| "Invalid input")?.into(); 136 | 137 | let sig = sr25519::sr_sign_sr25519(secret_key, input)?; 138 | 139 | let result = Hex::from(sig).into(); 140 | 141 | Ok(vec![result]) 142 | } 143 | 144 | fn sr_verify(matches: &ArgMatches) -> Result, String> { 145 | let public_key = matches.value_of("PUBLIC_KEY").ok_or("Invalid public key")?; 146 | let public_key: Vec = public_key 147 | .parse::() 148 | .map_err(|_| "Invalid secret key")? 149 | .into(); 150 | 151 | let sig = matches.value_of("SIGNATURE").ok_or("Invalid signature")?; 152 | let sig: Vec = sig.parse::().map_err(|_| "Invalid signature")?.into(); 153 | 154 | let input = base::input_string(matches)?; 155 | let input: Vec = input.parse::().map_err(|_| "Invalid input")?.into(); 156 | 157 | sr25519::sr_verify_sr25519(public_key, sig, input)?; 158 | 159 | let result = "true".to_string(); 160 | 161 | Ok(vec![result]) 162 | } 163 | 164 | fn sr_sk(matches: &ArgMatches) -> Result, String> { 165 | let mini_secret_key = matches 166 | .value_of("MINI_SECRET_KEY") 167 | .ok_or("Invalid mini secret key")?; 168 | let mini_secret_key: Vec = mini_secret_key 169 | .parse::() 170 | .map_err(|_| "Invalid mini secret key")? 171 | .into(); 172 | 173 | let secret_key = sr25519::sr_sk_sr25519(mini_secret_key)?; 174 | 175 | let result = Hex::from(secret_key).into(); 176 | 177 | Ok(vec![result]) 178 | } 179 | 180 | fn sr_pk(matches: &ArgMatches) -> Result, String> { 181 | let secret_key = get_alt_secret_key(matches)?; 182 | 183 | let public_key = sr25519::sr_pk_sr25519(secret_key)?; 184 | 185 | let result = Hex::from(public_key).into(); 186 | 187 | Ok(vec![result]) 188 | } 189 | 190 | fn get_alt_secret_key(matches: &ArgMatches) -> Result { 191 | if matches.is_present("MINI_SECRET_KEY") { 192 | let secret_key = matches 193 | .value_of("MINI_SECRET_KEY") 194 | .ok_or("Invalid mini secret key")?; 195 | let secret_key: Vec = secret_key 196 | .parse::() 197 | .map_err(|_| "Invalid mini secret key")? 198 | .into(); 199 | Ok(AltSecretKey::MiniSecretKey(secret_key)) 200 | } else if matches.is_present("SECRET_KEY") { 201 | let secret_key = matches.value_of("SECRET_KEY").ok_or("Invalid secret key")?; 202 | let secret_key: Vec = secret_key 203 | .parse::() 204 | .map_err(|_| "Invalid secret key")? 205 | .into(); 206 | Ok(AltSecretKey::SecretKey(secret_key)) 207 | } else { 208 | Err("Mini secret key or secret key should be provided".to_string()) 209 | } 210 | } 211 | 212 | mod cases { 213 | use super::sr25519; 214 | use crate::modules::Case; 215 | use linked_hash_map::LinkedHashMap; 216 | use std::iter::empty; 217 | 218 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 219 | empty() 220 | .chain(sr25519::cases()) 221 | .fold(LinkedHashMap::new(), |mut map, (name, mut cases)| { 222 | let list = map.entry(name).or_insert(vec![]); 223 | list.append(&mut cases); 224 | map 225 | }) 226 | } 227 | } 228 | 229 | #[cfg(test)] 230 | mod tests { 231 | use super::*; 232 | use crate::modules::base::test::test_module; 233 | 234 | #[test] 235 | fn test_cases() { 236 | test_module(module()); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/modules/srdsa/sr25519.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::srdsa::AltSecretKey; 2 | use crate::modules::Case; 3 | use linked_hash_map::LinkedHashMap; 4 | use rand::thread_rng; 5 | use schnorrkel::{ExpansionMode, Keypair}; 6 | 7 | pub fn sr_gk_sr25519() -> Result<(Vec, Vec), String> { 8 | let mini_secret_key = schnorrkel::MiniSecretKey::generate_with(&mut thread_rng()); 9 | 10 | let secret_key = mini_secret_key.expand(ExpansionMode::Ed25519); 11 | 12 | let public_key = secret_key.to_public().as_ref().to_vec(); 13 | 14 | let secret_key = mini_secret_key.as_bytes().to_vec(); 15 | 16 | Ok((secret_key, public_key)) 17 | } 18 | 19 | pub fn sr_sign_sr25519(secret_key: AltSecretKey, message: Vec) -> Result, String> { 20 | let key_pair = get_key_pair(secret_key)?; 21 | 22 | let signature = key_pair.sign_simple(&[], &message); 23 | 24 | let signature = signature.to_bytes().to_vec(); 25 | 26 | Ok(signature) 27 | } 28 | 29 | pub fn sr_verify_sr25519( 30 | public_key: Vec, 31 | sig: Vec, 32 | message: Vec, 33 | ) -> Result<(), String> { 34 | let public_key = 35 | schnorrkel::PublicKey::from_bytes(&public_key).map_err(|_| "Invalid public key")?; 36 | 37 | let signature = schnorrkel::Signature::from_bytes(&sig).map_err(|_| "Invalid signature")?; 38 | 39 | let result = public_key 40 | .verify_simple(&[], &message, &signature) 41 | .map_err(|e| format!("Invalid signature: {}", e))?; 42 | 43 | Ok(result) 44 | } 45 | 46 | pub fn sr_sk_sr25519(mini_secret_key: Vec) -> Result, String> { 47 | let mini_secret_key = schnorrkel::MiniSecretKey::from_bytes(&mini_secret_key) 48 | .map_err(|_| "Invalid mini secret key")?; 49 | let key_pair = mini_secret_key.expand_to_keypair(ExpansionMode::Ed25519); 50 | 51 | let secret_key = &key_pair.secret; 52 | let secret_key = secret_key.to_bytes().to_vec(); 53 | 54 | Ok(secret_key) 55 | } 56 | 57 | pub fn sr_pk_sr25519(secret_key: AltSecretKey) -> Result, String> { 58 | let key_pair = get_key_pair(secret_key)?; 59 | 60 | let public_key = key_pair.public; 61 | let public_key = public_key.as_ref().to_vec(); 62 | 63 | Ok(public_key) 64 | } 65 | 66 | fn get_key_pair(secret_key: AltSecretKey) -> Result { 67 | let key_pair = match secret_key { 68 | AltSecretKey::MiniSecretKey(key) => { 69 | let mini_secret_key = schnorrkel::MiniSecretKey::from_bytes(&key) 70 | .map_err(|_| "Invalid mini secret key")?; 71 | let key_pair = mini_secret_key.expand_to_keypair(ExpansionMode::Ed25519); 72 | key_pair 73 | } 74 | AltSecretKey::SecretKey(key) => { 75 | let secret_key = 76 | schnorrkel::SecretKey::from_bytes(&key).map_err(|_| "Invalid secret key")?; 77 | let key_pair = secret_key.to_keypair(); 78 | key_pair 79 | } 80 | }; 81 | Ok(key_pair) 82 | } 83 | 84 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 85 | vec![ 86 | ("sr_gk", 87 | vec![ 88 | Case { 89 | desc: "".to_string(), 90 | input: Vec::::new().into_iter().map(Into::into).collect(), 91 | output: vec!["(0xc243239f434f7a4b0ab8d4600537001e6479c807c3d3623f99c8ad9f2a588837, 0x6a8ee649b31efe7aabd8d5af58f85c60f12c48f8aa880cb50ae4cd57109e9d6c)"].into_iter().map(Into::into).collect(), 92 | is_example: true, 93 | is_test: false, 94 | since: "0.8.0".to_string(), 95 | }, 96 | ]), 97 | ("sr_sign", 98 | vec![ 99 | Case { 100 | desc: "Use mini secret key".to_string(), 101 | input: vec!["-m", "0xc243239f434f7a4b0ab8d4600537001e6479c807c3d3623f99c8ad9f2a588837", "0x616263"].into_iter().map(Into::into).collect(), 102 | output: vec!["0xced639526bb840107f33b7e6588219bae8657707f0537dce9969338748673d54b92e0efba5477a1494696e5cf3f5e7a40f03271b1ef2e2030ef60d6be1caa784"].into_iter().map(Into::into).collect(), 103 | is_example: true, 104 | is_test: false, 105 | since: "0.8.0".to_string(), 106 | }, 107 | Case { 108 | desc: "Use secret key".to_string(), 109 | input: vec!["-s", "0xb0f4e5710d79bf6a46391e1c6e50a883af767636d55bcad178aa7ec7f1aa750dee6c27bbe26656a29f06ea1612461a86a190db16b31ddd6b78354fb6ba57bf7d", "0x616263"].into_iter().map(Into::into).collect(), 110 | output: vec!["0xced639526bb840107f33b7e6588219bae8657707f0537dce9969338748673d54b92e0efba5477a1494696e5cf3f5e7a40f03271b1ef2e2030ef60d6be1caa784"].into_iter().map(Into::into).collect(), 111 | is_example: true, 112 | is_test: false, 113 | since: "0.8.0".to_string(), 114 | }, 115 | ]), 116 | ("sr_verify", 117 | vec![ 118 | Case { 119 | desc: "".to_string(), 120 | input: vec!["-p", "0x6a8ee649b31efe7aabd8d5af58f85c60f12c48f8aa880cb50ae4cd57109e9d6c", "-S", 121 | "0xced639526bb840107f33b7e6588219bae8657707f0537dce9969338748673d54b92e0efba5477a1494696e5cf3f5e7a40f03271b1ef2e2030ef60d6be1caa784", 122 | "0x616263"].into_iter().map(Into::into).collect(), 123 | output: vec!["true"].into_iter().map(Into::into).collect(), 124 | is_example: true, 125 | is_test: true, 126 | since: "0.8.0".to_string(), 127 | }, 128 | ]), 129 | ("sr_sk", 130 | vec![ 131 | Case { 132 | desc: "".to_string(), 133 | input: vec!["-m", "0xc243239f434f7a4b0ab8d4600537001e6479c807c3d3623f99c8ad9f2a588837"].into_iter().map(Into::into).collect(), 134 | output: vec!["0xb0f4e5710d79bf6a46391e1c6e50a883af767636d55bcad178aa7ec7f1aa750dee6c27bbe26656a29f06ea1612461a86a190db16b31ddd6b78354fb6ba57bf7d"].into_iter().map(Into::into).collect(), 135 | is_example: true, 136 | is_test: true, 137 | since: "0.8.0".to_string(), 138 | }, 139 | ]), 140 | ("sr_pk", 141 | vec![ 142 | Case { 143 | desc: "Use mini secret key".to_string(), 144 | input: vec!["-m", "0xc243239f434f7a4b0ab8d4600537001e6479c807c3d3623f99c8ad9f2a588837"].into_iter().map(Into::into).collect(), 145 | output: vec!["0x6a8ee649b31efe7aabd8d5af58f85c60f12c48f8aa880cb50ae4cd57109e9d6c"].into_iter().map(Into::into).collect(), 146 | is_example: true, 147 | is_test: true, 148 | since: "0.8.0".to_string(), 149 | }, 150 | Case { 151 | desc: "Use secret key".to_string(), 152 | input: vec!["-s", "0xb0f4e5710d79bf6a46391e1c6e50a883af767636d55bcad178aa7ec7f1aa750dee6c27bbe26656a29f06ea1612461a86a190db16b31ddd6b78354fb6ba57bf7d"].into_iter().map(Into::into).collect(), 153 | output: vec!["0x6a8ee649b31efe7aabd8d5af58f85c60f12c48f8aa880cb50ae4cd57109e9d6c"].into_iter().map(Into::into).collect(), 154 | is_example: true, 155 | is_test: true, 156 | since: "0.8.0".to_string(), 157 | }, 158 | ]), 159 | ].into_iter().collect() 160 | } 161 | -------------------------------------------------------------------------------- /src/modules/time.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::{base, Command, Module}; 2 | use chrono::offset::TimeZone; 3 | use chrono::{DateTime, FixedOffset, Local, NaiveDateTime}; 4 | use clap::{Arg, ArgMatches, SubCommand}; 5 | 6 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 7 | Module { 8 | desc: "Timestamp / date conversion".to_string(), 9 | commands: commands(), 10 | get_cases: cases::cases, 11 | } 12 | } 13 | 14 | enum Time { 15 | FixedOffset(DateTime), 16 | Local(DateTime), 17 | } 18 | 19 | pub fn commands<'a, 'b>() -> Vec> { 20 | vec![ 21 | Command { 22 | app: SubCommand::with_name("ts2d") 23 | .about("Convert timestamp to date") 24 | .arg( 25 | Arg::with_name("TIMEZONE") 26 | .long("timezone") 27 | .short("z") 28 | .help("Time zone\n8: CN\n0: UK\netc") 29 | .takes_value(true) 30 | .required(false), 31 | ) 32 | .arg(Arg::with_name("INPUT").required(false).index(1)), 33 | f: ts2d, 34 | }, 35 | Command { 36 | app: SubCommand::with_name("d2ts") 37 | .about("Convert date to timestamp") 38 | .arg( 39 | Arg::with_name("TIMEZONE") 40 | .long("timezone") 41 | .short("z") 42 | .help("Time zone\n8: CN\n0: UK\netc") 43 | .takes_value(true) 44 | .required(false), 45 | ) 46 | .arg(Arg::with_name("INPUT").required(false).index(1)), 47 | f: d2ts, 48 | }, 49 | Command { 50 | app: SubCommand::with_name("ts").about("Current timestamp"), 51 | f: ts, 52 | }, 53 | ] 54 | } 55 | 56 | fn ts2d(matches: &ArgMatches) -> Result, String> { 57 | let input = base::input_string(matches)?; 58 | 59 | let timestamp: i64 = input.parse().map_err(|_| "Invalid input")?; 60 | 61 | let timezone = matches.value_of("TIMEZONE"); 62 | 63 | let result = match timezone { 64 | Some(timezone) => { 65 | let timezone: i32 = timezone.parse().map_err(|_| "Invalid input")?; 66 | if timezone > 12 || timezone < -12 { 67 | return Err("Invalid timezone".to_string()); 68 | } 69 | FixedOffset::east(timezone * 3600) 70 | .timestamp(timestamp, 0) 71 | .format("%Y-%m-%d %H:%M:%S") 72 | .to_string() 73 | } 74 | None => Local 75 | .timestamp(timestamp, 0) 76 | .format("%Y-%m-%d %H:%M:%S") 77 | .to_string(), 78 | }; 79 | 80 | Ok(vec![result]) 81 | } 82 | 83 | fn d2ts(matches: &ArgMatches) -> Result, String> { 84 | let input = base::input_string(matches)?; 85 | 86 | let timezone = matches.value_of("TIMEZONE"); 87 | 88 | let result = parse_standard(&input, timezone) 89 | .or_else(|_| parse_rfc2822(&input)) 90 | .or_else(|_| parse_rfc3339(&input))?; 91 | 92 | let result = match result { 93 | Time::FixedOffset(time) => time.timestamp(), 94 | Time::Local(time) => time.timestamp(), 95 | }; 96 | 97 | let result = format!("{}", result); 98 | 99 | Ok(vec![result]) 100 | } 101 | 102 | fn ts(_matches: &ArgMatches) -> Result, String> { 103 | let now = Local::now(); 104 | let result = now.timestamp(); 105 | 106 | let result = format!("{}", result); 107 | 108 | Ok(vec![result]) 109 | } 110 | 111 | fn parse_standard(input: &str, timezone: Option<&str>) -> Result { 112 | let time = 113 | NaiveDateTime::parse_from_str(&input, "%Y-%m-%d %H:%M:%S").map_err(|_| "Invalid input")?; 114 | 115 | let result = match timezone { 116 | Some(timezone) => { 117 | let timezone: i32 = timezone.parse().map_err(|_| "Invalid input")?; 118 | if timezone > 12 || timezone < -12 { 119 | return Err("Invalid timezone".to_string()); 120 | } 121 | Time::FixedOffset( 122 | FixedOffset::east(timezone * 3600) 123 | .from_local_datetime(&time) 124 | .unwrap(), 125 | ) 126 | } 127 | None => Time::Local(Local.from_local_datetime(&time).unwrap()), 128 | }; 129 | 130 | Ok(result) 131 | } 132 | 133 | fn parse_rfc2822(input: &str) -> Result { 134 | DateTime::parse_from_rfc2822(input) 135 | .map(Time::FixedOffset) 136 | .map_err(|_| "Invalid input".to_string()) 137 | } 138 | 139 | fn parse_rfc3339(input: &str) -> Result { 140 | DateTime::parse_from_rfc3339(input) 141 | .map(Time::FixedOffset) 142 | .map_err(|_| "Invalid input".to_string()) 143 | } 144 | 145 | mod cases { 146 | use crate::modules::Case; 147 | use linked_hash_map::LinkedHashMap; 148 | 149 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 150 | vec![ 151 | ( 152 | "ts2d", 153 | vec![ 154 | Case { 155 | desc: "".to_string(), 156 | input: vec!["-z", "0", "0"].into_iter().map(Into::into).collect(), 157 | output: vec!["1970-01-01 00:00:00"] 158 | .into_iter() 159 | .map(Into::into) 160 | .collect(), 161 | is_example: true, 162 | is_test: true, 163 | since: "0.1.0".to_string(), 164 | }, 165 | Case { 166 | desc: "".to_string(), 167 | input: vec!["-z", "8", "10000"] 168 | .into_iter() 169 | .map(Into::into) 170 | .collect(), 171 | output: vec!["1970-01-01 10:46:40"] 172 | .into_iter() 173 | .map(Into::into) 174 | .collect(), 175 | is_example: false, 176 | is_test: true, 177 | since: "0.1.0".to_string(), 178 | }, 179 | ], 180 | ), 181 | ( 182 | "d2ts", 183 | vec![ 184 | Case { 185 | desc: "".to_string(), 186 | input: vec!["-z", "8", "'1970-01-01 08:00:00'"] 187 | .into_iter() 188 | .map(Into::into) 189 | .collect(), 190 | output: vec!["0"].into_iter().map(Into::into).collect(), 191 | is_example: true, 192 | is_test: true, 193 | since: "0.1.0".to_string(), 194 | }, 195 | Case { 196 | desc: "".to_string(), 197 | input: vec!["-z", "8", "1970-01-01 10:46:40"] 198 | .into_iter() 199 | .map(Into::into) 200 | .collect(), 201 | output: vec!["10000"].into_iter().map(Into::into).collect(), 202 | is_example: false, 203 | is_test: true, 204 | since: "0.1.0".to_string(), 205 | }, 206 | Case { 207 | desc: "Input rfc2822 format".to_string(), 208 | input: vec!["'Mon, 23 Dec 2019 17:41:26 +0800'"] 209 | .into_iter() 210 | .map(Into::into) 211 | .collect(), 212 | output: vec!["1577094086"].into_iter().map(Into::into).collect(), 213 | is_example: true, 214 | is_test: true, 215 | since: "0.1.0".to_string(), 216 | }, 217 | Case { 218 | desc: "Input rfc3339 format".to_string(), 219 | input: vec!["'2019-12-23T17:48:54+08:00'"] 220 | .into_iter() 221 | .map(Into::into) 222 | .collect(), 223 | output: vec!["1577094534"].into_iter().map(Into::into).collect(), 224 | is_example: true, 225 | is_test: true, 226 | since: "0.1.0".to_string(), 227 | }, 228 | ], 229 | ), 230 | ( 231 | "ts", 232 | vec![Case { 233 | desc: "".to_string(), 234 | input: vec![""].into_iter().map(Into::into).collect(), 235 | output: vec!["1647064300"].into_iter().map(Into::into).collect(), 236 | is_example: true, 237 | is_test: false, 238 | since: "0.12.0".to_string(), 239 | }], 240 | ), 241 | ] 242 | .into_iter() 243 | .collect() 244 | } 245 | } 246 | 247 | #[cfg(test)] 248 | mod tests { 249 | use super::*; 250 | use crate::modules::base::test::test_module; 251 | 252 | #[test] 253 | fn test_cases() { 254 | test_module(module()); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/modules/unicode.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::{base, Command, Module}; 2 | use clap::{Arg, ArgMatches, SubCommand}; 3 | use std::char::EscapeUnicode; 4 | 5 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 6 | Module { 7 | desc: "UTF-8 string / unicode conversion".to_string(), 8 | commands: commands(), 9 | get_cases: cases::cases, 10 | } 11 | } 12 | 13 | static FORMAT_HELP: &str = "Format 14 | : \\u7c 15 | html: | 16 | html_d: | 17 | rust: \\u{7c}"; 18 | 19 | pub fn commands<'a, 'b>() -> Vec> { 20 | vec![ 21 | Command { 22 | app: SubCommand::with_name("s2u") 23 | .about("UTF-8 string to unicode") 24 | .arg( 25 | Arg::with_name("FORMAT") 26 | .long("format") 27 | .short("f") 28 | .help(FORMAT_HELP) 29 | .takes_value(true) 30 | .required(false), 31 | ) 32 | .arg(Arg::with_name("INPUT").required(false).index(1)), 33 | f: s2u, 34 | }, 35 | Command { 36 | app: SubCommand::with_name("u2s") 37 | .about("Unicode to UTF-8 string") 38 | .arg(Arg::with_name("INPUT").required(false).index(1)), 39 | f: u2s, 40 | }, 41 | ] 42 | } 43 | 44 | fn s2u(matches: &ArgMatches) -> Result, String> { 45 | let input = base::input_string(matches)?; 46 | 47 | let format = match matches.value_of("FORMAT") { 48 | Some("html") => format_html, 49 | Some("html_d") => format_html_d, 50 | Some("rust") => format_rust, 51 | _ => format_default, 52 | }; 53 | 54 | let result = input 55 | .chars() 56 | .map(char::escape_unicode) 57 | .map(format) 58 | .collect::, String>>()?; 59 | 60 | let result = result.join(""); 61 | 62 | Ok(vec![result]) 63 | } 64 | 65 | fn u2s(matches: &ArgMatches) -> Result, String> { 66 | let input = base::input_string(matches)?; 67 | 68 | let format = match input { 69 | _ if input.starts_with("\\u{") => "rust", 70 | _ if input.starts_with("&#x") => "html", 71 | _ if input.starts_with("&#") => "html_d", 72 | _ => "", 73 | }; 74 | 75 | let result = match format { 76 | "html" => input 77 | .split(';') 78 | .filter_map(from_html) 79 | .collect::>(), 80 | "html_d" => input 81 | .split(';') 82 | .filter_map(from_html_d) 83 | .collect::>(), 84 | "rust" => input 85 | .split('}') 86 | .filter_map(from_rust) 87 | .collect::>(), 88 | _ => input 89 | .split("\\u") 90 | .filter_map(from_default) 91 | .collect::>(), 92 | }?; 93 | 94 | Ok(vec![result]) 95 | } 96 | 97 | fn format_html(data: EscapeUnicode) -> Result { 98 | Ok(data 99 | .map(|x| match x { 100 | '\\' => '&', 101 | 'u' => '#', 102 | '{' => 'x', 103 | '}' => ';', 104 | _ => x, 105 | }) 106 | .collect()) 107 | } 108 | 109 | fn from_html(data: &str) -> Option> { 110 | if data.len() > 3 { 111 | let r = u32::from_str_radix(&data[3..], 16) 112 | .map_err(|_| "Convert failed".to_string()) 113 | .and_then(|x| std::char::from_u32(x).ok_or_else(|| "Convert failed".to_string())); 114 | Some(r) 115 | } else { 116 | None 117 | } 118 | } 119 | 120 | fn format_html_d(data: EscapeUnicode) -> Result { 121 | let number = data 122 | .filter(|x| match x { 123 | '\\' | 'u' | '{' | '}' => false, 124 | _ => true, 125 | }) 126 | .collect::(); 127 | let number = u64::from_str_radix(&number, 16).map_err(|_| "Convert failed")?; 128 | 129 | Ok(format!("&#{};", number)) 130 | } 131 | 132 | fn from_html_d(data: &str) -> Option> { 133 | if data.len() > 2 { 134 | let r = u32::from_str_radix(&data[2..], 10) 135 | .map_err(|_| "Convert failed".to_string()) 136 | .and_then(|x| std::char::from_u32(x).ok_or_else(|| "Convert failed".to_string())); 137 | Some(r) 138 | } else { 139 | None 140 | } 141 | } 142 | 143 | fn format_rust(data: EscapeUnicode) -> Result { 144 | Ok(data.collect()) 145 | } 146 | 147 | fn from_rust(data: &str) -> Option> { 148 | if data.len() > 3 { 149 | let r = u32::from_str_radix(&data[3..], 16) 150 | .map_err(|_| "Convert failed".to_string()) 151 | .and_then(|x| std::char::from_u32(x).ok_or_else(|| "Convert failed".to_string())); 152 | Some(r) 153 | } else { 154 | None 155 | } 156 | } 157 | 158 | fn format_default(data: EscapeUnicode) -> Result { 159 | Ok(data.filter(|x| x != &'{' && x != &'}').collect()) 160 | } 161 | 162 | fn from_default(data: &str) -> Option> { 163 | if data.len() > 0 { 164 | let r = u32::from_str_radix(&data, 16) 165 | .map_err(|_| "Convert failed".to_string()) 166 | .and_then(|x| std::char::from_u32(x).ok_or_else(|| "Convert failed".to_string())); 167 | Some(r) 168 | } else { 169 | None 170 | } 171 | } 172 | 173 | mod cases { 174 | use crate::modules::Case; 175 | use linked_hash_map::LinkedHashMap; 176 | 177 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 178 | vec![ 179 | ( 180 | "s2u", 181 | vec![ 182 | Case { 183 | desc: "Default format".to_string(), 184 | input: vec!["abc"].into_iter().map(Into::into).collect(), 185 | output: vec!["\\u61\\u62\\u63"] 186 | .into_iter() 187 | .map(Into::into) 188 | .collect(), 189 | is_example: true, 190 | is_test: true, 191 | since: "0.3.0".to_string(), 192 | }, 193 | Case { 194 | desc: "HTML format".to_string(), 195 | input: vec!["-f", "html", "abc"] 196 | .into_iter() 197 | .map(Into::into) 198 | .collect(), 199 | output: vec!["abc"] 200 | .into_iter() 201 | .map(Into::into) 202 | .collect(), 203 | is_example: true, 204 | is_test: true, 205 | since: "0.3.0".to_string(), 206 | }, 207 | Case { 208 | desc: "HTML decimal format".to_string(), 209 | input: vec!["-f", "html_d", "abc"] 210 | .into_iter() 211 | .map(Into::into) 212 | .collect(), 213 | output: vec!["abc"] 214 | .into_iter() 215 | .map(Into::into) 216 | .collect(), 217 | is_example: true, 218 | is_test: true, 219 | since: "0.3.0".to_string(), 220 | }, 221 | Case { 222 | desc: "RUST format".to_string(), 223 | input: vec!["-f", "rust", "abc"] 224 | .into_iter() 225 | .map(Into::into) 226 | .collect(), 227 | output: vec!["\\u{61}\\u{62}\\u{63}"] 228 | .into_iter() 229 | .map(Into::into) 230 | .collect(), 231 | is_example: true, 232 | is_test: true, 233 | since: "0.3.0".to_string(), 234 | }, 235 | Case { 236 | desc: "Emoji".to_string(), 237 | input: vec!["💯"].into_iter().map(Into::into).collect(), 238 | output: vec!["\\u1f4af"].into_iter().map(Into::into).collect(), 239 | is_example: true, 240 | is_test: true, 241 | since: "0.3.0".to_string(), 242 | }, 243 | ], 244 | ), 245 | ( 246 | "u2s", 247 | vec![ 248 | Case { 249 | desc: "From default format".to_string(), 250 | input: vec!["'\\u61\\u62\\u63'"] 251 | .into_iter() 252 | .map(Into::into) 253 | .collect(), 254 | output: vec!["abc"].into_iter().map(Into::into).collect(), 255 | is_example: true, 256 | is_test: true, 257 | since: "0.3.0".to_string(), 258 | }, 259 | Case { 260 | desc: "HTML format".to_string(), 261 | input: vec!["'abc'"] 262 | .into_iter() 263 | .map(Into::into) 264 | .collect(), 265 | output: vec!["abc"].into_iter().map(Into::into).collect(), 266 | is_example: true, 267 | is_test: true, 268 | since: "0.3.0".to_string(), 269 | }, 270 | Case { 271 | desc: "HTML decimal format".to_string(), 272 | input: vec!["'abc'"] 273 | .into_iter() 274 | .map(Into::into) 275 | .collect(), 276 | output: vec!["abc"].into_iter().map(Into::into).collect(), 277 | is_example: true, 278 | is_test: true, 279 | since: "0.3.0".to_string(), 280 | }, 281 | Case { 282 | desc: "RUST format".to_string(), 283 | input: vec!["'\\u{61}\\u{62}\\u{63}'"] 284 | .into_iter() 285 | .map(Into::into) 286 | .collect(), 287 | output: vec!["abc"].into_iter().map(Into::into).collect(), 288 | is_example: true, 289 | is_test: true, 290 | since: "0.3.0".to_string(), 291 | }, 292 | Case { 293 | desc: "Emoji".to_string(), 294 | input: vec!["'\\u1f4af'"].into_iter().map(Into::into).collect(), 295 | output: vec!["💯"].into_iter().map(Into::into).collect(), 296 | is_example: true, 297 | is_test: true, 298 | since: "0.3.0".to_string(), 299 | }, 300 | ], 301 | ), 302 | ] 303 | .into_iter() 304 | .collect() 305 | } 306 | } 307 | 308 | #[cfg(test)] 309 | mod tests { 310 | use super::*; 311 | use crate::modules::base::test::test_module; 312 | 313 | #[test] 314 | fn test_cases() { 315 | test_module(module()); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/modules/url.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::{base, Command, Module}; 2 | use clap::{Arg, ArgMatches, SubCommand}; 3 | 4 | pub fn module<'a, 'b>() -> Module<'a, 'b> { 5 | Module { 6 | desc: "URL encode / decode".to_string(), 7 | commands: commands(), 8 | get_cases: cases::cases, 9 | } 10 | } 11 | 12 | pub fn commands<'a, 'b>() -> Vec> { 13 | vec![ 14 | Command { 15 | app: SubCommand::with_name("ue") 16 | .about("URL encode") 17 | .arg(Arg::with_name("INPUT").required(false).index(1)), 18 | f: ue, 19 | }, 20 | Command { 21 | app: SubCommand::with_name("ud") 22 | .about("URL decode") 23 | .arg(Arg::with_name("INPUT").required(false).index(1)), 24 | f: ud, 25 | }, 26 | ] 27 | } 28 | 29 | fn ue(matches: &ArgMatches) -> Result, String> { 30 | let input = base::input_string(matches)?; 31 | 32 | let result = urlencoding::encode(&input); 33 | 34 | Ok(vec![result]) 35 | } 36 | 37 | fn ud(matches: &ArgMatches) -> Result, String> { 38 | let input = base::input_string(matches)?; 39 | 40 | let result = urlencoding::decode(&input).map_err(|_| "Decode failed")?; 41 | 42 | Ok(vec![result]) 43 | } 44 | 45 | mod cases { 46 | use crate::modules::Case; 47 | use linked_hash_map::LinkedHashMap; 48 | 49 | pub fn cases() -> LinkedHashMap<&'static str, Vec> { 50 | vec![ 51 | ( 52 | "ue", 53 | vec![Case { 54 | desc: "".to_string(), 55 | input: vec!["a+b"].into_iter().map(Into::into).collect(), 56 | output: vec!["a%2Bb"].into_iter().map(Into::into).collect(), 57 | is_example: true, 58 | is_test: true, 59 | since: "0.1.0".to_string(), 60 | }], 61 | ), 62 | ( 63 | "ud", 64 | vec![Case { 65 | desc: "".to_string(), 66 | input: vec!["a%2Bb"].into_iter().map(Into::into).collect(), 67 | output: vec!["a+b"].into_iter().map(Into::into).collect(), 68 | is_example: true, 69 | is_test: true, 70 | since: "0.1.0".to_string(), 71 | }], 72 | ), 73 | ] 74 | .into_iter() 75 | .collect() 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use super::*; 82 | use crate::modules::base::test::test_module; 83 | 84 | #[test] 85 | fn test_cases() { 86 | test_module(module()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/modules/usage.rs: -------------------------------------------------------------------------------- 1 | use crate::modules::Module; 2 | use clap::{App, Arg, ArgMatches, SubCommand}; 3 | use linked_hash_map::LinkedHashMap; 4 | use madato::mk_table; 5 | use prettytable::{format, Cell, Row, Table}; 6 | use regex::Captures; 7 | use std::cmp::min; 8 | 9 | pub fn app<'a, 'b>() -> App<'a, 'b> { 10 | SubCommand::with_name("usage").about("Show usage") 11 | .arg( 12 | Arg::with_name("FORMAT") 13 | .long("format") 14 | .short("f").help("Output format\n: term table format\nmarkdown: markdown format\nplain: term plain format") 15 | .takes_value(true) 16 | .required(false)) 17 | .arg( 18 | Arg::with_name("SEARCH") 19 | .long("search") 20 | .short("s").help("") 21 | .takes_value(true) 22 | .required(false)) 23 | } 24 | 25 | pub fn run(matches: &ArgMatches, modules: &[Module]) -> Result, String> { 26 | let usage_info = get_usage_info(modules); 27 | 28 | let search = matches.value_of("SEARCH"); 29 | 30 | let usage_info = match search { 31 | Some(search) => { 32 | let search = &search.to_lowercase(); 33 | 34 | let usage_info = usage_info 35 | .into_iter() 36 | .filter_map(|(title, commands)| { 37 | let commands = commands 38 | .into_iter() 39 | .filter(|item| { 40 | let mut row = vec![&item.0, &item.1, &item.2, &item.3]; 41 | row.extend(&item.4); 42 | row.iter().any(|&cell| cell.to_lowercase().contains(search)) 43 | }) 44 | .collect::)>>(); 45 | if commands.len() > 0 { 46 | Some((title, commands)) 47 | } else { 48 | None 49 | } 50 | }) 51 | .collect(); 52 | usage_info 53 | } 54 | _ => usage_info, 55 | }; 56 | 57 | let format = matches.value_of("FORMAT"); 58 | 59 | match format { 60 | Some("markdown") => markdown_output(usage_info), 61 | Some("plain") => term_plain_output(usage_info), 62 | _ => term_table_output(usage_info), 63 | } 64 | } 65 | 66 | fn term_plain_output( 67 | usage_info: Vec<(String, Vec<(String, String, String, String, Vec)>)>, 68 | ) -> Result, String> { 69 | const WIDTH: usize = 100; 70 | 71 | let mut result = vec!["Usage".to_string()]; 72 | 73 | let body = usage_info 74 | .into_iter() 75 | .fold(Vec::new(), |mut vec, (_, mut commands)| { 76 | vec.append(&mut commands); 77 | vec 78 | }); 79 | let body = body.into_iter().fold(Vec::new(), |mut vec, items| { 80 | let tmp = vec![format!("# {}", items.0), items.1, items.2, items.3] 81 | .into_iter() 82 | .filter(|x| x.len() > 0) 83 | .map(|x| add_enter(x, WIDTH, false)) 84 | .chain(items.4.into_iter().map(|x| add_enter(x, WIDTH, true))) 85 | .collect::>(); 86 | vec.extend(tmp); 87 | vec.push("".to_string()); 88 | vec 89 | }); 90 | 91 | result.extend(body); 92 | 93 | Ok(result) 94 | } 95 | 96 | fn term_table_output( 97 | usage_info: Vec<(String, Vec<(String, String, String, String, Vec)>)>, 98 | ) -> Result, String> { 99 | const DESC_WIDTH: usize = 40; 100 | const EXAMPLE_WIDTH: usize = 60; 101 | 102 | let mut result = vec!["Usage".to_string()]; 103 | 104 | let body = usage_info 105 | .into_iter() 106 | .fold(Vec::new(), |mut vec, (_, mut commands)| { 107 | vec.append(&mut commands); 108 | vec 109 | }); 110 | let mut table = Table::init( 111 | body.into_iter() 112 | .map(|item| { 113 | let sub_command = item.0; 114 | let desc = vec![item.1, item.2, item.3] 115 | .into_iter() 116 | .filter(|x| x.len() > 0) 117 | .map(|x| add_enter(x, DESC_WIDTH, false)) 118 | .collect::>() 119 | .join("\n"); 120 | let example = item 121 | .4 122 | .into_iter() 123 | .map(|x| add_enter(x, EXAMPLE_WIDTH, true)) 124 | .collect::>() 125 | .join("\n"); 126 | Row::new(vec![ 127 | Cell::new(&sub_command), 128 | Cell::new(&desc), 129 | Cell::new(&example), 130 | ]) 131 | }) 132 | .collect(), 133 | ); 134 | table.set_format(*format::consts::FORMAT_NO_COLSEP); 135 | 136 | result.push(table.to_string()); 137 | 138 | Ok(result) 139 | } 140 | 141 | fn markdown_output( 142 | usage_info: Vec<(String, Vec<(String, String, String, String, Vec)>)>, 143 | ) -> Result, String> { 144 | const DESC_WIDTH: usize = 40; 145 | const EXAMPLE_WIDTH: usize = 60; 146 | 147 | let mut result = vec!["# Usage".to_string(), "".to_string()]; 148 | 149 | // table of contents 150 | let outline = usage_info 151 | .iter() 152 | .map(|(title, _)| format!("- [{}](#{})", title, anchor(title))) 153 | .collect::>(); 154 | 155 | result.push("## Table of Contents".to_string()); 156 | result.extend(outline); 157 | result.push("".to_string()); 158 | 159 | // body 160 | let blanks_re = regex::Regex::new(" {2,}").expect("qed"); 161 | 162 | let cell_process = |cell: String| { 163 | let cell = escaper::encode_minimal(&cell); 164 | // blank(more then 1) to   165 | let cell = blanks_re.replace_all(&cell, |caps: &Captures| caps[0].replace(" ", " ")); 166 | // enter to
167 | let cell = cell.replace("\\\n", "\\\\
"); 168 | cell.replace("\n", "
") 169 | }; 170 | 171 | let header = vec!["Sub command", "Desc", "Example"]; 172 | 173 | for module in usage_info { 174 | result.push(format!("## {}", module.0)); 175 | result.push("".to_string()); 176 | let body = module.1; 177 | let body = body 178 | .into_iter() 179 | .map(|item| { 180 | let sub_command = item.0; 181 | let desc = vec![item.1, item.2, item.3] 182 | .into_iter() 183 | .filter(|x| x.len() > 0) 184 | .map(|x| add_enter(x, DESC_WIDTH, false)) 185 | .collect::>() 186 | .join("\n"); 187 | let example = item 188 | .4 189 | .into_iter() 190 | .map(|x| add_enter(x, EXAMPLE_WIDTH, true)) 191 | .collect::>() 192 | .join("\n"); 193 | vec![sub_command, desc, example] 194 | }) 195 | .collect::>>(); 196 | 197 | let body: Vec> = body 198 | .into_iter() 199 | .map(|row| { 200 | row.into_iter() 201 | .enumerate() 202 | .map(|(col, cell)| (header[col].to_string(), cell_process(cell))) 203 | .collect::>() 204 | }) 205 | .collect(); 206 | 207 | let table = mk_table(&body, &None); 208 | result.push(table); 209 | result.push("".to_string()); 210 | result.push("".to_string()); 211 | } 212 | 213 | Ok(result) 214 | } 215 | 216 | /// Get usage info 217 | /// Sub command, Sub command desc, Case desc, Since, Example 218 | fn get_usage_info( 219 | modules: &[Module], 220 | ) -> Vec<(String, Vec<(String, String, String, String, Vec)>)> { 221 | let mut result = vec![]; 222 | for module in modules { 223 | let module_desc = module.desc.clone(); 224 | let mut body = vec![]; 225 | let commands = &module.commands; 226 | let cases = (module.get_cases)(); 227 | for command in commands { 228 | let name = command.app.get_name(); 229 | let about = get_about(&command.app); 230 | let cases = cases.get(name); 231 | if let Some(cases) = cases { 232 | for case in cases { 233 | if case.is_example { 234 | let sub_command = name.to_owned(); 235 | let sub_command_desc = about.clone(); 236 | let case_desc = case.desc.clone(); 237 | let since = format!("v{}", case.since); 238 | let example = { 239 | let input = case.input.clone(); 240 | let output = case.output.clone(); 241 | let input = vec!["$ dtool".to_string(), name.to_string()] 242 | .into_iter() 243 | .chain(input) 244 | .collect::>() 245 | .join(" "); 246 | vec![input] 247 | .into_iter() 248 | .chain(output) 249 | .collect::>() 250 | }; 251 | 252 | let item = (sub_command, sub_command_desc, case_desc, since, example); 253 | body.push(item); 254 | } 255 | } 256 | } 257 | } 258 | result.push((module_desc, body)); 259 | } 260 | result 261 | } 262 | 263 | fn get_about(app: &App) -> String { 264 | let mut help = Vec::new(); 265 | let _ = app.write_help(&mut help); 266 | 267 | let about = String::from_utf8(help).map(|x| x.split('\n').skip(1).take(1).collect::()); //about is the second line 268 | 269 | about.ok().unwrap_or_default() 270 | } 271 | 272 | fn add_enter(data: String, len: usize, add_escape: bool) -> String { 273 | let mut v = vec![]; 274 | let mut cur = data.as_str(); 275 | while !cur.is_empty() { 276 | let (chunk, rest) = cur.split_at(min(len, cur.len())); 277 | v.push(chunk); 278 | cur = rest; 279 | } 280 | let join_char = if add_escape { "\\\n" } else { "\n" }; 281 | v.join(join_char) 282 | } 283 | 284 | fn anchor(title: &str) -> String { 285 | title 286 | .chars() 287 | .filter_map(|x| { 288 | if x.is_whitespace() || x == '-' { 289 | Some('-') 290 | } else if x.is_alphanumeric() { 291 | Some(x.to_lowercase().next().expect("qed")) 292 | } else { 293 | None 294 | } 295 | }) 296 | .collect::() 297 | } 298 | --------------------------------------------------------------------------------