mastercard-client-encryption 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9426fa736ec3824895a9bc3a03f71bc99fb3ad26b965203a35b404965428668
4
- data.tar.gz: 1fb1aeda422b4536eb993767d2ae1ed6a8be4fe34eb821f76bc9baa3e69691fa
3
+ metadata.gz: b77cd2d8a1ded5e2456a290e91add5537e3de8ce397e02c049b58a23c4d93f79
4
+ data.tar.gz: 82350cf1e7aafc602f7efccdf94b271ef218b77fa26b90be3bd7baac18855c27
5
5
  SHA512:
6
- metadata.gz: 478919fe88592526cb8172e47173411fe866c0b35bd885eb3a19842908ea0924d2314a0ec56e064d42ae1c8ccdfaa8aa1336ce5605bc705507a730b74d0ecccb
7
- data.tar.gz: ddca20378a92bc44019a40abcf03ef548fe8278229c106161377aa3b52df21b5d2fa68631b8c618bfefd6ef64aa8ac2503f71519bbf14026934754cbd21f78f1
6
+ metadata.gz: a56bec98e3a1ed10af314ecf1c318abbf88b36a39dab24f3f888ed2a05d8767befde6fc807022a57ae1d33d6a50c163903bb436f52de480683be9d59f2044c43
7
+ data.tar.gz: cf09bf8da562269a9f481bea48662cf44c0381cdbfed88945436d627bea23cc8bb3b4fb63247042f7c3fbffc87ece128109ec128b832a868588f7db478a52012
@@ -37,8 +37,8 @@ module McAPI
37
37
  #
38
38
  # Generate encryption parameters.
39
39
  #
40
- # @param [String] iv IV to use instead to generate a random IV
41
- # @param [String] secret_key Secret Key to use instead to generate a random key
40
+ # @param [String,nil] iv IV to use instead to generate a random IV
41
+ # @param [String,nil] secret_key Secret Key to use instead to generate a random key
42
42
  #
43
43
  # @return [Hash] hash with the generated encryption parameters
44
44
  #
@@ -70,8 +70,8 @@ module McAPI
70
70
  # If +iv+, +secret_key+, +encryption_params+ and +encoding+ are not provided, randoms will be generated.
71
71
  #
72
72
  # @param [String] data json string to encrypt
73
- # @param [String] (optional) iv Initialization vector to use to create the cipher, if not provided generate a random one
74
- # @param [String] (optional) encryption_params encryption parameters
73
+ # @param [String,nil] (optional) iv Initialization vector to use to create the cipher, if not provided generate a random one
74
+ # @param [String,nil] (optional) encryption_params encryption parameters
75
75
  # @param [String] encoding encoding to use for the encrypted bytes (hex or base64)
76
76
  #
77
77
  # @return [String] encrypted data
@@ -103,11 +103,12 @@ module McAPI
103
103
  # @param [String] iv Initialization vector to use to create the Decipher
104
104
  # @param [String] encrypted_key Encrypted key to use to decrypt the data
105
105
  # (the key is the decrypted using the provided PrivateKey)
106
+ # @param [String] oaep_hashing_alg OAEP Algorithm to use
106
107
  #
107
108
  # @return [String] Decrypted JSON object
108
109
  #
109
- def decrypt_data(encrypted_data, iv, encrypted_key)
110
- md = Utils.create_message_digest(@oaep_hashing_alg)
110
+ def decrypt_data(encrypted_data, iv, encrypted_key, oaep_hashing_alg)
111
+ md = Utils.create_message_digest(oaep_hashing_alg)
111
112
  decrypted_key = @private_key.private_decrypt_oaep(Utils.decode(encrypted_key, @encoding), '', md, md)
112
113
  aes = OpenSSL::Cipher::AES.new(decrypted_key.size * 8, :CBC)
113
114
  aes.decrypt
@@ -121,7 +122,7 @@ module McAPI
121
122
  #
122
123
  # Compute the fingerprint for the provided public key
123
124
  #
124
- # @param [String] type: +certificate+ or +publickey+
125
+ # @param [Hash] type: +certificate+ or +publickey+
125
126
  #
126
127
  # @return [String] the computed fingerprint encoded using the configured encoding
127
128
  #
@@ -13,7 +13,7 @@ module McAPI
13
13
  #
14
14
  # Create a new instance with the provided configuration
15
15
  #
16
- # @param [Object] config Configuration object
16
+ # @param [Hash] config Configuration object
17
17
  #
18
18
  def initialize(config)
19
19
  @config = config
@@ -27,7 +27,7 @@ module McAPI
27
27
  # Encrypt parts of a HTTP request using the given config
28
28
  #
