Class: Familia::Encryption::Providers::SecureXChaCha20Poly1305Provider

Inherits:
Familia::Encryption::Provider show all
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:

  1. Minimizes key lifetime in memory
  2. Uses immediate secure wiping after operations
  3. Avoids unnecessary key duplication
  4. Uses locked memory where possible (future enhancement)

Constant Summary collapse

ALGORITHM =
'xchacha20poly1305-secure'.freeze
NONCE_SIZE =
24
AUTH_TAG_SIZE =
16

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

This class inherits a constructor from Familia::Encryption::Provider

Class Method Details

.available?Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 54

def self.available?
  !!defined?(RbNaCl) && !!defined?(FFI)
end

.priorityObject



58
59
60
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 58

def self.priority
  110 # Higher than regular XChaCha20Poly1305Provider
end

Instance Method Details

#decrypt(ciphertext, key, nonce, auth_tag, additional_data = nil) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 77

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

Raises:



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 96

def derive_key(master_key, context, personal: nil)
  validate_key_length!(master_key)

  raw_personal = personal || Familia.config.encryption_personalization
  raise EncryptionError, 'Personalization string must not contain null bytes' if raw_personal.include?("\0")

  personal_string = raw_personal.ljust(16, "\0")

  # Perform derivation and immediately clear intermediate values
  derived_key = RbNaCl::Hash.blake2b(
    context.force_encoding('BINARY'),
    key: master_key,
    digest_size: 32,
    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



62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 62

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_nonceObject



91
92
93
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 91

def generate_nonce
  RbNaCl::Random.random_bytes(NONCE_SIZE)
end

#secure_wipe(key) ⇒ Object

Clear key from memory (still no security guarantees in Ruby)



120
121
122
# File 'lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb', line 120

def secure_wipe(key)
  key&.clear
end