Class: Familia::Encryption::Providers::SecureXChaCha20Poly1305Provider
- Inherits:
-
Familia::Encryption::Provider
- Object
- Familia::Encryption::Provider
- Familia::Encryption::Providers::SecureXChaCha20Poly1305Provider
- Includes:
- Blake2bPersonalization
- Defined in:
- lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb
Overview
Enhanced XChaCha20Poly1305Provider with improved memory security
While complete avoidance of Ruby strings for secrets is challenging due to RbNaCl's internal implementation, this provider implements several security improvements:
- Minimizes key lifetime in memory
- Uses immediate secure wiping after operations
- Avoids unnecessary key duplication
- Uses locked memory where possible (future enhancement)
Constant Summary collapse
- ALGORITHM =
'xchacha20poly1305-secure'- NONCE_SIZE =
24- AUTH_TAG_SIZE =
16- KEY_SIZE =
32
Constants included from Blake2bPersonalization
Blake2bPersonalization::LEGACY_PERSONALIZATION
Class Method Summary collapse
Instance Method Summary collapse
- #decrypt(ciphertext, key, nonce, auth_tag, additional_data = nil) ⇒ Object
-
#derive_key(master_key, context, personal: nil) ⇒ Object
Enhanced key derivation with immediate cleanup.
- #encrypt(plaintext, key, additional_data = nil) ⇒ Object
- #generate_nonce ⇒ Object
-
#secure_wipe(key) ⇒ Object
Clear key from memory (still no security guarantees in Ruby).
Methods included from Blake2bPersonalization
#current_personalization, #personalizations
Constructor Details
This class inherits a constructor from Familia::Encryption::Provider
Class Method Details
.available? ⇒ Boolean
59 60 61 |
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 59 def self.available? !!defined?(RbNaCl) && !!defined?(FFI) end |
.dependency_hint ⇒ Object
63 64 65 |
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 63 def self.dependency_hint 'rbnacl/libsodium and ffi' end |
.priority ⇒ Object
67 68 69 |
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 67 def self.priority 110 # Higher than regular XChaCha20Poly1305Provider end |
Instance Method Details
#decrypt(ciphertext, key, nonce, auth_tag, additional_data = nil) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 86 def decrypt(ciphertext, key, nonce, auth_tag, additional_data = nil) validate_key_length!(key) # Minimize key exposure by performing operation immediately begin result = perform_decryption(ciphertext, key, nonce, auth_tag, additional_data) ensure # Attempt to clear the key parameter (if mutable) secure_wipe(key) end result end |
#derive_key(master_key, context, personal: nil) ⇒ Object
Enhanced key derivation with immediate cleanup.
Personalization resolution and validation are shared with XChaCha20Poly1305Provider via Blake2bPersonalization -- this file has drifted from its sibling before (#250, #356), so the two providers must stay on the identical code path (#333).
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 110 def derive_key(master_key, context, personal: nil) validate_key_length!(master_key) # validate_personalization! guards explicitly-passed candidates with # the same fail-closed checks as the config path (defense in depth -- # #personalizations already filters the decrypt walk). raw_personal = personal ? validate_personalization!(personal) : current_personalization personal_string = raw_personal.ljust(16, "\0") # Perform derivation and immediately clear intermediate values derived_key = RbNaCl::Hash.blake2b( # to_s before .b: tolerate non-String contexts (Symbol, nil) and # always operate on a fresh BINARY copy. force_encoding mutates its # receiver, which flipped the caller's context string to BINARY and # raised FrozenError on frozen literals (#356, mirroring the fix # made to XChaCha20Poly1305Provider in #250). context.to_s.b, key: master_key, digest_size: KEY_SIZE, personal: personal_string, ) # Clear personalization string from memory personal_string.clear # Return derived key (caller responsible for secure cleanup) derived_key end |
#encrypt(plaintext, key, additional_data = nil) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 71 def encrypt(plaintext, key, additional_data = nil) validate_key_length!(key) # Generate nonce first to avoid holding onto key longer than necessary nonce = generate_nonce # Minimize key exposure by performing operation immediately result = perform_encryption(plaintext, key, nonce, additional_data) # Attempt to clear the key parameter (if mutable) secure_wipe(key) result end |
#generate_nonce ⇒ Object
100 101 102 |
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 100 def generate_nonce RbNaCl::Random.random_bytes(NONCE_SIZE) end |
#secure_wipe(key) ⇒ Object
Clear key from memory (still no security guarantees in Ruby)
140 141 142 |
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 140 def secure_wipe(key) key&.clear end |