virgil-sdk 4.2.4 → 4.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -40,9 +40,8 @@ module Virgil
40
40
  Card = Struct.new(:id, :snapshot, :identity,
41
41
  :identity_type, :public_key, :scope,
42
42
  :data, :device, :device_name, :version,
43
- :signatures, :validation_token) do
43
+ :signatures, :validation_token, :relations) do
44
44
 
45
- extend SignaturesBase64
46
45
 
47
46
  def initialize(options)
48
47
  self.id = options[:id]
@@ -56,6 +55,7 @@ module Virgil
56
55
  self.device_name = options[:device_name]
57
56
  self.version = options[:version]
58
57
  self.signatures = options[:signatures] || {}
58
+ self.relations = options[:relations] || {}
59
59
  end
60
60
 
61
61
  # Create new Card from response containing json-encoded snapshot.
@@ -81,7 +81,8 @@ module Virgil
81
81
  data: snapshot_model.fetch("data", {}),
82
82
  scope: snapshot_model["scope"],
83
83
  version: response["meta"]["card_version"],
84
- signatures: response["meta"]["signs"]
84
+ signatures: response["meta"]["signs"],
85
+ relations: response["meta"]["relations"]
85
86
  )
86
87
  end
87
88
 
@@ -90,7 +91,7 @@ module Virgil
90
91
 
91
92
  def to_request
92
93
  request = Virgil::SDK::Client::Requests::CreateCardRequest.new({})
93
- request.restore(Crypto::Bytes.from_string(self.snapshot), self.signatures, validation_token)
94
+ request.restore(Crypto::Bytes.from_string(snapshot), signatures, validation_token, relations)
94
95
  request
95
96
  end
96
97
 
@@ -119,7 +120,8 @@ module Virgil
119
120
  device_name: info["device_name"],
120
121
  data: snapshot_model.fetch("data", {}),
121
122
  scope: snapshot_model["scope"],
122
- signatures: meta[:signs]
123
+ signatures: meta[:signs],
124
+ relations: meta[:relations]
123
125
  )
124
126
  end
125
127
 
@@ -132,9 +134,9 @@ module Virgil
132
134
 
133
135
 
134
136
 
135
- Card::SERVICE_URL = ENV["VIRGIL_SERVICE_URL"] || "https://ra.virgilsecurity.com"
137
+ Card::SERVICE_URL = ENV["VIRGIL_SERVICE_URL"] || "https://cards.virgilsecurity.com"
136
138
  Card::READ_ONLY_SERVICE_URL = ENV["VIRGIL_READ_ONLY_SERVICE_URL"] || "https://cards-ro.virgilsecurity.com"
137
-
139
+ Card::RA_SERVICE_URL = ENV["VIRGIL_RA_SERVICE_URL"] || "https://ra.virgilsecurity.com"
138
140
  Card::VRA_VERSION = "v1" # version of service, which creates and deletes local and global cards
139
141
  Card::VC_VERSION = "v4" # version of service, which gets, searchs card
140
142
  end
@@ -83,6 +83,12 @@ module Virgil
83
83
  30141 => "SCR one of signers Virgil Cards is not found",
84
84
  30142 => "SCR sign item is invalid or missing for the Client",
85
85
  30143 => "SCR sign item is invalid or missing for the Virgil Registration Authority service",
86
+ 30200 => "Virgil Card relation sign is invalid",
87
+ 30201 => "Virgil Card relation sign by the source Virgil Card was not found",
88
+ 30202 => "Related Virgil content snapshot parameter was not found",
89
+ 30203 => "The relation with this Virgil Card exists already",
90
+ 30204 => "The related Virgil Card was not found for the provided CSR",
91
+ 30205 => "The Virgil Card relation doesn't exist",
86
92
  30300 => "Development Portal sign was not found inside the meta.signs property",
87
93
  30301 => "Development Portal sign is invalid",
88
94
  30302 => "VirgilIdentity Validation Token is invalid or has expired",
@@ -44,6 +44,10 @@ module Virgil
44
44
  'virgil/sdk/client/requests/verify_identity_request'
45
45
  autoload :ConfirmIdentityRequest,
46
46
  'virgil/sdk/client/requests/confirm_identity_request'
47
+ autoload :AddRelationRequest,
48
+ 'virgil/sdk/client/requests/add_relation_request'
49
+ autoload :DeleteRelationRequest,
50
+ 'virgil/sdk/client/requests/delete_relation_request'
47
51
  end
