44//! - Secure key generation and storage
55//! - Key rotation and lifecycle management
66//! - Master key encryption (Key Encryption Keys)
7- //! - External key management service integration
7+ //! - External key management service integration (AWS KMS, Azure Key Vault, GCP KMS, HashiCorp Vault)
8+ //! - Azure Key Vault integration for master key retrieval with automatic credential resolution
89//! - Audit trails and key usage tracking
910//!
1011//! # Security Considerations
9293//! # }
9394//! # }
9495//! ```
96+ //!
97+ //! ## Azure Key Vault Master Key Integration
98+ //!
99+ //! ```rust,no_run
100+ //! # #[cfg(all(feature = "encryption", feature = "azure-kv"))]
101+ //! # {
102+ //! use hammerwork::encryption::{KeyManager, KeyManagerConfig, KeySource};
103+ //! use sqlx::postgres::PgPool;
104+ //! use std::env;
105+ //!
106+ //! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
107+ //! # let database_url = "postgres://user:pass@localhost/hammerwork";
108+ //! # let pool = sqlx::PgPool::connect(database_url).await?;
109+ //! // Set up Azure credentials via environment variables
110+ //! env::set_var("AZURE_CLIENT_ID", "your-client-id");
111+ //! env::set_var("AZURE_CLIENT_SECRET", "your-client-secret");
112+ //! env::set_var("AZURE_TENANT_ID", "your-tenant-id");
113+ //!
114+ //! // Configure key manager with Azure Key Vault for master key
115+ //! let config = KeyManagerConfig::new()
116+ //! .with_master_key_source(KeySource::External(
117+ //! "azure://my-vault.vault.azure.net/keys/master-key".to_string()
118+ //! ))
119+ //! .with_auto_rotation_enabled(true);
120+ //!
121+ //! let mut key_manager = KeyManager::new(config, pool).await?;
122+ //!
123+ //! // Master key is automatically loaded from Azure Key Vault
124+ //! // If Azure Key Vault is unavailable, falls back to deterministic generation
125+ //! let key_id = key_manager.generate_key("payment-key",
126+ //! hammerwork::encryption::EncryptionAlgorithm::AES256GCM).await?;
127+ //!
128+ //! println!("Generated key with Azure Key Vault master key: {}", key_id);
129+ //! # Ok(())
130+ //! # }
131+ //! # }
132+ //! ```
95133
96134use super :: { EncryptionAlgorithm , EncryptionError , KeySource } ;
97135use chrono:: { DateTime , Duration , Utc } ;
@@ -1756,6 +1794,38 @@ where
17561794 hash[ 0 ..32 ] . to_vec ( )
17571795 }
17581796
1797+ /// Load master key from Azure Key Vault
1798+ ///
1799+ /// This method attempts to retrieve the master key from Azure Key Vault using the
1800+ /// configured service URL and key name. If Azure Key Vault is not available or
1801+ /// the `azure-kv` feature is not enabled, it falls back to deterministic key generation.
1802+ ///
1803+ /// # Arguments
1804+ ///
1805+ /// * `service_config` - Azure Key Vault configuration string in format "azure://vault-name/path/key-name"
1806+ ///
1807+ /// # Returns
1808+ ///
1809+ /// A 32-byte master key suitable for AES-256 encryption
1810+ ///
1811+ /// # Examples
1812+ ///
1813+ /// ```rust,no_run
1814+ /// # #[cfg(feature = "encryption")]
1815+ /// # {
1816+ /// use hammerwork::encryption::key_manager::KeyManager;
1817+ ///
1818+ /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1819+ /// // Load master key from Azure Key Vault
1820+ /// let master_key = KeyManager::<sqlx::Postgres>::load_master_key_from_azure(
1821+ /// "azure://my-vault.vault.azure.net/keys/master-key"
1822+ /// ).await;
1823+ ///
1824+ /// assert_eq!(master_key.len(), 32); // AES-256 key size
1825+ /// # Ok(())
1826+ /// # }
1827+ /// # }
1828+ /// ```
17591829 #[ cfg( feature = "encryption" ) ]
17601830 async fn load_master_key_from_azure ( service_config : & str ) -> Vec < u8 > {
17611831 // Parse Azure Key Vault configuration for master key
@@ -1778,12 +1848,25 @@ where
17781848 vault_url, key_name
17791849 ) ;
17801850
1781- // In a real implementation, this would:
1782- // 1. Create Azure Key Vault client with authentication
1783- // 2. Retrieve the master key from the vault
1784- // 3. Decrypt and return the master key material
1851+ // Try to load from Azure Key Vault if the feature is enabled
1852+ #[ cfg( feature = "azure-kv" ) ]
1853+ {
1854+ use azure_identity:: { DefaultAzureCredential , TokenCredentialOptions } ;
1855+ use azure_security_keyvault:: KeyvaultClient ;
1856+
1857+ match Self :: load_from_azure_key_vault ( & vault_url, key_name) . await {
1858+ Ok ( key_material) => {
1859+ info ! ( "Successfully loaded master key from Azure Key Vault" ) ;
1860+ return key_material;
1861+ }
1862+ Err ( e) => {
1863+ warn ! ( "Failed to load master key from Azure Key Vault: {}" , e) ;
1864+ info ! ( "Falling back to deterministic key generation" ) ;
1865+ }
1866+ }
1867+ }
17851868
1786- // For now, generate a deterministic key based on the configuration
1869+ // Fallback to deterministic key generation for development/testing
17871870 use sha2:: { Digest , Sha256 } ;
17881871 let mut hasher = Sha256 :: new ( ) ;
17891872 hasher. update ( b"azure-kv-master-key" ) ;
@@ -1793,6 +1876,95 @@ where
17931876 hash[ 0 ..32 ] . to_vec ( )
17941877 }
17951878
1879+ /// Load key material directly from Azure Key Vault
1880+ ///
1881+ /// This method uses the Azure SDK to authenticate and retrieve key material from
1882+ /// Azure Key Vault. It supports automatic credential resolution through
1883+ /// `DefaultAzureCredential` and handles key size normalization.
1884+ ///
1885+ /// # Arguments
1886+ ///
1887+ /// * `vault_url` - Full URL to the Azure Key Vault (e.g., "https://my-vault.vault.azure.net")
1888+ /// * `key_name` - Name of the key to retrieve from the vault
1889+ ///
1890+ /// # Returns
1891+ ///
1892+ /// A 32-byte key material suitable for AES-256 encryption, or an error message
1893+ ///
1894+ /// # Security Features
1895+ ///
1896+ /// - Uses `DefaultAzureCredential` for secure authentication
1897+ /// - Automatically handles key size normalization via HMAC-based key derivation
1898+ /// - Supports base64-encoded key material from Azure Key Vault
1899+ /// - Includes proper error handling for authentication and network issues
1900+ ///
1901+ /// # Examples
1902+ ///
1903+ /// ```rust,no_run
1904+ /// # #[cfg(all(feature = "encryption", feature = "azure-kv"))]
1905+ /// # {
1906+ /// use hammerwork::encryption::key_manager::KeyManager;
1907+ ///
1908+ /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1909+ /// // Load key from Azure Key Vault
1910+ /// let key_material = KeyManager::<sqlx::Postgres>::load_from_azure_key_vault(
1911+ /// "https://my-vault.vault.azure.net",
1912+ /// "master-key"
1913+ /// ).await?;
1914+ ///
1915+ /// assert_eq!(key_material.len(), 32); // Always 32 bytes for AES-256
1916+ /// # Ok(())
1917+ /// # }
1918+ /// # }
1919+ /// ```
1920+ #[ cfg( all( feature = "encryption" , feature = "azure-kv" ) ) ]
1921+ async fn load_from_azure_key_vault ( vault_url : & str , key_name : & str ) -> Result < Vec < u8 > , String > {
1922+ use azure_identity:: { DefaultAzureCredential , TokenCredentialOptions } ;
1923+ use azure_security_keyvault:: KeyvaultClient ;
1924+
1925+ // Create Azure credentials
1926+ let credential = DefaultAzureCredential :: create ( TokenCredentialOptions :: default ( ) )
1927+ . map_err ( |e| format ! ( "Failed to create Azure credentials: {}" , e) ) ?;
1928+
1929+ // Create Azure Key Vault client
1930+ let client = KeyvaultClient :: new ( vault_url, std:: sync:: Arc :: new ( credential) )
1931+ . map_err ( |e| format ! ( "Failed to create Azure Key Vault client: {}" , e) ) ?;
1932+
1933+ // Retrieve the master key from Azure Key Vault
1934+ match client. key_client ( ) . get ( key_name. to_string ( ) ) . await {
1935+ Ok ( key_response) => {
1936+ if let Some ( key_material) = key_response. key . k {
1937+ // Decode the base64-encoded key material
1938+ let decoded_key = base64:: engine:: general_purpose:: URL_SAFE_NO_PAD
1939+ . decode ( key_material)
1940+ . map_err ( |e| format ! ( "Failed to decode key material: {}" , e) ) ?;
1941+
1942+ // Ensure the key is the correct size for AES-256 (32 bytes)
1943+ if decoded_key. len ( ) >= 32 {
1944+ Ok ( decoded_key[ 0 ..32 ] . to_vec ( ) )
1945+ } else {
1946+ // If the key is too short, use it as input for HMAC-based key derivation
1947+ use hmac:: { Hmac , Mac } ;
1948+ use sha2:: Sha256 ;
1949+
1950+ let mut hmac = <Hmac < Sha256 > as Mac >:: new_from_slice ( & decoded_key)
1951+ . map_err ( |e| format ! ( "Failed to create HMAC: {}" , e) ) ?;
1952+ hmac. update ( b"azure-kv-master-key-derivation" ) ;
1953+ hmac. update ( vault_url. as_bytes ( ) ) ;
1954+ hmac. update ( key_name. as_bytes ( ) ) ;
1955+ let result = hmac. finalize ( ) ;
1956+ Ok ( result. into_bytes ( ) [ 0 ..32 ] . to_vec ( ) )
1957+ }
1958+ } else {
1959+ Err ( "Key material not found in Azure Key Vault response" . to_string ( ) )
1960+ }
1961+ }
1962+ Err ( e) => {
1963+ Err ( format ! ( "Failed to retrieve key from Azure Key Vault: {}" , e) )
1964+ }
1965+ }
1966+ }
1967+
17961968 /// Store master key securely in the database
17971969 async fn store_master_key_securely (
17981970 & self ,
0 commit comments