diff --git a/Cargo.lock b/Cargo.lock index 407b057..005562e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -277,6 +277,16 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + [[package]] name = "buffered-reader" version = "1.1.2" @@ -366,7 +376,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.3.3" +version = "1.4.0" dependencies = [ "authenticator", "base64 0.13.0", @@ -384,12 +394,15 @@ dependencies = [ "rust_util", "sequoia-openpgp", "serde", + "serde_derive", "serde_json", "sha1", "sha2 0.10.2", "simpledateformat", "ssh-agent", + "tokio 1.18.0", "u2f", + "warp", "x509", "x509-parser 0.13.2", "yubico_manager", @@ -898,6 +911,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "fs2" version = "0.4.3" @@ -1078,12 +1101,56 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes 1.1.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio 1.18.0", + "tokio-util 0.7.2", + "tracing", +] + [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "headers" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +dependencies = [ + "base64 0.13.0", + "bitflags", + "bytes 1.1.0", + "headers-core", + "http", + "httpdate", + "mime", + "sha-1 0.10.0", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1115,12 +1182,70 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes 1.1.0", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes 1.1.0", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +dependencies = [ + "bytes 1.1.0", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.4", + "tokio 1.18.0", + "tower-service", + "tracing", + "want", +] + [[package]] name = "idna" version = "0.2.3" @@ -1377,6 +1502,22 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ac78937f19a0c7807e45a931eac41f766f210173ec664ec046d58e6d388a5cb" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1457,6 +1598,24 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "multipart" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +dependencies = [ + "buf_redux", + "httparse", + "log", + "mime", + "mime_guess", + "quick-error", + "rand 0.8.5", + "safemem", + "tempfile", + "twoway", +] + [[package]] name = "net2" version = "0.2.37" @@ -1868,6 +2027,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + [[package]] name = "petgraph" version = "0.5.1" @@ -1893,6 +2058,26 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote 1.0.18", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -2221,6 +2406,18 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + [[package]] name = "scopeguard" version = "1.1.0" @@ -2275,7 +2472,7 @@ dependencies = [ "tempfile", "thiserror", "tokio 1.18.0", - "tokio-util", + "tokio-util 0.6.9", "winapi 0.3.9", ] @@ -2340,6 +2537,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -2353,6 +2562,17 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + [[package]] name = "sha1" version = "0.6.1" @@ -2845,6 +3065,17 @@ dependencies = [ "tokio-sync", ] +[[package]] +name = "tokio-stream" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio 1.18.0", +] + [[package]] name = "tokio-sync" version = "0.1.8" @@ -2898,6 +3129,19 @@ dependencies = [ "tokio-executor", ] +[[package]] +name = "tokio-tungstenite" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" +dependencies = [ + "futures-util", + "log", + "pin-project", + "tokio 1.18.0", + "tungstenite", +] + [[package]] name = "tokio-udp" version = "0.1.6" @@ -2946,6 +3190,93 @@ dependencies = [ "tokio 1.18.0", ] +[[package]] +name = "tokio-util" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" +dependencies = [ + "bytes 1.1.0", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio 1.18.0", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +dependencies = [ + "proc-macro2", + "quote 1.0.18", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "tungstenite" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" +dependencies = [ + "base64 0.13.0", + "byteorder", + "bytes 1.1.0", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.9.8", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + [[package]] name = "typenum" version = "1.15.0" @@ -2969,6 +3300,15 @@ dependencies = [ "time 0.1.43", ] +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.8" @@ -3008,6 +3348,24 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "uuid" version = "0.8.2" @@ -3035,6 +3393,46 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "warp" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cef4e1e9114a4b7f1ac799f16ce71c14de5778500c5450ec6b7b920c55b587e" +dependencies = [ + "bytes 1.1.0", + "futures-channel", + "futures-util", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "multipart", + "percent-encoding", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio 1.18.0", + "tokio-stream", + "tokio-tungstenite", + "tokio-util 0.6.9", + "tower-service", + "tracing", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -3276,7 +3674,7 @@ dependencies = [ "hmac", "rand 0.8.5", "rusb", - "sha-1", + "sha-1 0.9.8", "structure", ] @@ -3304,7 +3702,7 @@ dependencies = [ "rand_core 0.6.3", "rsa", "secrecy", - "sha-1", + "sha-1 0.9.8", "sha2 0.9.9", "subtle", "subtle-encoding", diff --git a/Cargo.toml b/Cargo.toml index f49e173..8f0e369 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.3.3" +version = "1.4.0" authors = ["Hatter Jiang "] edition = "2018" @@ -31,6 +31,9 @@ yubico_manager = "0.9" x509 = "0.2" x509-parser = "0.13" ssh-agent = { version = "0.2.3", features = ["agent"] } +tokio = { version = "1", features = ["full"] } +serde_derive = "1.0" +warp = "0.3" #lazy_static = "1.4.0" #ssh-key = "0.4.0" #ctap-hid-fido2 = "2.1.3" diff --git a/src/cmd_pgpcardserve.rs b/src/cmd_pgpcardserve.rs new file mode 100644 index 0000000..5a4c07e --- /dev/null +++ b/src/cmd_pgpcardserve.rs @@ -0,0 +1,127 @@ +use std::collections::HashMap; + +use clap::{App, Arg, ArgMatches, SubCommand}; +use openpgp_card::OpenPgp; +use rust_util::util_clap::{Command, CommandError}; +use serde_derive::{Deserialize, Serialize}; +use warp::Filter; +use warp::reply::{Json, json}; + +pub struct CommandImpl; + +impl Command for CommandImpl { + fn name(&self) -> &str { "pgp-card-serve" } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("OpenPGP Card Decrypt subcommand") + .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).default_value("123456").help("OpenPGP card user pin")) + .arg(Arg::with_name("pass").long("pass").takes_value(true).help("[deprecated] now OpenPGP card user pin")) + .arg(Arg::with_name("listen").short("l").long("listen").takes_value(true).default_value("127.0.0.1:3627").help("Listen address")) + // .arg(Arg::with_name("ssh-attach-host").long("ssh-attach-host").takes_value(true).help("SSH attache (e.g. root@example.com)")) + } + + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let pin_opt = sub_arg_matches.value_of("pass").or_else(|| sub_arg_matches.value_of("pin")); + let pin = opt_value_result!(pin_opt, "User pin must be assigned"); + if pin.len() < 6 { return simple_error!("User pin length:{}, must >= 6!", pin.len()); } + + let mut card = crate::pgpcardutil::get_card()?; + let mut pgp = OpenPgp::new(&mut card); + let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}"); + + opt_result!(trans.verify_pw1_user(pin.as_ref()), "User pin verify failed: {}"); + success!("User pin verify success!"); + + // let cipher = sub_arg_matches.value_of("cipher"); + // let cipher_base64 = sub_arg_matches.value_of("cipher-base64"); + // + // let cipher_bytes = if let Some(cipher) = cipher { + // opt_result!(hex::decode(cipher), "Decode cipher failed: {}") + // } else if let Some(cipher_base64) = cipher_base64 { + // opt_result!(base64::decode(cipher_base64), "Decode cipher-base64 failed: {}") + // } else { + // return simple_error!("cipher or cipher-base64 must assign one"); + // }; + // + // let text = trans.decipher(Cryptogram::RSA(&cipher_bytes))?; + // success!("Clear text HEX: {}", hex::encode(&text)); + // success!("Clear text base64: {}", base64::encode(&text)); + // success!("Clear text UTF-8: {}", String::from_utf8_lossy(&text)); + + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async { + let version = warp::get().and(warp::path("version")).map(|| { + let mut version = HashMap::new(); + version.insert("version", env!("CARGO_PKG_VERSION")); + warp::reply::json(&version) + }); + let create_key = warp::post() + .and(warp::path("create_key")) + .and(warp::body::json()) + .map(|create_key_request: CreateKeyRequest| { + let len = create_key_request.len.unwrap_or(32); + if (len < 16) || (len > 1024) { + return ErrResponse::new(400, format!("Invalid length:{}", len)); + } + let key = crate::randutil::make_rand(len); + json(&CreateKeyResponse { + request_pub_key_encrypted_key_hex: None, + raw_key_hex: Some(hex::encode(&key)), + encrypted_key_hex: "".to_string(), + }) + }); + + information!("Serve at :3030"); + let routes = version.or(create_key); + warp::serve(routes) + .run(([127, 0, 0, 1], 3030)) + .await; + }); + + Ok(None) + } +} + +trait ErrorResponseMessage { + fn to_option_string(self) -> Option; +} + +impl ErrorResponseMessage for &str { + fn to_option_string(self) -> Option { + Some(self.to_string()) + } +} + +impl ErrorResponseMessage for String { + fn to_option_string(self) -> Option { + Some(self) + } +} + +#[derive(Deserialize, Serialize)] +struct ErrResponse { + code: i32, + message: Option, +} + +impl ErrResponse { + fn new(code: i32, message: impl ErrorResponseMessage) -> Json { + json(&ErrResponse { + code, + message: message.to_option_string(), + }) + } +} + +#[derive(Deserialize, Serialize)] +struct CreateKeyRequest { + len: Option, + request_pub_key_hex: Option, +} + +#[derive(Deserialize, Serialize)] +struct CreateKeyResponse { + request_pub_key_encrypted_key_hex: Option, + raw_key_hex: Option, + encrypted_key_hex: String, +} diff --git a/src/main.rs b/src/main.rs index da415de..74f8eb1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ mod fido; mod digest; mod rsautil; mod pkiutil; +mod randutil; mod pgpcardutil; mod cmd_u2fregister; mod cmd_u2fsign; @@ -20,6 +21,7 @@ mod cmd_pgpcardadmin; mod cmd_pgpcardlist; mod cmd_pgpcardsign; mod cmd_pgpcarddecrypt; +mod cmd_pgpcardserve; mod cmd_pgpcardmake; mod cmd_piv; mod cmd_pivsign; @@ -59,6 +61,7 @@ fn inner_main() -> CommandError { Box::new(cmd_pgpcardlist::CommandImpl), Box::new(cmd_pgpcardsign::CommandImpl), Box::new(cmd_pgpcarddecrypt::CommandImpl), + Box::new(cmd_pgpcardserve::CommandImpl), Box::new(cmd_pgpcardmake::CommandImpl), Box::new(cmd_piv::CommandImpl), Box::new(cmd_pivsign::CommandImpl), diff --git a/src/randutil.rs b/src/randutil.rs new file mode 100644 index 0000000..b8f3cd4 --- /dev/null +++ b/src/randutil.rs @@ -0,0 +1,11 @@ +use rand::Rng; + +pub fn make_rand(len: usize) -> Vec { + let mut rng = rand::thread_rng(); + let mut r = Vec::new(); + for _ in 0..len { + let b: u8 = rng.gen(); + r.push(b); + } + r +} \ No newline at end of file