Module: Pigeon::Encryption

Defined in:
lib/pigeon/encryption.rb

Overview

Encryption module for handling payload encryption and decryption

Defined Under Namespace

Classes: DecryptionError, EncryptionError

Constant Summary collapse

DEFAULT_CIPHER =
"aes-256-gcm"

Class Method Summary collapse

Class Method Details

.decrypt(encrypted_payload, encryption_key = nil) ⇒ String

Decrypt a payload

Raises:



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/pigeon/encryption.rb', line 59

def self.decrypt(encrypted_payload, encryption_key = nil)
  # Return the payload as is if it's not encrypted
  return encrypted_payload if encrypted_payload.is_a?(String)

  # Get the encryption key
  key = encryption_key || self.encryption_key
  raise EncryptionError, "Encryption key is required" unless key

  begin
    # Extract the encrypted data, IV, and auth tag
    encoded_data = encrypted_payload[:data]
    encoded_iv = encrypted_payload[:iv]
    encoded_auth_tag = encrypted_payload[:auth_tag]

    # Decode the data, IV, and auth tag
    encrypted_data = Base64.strict_decode64(encoded_data)
    iv = Base64.strict_decode64(encoded_iv)
    auth_tag = Base64.strict_decode64(encoded_auth_tag) if encoded_auth_tag

    # Decrypt using AES-256-GCM
    decipher = OpenSSL::Cipher.new(DEFAULT_CIPHER)
    decipher.decrypt
    decipher.key = key
    decipher.iv = iv
    decipher.auth_tag = auth_tag if auth_tag

    # Decrypt the payload
    decipher.update(encrypted_data) + decipher.final
  rescue StandardError => e
    raise DecryptionError, "Failed to decrypt payload: #{e.message}"
  end
end

.encrypt(payload, encryption_key = nil, encryption_iv = nil) ⇒ Hash

Encrypt a payload



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/pigeon/encryption.rb', line 16

def self.encrypt(payload, encryption_key = nil, encryption_iv = nil)
  # Return the payload as is if encryption is disabled
  return payload unless Pigeon.config.encrypt_payload

  # Get the encryption key
  key = encryption_key || self.encryption_key
  raise EncryptionError, "Encryption key is required" unless key

  # Generate a random IV if not provided (GCM uses 12-byte IV)
  iv = encryption_iv || OpenSSL::Random.random_bytes(12)

  # Encrypt the payload using AES-256-GCM
  cipher = OpenSSL::Cipher.new(DEFAULT_CIPHER)
  cipher.encrypt
  cipher.key = key
  cipher.iv = iv

  # Encrypt the payload
  encrypted_data = cipher.update(payload) + cipher.final

  # Get the authentication tag
  auth_tag = cipher.auth_tag

  # Encode the data, IV, and auth tag
  encoded_data = Base64.strict_encode64(encrypted_data)
  encoded_iv = Base64.strict_encode64(iv)
  encoded_auth_tag = Base64.strict_encode64(auth_tag)

  # Return the encrypted payload with metadata
  {
    data: encoded_data,
    iv: encoded_iv,
    auth_tag: encoded_auth_tag,
    cipher: DEFAULT_CIPHER
  }
rescue StandardError => e
  raise EncryptionError, "Failed to encrypt payload: #{e.message}"
end

.encryption_keyString?

Get the encryption key from the configuration



112
113
114
115
116
117
118
# File 'lib/pigeon/encryption.rb', line 112

def self.encryption_key
  key = key_from_config
  key ||= key_from_environment
  key ||= key_from_rails_credentials
  key ||= key_from_hanami_settings
  key
end

.mask_payload(payload, sensitive_fields = nil) ⇒ Hash, String

Mask sensitive data in a payload



96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/pigeon/encryption.rb', line 96

def self.mask_payload(payload, sensitive_fields = nil)
  # Return the payload as is if no sensitive fields are specified
  return payload if payload.is_a?(String) || sensitive_fields.nil? || sensitive_fields.empty?

  # Convert the payload to a hash if it's a string
  payload_hash = payload.is_a?(String) ? ::JSON.parse(payload) : payload.dup

  # Mask sensitive fields
  MaskingHelper.mask_fields(payload_hash, sensitive_fields)
rescue StandardError => e
  Pigeon.config.logger.warn("Failed to mask payload: #{e.message}")
  payload
end