Skip to content

Commit ae1ced1

Browse files
CodingAnarchyclaude
andcommitted
release: Version 1.14.0 with Azure Key Vault integration and KMS consolidation
- Add Azure Key Vault integration with real Azure SDK authentication - Consolidate duplicate KMS fallback implementations into shared utilities - Enhanced encryption documentation with comprehensive doc tests - Fix compilation issues and update test compatibility - Breaking change: Remove deprecated rotate_key_if_needed() method 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 7207964 commit ae1ced1

File tree

6 files changed

+98
-190
lines changed

6 files changed

+98
-190
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.14.0] - 2025-07-17
11+
1012
### Added
1113
- **🔐 Azure Key Vault Integration**
1214
- Added real Azure Key Vault integration with `azure-kv` feature flag

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ members = [
99
resolver = "2"
1010

1111
[workspace.package]
12-
version = "1.13.1"
12+
version = "1.14.0"
1313
edition = "2024"
1414
license = "MIT"
1515
repository = "https://github.com/CodingAnarchy/hammerwork"
@@ -19,7 +19,7 @@ documentation = "https://docs.rs/hammerwork"
1919
rust-version = "1.86"
2020

2121
[workspace.dependencies]
22-
hammerwork = { version = "1.13.1", path = "." }
22+
hammerwork = { version = "1.14.0", path = "." }
2323
tokio = { version = "1.0", features = ["full"] }
2424
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "chrono", "uuid", "json"] }
2525
chrono = { version = "0.4", features = ["serde"] }