48
52
  end
49
53
  end
@@ -0,0 +1,52 @@
1
+ # Copyright (C) 2016 Virgil Security Inc.
2
+ #
3
+ # Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ # (1) Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ #
14
+ # (2) Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in
16
+ # the documentation and/or other materials provided with the
17
+ # distribution.
18
+ #
19
+ # (3) Neither the name of the copyright holder nor the names of its
20
+ # contributors may be used to endorse or promote products derived from
21
+ # this software without specific prior written permission.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
24
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ # POSSIBILITY OF SUCH DAMAGE.
34
+
35
+
36
+ module Virgil
37
+ module SDK
38
+ module Client
39
+ module Requests
40
+ # Create request using trusted card's snapshot
41
+ class AddRelationRequest < SignableRequest
42
+
43
+ def initialize(relation_card)
44
+ super()
45
+ @snapshot = relation_card.to_request.snapshot
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -78,7 +78,8 @@ module Virgil
78
78
  end
79
79
  request.restore(Virgil::Crypto::Bytes.from_base64(request_model['content_snapshot']),
80
80
  request_model['meta']['signs'],
81
- validation_token
81
+ validation_token,
82
+ request_model['meta']['relations']
82
83
  )
83
84
  request
84
85
  end
@@ -0,0 +1,51 @@
1
+ # Copyright (C) 2016 Virgil Security Inc.
2
+ #
3
+ # Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ # (1) Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ #
14
+ # (2) Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in
16
+ # the documentation and/or other materials provided with the
17
+ # distribution.
18
+ #
19
+ # (3) Neither the name of the copyright holder nor the names of its
20
+ # contributors may be used to endorse or promote products derived from
21
+ # this software without specific prior written permission.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
24
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ # POSSIBILITY OF SUCH DAMAGE.
34
+
35
+
36
+ module Virgil
37
+ module SDK
38
+ module Client
39
+ module Requests
40
+ # Delete trusted card
41
+ class DeleteRelationRequest < RevokeCardRequest
42
+
43
+ def initialize(attributes)
44
+ super(attributes)
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -39,13 +39,10 @@ module Virgil
39
39
  module SDK
40
40
  module Client
41
41
  module Requests
42
- # Base class for all API requests.
42
+ # Base class for all cards API requests.
43
43
  class SignableRequest
44
- extend SignaturesBase64
45
- attr_reader :signatures, :snapshot, :validation_token
44
+ attr_reader :signatures, :snapshot, :validation_token, :relations
46
45
 
47
- # protected :signatures=, :snapshot=
48
- # attr_writer :snapshot
49
46
 
50
47
  # Constructs new SignableRequest object
51
48
  def initialize
@@ -81,10 +78,13 @@ module Virgil
81
78
  # Args:
82
79
  # snapshot: Json-encoded snapshot request will be restored from.
83
80
  # signatures: Request signatures.
84
- def restore(snapshot, signatures, validation_token = nil)
81
+ # validation_token: validation token gotten from Virgil Identity Service
82
+ # relations: relations
83
+ def restore(snapshot, signatures, validation_token = nil, relations = nil)
85
84
  @snapshot = snapshot
86
85
  @signatures = signatures
87
86
  @validation_token = validation_token
87
+ @relations = relations
88
88
  model = JSON.parse(Crypto::Bytes.new(snapshot).to_s)
89
89
  restore_from_snapshot_model(model)
90
90
  end
@@ -132,6 +132,9 @@ module Virgil
132
132
  if validation_token
133
133
  model[:meta][:validation] = {'token': validation_token.value}
134
134
  end
135
+ if relations
136
+ model[:meta][:relations] = relations
137
+ end
135
138
 
136
139
  return model
137
140
  end
@@ -53,19 +53,21 @@ module Virgil
53
53
  end
54
54
 
55
55
  attr_accessor :access_token, :cards_service_url, :identity_service_url,
56
- :cards_read_only_service_url, :card_validator
56
+ :cards_read_only_service_url, :ra_service_url, :card_validator
57
57
 
58
58
  # Constructs new VirgilClient object
