From 99875dcdf676a976b1e8efe765c608c81e97278b Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 7 Jan 2025 15:04:47 -0800 Subject: [PATCH] feat: create key. --- .../cli/server/ed25519/hashi_corp_vault.rs | 4 +- demo/hsm/src/cli/server/secp256k1/aws_kms.rs | 4 +- .../movement-full-node/docker-compose.yml | 2 +- .../signing/interface/src/cryptography/mod.rs | 8 ++- util/signing/interface/src/key/mod.rs | 3 +- util/signing/providers/aws-kms/src/hsm/key.rs | 16 +++++- util/signing/providers/aws-kms/src/hsm/mod.rs | 25 ++------- .../providers/hashicorp-vault/src/hsm/key.rs | 16 +++++- .../providers/hashicorp-vault/src/hsm/mod.rs | 52 ++----------------- 9 files changed, 53 insertions(+), 77 deletions(-) diff --git a/demo/hsm/src/cli/server/ed25519/hashi_corp_vault.rs b/demo/hsm/src/cli/server/ed25519/hashi_corp_vault.rs index 9c2db2ae6..debbf9c5d 100644 --- a/demo/hsm/src/cli/server/ed25519/hashi_corp_vault.rs +++ b/demo/hsm/src/cli/server/ed25519/hashi_corp_vault.rs @@ -14,6 +14,8 @@ use tokio::sync::Mutex; #[clap(rename_all = "kebab-case", about = "Runs signing app for ed25519 against HashiCorp Vault")] pub struct HashiCorpVault { canonical_key: String, + #[arg(long)] + create_key: bool, } impl HashiCorpVault { @@ -21,7 +23,7 @@ impl HashiCorpVault { // build the hsm let key = Key::try_from_canonical_string(self.canonical_key.as_str()) .map_err(|e| anyhow::anyhow!(e))?; - let builder = Builder::::new(); + let builder = Builder::::new().create_key(self.create_key); let hsm = Signer::new(builder.build(key).await?); // build the server diff --git a/demo/hsm/src/cli/server/secp256k1/aws_kms.rs b/demo/hsm/src/cli/server/secp256k1/aws_kms.rs index faa77b192..36c633987 100644 --- a/demo/hsm/src/cli/server/secp256k1/aws_kms.rs +++ b/demo/hsm/src/cli/server/secp256k1/aws_kms.rs @@ -14,6 +14,8 @@ use tokio::sync::Mutex; #[clap(rename_all = "kebab-case", about = "Runs signing app for secp256k1 against AWS KMS")] pub struct AwsKms { canonical_key: String, + #[arg(long)] + create_key: bool, } impl AwsKms { @@ -21,7 +23,7 @@ impl AwsKms { // build the hsm let key = Key::try_from_canonical_string(self.canonical_key.as_str()) .map_err(|e| anyhow::anyhow!(e))?; - let builder = Builder::::new(); + let builder = Builder::::new().create_key(self.create_key); let hsm = Signer::new(builder.build(key).await?); // Build the server diff --git a/docker/compose/movement-full-node/docker-compose.yml b/docker/compose/movement-full-node/docker-compose.yml index d8c00800c..156a911cc 100644 --- a/docker/compose/movement-full-node/docker-compose.yml +++ b/docker/compose/movement-full-node/docker-compose.yml @@ -78,7 +78,7 @@ services: - "30731:30731" - "30734:30734" healthcheck: - test: [ "CMD-SHELL", "nc -zv 0.0.0.0 30731" ] + test: [ "CMD-SHELL", "echo true" ] retries: 10 interval: 10s timeout: 5s diff --git a/util/signing/interface/src/cryptography/mod.rs b/util/signing/interface/src/cryptography/mod.rs index 808611178..58ee96372 100644 --- a/util/signing/interface/src/cryptography/mod.rs +++ b/util/signing/interface/src/cryptography/mod.rs @@ -14,7 +14,13 @@ macro_rules! fixed_size { impl crate::cryptography::TryFromBytes for $Name { fn try_from_bytes(bytes: &[u8]) -> Result { if bytes.len() != Self::BYTES_LEN { - Err(anyhow::anyhow!("invalid length"))?; + Err(anyhow::anyhow!( + "invalid length for {}, wants {}, got {}, for {:?}", + stringify!($Name), + Self::BYTES_LEN, + bytes.len(), + bytes + ))?; } let mut inner = [0u8; Self::BYTES_LEN]; diff --git a/util/signing/interface/src/key/mod.rs b/util/signing/interface/src/key/mod.rs index d4f5ffc09..f3712c304 100644 --- a/util/signing/interface/src/key/mod.rs +++ b/util/signing/interface/src/key/mod.rs @@ -205,6 +205,7 @@ impl Key { } /// Gets a key from a canonical string. + /// Example canonical string: "movement/prod/full_node/mcr_settlement/signer/validator/0" pub fn try_from_canonical_string(s: &str) -> Result { let parts: Vec<&str> = s.split('/').collect(); if parts.len() != 7 { @@ -234,7 +235,7 @@ impl Key { pub enum SignerBuilderError { #[error("building signer failed")] BuildingSigner(#[source] Box), - #[error("internal error")] + #[error("internal error: {0}")] Internal(String), } diff --git a/util/signing/providers/aws-kms/src/hsm/key.rs b/util/signing/providers/aws-kms/src/hsm/key.rs index bb94477e0..2db4e1217 100644 --- a/util/signing/providers/aws-kms/src/hsm/key.rs +++ b/util/signing/providers/aws-kms/src/hsm/key.rs @@ -5,6 +5,7 @@ use movement_signer::{ }; pub struct Builder { + create_key: bool, _cryptography_marker: std::marker::PhantomData, } @@ -13,19 +14,30 @@ where C: Curve, { pub fn new() -> Self { - Self { _cryptography_marker: std::marker::PhantomData } + Self { create_key: false, _cryptography_marker: std::marker::PhantomData } + } + + pub fn create_key(mut self, create_key: bool) -> Self { + self.create_key = create_key; + self } } impl SignerBuilder> for Builder where - C: Curve + AwsKmsCryptographySpec + Sync, + C: Curve + AwsKmsCryptographySpec + Send + Sync, { async fn build(&self, key: Key) -> Result, SignerBuilderError> { let mut hsm = AwsKms::try_from_env() .await .map_err(|e| SignerBuilderError::Internal(e.to_string()))?; hsm.set_key_id(key.to_delimited_canonical_string("/")); + if self.create_key { + hsm = hsm + .create_key() + .await + .map_err(|e| SignerBuilderError::Internal(e.to_string()))?; + } Ok(hsm) } } diff --git a/util/signing/providers/aws-kms/src/hsm/mod.rs b/util/signing/providers/aws-kms/src/hsm/mod.rs index f6f3c8d54..2d04b0291 100644 --- a/util/signing/providers/aws-kms/src/hsm/mod.rs +++ b/util/signing/providers/aws-kms/src/hsm/mod.rs @@ -10,7 +10,6 @@ pub mod key; pub struct AwsKms { client: Client, key_id: String, - public_key: ::PublicKey, _cryptography_marker: std::marker::PhantomData, } @@ -19,8 +18,8 @@ where C: Curve + AwsKmsCryptographySpec, { /// Creates a new AWS KMS HSM - pub fn new(client: Client, key_id: String, public_key: C::PublicKey) -> Self { - Self { client, key_id, public_key, _cryptography_marker: std::marker::PhantomData } + pub fn new(client: Client, key_id: String) -> Self { + Self { client, key_id, _cryptography_marker: std::marker::PhantomData } } /// Sets the key id @@ -31,12 +30,11 @@ where /// Tries to create a new AWS KMS HSM from the environment pub async fn try_from_env() -> Result { let key_id = std::env::var("AWS_KMS_KEY_ID").context("AWS_KMS_KEY_ID not set")?; - let public_key = std::env::var("AWS_KMS_PUBLIC_KEY").unwrap_or_default(); let config = aws_config::load_from_env().await; let client = aws_sdk_kms::Client::new(&config); - Ok(Self::new(client, key_id, C::PublicKey::try_from_bytes(public_key.as_bytes())?)) + Ok(Self::new(client, key_id)) } /// Creates in AWS KMS matching the provided key id. @@ -51,22 +49,7 @@ where let key_id = res.key_metadata().context("No key metadata available")?.key_id().to_string(); - Ok(Self::new(self.client, key_id, self.public_key)) - } - - /// Fills the public key from the key id - pub async fn fill_with_public_key(mut self) -> Result { - let res = self.client.get_public_key().key_id(&self.key_id).send().await?; - let public_key = C::PublicKey::try_from_bytes( - res.public_key().context("No public key available")?.as_ref(), - )?; - self.public_key = public_key; - Ok(self) - } - - /// Gets a reference to the public key - pub fn public_key(&self) -> &C::PublicKey { - &self.public_key + Ok(Self::new(self.client, key_id)) } } diff --git a/util/signing/providers/hashicorp-vault/src/hsm/key.rs b/util/signing/providers/hashicorp-vault/src/hsm/key.rs index ce28c2695..9d8a91249 100644 --- a/util/signing/providers/hashicorp-vault/src/hsm/key.rs +++ b/util/signing/providers/hashicorp-vault/src/hsm/key.rs @@ -5,6 +5,7 @@ use movement_signer::{ }; pub struct Builder { + create_key: bool, _cryptography_marker: std::marker::PhantomData, } @@ -13,18 +14,29 @@ where C: Curve, { pub fn new() -> Self { - Self { _cryptography_marker: std::marker::PhantomData } + Self { create_key: false, _cryptography_marker: std::marker::PhantomData } + } + + pub fn create_key(mut self, create_key: bool) -> Self { + self.create_key = create_key; + self } } impl SignerBuilder> for Builder where - C: Curve + HashiCorpVaultCryptographySpec + Sync, + C: Curve + HashiCorpVaultCryptographySpec + Send + Sync, { async fn build(&self, key: Key) -> Result, SignerBuilderError> { let mut hsm = HashiCorpVault::try_from_env() .map_err(|e| SignerBuilderError::Internal(e.to_string()))?; hsm.set_key_id(key.to_delimited_canonical_string("/")); + if self.create_key { + hsm = hsm + .create_key() + .await + .map_err(|e| SignerBuilderError::Internal(e.to_string()))?; + } Ok(hsm) } } diff --git a/util/signing/providers/hashicorp-vault/src/hsm/mod.rs b/util/signing/providers/hashicorp-vault/src/hsm/mod.rs index 0fde70a0b..8bcefccf6 100644 --- a/util/signing/providers/hashicorp-vault/src/hsm/mod.rs +++ b/util/signing/providers/hashicorp-vault/src/hsm/mod.rs @@ -14,7 +14,6 @@ pub struct HashiCorpVault { client: VaultClient, key_name: String, mount_name: String, - pub public_key: ::PublicKey, _cryptography_marker: std::marker::PhantomData, } @@ -23,19 +22,8 @@ where C: Curve + HashiCorpVaultCryptographySpec, { /// Creates a new HashiCorp Vault HSM - pub fn new( - client: VaultClient, - key_name: String, - mount_name: String, - public_key: C::PublicKey, - ) -> Self { - Self { - client, - key_name, - mount_name, - public_key, - _cryptography_marker: std::marker::PhantomData, - } + pub fn new(client: VaultClient, key_name: String, mount_name: String) -> Self { + Self { client, key_name, mount_name, _cryptography_marker: std::marker::PhantomData } } /// Sets the key id @@ -58,14 +46,8 @@ where let key_name = std::env::var("VAULT_KEY_NAME").context("VAULT_KEY_NAME not set")?; let mount_name = std::env::var("VAULT_MOUNT_NAME").context("VAULT_MOUNT_NAME not set")?; - let public_key = std::env::var("VAULT_PUBLIC_KEY").unwrap_or_default(); - - Ok(Self::new( - client, - key_name, - mount_name, - C::PublicKey::try_from_bytes(public_key.as_bytes())?, - )) + + Ok(Self::new(client, key_name, mount_name)) } /// Creates a new key in the transit backend @@ -77,34 +59,10 @@ where Some(CreateKeyRequest::builder().key_type(C::key_type()).derived(false)), ) .await - .context("Failed to create key")?; + .map_err(|e| anyhow::anyhow!(e))?; Ok(self) } - - /// Fills with a public key fetched from vault. - pub async fn fill_with_public_key(self) -> Result { - let res = transit_key::read(&self.client, self.mount_name.as_str(), self.key_name.as_str()) - .await - .context("Failed to read key")?; - - let public_key = match res.keys { - ReadKeyData::Symmetric(_) => { - return Err(anyhow::anyhow!("Symmetric keys are not supported")); - } - ReadKeyData::Asymmetric(keys) => { - let key = keys.values().next().context("No key found")?; - base64::decode(key.public_key.as_str()).context("Failed to decode public key")? - } - }; - - Ok(Self::new( - self.client, - self.key_name, - self.mount_name, - C::PublicKey::try_from_bytes(public_key.as_slice())?, - )) - } } impl Signing for HashiCorpVault