mastercard-client-encryption 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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