main
1# frozen_string_literal: true
2
3module Xml
4 module Kit
5 module Crypto
6 class SymmetricCipher
7 DEFAULT_ALGORITHM = "#{::Xml::Kit::Namespaces::XMLENC}aes256-cbc"
8 ALGORITHMS = {
9 "#{::Xml::Kit::Namespaces::XMLENC}tripledes-cbc" => 'DES-EDE3-CBC',
10 "#{::Xml::Kit::Namespaces::XMLENC}aes128-cbc" => 'AES-128-CBC',
11 "#{::Xml::Kit::Namespaces::XMLENC}aes192-cbc" => 'AES-192-CBC',
12 "#{::Xml::Kit::Namespaces::XMLENC}aes256-cbc" => 'AES-256-CBC',
13 }.freeze
14
15 attr_reader :algorithm, :key, :padding
16
17 def initialize(algorithm = DEFAULT_ALGORITHM, key = nil, padding = nil)
18 @algorithm = algorithm
19 @key = key || cipher.random_key
20 @padding = padding
21 end
22
23 def self.matches?(algorithm)
24 ALGORITHMS[algorithm]
25 end
26
27 def encrypt(plain_text)
28 cipher.encrypt
29 cipher.key = @key
30 cipher.random_iv + cipher.update(plain_text) + cipher.final
31 end
32
33 def decrypt(cipher_text)
34 bytes = cipher_text.bytes
35 result = default_decrypt(
36 bytes[0...cipher.iv_len],
37 bytes[cipher.iv_len..-1]
38 )
39 return result if padding.nil?
40
41 padding_size = result.bytes.last
42 result[0...-padding_size]
43 end
44
45 def to_s
46 algorithm
47 end
48
49 protected
50
51 def default_decrypt(initialization_vector, data)
52 cipher.decrypt
53 apply_padding_to(cipher)
54 cipher.key = @key
55 cipher.iv = initialization_vector.pack('c*')
56 cipher.update(data.pack('c*')) << cipher.final
57 end
58
59 private
60
61 def cipher
62 @cipher ||= OpenSSL::Cipher.new(ALGORITHMS[algorithm])
63 end
64
65 def apply_padding_to(cipher)
66 cipher.padding = padding unless padding.nil?
67 end
68 end
69 end
70 end
71end