From 6e0b65ed3108942a409da1306d79a883ddb15d6f Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Tue, 28 May 2024 12:13:12 +0800 Subject: [PATCH 01/16] create relay module Signed-off-by: Qihang Cai --- Cargo.toml | 1 + replay/Cargo.toml | 7 +++++++ replay/src/main.rs | 3 +++ 3 files changed, 11 insertions(+) create mode 100644 replay/Cargo.toml create mode 100644 replay/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 7cd113a27..fc17c7785 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "venus", "ceres", "libra", + "replay", ] exclude = ["craft"] resolver = "1" diff --git a/replay/Cargo.toml b/replay/Cargo.toml new file mode 100644 index 000000000..b088e1e15 --- /dev/null +++ b/replay/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "replay" +version = "0.1.0" +edition = "2021" + +[dependencies] +rusty_vault = "0.1.0" \ No newline at end of file diff --git a/replay/src/main.rs b/replay/src/main.rs new file mode 100644 index 000000000..e7a11a969 --- /dev/null +++ b/replay/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From f1b9c2006f21824a1b2d3d404f7e50d22baf6dff Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Sun, 2 Jun 2024 21:19:51 +0800 Subject: [PATCH 02/16] test `rusty_vault` Signed-off-by: Qihang Cai --- replay/Cargo.toml | 6 +- replay/src/lib.rs | 298 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 replay/src/lib.rs diff --git a/replay/Cargo.toml b/replay/Cargo.toml index b088e1e15..2362c70f5 100644 --- a/replay/Cargo.toml +++ b/replay/Cargo.toml @@ -4,4 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -rusty_vault = "0.1.0" \ No newline at end of file +rusty_vault = "0.1.0" +serde_json = "1.0.117" +go-defer = "0.1.0" +openssl = "0.10.63" +hex = "0.4.3" \ No newline at end of file diff --git a/replay/src/lib.rs b/replay/src/lib.rs new file mode 100644 index 000000000..37480be85 --- /dev/null +++ b/replay/src/lib.rs @@ -0,0 +1,298 @@ +#[cfg(test)] +mod tests { + use std::{ + collections::HashMap, + default::Default, + env, fs, + sync::{Arc, RwLock}, + time::{SystemTime, UNIX_EPOCH}, + }; + use std::io::Write; + + use go_defer::defer; + use openssl::{asn1::Asn1Time, ec::EcKey, nid::Nid, pkey::PKey, rsa::Rsa, x509::X509}; + use rusty_vault::{ + core::{Core, SealConfig}, + logical::{Operation, Request}, + storage::{barrier_aes_gcm, physical}, + }; + use rusty_vault::errors::RvError; + use rusty_vault::logical::Response; + use serde_json::{json, Map, Value}; + + fn test_read_api(core: &Core, token: &str, path: &str, is_ok: bool) -> Result, RvError> { + let mut req = Request::new(path); + req.operation = Operation::Read; + req.client_token = token.to_string(); + let resp = core.handle_request(&mut req); + assert_eq!(resp.is_ok(), is_ok); + resp + } + + fn test_write_api( + core: &Core, + token: &str, + path: &str, + is_ok: bool, + data: Option>, + ) -> Result, RvError> { + let mut req = Request::new(path); + req.operation = Operation::Write; + req.client_token = token.to_string(); + req.body = data; + + let resp = core.handle_request(&mut req); + println!("path: {}, req.body: {:?}", path, req.body); + assert_eq!(resp.is_ok(), is_ok); + resp + } + + fn test_pki_config_ca(core: Arc>, token: &str) { + let core = core.read().unwrap(); + + // mount pki backend to path: pki/ + let mount_data = json!({ + "type": "pki", + }) + .as_object() + .unwrap() + .clone(); + + let resp = test_write_api(&core, token, "sys/mounts/pki/", true, Some(mount_data)); + assert!(resp.is_ok()); + } + + fn test_pki_config_role(core: Arc>, token: &str) { + let core = core.read().unwrap(); + + let role_data = json!({ + "ttl": "60d", + "max_ttl": "365d", + "key_type": "rsa", + "key_bits": 4096, + "country": "CN", + "province": "Beijing", + "locality": "Beijing", + "organization": "OpenAtom", + "no_store": false, + }) + .as_object() + .unwrap() + .clone(); + + // config role + assert!(test_write_api(&core, token, "pki/roles/test", true, Some(role_data)).is_ok()); + let resp = test_read_api(&core, token, "pki/roles/test", true); + assert!(resp.as_ref().unwrap().is_some()); + let resp = resp.unwrap(); + assert!(resp.is_some()); + let data = resp.unwrap().data; + assert!(data.is_some()); + let role_data = data.unwrap(); + println!("role_data: {:?}", role_data); + assert_eq!(role_data["ttl"].as_u64().unwrap(), 60 * 24 * 60 * 60); + assert_eq!(role_data["max_ttl"].as_u64().unwrap(), 365 * 24 * 60 * 60); + assert_eq!(role_data["not_before_duration"].as_u64().unwrap(), 30); + assert_eq!(role_data["key_type"].as_str().unwrap(), "rsa"); + assert_eq!(role_data["key_bits"].as_u64().unwrap(), 4096); + assert_eq!(role_data["country"].as_str().unwrap(), "CN"); + assert_eq!(role_data["province"].as_str().unwrap(), "Beijing"); + assert_eq!(role_data["locality"].as_str().unwrap(), "Beijing"); + assert_eq!(role_data["organization"].as_str().unwrap(), "OpenAtom"); + assert_eq!(role_data["no_store"].as_bool().unwrap(), false); + } + + fn test_pki_generate_root(core: Arc>, token: &str, exported: bool, is_ok: bool) { + let core = core.read().unwrap(); + + let key_type = "rsa"; + let key_bits = 4096; + let common_name = "test-ca"; + let req_data = json!({ + "common_name": common_name, + "ttl": "365d", + "country": "cn", + "key_type": key_type, + "key_bits": key_bits, + }) + .as_object() + .unwrap() + .clone(); + // println!("generate root req_data: {:?}, is_ok: {}", req_data, is_ok); + let resp = test_write_api( + &core, + token, + format!("pki/root/generate/{}", if exported { "exported" } else { "internal" }).as_str(), + is_ok, + Some(req_data), + ); + if !is_ok { + return; + } + let resp_body = resp.unwrap(); + assert!(resp_body.is_some()); + let data = resp_body.unwrap().data; + assert!(data.is_some()); + let key_data = data.unwrap(); + // println!("generate root result: {:?}", key_data); + + let resp_ca_pem = test_read_api(&core, token, "pki/ca/pem", true); + let resp_ca_pem_cert_data = resp_ca_pem.unwrap().unwrap().data.unwrap(); + + let ca_cert = X509::from_pem(resp_ca_pem_cert_data["certificate"].as_str().unwrap().as_bytes()).unwrap(); + let subject = ca_cert.subject_name(); + let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap(); + assert_eq!(cn.data().as_slice(), common_name.as_bytes()); + + let not_after = Asn1Time::days_from_now(365).unwrap(); + let ttl_diff = ca_cert.not_after().diff(¬_after); + assert!(ttl_diff.is_ok()); + let ttl_diff = ttl_diff.unwrap(); + assert_eq!(ttl_diff.days, 0); + + if exported { + assert!(key_data["private_key_type"].as_str().is_some()); + assert_eq!(key_data["private_key_type"].as_str().unwrap(), key_type); + assert!(key_data["private_key"].as_str().is_some()); + let private_key_pem = key_data["private_key"].as_str().unwrap(); + match key_type { + "rsa" => { + let rsa_key = Rsa::private_key_from_pem(private_key_pem.as_bytes()); + assert!(rsa_key.is_ok()); + assert_eq!(rsa_key.unwrap().size() * 8, key_bits); + } + "ec" => { + let ec_key = EcKey::private_key_from_pem(private_key_pem.as_bytes()); + assert!(ec_key.is_ok()); + assert_eq!(ec_key.unwrap().group().degree(), key_bits); + } + _ => {} + } + } else { + assert!(key_data.get("private_key").is_none()); + } + } + + fn test_pki_issue_cert_by_generate_root(core: Arc>, token: &str) { + let core = core.read().unwrap(); + + let dns_sans = ["test.com", "a.test.com", "b.test.com"]; + let issue_data = json!({ + "ttl": "10d", + "common_name": "test.com", + "alt_names": "a.test.com,b.test.com", + }) + .as_object() + .unwrap() + .clone(); + + // issue cert + let resp = test_write_api(&core, token, "pki/issue/test", true, Some(issue_data)); + assert!(resp.is_ok()); + let resp_body = resp.unwrap(); + assert!(resp_body.is_some()); + let data = resp_body.unwrap().data; + assert!(data.is_some()); + let cert_data = data.unwrap(); + println!("issue cert result: {:?}", cert_data["certificate"]); + + let mut file = fs::File::create("/tmp/cert.crt").unwrap(); + file.write_all(cert_data["certificate"].as_str().unwrap().as_ref()).unwrap(); + + let cert = X509::from_pem(cert_data["certificate"].as_str().unwrap().as_bytes()).unwrap(); + let alt_names = cert.subject_alt_names(); + assert!(alt_names.is_some()); + let alt_names = alt_names.unwrap(); + assert_eq!(alt_names.len(), dns_sans.len()); + for alt_name in alt_names { + assert!(dns_sans.contains(&alt_name.dnsname().unwrap())); + } + assert_eq!(cert_data["private_key_type"].as_str().unwrap(), "rsa"); + let priv_key = PKey::private_key_from_pem(cert_data["private_key"].as_str().unwrap().as_bytes()).unwrap(); + assert_eq!(priv_key.bits(), 4096); + assert!(priv_key.public_eq(&cert.public_key().unwrap())); + let serial_number = cert.serial_number().to_bn().unwrap(); + let serial_number_hex = serial_number.to_hex_str().unwrap(); + assert_eq!( + cert_data["serial_number"].as_str().unwrap().replace(':', "").to_lowercase().as_str(), + serial_number_hex.to_lowercase().as_str() + ); + let expiration_time = Asn1Time::from_unix(cert_data["expiration"].as_i64().unwrap()).unwrap(); + let ttl_compare = cert.not_after().compare(&expiration_time); + assert!(ttl_compare.is_ok()); + assert_eq!(ttl_compare.unwrap(), std::cmp::Ordering::Equal); + let now_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); + let expiration_ttl = cert_data["expiration"].as_u64().unwrap(); + let ttl = expiration_ttl - now_timestamp; + let expect_ttl = 10 * 24 * 60 * 60; + assert!(ttl <= expect_ttl); + assert!((ttl + 10) > expect_ttl); + + let authority_key_id = cert.authority_key_id(); + assert!(authority_key_id.is_some()); + + println!("authority_key_id: {}", hex::encode(authority_key_id.unwrap().as_slice())); + + let resp_ca_pem = test_read_api(&core, token, "pki/ca/pem", true); + let resp_ca_pem_cert_data = resp_ca_pem.unwrap().unwrap().data.unwrap(); + + let ca_cert = X509::from_pem(resp_ca_pem_cert_data["certificate"].as_str().unwrap().as_bytes()).unwrap(); + let subject = ca_cert.subject_name(); + let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap(); + assert_eq!(cn.data().as_slice(), "test-ca".as_bytes()); + println!("ca subject_key_id: {}", hex::encode(ca_cert.subject_key_id().unwrap().as_slice())); + assert_eq!(ca_cert.subject_key_id().unwrap().as_slice(), authority_key_id.unwrap().as_slice()); + } + + #[test] + fn test_pki_module() { + let dir = env::temp_dir().join("rusty_vault_pki_module"); + assert!(fs::create_dir(&dir).is_ok()); + defer! ( + assert!(fs::remove_dir_all(&dir).is_ok()); + ); + + let mut root_token = String::new(); + println!("root_token: {:?}", root_token); + + let mut conf: HashMap = HashMap::new(); + conf.insert("path".to_string(), Value::String(dir.to_string_lossy().into_owned())); + + let backend = physical::new_backend("file", &conf).unwrap(); + let barrier = barrier_aes_gcm::AESGCMBarrier::new(Arc::clone(&backend)); + + let c = Arc::new(RwLock::new(Core { physical: backend, barrier: Arc::new(barrier), ..Default::default() })); + + { + let mut core = c.write().unwrap(); + assert!(core.config(Arc::clone(&c), None).is_ok()); + + let seal_config = SealConfig { secret_shares: 10, secret_threshold: 5 }; + + let result = core.init(&seal_config); + assert!(result.is_ok()); + let init_result = result.unwrap(); + println!("init_result: {:?}", init_result); + + let mut unsealed = false; + for i in 0..seal_config.secret_threshold { + let key = &init_result.secret_shares[i as usize]; + let unseal = core.unseal(key); + assert!(unseal.is_ok()); + unsealed = unseal.unwrap(); + } + + root_token = init_result.root_token; + + assert!(unsealed); + } + + { + println!("root_token: {:?}", root_token); + test_pki_config_ca(Arc::clone(&c), &root_token); + test_pki_generate_root(Arc::clone(&c), &root_token, false, true); + test_pki_config_role(Arc::clone(&c), &root_token); + test_pki_issue_cert_by_generate_root(Arc::clone(&c), &root_token); + } + } +} \ No newline at end of file From 7654bec9094ec9b13506c4e9d0ee727e0d527862 Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Mon, 3 Jun 2024 15:37:09 +0800 Subject: [PATCH 03/16] add `pki.rs` & fix module name `replay` to `relay` Signed-off-by: Qihang Cai --- Cargo.toml | 2 +- {replay => relay}/Cargo.toml | 3 +- relay/src/lib.rs | 1 + {replay => relay}/src/main.rs | 0 replay/src/lib.rs => relay/src/pki.rs | 212 +++++++++++++++++++++++++- 5 files changed, 215 insertions(+), 3 deletions(-) rename {replay => relay}/Cargo.toml (82%) create mode 100644 relay/src/lib.rs rename {replay => relay}/src/main.rs (100%) rename replay/src/lib.rs => relay/src/pki.rs (64%) diff --git a/Cargo.toml b/Cargo.toml index fc17c7785..cec3de321 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ "venus", "ceres", "libra", - "replay", + "relay", ] exclude = ["craft"] resolver = "1" diff --git a/replay/Cargo.toml b/relay/Cargo.toml similarity index 82% rename from replay/Cargo.toml rename to relay/Cargo.toml index 2362c70f5..a70de7641 100644 --- a/replay/Cargo.toml +++ b/relay/Cargo.toml @@ -8,4 +8,5 @@ rusty_vault = "0.1.0" serde_json = "1.0.117" go-defer = "0.1.0" openssl = "0.10.63" -hex = "0.4.3" \ No newline at end of file +hex = "0.4.3" +lazy_static = "1.4.0" \ No newline at end of file diff --git a/relay/src/lib.rs b/relay/src/lib.rs new file mode 100644 index 000000000..aa1c85506 --- /dev/null +++ b/relay/src/lib.rs @@ -0,0 +1 @@ +pub mod pki; \ No newline at end of file diff --git a/replay/src/main.rs b/relay/src/main.rs similarity index 100% rename from replay/src/main.rs rename to relay/src/main.rs diff --git a/replay/src/lib.rs b/relay/src/pki.rs similarity index 64% rename from replay/src/lib.rs rename to relay/src/pki.rs index 37480be85..0d483b83b 100644 --- a/replay/src/lib.rs +++ b/relay/src/pki.rs @@ -1,5 +1,215 @@ +use std::collections::HashMap; +use std::fs; +use std::io::Write; +use std::path::PathBuf; +use std::sync::{Arc, RwLock}; + +use lazy_static::lazy_static; +use rusty_vault::core::{Core, SealConfig}; +use rusty_vault::errors::RvError; +use rusty_vault::logical::{Operation, Request, Response}; +use rusty_vault::storage::{barrier_aes_gcm, physical}; +use serde_json::{json, Map, Value}; + +const ROLE: &str = "test"; + +lazy_static! { + static ref CA: CAInfo = { + let dir = PathBuf::from("/tmp/rusty_vault_pki_module"); + // let dir = env::temp_dir().join("rusty_vault_pki_module"); // TODO: 1. 能否复用文件 2. 改成数据库? + + if dir.exists() { + fs::remove_dir_all(&dir).unwrap(); + } + assert!(fs::create_dir(&dir).is_ok()); + let mut root_token = String::new(); + + let mut conf: HashMap = HashMap::new(); + conf.insert("path".to_string(), Value::String(dir.to_string_lossy().into_owned())); + + let backend = physical::new_backend("file", &conf).unwrap(); // file or database + let barrier = barrier_aes_gcm::AESGCMBarrier::new(Arc::clone(&backend)); + + let c = Arc::new(RwLock::new(Core { physical: backend, barrier: Arc::new(barrier), ..Default::default() })); + + { + let mut core = c.write().unwrap(); + assert!(core.config(Arc::clone(&c), None).is_ok()); + + let seal_config = SealConfig { secret_shares: 10, secret_threshold: 5 }; + + let result = core.init(&seal_config); + assert!(result.is_ok()); + let init_result = result.unwrap(); + println!("init_result: {:?}", init_result); + + let mut unsealed = false; + for i in 0..seal_config.secret_threshold { + let key = &init_result.secret_shares[i as usize]; + let unseal = core.unseal(key); + assert!(unseal.is_ok()); + unsealed = unseal.unwrap(); + } + + root_token = init_result.root_token; + println!("root_token: {:?}", root_token); + + assert!(unsealed); + } + + config_ca(Arc::clone(&c), &root_token); + generate_root(Arc::clone(&c), &root_token, false, true); + config_role(Arc::clone(&c), &root_token); + + CAInfo { core: c, token: root_token } + }; +} + +struct CAInfo { + core: Arc>, + token: String, +} +#[allow(dead_code)] +fn read_api(core: &Core, token: &str, path: &str, is_ok: bool) -> Result, RvError> { + let mut req = Request::new(path); + req.operation = Operation::Read; + req.client_token = token.to_string(); + let resp = core.handle_request(&mut req); + assert_eq!(resp.is_ok(), is_ok); + resp +} + +fn write_api( + core: &Core, + token: &str, + path: &str, + is_ok: bool, + data: Option>, +) -> Result, RvError> { + let mut req = Request::new(path); + req.operation = Operation::Write; + req.client_token = token.to_string(); + req.body = data; + + let resp = core.handle_request(&mut req); + println!("path: {}, req.body: {:?}", path, req.body); + assert_eq!(resp.is_ok(), is_ok); + resp +} + +fn config_ca(core: Arc>, token: &str) { + let core = core.read().unwrap(); + + // mount pki backend to path: pki/ + let mount_data = json!({ + "type": "pki", + }) + .as_object() + .unwrap() + .clone(); + + let resp = write_api(&core, token, "sys/mounts/pki/", true, Some(mount_data)); + assert!(resp.is_ok()); +} + +fn config_role(core: Arc>, token: &str) { + let core = core.read().unwrap(); + + let role_data = json!({ + "ttl": "60d", + "max_ttl": "365d", + "key_type": "rsa", + "key_bits": 4096, + "country": "CN", + "province": "Beijing", + "locality": "Beijing", + "organization": "OpenAtom", + "no_store": false, + }) + .as_object() + .unwrap() + .clone(); + + // config role + let result = write_api(&core, token, &format!("pki/roles/{}", ROLE), true, Some(role_data)); + assert!(result.is_ok()); +} + +/// generate root cert, so that you can read from `pki/ca/pem` +/// - if `exported` is true, then the response will contain `private key` +fn generate_root(core: Arc>, token: &str, exported: bool, is_ok: bool) { + let core = core.read().unwrap(); + + let key_type = "rsa"; + let key_bits = 4096; + let common_name = "test-ca"; + let req_data = json!({ + "common_name": common_name, + "ttl": "365d", + "country": "cn", + "key_type": key_type, + "key_bits": key_bits, + }) + .as_object() + .unwrap() + .clone(); + + let resp = write_api( + &core, + token, + format!("pki/root/generate/{}", if exported { "exported" } else { "internal" }).as_str(), + is_ok, + Some(req_data), + ); + assert!(resp.is_ok()); + // let resp_body = resp.unwrap(); + // let data = resp_body.unwrap().data; + // let key_data = data.unwrap(); + // println!("generate root result: {:?}", key_data); + + // let resp_ca_pem = read_api(&core, token, "pki/ca/pem", true); + // let resp_ca_pem_cert_data = resp_ca_pem.unwrap().unwrap().data.unwrap(); + // + // println!("resp_ca_pem_cert_data: {:?}", resp_ca_pem_cert_data); +} + +pub fn issue_cert(core: Arc>, token: &str) { + let core = core.read().unwrap(); + + // let dns_sans = ["test.com", "a.test.com", "b.test.com"]; + let issue_data = json!({ + "ttl": "10d", + "common_name": "test.com", + "alt_names": "a.test.com,b.test.com", + }) + .as_object() + .unwrap() + .clone(); + + // issue cert + let resp = write_api(&core, token, &format!("pki/issue/{}", ROLE), true, Some(issue_data)); + assert!(resp.is_ok()); + let resp_body = resp.unwrap(); + let cert_data = resp_body.unwrap().data.unwrap(); + println!("issue cert result: {:?}", cert_data["certificate"]); + + let mut file = fs::File::create("/tmp/cert.crt").unwrap(); + file.write_all(cert_data["certificate"].as_str().unwrap().as_ref()).unwrap(); +} + #[cfg(test)] mod tests { + use super::*; + + #[test] + fn test_pki() { + issue_cert(Arc::clone(&CA.core), &CA.token); + issue_cert(Arc::clone(&CA.core), &CA.token); + } +} + +#[cfg(test)] +mod tests_raw { use std::{ collections::HashMap, default::Default, @@ -295,4 +505,4 @@ mod tests { test_pki_issue_cert_by_generate_root(Arc::clone(&c), &root_token); } } -} \ No newline at end of file +} From 042026b03e67120f8b30a92ab9337179eb9a4e14 Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Mon, 3 Jun 2024 19:06:44 +0800 Subject: [PATCH 04/16] move `config_role` out of `init` & expose more parameters in API Signed-off-by: Qihang Cai --- relay/src/pki.rs | 91 ++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/relay/src/pki.rs b/relay/src/pki.rs index 0d483b83b..9be3b0aad 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -58,8 +58,7 @@ lazy_static! { } config_ca(Arc::clone(&c), &root_token); - generate_root(Arc::clone(&c), &root_token, false, true); - config_role(Arc::clone(&c), &root_token); + generate_root(Arc::clone(&c), &root_token, false); CAInfo { core: c, token: root_token } }; @@ -70,12 +69,12 @@ struct CAInfo { token: String, } #[allow(dead_code)] -fn read_api(core: &Core, token: &str, path: &str, is_ok: bool) -> Result, RvError> { +fn read_api(core: &Core, token: &str, path: &str) -> Result, RvError> { let mut req = Request::new(path); req.operation = Operation::Read; req.client_token = token.to_string(); let resp = core.handle_request(&mut req); - assert_eq!(resp.is_ok(), is_ok); + assert!(resp.is_ok()); resp } @@ -83,7 +82,6 @@ fn write_api( core: &Core, token: &str, path: &str, - is_ok: bool, data: Option>, ) -> Result, RvError> { let mut req = Request::new(path); @@ -93,7 +91,7 @@ fn write_api( let resp = core.handle_request(&mut req); println!("path: {}, req.body: {:?}", path, req.body); - assert_eq!(resp.is_ok(), is_ok); + assert!(resp.is_ok()); resp } @@ -102,47 +100,37 @@ fn config_ca(core: Arc>, token: &str) { // mount pki backend to path: pki/ let mount_data = json!({ - "type": "pki", - }) - .as_object() - .unwrap() - .clone(); + "type": "pki", + }) + .as_object() + .unwrap() + .clone(); - let resp = write_api(&core, token, "sys/mounts/pki/", true, Some(mount_data)); + let resp = write_api(&core, token, "sys/mounts/pki/", Some(mount_data)); assert!(resp.is_ok()); } -fn config_role(core: Arc>, token: &str) { +/// - `data`: see [RoleEntry](rusty_vault::modules::pki::path_roles) +fn config_role(core: Arc>, token: &str, data: Value) { let core = core.read().unwrap(); - let role_data = json!({ - "ttl": "60d", - "max_ttl": "365d", - "key_type": "rsa", - "key_bits": 4096, - "country": "CN", - "province": "Beijing", - "locality": "Beijing", - "organization": "OpenAtom", - "no_store": false, - }) - .as_object() - .unwrap() + let role_data = data.as_object() + .expect("`data` must be a JSON object") .clone(); // config role - let result = write_api(&core, token, &format!("pki/roles/{}", ROLE), true, Some(role_data)); + let result = write_api(&core, token, &format!("pki/roles/{}", ROLE), Some(role_data)); assert!(result.is_ok()); } /// generate root cert, so that you can read from `pki/ca/pem` /// - if `exported` is true, then the response will contain `private key` -fn generate_root(core: Arc>, token: &str, exported: bool, is_ok: bool) { +fn generate_root(core: Arc>, token: &str, exported: bool) { let core = core.read().unwrap(); let key_type = "rsa"; let key_bits = 4096; - let common_name = "test-ca"; + let common_name = "mega-ca"; let req_data = json!({ "common_name": common_name, "ttl": "365d", @@ -158,7 +146,6 @@ fn generate_root(core: Arc>, token: &str, exported: bool, is_ok: bo &core, token, format!("pki/root/generate/{}", if exported { "exported" } else { "internal" }).as_str(), - is_ok, Some(req_data), ); assert!(resp.is_ok()); @@ -167,34 +154,33 @@ fn generate_root(core: Arc>, token: &str, exported: bool, is_ok: bo // let key_data = data.unwrap(); // println!("generate root result: {:?}", key_data); - // let resp_ca_pem = read_api(&core, token, "pki/ca/pem", true); + // let resp_ca_pem = read_api(&core, token, "pki/ca/pem"); // let resp_ca_pem_cert_data = resp_ca_pem.unwrap().unwrap().data.unwrap(); // // println!("resp_ca_pem_cert_data: {:?}", resp_ca_pem_cert_data); } -pub fn issue_cert(core: Arc>, token: &str) { +/// - `data`: see [issue_path](rusty_vault::modules::pki::path_issue) +pub fn issue_cert(core: Arc>, token: &str, data: Value) { let core = core.read().unwrap(); // let dns_sans = ["test.com", "a.test.com", "b.test.com"]; - let issue_data = json!({ - "ttl": "10d", - "common_name": "test.com", - "alt_names": "a.test.com,b.test.com", - }) - .as_object() - .unwrap() + let issue_data = data.as_object() + .expect("`data` must be a JSON object") .clone(); // issue cert - let resp = write_api(&core, token, &format!("pki/issue/{}", ROLE), true, Some(issue_data)); + let resp = write_api(&core, token, &format!("pki/issue/{}", ROLE), Some(issue_data)); assert!(resp.is_ok()); let resp_body = resp.unwrap(); let cert_data = resp_body.unwrap().data.unwrap(); println!("issue cert result: {:?}", cert_data["certificate"]); - let mut file = fs::File::create("/tmp/cert.crt").unwrap(); - file.write_all(cert_data["certificate"].as_str().unwrap().as_ref()).unwrap(); + #[cfg(test)] + { + let mut file = fs::File::create("/tmp/cert.crt").unwrap(); // TODO add root cert in it + file.write_all(cert_data["certificate"].as_str().unwrap().as_ref()).unwrap(); + } } #[cfg(test)] @@ -202,9 +188,24 @@ mod tests { use super::*; #[test] - fn test_pki() { - issue_cert(Arc::clone(&CA.core), &CA.token); - issue_cert(Arc::clone(&CA.core), &CA.token); + fn test_pki_issue() { + config_role(Arc::clone(&CA.core), &CA.token, json!({ + "ttl": "60d", + "max_ttl": "365d", + "key_type": "rsa", + "key_bits": 4096, + "country": "CN", + "province": "Beijing", + "locality": "Beijing", + "organization": "OpenAtom-Mega", + "no_store": false, + })); + + issue_cert(Arc::clone(&CA.core), &CA.token, json!({ + "ttl": "10d", + "common_name": "test.com", + "alt_names": "a.test.com,b.test.com", + })); } } From 7e7360e2f6480a8f00a67e7d9c3314ab7ae3cc9a Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Wed, 5 Jun 2024 15:34:56 +0800 Subject: [PATCH 05/16] add `verify_cert` Signed-off-by: Qihang Cai --- relay/src/pki.rs | 54 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/relay/src/pki.rs b/relay/src/pki.rs index 9be3b0aad..93e0d2574 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -1,10 +1,14 @@ +use std::cmp::Ordering; use std::collections::HashMap; use std::fs; use std::io::Write; use std::path::PathBuf; use std::sync::{Arc, RwLock}; +use std::time::{SystemTime, UNIX_EPOCH}; use lazy_static::lazy_static; +use openssl::asn1::Asn1Time; +use openssl::x509::X509; use rusty_vault::core::{Core, SealConfig}; use rusty_vault::errors::RvError; use rusty_vault::logical::{Operation, Request, Response}; @@ -68,13 +72,11 @@ struct CAInfo { core: Arc>, token: String, } -#[allow(dead_code)] fn read_api(core: &Core, token: &str, path: &str) -> Result, RvError> { let mut req = Request::new(path); req.operation = Operation::Read; req.client_token = token.to_string(); let resp = core.handle_request(&mut req); - assert!(resp.is_ok()); resp } @@ -91,7 +93,6 @@ fn write_api( let resp = core.handle_request(&mut req); println!("path: {}, req.body: {:?}", path, req.body); - assert!(resp.is_ok()); resp } @@ -111,8 +112,9 @@ fn config_ca(core: Arc>, token: &str) { } /// - `data`: see [RoleEntry](rusty_vault::modules::pki::path_roles) -fn config_role(core: Arc>, token: &str, data: Value) { - let core = core.read().unwrap(); +fn config_role(data: Value) { + let core = CA.core.read().unwrap(); + let token = &CA.token; let role_data = data.as_object() .expect("`data` must be a JSON object") @@ -161,8 +163,9 @@ fn generate_root(core: Arc>, token: &str, exported: bool) { } /// - `data`: see [issue_path](rusty_vault::modules::pki::path_issue) -pub fn issue_cert(core: Arc>, token: &str, data: Value) { - let core = core.read().unwrap(); +pub fn issue_cert(data: Value) -> String { + let core = CA.core.read().unwrap(); + let token = &CA.token; // let dns_sans = ["test.com", "a.test.com", "b.test.com"]; let issue_data = data.as_object() @@ -181,6 +184,37 @@ pub fn issue_cert(core: Arc>, token: &str, data: Value) { let mut file = fs::File::create("/tmp/cert.crt").unwrap(); // TODO add root cert in it file.write_all(cert_data["certificate"].as_str().unwrap().as_ref()).unwrap(); } + + cert_data["certificate"].as_str().unwrap().to_owned() +} + +pub fn verify_cert(cert_pem: &[u8]) -> bool { + let ca_cert = { + let core = CA.core.read().unwrap(); + + let resp_ca_pem = read_api(&core, &CA.token, "pki/ca/pem").unwrap().unwrap(); + let ca_cert = resp_ca_pem.data.unwrap(); + let ca_cert_pem = ca_cert["certificate"].as_str().unwrap(); + X509::from_pem(ca_cert_pem.as_ref()).unwrap() + }; + + let cert = X509::from_pem(cert_pem).unwrap(); + // verify time + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64; + let now = Asn1Time::from_unix(now).unwrap(); + let not_before = cert.not_before(); + let not_after = cert.not_after(); + match now.compare(not_before) { + Ok(Ordering::Less) | Err(_) => return false, + _ => {} + } + match now.compare(not_after) { + Ok(Ordering::Greater) | Err(_) => return false, + _ => {} + } + + // verify signature + cert.verify(&ca_cert.public_key().unwrap()).unwrap() } #[cfg(test)] @@ -189,7 +223,7 @@ mod tests { #[test] fn test_pki_issue() { - config_role(Arc::clone(&CA.core), &CA.token, json!({ + config_role(json!({ "ttl": "60d", "max_ttl": "365d", "key_type": "rsa", @@ -201,11 +235,13 @@ mod tests { "no_store": false, })); - issue_cert(Arc::clone(&CA.core), &CA.token, json!({ + let cert_pem = issue_cert(json!({ "ttl": "10d", "common_name": "test.com", "alt_names": "a.test.com,b.test.com", })); + + assert!(verify_cert(cert_pem.as_ref())); } } From 4c9c70fbfa6308b1fcda2427e071f8d4116fd708 Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Wed, 5 Jun 2024 17:03:31 +0800 Subject: [PATCH 06/16] add secret-access test Signed-off-by: Qihang Cai --- relay/src/pki.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/relay/src/pki.rs b/relay/src/pki.rs index 93e0d2574..2abc4e3d9 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -243,6 +243,29 @@ mod tests { assert!(verify_cert(cert_pem.as_ref())); } + + #[test] + fn test_secret() { + let core = CA.core.read().unwrap(); + + // create secret + let kv_data = json!({ + "foo": "bar", + "id": "xxxID", + }) + .as_object() + .unwrap() + .clone(); + write_api(&core, &CA.token, "secret/goo", Some(kv_data.clone())).unwrap(); + + // get secret + let secret = read_api(&core, &CA.token, "secret/goo").unwrap().unwrap().data; + assert_eq!(secret, Some(kv_data)); + println!("secret: {:?}", secret.unwrap()); + + assert!(read_api(&core, &CA.token, "secret/foo").unwrap().is_none()); + assert!(read_api(&core, &CA.token, "secret1/foo").is_err()); + } } #[cfg(test)] From 23262f91e17ee1ce9c981a5d566bd1c5d6e871f3 Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Thu, 13 Jun 2024 18:01:07 +0800 Subject: [PATCH 07/16] Save `secret_shares` & `root_token` to unseal `Core` for reusing existed files Signed-off-by: Qihang Cai --- relay/Cargo.toml | 5 ++- relay/src/pki.rs | 110 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 90 insertions(+), 25 deletions(-) diff --git a/relay/Cargo.toml b/relay/Cargo.toml index a70de7641..61d0651ce 100644 --- a/relay/Cargo.toml +++ b/relay/Cargo.toml @@ -9,4 +9,7 @@ serde_json = "1.0.117" go-defer = "0.1.0" openssl = "0.10.63" hex = "0.4.3" -lazy_static = "1.4.0" \ No newline at end of file +lazy_static = "1.4.0" +secp256k1 = { version = "0.27.0", features = ["serde", "bitcoin-hashes", "bitcoin-hashes-std", "rand"] } +libp2p = { version = "0.53.0", features = ["secp256k1"] } +serde = { version = "1.0.117", features = ["derive"] } \ No newline at end of file diff --git a/relay/src/pki.rs b/relay/src/pki.rs index 2abc4e3d9..77747fa21 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use std::collections::HashMap; use std::fs; -use std::io::Write; +use std::io::{Read, Write}; use std::path::PathBuf; use std::sync::{Arc, RwLock}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -15,57 +15,98 @@ use rusty_vault::logical::{Operation, Request, Response}; use rusty_vault::storage::{barrier_aes_gcm, physical}; use serde_json::{json, Map, Value}; +use secp256k1::{rand, SecretKey}; +use libp2p::identity::PeerId; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +struct CoreKey { + secret_shares: Vec>, + root_token: String, +} + const ROLE: &str = "test"; +// coding in `lazy_static!` with copilot seems lagging, so using function `init` instead lazy_static! { - static ref CA: CAInfo = { - let dir = PathBuf::from("/tmp/rusty_vault_pki_module"); - // let dir = env::temp_dir().join("rusty_vault_pki_module"); // TODO: 1. 能否复用文件 2. 改成数据库? + static ref CA: CAInfo = init(); +} - if dir.exists() { - fs::remove_dir_all(&dir).unwrap(); - } +/// Initialize the CA +fn init() -> CAInfo { + const CORE_KEY_FILE: &str = "core_key.json"; + let dir = PathBuf::from("/tmp/rusty_vault_pki_module"); + let core_key_path = dir.join(CORE_KEY_FILE); + // let dir = env::temp_dir().join("rusty_vault_pki_module"); // TODO: 改成数据库? + + let inited = dir.exists(); + if !inited { assert!(fs::create_dir(&dir).is_ok()); - let mut root_token = String::new(); + } - let mut conf: HashMap = HashMap::new(); - conf.insert("path".to_string(), Value::String(dir.to_string_lossy().into_owned())); + let mut root_token = String::new(); - let backend = physical::new_backend("file", &conf).unwrap(); // file or database - let barrier = barrier_aes_gcm::AESGCMBarrier::new(Arc::clone(&backend)); + let mut conf: HashMap = HashMap::new(); + conf.insert("path".to_string(), Value::String(dir.to_string_lossy().into_owned())); - let c = Arc::new(RwLock::new(Core { physical: backend, barrier: Arc::new(barrier), ..Default::default() })); + let backend = physical::new_backend("file", &conf).unwrap(); // file or database + let barrier = barrier_aes_gcm::AESGCMBarrier::new(Arc::clone(&backend)); - { - let mut core = c.write().unwrap(); - assert!(core.config(Arc::clone(&c), None).is_ok()); + let c = Arc::new(RwLock::new(Core { physical: backend, barrier: Arc::new(barrier), ..Default::default() })); - let seal_config = SealConfig { secret_shares: 10, secret_threshold: 5 }; + { + let mut core = c.write().unwrap(); + assert!(core.config(Arc::clone(&c), None).is_ok()); + let seal_config = SealConfig { secret_shares: 10, secret_threshold: 5 }; + + let mut unsealed = false; + if !inited { let result = core.init(&seal_config); assert!(result.is_ok()); let init_result = result.unwrap(); println!("init_result: {:?}", init_result); - let mut unsealed = false; for i in 0..seal_config.secret_threshold { let key = &init_result.secret_shares[i as usize]; let unseal = core.unseal(key); + println!("unseal: {:?}", unseal); assert!(unseal.is_ok()); unsealed = unseal.unwrap(); } root_token = init_result.root_token; - println!("root_token: {:?}", root_token); - assert!(unsealed); + let core_key = CoreKey { + secret_shares: Vec::from(&init_result.secret_shares[..]), + root_token: root_token.clone(), + }; + let file = fs::File::create(core_key_path).unwrap(); + serde_json::to_writer_pretty(file, &core_key).unwrap(); + } else { + let file = fs::File::open(core_key_path).unwrap(); + let core_key: CoreKey = serde_json::from_reader(file).unwrap(); + root_token = core_key.root_token.clone(); + + for i in 0..seal_config.secret_threshold { + let key = &core_key.secret_shares[i as usize]; + let unseal = core.unseal(&key); + println!("unseal: {:?}", unseal); + assert!(unseal.is_ok()); + unsealed = unseal.unwrap(); + } } + assert!(unsealed); + println!("root_token: {:?}", root_token); + } + + if !inited { config_ca(Arc::clone(&c), &root_token); generate_root(Arc::clone(&c), &root_token, false); + } - CAInfo { core: c, token: root_token } - }; + CAInfo { core: c, token: root_token } } struct CAInfo { @@ -219,6 +260,8 @@ pub fn verify_cert(cert_pem: &[u8]) -> bool { #[cfg(test)] mod tests { + use libp2p::identity; + use secp256k1::Secp256k1; use super::*; #[test] @@ -237,8 +280,8 @@ mod tests { let cert_pem = issue_cert(json!({ "ttl": "10d", - "common_name": "test.com", - "alt_names": "a.test.com,b.test.com", + "common_name": "16Uiu2HAmCMrtR11EPbekyX99VCuSiMsjgA1teXAVB1FdJjddKXTC", //nostr id + // "alt_names": "a.test.com,b.test.com", })); assert!(verify_cert(cert_pem.as_ref())); @@ -266,6 +309,25 @@ mod tests { assert!(read_api(&core, &CA.token, "secret/foo").unwrap().is_none()); assert!(read_api(&core, &CA.token, "secret1/foo").is_err()); } + + #[test] + fn test_nostr() { + // let secp = Secp256k1::new(); + let secret_key = SecretKey::new(&mut rand::thread_rng()); + // println!("secret_key: {}", secret_key.display_secret()); + // println!("public_key: {:?}", secret_key.public_key(&secp)); + + // let secret_key = identity::secp256k1::SecretKey::generate(); + // println!("{:?}", secret_key.to_bytes()); + + let libp2p_sk = identity::secp256k1::SecretKey::try_from_bytes(secret_key.secret_bytes()).unwrap(); + let secp256k1_kp = identity::secp256k1::Keypair::from(libp2p_sk.clone()); + let local_key = identity::Keypair::from(secp256k1_kp.clone()); // Just encapsulate + let local_peer_id = PeerId::from(local_key.public()); + println!("peer_id: {:?}", local_peer_id); + // println!("{:?}", hex::encode(secret_key.public_key(&secp).serialize())); + } + } #[cfg(test)] From a0973b5c79bff69078cf4858000c615f0258309e Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Fri, 14 Jun 2024 00:10:30 +0800 Subject: [PATCH 08/16] move core_init to `vault.rs` & generate Nostr ID by `bs58` instead of `libp2p` Signed-off-by: Qihang Cai --- relay/Cargo.toml | 2 +- relay/src/lib.rs | 3 +- relay/src/pki.rs | 121 ++++++--------------------------------------- relay/src/vault.rs | 95 +++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 109 deletions(-) create mode 100644 relay/src/vault.rs diff --git a/relay/Cargo.toml b/relay/Cargo.toml index 61d0651ce..12f11b8ee 100644 --- a/relay/Cargo.toml +++ b/relay/Cargo.toml @@ -11,5 +11,5 @@ openssl = "0.10.63" hex = "0.4.3" lazy_static = "1.4.0" secp256k1 = { version = "0.27.0", features = ["serde", "bitcoin-hashes", "bitcoin-hashes-std", "rand"] } -libp2p = { version = "0.53.0", features = ["secp256k1"] } +bs58 = "0.5.1" serde = { version = "1.0.117", features = ["derive"] } \ No newline at end of file diff --git a/relay/src/lib.rs b/relay/src/lib.rs index aa1c85506..f50a098b7 100644 --- a/relay/src/lib.rs +++ b/relay/src/lib.rs @@ -1 +1,2 @@ -pub mod pki; \ No newline at end of file +pub mod pki; +pub mod vault; \ No newline at end of file diff --git a/relay/src/pki.rs b/relay/src/pki.rs index 77747fa21..0eb016b4f 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -1,118 +1,35 @@ use std::cmp::Ordering; -use std::collections::HashMap; use std::fs; -use std::io::{Read, Write}; -use std::path::PathBuf; +use std::io::Write; use std::sync::{Arc, RwLock}; use std::time::{SystemTime, UNIX_EPOCH}; use lazy_static::lazy_static; use openssl::asn1::Asn1Time; use openssl::x509::X509; -use rusty_vault::core::{Core, SealConfig}; +use rusty_vault::core::Core; use rusty_vault::errors::RvError; use rusty_vault::logical::{Operation, Request, Response}; -use rusty_vault::storage::{barrier_aes_gcm, physical}; use serde_json::{json, Map, Value}; use secp256k1::{rand, SecretKey}; -use libp2p::identity::PeerId; -use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug)] -struct CoreKey { - secret_shares: Vec>, - root_token: String, -} +use super::vault::{CoreInfo, CORE}; const ROLE: &str = "test"; -// coding in `lazy_static!` with copilot seems lagging, so using function `init` instead lazy_static! { - static ref CA: CAInfo = init(); -} - -/// Initialize the CA -fn init() -> CAInfo { - const CORE_KEY_FILE: &str = "core_key.json"; - let dir = PathBuf::from("/tmp/rusty_vault_pki_module"); - let core_key_path = dir.join(CORE_KEY_FILE); - // let dir = env::temp_dir().join("rusty_vault_pki_module"); // TODO: 改成数据库? - - let inited = dir.exists(); - if !inited { - assert!(fs::create_dir(&dir).is_ok()); - } - - let mut root_token = String::new(); - - let mut conf: HashMap = HashMap::new(); - conf.insert("path".to_string(), Value::String(dir.to_string_lossy().into_owned())); - - let backend = physical::new_backend("file", &conf).unwrap(); // file or database - let barrier = barrier_aes_gcm::AESGCMBarrier::new(Arc::clone(&backend)); - - let c = Arc::new(RwLock::new(Core { physical: backend, barrier: Arc::new(barrier), ..Default::default() })); - - { - let mut core = c.write().unwrap(); - assert!(core.config(Arc::clone(&c), None).is_ok()); - - let seal_config = SealConfig { secret_shares: 10, secret_threshold: 5 }; - - let mut unsealed = false; - if !inited { - let result = core.init(&seal_config); - assert!(result.is_ok()); - let init_result = result.unwrap(); - println!("init_result: {:?}", init_result); - - for i in 0..seal_config.secret_threshold { - let key = &init_result.secret_shares[i as usize]; - let unseal = core.unseal(key); - println!("unseal: {:?}", unseal); - assert!(unseal.is_ok()); - unsealed = unseal.unwrap(); - } - - root_token = init_result.root_token; - - let core_key = CoreKey { - secret_shares: Vec::from(&init_result.secret_shares[..]), - root_token: root_token.clone(), - }; - let file = fs::File::create(core_key_path).unwrap(); - serde_json::to_writer_pretty(file, &core_key).unwrap(); - } else { - let file = fs::File::open(core_key_path).unwrap(); - let core_key: CoreKey = serde_json::from_reader(file).unwrap(); - root_token = core_key.root_token.clone(); - - for i in 0..seal_config.secret_threshold { - let key = &core_key.secret_shares[i as usize]; - let unseal = core.unseal(&key); - println!("unseal: {:?}", unseal); - assert!(unseal.is_ok()); - unsealed = unseal.unwrap(); - } + static ref CA: CoreInfo = { + let c = CORE.clone(); + // init CA if not + if let Err(_) = read_api(&c.core.read().unwrap(), &c.token, "pki/ca/pem") { + config_ca(c.core.clone(), &c.token); + generate_root(c.core.clone(), &c.token, false); } - - assert!(unsealed); - println!("root_token: {:?}", root_token); - } - - if !inited { - config_ca(Arc::clone(&c), &root_token); - generate_root(Arc::clone(&c), &root_token, false); - } - - CAInfo { core: c, token: root_token } + c + }; } -struct CAInfo { - core: Arc>, - token: String, -} fn read_api(core: &Core, token: &str, path: &str) -> Result, RvError> { let mut req = Request::new(path); req.operation = Operation::Read; @@ -260,7 +177,6 @@ pub fn verify_cert(cert_pem: &[u8]) -> bool { #[cfg(test)] mod tests { - use libp2p::identity; use secp256k1::Secp256k1; use super::*; @@ -312,20 +228,11 @@ mod tests { #[test] fn test_nostr() { - // let secp = Secp256k1::new(); + let secp = Secp256k1::new(); let secret_key = SecretKey::new(&mut rand::thread_rng()); + let public_key = secret_key.public_key(&secp); // println!("secret_key: {}", secret_key.display_secret()); - // println!("public_key: {:?}", secret_key.public_key(&secp)); - - // let secret_key = identity::secp256k1::SecretKey::generate(); - // println!("{:?}", secret_key.to_bytes()); - - let libp2p_sk = identity::secp256k1::SecretKey::try_from_bytes(secret_key.secret_bytes()).unwrap(); - let secp256k1_kp = identity::secp256k1::Keypair::from(libp2p_sk.clone()); - let local_key = identity::Keypair::from(secp256k1_kp.clone()); // Just encapsulate - let local_peer_id = PeerId::from(local_key.public()); - println!("peer_id: {:?}", local_peer_id); - // println!("{:?}", hex::encode(secret_key.public_key(&secp).serialize())); + println!("{}", bs58::encode(public_key.serialize()).into_string()); } } diff --git a/relay/src/vault.rs b/relay/src/vault.rs new file mode 100644 index 000000000..37f0a4870 --- /dev/null +++ b/relay/src/vault.rs @@ -0,0 +1,95 @@ +use std::collections::HashMap; +use std::fs; +use std::path::PathBuf; +use std::sync::{Arc, RwLock}; +use lazy_static::lazy_static; +use rusty_vault::core::{Core, SealConfig}; +use rusty_vault::storage::{barrier_aes_gcm, physical}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +#[derive(Serialize, Deserialize, Debug)] +struct CoreKey { + secret_shares: Vec>, + root_token: String, +} + +#[derive(Clone)] +pub struct CoreInfo { + pub core: Arc>, + pub token: String, +} + +// coding in `lazy_static!` with copilot seems lagging, so using function `init` instead +lazy_static! { + pub static ref CORE: CoreInfo = init(); +} + +/// Initialize the vault core +fn init() -> CoreInfo { + const CORE_KEY_FILE: &str = "core_key.json"; + let dir = PathBuf::from("/tmp/rusty_vault_pki_module"); + let core_key_path = dir.join(CORE_KEY_FILE); + // let dir = env::temp_dir().join("rusty_vault_pki_module"); // TODO: 改成数据库? + + let inited = dir.exists(); + if !inited { + assert!(fs::create_dir(&dir).is_ok()); + } + + let mut conf: HashMap = HashMap::new(); + conf.insert("path".to_string(), Value::String(dir.to_string_lossy().into_owned())); + + let backend = physical::new_backend("file", &conf).unwrap(); // file or database + let barrier = barrier_aes_gcm::AESGCMBarrier::new(Arc::clone(&backend)); + + let c = Arc::new(RwLock::new(Core { physical: backend, barrier: Arc::new(barrier), ..Default::default() })); + + let root_token; + { + let mut core = c.write().unwrap(); + assert!(core.config(Arc::clone(&c), None).is_ok()); + + let seal_config = SealConfig { secret_shares: 10, secret_threshold: 5 }; + + let mut unsealed = false; + if !inited { + let result = core.init(&seal_config); + assert!(result.is_ok()); + let init_result = result.unwrap(); + println!("init_result: {:?}", init_result); + + for i in 0..seal_config.secret_threshold { + let key = &init_result.secret_shares[i as usize]; + let unseal = core.unseal(key); + assert!(unseal.is_ok()); + unsealed = unseal.unwrap(); + } + + root_token = init_result.root_token; + + let core_key = CoreKey { + secret_shares: Vec::from(&init_result.secret_shares[..]), + root_token: root_token.clone(), + }; + let file = fs::File::create(core_key_path).unwrap(); + serde_json::to_writer_pretty(file, &core_key).unwrap(); + } else { + let file = fs::File::open(core_key_path).unwrap(); + let core_key: CoreKey = serde_json::from_reader(file).unwrap(); + root_token = core_key.root_token.clone(); + + for i in 0..seal_config.secret_threshold { + let key = &core_key.secret_shares[i as usize]; + let unseal = core.unseal(&key); + assert!(unseal.is_ok()); + unsealed = unseal.unwrap(); + } + } + + assert!(unsealed); + println!("root_token: {:?}", root_token); + } + + CoreInfo { core: c, token: root_token } +} \ No newline at end of file From 6991ab9dcfcc5fef86a1c4330016723cbdad8d2c Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Fri, 14 Jun 2024 16:12:32 +0800 Subject: [PATCH 09/16] move `secret access` to `vault.rs` Signed-off-by: Qihang Cai --- relay/src/pki.rs | 57 +++-------------------------------------- relay/src/vault.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 54 deletions(-) diff --git a/relay/src/pki.rs b/relay/src/pki.rs index 0eb016b4f..2d36af705 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -8,13 +8,11 @@ use lazy_static::lazy_static; use openssl::asn1::Asn1Time; use openssl::x509::X509; use rusty_vault::core::Core; -use rusty_vault::errors::RvError; -use rusty_vault::logical::{Operation, Request, Response}; -use serde_json::{json, Map, Value}; +use serde_json::{json, Value}; use secp256k1::{rand, SecretKey}; -use super::vault::{CoreInfo, CORE}; +use super::vault::{CoreInfo, CORE, read_api, write_api}; const ROLE: &str = "test"; @@ -30,30 +28,6 @@ lazy_static! { }; } -fn read_api(core: &Core, token: &str, path: &str) -> Result, RvError> { - let mut req = Request::new(path); - req.operation = Operation::Read; - req.client_token = token.to_string(); - let resp = core.handle_request(&mut req); - resp -} - -fn write_api( - core: &Core, - token: &str, - path: &str, - data: Option>, -) -> Result, RvError> { - let mut req = Request::new(path); - req.operation = Operation::Write; - req.client_token = token.to_string(); - req.body = data; - - let resp = core.handle_request(&mut req); - println!("path: {}, req.body: {:?}", path, req.body); - resp -} - fn config_ca(core: Arc>, token: &str) { let core = core.read().unwrap(); @@ -182,7 +156,7 @@ mod tests { #[test] fn test_pki_issue() { - config_role(json!({ + config_role(json!({ // TODO move to `init` "ttl": "60d", "max_ttl": "365d", "key_type": "rsa", @@ -196,36 +170,13 @@ mod tests { let cert_pem = issue_cert(json!({ "ttl": "10d", - "common_name": "16Uiu2HAmCMrtR11EPbekyX99VCuSiMsjgA1teXAVB1FdJjddKXTC", //nostr id + "common_name": "oqpXWgEhXa1WDqMWBnpUW4jvrxGqJKVuJATy4MSPdKNS", //nostr id // "alt_names": "a.test.com,b.test.com", })); assert!(verify_cert(cert_pem.as_ref())); } - #[test] - fn test_secret() { - let core = CA.core.read().unwrap(); - - // create secret - let kv_data = json!({ - "foo": "bar", - "id": "xxxID", - }) - .as_object() - .unwrap() - .clone(); - write_api(&core, &CA.token, "secret/goo", Some(kv_data.clone())).unwrap(); - - // get secret - let secret = read_api(&core, &CA.token, "secret/goo").unwrap().unwrap().data; - assert_eq!(secret, Some(kv_data)); - println!("secret: {:?}", secret.unwrap()); - - assert!(read_api(&core, &CA.token, "secret/foo").unwrap().is_none()); - assert!(read_api(&core, &CA.token, "secret1/foo").is_err()); - } - #[test] fn test_nostr() { let secp = Secp256k1::new(); diff --git a/relay/src/vault.rs b/relay/src/vault.rs index 37f0a4870..b111aac3d 100644 --- a/relay/src/vault.rs +++ b/relay/src/vault.rs @@ -4,9 +4,11 @@ use std::path::PathBuf; use std::sync::{Arc, RwLock}; use lazy_static::lazy_static; use rusty_vault::core::{Core, SealConfig}; +use rusty_vault::errors::RvError; +use rusty_vault::logical::{Operation, Request, Response}; use rusty_vault::storage::{barrier_aes_gcm, physical}; use serde::{Deserialize, Serialize}; -use serde_json::Value; +use serde_json::{Map, Value}; #[derive(Serialize, Deserialize, Debug)] struct CoreKey { @@ -92,4 +94,64 @@ fn init() -> CoreInfo { } CoreInfo { core: c, token: root_token } +} + +pub fn read_api(core: &Core, token: &str, path: &str) -> Result, RvError> { + let mut req = Request::new(path); + req.operation = Operation::Read; + req.client_token = token.to_string(); + let resp = core.handle_request(&mut req); + resp +} + +pub fn write_api( + core: &Core, + token: &str, + path: &str, + data: Option>, +) -> Result, RvError> { + let mut req = Request::new(path); + req.operation = Operation::Write; + req.client_token = token.to_string(); + req.body = data; + + let resp = core.handle_request(&mut req); + println!("path: {}, req.body: {:?}", path, req.body); + resp +} + +/// Write a secret to the vault (k-v) +pub fn write_secret(name: &str, data: Option>) -> Result, RvError> { + write_api(&CORE.core.read().unwrap(), &CORE.token, &format!("secret/{}", name), data) +} + +/// Read a secret from the vault (k-v) +pub fn read_secret(name: &str) -> Result, RvError> { + read_api(&CORE.core.read().unwrap(), &CORE.token, &format!("secret/{}", name)) +} + +#[cfg(test)] +mod tests { + use serde_json::json; + use super::*; + + #[test] + fn test_secret() { + // create secret + let kv_data = json!({ + "foo": "bar", + "id": "oqpXWgEhXa1WDqMWBnpUW4jvrxGqJKVuJATy4MSPdKNS", + }) + .as_object() + .unwrap() + .clone(); + write_secret("keyInfo", Some(kv_data.clone())).unwrap(); + + let secret = read_secret("keyInfo").unwrap().unwrap().data; + assert_eq!(secret, Some(kv_data)); + println!("secret: {:?}", secret.unwrap()); + + assert!(read_secret("foo").unwrap().is_none()); + assert!(read_api(&CORE.core.read().unwrap(), &CORE.token, "secret1/foo").is_err()); + } } \ No newline at end of file From cd4fcf04fe78270af3b7efba6323b2810f112bc7 Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Mon, 17 Jun 2024 15:49:54 +0800 Subject: [PATCH 10/16] move `nostr gen` to `nostr.rs` Signed-off-by: Qihang Cai --- relay/src/lib.rs | 3 ++- relay/src/nostr.rs | 22 ++++++++++++++++++++++ relay/src/pki.rs | 13 ------------- 3 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 relay/src/nostr.rs diff --git a/relay/src/lib.rs b/relay/src/lib.rs index f50a098b7..67a346a27 100644 --- a/relay/src/lib.rs +++ b/relay/src/lib.rs @@ -1,2 +1,3 @@ pub mod pki; -pub mod vault; \ No newline at end of file +pub mod vault; +pub mod nostr; \ No newline at end of file diff --git a/relay/src/nostr.rs b/relay/src/nostr.rs new file mode 100644 index 000000000..f8ccab5ad --- /dev/null +++ b/relay/src/nostr.rs @@ -0,0 +1,22 @@ +use secp256k1::{PublicKey, rand, Secp256k1, SecretKey}; + +pub fn generate_nostr_id() -> (String, (SecretKey, PublicKey)) { + let secp = Secp256k1::new(); + let secret_key = SecretKey::new(&mut rand::thread_rng()); + let public_key = secret_key.public_key(&secp); + let nostr = bs58::encode(public_key.serialize()).into_string(); + + (nostr, (secret_key, public_key)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_generate_nostr_id() { + let (nostr, keypair) = generate_nostr_id(); + println!("nostr: {:?}", nostr); + println!("keypair: {:?}", keypair); + } +} \ No newline at end of file diff --git a/relay/src/pki.rs b/relay/src/pki.rs index 2d36af705..683453a36 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -10,8 +10,6 @@ use openssl::x509::X509; use rusty_vault::core::Core; use serde_json::{json, Value}; -use secp256k1::{rand, SecretKey}; - use super::vault::{CoreInfo, CORE, read_api, write_api}; const ROLE: &str = "test"; @@ -151,7 +149,6 @@ pub fn verify_cert(cert_pem: &[u8]) -> bool { #[cfg(test)] mod tests { - use secp256k1::Secp256k1; use super::*; #[test] @@ -176,16 +173,6 @@ mod tests { assert!(verify_cert(cert_pem.as_ref())); } - - #[test] - fn test_nostr() { - let secp = Secp256k1::new(); - let secret_key = SecretKey::new(&mut rand::thread_rng()); - let public_key = secret_key.public_key(&secp); - // println!("secret_key: {}", secret_key.display_secret()); - println!("{}", bs58::encode(public_key.serialize()).into_string()); - } - } #[cfg(test)] From 0b5d3c17808c153d7a7985a2fc8af257ec475156 Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Mon, 17 Jun 2024 16:59:31 +0800 Subject: [PATCH 11/16] add `init`: generate `Nostr ID` & save with `secret_key` Signed-off-by: Qihang Cai --- relay/src/lib.rs | 34 +++++++++++++++++++++++++++++++++- relay/src/nostr.rs | 12 ++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/relay/src/lib.rs b/relay/src/lib.rs index 67a346a27..9744a0864 100644 --- a/relay/src/lib.rs +++ b/relay/src/lib.rs @@ -1,3 +1,35 @@ +use crate::vault::{read_secret, write_secret}; + pub mod pki; pub mod vault; -pub mod nostr; \ No newline at end of file +pub mod nostr; + +pub fn init() { + let mut id = read_secret("id").unwrap(); + if id.is_none() { + println!("Nostr ID not found, generating new one..."); + let (nostr, (secret_key, _)) = nostr::generate_nostr_id(); + let data = serde_json::json!({ + "nostr": nostr, + "secret_key": secret_key.display_secret().to_string(), + }) + .as_object() + .unwrap() + .clone(); + write_secret("id", Some(data)).unwrap_or_else(|e| { + panic!("Failed to write Nostr ID: {:?}", e); + }); + id = read_secret("id").unwrap(); + } + println!("Nostr ID: {:?}", id.unwrap().data.unwrap()); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_init() { + init(); + } +} \ No newline at end of file diff --git a/relay/src/nostr.rs b/relay/src/nostr.rs index f8ccab5ad..c8a1922cf 100644 --- a/relay/src/nostr.rs +++ b/relay/src/nostr.rs @@ -11,6 +11,7 @@ pub fn generate_nostr_id() -> (String, (SecretKey, PublicKey)) { #[cfg(test)] mod tests { + use secp256k1::Message; use super::*; #[test] @@ -18,5 +19,16 @@ mod tests { let (nostr, keypair) = generate_nostr_id(); println!("nostr: {:?}", nostr); println!("keypair: {:?}", keypair); + let secret_key = keypair.0; + let public_key = keypair.1; + + let nostr_decode = bs58::decode(&nostr).into_vec().unwrap(); + assert_eq!(nostr_decode, public_key.serialize().to_vec()); + assert_eq!(PublicKey::from_slice(&nostr_decode).unwrap(), public_key); + // verify + let secp = Secp256k1::new(); + let message = Message::from_slice(&[0xab; 32]).expect("32 bytes"); + let sig = secp.sign_ecdsa(&message, &secret_key); + assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(())); } } \ No newline at end of file From ebfbac834110781369700bcada0731b51cb87f1a Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Mon, 17 Jun 2024 20:56:11 +0800 Subject: [PATCH 12/16] move `config_role` to CA-init & add `get_root_cert` Signed-off-by: Qihang Cai --- relay/src/pki.rs | 52 +++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/relay/src/pki.rs b/relay/src/pki.rs index 683453a36..d3470e417 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -19,8 +19,20 @@ lazy_static! { let c = CORE.clone(); // init CA if not if let Err(_) = read_api(&c.core.read().unwrap(), &c.token, "pki/ca/pem") { - config_ca(c.core.clone(), &c.token); - generate_root(c.core.clone(), &c.token, false); + let token = &c.token; + config_ca(c.core.clone(), token); + generate_root(c.core.clone(), token, false); + config_role(c.core.clone(), token, json!({ + "ttl": "60d", + "max_ttl": "365d", + "key_type": "rsa", + "key_bits": 4096, + "country": "CN", + "province": "Beijing", + "locality": "Beijing", + "organization": "OpenAtom-Mega", + "no_store": false, + })); } c }; @@ -42,9 +54,8 @@ fn config_ca(core: Arc>, token: &str) { } /// - `data`: see [RoleEntry](rusty_vault::modules::pki::path_roles) -fn config_role(data: Value) { - let core = CA.core.read().unwrap(); - let token = &CA.token; +fn config_role(core: Arc>, token: &str, data: Value) { + let core = core.read().unwrap(); let role_data = data.as_object() .expect("`data` must be a JSON object") @@ -119,14 +130,7 @@ pub fn issue_cert(data: Value) -> String { } pub fn verify_cert(cert_pem: &[u8]) -> bool { - let ca_cert = { - let core = CA.core.read().unwrap(); - - let resp_ca_pem = read_api(&core, &CA.token, "pki/ca/pem").unwrap().unwrap(); - let ca_cert = resp_ca_pem.data.unwrap(); - let ca_cert_pem = ca_cert["certificate"].as_str().unwrap(); - X509::from_pem(ca_cert_pem.as_ref()).unwrap() - }; + let ca_cert = X509::from_pem(get_root_cert().as_ref()).unwrap(); let cert = X509::from_pem(cert_pem).unwrap(); // verify time @@ -147,24 +151,22 @@ pub fn verify_cert(cert_pem: &[u8]) -> bool { cert.verify(&ca_cert.public_key().unwrap()).unwrap() } +/// get root certificate +pub fn get_root_cert() -> String { + let core = CA.core.read().unwrap(); + + let resp_ca_pem = read_api(&core, &CA.token, "pki/ca/pem").unwrap().unwrap(); + let ca_data = resp_ca_pem.data.unwrap(); + + ca_data["certificate"].as_str().unwrap().to_owned() +} + #[cfg(test)] mod tests { use super::*; #[test] fn test_pki_issue() { - config_role(json!({ // TODO move to `init` - "ttl": "60d", - "max_ttl": "365d", - "key_type": "rsa", - "key_bits": 4096, - "country": "CN", - "province": "Beijing", - "locality": "Beijing", - "organization": "OpenAtom-Mega", - "no_store": false, - })); - let cert_pem = issue_cert(json!({ "ttl": "10d", "common_name": "oqpXWgEhXa1WDqMWBnpUW4jvrxGqJKVuJATy4MSPdKNS", //nostr id From 529ce4db9ddf0f074eb2a05a38b856212027402c Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Tue, 18 Jun 2024 10:26:53 +0800 Subject: [PATCH 13/16] return `private_key` in `issue_cert` meanwhile Signed-off-by: Qihang Cai --- relay/src/pki.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/relay/src/pki.rs b/relay/src/pki.rs index d3470e417..7f64dcb19 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -104,7 +104,8 @@ fn generate_root(core: Arc>, token: &str, exported: bool) { } /// - `data`: see [issue_path](rusty_vault::modules::pki::path_issue) -pub fn issue_cert(data: Value) -> String { +/// - return: (cert, private_key) +pub fn issue_cert(data: Value) -> (String, String) { let core = CA.core.read().unwrap(); let token = &CA.token; @@ -118,15 +119,12 @@ pub fn issue_cert(data: Value) -> String { assert!(resp.is_ok()); let resp_body = resp.unwrap(); let cert_data = resp_body.unwrap().data.unwrap(); - println!("issue cert result: {:?}", cert_data["certificate"]); + // println!("cert_data: {:?}", cert_data); - #[cfg(test)] - { - let mut file = fs::File::create("/tmp/cert.crt").unwrap(); // TODO add root cert in it - file.write_all(cert_data["certificate"].as_str().unwrap().as_ref()).unwrap(); - } - - cert_data["certificate"].as_str().unwrap().to_owned() + ( + cert_data["certificate"].as_str().unwrap().to_owned(), // TODO may add root cert in it + cert_data["private_key"].as_str().unwrap().to_owned(), + ) } pub fn verify_cert(cert_pem: &[u8]) -> bool { @@ -167,13 +165,19 @@ mod tests { #[test] fn test_pki_issue() { - let cert_pem = issue_cert(json!({ + let (cert_pem, private_key) = issue_cert(json!({ "ttl": "10d", "common_name": "oqpXWgEhXa1WDqMWBnpUW4jvrxGqJKVuJATy4MSPdKNS", //nostr id // "alt_names": "a.test.com,b.test.com", })); + println!("cert_pem: {}", cert_pem); + println!("private_key: {}", private_key); + assert!(verify_cert(cert_pem.as_ref())); + + let mut file = fs::File::create("/tmp/cert.crt").unwrap(); + file.write_all(cert_pem.as_ref()).unwrap(); } } From 5ef11ce836b57db3e1bd86c0cebbcf2a85735bc2 Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Tue, 18 Jun 2024 13:19:08 +0800 Subject: [PATCH 14/16] return `(NostrID, secret_key)` in `init` & add comments Signed-off-by: Qihang Cai --- relay/src/lib.rs | 15 ++++++++++++--- relay/src/pki.rs | 14 ++++++++------ relay/src/vault.rs | 6 +++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/relay/src/lib.rs b/relay/src/lib.rs index 9744a0864..a3ab9f87d 100644 --- a/relay/src/lib.rs +++ b/relay/src/lib.rs @@ -4,7 +4,10 @@ pub mod pki; pub mod vault; pub mod nostr; -pub fn init() { +/// Initialize the Nostr ID if it's not found. +/// - return: `(Nostr ID, secret_key)` +/// - You can get `Public Key` by just `base58::decode(nostr)` +pub fn init() -> (String, String) { let mut id = read_secret("id").unwrap(); if id.is_none() { println!("Nostr ID not found, generating new one..."); @@ -21,7 +24,11 @@ pub fn init() { }); id = read_secret("id").unwrap(); } - println!("Nostr ID: {:?}", id.unwrap().data.unwrap()); + let id_data = id.unwrap().data.unwrap(); + ( + id_data["nostr"].as_str().unwrap().to_string(), + id_data["secret_key"].as_str().unwrap().to_string(), + ) } #[cfg(test)] @@ -30,6 +37,8 @@ mod tests { #[test] fn test_init() { - init(); + let id = init(); + println!("Nostr ID: {:?}", id.0); + println!("Secret Key: {:?}", id.1); // private key } } \ No newline at end of file diff --git a/relay/src/pki.rs b/relay/src/pki.rs index 7f64dcb19..e1e225d1a 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -13,7 +13,7 @@ use serde_json::{json, Value}; use super::vault::{CoreInfo, CORE, read_api, write_api}; const ROLE: &str = "test"; - +// Automatically initialize CA when you first use it lazy_static! { static ref CA: CoreInfo = { let c = CORE.clone(); @@ -22,7 +22,7 @@ lazy_static! { let token = &c.token; config_ca(c.core.clone(), token); generate_root(c.core.clone(), token, false); - config_role(c.core.clone(), token, json!({ + config_role(c.core.clone(), token, json!({ // TODO You may want to customize this "ttl": "60d", "max_ttl": "365d", "key_type": "rsa", @@ -54,7 +54,7 @@ fn config_ca(core: Arc>, token: &str) { } /// - `data`: see [RoleEntry](rusty_vault::modules::pki::path_roles) -fn config_role(core: Arc>, token: &str, data: Value) { +pub fn config_role(core: Arc>, token: &str, data: Value) { let core = core.read().unwrap(); let role_data = data.as_object() @@ -103,8 +103,9 @@ fn generate_root(core: Arc>, token: &str, exported: bool) { // println!("resp_ca_pem_cert_data: {:?}", resp_ca_pem_cert_data); } +/// issue certificate /// - `data`: see [issue_path](rusty_vault::modules::pki::path_issue) -/// - return: (cert, private_key) +/// - return: `(cert_pem, private_key)` pub fn issue_cert(data: Value) -> (String, String) { let core = CA.core.read().unwrap(); let token = &CA.token; @@ -122,11 +123,12 @@ pub fn issue_cert(data: Value) -> (String, String) { // println!("cert_data: {:?}", cert_data); ( - cert_data["certificate"].as_str().unwrap().to_owned(), // TODO may add root cert in it + cert_data["certificate"].as_str().unwrap().to_owned(), // TODO may add root cert (chain) in it cert_data["private_key"].as_str().unwrap().to_owned(), ) } +/// Verify certificate: time & signature pub fn verify_cert(cert_pem: &[u8]) -> bool { let ca_cert = X509::from_pem(get_root_cert().as_ref()).unwrap(); @@ -149,7 +151,7 @@ pub fn verify_cert(cert_pem: &[u8]) -> bool { cert.verify(&ca_cert.public_key().unwrap()).unwrap() } -/// get root certificate +/// Get root certificate of CA pub fn get_root_cert() -> String { let core = CA.core.read().unwrap(); diff --git a/relay/src/vault.rs b/relay/src/vault.rs index b111aac3d..55cbe889c 100644 --- a/relay/src/vault.rs +++ b/relay/src/vault.rs @@ -27,10 +27,10 @@ lazy_static! { pub static ref CORE: CoreInfo = init(); } -/// Initialize the vault core +/// Initialize the vault core, used in `lazy_static!` fn init() -> CoreInfo { - const CORE_KEY_FILE: &str = "core_key.json"; - let dir = PathBuf::from("/tmp/rusty_vault_pki_module"); + const CORE_KEY_FILE: &str = "core_key.json"; // where the core key is stored, like `root_token` + let dir = PathBuf::from("/tmp/rusty_vault_pki_module"); // RustyVault files TODO configurable let core_key_path = dir.join(CORE_KEY_FILE); // let dir = env::temp_dir().join("rusty_vault_pki_module"); // TODO: 改成数据库? From fc7c876a76ac579cab79429a4b5a0fbb777eddf7 Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Tue, 18 Jun 2024 13:45:54 +0800 Subject: [PATCH 15/16] clear clippy warnings Signed-off-by: Qihang Cai --- relay/src/pki.rs | 8 ++++---- relay/src/vault.rs | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/relay/src/pki.rs b/relay/src/pki.rs index e1e225d1a..2944f23af 100644 --- a/relay/src/pki.rs +++ b/relay/src/pki.rs @@ -1,6 +1,4 @@ use std::cmp::Ordering; -use std::fs; -use std::io::Write; use std::sync::{Arc, RwLock}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -18,7 +16,7 @@ lazy_static! { static ref CA: CoreInfo = { let c = CORE.clone(); // init CA if not - if let Err(_) = read_api(&c.core.read().unwrap(), &c.token, "pki/ca/pem") { + if read_api(&c.core.read().unwrap(), &c.token, "pki/ca/pem").is_err() { // err = not found let token = &c.token; config_ca(c.core.clone(), token); generate_root(c.core.clone(), token, false); @@ -163,6 +161,8 @@ pub fn get_root_cert() -> String { #[cfg(test)] mod tests { + use std::fs; + use std::io::Write; use super::*; #[test] @@ -284,7 +284,7 @@ mod tests_raw { assert_eq!(role_data["province"].as_str().unwrap(), "Beijing"); assert_eq!(role_data["locality"].as_str().unwrap(), "Beijing"); assert_eq!(role_data["organization"].as_str().unwrap(), "OpenAtom"); - assert_eq!(role_data["no_store"].as_bool().unwrap(), false); + assert!(!role_data["no_store"].as_bool().unwrap()); } fn test_pki_generate_root(core: Arc>, token: &str, exported: bool, is_ok: bool) { diff --git a/relay/src/vault.rs b/relay/src/vault.rs index 55cbe889c..a9c565ddd 100644 --- a/relay/src/vault.rs +++ b/relay/src/vault.rs @@ -83,7 +83,7 @@ fn init() -> CoreInfo { for i in 0..seal_config.secret_threshold { let key = &core_key.secret_shares[i as usize]; - let unseal = core.unseal(&key); + let unseal = core.unseal(key); assert!(unseal.is_ok()); unsealed = unseal.unwrap(); } @@ -100,8 +100,7 @@ pub fn read_api(core: &Core, token: &str, path: &str) -> Result let mut req = Request::new(path); req.operation = Operation::Read; req.client_token = token.to_string(); - let resp = core.handle_request(&mut req); - resp + core.handle_request(&mut req) } pub fn write_api( From 0ad2cf8df4a35adc13798ef612ed2ba0adf01f51 Mon Sep 17 00:00:00 2001 From: Qihang Cai Date: Tue, 18 Jun 2024 14:28:06 +0800 Subject: [PATCH 16/16] clear clippy warnings (more) Signed-off-by: Qihang Cai --- gateway/src/git_protocol/ssh.rs | 2 +- gateway/src/model/query.rs | 4 ++-- libra/src/internal/config.rs | 2 +- mercury/src/internal/pack/encode.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gateway/src/git_protocol/ssh.rs b/gateway/src/git_protocol/ssh.rs index c96fbef5e..64ddc4590 100644 --- a/gateway/src/git_protocol/ssh.rs +++ b/gateway/src/git_protocol/ssh.rs @@ -19,7 +19,7 @@ use ceres::protocol::{SmartProtocol, TransportProtocol}; use jupiter::context::Context; type ClientMap = HashMap<(usize, ChannelId), Channel>; - +#[allow(dead_code)] #[derive(Clone)] pub struct SshServer { pub client_pubkey: Arc, diff --git a/gateway/src/model/query.rs b/gateway/src/model/query.rs index 6963ccec3..51a720af8 100644 --- a/gateway/src/model/query.rs +++ b/gateway/src/model/query.rs @@ -1,5 +1,5 @@ use serde::Deserialize; - +#[allow(dead_code)] #[derive(Debug, Deserialize)] pub struct DirectoryQuery { #[serde(default)] // Use default value if not provided in the query string @@ -7,7 +7,7 @@ pub struct DirectoryQuery { #[serde(default = "default_path")] pub repo_path: String, } - +#[allow(dead_code)] #[derive(Debug, Deserialize)] pub struct CodePreviewQuery { #[serde(default)] diff --git a/libra/src/internal/config.rs b/libra/src/internal/config.rs index f45bf4a45..a88e9f770 100644 --- a/libra/src/internal/config.rs +++ b/libra/src/internal/config.rs @@ -16,7 +16,7 @@ pub struct RemoteConfig { pub name: String, pub url: String, } - +#[allow(dead_code)] pub struct BranchConfig { pub name: String, pub merge: String, diff --git a/mercury/src/internal/pack/encode.rs b/mercury/src/internal/pack/encode.rs index 402cfe49d..bdc7b0bbe 100644 --- a/mercury/src/internal/pack/encode.rs +++ b/mercury/src/internal/pack/encode.rs @@ -236,7 +236,7 @@ impl PackEncoder { #[cfg(test)] mod tests { - use std::{io::Cursor, path::PathBuf, usize}; + use std::{io::Cursor, path::PathBuf}; use crate::internal::object::blob::Blob; use crate::internal::pack::Pack;