smart_id 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f1a4792f8f44f13df58feb85e9641bf8c249b0939d6d727ca7234060717a4d86
4
+ data.tar.gz: f58e94d4586fb872662853aa5cf4d50a8e6a2b40b1e256c83cafe3f1f13acd5f
5
+ SHA512:
6
+ metadata.gz: eab5dfbaa386542421940cfc1490121a012f047f95c38f967b4fb4365aa9e677c8a2ce31dbf430a681092834a96557a1e8760145fc3adaa30c73079a4e63069b
7
+ data.tar.gz: 81f9b99115c31819d1335592bd2a18d6f0ee4441b1b2ef479b1d9c303ecc83dd3f12c83a199a77e36837918e671bfc8b4e8cba090be5180e180ef12fbe20bf4f
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.0
7
+ before_install: gem install bundler -v 2.0.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in smart_id.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ smart_id (0.1.0)
5
+ rest-client (~> 2.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ byebug (11.1.1)
11
+ diff-lcs (1.3)
12
+ domain_name (0.5.20190701)
13
+ unf (>= 0.0.5, < 1.0.0)
14
+ http-accept (1.7.0)
15
+ http-cookie (1.0.3)
16
+ domain_name (~> 0.5)
17
+ mime-types (3.3.1)
18
+ mime-types-data (~> 3.2015)
19
+ mime-types-data (3.2019.1009)
20
+ netrc (0.11.0)
21
+ rake (10.5.0)
22
+ rest-client (2.1.0)
23
+ http-accept (>= 1.7.0, < 2.0)
24
+ http-cookie (>= 1.0.2, < 2.0)
25
+ mime-types (>= 1.16, < 4.0)
26
+ netrc (~> 0.8)
27
+ rspec (3.9.0)
28
+ rspec-core (~> 3.9.0)
29
+ rspec-expectations (~> 3.9.0)
30
+ rspec-mocks (~> 3.9.0)
31
+ rspec-core (3.9.1)
32
+ rspec-support (~> 3.9.1)
33
+ rspec-expectations (3.9.0)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.9.0)
36
+ rspec-mocks (3.9.1)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.9.0)
39
+ rspec-support (3.9.2)
40
+ unf (0.1.4)
41
+ unf_ext
42
+ unf_ext (0.0.7.6)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ bundler (~> 2.0)
49
+ byebug
50
+ rake (~> 10.0)
51
+ rspec (~> 3.0)
52
+ smart_id!
53
+
54
+ BUNDLED WITH
55
+ 2.0.2
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 lekristapino
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # SmartId
2
+
3
+ This gem provides a wrapper around [Smart ID API](https://github.com/SK-EID/smart-id-documentation). All the necessary checks, listed in point 3.5 are implemented. Currently this gem only supports authentication actions.
4
+
5
+ # TODO
6
+ - [x] Add authentication functionality
7
+ - [ ] Add Signing functionality (see if possible)
8
+ - [ ] More test coverage
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'smart_id', "~> 0.1"
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install smart_id
25
+
26
+ ## Usage
27
+
28
+ ### Configuration
29
+
30
+ configuration can be done, by creating an initializer file and loading it before the application starts.
31
+
32
+ ```ruby
33
+ SmartId.configure do |config|
34
+ config.relying_party_uuid = "MySmartIdUUID"
35
+ config.relying_party_name = "My Smart ID name"
36
+ config.environment = "demo" # possible options ar "demo" and "production", uses according smart-id parameters and keys
37
+ config.default_certificate_level = "ADVANCED" # Possible options are "ADVANCED" or "QUALIFIED". Defaults to "ADVANCED"
38
+ config.poller_timeout_secods = 10 # seconds to wait when fetching authentication confirmation
39
+ end
40
+
41
+ ```
42
+ ### Authentication types
43
+
44
+ Authentication can be done either with providing user's national identity number or an identity document number
45
+
46
+ For national identity number use
47
+ ```ruby
48
+ SmartId::Api::Authentication::IdentityNumber
49
+ ```
50
+
51
+ For document number use
52
+ ```ruby
53
+ SmartId::Api::Authentication::Document
54
+ ```
55
+ Smart ID authentication is done in 2 steps - **initializing the authentication** and then **getting confimation from Smart ID** service. Those two steps happen asynchronously, so some parameters should be persisted either in session storage or in database
56
+
57
+ * Back-end initializes authentification - user can see verification code on the app, and receives smart ID request to input PIN in they're mobile
58
+
59
+ * Back-end authenfication confirmation - check whether user has authenticated by correctly typing in they're PIN on the mobile device
60
+
61
+ ## Authentication Request
62
+
63
+ To initialize authentication make a controller action.
64
+
65
+ #### National identity number
66
+
67
+ ```ruby
68
+ class UserController < AplicationController
69
+ #...
70
+ def authenticate_smart_id
71
+ # authentication hash by default will generate random bytes, that will be hashed for signature check
72
+ # if you wish to provide your own randomization, you can pass a parameter to AuthenticationHash with the random string
73
+ # authentication_hash = SmartId::Utils::AuthenticationHash.new(SecureRandom.hex(64))
74
+ # each authentication should have a unique random string passed
75
+ authentication_hash = SmartId::Utils::AuthenticationHash.new
76
+
77
+ auth_response = SmartId::Api::Authentication::IdentityNumber.authenticate(
78
+ country: params[:country], # 2 character ISO 3166-1 alpha-2 format(for example EE, LT, LV, KZ)
79
+ identity_number: params[:identity_number],
80
+ authentication_hash: authentication_hash
81
+ )
82
+
83
+ session[:smart_id_session] = auth_response.session_id
84
+ session[:auth_hash] = authentication_hash.hash_data
85
+
86
+ # Screen/page after this call should show the user verification code, to see if it matches
87
+ # the one they see on their mobile device
88
+ render json: { verification_code: auth_response.verification_code }
89
+
90
+ end
91
+ #...
92
+ end
93
+
94
+ ```
95
+
96
+ #### Document number
97
+
98
+ ```ruby
99
+ class UserController < AplicationController
100
+ #...
101
+ def authenticate_smart_id
102
+ # authentication hash by default will generate random bytes, that will be hashed for signature check
103
+ # if you wish to provide your own randomization, you can pass a parameter to AuthenticationHash with the random string
104
+ # authentication_hash = SmartId::Utils::AuthenticationHash.new(SecureRandom.hex(64))
105
+ # each authentication should have a unique random string passeds
106
+ authentication_hash = SmartId::Utils::AuthenticationHash.new
107
+
108
+ auth_response = SmartId::Api::Authentication::Document.authenticate(
109
+ document_number: params[:document_number],
110
+ authentication_hash: authentication_hash
111
+ )
112
+
113
+ session[:smart_id_session] = auth_response.session_id
114
+ session[:auth_hash] = authentication_hash.hash_data
115
+
116
+ # Screen/page after this call should show the user verification code, to see if it matches
117
+ # the one they see on their mobile device
118
+ render json: { verification_code: auth_response.verification_code }
119
+
120
+ end
121
+ #...
122
+ end
123
+
124
+ ```
125
+
126
+ ## Authentication Confirmation
127
+ Create another controller action
128
+
129
+ ```ruby
130
+ class UserController < AplicationController
131
+ #...
132
+ def confirm_smart_id
133
+ # use hash_data saved on authentication initialization as parameter
134
+ authentication_hash = SmartId::Utils::AuthenticationHash.new(session[:auth_hash])
135
+
136
+ confirmation_response = SmartId::Api::Authentication::ConfirmationPoller.confirm(
137
+ session_id: session[:smart_id_session],
138
+ authentication_hash: authentication_hash,
139
+ # if true, will continously make requests to smart-id and return only after verification is completed
140
+ # you can set this parameter to false, to handle polling yourself
141
+ poll: true # default - true
142
+ )
143
+ end
144
+ #...
145
+ end
146
+ ```
147
+
148
+
149
+ ## Response structure
150
+
151
+ confirmation response will have the following attributes
152
+ ```ruby
153
+ authentication_hash = SmartId::Utils::AuthenticationHash.new(session[:auth_hash])
154
+
155
+ confirmation_response = SmartId::Api::Authentication::ConfirmationPoller.confirm(
156
+ session_id: session[:smart_id_session],
157
+ authentication_hash: authentication_hash,
158
+ # if true, will continously make requests to smart-id and return only after verification is completed
159
+ # you can set this parameter to false, to handle polling yourself
160
+ poll: true # default - true
161
+ )
162
+
163
+ confirmation_response.confirmation_running? # => true/false whether the user has finished authentication. Relevant, only if polling is not handled by the gem (with `poll` parameter set to false)
164
+ confirmation_response.end_result # => end result of the verification. possible values are "OK"/"USER_REFUSED"/"TIMEOUT"/"DOCUMENT_UNUSABLE", see details in https://github.com/SK-EID/smart-id-documentation#5-session-end-result-codes
165
+ confirmation_response.document_number #=> document number for user
166
+ confirmation_response.certificate_level #=> certificate level for user - values are "ADVANCED" or "QUALIFIED"
167
+
168
+ confirmation_response.certificate.content.given_name #=> given name for user
169
+ confirmation_response.certificate.content.surname #=> surname for user
170
+ confirmation_response.certificate.content.serial_number #=> string, that includes user's national identity number
171
+
172
+ ```
173
+
174
+ ## Customization options
175
+ You can provide extra parameters when initializing authentication (for both - identity number and document)
176
+
177
+ ```ruby
178
+ SmartId::Api::Authentication::Document.authenticate(
179
+ document_number: "", # REQUIRED - document number
180
+ authentication_hash: obj, # REQUIRED - authentification hash object of SmartId::Utils::AuthenticationHash
181
+ certificate_level: "", # OPTIONAL - Either "ADVANCED" or "QUALIFIED" - if none are provided, default certificate level is used
182
+ display_text: nil, # OPTIONAL - Text that user will see on their mobile device when asked for authentication
183
+ multiple_choice: false, # OPTIONAL - If true, user will be asked to choose the correct verification code from supplied options on their device
184
+ )
185
+
186
+ SmartId::Api::Authentication::IdentityNumber.authenticate(
187
+ country: "", # REQUIRED - 2 character ISO 3166-1 alpha-2 format(for example EE, LT, LV, KZ)
188
+ identity_number: "", # REQUIRED - natioanl identity number
189
+ authentication_hash: obj, # REQUIRED - authentification hash object of SmartId::Utils::AuthenticationHash
190
+ certificate_level: "", # OPTIONAL - Either "ADVANCED" or "QUALIFIED" - if none are provided, default certificate level is used
191
+ display_text: nil, # OPTIONAL - Text that user will see on their mobile device when asked for authentication
192
+ multiple_choice: false, # OPTIONAL - If true, user will be asked to choose the correct verification code from supplied options on their device
193
+ )
194
+ ```
195
+
196
+ ## Exceptions
197
+ All exceptions inherit from `SmartId::Exception`
198
+ | Exception class | Description |
199
+ | --------------- | :-----------: |
200
+ | `SmartId::InvalidParamsError` | either country or identity_number were not provided when trying to authenticate with identity number |
201
+ | `SmartId::ConnectionError` | authentication/confirmation request failed, when rescuing see `e.original_error` for more details |
202
+ | `SmartId::NoUserFoundError`| user with the supplied parameters (id number, document number, country) does not exist in smart ID system |
203
+ | `SmartId::SSLCertificateNotVerified` | SSL certificate for smart ID service was not verified. Check for newest version of this gem to always keep cerficates updated |
204
+ | `SmartId::InvalidResponseCertificate` | Certificate used in confirmation response is invalid|
205
+ | `SmartId::InvalidResponseSignature` | Signature used in confirmation response is invalid. |
206
+ | `SmartId::IncorrectAccountLevelError` | User's Smart ID account is below the required level by the authentication request ( "ADVANCED" < "QUALIFIED") |
207
+ | `SmartId::InvalidPermissionsError` | Relying Party has no permission to issue the request. This may happen when Relying Party has no permission to invoke operations on accounts with ADVANCED certificates. |
208
+ | `SmartId::OutdatedApiError` | API used by the gem is outdated, please see if you are running the newest version of the gem |
209
+ | `SmartId::SystemUnderMaintenanceError` | Smart ID System is under maintenance, try again later |
210
+
211
+ ## Testing
212
+ Smart ID demo environment has provided some sample values to use when testing applications see [Smart ID WIKI page](https://github.com/SK-EID/smart-id-documentation/wiki/Environment-technical-parameters)
213
+ ## Development
214
+
215
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
216
+
217
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
218
+
219
+ ## Contributing
220
+
221
+ Bug reports and pull requests are welcome on GitHub at https://github.com/zippyvision/smart-id-ruby.
222
+
223
+ ## License
224
+
225
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "smart_id"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/smart_id.rb ADDED
@@ -0,0 +1,64 @@
1
+ require "smart_id/version"
2
+ require "smart_id/utils/authentication_hash"
3
+ require "smart_id/utils/certificate_validator"
4
+ require "smart_id/utils/verification_code_calculator"
5
+ require "smart_id/api/request"
6
+ require "smart_id/api/response"
7
+ require "smart_id/api/confirmation_response"
8
+ require "smart_id/api/authentication/identity_number"
9
+ require "smart_id/api/authentication/document"
10
+ require "smart_id/api/authentication/confirmation_poller"
11
+ require "smart_id/authentication_certificate/certificate"
12
+ require "smart_id/authentication_certificate/content"
13
+
14
+ module SmartId
15
+ @@environment = "DEMO" # possible options are demo and production
16
+ @@relying_party_uuid = nil
17
+ @@relying_party_name = nil
18
+ @@default_certificate_level = "ADVANCED" # possible values are "ADVANCED", "QUALIFIED"
19
+ @@poller_timeout_seconds = 10
20
+
21
+ def self.configure(&block)
22
+ yield(self)
23
+ end
24
+
25
+ def self.relying_party_uuid=(value)
26
+ @@relying_party_uuid = value
27
+ end
28
+
29
+ def self.relying_party_uuid
30
+ @@relying_party_uuid
31
+ end
32
+
33
+ def self.relying_party_name=(value)
34
+ @@relying_party_name = value
35
+ end
36
+
37
+ def self.relying_party_name
38
+ @@relying_party_name
39
+ end
40
+
41
+ def self.default_certificate_level=(value)
42
+ @@default_certificate_level = value
43
+ end
44
+
45
+ def self.default_certificate_level
46
+ @@default_certificate_level
47
+ end
48
+
49
+ def self.poller_timeout_seconds=(value)
50
+ @@poller_timeout_seconds = value
51
+ end
52
+
53
+ def self.poller_timeout_seconds
54
+ @@poller_timeout_seconds
55
+ end
56
+
57
+ def self.environment=(value)
58
+ @@environment = value.upcase
59
+ end
60
+
61
+ def self.environment
62
+ @@environment
63
+ end
64
+ end
@@ -0,0 +1,55 @@
1
+ require "rest-client"
2
+ require "smart_id/exceptions"
3
+ require "smart_id/utils/authentication_hash"
4
+ require "json"
5
+
6
+ module SmartId::Api
7
+ module Authentication
8
+ class Base
9
+ attr_reader :authentication_hash
10
+
11
+ def self.authenticate(**opts)
12
+ new(**opts).call
13
+ end
14
+
15
+ def initialize(**opts)
16
+ @authentication_hash = opts[:authentication_hash]
17
+ @display_text = opts[:display_text]
18
+ @certificate_level = opts[:certificate_level]
19
+ @multiple_choice = opts[:multiple_choice]
20
+ end
21
+
22
+
23
+ def call
24
+ response = SmartId::Api::Request.execute(method: :post, uri: api_uri, params: request_params)
25
+ SmartId::Api::Response.new(JSON.parse(response.body), authentication_hash)
26
+ end
27
+
28
+ private
29
+
30
+ def request_params
31
+ params = {
32
+ relyingPartyUUID: SmartId.relying_party_uuid,
33
+ relyingPartyName: SmartId.relying_party_name,
34
+ certificateLevel: @certificate_level || SmartId.default_certificate_level,
35
+ hash: authentication_hash.calculate_base64_digest,
36
+ hashType: "SHA256"
37
+ }
38
+
39
+ if @display_text
40
+ params.merge!(displayText: @display_text)
41
+ end
42
+
43
+ if @multiple_choice
44
+ params.merge!(requestProperties: { vcChoice: @multiple_choice })
45
+ end
46
+
47
+ params
48
+ end
49
+
50
+ def api_uri
51
+ raise NotImplementedError
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ module SmartId::Api
2
+ module Authentication
3
+ class ConfirmationPoller
4
+ BASE_URI = "session/"
5
+
6
+ def self.confirm(session_id:, authentication_hash:, poll: true)
7
+ new(session_id, authentication_hash, poll).call
8
+
9
+ end
10
+
11
+ def initialize(session_id, authentication_hash, poll)
12
+ @session_id = session_id
13
+ @authentication_hash = authentication_hash
14
+ @poll = poll
15
+ end
16
+
17
+ def call
18
+ params = { timeoutMs: SmartId.poller_timeout_seconds * 1000 }
19
+ uri = BASE_URI + @session_id
20
+
21
+ raw_response = SmartId::Api::Request.execute(method: :get, uri: uri, params: params)
22
+
23
+ response = SmartId::Api::ConfirmationResponse.new(
24
+ JSON.parse(raw_response.body),
25
+ @authentication_hash.hash_data
26
+ )
27
+
28
+ # repeat request if confirmation is still running
29
+ if response.confirmation_running? && @poll
30
+ call
31
+ else
32
+ response
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ require "smart_id/api/authentication/base"
2
+
3
+ module SmartId::Api
4
+ module Authentication
5
+ class Document < Base
6
+ BASE_URI = "authentication/document"
7
+
8
+ def initialize(**opts)
9
+ @document_number = opts[:document_number]
10
+ super(**opts)
11
+ end
12
+
13
+ private
14
+
15
+ def api_uri
16
+ "#{BASE_URI}/#{@document_number}"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ require "smart_id/api/authentication/base"
2
+ require "smart_id/exceptions"
3
+
4
+ module SmartId::Api
5
+ module Authentication
6
+ class IdentityNumber < Base
7
+ BASE_URI = "authentication/pno"
8
+
9
+ # @param country: 2 character ISO 3166-1 alpha-2 format(for example EE, LT, LV, KZ)
10
+ # @param identity_number: national identity number of the individuals
11
+ def initialize(**opts)
12
+ @country = opts[:country].upcase
13
+ @identity_number = opts[:identity_number]
14
+
15
+ unless @country && @identity_number
16
+ raise InvalidParamsError
17
+ end
18
+
19
+ super(**opts)
20
+ end
21
+
22
+ private
23
+
24
+ def api_uri
25
+ "#{BASE_URI}/#{@country}/#{@identity_number}"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,55 @@
1
+ module SmartId::Api
2
+ class ConfirmationResponse
3
+ RUNNING_STATE = "RUNNING"
4
+ COMPLETED_STATE = "COMPLETE"
5
+
6
+ attr_reader :body
7
+
8
+ def initialize(response_body, hashed_data)
9
+ @body = response_body
10
+ validate!(hashed_data)
11
+ end
12
+
13
+ def confirmation_running?
14
+ state == RUNNING_STATE
15
+ end
16
+
17
+ def state
18
+ @body["state"]
19
+ end
20
+
21
+ def end_result
22
+ @body.dig("result", "endResult")
23
+ end
24
+
25
+ def document_number
26
+ @body.dig("result", "documentNumber")
27
+ end
28
+
29
+ def certificate_level
30
+ @body.dig("cert", "certificateLevel")
31
+ end
32
+
33
+ def certificate
34
+ @certificate ||= SmartId::AuthenticationCertificate::Certificate.new(@body.dig("cert", "value"))
35
+ end
36
+
37
+ def signature_algorithm
38
+ @body.dig("signature", "algorithm")
39
+ end
40
+
41
+ def signature
42
+ @body.dig("signature", "value")
43
+ end
44
+
45
+ def ignored_properties
46
+ @body["ignoredProperties"]
47
+ end
48
+
49
+ private
50
+
51
+ def validate!(hashed_data)
52
+ SmartId::Utils::CertificateValidator.validate!(hashed_data, signature, certificate)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,88 @@
1
+ require 'logger'
2
+ require 'date'
3
+ module SmartId::Api
4
+ class Request
5
+ DEMO_BASE_URL = "https://sid.demo.sk.ee/smart-id-rp/v1/"
6
+ PRODUCTION_BASE_URL = "https://rp-api.smart-id.com/v1/"
7
+
8
+ DEMO_SSL_KEY = "QLZIaH7Qx9Rjq3gyznQuNsvwMQb7maC5L4SLu/z5qNU="
9
+ PROD_KEY_EXPIRY = Date.new(2020,11,5)
10
+ PRODUCTION_SSL_KEY = "l2uvq6ftLN4LZ+8Un+71J2vH1BT9wTbtrE5+Fj3Vc5g="
11
+
12
+ def initialize(method, uri, params)
13
+ @method = method
14
+ @url = self.class.const_get("#{SmartId.environment}_BASE_URL") + uri
15
+ @params = params
16
+ @logger = Logger.new(STDOUT)
17
+ end
18
+
19
+ def self.execute(method:, uri:, params:)
20
+ begin
21
+ api_request = new(method, uri, params)
22
+ api_request.execute
23
+ rescue RestClient::RequestFailed => e
24
+ case e.http_code
25
+ when 471
26
+ raise SmartId::IncorrectAccountLevelError
27
+ when 403
28
+ raise SmartId::InvalidPermissionsError
29
+ when 404
30
+ raise SmartId::UserNotFoundError
31
+ when 480
32
+ raise SmartId::OutdatedApiError
33
+ when 580
34
+ raise SmartId::SystemUnderMaintenanceError
35
+ else
36
+ raise SmartId::ConnectionError.new(e)
37
+ end
38
+ rescue RestClient::SSLCertificateNotVerified
39
+ raise SmartId::SSLCertificateNotVerified
40
+ end
41
+ end
42
+
43
+ def maybe_warn_of_ssl_key_expiry
44
+ if (PROD_KEY_EXPIRY - Date.today).to_i < 60
45
+ @logger.warn("[Smart-id-Ruby] SSL KEY for security checks will soon expire, please update to newer version of this gem")
46
+ end
47
+ end
48
+
49
+ def execute
50
+ maybe_warn_of_ssl_key_expiry
51
+
52
+ if @method.to_sym == :post
53
+ attrs = post_request_attrs
54
+ else
55
+ attrs = get_request_attrs
56
+ end
57
+
58
+ request = RestClient::Request.execute(**attrs)
59
+ end
60
+
61
+ private
62
+
63
+ def default_attrs
64
+ {
65
+ method: @method,
66
+ url: @url,
67
+ headers: { content_type: :json, accept: :json },
68
+ timeout: SmartId.poller_timeout_seconds + 1,
69
+ ssl_verify_callback: lambda do |_, cert_store|
70
+ provided_pub_key = cert_store.chain[0].public_key
71
+ saved_key = self.class.const_get("#{SmartId.environment}_SSL_KEY")
72
+ Digest::SHA256.digest(provided_pub_key.to_der) == Base64.decode64(saved_key)
73
+ end
74
+ }
75
+ end
76
+
77
+ def get_request_attrs
78
+ default_attrs.merge(headers: {
79
+ **default_attrs[:headers],
80
+ params: @params
81
+ })
82
+ end
83
+
84
+ def post_request_attrs
85
+ default_attrs.merge(payload: JSON.generate(@params))
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,15 @@
1
+ module SmartId::Api
2
+ class Response
3
+ attr_reader :session_id
4
+
5
+ def initialize(response_body, authentication_hash)
6
+ @body = response_body
7
+ @session_id = response_body["sessionID"]
8
+ @authentication_hash = authentication_hash
9
+ end
10
+
11
+ def verification_code
12
+ @verification_code ||= SmartId::VerificationCodeCalculator.calculate(@authentication_hash.calculate_digest)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module SmartId
2
+ module AuthenticationCertificate
3
+ class Certificate
4
+ def initialize(base64_cert)
5
+ @base64_cert = base64_cert
6
+ end
7
+
8
+ def content
9
+ @content ||= SmartId::AuthenticationCertificate::Content.new(cert.subject.to_s)
10
+ end
11
+
12
+ def cert
13
+ @cert ||= OpenSSL::X509::Certificate.new(Base64.decode64(@base64_cert))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ module SmartId
2
+ module AuthenticationCertificate
3
+ class Content
4
+ def initialize(raw_content)
5
+ @raw_content = raw_content
6
+ end
7
+
8
+ def given_name
9
+ structured_raw_content["GN"].gsub(",", " ")
10
+ end
11
+
12
+ def surname
13
+ structured_raw_content["SN"].gsub(",", " ")
14
+ end
15
+
16
+ def country
17
+ structured_raw_content["C"].gsub(",", " ")
18
+ end
19
+
20
+ def all_info
21
+ structured_raw_content["CN"]
22
+ end
23
+
24
+ def organizational_unit
25
+ structured_raw_content["OU"]
26
+ end
27
+
28
+ def serial_number
29
+ structured_raw_content["serialNumber"]
30
+ end
31
+
32
+ private
33
+
34
+ def structured_raw_content
35
+ return @structured_raw_content if @structured_raw_content
36
+ @structured_raw_content = @raw_content.split("/").each_with_object({}) do |c, result|
37
+ if c.include?("=")
38
+ key, val = c.split("=")
39
+ result[key] = val
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ module SmartId
2
+ class Exception < ::Exception; end
3
+ class InvalidParamsError < Exception; end
4
+ class SSLCertificateNotVerified < Exception; end
5
+ class InvalidResponseCertificate < Exception; end
6
+ class InvalidResponseSignature < Exception; end
7
+ class UserNotFoundError < Exception; end
8
+ class OutdatedApiError < Exception; end
9
+ class SystemUnderMaintenanceError < Exception; end
10
+
11
+ class ConnectionError < Exception;
12
+ attr_reader :original_error
13
+ def initialize(original_error)
14
+ @original_error = original_error
15
+ end
16
+ end
17
+
18
+ class IncorrectAccountLevelError < Exception
19
+ def message
20
+ "User exists, but has lower level account than required by request"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ require 'digest'
2
+ require 'base64'
3
+
4
+ module SmartId
5
+ module Utils
6
+ class AuthenticationHash
7
+ attr_reader :hash_data
8
+
9
+ def initialize(hash_data = nil)
10
+ @hash_data = hash_data || random_bytes
11
+ end
12
+
13
+ def calculate_digest
14
+ Digest::SHA256.digest(hash_data)
15
+ end
16
+
17
+ def calculate_base64_digest
18
+ Base64.strict_encode64(calculate_digest)
19
+ end
20
+
21
+ private
22
+
23
+ def random_bytes
24
+ OpenSSL::Random.random_bytes(64)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,55 @@
1
+ module SmartId::Utils
2
+ class CertificateValidator
3
+ def self.validate!(hash_data, signature, certificate)
4
+ obj = new(hash_data, signature, certificate)
5
+ obj.validate_certificate!
6
+ obj.validate_signature!
7
+ end
8
+
9
+ def initialize(hash_data, signature, certificate)
10
+ @hash_data = hash_data
11
+ @signature = signature
12
+ begin
13
+ @certificate = certificate.cert
14
+ rescue Exception
15
+ debugger
16
+ end
17
+ end
18
+
19
+ def certificate_valid?
20
+ ### TODO: Currently not working, because of error "unable to get local issuer certificate" - same error in bash with openssl
21
+ # cert_store = OpenSSL::X509::Store.new
22
+ # cert_chain.each {|c| cert_store.add_cert(c) }
23
+ # cert_store.add_dir(File.dirname(__FILE__)+"/../../../trusted_certs/")
24
+ # cert_store.purpose = OpenSSL::X509::PURPOSE_ANY
25
+ # OpenSSL::X509::Store.new.verify(@certificate) &&
26
+ @certificate.not_before.to_date < Date.today &&
27
+ @certificate.not_after.to_date > Date.today
28
+ end
29
+
30
+ def validate_certificate!
31
+ unless certificate_valid?
32
+ raise SmartId::InvalidResponseCertificate
33
+ end
34
+ end
35
+
36
+ def cert_chain
37
+ [
38
+ OpenSSL::X509::Certificate.new(
39
+ File.read(File.dirname(__FILE__)+"/../../../trusted_certs/EID-SK_2016.pem.crt")
40
+ ),
41
+ OpenSSL::X509::Certificate.new(
42
+ File.read(File.dirname(__FILE__)+"/../../../trusted_certs/NQ-SK_2016.pem.crt")
43
+ )
44
+ ]
45
+ end
46
+
47
+ def validate_signature!
48
+ public_key = @certificate.public_key
49
+
50
+ unless public_key.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(@signature), @hash_data)
51
+ raise SmartId::InvalidResponseSignature
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,24 @@
1
+ module SmartId
2
+ module Utils
3
+ class VerificationCodeCalculator
4
+ ##
5
+ # The Verification Code (VC) is computed as:
6
+ #
7
+ # integer(SHA256(hash)[−2:−1]) mod 10000
8
+ #
9
+ # where we take SHA256 result, extract 2 rightmost bytes from it,
10
+ # interpret them as a big-endian unsigned short and take the last 4 digits in decimal for display.
11
+ #
12
+ # SHA256 is always used here, no matter what was the algorithm used to calculate hash.
13
+
14
+ def self.calculate(digest)
15
+ rightmost_bytes = digest[-2..-1]
16
+ int = rightmost_bytes.unpack('n*')[0]
17
+ paddable_string = (int % 10000).to_s.chars.last(4).join
18
+ pad = 4 - paddable_string.length
19
+
20
+ "0" * pad + paddable_string
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module SmartId
2
+ VERSION = "0.1.0"
3
+ end
data/smart_id.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "smart_id/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "smart_id"
7
+ spec.version = SmartId::VERSION
8
+ spec.authors = ["Kristaps Kulikovskis"]
9
+ spec.email = ["kristaps.kulikovskis@gmail.com"]
10
+
11
+ spec.summary = %q{Smart ID wrapper libary for using Smart ID in Ruby applications}
12
+ spec.homepage = "https://github.com/zippyvision/smart-id-ruby"
13
+ spec.license = "MIT"
14
+
15
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["changelog_uri"] = spec.homepage + "/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "rest-client", "~> 2.0"
31
+ spec.add_development_dependency "bundler", "~> 2.0"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "rspec", "~> 3.0"
34
+ spec.add_development_dependency "byebug"
35
+ end
@@ -0,0 +1,63 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1
3
+ MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
4
+ czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
5
+ CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy
6
+ MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl
7
+ ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS
8
+ b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB
9
+ AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy
10
+ euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO
11
+ bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw
12
+ WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d
13
+ MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE
14
+ 1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD
15
+ VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/
16
+ zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB
17
+ BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
18
+ BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV
19
+ v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG
20
+ E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
21
+ uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW
22
+ iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v
23
+ GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0=
24
+ -----END CERTIFICATE-----
25
+ -----BEGIN CERTIFICATE-----
26
+ MIIG4jCCBcqgAwIBAgIQO4A6a2nBKoxXxVAFMRvE2jANBgkqhkiG9w0BAQwFADB1
27
+ MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
28
+ czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
29
+ CSqGSIb3DQEJARYJcGtpQHNrLmVlMCAXDTE2MDgzMDA5MjEwOVoYDzIwMzAxMjE3
30
+ MjM1OTU5WjBgMQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVy
31
+ aW1pc2tlc2t1czEXMBUGA1UEYQwOTlRSRUUtMTA3NDcwMTMxFDASBgNVBAMMC0VJ
32
+ RC1TSyAyMDE2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7XWFN0j
33
+ 1CFoGIuVe9xRezEnA0Tk3vmvIpvURX+y7Z5DJsfub2mtpSLtbhXjAeynq9QV78zj
34
+ gQ73pNVGh+GQ6oPG7HF8KIlZuIYsf1+gBxPxNiLa0+sCWxa6p4HQbgdgYRVGod4I
35
+ Qbib9KbOki3wjCG5WiWh1SP9qcuTZVY+9zawkSMf65Px/Y4ChjtNFtY66MEvsPCh
36
+ lHHfsBNiUbtZ68jJNYCECjtkm0vxz2iiSXB2WRIv3/hTrRgMJ2CNMyFjRQoGQlpH
37
+ 010+fcisObKeyPwA8kI22Oto9MzLw7KsY524OD3B1L5MExYxHD916XIEHT/9gBP2
38
+ Zn8qZu/BllKdSIapOIJW9ZEw+3w5UOU6LT3tTSbAzeQAnD3eCABPifYwHYC0lmKs
39
+ PpQJqtx0Q3Jbm3BGReYiZ9KuK36nF/G78YjhM+yioERr2B/cKf31j0W/GuGvyHak
40
+ bokwy7nsbL30sTuRLR70Oqi5UBMy4e8J2CduR3R3NJw5UqpScJIchngsLAx+WsyC
41
+ 0w38AmMewMBcnlp/QbakKo52HrsYRR1m+NhCVDBy45Lzl8I0/OGd9Ikdg1h7T7SI
42
+ guZVpyzys8E0yfrcS5YMEd9hMqVPr7rszXCzbxyw0tVIk8QLMw/lI+XE1Oi7Skgz
43
+ A2i5Vpa6i2K0ard6GPHzRqGPTkjc5Z4DzZMCAwEAAaOCAn8wggJ7MB8GA1UdIwQY
44
+ MBaAFBLyWj7qVhy/zQas8fElyalL1BSZMB0GA1UdDgQWBBScCagHhww9rC6H/KCu
45
+ 0vtlSYgo+zAOBgNVHQ8BAf8EBAMCAQYwgcQGA1UdIASBvDCBuTA8BgcEAIvsQAEC
46
+ MDEwLwYIKwYBBQUHAgEWI2h0dHBzOi8vd3d3LnNrLmVlL3JlcG9zaXRvb3JpdW0v
47
+ Q1BTMDwGBwQAi+xAAQAwMTAvBggrBgEFBQcCARYjaHR0cHM6Ly93d3cuc2suZWUv
48
+ cmVwb3NpdG9vcml1bS9DUFMwOwYGBACPegECMDEwLwYIKwYBBQUHAgEWI2h0dHBz
49
+ Oi8vd3d3LnNrLmVlL3JlcG9zaXRvb3JpdW0vQ1BTMBIGA1UdEwEB/wQIMAYBAf8C
50
+ AQAwJwYDVR0lBCAwHgYIKwYBBQUHAwkGCCsGAQUFBwMCBggrBgEFBQcDBDB8Bggr
51
+ BgEFBQcBAQRwMG4wIAYIKwYBBQUHMAGGFGh0dHA6Ly9vY3NwLnNrLmVlL0NBMEoG
52
+ CCsGAQUFBzAChj5odHRwOi8vd3d3LnNrLmVlL2NlcnRzL0VFX0NlcnRpZmljYXRp
53
+ b25fQ2VudHJlX1Jvb3RfQ0EuZGVyLmNydDBBBgNVHR4EOjA4oTYwBIICIiIwCocI
54
+ AAAAAAAAAAAwIocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwJQYI
55
+ KwYBBQUHAQMEGTAXMBUGCCsGAQUFBwsCMAkGBwQAi+xJAQEwPQYDVR0fBDYwNDAy
56
+ oDCgLoYsaHR0cDovL3d3dy5zay5lZS9yZXBvc2l0b3J5L2NybHMvZWVjY3JjYS5j
57
+ cmwwDQYJKoZIhvcNAQEMBQADggEBAKSIoud5DSfhDU6yp+VrXYL40wi5zFTf19ha
58
+ /kO/zzLxZ1hf45VJmSyukMWaWXEqhaLWBZuw5kP78mQ0HyaRUennN0hom/pEiBz6
59
+ cuz9oc+xlmPAZM25ZoaLqa4upP2/+NCWoRTzYkIdc9MEECs5RMBUmyT1G4s8J6n8
60
+ L2M2yYadBMvPGJS3yXxYdc/b3a2foiw3kKa/q1tXAHXZCsuxFVYxXdZt3AwInYHe
61
+ mCVKjZg8BaRpvIEXd3AgJwt+9bpV/x0/MouRPNRv0jjWIx1sAlL94hO74WZDMFbZ
62
+ VaV6gpG77X2P3dPHKFIRWzjtSQJX4C5n1uvQBxO4ABoMswq0lq0=
63
+ -----END CERTIFICATE-----
@@ -0,0 +1,61 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1
3
+ MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
4
+ czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
5
+ CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy
6
+ MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl
7
+ ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS
8
+ b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB
9
+ AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy
10
+ euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO
11
+ bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw
12
+ WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d
13
+ MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE
14
+ 1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD
15
+ VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/
16
+ zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB
17
+ BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
18
+ BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV
19
+ v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG
20
+ E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
21
+ uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW
22
+ iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v
23
+ GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0=
24
+ -----END CERTIFICATE-----
25
+ -----BEGIN CERTIFICATE-----
26
+ MIIGYjCCBUqgAwIBAgIQV6nz7KIvDihXxU71YTbgWjANBgkqhkiG9w0BAQwFADB1
27
+ MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
28
+ czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
29
+ CSqGSIb3DQEJARYJcGtpQHNrLmVlMCAXDTE2MDgzMDA5MTYzN1oYDzIwMzAxMjE3
30
+ MjM1OTU5WjBfMQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVy
31
+ aW1pc2tlc2t1czEXMBUGA1UEYQwOTlRSRUUtMTA3NDcwMTMxEzARBgNVBAMMCk5R
32
+ LVNLIDIwMTYwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDdkRRNDxfO
33
+ 6oKU9GDrGLNQc41PA+pqDKCEcDhSw1bnkC/nDumg4PawQk8xklyDHr2ShrsFrTo5
34
+ wps5UcgxxTMqb98bmMxQYghqxu5NqqpaZopbbSj+qDYUzrZkXIlVe+HFpUt5ce9W
35
+ NpEmeenVAlt4ZaN1/srDfv3NSMmcF2r9XiUIIhDavxQ+QgPy3CrgT0Ja3yw/PLpF
36
+ /ajCNQWaGWJHYkgNVzrnrKhKYDhgorc3lSqGfTfhW2Xf5klvBZokPfbhD26csnPe
37
+ JjQQQJ2Loot3Z9/QPzfY/Qnqp5hjkvfqjKksX2wAt/UB+Hk4sRG+6Nqa3b+gxqMc
38
+ ih1eI/I93Ii6OC7LijhN2k0R9L5+ArgQXhlAQYZGeCAC/unHmpCkiUQrEJq27kst
39
+ mzoENnwQnF3mhq81KQGZul/Guw1fsQOolALESEWG6dTP1szaLeba4LYN707b9puR
40
+ OVXk1WLoau131KZnIdc/+Ktu2ni4SVL3+qKbJ7+oqIfiFAqlSuCPTKssdFC49m7V
41
+ G4bXnrYeA5svUQjCvpANmzXqRs6DmdctKPuXUj+W/gnQNoLOvIEkK30TD/RKd4eh
42
+ uzzYj9qirhqBDFg+Ipqh9OByK7aY6f9KZ6qKmKttcPb4R7arBtuQoBlqadcXoGig
43
+ o/kr/iXVRabWfGVM73iQo36RZrklrSu5awIDAQABo4ICADCCAfwwHwYDVR0jBBgw
44
+ FoAUEvJaPupWHL/NBqzx8SXJqUvUFJkwHQYDVR0OBBYEFHq3hV+h88xBt67p6gZR
45
+ CuD5AsisMA4GA1UdDwEB/wQEAwIBBjBGBgNVHSAEPzA9MDsGBgQAj3oBATAxMC8G
46
+ CCsGAQUFBwIBFiNodHRwczovL3d3dy5zay5lZS9yZXBvc2l0b29yaXVtL0NQUzAS
47
+ BgNVHRMBAf8ECDAGAQH/AgEAMCcGA1UdJQQgMB4GCCsGAQUFBwMJBggrBgEFBQcD
48
+ AgYIKwYBBQUHAwQwfAYIKwYBBQUHAQEEcDBuMCAGCCsGAQUFBzABhhRodHRwOi8v
49
+ b2NzcC5zay5lZS9DQTBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5zay5lZS9jZXJ0
50
+ cy9FRV9DZXJ0aWZpY2F0aW9uX0NlbnRyZV9Sb290X0NBLmRlci5jcnQwQQYDVR0e
51
+ BDowOKE2MASCAiIiMAqHCAAAAAAAAAAAMCKHIAAAAAAAAAAAAAAAAAAAAAAAAAAA
52
+ AAAAAAAAAAAAAAAAMCUGCCsGAQUFBwEDBBkwFzAVBggrBgEFBQcLAjAJBgcEAIvs
53
+ SQEBMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly93d3cuc2suZWUvcmVwb3NpdG9y
54
+ eS9jcmxzL2VlY2NyY2EuY3JsMA0GCSqGSIb3DQEBDAUAA4IBAQCu4HLsEBBpKmXw
55
+ agXpFkmEGqTOC/eYWrtwVZNnz/MB+z8c6TyxwW2cDmNwMwMfojXT447rQ/xlai/5
56
+ gjGkRwRE8P5W90h/JkO3rUWG4asrvPAwmkIiUHsHIHDVHCsSmhLNEgPdM4zP88/L
57
+ EmV89ZIvUWGjzZYcgBljYeAlK4dCy4/7U14JW9FCwvFjFOyfDcpoYwxbV7Jkbhsw
58
+ 9J8uzzxjspGCvoq5izeTGuRV+WtV+yy6W/UOnpmYOJ6jxzUoYq6fnQGU+J9CLVxj
59
+ jE8Jj25fuWSU3BvPs8cms7RzvvuZvPgQWm8IZt6L5P6EHOzeER3m3nftERhG2OXE
60
+ +MHJ+onc
61
+ -----END CERTIFICATE-----
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smart_id
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kristaps Kulikovskis
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-04-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - kristaps.kulikovskis@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/console
99
+ - bin/setup
100
+ - lib/smart_id.rb
101
+ - lib/smart_id/api/authentication/base.rb
102
+ - lib/smart_id/api/authentication/confirmation_poller.rb
103
+ - lib/smart_id/api/authentication/document.rb
104
+ - lib/smart_id/api/authentication/identity_number.rb
105
+ - lib/smart_id/api/confirmation_response.rb
106
+ - lib/smart_id/api/request.rb
107
+ - lib/smart_id/api/response.rb
108
+ - lib/smart_id/authentication_certificate/certificate.rb
109
+ - lib/smart_id/authentication_certificate/content.rb
110
+ - lib/smart_id/exceptions.rb
111
+ - lib/smart_id/utils/authentication_hash.rb
112
+ - lib/smart_id/utils/certificate_validator.rb
113
+ - lib/smart_id/utils/verification_code_calculator.rb
114
+ - lib/smart_id/version.rb
115
+ - smart_id.gemspec
116
+ - trusted_certs/EID-SK_2016.pem.crt
117
+ - trusted_certs/NQ-SK_2016.pem.crt
118
+ homepage: https://github.com/zippyvision/smart-id-ruby
119
+ licenses:
120
+ - MIT
121
+ metadata:
122
+ homepage_uri: https://github.com/zippyvision/smart-id-ruby
123
+ source_code_uri: https://github.com/zippyvision/smart-id-ruby
124
+ changelog_uri: https://github.com/zippyvision/smart-id-ruby/CHANGELOG.md
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubygems_version: 3.0.6
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: Smart ID wrapper libary for using Smart ID in Ruby applications
144
+ test_files: []