src/encryption.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,7 @@ impl EncryptionStats {
969969
/// ```
970970
pub fn generate_deterministic_key(service_prefix: &str, inputs: &[&str]) -> Vec<u8> {
971971
use sha2::{Digest, Sha256};
972-
972+
973973
let mut hasher = Sha256::new();
974974
hasher.update(service_prefix.as_bytes());
975975
for input in inputs {
@@ -1010,16 +1010,20 @@ pub fn generate_deterministic_key(service_prefix: &str, inputs: &[&str]) -> Vec<
10101010
/// assert_eq!(key.len(), 16);
10111011
/// # }
10121012
/// ```
1013-
pub fn generate_deterministic_key_with_size(service_prefix: &str, inputs: &[&str], key_size: usize) -> Vec<u8> {
1013+
pub fn generate_deterministic_key_with_size(
1014+
service_prefix: &str,
1015+
inputs: &[&str],
1016+
key_size: usize,
1017+
) -> Vec<u8> {
10141018
use sha2::{Digest, Sha256};
1015-
1019+
10161020
let mut hasher = Sha256::new();
10171021
hasher.update(service_prefix.as_bytes());
10181022
for input in inputs {
10191023
hasher.update(input.as_bytes());
10201024
}
10211025
let hash = hasher.finalize();
1022-
1026+
10231027
// Generate key of the requested size by repeating the hash pattern
10241028
let mut key = vec![0u8; key_size];
10251029
let hash_bytes = hash.as_slice();

src/encryption/engine.rs

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
//! This module provides the actual cryptographic operations for encrypting and
44
//! decrypting job payloads using various algorithms and key management strategies.
55
6+
#[cfg(feature = "encryption")]
7+
use super::key_manager::KeyManager;
68
use super::{
79
EncryptedPayload, EncryptionAlgorithm, EncryptionConfig, EncryptionError, EncryptionMetadata,
810
EncryptionStats, KeySource, RetentionPolicy,
911
};
10-
#[cfg(feature = "encryption")]
11-
use super::key_manager::KeyManager;
1212
use serde_json::Value;
1313
use sqlx::Database;
1414
use std::collections::HashMap;
@@ -72,7 +72,7 @@ pub struct EncryptionEngine<DB: Database> {
7272
key_manager: Option<Arc<Mutex<KeyManager<DB>>>>,
7373
}
7474

75-
impl<DB: Database> EncryptionEngine<DB>
75+
impl<DB: Database> EncryptionEngine<DB>
7676
where
7777
for<'c> &'c mut DB::Connection: sqlx::Executor<'c, Database = DB>,
7878
for<'q> <DB as Database>::Arguments<'q>: sqlx::IntoArguments<'q, DB>,
@@ -547,8 +547,6 @@ where
547547
pub fn set_key_manager(&mut self, key_manager: Arc<Mutex<KeyManager<DB>>>) {
548548
self.key_manager = Some(key_manager);
549549
}
550-
551-
552550

553551
/// Cleans up expired encrypted data based on retention policies.
554552
///
@@ -670,7 +668,7 @@ where
670668
let key = super::generate_deterministic_key_with_size(
671669
"aws-kms-data-key",
672670
&[key_id, region],
673-
expected_size
671+
expected_size,
674672
);
675673

676674
Ok(key)
@@ -836,7 +834,7 @@ where
836834
let key = super::generate_deterministic_key_with_size(
837835
"vault-data-key",
838836
&[secret_path, &vault_addr],
839-
expected_size
837+
expected_size,
840838
);
841839

842840
Ok(key)
@@ -934,7 +932,7 @@ where
934932
let key = super::generate_deterministic_key_with_size(
935933
"gcp-kms-data-key",
936934
&[key_resource],
937-
expected_size
935+
expected_size,
938936
);
939937

940938
Ok(key)
@@ -970,15 +968,25 @@ where
970968
{
971969
use azure_identity::{DefaultAzureCredential, TokenCredentialOptions};
972970
use azure_security_keyvault::KeyvaultClient;
973-
971+
974972
// Create Azure credentials using default credential chain
975973
let credential = DefaultAzureCredential::create(TokenCredentialOptions::default())
976-
.map_err(|e| EncryptionError::KeyManagement(format!("Failed to create Azure credentials: {}", e)))?;
977-
974+
.map_err(|e| {
975+
EncryptionError::KeyManagement(format!(
976+
"Failed to create Azure credentials: {}",
977+
e
978+
))
979+
})?;
980+
978981
// Create Key Vault client
979-
let client = KeyvaultClient::new(&vault_url, std::sync::Arc::new(credential))
980-
.map_err(|e| EncryptionError::KeyManagement(format!("Failed to create Azure Key Vault client: {}", e)))?;
981-
982+
let client =
983+
KeyvaultClient::new(&vault_url, std::sync::Arc::new(credential)).map_err(|e| {
984+
EncryptionError::KeyManagement(format!(
985+
"Failed to create Azure Key Vault client: {}",
986+
e
987+
))
988+
})?;
989+
982990
// Retrieve the key from the vault
983991
match client.key_client().get(key_name.to_string()).await {
984992
Ok(key_response) => {
@@ -987,8 +995,13 @@ where
987995
// Decode the base64url encoded key material
988996
let decoded_key = base64::engine::general_purpose::URL_SAFE_NO_PAD
989997
.decode(key_material)
990-
.map_err(|e| EncryptionError::KeyManagement(format!("Failed to decode key material: {}", e)))?;
991-
998+
.map_err(|e| {
999+
EncryptionError::KeyManagement(format!(
1000+
"Failed to decode key material: {}",
1001+
e
1002+
))
1003+
})?;
1004+
9921005
// Ensure the key is the expected size
9931006
if decoded_key.len() >= expected_size {
9941007
info!("Successfully loaded encryption key from Azure Key Vault");
@@ -998,31 +1011,38 @@ where
9981011
let mut final_key = vec![0u8; expected_size];
9991012
let copy_len = std::cmp::min(decoded_key.len(), expected_size);
10001013
final_key[..copy_len].copy_from_slice(&decoded_key[..copy_len]);
1001-
1014+
10021015
// Pad remaining bytes with HKDF-derived material
10031016
if decoded_key.len() < expected_size {
10041017
use hmac::{Hmac, Mac};
10051018
use sha2::Sha256;
10061019
type HmacSha256 = Hmac<Sha256>;
1007-
1020+
10081021
let mut mac = <HmacSha256 as Mac>::new_from_slice(&decoded_key)
1009-
.map_err(|e| EncryptionError::KeyManagement(format!("HMAC creation failed: {}", e)))?;
1022+
.map_err(|e| {
1023+
EncryptionError::KeyManagement(format!(
1024+
"HMAC creation failed: {}",
1025+
e
1026+
))
1027+
})?;
10101028
mac.update(b"azure-kv-key-derivation");
10111029
mac.update(vault_url.as_bytes());
10121030
mac.update(key_name.as_bytes());
10131031
let derived = mac.finalize().into_bytes();
1014-
1032+
10151033
for i in decoded_key.len()..expected_size {
10161034
final_key[i] = derived[i % derived.len()];
10171035
}
10181036
}
1019-
1020-
info!("Successfully loaded and padded encryption key from Azure Key Vault");
1037+
1038+
info!(
1039+
"Successfully loaded and padded encryption key from Azure Key Vault"
1040+
);
10211041
return Ok(final_key);
10221042
}
10231043
} else {
10241044
return Err(EncryptionError::KeyManagement(
1025-
"Azure Key Vault key response missing key material".to_string()
1045+
"Azure Key Vault key response missing key material".to_string(),
10261046
));
10271047
}
10281048
}
@@ -1035,14 +1055,16 @@ where
10351055

10361056
#[cfg(not(feature = "azure-kv"))]
10371057
{
1038-
warn!("Azure Key Vault feature not enabled, falling back to deterministic key generation");
1058+
warn!(
1059+
"Azure Key Vault feature not enabled, falling back to deterministic key generation"
1060+
);
10391061
}
10401062

10411063
// Fallback to deterministic key generation for development/testing
10421064
let key = super::generate_deterministic_key_with_size(
10431065
"azure-kv-data-key",
10441066
&[&vault_url, key_name],
1045-
expected_size
1067+
expected_size,
10461068
);
10471069

10481070
info!("Using deterministic key generation for Azure Key Vault (fallback)");
@@ -1600,10 +1622,6 @@ mod tests {
16001622
assert_eq!(expired_count, 1);
16011623
}
16021624

1603-
1604-
1605-
1606-
16071625
#[cfg(feature = "encryption")]
16081626
#[tokio::test]
16091627
async fn test_set_key_manager() {
@@ -1620,7 +1638,7 @@ mod tests {
16201638
let config = EncryptionConfig::new(EncryptionAlgorithm::AES256GCM)
16211639
.with_key_source(KeySource::Environment("TEST_ENCRYPTION_KEY".to_string()));
16221640

1623-
let mut engine = EncryptionEngine::new(config).await.unwrap();
1641+
let engine = EncryptionEngine::new(config).await.unwrap();
16241642

16251643
// Initially no key manager
16261644
assert!(engine.key_manager.is_none());
@@ -1633,5 +1651,4 @@ mod tests {
16331651
// 4. Test that key rotation uses the key manager
16341652
assert!(engine.key_manager.is_none());
16351653
}
1636-
16371654
}

0 commit comments

Comments
 (0)