29
29
  # @param [String] endpoint HTTP URL for the current call
30
- # @param [Object] header HTTP header
30
+ # @param [Object|nil] header HTTP header
31
31
  # @param [String,Hash] body HTTP body
32
32
  #
33
33
  # @return [Hash] Hash with two keys:
@@ -37,19 +37,20 @@ module McAPI
37
37
  def encrypt(endpoint, header, body)
38
38
  body = JSON.parse(body) if body.is_a?(String)
39
39
  config = config?(endpoint)
40
+ body_map = body
40
41
  if config
41
42
  if !@is_with_header
42
- config['toEncrypt'].each do |v|
43
+ body_map = config['toEncrypt'].map do |v|
43
44
  encrypt_with_body(v, body)
44
45
  end
45
46
  else
46
47
  enc_params = @crypto.new_encryption_params
47
- config['toEncrypt'].each do |v|
48
+ body_map = config['toEncrypt'].map do |v|
48
49
  body = encrypt_with_header(v, enc_params, header, body)
49
50
  end
50
51
  end
51
52
  end
52
- { header: header, body: body.json }
53
+ { header: header, body: config ? compute_body(config['toEncrypt'], body_map) { body.json } : body.json }
53
54
  end
54
55
 
55
56
  #
@@ -62,9 +63,10 @@ module McAPI
62
63
  def decrypt(response)
63
64
  response = JSON.parse(response)
64
65
  config = config?(response['request']['url'])
66
+ body_map = response
65
67
  if config
66
68
  if !@is_with_header
67
- config['toDecrypt'].each do |v|
69
+ body_map = config['toDecrypt'].map do |v|
68
70
  decrypt_with_body(v, response['body'])
69
71
  end
70
72
  else
@@ -74,6 +76,7 @@ module McAPI
74
76
  end
75
77
  end
76
78
  end
79
+ response['body'] = compute_body(config['toDecrypt'], body_map) { response['body'] } unless config.nil?
77
80
  JSON.generate(response)
78
81
  end
79
82
 
@@ -82,14 +85,19 @@ module McAPI
82
85
  def encrypt_with_body(path, body)
83
86
  elem = elem_from_path(path['element'], body)
84
87
  return unless elem && elem[:node]
88
+
85
89
  encrypted_data = @crypto.encrypt_data(data: JSON.generate(elem[:node]))
86
- McAPI::Utils.mutate_obj_prop(path['obj'], encrypted_data, body)
87
- McAPI::Utils.delete_node(path['element'], body) if path['element'] != "#{path['obj']}.#{@config['encryptedValueFieldName']}"
90
+ body = McAPI::Utils.mutate_obj_prop(path['obj'], encrypted_data, body)
91
+ unless json_root?(path['obj']) || path['element'] == "#{path['obj']}.#{@config['encryptedValueFieldName']}"
92
+ McAPI::Utils.delete_node(path['element'], body)
93
+ end
94
+ body
88
95
  end
89
96
 
90
97
  def encrypt_with_header(path, enc_params, header, body)
91
98
  elem = elem_from_path(path['element'], body)
92
99
  return unless elem && elem[:node]
100
+
93
101
  encrypted_data = @crypto.encrypt_data(data: JSON.generate(elem[:node]), encryption_params: enc_params)
94
102
  body = { path['obj'] => { @config['encryptedValueFieldName'] => encrypted_data[@config['encryptedValueFieldName']] } }
95
103
  set_header(header, enc_params)
@@ -99,9 +107,11 @@ module McAPI
99
107
  def decrypt_with_body(path, body)
100
108
  elem = elem_from_path(path['element'], body)
101
109
  return unless elem && elem[:node]
110
+
102
111
  decrypted = @crypto.decrypt_data(elem[:node][@config['encryptedValueFieldName']],
103
- elem[:node][@config['ivFieldName']],
104
- elem[:node][@config['encryptedKeyFieldName']])
112
+ elem[:node][@config['ivFieldName']],
113
+ elem[:node][@config['encryptedKeyFieldName']],
114
+ elem[:node][@config['oaepHashingAlgorithmFieldName']])
105
115
  begin
106
116
  decrypted = JSON.parse(decrypted)
107
117
  rescue JSON::ParserError
@@ -116,7 +126,8 @@ module McAPI
116
126
  response['body'].clear
117
127
  response['body'] = JSON.parse(@crypto.decrypt_data(encrypted_data,
118
128
  response['headers'][@config['ivHeaderName']][0],
119
- response['headers'][@config['encryptedKeyHeaderName']][0]))
129
+ response['headers'][@config['encryptedKeyHeaderName']][0],
130
+ response['headers'][@config['oaepHashingAlgorithmHeaderName']][0]))
120
131
  end
