diff --git a/Cargo.lock b/Cargo.lock index fe20e4c..4a07913 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + [[package]] name = "ahash" version = "0.8.3" @@ -123,6 +135,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -132,6 +153,22 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-modes" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" +dependencies = [ + "block-padding", + "cipher", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "bumpalo" version = "3.13.0" @@ -180,6 +217,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + [[package]] name = "clap" version = "2.34.0" @@ -229,19 +275,38 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "deranged" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "crypto-common", ] @@ -478,6 +543,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + [[package]] name = "http" version = "0.2.9" @@ -640,6 +715,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libusb1-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22e89d08bbe6816c6c5d446203b859eba35b8fa94bf1b7edb2f6d25d43f023f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.0.46" @@ -663,6 +750,7 @@ dependencies = [ "josekit", "lazy_static", "procfs", + "rpassword", "rusqlite", "rust_util", "seckey", @@ -672,6 +760,7 @@ dependencies = [ "serde_json", "sha2", "tokio", + "yubico_manager", "zeroize", ] @@ -762,6 +851,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "openssl" version = "0.10.56" @@ -784,7 +879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.32", "syn", ] @@ -841,6 +936,27 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-hack" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f95648580798cc44ff8efb9bb0d7ee5205ea32e087b31b0732f3e8c2648ee2" +dependencies = [ + "proc-macro-hack-impl", +] + +[[package]] +name = "proc-macro-hack-impl" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be55bf0ae1635f4d7c7ddd6efc05c631e98a82104a73d35550bbc52db960027" + [[package]] name = "proc-macro2" version = "1.0.66" @@ -865,6 +981,12 @@ dependencies = [ "rustix 0.35.14", ] +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" + [[package]] name = "quote" version = "1.0.32" @@ -874,6 +996,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -932,6 +1084,37 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +[[package]] +name = "rpassword" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +dependencies = [ + "libc", + "rtoolbox", + "winapi", +] + +[[package]] +name = "rtoolbox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "rusb" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9a5084628cc5be77b1c750b3e5ee0cc519d2f2491b3f06b78b3aac3328b00ad" +dependencies = [ + "libc", + "libusb1-sys", +] + [[package]] name = "rusqlite" version = "0.28.0" @@ -1051,7 +1234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.32", "syn", ] @@ -1067,6 +1250,19 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.7" @@ -1075,7 +1271,7 @@ checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -1128,6 +1324,33 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "structure" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8379aeb9cf8935b018b14d9191b15096e984ad8b77424b9bce075ea15d1fa59" +dependencies = [ + "byteorder", + "proc-macro-hack", + "structure-macro-impl", +] + +[[package]] +name = "structure-macro-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c0529f2429c8bb5878688fffab7f700087f4bd47906e6acf03fd7361f77aca" +dependencies = [ + "proc-macro-hack", + "quote 0.3.15", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "2.0.28" @@ -1135,7 +1358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.32", "unicode-ident", ] @@ -1185,7 +1408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.32", "syn", ] @@ -1243,7 +1466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.32", "syn", ] @@ -1370,7 +1593,7 @@ dependencies = [ "log", "once_cell", "proc-macro2", - "quote", + "quote 1.0.32", "syn", "wasm-bindgen-shared", ] @@ -1381,7 +1604,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ - "quote", + "quote 1.0.32", "wasm-bindgen-macro-support", ] @@ -1392,7 +1615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.32", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -1582,6 +1805,22 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "yubico_manager" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff64094218f27836b8683bd93fa7f257475d217449eb7629746fa8eaee068eee" +dependencies = [ + "aes", + "bitflags 1.3.2", + "block-modes", + "hmac", + "rand", + "rusb", + "sha-1", + "structure", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 0d7decc..ccbb1d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ rust_util = { version = "0.6", features = ["use_clap"] } tokio = { version = "1.19", features = ["full"] } hyper = { version = "0.14", features = ["client", "server", "tcp", "http1", "http2"] } rusqlite = "0.28" +yubico_manager = "0.9" +rpassword = "7.2" [target.'cfg(target_os = "linux")'.dependencies] procfs = "0.13" diff --git a/README.md b/README.md index 7c4693e..af13056 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,33 @@ Mini-KMS runs local written by Rust ## Startup Server ```shell -./local-mini-kms serve -``` - -```shell -./local-mini-kms cli --init +local-mini-kms serve [--init-encrypted-master-key LKMS:*** [--yubikey-challenge *challenge*]] ``` ## Local Client ```shell -./local-mini-kms cli --offline-init +local-mini-kms cli --init ``` +```shell +local-mini-kms cli --offline-init +``` + +```shell +local-mini-kms cli --direct-init --value-base64 wNdr9sZN4**** [--yubikey-challenge *challenge*] +``` + +```shell +local-mini-kms cli --encrypt --value hello +``` + +```shell +local-mini-kms cli --decrypt --value LKMS:*** +``` + +## cURL + Write value: ```shell curl -X POST http://127.0.0.1:5567/write \ diff --git a/src/cli.rs b/src/cli.rs index b247a2b..6310b3d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -8,7 +8,7 @@ use hyper::body::Buf; use josekit::jwk::Jwk; use rust_util::{debugging, opt_value_result, simple_error, success, XResult}; use rust_util::util_clap::{Command, CommandError}; -use serde_json::{json, Value}; +use serde_json::{json, Map, Value}; use crate::jose; @@ -19,7 +19,7 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("Local mini KMS cli") - .arg(Arg::with_name("connect").long("connect").takes_value(true).default_value("127.0.0.1:5567").help("Connect server")) + .arg(Arg::with_name("connect").long("connect").short("C").takes_value(true).default_value("127.0.0.1:5567").help("Connect server")) .arg(Arg::with_name("init").long("init").help("Init server")) .arg(Arg::with_name("direct-init").long("direct-init").help("Direct init server")) .arg(Arg::with_name("offline-init").long("offline-init").help("Offline init server")) @@ -31,7 +31,8 @@ impl Command for CommandImpl { .arg(Arg::with_name("key").long("key").takes_value(true).help("Read/Write key name")) .arg(Arg::with_name("value-hex").long("value-hex").takes_value(true).help("Value(hex), for encrypt")) .arg(Arg::with_name("value-base64").long("value-base64").takes_value(true).help("Value(base64), for encrypt")) - .arg(Arg::with_name("force-write").long("force-write").help("Force write value")) + .arg(Arg::with_name("yubikey-challenge").long("yubikey-challenge").short("c").takes_value(true).help("Yubikey challenge")) + .arg(Arg::with_name("force-write").long("force-write").short("F").help("Force write value")) } fn run(&self, arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { @@ -64,33 +65,22 @@ impl Command for CommandImpl { } async fn do_direct_init(_arg_matches: &ArgMatches<'_>, sub_arg_matches: &ArgMatches<'_>) -> CommandError { - let connect = sub_arg_matches.value_of("connect").expect("Get argument listen error"); let value_hex = sub_arg_matches.value_of("value-hex"); let value_base64 = sub_arg_matches.value_of("value-base64"); + let yubikey_challenge = sub_arg_matches.value_of("yubikey-challenge"); - let client = Client::new(); - let uri = format!("http://{}/init", connect); - debugging!("Request uri: {}", &uri); - let body = if let Some(value_hex) = value_hex { - json!({ - "clear_master_key_hex": value_hex, - }) + let mut body_map = Map::new(); + if let Some(value_hex) = value_hex { + body_map.insert("clear_master_key_hex".to_string(), value_hex.into()); } else if let Some(value_base64) = value_base64 { - json!({ - "clear_master_key_base64": value_base64, - }) + body_map.insert("clear_master_key_base64".to_string(), value_base64.into()); } else { return simple_error!("Requires value hex or value base64"); - }; - let body = serde_json::to_string(&body)?; - let req = Request::builder().method(Method::POST).uri(uri).body(Body::from(body))?; - - let req_response = client.request(req).await?; - if req_response.status() != StatusCode::OK { - let status = req_response.status().as_u16(); - let data = response_to_value(req_response).await?; - return simple_error!("Server status is not success: {}, response: {}", status, data); } + if let Some(yubikey_challenge) = yubikey_challenge { + body_map.insert("yubikey_challenge".to_string(), yubikey_challenge.into()); + } + let _data = do_inner_request(sub_arg_matches, "init", &Value::Object(body_map)).await?; success!("Init finished"); Ok(Some(0)) } diff --git a/src/main.rs b/src/main.rs index 6963f05..07feb1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod db; mod proc; mod jose; mod cli; +mod yubikey_hmac; mod serve; mod serve_common; mod serve_status; diff --git a/src/serve.rs b/src/serve.rs index a2f5eae..5febd56 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -5,17 +5,20 @@ use hyper::{Body, Client, Method, Request, Response, Server, StatusCode}; use hyper::client::HttpConnector; use hyper::server::conn::AddrStream; use hyper::service::{make_service_fn, service_fn}; -use rust_util::{failure_and_exit, information, success, XResult}; +use rust_util::{failure_and_exit, information, success, warning, XResult}; use rust_util::util_clap::{Command, CommandError}; use serde_json::{json, Value}; +use tokio::runtime::Runtime; use crate::{db, jose, proc}; use crate::do_response; use crate::serve_common::{self, GenericError, MemoryKey, Result}; use crate::serve_encrypt_decrypt; use crate::serve_init; +use crate::serve_init::InitRequest; use crate::serve_read_write; use crate::serve_status; +use crate::yubikey_hmac; pub struct CommandImpl; @@ -24,8 +27,10 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("Local mini KMS serve") - .arg(Arg::with_name("listen").long("listen").takes_value(true).default_value("127.0.0.1:5567").help("Listen")) - .arg(Arg::with_name("local-db").long("local-db").takes_value(true).default_value("local-mini-kms.db").help("Local db file")) + .arg(Arg::with_name("listen").long("listen").short("L").takes_value(true).default_value("127.0.0.1:5567").help("Listen")) + .arg(Arg::with_name("local-db").long("local-db").short("d").takes_value(true).default_value("local-mini-kms.db").help("Local db file")) + .arg(Arg::with_name("yubikey-challenge").long("yubikey-challenge").short("c").takes_value(true).help("Yubikey challenge")) + .arg(Arg::with_name("init-encrypted-master-key").long("init-encrypted-master-key").short("k").takes_value(true).help("Init encrypted mater key")) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { @@ -36,8 +41,10 @@ impl Command for CommandImpl { Err(e) => failure_and_exit!("Init server failed: {}", e), } + let rt = Runtime::new().expect("Create tokio runtime error"); + init_with_yubikey_challenge(&rt, sub_arg_matches); + let listen = sub_arg_matches.value_of("listen").expect("Get argument listen error"); - let rt = tokio::runtime::Runtime::new().expect("Create tokio runtime error"); rt.block_on(async { let addr = listen.parse().expect(&format!("Parse listen error: {}", listen)); let client = Client::new(); @@ -156,3 +163,49 @@ Supports commands: Ok(Response::builder().body("Root Not Found\n".into())?) } } + +fn init_with_yubikey_challenge(rt: &Runtime, sub_arg_matches: &ArgMatches) { + let mut yubikey_challenge = sub_arg_matches.value_of("yubikey-challenge").map(ToString::to_string); + let init_encrypted_master_key = sub_arg_matches.value_of("init-encrypted-master-key"); + + if yubikey_challenge.is_none() { + yubikey_challenge = rpassword::prompt_password("Yubikey challenge: ").ok(); + } + + let (challenge_key, init_encrypted_master_key) = match (yubikey_challenge, init_encrypted_master_key) { + (Some(yubikey_challenge), Some(init_encrypted_master_key)) => { + match yubikey_hmac::yubikey_challenge_as_32_bytes(yubikey_challenge.as_bytes()) { + Err(e) => { + warning!("Yubikey challenge failed: {}", e); + return; + } + Ok(challenge_key) => (challenge_key, init_encrypted_master_key), + } + } + (Some(_), None) | (None, Some(_)) => { + warning!("Arguments yubikey-challenge and init-encrypted-master-key should both assigned."); + return; + } + _ => return, + }; + + match jose::deserialize_jwe_aes(&init_encrypted_master_key, &challenge_key) { + Err(e) => warning!("Yubikey seal master key failed: {}", e), + Ok((key, _)) => { + success!("Yubikey un-seal master key success"); + let init_master_key_result = rt.block_on(async { + serve_init::inner_init_request(InitRequest { + yubikey_challenge: None, + clear_master_key_hex: Some(hex::encode(&key)), + clear_master_key_base64: None, + encrypted_master_key: None, + }).await + }); + match init_master_key_result { + Err(e) => warning!("Init master key failed: {}", e), + Ok((StatusCode::OK, _)) => success!("Init master key success"), + Ok((_, response)) => warning!("Init master failed: {}", response), + } + } + } +} diff --git a/src/serve_init.rs b/src/serve_init.rs index 16c8b00..3efedb6 100644 --- a/src/serve_init.rs +++ b/src/serve_init.rs @@ -2,7 +2,7 @@ use base64::Engine; use base64::engine::general_purpose::STANDARD; use hyper::{Body, Request, Response, StatusCode}; use hyper::body::Buf; -use rust_util::{debugging, information, opt_result, XResult}; +use rust_util::{debugging, information, opt_result, success, warning, XResult}; use seckey::SecBytes; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; @@ -12,22 +12,28 @@ use crate::{db, jose}; use crate::db::Key; use crate::do_response; use crate::serve_common::{self, Result}; +use crate::yubikey_hmac; pub async fn init(req: Request) -> Result> { do_response!(inner_init(req).await) } #[derive(Serialize, Deserialize)] -struct InitRequest { - clear_master_key_hex: Option, - clear_master_key_base64: Option, - encrypted_master_key: Option, +pub struct InitRequest { + pub yubikey_challenge: Option, + pub clear_master_key_hex: Option, + pub clear_master_key_base64: Option, + pub encrypted_master_key: Option, } async fn inner_init(req: Request) -> XResult<(StatusCode, Value)> { let whole_body = hyper::body::aggregate(req).await?; let init_request: InitRequest = serde_json::from_reader(whole_body.reader())?; + inner_init_request(init_request).await +} + +pub async fn inner_init_request(init_request: InitRequest) -> XResult<(StatusCode, Value)> { let mut startup_rw_lock = serve_common::STATUP_RW_LOCK.lock().expect("Lock read startup rw lock error"); match &*startup_rw_lock { None => return Ok((StatusCode::INTERNAL_SERVER_ERROR, json!({ "error": "internal_error", "error_message": "not init " }))), @@ -74,6 +80,17 @@ async fn inner_init(req: Request) -> XResult<(StatusCode, Value)> { } } information!("Set master key success"); + + if let Some(yubikey_challenge) = &init_request.yubikey_challenge { + match yubikey_hmac::yubikey_challenge_as_32_bytes(yubikey_challenge.as_bytes()) { + Err(e) => warning!("Yubikey challenge failed: {}", e), + Ok(challenge_key) => match jose::serialize_jwe_aes(&clear_master_key, &challenge_key) { + Err(e) => warning!("Yubikey seal master key failed: {}", e), + Ok(jwe) => success!("Yubikey sealed master key: {}", jwe) + }, + } + } + let sec_bytes = SecBytes::with(clear_master_key.len(), |buf| buf.copy_from_slice(&clear_master_key.as_slice()[..])); let mut clear_master_key = clear_master_key; clear_master_key.zeroize(); diff --git a/src/yubikey_hmac.rs b/src/yubikey_hmac.rs new file mode 100644 index 0000000..c40e9c5 --- /dev/null +++ b/src/yubikey_hmac.rs @@ -0,0 +1,29 @@ +use std::ops::Deref; +use rust_util::{opt_result, success, XResult}; +use yubico_manager::config::{Config, Mode, Slot}; +use yubico_manager::Yubico; + +pub fn yubikey_challenge_as_32_bytes(challenge_bytes: &[u8]) -> XResult> { + let mut yubi = Yubico::new(); + let device = opt_result!(yubi.find_yubikey(), "Find yubikey failed: {}"); + + success!("Found key, Vendor ID: {:?}, Product ID: {:?}", device.vendor_id, device.product_id); + + let config = Config::default() + .set_vendor_id(device.vendor_id) + .set_product_id(device.product_id) + .set_variable_size(true) + .set_mode(Mode::Sha1) + .set_slot(Slot::Slot2); + + // In HMAC Mode, the result will always be the SAME for the SAME provided challenge + let hmac_result = opt_result!(yubi.challenge_response_hmac(&challenge_bytes, config), "Challenge HMAC failed: {}"); + + // Just for debug, lets check the hex + let v: &[u8] = hmac_result.deref(); + + let mut r = vec![]; + r.extend_from_slice(v); + r.extend_from_slice(&v[0..12]); + Ok(r) +}