simplicity_client 0.2.0 → 0.2.1
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 +4 -4
- data/lib/fido2_client.rb +91 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3283578b786c0a520eeaee6b46290f1459e9075925f8fdf172534ac533270e33
|
4
|
+
data.tar.gz: cee5f3f5ac077c4cea0bc246d7f1a4b2ea03a23db7ad21795485730685ba3959
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ddd8ee90c05b00ba4ed232846f5c2e546e087c93c7e2c3a58965c74480640fe04823bea6d94c9b3b0f58e64f41cd738b8b157420b71d5864846cb74e0600185
|
7
|
+
data.tar.gz: fbf5d020b63bae7fd804154b34513d95e9991afa39023877905072e69df66879b4afd3e7028251204e314720c4ca472860dc9e81d86d4a90e521202791dd7bbe
|
data/lib/fido2_client.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require "json"
|
2
|
+
require "digest"
|
3
|
+
require "openssl"
|
4
|
+
require "base64"
|
5
|
+
|
6
|
+
module Fido2Client
|
7
|
+
Passkey = Data.define(:credentialId, :keyAlgorithm, :keyCurve, :keyValue, :userHandle)
|
8
|
+
Assertion = Data.define(:authenticator_data, :client_data_json, :credential_id, :user_handle, :signature)
|
9
|
+
|
10
|
+
class Client
|
11
|
+
def initialize
|
12
|
+
@origin = "https://app.simplicity.kiwi"
|
13
|
+
@rp_id = "simplicity.kiwi"
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_assertion(passkey, challenge)
|
17
|
+
collected_client_data = {
|
18
|
+
type: "webauthn.get",
|
19
|
+
challenge: challenge,
|
20
|
+
origin: @origin,
|
21
|
+
crossOrigin: false,
|
22
|
+
}
|
23
|
+
client_data_json = JSON.dump(collected_client_data)
|
24
|
+
client_data_hash = Digest::SHA256.digest(client_data_json)
|
25
|
+
|
26
|
+
# Assertion
|
27
|
+
auth_data = generate_auth_data
|
28
|
+
private_key = parse_private_key(passkey.keyAlgorithm, passkey.keyCurve, passkey.keyValue)
|
29
|
+
signature = generate_signature(auth_data, client_data_hash, private_key)
|
30
|
+
|
31
|
+
Assertion.new(
|
32
|
+
authenticator_data: Base64.urlsafe_encode64(auth_data.pack("c*"), padding: false),
|
33
|
+
client_data_json: Base64.urlsafe_encode64(client_data_json, padding: false),
|
34
|
+
credential_id: Base64.urlsafe_encode64(guid_to_raw_format(passkey.credentialId), padding: false),
|
35
|
+
user_handle: passkey.userHandle,
|
36
|
+
signature: Base64.urlsafe_encode64(signature, padding: false),
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def box(tag, lines)
|
43
|
+
lines.unshift "-----BEGIN #{tag}-----"
|
44
|
+
lines.push "-----END #{tag}-----"
|
45
|
+
lines.join("\n")
|
46
|
+
end
|
47
|
+
|
48
|
+
def der_to_pem(tag, der)
|
49
|
+
box tag, Base64.strict_encode64(der).scan(/.{1,64}/)
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_private_key(key_algorithm, key_curve, key_value)
|
53
|
+
raise "Unsupported key algorithm: #{key_algorithm}" unless key_algorithm == "ECDSA"
|
54
|
+
raise "Unsupported key curve: #{key_curve}" unless key_curve == "P-256"
|
55
|
+
|
56
|
+
# Decode the Base64 key value
|
57
|
+
key_value_bin = Base64.urlsafe_decode64(key_value)
|
58
|
+
|
59
|
+
pem = der_to_pem("PRIVATE KEY", key_value_bin)
|
60
|
+
OpenSSL::PKey::EC.new(pem)
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_signature(auth_data, client_data_hash, private_key)
|
64
|
+
sig_base = [*auth_data, *client_data_hash.bytes]
|
65
|
+
digest = OpenSSL::Digest.new("SHA256")
|
66
|
+
private_key.sign(digest, sig_base.pack("c*"))
|
67
|
+
end
|
68
|
+
|
69
|
+
def generate_auth_data
|
70
|
+
auth_data = []
|
71
|
+
rp_id_hash = Digest::SHA256.digest(@rp_id)
|
72
|
+
auth_data += rp_id_hash.bytes
|
73
|
+
|
74
|
+
# Flags asserted: Backup eligibility, Backup State, User Verification, User Presence
|
75
|
+
flags = 0x1D
|
76
|
+
auth_data.push(flags)
|
77
|
+
|
78
|
+
# Counter
|
79
|
+
auth_data += [0, 0, 0, 0]
|
80
|
+
|
81
|
+
auth_data
|
82
|
+
end
|
83
|
+
|
84
|
+
def guid_to_raw_format(guid)
|
85
|
+
raise TypeError, "GUID parameter is invalid" unless guid.match?(/\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\z/)
|
86
|
+
|
87
|
+
# Remove the hyphens and pack the string into raw binary
|
88
|
+
[guid.delete("-")].pack("H*")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simplicity_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- George Dewar
|
@@ -101,6 +101,7 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
+
- lib/fido2_client.rb
|
104
105
|
- lib/simplicity_client.rb
|
105
106
|
homepage: https://github.com/GeorgeDewar/simplicity_client
|
106
107
|
licenses:
|