g3keymess: allow to check key existence

This commit is contained in:
Zhang Jingqiang 2023-10-16 17:50:58 +08:00
parent a9da70cd0d
commit 9856786d6d
13 changed files with 113 additions and 31 deletions

4
Cargo.lock generated
View file

@ -1142,7 +1142,7 @@ dependencies = [
[[package]]
name = "g3-tls-cert"
version = "0.3.0"
version = "0.4.0"
dependencies = [
"anyhow",
"chrono",
@ -1373,11 +1373,13 @@ dependencies = [
"clap_complete",
"futures-util",
"g3-ctl",
"g3-tls-cert",
"g3keymess-proto",
"hex",
"thiserror",
"tokio",
"tokio-util",
"tongsuo",
]
[[package]]

View file

@ -200,7 +200,7 @@ g3-stdlog = { version = "0.1", path = "lib/g3-stdlog" }
g3-syslog = { version = "0.6", path = "lib/g3-syslog" }
g3-slog-types = { version = "0.1", path = "lib/g3-slog-types" }
g3-geoip = { version = "0.1", path = "lib/g3-geoip" }
g3-tls-cert = { version = "0.3", path = "lib/g3-tls-cert" }
g3-tls-cert = { version = "0.4", path = "lib/g3-tls-cert" }
g3-types = { version = "0.3", path = "lib/g3-types" }
g3-xcrypt = { version = "0.1", path = "lib/g3-xcrypt" }
g3-yaml = { version = "0.4.1", path = "lib/g3-yaml" }

View file

@ -27,7 +27,7 @@ use openssl::pkey::{Id, PKey, Private, Public};
use openssl::pkey_ctx::PkeyCtx;
use openssl::rsa::Padding;
use g3_tls_cert::ext::{X509Ext, X509Pubkey};
use g3_tls_cert::ext::{PublicKeyExt, X509Ext};
const ARG_CERT: &str = "cert";
const ARG_PKEY: &str = "key";
@ -203,18 +203,10 @@ impl KeylessGlobalArgs {
let key = crate::target::openssl::load_key(file)?;
// verify SKI match
let x509_pubkey = X509Pubkey::from_pubkey(&key).map_err(|e| {
anyhow!(
"failed to create x509 pubkey from key in file {}: {e}",
file.display()
)
})?;
let encoded_bytes = x509_pubkey
.encoded_bytes()
.map_err(|e| anyhow!("failed to get encoded pubkey data: {e}"))?;
let digest = openssl::hash::hash(MessageDigest::sha1(), encoded_bytes)
.map_err(|e| anyhow!("failed to get sha1 hash of pubkey: {e}"))?;
let ski = digest.to_vec();
let ski = key
.ski()
.map_err(|e| anyhow!("failed to get SKI from key file {}: {e}", file.display()))?;
let ski = ski.to_vec();
if let Some(ski_cert) = &public_key_ski {
if ski.ne(ski_cert) {

View file

@ -15,6 +15,7 @@ interface ProcControl {
publishKey @4 (pem: Text) -> (result :Types.OperationResult);
listKeys @5 () -> (result :List(Data));
checkKey @7 (ski: Data) -> (result: Types.OperationResult);
addMetricsTag @6 (name :Text, value :Text) -> (result :Types.OperationResult);
}

View file

@ -37,6 +37,15 @@ pub(crate) async fn list_keys() -> anyhow::Result<Vec<Vec<u8>>> {
run_in_main_thread(async move { Ok(crate::store::get_all_ski()) }).await
}
pub(crate) async fn check_key(ski: Vec<u8>) -> anyhow::Result<()> {
run_in_main_thread(async move {
crate::store::get_by_ski(&ski)
.map(|_| ())
.ok_or_else(|| anyhow!("key not found"))
})
.await
}
async fn run_in_main_thread<T, F>(future: F) -> anyhow::Result<T>
where
T: Send + 'static,

View file

@ -108,6 +108,19 @@ impl proc_control::Server for ProcControlImpl {
})
}
fn check_key(
&mut self,
params: proc_control::CheckKeyParams,
mut results: proc_control::CheckKeyResults,
) -> Promise<(), capnp::Error> {
let ski = pry!(pry!(params.get()).get_ski()).to_vec();
Promise::from_future(async move {
let r = crate::control::bridge::check_key(ski).await;
set_operation_result(results.get().init_result(), r);
Ok(())
})
}
fn add_metrics_tag(
&mut self,
params: proc_control::AddMetricsTagParams,

View file

@ -18,10 +18,9 @@ use std::cell::RefCell;
use ahash::AHashMap;
use anyhow::anyhow;
use openssl::hash::{DigestBytes, MessageDigest};
use openssl::pkey::{HasPublic, PKey, Private};
use openssl::pkey::{PKey, Private};
use g3_tls_cert::ext::X509Pubkey;
use g3_tls_cert::ext::PublicKeyExt;
mod ops;
pub use ops::{load_all, reload_all};
@ -33,7 +32,7 @@ thread_local! {
}
pub(crate) fn add_global(key: PKey<Private>) -> anyhow::Result<()> {
let ski = public_key_ski(&key)?;
let ski = key.ski().map_err(|e| anyhow!("failed to get SKI: {e}"))?;
GLOBAL_SKI_MAP.with_borrow_mut(|map| {
map.insert(ski.to_vec(), key);
});
@ -48,13 +47,3 @@ pub(crate) fn get_all_ski() -> Vec<Vec<u8>> {
pub(crate) fn get_by_ski(ski: &[u8]) -> Option<PKey<Private>> {
GLOBAL_SKI_MAP.with_borrow(|map| map.get(ski).cloned())
}
fn public_key_ski<T: HasPublic>(key: &PKey<T>) -> anyhow::Result<DigestBytes> {
let x =
X509Pubkey::from_pubkey(key).map_err(|e| anyhow!("failed to build X509 PUBKEY: {e}"))?;
let encoded = x
.encoded_bytes()
.map_err(|e| anyhow!("failed to get encoded X509 PUBKEY bytes: {e}"))?;
openssl::hash::hash(MessageDigest::sha1(), encoded)
.map_err(|e| anyhow!("failed to calculate SKI value: {e}"))
}

View file

@ -17,5 +17,7 @@ futures-util.workspace = true
capnp-rpc.workspace = true
capnp.workspace = true
hex.workspace = true
openssl.workspace = true
g3-ctl.workspace = true
g3-tls-cert.workspace = true
g3keymess-proto = { path = "../../proto" }

View file

@ -130,6 +130,7 @@ fn build_cli_args() -> Command {
.subcommand(proc::commands::offline())
.subcommand(proc::commands::list())
.subcommand(proc::commands::publish_key())
.subcommand(proc::commands::check_key())
.subcommand(server::command())
}
@ -172,6 +173,7 @@ async fn main() -> anyhow::Result<()> {
proc::COMMAND_OFFLINE => proc::offline(&proc_control).await,
proc::COMMAND_LIST => proc::list(&proc_control, args).await,
proc::COMMAND_PUBLISH_KEY => proc::publish_key(&proc_control, args).await,
proc::COMMAND_CHECK_KEY => proc::check_key(&proc_control, args).await,
server::COMMAND => server::run(&proc_control, args).await,
_ => unreachable!(),
}

View file

@ -18,9 +18,11 @@ use std::path::PathBuf;
use anyhow::anyhow;
use clap::ArgMatches;
use openssl::pkey::PKey;
use g3_ctl::{CommandError, CommandResult};
use g3_tls_cert::ext::PublicKeyExt;
use g3keymess_proto::proc_capnp::proc_control;
use g3keymess_proto::server_capnp::server_control;
@ -30,6 +32,7 @@ pub const COMMAND_VERSION: &str = "version";
pub const COMMAND_OFFLINE: &str = "offline";
pub const COMMAND_LIST: &str = "list";
pub const COMMAND_PUBLISH_KEY: &str = "publish-key";
pub const COMMAND_CHECK_KEY: &str = "check-key";
const COMMAND_LIST_ARG_RESOURCE: &str = "resource";
const RESOURCE_VALUE_SERVER: &str = "server";
@ -69,6 +72,17 @@ pub mod commands {
.value_hint(ValueHint::FilePath),
)
}
pub fn check_key() -> Command {
Command::new(COMMAND_CHECK_KEY).arg(
Arg::new(COMMAND_ARG_FILE)
.help("Private key file in pem format")
.required(true)
.num_args(1)
.value_parser(value_parser!(PathBuf))
.value_hint(ValueHint::FilePath),
)
}
}
pub async fn version(client: &proc_control::Client) -> CommandResult<()> {
@ -130,3 +144,25 @@ pub async fn publish_key(client: &proc_control::Client, args: &ArgMatches) -> Co
let rsp = req.send().promise.await?;
parse_operation_result(rsp.get()?.get_result()?)
}
pub async fn check_key(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
let file = args.get_one::<PathBuf>(COMMAND_ARG_FILE).unwrap();
let content = std::fs::read_to_string(file).map_err(|e| {
CommandError::Cli(anyhow!(
"failed to read content of file {}: {e}",
file.display()
))
})?;
let key = PKey::private_key_from_pem(content.as_bytes()).map_err(|e| {
CommandError::Cli(anyhow!("failed to load key from {}: {e}", file.display()))
})?;
let ski = key.ski().map_err(|e| {
CommandError::Cli(anyhow!("failed to get SKI for key {}: {e}", file.display()))
})?;
let mut req = client.check_key_request();
req.get().set_ski(&ski);
let rsp = req.send().promise.await?;
parse_operation_result(rsp.get()?.get_result()?)
}

View file

@ -1,6 +1,6 @@
[package]
name = "g3-tls-cert"
version = "0.3.0"
version = "0.4.0"
license.workspace = true
edition.workspace = true

View file

@ -23,7 +23,10 @@ mod x509_builder;
pub use x509_builder::X509BuilderExt;
mod x509_pubkey;
pub use x509_pubkey::{X509Pubkey, X509PubkeyRef};
use x509_pubkey::{X509Pubkey, X509PubkeyRef};
mod rsa;
pub use rsa::RsaExt;
mod pkey;
pub use pkey::PublicKeyExt;

View file

@ -0,0 +1,33 @@
/*
* Copyright 2023 ByteDance and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use openssl::error::ErrorStack;
use openssl::hash::{DigestBytes, MessageDigest};
use openssl::pkey::{HasPublic, PKey};
use super::X509Pubkey;
pub trait PublicKeyExt {
fn ski(&self) -> Result<DigestBytes, ErrorStack>;
}
impl<T: HasPublic> PublicKeyExt for PKey<T> {
fn ski(&self) -> Result<DigestBytes, ErrorStack> {
let x = X509Pubkey::from_pubkey(self)?;
let encoded = x.encoded_bytes()?;
openssl::hash::hash(MessageDigest::sha1(), encoded)
}
}