Files
Dalan/app/services/encryption_service.rb
2026-08-13 19:56:46 +03:30

76 lines
1.8 KiB
Ruby

# frozen_string_literal: true
class EncryptionService
# Example: 202040
# Week number starts from 0
def self.current_salt
Time.now.strftime('%Y%W')
end
def self.pack(salt, value)
[salt, value].join('.')
end
def self.unpack(str)
raise "Invalid encrypted value: #{str}" unless str =~ (/(\w*)\.(.*)/)
[$1, $2]
end
def self.encrypt(value)
# Get current salt
salt = current_salt
# Get or generate new master key from salt
current_key = get_master_key(salt)
# Encrypt attribute value
encrypted_key = encryptor(current_key).encrypt_and_sign(value)
# Add salt before encrypted value
pack(salt, encrypted_key)
end
def self.decrypt(value)
# Unpack salt and encrypted_key
salt, encrypted_key = unpack(value)
# Get master key from salt
master_key = get_master_key(salt)
# Decrypt encrypted value for attribute
encryptor(master_key).decrypt_and_verify(encrypted_key)
end
private
def self.encryptor(key)
ActiveSupport::MessageEncryptor.new(key)
end
def self.get_master_key(salt)
@cache ||= {}
# Delete keys with expired date
delete_expired_keys
unless @cache[salt]
# Initialize hash for specific salt
@cache[salt] = {}
# Put key value from key generator
@cache[salt]['key'] = ActiveSupport::KeyGenerator.new(
ENV.fetch('SECRET_KEY_BASE')
).generate_key(salt, ActiveSupport::MessageEncryptor.key_len)
# Put expire date for specific key
@cache[salt]['expire_date'] = 1.week.from_now
end
@cache[salt]['key']
end
def self.delete_expired_keys
return unless @cache.is_a?(Hash)
# Iterate through all @cache values
@cache.each do |salt, values|
# Delete key if expire date expired
@cache.delete(salt) if values['expire_date'].present? && values['expire_date'] < Time.now
end
end
end