smart-id-ruby-client 0.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/CHANGELOG.md +13 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +436 -0
- data/Rakefile +12 -0
- data/lib/smart-id-ruby-client.rb +3 -0
- data/lib/smart_id_ruby/callback_url.rb +18 -0
- data/lib/smart_id_ruby/callback_url_util.rb +54 -0
- data/lib/smart_id_ruby/client.rb +124 -0
- data/lib/smart_id_ruby/configuration.rb +184 -0
- data/lib/smart_id_ruby/device_link_builder.rb +301 -0
- data/lib/smart_id_ruby/device_link_interaction.rb +67 -0
- data/lib/smart_id_ruby/errors/certificate_level_mismatch_error.rb +8 -0
- data/lib/smart_id_ruby/errors/document_unusable_error.rb +12 -0
- data/lib/smart_id_ruby/errors/error.rb +8 -0
- data/lib/smart_id_ruby/errors/expected_linked_session_error.rb +15 -0
- data/lib/smart_id_ruby/errors/no_suitable_account_of_requested_type_found_error.rb +10 -0
- data/lib/smart_id_ruby/errors/person_should_view_smart_id_portal_error.rb +8 -0
- data/lib/smart_id_ruby/errors/protocol_failure_error.rb +13 -0
- data/lib/smart_id_ruby/errors/relying_party_account_configuration_error.rb +10 -0
- data/lib/smart_id_ruby/errors/request_setup_error.rb +10 -0
- data/lib/smart_id_ruby/errors/request_validation_error.rb +8 -0
- data/lib/smart_id_ruby/errors/required_interaction_not_supported_by_app_error.rb +13 -0
- data/lib/smart_id_ruby/errors/response_error.rb +8 -0
- data/lib/smart_id_ruby/errors/server_maintenance_error.rb +8 -0
- data/lib/smart_id_ruby/errors/session_end_result_error.rb +15 -0
- data/lib/smart_id_ruby/errors/session_not_complete_error.rb +8 -0
- data/lib/smart_id_ruby/errors/session_not_found_error.rb +8 -0
- data/lib/smart_id_ruby/errors/session_secret_mismatch_error.rb +8 -0
- data/lib/smart_id_ruby/errors/session_timeout_error.rb +12 -0
- data/lib/smart_id_ruby/errors/smart_id_server_error.rb +12 -0
- data/lib/smart_id_ruby/errors/unprocessable_response_error.rb +9 -0
- data/lib/smart_id_ruby/errors/unsupported_client_api_version_error.rb +8 -0
- data/lib/smart_id_ruby/errors/user_account_not_found_error.rb +8 -0
- data/lib/smart_id_ruby/errors/user_account_unusable_error.rb +12 -0
- data/lib/smart_id_ruby/errors/user_refused_cert_choice_error.rb +14 -0
- data/lib/smart_id_ruby/errors/user_refused_confirmation_message_error.rb +13 -0
- data/lib/smart_id_ruby/errors/user_refused_confirmation_message_with_verification_choice_error.rb +13 -0
- data/lib/smart_id_ruby/errors/user_refused_display_text_and_pin_error.rb +13 -0
- data/lib/smart_id_ruby/errors/user_refused_error.rb +12 -0
- data/lib/smart_id_ruby/errors/user_selected_wrong_verification_code_error.rb +13 -0
- data/lib/smart_id_ruby/errors.rb +31 -0
- data/lib/smart_id_ruby/flows/base_builder.rb +90 -0
- data/lib/smart_id_ruby/flows/certificate_by_document_number_request_builder.rb +130 -0
- data/lib/smart_id_ruby/flows/device_link_authentication_session_request_builder.rb +208 -0
- data/lib/smart_id_ruby/flows/device_link_certificate_choice_session_request_builder.rb +112 -0
- data/lib/smart_id_ruby/flows/device_link_signature_session_request_builder.rb +286 -0
- data/lib/smart_id_ruby/flows/linked_notification_signature_session_request_builder.rb +235 -0
- data/lib/smart_id_ruby/flows/notification_authentication_session_request_builder.rb +184 -0
- data/lib/smart_id_ruby/flows/notification_certificate_choice_session_request_builder.rb +96 -0
- data/lib/smart_id_ruby/flows/notification_signature_session_request_builder.rb +272 -0
- data/lib/smart_id_ruby/models/authentication_identity.rb +19 -0
- data/lib/smart_id_ruby/models/authentication_response.rb +38 -0
- data/lib/smart_id_ruby/models/certificate_choice_response.rb +19 -0
- data/lib/smart_id_ruby/models/device_link_session_response.rb +34 -0
- data/lib/smart_id_ruby/models/notification_authentication_session_response.rb +25 -0
- data/lib/smart_id_ruby/models/notification_certificate_choice_session_response.rb +25 -0
- data/lib/smart_id_ruby/models/notification_signature_session_response.rb +29 -0
- data/lib/smart_id_ruby/models/session_status.rb +261 -0
- data/lib/smart_id_ruby/models/signature_response.rb +38 -0
- data/lib/smart_id_ruby/notification_interaction.rb +70 -0
- data/lib/smart_id_ruby/qr_code_generator.rb +65 -0
- data/lib/smart_id_ruby/rest/connector.rb +364 -0
- data/lib/smart_id_ruby/rest/session_status_poller.rb +125 -0
- data/lib/smart_id_ruby/rp_challenge.rb +37 -0
- data/lib/smart_id_ruby/rp_challenge_generator.rb +28 -0
- data/lib/smart_id_ruby/semantics_identifier.rb +35 -0
- data/lib/smart_id_ruby/validation/authentication_certificate_validator.rb +90 -0
- data/lib/smart_id_ruby/validation/authentication_identity_mapper.rb +227 -0
- data/lib/smart_id_ruby/validation/base_authentication_response_validator.rb +304 -0
- data/lib/smart_id_ruby/validation/certificate_choice_response_validator.rb +104 -0
- data/lib/smart_id_ruby/validation/certificate_validator.rb +170 -0
- data/lib/smart_id_ruby/validation/device_link_authentication_response_validator.rb +76 -0
- data/lib/smart_id_ruby/validation/error_result_handler.rb +88 -0
- data/lib/smart_id_ruby/validation/notification_authentication_response_validator.rb +16 -0
- data/lib/smart_id_ruby/validation/signature_payload_builder.rb +62 -0
- data/lib/smart_id_ruby/validation/signature_response_validator.rb +345 -0
- data/lib/smart_id_ruby/validation/signature_value_validator.rb +76 -0
- data/lib/smart_id_ruby/validation/trusted_ca_cert_store.rb +20 -0
- data/lib/smart_id_ruby/verification_code_calculator.rb +31 -0
- data/lib/smart_id_ruby/version.rb +5 -0
- data/lib/smart_id_ruby.rb +76 -0
- metadata +173 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartIdRuby
|
|
4
|
+
module Flows
|
|
5
|
+
# Builds notification certificate choice session requests.
|
|
6
|
+
class NotificationCertificateChoiceSessionRequestBuilder < BaseBuilder
|
|
7
|
+
NONCE_MAX_LENGTH = 30
|
|
8
|
+
|
|
9
|
+
def initialize(connector)
|
|
10
|
+
super(connector)
|
|
11
|
+
@certificate_level = nil
|
|
12
|
+
@nonce = nil
|
|
13
|
+
@capabilities = nil
|
|
14
|
+
@share_md_client_ip_address = nil
|
|
15
|
+
@semantics_identifier = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def with_certificate_level(certificate_level)
|
|
19
|
+
@certificate_level = certificate_level
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def with_nonce(nonce)
|
|
24
|
+
@nonce = nonce
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def with_capabilities(*capabilities)
|
|
29
|
+
@capabilities = normalize_capabilities(capabilities)
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def with_share_md_client_ip_address(share_md_client_ip_address)
|
|
34
|
+
@share_md_client_ip_address = share_md_client_ip_address
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def with_semantics_identifier(semantics_identifier)
|
|
39
|
+
@semantics_identifier = semantics_identifier
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def init_certificate_choice
|
|
44
|
+
validate_request_parameters
|
|
45
|
+
request = create_certificate_choice_request
|
|
46
|
+
response = init_certificate_choice_session(request)
|
|
47
|
+
validate_response_parameters(response)
|
|
48
|
+
SmartIdRuby::Models::NotificationCertificateChoiceSessionResponse.from_h(response)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def init_certificate_choice_session(request)
|
|
54
|
+
if @semantics_identifier.nil?
|
|
55
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'semanticIdentifier' must be set"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
connector.init_notification_certificate_choice(request, @semantics_identifier)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate_request_parameters
|
|
62
|
+
if blank?(relying_party_uuid)
|
|
63
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'relyingPartyUUID' cannot be empty"
|
|
64
|
+
end
|
|
65
|
+
if blank?(relying_party_name)
|
|
66
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'relyingPartyName' cannot be empty"
|
|
67
|
+
end
|
|
68
|
+
if !@nonce.nil? && (@nonce.empty? || @nonce.length > NONCE_MAX_LENGTH)
|
|
69
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'nonce' length must be between 1 and 30 characters"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def create_certificate_choice_request
|
|
74
|
+
{
|
|
75
|
+
relyingPartyUUID: relying_party_uuid,
|
|
76
|
+
relyingPartyName: relying_party_name,
|
|
77
|
+
certificateLevel: @certificate_level&.to_s,
|
|
78
|
+
nonce: @nonce,
|
|
79
|
+
capabilities: @capabilities,
|
|
80
|
+
requestProperties: request_properties
|
|
81
|
+
}.compact
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def request_properties
|
|
85
|
+
request_properties_for_share_md(@share_md_client_ip_address)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def validate_response_parameters(response)
|
|
89
|
+
return unless blank?(fetch_value(response, :sessionID))
|
|
90
|
+
|
|
91
|
+
raise SmartIdRuby::Errors::UnprocessableResponseError,
|
|
92
|
+
"Notification-based certificate choice response field 'sessionID' is missing or empty"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "base64"
|
|
4
|
+
require "openssl"
|
|
5
|
+
|
|
6
|
+
module SmartIdRuby
|
|
7
|
+
module Flows
|
|
8
|
+
# Builds notification signature session requests.
|
|
9
|
+
class NotificationSignatureSessionRequestBuilder < BaseBuilder
|
|
10
|
+
VERIFICATION_CODE_PATTERN = /\A[0-9]{4}\z/
|
|
11
|
+
NONCE_MAX_LENGTH = 30
|
|
12
|
+
DEFAULT_HASH_ALGORITHM = "SHA-512"
|
|
13
|
+
SUPPORTED_VC_TYPE = "numeric4"
|
|
14
|
+
|
|
15
|
+
def initialize(connector)
|
|
16
|
+
super(connector)
|
|
17
|
+
@document_number = nil
|
|
18
|
+
@semantics_identifier = nil
|
|
19
|
+
@certificate_level = nil
|
|
20
|
+
@nonce = nil
|
|
21
|
+
@capabilities = nil
|
|
22
|
+
@interactions = nil
|
|
23
|
+
@share_md_client_ip_address = nil
|
|
24
|
+
@signature_algorithm = "rsassa-pss"
|
|
25
|
+
@digest_input = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def with_document_number(document_number)
|
|
29
|
+
@document_number = document_number
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def with_semantics_identifier(semantics_identifier)
|
|
34
|
+
@semantics_identifier = semantics_identifier
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def with_certificate_level(certificate_level)
|
|
39
|
+
@certificate_level = certificate_level
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def with_nonce(nonce)
|
|
44
|
+
@nonce = nonce
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def with_capabilities(*capabilities)
|
|
49
|
+
@capabilities = normalize_capabilities(capabilities)
|
|
50
|
+
self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def with_interactions(interactions)
|
|
54
|
+
@interactions = interactions
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def with_share_md_client_ip_address(share_md_client_ip_address)
|
|
59
|
+
@share_md_client_ip_address = share_md_client_ip_address
|
|
60
|
+
self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def with_signature_algorithm(signature_algorithm)
|
|
64
|
+
@signature_algorithm = signature_algorithm
|
|
65
|
+
self
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def with_signable_data(signable_data)
|
|
69
|
+
if digest_input_kind == :signable_hash
|
|
70
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'digestInput' has already been set with SignableHash"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
@digest_input = build_signable_data_digest_input(signable_data)
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def with_signable_hash(signable_hash)
|
|
78
|
+
if digest_input_kind == :signable_data
|
|
79
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'digestInput' has already been set with SignableData"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@digest_input = build_signable_hash_digest_input(signable_hash)
|
|
83
|
+
self
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def init_signature_session
|
|
87
|
+
validate_request_parameters
|
|
88
|
+
request = create_signature_session_request
|
|
89
|
+
response = init_session(request)
|
|
90
|
+
validate_response_parameters(response)
|
|
91
|
+
SmartIdRuby::Models::NotificationSignatureSessionResponse.from_h(response)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def init_session(request)
|
|
97
|
+
if @semantics_identifier && @document_number
|
|
98
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Only one of 'semanticsIdentifier' or 'documentNumber' may be set"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if @document_number
|
|
102
|
+
connector.init_notification_signature_with_document(request, @document_number)
|
|
103
|
+
elsif @semantics_identifier
|
|
104
|
+
connector.init_notification_signature(request, @semantics_identifier)
|
|
105
|
+
else
|
|
106
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Either 'documentNumber' or 'semanticsIdentifier' must be set"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def create_signature_session_request
|
|
111
|
+
{
|
|
112
|
+
relyingPartyUUID: relying_party_uuid,
|
|
113
|
+
relyingPartyName: relying_party_name,
|
|
114
|
+
certificateLevel: @certificate_level&.to_s,
|
|
115
|
+
signatureProtocol: "RAW_DIGEST_SIGNATURE",
|
|
116
|
+
signatureProtocolParameters: {
|
|
117
|
+
digest: @digest_input[:digest],
|
|
118
|
+
signatureAlgorithm: @signature_algorithm.to_s,
|
|
119
|
+
signatureAlgorithmParameters: {
|
|
120
|
+
hashAlgorithm: @digest_input[:hash_algorithm].to_s
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
nonce: @nonce,
|
|
124
|
+
capabilities: @capabilities,
|
|
125
|
+
interactions: encode_interactions(@interactions),
|
|
126
|
+
requestProperties: request_properties
|
|
127
|
+
}.compact
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def validate_request_parameters
|
|
131
|
+
if blank?(relying_party_uuid)
|
|
132
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'relyingPartyUUID' cannot be empty"
|
|
133
|
+
end
|
|
134
|
+
if blank?(relying_party_name)
|
|
135
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'relyingPartyName' cannot be empty"
|
|
136
|
+
end
|
|
137
|
+
if @signature_algorithm.nil?
|
|
138
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'signatureAlgorithm' must be set"
|
|
139
|
+
end
|
|
140
|
+
if @digest_input.nil?
|
|
141
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'digestInput' must be set with either SignableData or SignableHash"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
validate_interactions
|
|
145
|
+
validate_nonce
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def validate_interactions
|
|
149
|
+
normalized_interactions = normalize_interactions(@interactions)
|
|
150
|
+
if normalized_interactions.empty?
|
|
151
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'interactions' cannot be empty"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
interaction_types = normalized_interactions.map { |interaction| interaction[:type] }
|
|
155
|
+
if interaction_types.uniq.length != interaction_types.length
|
|
156
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'interactions' cannot contain duplicate types"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def validate_nonce
|
|
161
|
+
return if @nonce.nil?
|
|
162
|
+
return if @nonce.length.between?(1, NONCE_MAX_LENGTH)
|
|
163
|
+
|
|
164
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'nonce' length must be between 1 and 30 characters"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def validate_response_parameters(response)
|
|
168
|
+
if blank?(fetch_value(response, :sessionID))
|
|
169
|
+
raise SmartIdRuby::Errors::UnprocessableResponseError,
|
|
170
|
+
"Notification-based signature response field 'sessionID' is missing or empty"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
verification_code = fetch_value(response, :vc)
|
|
174
|
+
if verification_code.nil?
|
|
175
|
+
raise SmartIdRuby::Errors::UnprocessableResponseError, "Notification-based signature response field 'vc' is missing"
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
vc_type = fetch_value(verification_code, :type)
|
|
179
|
+
if blank?(vc_type)
|
|
180
|
+
raise SmartIdRuby::Errors::UnprocessableResponseError,
|
|
181
|
+
"Notification-based signature response field 'vc.type' is missing or empty"
|
|
182
|
+
end
|
|
183
|
+
unless vc_type == SUPPORTED_VC_TYPE
|
|
184
|
+
raise SmartIdRuby::Errors::UnprocessableResponseError,
|
|
185
|
+
"Notification-based signature response field 'vc.type' contains unsupported value"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
vc_value = fetch_value(verification_code, :value)
|
|
189
|
+
if blank?(vc_value)
|
|
190
|
+
raise SmartIdRuby::Errors::UnprocessableResponseError,
|
|
191
|
+
"Notification-based signature response field 'vc.value' is missing or empty"
|
|
192
|
+
end
|
|
193
|
+
return if VERIFICATION_CODE_PATTERN.match?(vc_value.to_s)
|
|
194
|
+
|
|
195
|
+
raise SmartIdRuby::Errors::UnprocessableResponseError,
|
|
196
|
+
"Notification-based signature response field 'vc.value' does not match the required pattern"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def request_properties
|
|
200
|
+
request_properties_for_share_md(@share_md_client_ip_address)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def build_signable_data_digest_input(signable_data)
|
|
204
|
+
return nil if signable_data.nil?
|
|
205
|
+
|
|
206
|
+
data_to_sign, hash_algorithm = extract_signable_data(signable_data)
|
|
207
|
+
data = normalize_binary_input(data_to_sign)
|
|
208
|
+
algorithm_name = normalize_hash_algorithm(hash_algorithm)
|
|
209
|
+
|
|
210
|
+
digest = OpenSSL::Digest.new(openssl_algorithm_name(algorithm_name)).digest(data)
|
|
211
|
+
{ kind: :signable_data, digest: Base64.strict_encode64(digest), hash_algorithm: algorithm_name }
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def build_signable_hash_digest_input(signable_hash)
|
|
215
|
+
return nil if signable_hash.nil?
|
|
216
|
+
|
|
217
|
+
hash_to_sign, hash_algorithm = extract_signable_hash(signable_hash)
|
|
218
|
+
hash_bytes = normalize_binary_input(hash_to_sign)
|
|
219
|
+
algorithm_name = normalize_hash_algorithm(hash_algorithm)
|
|
220
|
+
|
|
221
|
+
{ kind: :signable_hash, digest: Base64.strict_encode64(hash_bytes), hash_algorithm: algorithm_name }
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def extract_signable_data(input)
|
|
225
|
+
if input.respond_to?(:to_h)
|
|
226
|
+
normalized = input.to_h.transform_keys(&:to_sym)
|
|
227
|
+
[normalized[:data_to_sign] || normalized[:data], normalized[:hash_algorithm]]
|
|
228
|
+
elsif input.respond_to?(:data_to_sign)
|
|
229
|
+
[input.data_to_sign, input.respond_to?(:hash_algorithm) ? input.hash_algorithm : nil]
|
|
230
|
+
else
|
|
231
|
+
[input, nil]
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def extract_signable_hash(input)
|
|
236
|
+
if input.respond_to?(:to_h)
|
|
237
|
+
normalized = input.to_h.transform_keys(&:to_sym)
|
|
238
|
+
[normalized[:hash_to_sign] || normalized[:hash] || normalized[:digest], normalized[:hash_algorithm]]
|
|
239
|
+
elsif input.respond_to?(:hash_to_sign)
|
|
240
|
+
[input.hash_to_sign, input.respond_to?(:hash_algorithm) ? input.hash_algorithm : nil]
|
|
241
|
+
else
|
|
242
|
+
[input, nil]
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def normalize_binary_input(input)
|
|
247
|
+
data = input.is_a?(String) ? input.dup : input
|
|
248
|
+
data = data.pack("C*") if data.is_a?(Array) && data.all? { |value| value.is_a?(Integer) && value.between?(0, 255) }
|
|
249
|
+
if data.nil? || data.to_s.bytesize.zero?
|
|
250
|
+
raise SmartIdRuby::Errors::RequestSetupError, "Value for 'digestInput' must be set with either SignableData or SignableHash"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
data
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def normalize_hash_algorithm(hash_algorithm)
|
|
257
|
+
value = hash_algorithm&.to_s
|
|
258
|
+
return DEFAULT_HASH_ALGORITHM if blank?(value)
|
|
259
|
+
|
|
260
|
+
value
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def openssl_algorithm_name(hash_algorithm)
|
|
264
|
+
hash_algorithm.to_s.delete("-")
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def digest_input_kind
|
|
268
|
+
@digest_input && @digest_input[:kind]
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartIdRuby
|
|
4
|
+
module Models
|
|
5
|
+
# Represents mapped identity details from authentication certificate.
|
|
6
|
+
class AuthenticationIdentity
|
|
7
|
+
attr_reader :given_name, :surname, :identity_number, :country, :auth_certificate, :date_of_birth
|
|
8
|
+
|
|
9
|
+
def initialize(given_name: nil, surname: nil, identity_number: nil, country: nil, auth_certificate: nil, date_of_birth: nil)
|
|
10
|
+
@given_name = given_name
|
|
11
|
+
@surname = surname
|
|
12
|
+
@identity_number = identity_number
|
|
13
|
+
@country = country
|
|
14
|
+
@auth_certificate = auth_certificate
|
|
15
|
+
@date_of_birth = date_of_birth
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartIdRuby
|
|
4
|
+
module Models
|
|
5
|
+
# Represents validated authentication response data.
|
|
6
|
+
class AuthenticationResponse
|
|
7
|
+
attr_reader :end_result, :document_number, :signature_value_base64, :server_random,
|
|
8
|
+
:user_challenge, :flow_type, :signature_algorithm, :certificate_value,
|
|
9
|
+
:certificate_level, :interaction_type_used, :device_ip_address
|
|
10
|
+
|
|
11
|
+
def initialize(
|
|
12
|
+
end_result:,
|
|
13
|
+
document_number:,
|
|
14
|
+
signature_value_base64:,
|
|
15
|
+
server_random:,
|
|
16
|
+
user_challenge:,
|
|
17
|
+
flow_type:,
|
|
18
|
+
signature_algorithm:,
|
|
19
|
+
certificate_value:,
|
|
20
|
+
certificate_level:,
|
|
21
|
+
interaction_type_used:,
|
|
22
|
+
device_ip_address:
|
|
23
|
+
)
|
|
24
|
+
@end_result = end_result
|
|
25
|
+
@document_number = document_number
|
|
26
|
+
@signature_value_base64 = signature_value_base64
|
|
27
|
+
@server_random = server_random
|
|
28
|
+
@user_challenge = user_challenge
|
|
29
|
+
@flow_type = flow_type
|
|
30
|
+
@signature_algorithm = signature_algorithm
|
|
31
|
+
@certificate_value = certificate_value
|
|
32
|
+
@certificate_level = certificate_level
|
|
33
|
+
@interaction_type_used = interaction_type_used
|
|
34
|
+
@device_ip_address = device_ip_address
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartIdRuby
|
|
4
|
+
module Models
|
|
5
|
+
# Represents validated certificate choice response data.
|
|
6
|
+
class CertificateChoiceResponse
|
|
7
|
+
attr_reader :end_result, :certificate, :certificate_level, :document_number, :interaction_flow_used, :device_ip_address
|
|
8
|
+
|
|
9
|
+
def initialize(end_result:, certificate:, certificate_level:, document_number:, interaction_flow_used:, device_ip_address:)
|
|
10
|
+
@end_result = end_result
|
|
11
|
+
@certificate = certificate
|
|
12
|
+
@certificate_level = certificate_level
|
|
13
|
+
@document_number = document_number
|
|
14
|
+
@interaction_flow_used = interaction_flow_used
|
|
15
|
+
@device_ip_address = device_ip_address
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartIdRuby
|
|
4
|
+
module Models
|
|
5
|
+
# Represents device-link session initialization response.
|
|
6
|
+
class DeviceLinkSessionResponse
|
|
7
|
+
attr_reader :session_id, :session_token, :session_secret, :device_link_base, :received_at
|
|
8
|
+
|
|
9
|
+
def initialize(session_id:, session_token:, session_secret:, device_link_base:, received_at: Time.now)
|
|
10
|
+
@session_id = session_id
|
|
11
|
+
@session_token = session_token
|
|
12
|
+
@session_secret = session_secret
|
|
13
|
+
@device_link_base = device_link_base
|
|
14
|
+
@received_at = received_at
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.from_h(payload)
|
|
18
|
+
return payload if payload.is_a?(self)
|
|
19
|
+
return new(session_id: nil, session_token: nil, session_secret: nil, device_link_base: nil) unless payload.is_a?(Hash)
|
|
20
|
+
|
|
21
|
+
new(
|
|
22
|
+
session_id: fetch(payload, :sessionID),
|
|
23
|
+
session_token: fetch(payload, :sessionToken),
|
|
24
|
+
session_secret: fetch(payload, :sessionSecret),
|
|
25
|
+
device_link_base: fetch(payload, :deviceLinkBase)
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.fetch(payload, key)
|
|
30
|
+
payload[key] || payload[key.to_s]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartIdRuby
|
|
4
|
+
module Models
|
|
5
|
+
# Represents notification-based authentication session initialization response.
|
|
6
|
+
class NotificationAuthenticationSessionResponse
|
|
7
|
+
attr_reader :session_id
|
|
8
|
+
|
|
9
|
+
def initialize(session_id:)
|
|
10
|
+
@session_id = session_id
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.from_h(payload)
|
|
14
|
+
return payload if payload.is_a?(self)
|
|
15
|
+
return new(session_id: nil) unless payload.is_a?(Hash)
|
|
16
|
+
|
|
17
|
+
new(session_id: fetch(payload, :sessionID))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.fetch(payload, key)
|
|
21
|
+
payload[key] || payload[key.to_s]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartIdRuby
|
|
4
|
+
module Models
|
|
5
|
+
# Represents notification-based certificate choice session initialization response.
|
|
6
|
+
class NotificationCertificateChoiceSessionResponse
|
|
7
|
+
attr_reader :session_id
|
|
8
|
+
|
|
9
|
+
def initialize(session_id:)
|
|
10
|
+
@session_id = session_id
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.from_h(payload)
|
|
14
|
+
return payload if payload.is_a?(self)
|
|
15
|
+
return new(session_id: nil) unless payload.is_a?(Hash)
|
|
16
|
+
|
|
17
|
+
new(session_id: fetch(payload, :sessionID))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.fetch(payload, key)
|
|
21
|
+
payload[key] || payload[key.to_s]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartIdRuby
|
|
4
|
+
module Models
|
|
5
|
+
# Represents notification-based signature session initialization response.
|
|
6
|
+
class NotificationSignatureSessionResponse
|
|
7
|
+
attr_reader :session_id, :vc
|
|
8
|
+
|
|
9
|
+
def initialize(session_id:, vc:)
|
|
10
|
+
@session_id = session_id
|
|
11
|
+
@vc = vc
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.from_h(payload)
|
|
15
|
+
return payload if payload.is_a?(self)
|
|
16
|
+
return new(session_id: nil, vc: nil) unless payload.is_a?(Hash)
|
|
17
|
+
|
|
18
|
+
new(
|
|
19
|
+
session_id: fetch(payload, :sessionID),
|
|
20
|
+
vc: fetch(payload, :vc)
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.fetch(payload, key)
|
|
25
|
+
payload[key] || payload[key.to_s]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|