Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/services/aws_kms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ use aws_sdk_kms::{
types::{MessageType, SigningAlgorithmSpec},
Client,
};
use once_cell::sync::Lazy;
use serde::Serialize;
use std::collections::HashMap;
use tokio::sync::RwLock;

use crate::{
models::{Address, AwsKmsSignerConfig},
Expand Down Expand Up @@ -112,6 +115,10 @@ mock! {

}

// Global cache - HashMap keyed by kms_key_id
static KMS_DER_PK_CACHE: Lazy<RwLock<HashMap<String, Vec<u8>>>> =
Lazy::new(|| RwLock::new(HashMap::new()));

#[derive(Debug, Clone)]
pub struct AwsKmsClient {
inner: Client,
Expand All @@ -120,6 +127,16 @@ pub struct AwsKmsClient {
#[async_trait]
impl AwsKmsK256 for AwsKmsClient {
async fn get_der_public_key<'a, 'b>(&'a self, key_id: &'b str) -> AwsKmsResult<Vec<u8>> {
// Try cache first with minimal lock time
let cached = {
let cache_read = KMS_DER_PK_CACHE.read().await;
cache_read.get(key_id).cloned()
};
if let Some(cached) = cached {
return Ok(cached);
}

// Fetch from AWS KMS
let get_output = self
.inner
.get_public_key()
Expand All @@ -135,6 +152,11 @@ impl AwsKmsK256 for AwsKmsClient {
))?
.into_inner();

// Cache the result
let mut cache_write = KMS_DER_PK_CACHE.write().await;
cache_write.insert(key_id.to_string(), der_pk_blob.clone());
drop(cache_write);

Ok(der_pk_blob)
}

Expand Down Expand Up @@ -216,9 +238,13 @@ impl<T: AwsKmsK256 + Clone> AwsKmsService<T> {
let der_signature = self.client.sign_digest(&self.kms_key_id, digest).await?;

// Parse DER into Secp256k1 format
let rs = k256::ecdsa::Signature::from_der(&der_signature)
let mut rs = k256::ecdsa::Signature::from_der(&der_signature)
.map_err(|e| AwsKmsError::ParseError(e.to_string()))?;

// Normalize to low-s if necessary
if let Some(normalized) = rs.normalize_s() {
rs = normalized;
}
let der_pk = self.client.get_der_public_key(&self.kms_key_id).await?;

// Extract public key from AWS KMS and convert it to an uncompressed 64 pk
Expand Down
18 changes: 10 additions & 8 deletions src/utils/secp256k.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ pub fn recover_public_key(pk: &[u8], sig: &Signature, bytes: &[u8]) -> Result<u8
let mut hasher = Keccak256::new();
hasher.update(bytes);
for v in 0..2 {
let rec_id =
RecoveryId::try_from(v).map_err(|e| Secp256k1Error::RecoveryError(e.to_string()))?;

let recovered_key = VerifyingKey::recover_from_digest(hasher.clone(), sig, rec_id)
.map_err(|e| Secp256k1Error::RecoveryError(e.to_string()))?
.to_encoded_point(false)
.as_bytes()
.to_vec();
let rec_id = match RecoveryId::try_from(v) {
Ok(id) => id,
Err(_) => continue,
};

let recovered_key = match VerifyingKey::recover_from_digest(hasher.clone(), sig, rec_id) {
Ok(key) => key.to_encoded_point(false).as_bytes().to_vec(),
Err(_) => {
continue;
}
};
if recovered_key[1..] == pk[..] {
return Ok(v);
}
Expand Down
Loading