Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions app/models/resonance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def self.find_by_google_id(google_id)
# Encrypt data using Google ID as key
def encrypt_field(value)
return nil if value.nil?

cipher = OpenSSL::Cipher.new("aes-256-gcm")
cipher.encrypt
cipher.key = encryption_key
Expand All @@ -43,24 +44,22 @@ def encrypt_field(value)
encrypted = cipher.update(value.to_s) + cipher.final
auth_tag = cipher.auth_tag

# Store: iv + auth_tag + encrypted_data (all base64 encoded)
Base64.strict_encode64([ iv, auth_tag, encrypted ].map { |d| Base64.strict_encode64(d) }.join(":"))
# Fixed-length concatenation: iv (12 bytes) + auth_tag (16 bytes) + encrypted data
# Works for empty strings since we just concatenate the bytes directly
Base64.strict_encode64(iv + auth_tag + encrypted)
end

# Decrypt data using Google ID as key
def decrypt_field(encrypted_value)
return nil if encrypted_value.nil? || encrypted_value.blank?
return nil unless google_id # Can't decrypt without Google ID

# Handle case where encrypted_value might not be a string
encrypted_str = encrypted_value.to_s

decoded = Base64.strict_decode64(encrypted_str)
iv_b64, auth_tag_b64, encrypted_b64 = decoded.split(":")
raw = Base64.strict_decode64(encrypted_value.to_s)

iv = Base64.strict_decode64(iv_b64)
auth_tag = Base64.strict_decode64(auth_tag_b64)
encrypted = Base64.strict_decode64(encrypted_b64)
# Fixed-length extraction: first 12 bytes = iv, next 16 bytes = auth_tag, rest = encrypted
iv = raw[0, 12]
auth_tag = raw[12, 16]
encrypted = raw[28..-1] || "" # Handle case where encrypted portion is empty

decipher = OpenSSL::Cipher.new("aes-256-gcm")
decipher.decrypt
Expand Down
11 changes: 11 additions & 0 deletions spec/models/resonance_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@
reloaded = Resonance.find_by(encrypted_google_id_hash: resonance.encrypted_google_id_hash)
expect(reloaded.stripe_customer_id).to be_nil
end

it "can encrypt and decrypt empty strings" do
resonance = Resonance.find_or_create_by_google_id(google_id)

encrypted = resonance.encrypt_field("")
expect(encrypted).to be_present
expect(encrypted).to be_a(String)

decrypted = resonance.decrypt_field(encrypted)
expect(decrypted).to eq("")
end
end

describe "#universe_day" do
Expand Down