59
59
  def initialize(
60
60
  access_token=nil,
61
61
  cards_service_url=Card::SERVICE_URL,
62
62
  cards_read_only_service_url=Card::READ_ONLY_SERVICE_URL,
63
- identity_service_url=Virgil::SDK::VirgilIdentity::IDENTITY_SERVICE_URL
63
+ identity_service_url=Virgil::SDK::VirgilIdentity::IDENTITY_SERVICE_URL,
64
+ ra_service_url=Card::RA_SERVICE_URL
64
65
  )
65
66
  self.access_token = access_token
66
67
  self.cards_service_url = cards_service_url
67
68
  self.cards_read_only_service_url = cards_read_only_service_url
68
69
  self.identity_service_url = identity_service_url
70
+ self.ra_service_url = ra_service_url
69
71
  end
70
72
 
71
73
  # Create published new card from given attributes.
@@ -192,7 +194,7 @@ module Virgil
192
194
  endpoint: "/#{Card::VRA_VERSION}/card",
193
195
  body: create_request.request_model
194
196
  )
195
- raw_response = self.cards_connection.send_request(http_request)
197
+ raw_response = self.ra_connection.send_request(http_request)
196
198
  card = Card.from_response(raw_response)
197
199
  self.validate_cards([card]) if self.card_validator
198
200
  card
@@ -208,6 +210,38 @@ module Virgil
208
210
  end
209
211
 
210
212
 
213
+ def add_relation(request)
214
+ unless (request.is_a?(Requests::AddRelationRequest) && !request.snapshot.nil? && request.signatures.count == 1)
215
+ raise ArgumentError.new("Request is not valid. Request must have snapshot and exactly 1 relation signature.")
216
+ end
217
+ http_request = Virgil::SDK::Client::HTTP::Request.new(
218
+ method: Virgil::SDK::Client::HTTP::Request::POST,
219
+ endpoint: "/#{Card::VC_VERSION}/card/#{request.signatures.keys.first}/collections/relations",
220
+ body: request.request_model
221
+ )
222
+ raw_response = self.cards_connection.send_request(http_request)
223
+ card = Card.from_response(raw_response)
224
+ self.validate_cards([card]) if self.card_validator
225
+ card
226
+ end
227
+
228
+
229
+ def delete_relation(request)
230
+ unless (request.is_a?(Requests::DeleteRelationRequest) && !request.snapshot.nil? && request.signatures.count == 1)
231
+ raise ArgumentError.new("Request is not valid. Request must have snapshot and exactly 1 relation signature.")
232
+ end
233
+ http_request = Virgil::SDK::Client::HTTP::Request.new(
234
+ method: Virgil::SDK::Client::HTTP::Request::DELETE,
235
+ endpoint: "/#{Card::VC_VERSION}/card/#{request.signatures.keys.first}/collections/relations",
236
+ body: request.request_model
237
+ )
238
+ raw_response = self.cards_connection.send_request(http_request)
239
+ card = Card.from_response(raw_response)
240
+ self.validate_cards([card]) if self.card_validator
241
+ card
242
+ end
243
+
244
+
211
245
  # Revoke card by id.
212
246
  #
213
247
  # Args:
@@ -258,7 +292,7 @@ module Virgil
258
292
  endpoint: "/#{Card::VRA_VERSION}/card/#{revocation_request.card_id}",
259
293
  body: revocation_request.request_model
260
294
  )
261
- self.cards_connection.send_request(http_request)
295
+ self.ra_connection.send_request(http_request)
262
296
  end
263
297
 
264
298
  def verify_identity(identity, identity_type)
@@ -372,6 +406,7 @@ module Virgil
372
406
  return cards
373
407
  end
374
408
 
409
+
375
410
  # Validate cards signatures.
376
411
  # Args:
377
412
  # cards: list of cards to validate.
@@ -393,6 +428,12 @@ module Virgil
393
428
  )
394
429
  end
395
430
 
431
+ def ra_connection
432
+ @_ra_connection ||= HTTP::CardsServiceConnection.new(
433
+ self.access_token,
434
+ self.ra_service_url
435
+ )
436
+ end
396
437
  # Cards service connection used for getting and searching cards.
397
438
  def read_cards_connection
