From 1b3cc9c31deed218b298cb3de42a35f2f3eaea57 Mon Sep 17 00:00:00 2001 From: Jonathan Grahl Date: Sat, 5 Oct 2024 16:09:56 +0200 Subject: [PATCH] feat: error handling (#7) * feat: proper error handling --- .github/workflows/linting.yml | 2 +- Cargo.lock | 1 - crates/valv/Cargo.toml | 18 +- crates/valv/build.rs | 1 + crates/valv/proto/internal/resources.proto | 2 +- crates/valv/src/api/server.rs | 47 ++- crates/valv/src/cmd/main.rs | 8 +- crates/valv/src/errors.rs | 45 +++ .../src/{tests.rs => integration_tests.rs} | 57 ++- crates/valv/src/lib.rs | 143 ++++--- crates/valv/src/storage/fdb.rs | 113 +++--- crates/valv/src/storage/interface.rs | 16 +- examples/google-kms/.gitignore | 1 + examples/google-kms/Cargo.lock | 1 - examples/google-kms/Cargo.toml | 19 +- examples/google-kms/build.rs | 6 +- .../src/{tests.rs => integration_tests.rs} | 30 +- examples/google-kms/src/main.rs | 348 +++++++++++++----- 18 files changed, 568 insertions(+), 290 deletions(-) create mode 100644 crates/valv/src/errors.rs rename crates/valv/src/{tests.rs => integration_tests.rs} (79%) create mode 100644 examples/google-kms/.gitignore rename examples/google-kms/src/{tests.rs => integration_tests.rs} (96%) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 6934273..477e014 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -32,6 +32,6 @@ jobs: - name: Generate buf run: cd crates/valv && buf generate - name: Run Clippy - run: cargo clippy --all -- -D warnings + run: cargo clippy --all - name: Run fmt run: cargo fmt --all -- --check diff --git a/Cargo.lock b/Cargo.lock index 9b2202b..a7fd414 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1593,7 +1593,6 @@ checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" name = "valv" version = "0.1.0" dependencies = [ - "anyhow", "async-trait", "bincode", "boring", diff --git a/crates/valv/Cargo.toml b/crates/valv/Cargo.toml index 07bf2ea..848740f 100644 --- a/crates/valv/Cargo.toml +++ b/crates/valv/Cargo.toml @@ -14,7 +14,6 @@ name = "server" path = "src/cmd/main.rs" [dependencies] -anyhow = "1.0.79" async-trait = "0.1.77" bincode = "1.3.3" boring = "4.1.0" @@ -33,3 +32,20 @@ tonic = "0.10.2" [build-dependencies] tonic-build = { version = "0.10.2", features = ["prost"] } + +[lints.clippy] +single_match = "warn" +single_match_else = "warn" +needless_match = "warn" +needless_late_init = "warn" +redundant_pattern_matching = "warn" +redundant_pattern = "warn" +redundant_guards = "warn" +collapsible_match = "warn" +match_single_binding = "warn" +match_same_arms = "warn" +match_ref_pats = "warn" +match_bool = "warn" +needless_bool = "deny" +unwrap_used = "warn" +expect_used = "warn" diff --git a/crates/valv/build.rs b/crates/valv/build.rs index db921c7..564b04a 100644 --- a/crates/valv/build.rs +++ b/crates/valv/build.rs @@ -4,6 +4,7 @@ use std::process::{exit, Command}; fn main() -> Result<(), Box> { // We do not want to perform this action for a CICD release build. if Ok("debug".to_owned()) == env::var("PROFILE") { + #[allow(clippy::unwrap_used)] let status = Command::new("buf") .arg("generate") .current_dir(env!("CARGO_MANIFEST_DIR")) diff --git a/crates/valv/proto/internal/resources.proto b/crates/valv/proto/internal/resources.proto index 09cfbdc..f95cae7 100644 --- a/crates/valv/proto/internal/resources.proto +++ b/crates/valv/proto/internal/resources.proto @@ -6,7 +6,7 @@ import "google/protobuf/timestamp.proto"; message Key { string key_id = 1; - string primary_version_id = 2; + uint32 primary_version_id = 2; string purpose = 3; // e.g., "ENCRYPT_DECRYPT", "SIGN_VERIFY" google.protobuf.Timestamp creation_time = 4; google.protobuf.Duration rotation_schedule = 5; diff --git a/crates/valv/src/api/server.rs b/crates/valv/src/api/server.rs index 74367b1..2fc4be2 100644 --- a/crates/valv/src/api/server.rs +++ b/crates/valv/src/api/server.rs @@ -30,14 +30,24 @@ impl MasterKeyManagementService for API { ) .await; - let reply = CreateMasterKeyResponse { - master_key: Some(MasterKey { - name: key.key_id, - ..Default::default() - }), - }; - - Ok(tonic::Response::new(reply)) + match key { + Ok(key) => { + let reply = CreateMasterKeyResponse { + master_key: Some(MasterKey { + name: key.key_id, + ..Default::default() + }), + }; + return Ok(tonic::Response::new(reply)); + } + Err(err) => { + println!("Failed to create master key {err}"); + return Err(tonic::Status::new( + tonic::Code::Internal, + format!("Failed to create master key: {err}"), + )); + } + } } async fn list_master_keys( @@ -97,12 +107,23 @@ impl MasterKeyManagementService for API { ) .await; - let reply = crate::valv::proto::v1::EncryptResponse { - name: request.get_ref().master_key_id.clone(), - ciphertext: encrypted_value.into(), - }; + match encrypted_value { + Ok(encrypted_value) => { + let reply = crate::valv::proto::v1::EncryptResponse { + name: request.get_ref().master_key_id.clone(), + ciphertext: encrypted_value.into(), + }; - Ok(tonic::Response::new(reply)) + Ok(tonic::Response::new(reply)) + } + Err(err) => { + println!("Failed to encrypt plaintext {err}"); + return Err(tonic::Status::new( + tonic::Code::Internal, + "Failed to encrypt plaintext", + )); + } + } } async fn decrypt( diff --git a/crates/valv/src/cmd/main.rs b/crates/valv/src/cmd/main.rs index 6485145..072069f 100644 --- a/crates/valv/src/cmd/main.rs +++ b/crates/valv/src/cmd/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::unwrap_used, clippy::expect_used)] + use std::sync::Arc; use clap::Parser; @@ -18,13 +20,15 @@ struct Cli { #[tokio::main] async fn main() { + let _guard = unsafe { foundationdb::boot() }; + let args = Cli::parse(); - let mut valv = Valv::new().await; + let mut valv = Valv::new().await.expect("Failed to initialize Valv"); let master_key = args.master_key.clone().expose_secret().clone().into_bytes()[..32] .try_into() - .unwrap(); + .expect("Master key must be 32 bytes"); valv.set_master_key(master_key); diff --git a/crates/valv/src/errors.rs b/crates/valv/src/errors.rs new file mode 100644 index 0000000..343a0d3 --- /dev/null +++ b/crates/valv/src/errors.rs @@ -0,0 +1,45 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ValvError { + #[error("IO error")] + Io(#[from] std::io::Error), + + #[error("Database error")] + Database(#[from] foundationdb::FdbError), + + #[error("Transaction commit error")] + TransactionCommitError(#[from] foundationdb::TransactionCommitError), + + #[error("Directory error")] + DirectoryError(foundationdb::directory::DirectoryError), + + #[error("TuplePacking error")] + TuplePacking(#[from] foundationdb::tuple::PackError), + + #[error("Prost decode error")] + Decode(#[from] prost::DecodeError), + + #[error("BoringSSL error")] + BoringSSL(#[from] boring::error::ErrorStack), + + #[error("Serialization error")] + Serialization(#[from] bincode::Error), + + #[error("Parse error: {0}")] + Parse(String), + + #[error("Invalid data: {0}")] + InvalidData(String), + + #[error("Key not found: {0}")] + KeyNotFound(String), + + #[error("Transport error")] + Transport(#[from] tonic::transport::Error), + + #[error("Internal error: {0}")] + Internal(String), +} + +pub type Result = std::result::Result; diff --git a/crates/valv/src/tests.rs b/crates/valv/src/integration_tests.rs similarity index 79% rename from crates/valv/src/tests.rs rename to crates/valv/src/integration_tests.rs index 84d2f99..b5a7480 100644 --- a/crates/valv/src/tests.rs +++ b/crates/valv/src/integration_tests.rs @@ -1,12 +1,14 @@ +#[allow(clippy::unwrap_used, clippy::expect_used)] #[cfg(test)] mod tests { use crate::{ api::server::API, + errors::ValvError, valv::proto::v1::{ master_key_management_service_server::MasterKeyManagementServiceServer, CreateMasterKeyRequest, DecryptRequest, EncryptRequest, MasterKey, }, - Valv, ValvAPI, + Valv, }; use std::{sync::Arc, time::Duration}; @@ -23,9 +25,6 @@ mod tests { let _guard = unsafe { foundationdb::boot() }; println!("Running server test suite"); - println!("Testing valv"); - test_valv().await.expect("test_valv error"); - println!("Valv test passed\n"); println!("Testing create master key"); test_create_master_key() @@ -41,57 +40,51 @@ mod tests { } async fn setup_server( - ) -> anyhow::Result>> { - let addr = SERVER_ADDR.parse()?; - let mut valv = Valv::new().await; + ) -> Result>, ValvError> { + let addr = SERVER_ADDR.parse().expect("Invalid address"); + let mut valv = Valv::new().await?; + let master_key_bytes: [u8; 32] = "77aaee825aa561995d7bda258f9b76b0" .as_bytes() .try_into() - .unwrap(); + .expect("Invalid master key"); + valv.set_master_key(master_key_bytes); let api = API { valv: Arc::new(valv), }; + let svc = MasterKeyManagementServiceServer::new(api); + Ok(tokio::spawn(Server::builder().add_service(svc).serve(addr))) } async fn setup_client( - ) -> anyhow::Result> { + ) -> Result, tonic::transport::Error> + { let channel = tonic::transport::Channel::from_static(CLIENT_ADDR) .connect() .await?; Ok(MasterKeyManagementServiceClient::new(channel)) } - async fn wait_for_server() -> anyhow::Result<()> { + async fn wait_for_server() { for _ in 0..10 { if tonic::transport::Channel::from_static(CLIENT_ADDR) .connect() .await .is_ok() { - return Ok(()); + return; } sleep(Duration::from_millis(100)).await; } - anyhow::bail!("Failed to connect to server") + panic!("Failed to connect to server") } - async fn test_valv() -> anyhow::Result<()> { - let valv = Valv::new().await; - let key = valv - .create_key("tenant".to_string(), "test".to_string()) - .await; - let key_metadata = valv.get_key("tenant".to_string(), key.key_id).await; - assert_eq!(key_metadata.unwrap().key_id, "test"); - - Ok(()) - } - - async fn test_create_master_key() -> anyhow::Result<()> { + async fn test_create_master_key() -> Result<(), ValvError> { let server_handle = setup_server().await?; - wait_for_server().await?; + wait_for_server().await; let mut client = setup_client().await?; @@ -101,10 +94,10 @@ mod tests { master_key_id: "test".to_string(), keyring_name: "test_tenant".to_string(), }); - let response = client.create_master_key(request).await?; + let response = client.create_master_key(request).await.unwrap(); // Assert the response - assert_eq!(response.get_ref().master_key.is_some(), true); + assert!(response.get_ref().master_key.is_some()); // Stop the server server_handle.abort(); @@ -113,9 +106,9 @@ mod tests { } // Similar tests can be written for other API methods - async fn test_encrypt_decrypt() -> anyhow::Result<()> { + async fn test_encrypt_decrypt() -> Result<(), ValvError> { let server_handle = setup_server().await?; - wait_for_server().await?; + wait_for_server().await; let mut client = setup_client().await?; @@ -128,7 +121,7 @@ mod tests { let response = client.create_master_key(request).await.unwrap(); // Assert the response - assert_eq!(response.get_ref().master_key.is_some(), true); + assert!(response.get_ref().master_key.is_some()); // Make the gRPC call to encrypt let encrypt_request = tonic::Request::new(EncryptRequest { @@ -168,7 +161,7 @@ mod tests { let response = client.create_master_key(request).await.unwrap(); // Assert the response - assert_eq!(response.get_ref().master_key.is_some(), true); + assert!(response.get_ref().master_key.is_some()); // Make the gRPC call to decrypt with another master key println!("Decrypting with the wrong key, should fail"); @@ -181,7 +174,7 @@ mod tests { // Assert the decrypt response // This should fail because the ciphertext was encrypted with a different master key - assert_eq!(decrypt_response_another_key.is_err(), true); + assert!(decrypt_response_another_key.is_err()); // Stop the server server_handle.abort(); diff --git a/crates/valv/src/lib.rs b/crates/valv/src/lib.rs index fd552ae..86d7751 100644 --- a/crates/valv/src/lib.rs +++ b/crates/valv/src/lib.rs @@ -1,4 +1,4 @@ -use boring::error::ErrorStack; +use errors::{Result, ValvError}; use gen::valv::internal; use prost::bytes::Buf; use secrecy::{ExposeSecret, Secret}; @@ -6,13 +6,15 @@ use serde::{Deserialize, Serialize}; use storage::{fdb::FoundationDB, interface::ValvStorage}; pub mod api; +pub mod errors; pub mod gen; +mod integration_tests; mod storage; -mod tests; pub mod valv { pub mod proto { pub mod v1 { + #![allow(clippy::unwrap_used)] include!("gen/valv.v1.rs"); } } @@ -38,25 +40,29 @@ pub struct KeyMaterial { #[async_trait::async_trait] pub trait ValvAPI: Send + Sync { - async fn create_key(&self, tenant: String, name: String) -> internal::Key; - async fn get_key(&self, tenant: String, name: String) -> Option; - async fn list_keys(&self, tenant: String) -> Option>; - async fn update_key(&self, tenant: String, key: internal::Key) -> internal::Key; + async fn create_key(&self, tenant: String, name: String) -> Result; + async fn get_key(&self, tenant: String, name: String) -> Result>; + async fn list_keys(&self, tenant: String) -> Result>>; + async fn update_key(&self, tenant: String, key: internal::Key) -> Result; async fn get_key_version( &self, tenant: String, key_name: String, version_id: u32, - ) -> Option; - - async fn encrypt(&self, tenant: String, key_name: String, plaintext: Vec) -> Vec; + ) -> Result>; + async fn encrypt( + &self, + tenant: String, + key_name: String, + plaintext: Vec, + ) -> Result>; async fn decrypt( &self, tenant: String, key_name: String, ciphertext: Vec, - ) -> Result, ErrorStack>; + ) -> Result>; } pub struct Valv { @@ -65,11 +71,11 @@ pub struct Valv { } impl Valv { - pub async fn new() -> Valv { - Valv { - db: FoundationDB::new("local").await.unwrap(), + pub async fn new() -> Result { + Ok(Valv { + db: FoundationDB::new("local").await?, master_key: [0; 32].into(), - } + }) } pub fn set_master_key(&mut self, key: [u8; 32]) { @@ -79,27 +85,33 @@ impl Valv { #[async_trait::async_trait] impl ValvAPI for Valv { - async fn get_key(&self, tenant: String, name: String) -> Option { + async fn get_key(&self, tenant: String, name: String) -> Result> { let key = self .db .get_key_metadata(tenant.as_str(), name.as_str()) - .await - .unwrap(); - - Some(key) + .await; + + match key { + Ok(key) => Ok(Some(key)), + Err(ValvError::KeyNotFound(_)) => Ok(None), + Err(e) => { + println!("Error getting key {name}: {e}"); + return Err(ValvError::Internal(e.to_string())); + } + } } - async fn list_keys(&self, tenant: String) -> Option> { - let keys = self.db.list_key_metadata(tenant.as_str()).await.unwrap(); - Some(keys) + async fn list_keys(&self, tenant: String) -> Result>> { + let keys = self.db.list_key_metadata(tenant.as_str()).await?; + Ok(Some(keys)) } - async fn create_key(&self, tenant: String, name: String) -> internal::Key { + async fn create_key(&self, tenant: String, name: String) -> Result { let mut iv = [0; 12]; let mut key = [0; 32]; let mut tag = [0; 16]; - boring::rand::rand_bytes(&mut iv).unwrap(); - boring::rand::rand_bytes(&mut key).unwrap(); + boring::rand::rand_bytes(&mut iv)?; + boring::rand::rand_bytes(&mut key)?; let encrypted_key = boring::symm::encrypt_aead( boring::symm::Cipher::aes_256_gcm(), @@ -108,8 +120,7 @@ impl ValvAPI for Valv { &[], &key, &mut tag, - ) - .unwrap(); + )?; let mut encrypted_result = Vec::with_capacity(iv.len() + encrypted_key.len() + tag.len()); @@ -120,7 +131,7 @@ impl ValvAPI for Valv { let key = internal::Key { key_id: name.clone(), - primary_version_id: 1.to_string(), + primary_version_id: 1, purpose: "ENCRYPT_DECRYPT".to_string(), creation_time: Some(prost_types::Timestamp { seconds: chrono::Utc::now().timestamp(), @@ -134,8 +145,7 @@ impl ValvAPI for Valv { self.db .update_key_metadata(tenant.as_str(), key.clone()) - .await - .unwrap(); + .await?; let key_version = internal::KeyVersion { key_id: name.clone(), @@ -151,19 +161,17 @@ impl ValvAPI for Valv { self.db .append_key_version(tenant.as_str(), key.clone(), key_version) - .await - .unwrap(); + .await?; - key + Ok(key) } - async fn update_key(&self, tenant: String, key: internal::Key) -> internal::Key { + async fn update_key(&self, tenant: String, key: internal::Key) -> Result { self.db .update_key_metadata(tenant.as_str(), key.clone()) - .await - .unwrap(); + .await?; - key + Ok(key) } async fn get_key_version( @@ -171,30 +179,27 @@ impl ValvAPI for Valv { tenant: String, key_name: String, version_id: u32, - ) -> Option { + ) -> Result> { let key_version = self .db .get_key_version(tenant.as_str(), &key_name, version_id) - .await - .unwrap(); - Some(key_version) + .await?; + + Ok(Some(key_version)) } - async fn encrypt(&self, tenant: String, key_name: String, plaintext: Vec) -> Vec { - let key = self - .db - .get_key_metadata(tenant.as_str(), &key_name) - .await - .unwrap(); + async fn encrypt( + &self, + tenant: String, + key_name: String, + plaintext: Vec, + ) -> Result> { + let key = self.db.get_key_metadata(tenant.as_str(), &key_name).await?; + let key_version = self .db - .get_key_version( - tenant.as_str(), - &key.key_id, - key.primary_version_id.parse().unwrap(), - ) - .await - .unwrap(); + .get_key_version(tenant.as_str(), &key.key_id, key.primary_version_id) + .await?; let (iv, remainder) = key_version.key_material.split_at(12); let (cipher, tag) = remainder.split_at(remainder.len() - 16); @@ -206,11 +211,10 @@ impl ValvAPI for Valv { &[], cipher, tag, - ) - .expect("Failed to decrypt key material"); + )?; let mut iv = [0; 12]; - boring::rand::rand_bytes(&mut iv).unwrap(); + boring::rand::rand_bytes(&mut iv)?; let mut tag = [0; 16]; @@ -221,8 +225,7 @@ impl ValvAPI for Valv { &[], &plaintext, &mut tag, - ) - .unwrap(); + )?; let mut encrypted_result = Vec::with_capacity( 4 + // Key version (u32) @@ -238,7 +241,7 @@ impl ValvAPI for Valv { encrypted_result.extend_from_slice(&encrypted_key); encrypted_result.extend_from_slice(&tag); - encrypted_result + Ok(encrypted_result) } async fn decrypt( @@ -246,22 +249,17 @@ impl ValvAPI for Valv { tenant: String, key_name: String, ciphertext: Vec, - ) -> Result, ErrorStack> { + ) -> Result> { let (key_version_id, remainder) = ciphertext.split_at(4); let (iv, remainder) = remainder.split_at(12); let (cipher, tag) = remainder.split_at(remainder.len() - 16); - let key = self - .db - .get_key_metadata(tenant.as_str(), &key_name) - .await - .unwrap(); + let key = self.db.get_key_metadata(tenant.as_str(), &key_name).await?; let key_version_id = std::io::Cursor::new(key_version_id).get_u32(); let key_version = self .db .get_key_version(tenant.as_str(), &key.key_id, key_version_id) - .await - .unwrap(); + .await?; let (kv_iv, kv_remainder) = key_version.key_material.split_at(12); let (kv_cipher, kv_tag) = kv_remainder.split_at(kv_remainder.len() - 16); @@ -273,16 +271,17 @@ impl ValvAPI for Valv { &[], kv_cipher, kv_tag, - ) - .expect("Failed to decrypt key material"); + )?; - boring::symm::decrypt_aead( + let plaintext = boring::symm::decrypt_aead( boring::symm::Cipher::aes_256_gcm(), &decrypted_key_material, Some(iv), &[], cipher, tag, - ) + )?; + + Ok(plaintext) } } diff --git a/crates/valv/src/storage/fdb.rs b/crates/valv/src/storage/fdb.rs index c42edb2..211fc97 100644 --- a/crates/valv/src/storage/fdb.rs +++ b/crates/valv/src/storage/fdb.rs @@ -1,9 +1,11 @@ -use anyhow::anyhow; use async_trait::async_trait; -use foundationdb::{directory::Directory, tuple::unpack, FdbError, RangeOption}; +use foundationdb::{directory::Directory, tuple::unpack, RangeOption}; use prost::Message; -use crate::gen::valv::internal; +use crate::{ + errors::{Result, ValvError}, + gen::valv::internal, +}; use super::interface::ValvStorage; @@ -13,7 +15,7 @@ pub struct FoundationDB { } impl FoundationDB { - pub async fn new(location: &str) -> Result { + pub async fn new(location: &str) -> Result { Ok(FoundationDB { database: foundationdb::Database::new(None)?, location: location.to_string(), @@ -29,7 +31,7 @@ impl FoundationDB { trx: &foundationdb::Transaction, tenant: &str, key_id: &str, - ) -> Vec { + ) -> Result> { let directory = foundationdb::directory::DirectoryLayer::default(); let path = vec![ @@ -38,7 +40,7 @@ impl FoundationDB { String::from("keys"), ]; - let tenant_subspace = directory + let tenant_subspace = match directory .create_or_open( // the transaction used to read/write the directory. trx, @@ -47,11 +49,17 @@ impl FoundationDB { None, None, ) .await - .expect("could not create directory"); + { + Ok(subspace) => subspace, + Err(e) => return Err(ValvError::DirectoryError(e)), + }; let path = vec![String::from(key_id), String::from("metadata")]; - tenant_subspace.pack(&path).unwrap() + match tenant_subspace.pack(&path) { + Ok(key) => Ok(key), + Err(e) => Err(ValvError::DirectoryError(e)), + } } // Key structure @@ -62,7 +70,7 @@ impl FoundationDB { tenant: &str, key_id: &str, version: u32, - ) -> Vec { + ) -> Result> { let directory = foundationdb::directory::DirectoryLayer::default(); let path = vec![ @@ -71,7 +79,7 @@ impl FoundationDB { String::from("keys"), ]; - let tenant_subspace = directory + let tenant_subspace = match directory .create_or_open( // the transaction used to read/write the directory. trx, @@ -80,7 +88,10 @@ impl FoundationDB { None, None, ) .await - .unwrap(); + { + Ok(subspace) => subspace, + Err(e) => return Err(ValvError::DirectoryError(e)), + }; let path = vec![ String::from(key_id), @@ -88,27 +99,30 @@ impl FoundationDB { version.to_string(), ]; - tenant_subspace.pack(&path).unwrap() + match tenant_subspace.pack(&path) { + Ok(key) => Ok(key), + Err(e) => Err(ValvError::DirectoryError(e)), + } } } #[async_trait] impl ValvStorage for FoundationDB { - async fn get_key_metadata(&self, tenant: &str, key_id: &str) -> anyhow::Result { + async fn get_key_metadata(&self, tenant: &str, key_id: &str) -> Result { let trx = self.database.create_trx()?; - let key = self.get_metadata_fdb_key(&trx, tenant, key_id).await; + let key = self.get_metadata_fdb_key(&trx, tenant, key_id).await?; let key_value = trx.get(&key, false).await?; match key_value { Some(key_value) => { - let key = internal::Key::decode(&key_value[..]).expect("Failed to decode key"); + let key = internal::Key::decode(&key_value[..]).map_err(ValvError::Decode)?; Ok(key) } - None => Err(anyhow!("Key not found")), + None => Err(ValvError::KeyNotFound(key_id.to_string())), } } - async fn list_key_metadata(&self, tenant: &str) -> anyhow::Result> { + async fn list_key_metadata(&self, tenant: &str) -> Result> { let trx = self.database.create_trx()?; let directory = foundationdb::directory::DirectoryLayer::default(); @@ -118,7 +132,7 @@ impl ValvStorage for FoundationDB { String::from("keys"), ]; - let tenant_subspace = directory + let tenant_subspace = match directory .create_or_open( // the transaction used to read/write the directory. &trx, @@ -127,14 +141,17 @@ impl ValvStorage for FoundationDB { None, None, ) .await - .unwrap(); + { + Ok(subspace) => subspace, + Err(e) => return Err(ValvError::DirectoryError(e)), + }; - let range = RangeOption::from(tenant_subspace.range().unwrap()); + let range = match tenant_subspace.range() { + Ok(range) => RangeOption::from(range), + Err(e) => return Err(ValvError::DirectoryError(e)), + }; - let key_values = trx - .get_range(&range, 1_024, false) - .await - .expect("failed to get keys"); + let key_values = trx.get_range(&range, 1_024, false).await?; let mut keys: Vec = vec![]; @@ -145,19 +162,19 @@ impl ValvStorage for FoundationDB { continue; } - let key = internal::Key::decode(key_value.value()).expect("Failed to decode key"); + let key = internal::Key::decode(key_value.value())?; keys.push(key); } Ok(keys) } - async fn update_key_metadata(&self, tenant: &str, key: internal::Key) -> anyhow::Result<()> { + async fn update_key_metadata(&self, tenant: &str, key: internal::Key) -> Result<()> { let trx = self.database.create_trx()?; - let path = self.get_metadata_fdb_key(&trx, tenant, &key.key_id).await; + let path = self.get_metadata_fdb_key(&trx, tenant, &key.key_id).await?; trx.set(&path, &key.encode_to_vec()); - trx.commit().await.unwrap(); + trx.commit().await?; Ok(()) } @@ -167,22 +184,22 @@ impl ValvStorage for FoundationDB { tenant: &str, key_id: &str, version_id: u32, - ) -> anyhow::Result { + ) -> Result { let trx = self.database.create_trx()?; let version_key = self .get_version_fdb_key(&trx, tenant, key_id, version_id) - .await; + .await?; let key_value = trx.get(&version_key, false).await?; match key_value { Some(key_value) => { let version = - internal::KeyVersion::decode(&key_value[..]).expect("Failed to decode key"); + internal::KeyVersion::decode(&key_value[..]).map_err(ValvError::Decode)?; Ok(version) } - None => Err(anyhow!("Key not found")), + None => Err(ValvError::KeyNotFound(key_id.to_string())), } } @@ -190,7 +207,7 @@ impl ValvStorage for FoundationDB { &self, tenant: &str, key_id: &str, - ) -> anyhow::Result> { + ) -> Result> { let trx = self.database.create_trx()?; let directory = foundationdb::directory::DirectoryLayer::default(); @@ -202,7 +219,7 @@ impl ValvStorage for FoundationDB { String::from("versions"), ]; - let versions_directory = directory + let versions_directory = match directory .create_or_open( // the transaction used to read/write the directory. &trx, @@ -211,21 +228,23 @@ impl ValvStorage for FoundationDB { None, None, ) .await - .unwrap(); + { + Ok(subspace) => subspace, + Err(e) => return Err(ValvError::DirectoryError(e)), + }; - let range = RangeOption::from(versions_directory.range().unwrap()); + let range = match versions_directory.range() { + Ok(range) => RangeOption::from(range), + Err(e) => return Err(ValvError::DirectoryError(e)), + }; - let key_values = trx - .get_range(&range, 1_024, false) - .await - .expect("failed to get key versions"); + let key_values = trx.get_range(&range, 1_024, false).await?; let mut key_versions: Vec = vec![]; for key_value in key_values.iter() { - let test: Vec = unpack(key_value.value()).expect("Failed to unpack key value"); - let version = - internal::KeyVersion::decode(&test[..]).expect("Failed to decode key version"); + let test: Vec = unpack(key_value.value()).map_err(ValvError::TuplePacking)?; + let version = internal::KeyVersion::decode(&test[..]).map_err(ValvError::Decode)?; key_versions.push(version); } @@ -237,15 +256,15 @@ impl ValvStorage for FoundationDB { tenant: &str, key: internal::Key, key_version: internal::KeyVersion, - ) -> anyhow::Result<()> { + ) -> Result<()> { let trx = self.database.create_trx()?; let version_key = self .get_version_fdb_key(&trx, tenant, &key.key_id, key_version.version) - .await; + .await?; trx.set(&version_key, &key_version.encode_to_vec()); - trx.commit().await.unwrap(); + trx.commit().await?; Ok(()) } @@ -256,7 +275,7 @@ impl ValvStorage for FoundationDB { _key_id: &str, _version_id: u32, _version: internal::KeyVersion, - ) -> anyhow::Result<()> { + ) -> Result<()> { todo!() } } diff --git a/crates/valv/src/storage/interface.rs b/crates/valv/src/storage/interface.rs index cc5a2c9..b78334c 100644 --- a/crates/valv/src/storage/interface.rs +++ b/crates/valv/src/storage/interface.rs @@ -1,30 +1,30 @@ use async_trait::async_trait; -use crate::gen::valv::internal; +use crate::{errors::Result, gen::valv::internal}; #[async_trait] pub trait ValvStorage { - async fn get_key_metadata(&self, tenant: &str, key_id: &str) -> anyhow::Result; - async fn list_key_metadata(&self, tenant: &str) -> anyhow::Result>; - async fn update_key_metadata(&self, tenant: &str, key: internal::Key) -> anyhow::Result<()>; + async fn get_key_metadata(&self, tenant: &str, key_id: &str) -> Result; + async fn list_key_metadata(&self, tenant: &str) -> Result>; + async fn update_key_metadata(&self, tenant: &str, key: internal::Key) -> Result<()>; async fn get_key_version( &self, tenant: &str, key_id: &str, version_id: u32, - ) -> anyhow::Result; + ) -> Result; #[allow(unused)] async fn get_key_versions( &self, tenant: &str, key_id: &str, - ) -> anyhow::Result>; + ) -> Result>; async fn append_key_version( &self, tenant: &str, key: internal::Key, key_version: internal::KeyVersion, - ) -> anyhow::Result<()>; + ) -> Result<()>; #[allow(unused)] async fn update_key_version( &self, @@ -32,5 +32,5 @@ pub trait ValvStorage { key_id: &str, version_id: u32, version: internal::KeyVersion, - ) -> anyhow::Result<()>; + ) -> Result<()>; } diff --git a/examples/google-kms/.gitignore b/examples/google-kms/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/examples/google-kms/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/examples/google-kms/Cargo.lock b/examples/google-kms/Cargo.lock index c0c40f4..0dc98fa 100644 --- a/examples/google-kms/Cargo.lock +++ b/examples/google-kms/Cargo.lock @@ -1675,7 +1675,6 @@ dependencies = [ name = "valv" version = "0.1.0" dependencies = [ - "anyhow", "async-trait", "bincode", "boring", diff --git a/examples/google-kms/Cargo.toml b/examples/google-kms/Cargo.toml index df062fb..fff6a05 100644 --- a/examples/google-kms/Cargo.toml +++ b/examples/google-kms/Cargo.toml @@ -23,4 +23,21 @@ crc32c = "0.6.8" prost-build = "0.12.3" tonic-build = { version = "0.10.2", features = ["prost"] } -[workspace] \ No newline at end of file +[workspace] + +[lints.clippy] +single_match = "warn" +single_match_else = "warn" +needless_match = "warn" +needless_late_init = "warn" +redundant_pattern_matching = "warn" +redundant_pattern = "warn" +redundant_guards = "warn" +collapsible_match = "warn" +match_single_binding = "warn" +match_same_arms = "warn" +match_ref_pats = "warn" +match_bool = "warn" +needless_bool = "deny" +unwrap_used = "warn" +expect_used = "warn" diff --git a/examples/google-kms/build.rs b/examples/google-kms/build.rs index d798f6f..8ceb26e 100644 --- a/examples/google-kms/build.rs +++ b/examples/google-kms/build.rs @@ -1,6 +1,7 @@ extern crate tonic_build; fn main() { + #[allow(clippy::unwrap_used)] tonic_build::configure() .build_server(true) .compile( @@ -10,7 +11,10 @@ fn main() { )], &[ format!("{}/vendor/googleapis", env!("CARGO_MANIFEST_DIR")), - format!("{}/vendor/googleapis/google/protobuf/timestamp.proto", env!("CARGO_MANIFEST_DIR")), + format!( + "{}/vendor/googleapis/google/protobuf/timestamp.proto", + env!("CARGO_MANIFEST_DIR") + ), ], ) .unwrap(); diff --git a/examples/google-kms/src/tests.rs b/examples/google-kms/src/integration_tests.rs similarity index 96% rename from examples/google-kms/src/tests.rs rename to examples/google-kms/src/integration_tests.rs index e78ede6..95bda04 100644 --- a/examples/google-kms/src/tests.rs +++ b/examples/google-kms/src/integration_tests.rs @@ -1,3 +1,4 @@ +#[allow(clippy::unwrap_used, clippy::expect_used, dead_code)] #[cfg(test)] mod tests { use crate::google::kms::{ @@ -14,8 +15,8 @@ mod tests { ListKeyRingsRequest, ListKeyRingsResponse, ProtectionLevel, UpdateCryptoKeyPrimaryVersionRequest, UpdateCryptoKeyRequest, }; - use std::time::Duration; use crc32c::crc32c; + use std::time::Duration; use tonic::{Request, Response, Status}; use uuid::Uuid; @@ -406,11 +407,16 @@ mod tests { RotationSchedule::RotationPeriod(period) => { let rotation_duration = Duration::from_secs(period.seconds as u64) + Duration::from_nanos(period.nanos as u64); - let last_rotated_time = primary.create_time.as_ref().unwrap(); + let last_rotated_time = primary.create_time.as_ref(); let now = std::time::SystemTime::now(); - let duration_since_creation = now.duration_since( - std::time::UNIX_EPOCH + Duration::from_secs(last_rotated_time.seconds as u64) - ).expect("Time went backwards"); + let duration_since_creation = now + .duration_since( + std::time::UNIX_EPOCH + + Duration::from_secs( + last_rotated_time.unwrap().seconds as u64, + ), + ) + .expect("Time went backwards"); assert!( duration_since_creation <= rotation_duration, @@ -421,10 +427,10 @@ mod tests { } else { panic!("Rotation schedule is not set"); } - }, + } None => {} } - + Ok(()) } @@ -440,7 +446,10 @@ mod tests { .encrypt(key_ring_id, crypto_key_id, plaintext) .await?; assert!(!encrypt_response.get_ref().ciphertext.is_empty()); - assert_eq!(encrypt_response.get_ref().ciphertext_crc32c, Some(crc32c(&encrypt_response.get_ref().ciphertext) as i64)); + assert_eq!( + encrypt_response.get_ref().ciphertext_crc32c, + Some(crc32c(&encrypt_response.get_ref().ciphertext) as i64) + ); // Decrypt let decrypt_response = client @@ -553,13 +562,12 @@ mod tests { .find(|v| { v.state == CryptoKeyVersionState::Enabled as i32 && v.name != create_response.get_ref().primary.as_ref().unwrap().name - }) - .unwrap(); + }); let destroy_response = client .destroy_crypto_key_version( key_ring_id, &crypto_key_id, - version_to_destroy.name.split('/').last().unwrap(), + version_to_destroy.unwrap().name.split('/').last().unwrap(), ) .await?; assert_eq!( diff --git a/examples/google-kms/src/main.rs b/examples/google-kms/src/main.rs index bb19d9b..b2ff3c4 100644 --- a/examples/google-kms/src/main.rs +++ b/examples/google-kms/src/main.rs @@ -5,12 +5,13 @@ pub mod google { } use crc32c::crc32c; -use google::kms::{crypto_key::RotationSchedule, key_management_service_server::KeyManagementService}; -use valv::{gen::valv::internal, ValvAPI}; -use prost_types::Duration; +use google::kms::{ + crypto_key::RotationSchedule, key_management_service_server::KeyManagementService, +}; use tokio::sync::Mutex; +use valv::{gen::valv::internal, ValvAPI}; -mod tests; +mod integration_tests; struct GoogleKMS { pub valv: Mutex, @@ -29,20 +30,36 @@ impl KeyManagementService for GoogleKMS { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let keys = self.valv.lock().await.list_keys(request.into_inner().parent.split('/').last().unwrap().to_string()).await; - - return Ok(tonic::Response::new(google::kms::ListCryptoKeysResponse { - crypto_keys: keys.unwrap_or_default().into_iter().map(|key| { - google::kms::CryptoKey { - name: key.key_id, - purpose: google::kms::crypto_key::CryptoKeyPurpose::EncryptDecrypt as i32, - crypto_key_backend: "valv".to_string(), - ..Default::default() - } - }).collect(), - next_page_token: "".to_string(), - total_size: 0, - })); + let keys = match request.into_inner().parent.split('/').last() { + Some(key_id) => self.valv.lock().await.list_keys(key_id.to_string()).await, + None => { + return Err(tonic::Status::invalid_argument("key_id malformated")); + } + }; + + match keys { + Ok(keys) => { + return Ok(tonic::Response::new(google::kms::ListCryptoKeysResponse { + crypto_keys: keys + .unwrap_or_default() + .into_iter() + .map(|key| google::kms::CryptoKey { + name: key.key_id, + purpose: google::kms::crypto_key::CryptoKeyPurpose::EncryptDecrypt + as i32, + crypto_key_backend: "valv".to_string(), + ..Default::default() + }) + .collect(), + next_page_token: "".to_string(), + total_size: 0, + })); + } + Err(e) => { + println!("error: {}", e); + return Err(tonic::Status::internal(e.to_string())); + } + } } async fn list_crypto_key_versions( @@ -71,27 +88,57 @@ impl KeyManagementService for GoogleKMS { request: tonic::Request, ) -> Result, tonic::Status> { let request = request.into_inner(); - - let tenant_name = request.name.split('/').nth(5).unwrap().to_string(); - let key_name = request.name.split('/').last().unwrap().to_string(); - let key = self.valv.lock().await.get_key( - tenant_name.clone(), - key_name.clone(), - ).await; + let tenant_name = match request.name.split('/').nth(5) { + Some(tenant_name) => tenant_name.to_string(), + None => { + return Err(tonic::Status::invalid_argument("tenant_name malformated")); + } + }; + let key_name = match request.name.split('/').last() { + Some(key_name) => key_name.to_string(), + None => { + return Err(tonic::Status::invalid_argument("key_name malformated")); + } + }; + + let key = self + .valv + .lock() + .await + .get_key(tenant_name.clone(), key_name.clone()) + .await; match key { - Some(key) => { - let primary = self.valv.lock().await.get_key_version(tenant_name, key_name, key.primary_version_id.parse().unwrap()).await; - - return Ok(tonic::Response::new(valv_key_to_google_key(key, primary.unwrap()))); + Ok(Some(key)) => { + let primary = self + .valv + .lock() + .await + .get_key_version(tenant_name, key_name, key.primary_version_id) + .await; + + match primary { + Ok(Some(primary)) => { + return Ok(tonic::Response::new(valv_key_to_google_key(key, primary))); + } + Ok(None) => { + return Err(tonic::Status::not_found("primary key version not found")); + } + Err(e) => { + println!("error: {}", e); + return Err(tonic::Status::internal(e.to_string())); + } + } } - None => { + Ok(None) => { return Err(tonic::Status::not_found("key not found")); } + Err(e) => { + println!("error: {}", e); + return Err(tonic::Status::internal(e.to_string())); + } } - - } async fn get_crypto_key_version( @@ -127,24 +174,41 @@ impl KeyManagementService for GoogleKMS { request: tonic::Request, ) -> Result, tonic::Status> { let request = request.into_inner(); - - let tenant_name = request.parent.split('/').nth(5).unwrap().to_string(); + + let tenant_name = match request.parent.split('/').nth(5) { + Some(tenant_name) => tenant_name.to_string(), + None => { + return Err(tonic::Status::invalid_argument("tenant_name malformated")); + } + }; let key_name = request.crypto_key_id; let valv = self.valv.lock().await; - let key = valv - .create_key( - tenant_name.clone(), - key_name.clone(), - ) - .await; + let key = valv.create_key(tenant_name.clone(), key_name.clone()).await; - let primary = valv.get_key_version(tenant_name, key_name, key.primary_version_id.parse().unwrap()).await; - if primary.is_none() { - return Err(tonic::Status::not_found("primary key version not found")); + match key { + Ok(key) => { + let primary = valv + .get_key_version(tenant_name, key_name, key.primary_version_id) + .await; + match primary { + Ok(Some(primary)) => { + return Ok(tonic::Response::new(valv_key_to_google_key(key, primary))); + } + Ok(None) => { + return Err(tonic::Status::not_found("primary key version not found")); + } + Err(e) => { + println!("error: {}", e); + return Err(tonic::Status::internal(e.to_string())); + } + } + } + Err(e) => { + println!("error: {}", e); + return Err(tonic::Status::internal(e.to_string())); + } } - - return Ok(tonic::Response::new(valv_key_to_google_key(key, primary.unwrap()))); } async fn create_crypto_key_version( @@ -174,40 +238,78 @@ impl KeyManagementService for GoogleKMS { ) -> Result, tonic::Status> { let request = request.into_inner(); - if request.crypto_key.is_none() { - return Err(tonic::Status::invalid_argument("crypto_key is required")); - } - - let crypto_key = request.crypto_key.unwrap(); - - let tenant_name = crypto_key.name.split('/').nth(5).unwrap().to_string(); - let key_name = crypto_key.name.split('/').last().unwrap().to_string(); + let crypto_key = match request.crypto_key { + Some(crypto_key) => crypto_key, + None => { + return Err(tonic::Status::invalid_argument("crypto_key is required")); + } + }; + let tenant_name = match crypto_key.name.split('/').nth(5) { + Some(tenant_name) => tenant_name.to_string(), + None => { + return Err(tonic::Status::invalid_argument("tenant_name malformated")); + } + }; + let key_name = match crypto_key.name.split('/').last() { + Some(key_name) => key_name.to_string(), + None => { + return Err(tonic::Status::invalid_argument("key_name malformated")); + } + }; let valv = self.valv.lock().await; let key = valv.get_key(tenant_name.clone(), key_name.clone()).await; - let mut key = if key.is_none() { - return Err(tonic::Status::not_found("key not found")); - } else { - key.unwrap() + let mut key = match key { + Ok(key) => match key { + Some(key) => key, + None => { + return Err(tonic::Status::not_found("key not found")); + } + }, + Err(e) => { + println!("error: {}", e); + return Err(tonic::Status::internal(e.to_string())); + } }; - let rotation_schedule: RotationSchedule = crypto_key.rotation_schedule.unwrap(); - - let rotation_period = match rotation_schedule { - RotationSchedule::RotationPeriod(period) => period, + let rotation_schedule = match crypto_key.rotation_schedule { + Some(rotation_schedule) => rotation_schedule, + None => { + return Err(tonic::Status::invalid_argument( + "rotation_schedule is required", + )); + } }; + let RotationSchedule::RotationPeriod(rotation_period) = rotation_schedule; + key.rotation_schedule = Some(rotation_period.clone()); - let key = valv.update_key(tenant_name.clone(), key).await; + let key = match valv.update_key(tenant_name.clone(), key).await { + Ok(key) => key, + Err(e) => { + println!("error: {}", e); + return Err(tonic::Status::internal(e.to_string())); + } + }; - let primary = valv.get_key_version(tenant_name, key_name, key.primary_version_id.parse().unwrap()).await; - if primary.is_none() { - return Err(tonic::Status::not_found("primary key version not found")); - } + let primary = valv + .get_key_version(tenant_name, key_name, key.primary_version_id) + .await; - return Ok(tonic::Response::new(valv_key_to_google_key(key, primary.unwrap()))); + match primary { + Ok(Some(primary)) => { + return Ok(tonic::Response::new(valv_key_to_google_key(key, primary))); + } + Ok(None) => { + return Err(tonic::Status::not_found("primary key version not found")); + } + Err(e) => { + println!("error: {}", e); + return Err(tonic::Status::internal(e.to_string())); + } + } } async fn update_crypto_key_version( @@ -223,17 +325,39 @@ impl KeyManagementService for GoogleKMS { ) -> Result, tonic::Status> { let request = request.into_inner(); - let tenant_name = request.name.split('/').nth(5).unwrap().to_string(); - let key_name = request.name.split('/').last().unwrap().to_string(); + let tenant_name = match request.name.split('/').nth(5) { + Some(tenant_name) => tenant_name.to_string(), + None => { + return Err(tonic::Status::invalid_argument("tenant_name malformated")); + } + }; + let key_name = match request.name.split('/').last() { + Some(key_name) => key_name.to_string(), + None => { + return Err(tonic::Status::invalid_argument("key_name malformated")); + } + }; - let ciphertext = self.valv.lock().await.encrypt(tenant_name, key_name, request.plaintext).await; + let ciphertext = self + .valv + .lock() + .await + .encrypt(tenant_name, key_name, request.plaintext) + .await; - return Ok(tonic::Response::new(google::kms::EncryptResponse { - name: 0.to_string(), - ciphertext: ciphertext.clone(), - ciphertext_crc32c: Some(crc32c(&ciphertext) as i64), - ..Default::default() - })); + match ciphertext { + Ok(ciphertext) => { + return Ok(tonic::Response::new(google::kms::EncryptResponse { + name: 0.to_string(), + ciphertext: ciphertext.clone(), + ciphertext_crc32c: Some(crc32c(&ciphertext) as i64), + ..Default::default() + })); + } + Err(e) => { + return Err(tonic::Status::internal(e.to_string())); + } + } } async fn decrypt( @@ -242,10 +366,25 @@ impl KeyManagementService for GoogleKMS { ) -> Result, tonic::Status> { let request = request.into_inner(); - let tenant_name = request.name.split('/').nth(5).unwrap().to_string(); - let key_name = request.name.split('/').last().unwrap().to_string(); + let tenant_name = match request.name.split('/').nth(5) { + Some(tenant_name) => tenant_name.to_string(), + None => { + return Err(tonic::Status::invalid_argument("tenant_name malformated")); + } + }; + let key_name = match request.name.split('/').last() { + Some(key_name) => key_name.to_string(), + None => { + return Err(tonic::Status::invalid_argument("key_name malformated")); + } + }; - let result = self.valv.lock().await.decrypt(tenant_name, key_name, request.ciphertext).await; + let result = self + .valv + .lock() + .await + .decrypt(tenant_name, key_name, request.ciphertext) + .await; match result { Ok(plaintext) => { @@ -332,43 +471,56 @@ impl KeyManagementService for GoogleKMS { } } -fn valv_key_to_google_key(key: internal::Key, primary: internal::KeyVersion) -> google::kms::CryptoKey { - let next_rotation_time = Some(prost_types::Timestamp { - seconds: primary.creation_time.clone().unwrap().seconds + key.rotation_schedule.as_ref().unwrap().seconds, - nanos: primary.creation_time.clone().unwrap().nanos + key.rotation_schedule.as_ref().unwrap().nanos, - }); - - google::kms::CryptoKey { +fn valv_key_to_google_key( + key: internal::Key, + primary: internal::KeyVersion, +) -> google::kms::CryptoKey { + let mut crypto_key = google::kms::CryptoKey { name: key.key_id, purpose: google::kms::crypto_key::CryptoKeyPurpose::EncryptDecrypt as i32, crypto_key_backend: "valv".to_string(), - rotation_schedule: key.rotation_schedule.map(|rotation_schedule| { - RotationSchedule::RotationPeriod(rotation_schedule) - }), - next_rotation_time: next_rotation_time, + rotation_schedule: key + .rotation_schedule + .as_ref() + .map(|rotation_schedule| RotationSchedule::RotationPeriod(rotation_schedule.clone())), primary: Some(google::kms::CryptoKeyVersion { name: primary.version.to_string(), state: google::kms::crypto_key_version::CryptoKeyVersionState::Enabled as i32, - create_time: Some(prost_types::Timestamp { - seconds: primary.creation_time.clone().unwrap().seconds, - nanos: primary.creation_time.unwrap().nanos, - }), + ..Default::default() }), ..Default::default() + }; + if let Some(creation_time) = primary.creation_time { + crypto_key.create_time = Some(prost_types::Timestamp { + seconds: creation_time.seconds, + nanos: creation_time.nanos, + }); + + if let Some(rotation_schedule) = key.rotation_schedule.as_ref() { + let next_rotation_time = Some(prost_types::Timestamp { + seconds: creation_time.seconds + rotation_schedule.seconds, + nanos: creation_time.nanos + rotation_schedule.nanos, + }); + + crypto_key.next_rotation_time = next_rotation_time; + } } + + crypto_key } #[tokio::main] async fn main() { - let addr = "[::1]:50051".parse().unwrap(); + #![allow(clippy::expect_used)] + let addr = "[::1]:50051".parse().expect("failed to parse address"); let _guard = unsafe { foundationdb::boot() }; let mut key = [0; 32]; - boring::rand::rand_bytes(&mut key).unwrap(); + boring::rand::rand_bytes(&mut key).expect("failed to generate random key"); - let mut store = valv::Valv::new().await; + let mut store = valv::Valv::new().await.expect("failed to create valv"); store.set_master_key(key); let api = GoogleKMS { @@ -381,5 +533,5 @@ async fn main() { ) .serve(addr) .await - .unwrap(); + .expect("failed to start grpc server"); }