121
132
 
122
133
  def elem_from_path(path, obj)
@@ -125,7 +136,7 @@ module McAPI
125
136
  if path && !paths.empty?
126
137
  paths.each do |e|
127
138
  parent = obj
128
- obj = obj[e]
139
+ obj = json_root?(e) ? obj : obj[e]
129
140
  end
130
141
  end
131
142
  { node: obj, parent: parent }
@@ -147,6 +158,18 @@ module McAPI
147
158
  header[@config['oaepHashingAlgorithmHeaderName']] = params[:oaepHashingAlgorithm].sub('-', '')
148
159
  header[@config['publicKeyFingerprintHeaderName']] = params[:publicKeyFingerprint]
149
160
  end
161
+
162
+ def json_root?(elem)
163
+ elem == '$'
164
+ end
165
+
166
+ def compute_body(config_param, body_map)
167
+ encryption_param?(config_param, body_map) ? body_map[0] : yield
168
+ end
169
+
170
+ def encryption_param?(enc_param, body_map)
171
+ enc_param.length == 1 && body_map.length == 1
172
+ end
150
173
  end
151
174
  end
152
175
  end
@@ -15,7 +15,7 @@ module McAPI
15
15
  # adding encryption/decryption capabilities for the request/response payload.
16
16
  #
17
17
  # @param [Object] swagger_client OpenAPI service client (it can be generated by the swagger code generator)
18
- # @param [Object] config configuration object describing which field to enable encryption/decryption
18
+ # @param [Hash] config configuration object describing which field to enable encryption/decryption
19
19
  #
20
20
  def install_field_level_encryption(swagger_client, config)
21
21
  fle = McAPI::Encryption::FieldLevelEncryption.new(config)
@@ -45,7 +45,7 @@ module McAPI
45
45
  def hook_deserialize(fle)
46
46
  self.class.send :define_method, :init_deserialize do |client|
47
47
  client.define_singleton_method(:deserialize) do |response, return_type|
48
- if response && response.body
48
+ if response&.body
49
49
  endpoint = response.request.base_url.sub client.config.base_url, ''
50
50
  to_decrypt = { headers: McAPI::Utils.parse_header(response.options[:response_headers]),
51
51
  request: { url: endpoint },
@@ -51,7 +51,7 @@ module OpenSSL
51
51
  em = str.bytes
52
52
  raise OpenSSL::PKey::RSAError if em.size < 2 * mdlen + 2
53
53
 
54
- # Keep constant calculation even if the text is invaid in order to avoid attacks.
54
+ # Keep constant calculation even if the text is invalid in order to avoid attacks.
55
55
  good = secure_byte_is_zero(em[0])
56
56
  masked_seed = em[1...1 + mdlen].pack('C*')
57
57
  masked_db = em[1 + mdlen...em.size].pack('C*')
@@ -66,22 +66,27 @@ module McAPI
66
66
  def self.mutate_obj_prop(path, value, obj, src_path = nil, properties = [])
67
67
  tmp = obj
68
68
  prev = nil
69
- return unless path
69
+ return obj unless path
70
70
 
71
71
  delete_node(src_path, obj, properties) if src_path
72
72
  paths = path.split('.')
73
- paths.each do |e|
74
- tmp[e] = {} unless tmp[e]
75
- prev = tmp
76
- tmp = tmp[e]
73
+ unless path == '$'
74
+ paths.each do |e|
75
+ tmp[e] = {} unless tmp[e]
76
+ prev = tmp
77
+ tmp = tmp[e]
78
+ end
77
79
  end
78
80
  elem = path.split('.').pop
79
- if value.is_a?(Hash) && !value.is_a?(Array)
81
+ if elem == '$'
82
+ obj = value # replace root
83
+ elsif value.is_a?(Hash) && !value.is_a?(Array)
80
84
  prev[elem] = {} unless prev[elem].is_a?(Hash)
81
85
  override_props(prev[elem], value)
82
86
  else
83
87
  prev[elem] = value
84
88
  end
89
+ obj
85
90
  end
86
91
 
87
92
  def self.override_props(target, obj)
@@ -105,6 +110,7 @@ module McAPI
105
110
  obj = obj[e]
106
111
  prev.delete(to_delete) if obj && index == paths.size - 1
107
112
  end
113
+ obj.keys.each { |e| obj.delete(e) } if paths.length == 1 && paths[0] == '$'
108
114
  properties.each { |e| obj.delete(e) } if paths.empty?
109
115
  end
110
116
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mastercard-client-encryption
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mastercard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-18 00:00:00.000000000 Z
11
+ date: 2021-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler