Module: Familia::Encryption

Defined in:
lib/familia/encryption.rb,
lib/familia/encryption/manager.rb,
lib/familia/encryption/provider.rb,
lib/familia/encryption/registry.rb,
lib/familia/encryption/request_cache.rb,
lib/familia/encryption/encrypted_data.rb,
lib/familia/encryption/providers/aes_gcm_provider.rb,
lib/familia/encryption/providers/xchacha20_poly1305_provider.rb,
lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb

Defined Under Namespace

Modules: Providers Classes: EncryptedData, Manager, Provider, Registry

Class Method Summary collapse

Class Method Details

.benchmark(iterations: 1000) ⇒ Object

Benchmark available providers



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/familia/encryption.rb', line 129

def benchmark(iterations: 1000)
  require 'benchmark'
  test_data = 'x' * 1024 # 1KB test
  context = 'benchmark:test'

  results = {}
  Registry.providers.each do |algo, provider_class|
    next unless provider_class.available?

    mgr = Manager.new(algorithm: algo)
    time = Benchmark.realtime do
      iterations.times do
        encrypted = mgr.encrypt(test_data, context: context)
        mgr.decrypt(encrypted, context: context)
      end
    end

    results[algo] = {
      time: time,
      ops_per_sec: (iterations * 2 / time).round,
      priority: provider_class.priority,
    }
  end

  results
end

.clear_request_cache!Object

Clear all cached keys and disable caching



31
32
33
34
35
36
37
38
# File 'lib/familia/encryption/request_cache.rb', line 31

def clear_request_cache!
  if (cache = Fiber[:familia_request_cache])
    cache.each_value { |key| secure_wipe(key) }
    cache.clear
  end
  Fiber[:familia_request_cache_enabled] = false
  Fiber[:familia_request_cache] = nil
end

.decrypt(encrypted_json, context:, additional_data: nil) ⇒ Object

Quick decryption (auto-detects algorithm from data)



81
82
83
# File 'lib/familia/encryption.rb', line 81

def decrypt(encrypted_json, context:, additional_data: nil)
  manager.decrypt(encrypted_json, context: context, additional_data: additional_data)
end

.derivation_countObject

Derivation counter for monitoring no-caching behavior



95
96
97
# File 'lib/familia/encryption.rb', line 95

def derivation_count
  @derivation_count ||= Concurrent::AtomicFixnum.new(0)
end

.encrypt(plaintext, context:, additional_data: nil) ⇒ Object

Quick encryption with auto-selected best provider



76
77
78
# File 'lib/familia/encryption.rb', line 76

def encrypt(plaintext, context:, additional_data: nil)
  manager.encrypt(plaintext, context: context, additional_data: additional_data)
end

.encrypt_with(algorithm, plaintext, context:, additional_data: nil) ⇒ Object

Encrypt with specific algorithm



86
87
88
89
90
91
92
# File 'lib/familia/encryption.rb', line 86

def encrypt_with(algorithm, plaintext, context:, additional_data: nil)
  manager(algorithm: algorithm).encrypt(
    plaintext,
    context: context,
    additional_data: additional_data
  )
end

.hardware_acceleration?Boolean

Check if we're using hardware acceleration

Returns:

  • (Boolean)


123
124
125
126
# File 'lib/familia/encryption.rb', line 123

def hardware_acceleration?
  provider = Registry.default_provider
  provider && provider.class.name.include?('Hardware')
end

.manager(algorithm: nil) ⇒ Object

Get or create a manager with specific algorithm

Thread-safe lazy initialization using Concurrent::Map to ensure only a single Manager instance is created per algorithm even under concurrent encryption/decryption requests.



70
71
72
73
# File 'lib/familia/encryption.rb', line 70

def manager(algorithm: nil)
  @managers ||= Concurrent::Map.new
  @managers.fetch_or_store(algorithm) { Manager.new(algorithm: algorithm) }
end

.reset_derivation_count!Object



99
100
101
# File 'lib/familia/encryption.rb', line 99

def reset_derivation_count!
  derivation_count.value = 0
end

.secure_wipe(key) ⇒ Object

Clear key from memory (no security guarantees in Ruby)



104
105
106
# File 'lib/familia/encryption.rb', line 104

def secure_wipe(key)
  key&.clear
end

.statusObject

Get info about current encryption setup



109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/familia/encryption.rb', line 109

def status
  Registry.setup! if Registry.providers.empty?

  {
    default_algorithm: Registry.default_provider&.algorithm,
    available_algorithms: Registry.available_algorithms,
    preferred_available: Registry.default_provider&.class&.name,
    using_hardware: hardware_acceleration?,
    key_versions: encryption_keys.keys,
    current_version: current_key_version,
  }
end

.validate_configuration!Object

Raises:



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/familia/encryption.rb', line 156

def validate_configuration!
  raise EncryptionError, 'No encryption keys configured' if encryption_keys.empty?
  raise EncryptionError, 'No current key version set' unless current_key_version

  current_key = encryption_keys[current_key_version]
  raise EncryptionError, "Current key version not found: #{current_key_version}" unless current_key

  begin
    Base64.strict_decode64(current_key)
  rescue ArgumentError
    raise EncryptionError, 'Current encryption key is not valid Base64'
  end

  Registry.setup!
  raise EncryptionError, 'No encryption providers available' unless Registry.default_provider
end

.with_request_cacheObject

Enable request-scoped caching (opt-in for performance)



22
23
24
25
26
27
28
# File 'lib/familia/encryption/request_cache.rb', line 22

def with_request_cache
  Fiber[:familia_request_cache_enabled] = true
  Fiber[:familia_request_cache] = {}
  yield
ensure
  clear_request_cache!
end