398
439
  @_read_cards_connection = HTTP::CardsServiceConnection.new(
@@ -42,6 +42,12 @@ module Virgil
42
42
  class VirgilCrypto
43
43
  include Virgil::Crypto
44
44
 
45
+ attr_accessor :key_pair_type
46
+
47
+ def initialize(key_pair_type=Keys::KeyPairType::Default)
48
+ @key_pair_type = key_pair_type
49
+ end
50
+
45
51
  # Exception raised when Signature is not valid
46
52
  class SignatureIsNotValid < StandardError
47
53
  def to_s
@@ -59,8 +65,8 @@ module Virgil
59
65
  # The possible values can be found in KeyPairType enum.
60
66
  # Returns:
61
67
  # Generated key pair.
62
- def generate_keys(key_pair_type=Keys::KeyPairType::Default)
63
- native_type = Keys::KeyPairType.convert_to_native(key_pair_type)
68
+ def generate_keys(keys_type=@key_pair_type)
69
+ native_type = Keys::KeyPairType.convert_to_native(keys_type)
64
70
  native_key_pair = Crypto::Native::VirgilKeyPair.generate(native_type)
65
71
  key_pair_id = self.compute_public_key_hash(native_key_pair.public_key)
66
72
  private_key = Keys::PrivateKey.new(
@@ -3,18 +3,18 @@ module Virgil
3
3
  module HighLevel
4
4
  autoload :VirgilApi, 'virgil/sdk/high_level/virgil_api'
5
5
  autoload :VirgilIdentity, 'virgil/sdk/high_level/virgil_identity'
6
- autoload :Card, 'virgil/sdk/client/card'
7
6
  autoload :VirgilCard, 'virgil/sdk/high_level/virgil_card'
8
7
  autoload :VirgilKey, 'virgil/sdk/high_level/virgil_key'
9
8
  autoload :VirgilContext, 'virgil/sdk/high_level/virgil_context'
10
9
  autoload :VirgilKeyManager, 'virgil/sdk/high_level/virgil_key_manager'
11
10
  autoload :VirgilCardManager, 'virgil/sdk/high_level/virgil_card_manager'
12
11
  autoload :VirgilAppCredentials, 'virgil/sdk/high_level/virgil_app_credentials'
13
- autoload :IdentityAttempt, 'virgil/sdk/high_level/identity_attempt'
14
12
  autoload :VirgilBuffer, 'virgil/sdk/high_level/virgil_buffer'
15
13
  autoload :VirgilStringEncoding, 'virgil/sdk/high_level/virgil_buffer'
16
14
  autoload :VirgilCardVerifierInfo, 'virgil/sdk/high_level/virgil_card_verifier_info'
17
15
 
16
+ VirgilCrypto = Virgil::SDK::Cryptography::VirgilCrypto
17
+ KeyPairType = Virgil::SDK::Cryptography::Keys::KeyPairType
18
18
  end
19
19
  end
20
20
  end
@@ -47,8 +47,9 @@ module Virgil
47
47
 
48
48
  super
49
49
  end
50
+
50
51
 
51
-
52
+ # Initializes a new buffer from array of bytes
52
53
  def self.from_bytes(bytes)
53
54
 
54
55
  self.validate_bytes_param(bytes)
@@ -68,7 +69,7 @@ module Virgil
68
69
  when VirgilStringEncoding::UTF8
69
70
  return self.from_utf8(str)
70
71
  else
71
- ArgumentError.new("encoding is undefined")
72
+ raise ArgumentError.new("encoding is undefined")
72
73
  end
73
74
 
74
75
  end
@@ -83,7 +84,7 @@ module Virgil
83
84
  when VirgilStringEncoding::UTF8
84
85
  return to_s
85
86
  else
86
- ArgumentError.new("encoding is undefined")
87
+ raise ArgumentError.new("encoding is undefined")
87
88
  end
88
89
  end
89
90
 
@@ -94,7 +95,7 @@ module Virgil
94
95
 
95
96
  # Initializes a new buffer from file.
96
97
  def self.from_file(key_file_path)
97
- ArgumentError.new("file_path is not valide") unless (File.exist?(key_file_path) && File.readable?(key_file_path))
98
+ raise ArgumentError.new("file_path is not valide") unless (File.exist?(key_file_path) && File.readable?(key_file_path))
98
99
  str = File.read(key_file_path)
99
100
  from_string(str)
100
101
  end
@@ -137,15 +138,12 @@ module Virgil
137
138
  to_s.each_byte.map { |b| b.to_s(16) }.join
138
139
  end
139
140
 
140
-
141
- def self.validate_buffer_param(param, param_name="buffer")
142
- raise ArgumentError.new("#{param_name} is not valid") unless (param.is_a?(VirgilBuffer) || param.is_a?(String))
143
- end
144
-
145
141
  private
146
142
 
147
143
  def self.validate_bytes_param(param)
148
- raise ArgumentError.new("bytes is not valid") if (!param.is_a?(Array) || param.empty?)
144
+ unless (!param.nil? && param.is_a?(Array) && !param.empty? && param.all? { |el| el.is_a? Integer })
145
+ raise ArgumentError.new("Bytes is not valid")
146
+ end
149
147
  end
150
148
 
151
149
  end
@@ -138,18 +138,28 @@ module Virgil
138
138
  # Encrypts the specified data for current Virgil card recipient
139
139
  #
140
140
  # Args:
141
- # buffer: The data to be encrypted.
141
+ # buffer: The data to be encrypted. It can be VirgilBuffer, utf8-String or Array of bytes
142
142
  #
143
143
  # Returns:
144
144
  # Encrypted data for current Virgil card recipient
145
145
  #
146
146
  # Raises:
147
- # ArgumentError: buffer is not valid if buffer doesn't have type VirgilBuffer or String
147
+ # ArgumentError: Buffer has unsupported type if buffer doesn't have type VirgilBuffer, String or Array of bytes
148
148
  def encrypt(buffer)
149
149
 
150
- VirgilBuffer.validate_buffer_param(buffer)
150
+ buffer_to_encrypt = case buffer.class.name.split("::").last
151
+ when 'VirgilBuffer'
152
+ buffer
153
+ when 'String'
154
+ VirgilBuffer.from_string(buffer)
155
+ when 'Array'
156
+ VirgilBuffer.from_bytes(buffer)
157
+ else
158
+ raise ArgumentError.new("Buffer has unsupported type")
159
+ end
151
160
 
152
- VirgilBuffer.new(context.crypto.encrypt(buffer.bytes, public_key))
161
+
162
+ VirgilBuffer.new(context.crypto.encrypt(buffer_to_encrypt.bytes, public_key))
153
163
  end
154
164
 
155
165
 
@@ -173,19 +183,39 @@ module Virgil
173
183
  # Verifies the specified buffer and signature with current VirgilCard recipient
174
184
  #
175
185
  # Args:
176
- # buffer: The data to be verified.
177
- # signature: The signature used to verify the data integrity.
186
+ # buffer: The data to be verified. It can be VirgilBuffer, utf8-encoded String or Array of bytes
187
+ # signature: The signature used to verify the data integrity. It can be VirgilBuffer, base64-encoded String or Array of bytes
178
188
  #
179
189
  # Returns:
180
190
  # true if signature is valid, false otherwise.
181
191
  #
182
192
  # Raises:
183
- # ArgumentError: buffer is not valid if buffer doesn't have type VirgilBuffer or String
184
- # ArgumentError: buffer is not valid if signature doesn't have type VirgilBuffer or String
193
+ # ArgumentError: Buffer has unsupported type if buffer doesn't have type VirgilBuffer, Array of bytes or utf8-encoded String
194
+ # ArgumentError: Signature has unsupported type if signature doesn't have type VirgilBuffer, base64-encoded String or Array of bytes
185
195
  def verify(buffer, signature)
186
- VirgilBuffer.validate_buffer_param(buffer)
187
- VirgilBuffer.validate_buffer_param(signature, "signature")
188
- context.crypto.verify(buffer.bytes, signature.bytes, public_key)
196
+
197
+ buffer_to_verify = case buffer.class.name.split("::").last
198
+ when 'VirgilBuffer'
199
+ buffer
200
+ when 'String'
201
+ VirgilBuffer.from_string(buffer)
202
+ when 'Array'
203
+ VirgilBuffer.from_bytes(buffer)
204
+ else
205
+ raise ArgumentError.new("Buffer has unsupported type")
206
+ end
207
+
208
+ signature_to_verify = case signature.class.name.split("::").last
209
+ when 'VirgilBuffer'
210
+ signature
211
+ when 'String'
212
+ VirgilBuffer.from_base64(signature)
213
+ when 'Array'
214
+ VirgilBuffer.from_bytes(signature)
215
+ else
216
+ raise ArgumentError.new("Signature has unsupported type")
217
+ end
218
+ context.crypto.verify(buffer_to_verify.bytes, signature_to_verify.bytes, public_key)
189
219
  end
190
220
 
191
221
  private