triauth 0.0.1 → 1.0.0.beta.4
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/CHANGELOG.md +56 -0
- data/LICENSE +202 -28
- data/README.md +2036 -7
- data/lib/triauth/api/attestation.rb +419 -0
- data/lib/triauth/api/authentication.rb +651 -0
- data/lib/triauth/api/identity.rb +245 -0
- data/lib/triauth/api/signing.rb +740 -0
- data/lib/triauth/api/validation.rb +162 -0
- data/lib/triauth/api.rb +33 -0
- data/lib/triauth/authentication_endpoint.rb +196 -0
- data/lib/triauth/challenge.rb +115 -0
- data/lib/triauth/challenge_response_flow.rb +344 -0
- data/lib/triauth/config.rb +93 -0
- data/lib/triauth/error.rb +95 -0
- data/lib/triauth/helpers.rb +1222 -0
- data/lib/triauth/identity.rb +301 -0
- data/lib/triauth/identity_domain.rb +212 -0
- data/lib/triauth/identity_includes.rb +249 -0
- data/lib/triauth/identity_keys.rb +309 -0
- data/lib/triauth/logger.rb +156 -0
- data/lib/triauth/multi_signature.rb +177 -0
- data/lib/triauth/protocol.rb +74 -0
- data/lib/triauth/request_cipher.rb +121 -0
- data/lib/triauth/resolvers/base.rb +147 -0
- data/lib/triauth/resolvers/caching_resolver.rb +202 -0
- data/lib/triauth/resolvers/cloudflare.rb +40 -0
- data/lib/triauth/resolvers/dns_json.rb +391 -0
- data/lib/triauth/resolvers/dns_sb.rb +40 -0
- data/lib/triauth/resolvers/google.rb +39 -0
- data/lib/triauth/resolvers/multi_resolver.rb +202 -0
- data/lib/triauth/resolvers/system_dns.rb +259 -0
- data/lib/triauth/resolvers.rb +33 -0
- data/lib/triauth/response.rb +142 -0
- data/lib/triauth/response_cipher.rb +341 -0
- data/lib/triauth/signature.rb +405 -0
- data/lib/triauth/validator.rb +549 -0
- data/lib/triauth/verifiers/base.rb +147 -0
- data/lib/triauth/verifiers/ecdsa.rb +87 -0
- data/lib/triauth/verifiers/ed25519.rb +89 -0
- data/lib/triauth/verifiers/registry.rb +52 -0
- data/lib/triauth/verifiers/web_authn.rb +253 -0
- data/lib/triauth/verifiers.rb +37 -0
- data/lib/triauth/version.rb +23 -0
- data/lib/triauth.rb +104 -7
- metadata +55 -13
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2026 The Triauth Authors (https://www.triauth.org/)
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
#
|
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
require 'openssl'
|
|
21
|
+
|
|
22
|
+
module Triauth
|
|
23
|
+
module Verifiers
|
|
24
|
+
# The base abstract class from which all Verifiers inherit common functions.
|
|
25
|
+
class Base
|
|
26
|
+
# Constructs a verifier for a public key as it is published in an identity record.
|
|
27
|
+
#
|
|
28
|
+
# This is the abstract contract that concrete verifiers (e.g., Ecdsa, Ed25519, WebAuthn) implement.
|
|
29
|
+
#
|
|
30
|
+
# @param raw_public_key [String] The publishable key material, as read from the identity record.
|
|
31
|
+
# @param options [Hash] Key options as read from the DNS record that carried the key (e.g., `use`, `uv`). Defaults to `{}`.
|
|
32
|
+
# @param context [Hash] Per-verification context threaded through from `Triauth::IdentityKeys#verify`
|
|
33
|
+
# (e.g., `idx`, `mode`, `webAuthnOrigin`). Defaults to `{}`.
|
|
34
|
+
# @param config [Hash] Optional overrides for the global Triauth.config settings. Defaults to `{}`.
|
|
35
|
+
#
|
|
36
|
+
# @return [Base, nil] A verifier instance, or nil when the key
|
|
37
|
+
# material cannot be imported. A key whose verifier cannot be constructed is skipped, never trusted.
|
|
38
|
+
def self.from_publishable_key(raw_public_key, options = {}, context = {}, config = {}) # rubocop:disable Lint/UnusedMethodArgument -- the abstract contract keeps its parameters
|
|
39
|
+
raise 'Triauth.Verifiers.Base.fromPublishableKey is abstract and must be implemented by a subclass'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Verifies that a given signature matches the verifier's public key and the given message.
|
|
43
|
+
#
|
|
44
|
+
# This is the abstract contract that concrete verifiers (e.g., Ecdsa, Ed25519, WebAuthn) implement.
|
|
45
|
+
#
|
|
46
|
+
# @param message [String] The message to verify against.
|
|
47
|
+
# @param signature_base64url [String] Base64Url encoded signature.
|
|
48
|
+
# @param signed_data [Hash] Signature envelope's signedMetadata.
|
|
49
|
+
# @param unsigned_data [Hash] Signature envelope's unsignedMetadata.
|
|
50
|
+
#
|
|
51
|
+
# @return [Boolean] true or false basing on the verification result.
|
|
52
|
+
def verify(message, signature_base64url, signed_data, unsigned_data) # rubocop:disable Lint/UnusedMethodArgument -- the abstract contract keeps its parameters
|
|
53
|
+
raise 'Triauth.Verifiers.Base#verify is abstract and must be implemented by a subclass'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Crypto encodings with no triauth-js counterpart (the profile's idiom table): byte conversions only, all math is OpenSSL's.
|
|
57
|
+
|
|
58
|
+
# The `SubjectPublicKeyInfo` prefix of an Ed25519 public key (`SEQUENCE { SEQUENCE { OID 1.3.101.112 }, BIT STRING }` ahead
|
|
59
|
+
# of the 32 raw key bytes): `OpenSSL::PKey` reads a raw key through this wrapper only.
|
|
60
|
+
ED25519_SPKI_PREFIX = ['302a300506032b6570032100'].pack('H*').freeze
|
|
61
|
+
private_constant :ED25519_SPKI_PREFIX
|
|
62
|
+
|
|
63
|
+
# The port of `crypto.subtle.importKey('raw', raw_bytes, algorithm, true, ['verify'])`: reads a raw public key through
|
|
64
|
+
# its SPKI DER wrapper, so that OpenSSL validates the key material (a P-256 point that is not on the curve is rejected).
|
|
65
|
+
#
|
|
66
|
+
# @param raw_bytes [String] The raw public key (a binary String): the 65-byte uncompressed point `04 || X || Y` for
|
|
67
|
+
# ECDSA P-256 (the verifiers decode a compressed point to this form first, with
|
|
68
|
+
# `Helpers.decode_p256_public_key`; the import itself still refuses every other form before
|
|
69
|
+
# OpenSSL sees it), the 32 raw bytes for Ed25519.
|
|
70
|
+
# @param algorithm [Hash] The WebCrypto import algorithm identifier, kept as data:
|
|
71
|
+
# `{name: 'ECDSA', namedCurve: 'P-256', hash: 'SHA-256'}` or `{name: 'Ed25519'}`.
|
|
72
|
+
#
|
|
73
|
+
# @return [OpenSSL::PKey::PKey] the public key (an `OpenSSL::PKey::EC` for ECDSA).
|
|
74
|
+
#
|
|
75
|
+
# @raise [ArgumentError] when the bytes do not have the length (and prefix) of the algorithm's raw form, or the
|
|
76
|
+
# algorithm identifier is not one of the two above.
|
|
77
|
+
# @raise [OpenSSL::PKey::PKeyError] when OpenSSL rejects the key material (e.g., a point that is not on the curve).
|
|
78
|
+
def self.import_key(raw_bytes, algorithm)
|
|
79
|
+
case algorithm[:name]
|
|
80
|
+
when 'ECDSA'
|
|
81
|
+
raise ArgumentError, "Unsupported ECDSA curve: #{algorithm[:namedCurve]}" unless algorithm[:namedCurve] == 'P-256'
|
|
82
|
+
raise ArgumentError, 'ECDSA raw public key must be the 65-byte uncompressed point' unless raw_bytes.bytesize == 65 && raw_bytes.getbyte(0) == 0x04
|
|
83
|
+
|
|
84
|
+
# SPKI: SEQUENCE { SEQUENCE { id-ecPublicKey, prime256v1 }, BIT STRING point }
|
|
85
|
+
algorithm_identifier = OpenSSL::ASN1::Sequence([OpenSSL::ASN1::ObjectId('id-ecPublicKey'), OpenSSL::ASN1::ObjectId('prime256v1')])
|
|
86
|
+
der = OpenSSL::ASN1::Sequence([algorithm_identifier, OpenSSL::ASN1::BitString(raw_bytes)]).to_der
|
|
87
|
+
return OpenSSL::PKey::EC.new(der)
|
|
88
|
+
when 'Ed25519'
|
|
89
|
+
raise ArgumentError, 'Ed25519 raw public key must be 32 bytes' unless raw_bytes.bytesize == 32
|
|
90
|
+
|
|
91
|
+
return OpenSSL::PKey.read(ED25519_SPKI_PREFIX + raw_bytes)
|
|
92
|
+
else
|
|
93
|
+
raise ArgumentError, "Unsupported algorithm: #{algorithm[:name]}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The port of `crypto.subtle.verify(algorithm, public_key, signature_bytes, data_bytes)`: verifies the raw signature of
|
|
98
|
+
# the wire (`r || s` for ECDSA, the 64 raw bytes for Ed25519) over the data bytes. The length is checked before any
|
|
99
|
+
# conversion, so a DER-encoded ECDSA signature is refused exactly as WebCrypto refuses it (no widened accept-set).
|
|
100
|
+
#
|
|
101
|
+
# @param algorithm [Hash] The WebCrypto verify algorithm identifier, kept as data:
|
|
102
|
+
# `{name: 'ECDSA', hash: 'SHA-256'}` or `{name: 'Ed25519'}`.
|
|
103
|
+
# @param public_key [OpenSSL::PKey::PKey] The public key, as returned by {Triauth::Verifiers::Base.import_key}.
|
|
104
|
+
# @param signature_bytes [String] The raw signature (a binary String): exactly 64 bytes for both algorithms.
|
|
105
|
+
# @param data_bytes [String] The signed data (a binary String).
|
|
106
|
+
#
|
|
107
|
+
# @return [Boolean] true when the signature verifies; false for a signature of another length, a signature that does
|
|
108
|
+
# not verify, or an operation OpenSSL refuses (`OpenSSL::PKey::PKeyError`).
|
|
109
|
+
#
|
|
110
|
+
# @raise [ArgumentError] when the algorithm identifier is not one of the two above.
|
|
111
|
+
def self.verify_signature(algorithm, public_key, signature_bytes, data_bytes)
|
|
112
|
+
case algorithm[:name]
|
|
113
|
+
when 'ECDSA'
|
|
114
|
+
raise ArgumentError, "Unsupported ECDSA hash: #{algorithm[:hash]}" unless algorithm[:hash] == 'SHA-256'
|
|
115
|
+
return false unless signature_bytes.bytesize == 64
|
|
116
|
+
|
|
117
|
+
return public_key.verify('SHA256', raw_to_der(signature_bytes), data_bytes)
|
|
118
|
+
when 'Ed25519'
|
|
119
|
+
return false unless signature_bytes.bytesize == 64
|
|
120
|
+
|
|
121
|
+
return public_key.verify(nil, signature_bytes, data_bytes)
|
|
122
|
+
else
|
|
123
|
+
raise ArgumentError, "Unsupported algorithm: #{algorithm[:name]}"
|
|
124
|
+
end
|
|
125
|
+
rescue OpenSSL::PKey::PKeyError
|
|
126
|
+
return false
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Converts a raw ECDSA signature `r || s` (two 32-byte big-endian unsigned integers) into the DER
|
|
130
|
+
# `SEQUENCE { INTEGER r, INTEGER s }` that OpenSSL verifies. `OpenSSL::ASN1::Integer` writes the minimal positive
|
|
131
|
+
# encodings (a leading zero byte ahead of a high bit, leading zero bytes dropped).
|
|
132
|
+
#
|
|
133
|
+
# @param signature_bytes [String] The 64-byte raw signature (a binary String).
|
|
134
|
+
#
|
|
135
|
+
# @return [String] the DER-encoded signature (a binary String).
|
|
136
|
+
#
|
|
137
|
+
# @raise [ArgumentError] when the signature is not 64 bytes.
|
|
138
|
+
def self.raw_to_der(signature_bytes)
|
|
139
|
+
raise ArgumentError, 'ECDSA raw signature must be 64 bytes' unless signature_bytes.bytesize == 64
|
|
140
|
+
|
|
141
|
+
r = OpenSSL::BN.new(signature_bytes.byteslice(0, 32), 2)
|
|
142
|
+
s = OpenSSL::BN.new(signature_bytes.byteslice(32, 32), 2)
|
|
143
|
+
return OpenSSL::ASN1::Sequence([OpenSSL::ASN1::Integer(r), OpenSSL::ASN1::Integer(s)]).to_der
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2026 The Triauth Authors (https://www.triauth.org/)
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
#
|
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
require_relative 'base'
|
|
21
|
+
require_relative 'registry'
|
|
22
|
+
require_relative '../helpers'
|
|
23
|
+
|
|
24
|
+
module Triauth
|
|
25
|
+
module Verifiers
|
|
26
|
+
# The verifier for ECDSA signatures (P-256 curve with SHA-256).
|
|
27
|
+
# This is the default verifier in triauth.
|
|
28
|
+
class Ecdsa < Base
|
|
29
|
+
# @return [OpenSSL::PKey::EC] The public key the signatures are verified against.
|
|
30
|
+
attr_reader :public_key
|
|
31
|
+
# @return [Hash{Symbol => Object}] The effective settings: the global `Triauth.config` with the constructor's overrides merged over it.
|
|
32
|
+
attr_reader :config
|
|
33
|
+
|
|
34
|
+
# Constructs a new instance of ECDSA verifier for the given public crypto key and options.
|
|
35
|
+
#
|
|
36
|
+
# @param public_key [OpenSSL::PKey::EC] A public key in form of an `OpenSSL::PKey` object.
|
|
37
|
+
# @param options [Hash] Key options as read from the DNS record that contained the public_key. Defaults to `{}`.
|
|
38
|
+
# @param context [Hash] Per-verification context. Unused by this verifier. Defaults to `{}`.
|
|
39
|
+
# @param config [Hash] Optional overrides for the global Triauth.config settings. Defaults to `{}`.
|
|
40
|
+
def initialize(public_key, options = {}, context = {}, config = {}) # rubocop:disable Lint/UnusedMethodArgument -- `options` and `context` are the verifier constructor contract, unused here as in triauth-js
|
|
41
|
+
super()
|
|
42
|
+
|
|
43
|
+
@public_key = public_key
|
|
44
|
+
@config = Helpers.merge_config(config)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Builds an instance of the Verifier from the raw key information read from the DNS.
|
|
48
|
+
#
|
|
49
|
+
# @param raw_public_key [String] A key as published in the DNS record (base64url of the 65-byte uncompressed or the 33-byte compressed P-256 point).
|
|
50
|
+
# @param options [Hash] Key options as read from the DNS record that contained the raw_public_key. Defaults to `{}`.
|
|
51
|
+
# @param context [Hash] Per-verification context. Unused by this verifier. Defaults to `{}`.
|
|
52
|
+
# @param config [Hash] Optional overrides for the global Triauth.config settings. Defaults to `{}`.
|
|
53
|
+
#
|
|
54
|
+
# @return [Ecdsa, nil] The instance of this verifier, or nil if key is invalid or other problem occurred.
|
|
55
|
+
def self.from_publishable_key(raw_public_key, options = {}, context = {}, config = {})
|
|
56
|
+
config = Helpers.merge_config(config)
|
|
57
|
+
|
|
58
|
+
begin
|
|
59
|
+
public_key = Base.import_key(Helpers.decode_p256_public_key(Helpers.base64url_to_uint8(raw_public_key)), { name: 'ECDSA', namedCurve: 'P-256', hash: 'SHA-256' })
|
|
60
|
+
return new(public_key, options, context, config)
|
|
61
|
+
rescue StandardError => err
|
|
62
|
+
Helpers.log(config, :debug, err)
|
|
63
|
+
return nil
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Verifies that a given signature matches the verifier's public key and the given message.
|
|
68
|
+
#
|
|
69
|
+
# @param message [String] The message to verify against.
|
|
70
|
+
# @param signature_base64url [String] Base64Url encoded signature.
|
|
71
|
+
# @param signed_data [Hash] Signature envelope's signedMetadata. Unused by this verifier.
|
|
72
|
+
# @param unsigned_data [Hash] Signature envelope's unsignedMetadata. Unused by this verifier.
|
|
73
|
+
#
|
|
74
|
+
# @return [Boolean] true or false basing on the verification result.
|
|
75
|
+
def verify(message, signature_base64url, signed_data, unsigned_data) # rubocop:disable Lint/UnusedMethodArgument -- `signed_data` and `unsigned_data` are the verifier contract, unused here as in triauth-js
|
|
76
|
+
data = Helpers.string_to_utf8_bytes(message)
|
|
77
|
+
signature = Helpers.base64url_to_uint8(signature_base64url)
|
|
78
|
+
return Base.verify_signature({ name: 'ECDSA', hash: 'SHA-256' }, @public_key, signature, data)
|
|
79
|
+
rescue StandardError => err
|
|
80
|
+
Helpers.log(@config, :debug, err)
|
|
81
|
+
return false
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
Triauth::Verifiers.register('es256', Ecdsa)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2026 The Triauth Authors (https://www.triauth.org/)
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
#
|
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
require_relative 'base'
|
|
21
|
+
require_relative 'registry'
|
|
22
|
+
require_relative '../helpers'
|
|
23
|
+
|
|
24
|
+
module Triauth
|
|
25
|
+
module Verifiers
|
|
26
|
+
# A verifier for Ed25519 (EdDSA over Curve25519) signatures.
|
|
27
|
+
#
|
|
28
|
+
# Support for Ed25519 keys in triauth is considered experimental: it needs the openssl gem 3.x over
|
|
29
|
+
# OpenSSL 1.1.1 or later (the Ruby 3.2 default gem over a current OpenSSL library has it).
|
|
30
|
+
class Ed25519 < Base
|
|
31
|
+
# @return [OpenSSL::PKey::PKey] The public key the signatures are verified against.
|
|
32
|
+
attr_reader :public_key
|
|
33
|
+
# @return [Hash{Symbol => Object}] The effective settings: the global `Triauth.config` with the constructor's overrides merged over it.
|
|
34
|
+
attr_reader :config
|
|
35
|
+
|
|
36
|
+
# Constructs a new instance of Ed25519 verifier for the given public crypto key and options.
|
|
37
|
+
#
|
|
38
|
+
# @param public_key [OpenSSL::PKey::PKey] A public key in form of an `OpenSSL::PKey` object.
|
|
39
|
+
# @param options [Hash] Key options as read from the DNS record that contained the public_key. Defaults to `{}`.
|
|
40
|
+
# @param context [Hash] Per-verification context. Unused by this verifier. Defaults to `{}`.
|
|
41
|
+
# @param config [Hash] Optional overrides for the global Triauth.config settings. Defaults to `{}`.
|
|
42
|
+
def initialize(public_key, options = {}, context = {}, config = {}) # rubocop:disable Lint/UnusedMethodArgument -- `options` and `context` are the verifier constructor contract, unused here as in triauth-js
|
|
43
|
+
super()
|
|
44
|
+
|
|
45
|
+
@public_key = public_key
|
|
46
|
+
@config = Helpers.merge_config(config)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Builds an instance of the Verifier from the raw key information read from the DNS.
|
|
50
|
+
#
|
|
51
|
+
# @param raw_public_key [String] A key as published in the DNS record (base64url of the 32-byte raw public key).
|
|
52
|
+
# @param options [Hash] Key options as read from the DNS record that contained the raw_public_key. Defaults to `{}`.
|
|
53
|
+
# @param context [Hash] Per-verification context. Unused by this verifier. Defaults to `{}`.
|
|
54
|
+
# @param config [Hash] Optional overrides for the global Triauth.config settings. Defaults to `{}`.
|
|
55
|
+
#
|
|
56
|
+
# @return [Ed25519, nil] The instance of this verifier, or nil if key is invalid or other problem occurred.
|
|
57
|
+
def self.from_publishable_key(raw_public_key, options = {}, context = {}, config = {})
|
|
58
|
+
config = Helpers.merge_config(config)
|
|
59
|
+
|
|
60
|
+
begin
|
|
61
|
+
public_key = Base.import_key(Helpers.base64url_to_uint8(raw_public_key), { name: 'Ed25519' })
|
|
62
|
+
return new(public_key, options, context, config)
|
|
63
|
+
rescue StandardError => err
|
|
64
|
+
Helpers.log(config, :debug, err)
|
|
65
|
+
return nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Verifies that a given signature matches the verifier's public key and the given message.
|
|
70
|
+
#
|
|
71
|
+
# @param message [String] The message to verify against.
|
|
72
|
+
# @param signature_base64url [String] Base64Url encoded signature.
|
|
73
|
+
# @param signed_data [Hash] Signature envelope's signedMetadata. Unused by this verifier.
|
|
74
|
+
# @param unsigned_data [Hash] Signature envelope's unsignedMetadata. Unused by this verifier.
|
|
75
|
+
#
|
|
76
|
+
# @return [Boolean] true or false basing on the verification result.
|
|
77
|
+
def verify(message, signature_base64url, signed_data, unsigned_data) # rubocop:disable Lint/UnusedMethodArgument -- `signed_data` and `unsigned_data` are the verifier contract, unused here as in triauth-js
|
|
78
|
+
data = Helpers.string_to_utf8_bytes(message)
|
|
79
|
+
signature = Helpers.base64url_to_uint8(signature_base64url)
|
|
80
|
+
return Base.verify_signature({ name: 'Ed25519' }, @public_key, signature, data)
|
|
81
|
+
rescue StandardError => err
|
|
82
|
+
Helpers.log(@config, :debug, err)
|
|
83
|
+
return false
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
Triauth::Verifiers.register('ed25519', Ed25519)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2026 The Triauth Authors (https://www.triauth.org/)
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
#
|
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
require_relative '../helpers'
|
|
21
|
+
|
|
22
|
+
module Triauth
|
|
23
|
+
# Holds information about registered digital signature verifiers.
|
|
24
|
+
# Each public key stored in the identity records in the DNS may be associated with a type of verifier that is to be used with it (e.g., `type=es256`).
|
|
25
|
+
module Verifiers
|
|
26
|
+
# the registry keys are wire values: the type String as published in the key record (`'es256'`), never a Symbol - like the LRU cache keys
|
|
27
|
+
@verifiers = {}
|
|
28
|
+
|
|
29
|
+
# Registers a verifier class that should be used for keys of the given type.
|
|
30
|
+
#
|
|
31
|
+
# @param type [String] the type of key, e.g., 'es256'
|
|
32
|
+
# @param verifier_class [Class] a class descending from `Triauth::Verifiers::Base` that should handle the keys of given type
|
|
33
|
+
def self.register(type, verifier_class)
|
|
34
|
+
@verifiers[type] = verifier_class
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Returns a verifier registered for the given key type.
|
|
38
|
+
#
|
|
39
|
+
# @param type [String] the type of the key for which verifier is being looked for
|
|
40
|
+
# @return [Class, nil] A class that descends from `Triauth::Verifiers::Base` (with a `from_publishable_key` factory) that is responsible for handling given keys, or nil if not found.
|
|
41
|
+
def self.get(type)
|
|
42
|
+
return @verifiers[type]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns a list of registered verifier's types
|
|
46
|
+
#
|
|
47
|
+
# @return [Array<String>]
|
|
48
|
+
def self.list
|
|
49
|
+
return Helpers.ordered_entries(@verifiers).map(&:first) # `Object.keys`: an integer-like type lists first, in numeric order
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2026 The Triauth Authors (https://www.triauth.org/)
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
#
|
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
require_relative 'base'
|
|
21
|
+
require_relative 'registry'
|
|
22
|
+
require_relative '../helpers'
|
|
23
|
+
|
|
24
|
+
module Triauth
|
|
25
|
+
module Verifiers
|
|
26
|
+
# The base abstract verifier for WebAuthn signatures.
|
|
27
|
+
#
|
|
28
|
+
# Performs the WebAuthn-level assertion validation.
|
|
29
|
+
# Subclasses must define concrete import_algorithm, verify_algorithm, and register themselves (e.g., as `webauthn-es256`).
|
|
30
|
+
class WebAuthn < Base
|
|
31
|
+
# The Web Crypto parameters used to import the publishable key and to verify
|
|
32
|
+
# assertion signatures. Concrete subclasses MUST override these to bind a
|
|
33
|
+
# credential algorithm; left nil, the verifier fails closed.
|
|
34
|
+
|
|
35
|
+
# @return [Hash, nil] The import algorithm identifier a concrete subclass binds (`{name: 'ECDSA', namedCurve: 'P-256', hash: 'SHA-256'}` or `{name: 'Ed25519'}`); nil here.
|
|
36
|
+
def self.import_algorithm = nil
|
|
37
|
+
|
|
38
|
+
# @return [Hash, nil] The verify algorithm identifier a concrete subclass binds (`{name: 'ECDSA', hash: 'SHA-256'}` or `{name: 'Ed25519'}`); nil here.
|
|
39
|
+
def self.verify_algorithm = nil
|
|
40
|
+
|
|
41
|
+
# Returns the bytes of a publishable key in the form that the `raw` import of `import_algorithm` accepts.
|
|
42
|
+
# Subclasses whose credential algorithm has more than one key form override it.
|
|
43
|
+
#
|
|
44
|
+
# @param raw_public_key [String] A key as published in the DNS record.
|
|
45
|
+
# @return [String] the key bytes (a binary String).
|
|
46
|
+
#
|
|
47
|
+
# @raise [TypeError] when the key is not in a form that the credential algorithm accepts.
|
|
48
|
+
def self.public_key_bytes(raw_public_key)
|
|
49
|
+
return Helpers.base64url_to_uint8(raw_public_key)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [OpenSSL::PKey::PKey] The public key the assertion signatures are verified against.
|
|
53
|
+
attr_reader :public_key
|
|
54
|
+
# @return [Hash] Key options as read from the DNS record that contained the public key (see {#initialize}).
|
|
55
|
+
attr_reader :options
|
|
56
|
+
# @return [Hash] Per-verification context (see {#initialize}).
|
|
57
|
+
attr_reader :context
|
|
58
|
+
# @return [Hash{Symbol => Object}] The effective settings: the global `Triauth.config` with the constructor's overrides merged over it.
|
|
59
|
+
attr_reader :config
|
|
60
|
+
|
|
61
|
+
# Constructs a new instance of verifier for the given public crypto key and options.
|
|
62
|
+
#
|
|
63
|
+
# @param public_key [OpenSSL::PKey::PKey] A public key in form of an `OpenSSL::PKey` object.
|
|
64
|
+
# @param options [Hash] Key options as read from the DNS record that contained the public_key. Defaults to `{}`.
|
|
65
|
+
# Supports `uv: 'required'` - assertions are then accepted only with the User Verified (UV) flag set
|
|
66
|
+
# (i.e., the authenticator checked PIN/biometrics).
|
|
67
|
+
# @param context [Hash] Per-verification context (`idx`, `mode`, `webAuthnOrigin`). `idx` (key's index in its keyGroup) selects
|
|
68
|
+
# which `unsignedData.sig[idx]` entry to verify against; `webAuthnOrigin`, if set, is enforced against `clientData.origin`.
|
|
69
|
+
# Stored on `context`. Defaults to `{}`.
|
|
70
|
+
# @param config [Hash] Optional overrides for the global Triauth.config settings. Defaults to `{}`.
|
|
71
|
+
def initialize(public_key, options = {}, context = {}, config = {})
|
|
72
|
+
super()
|
|
73
|
+
|
|
74
|
+
@public_key = public_key
|
|
75
|
+
@options = options
|
|
76
|
+
@context = context
|
|
77
|
+
@config = Helpers.merge_config(config)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Builds an instance of the Verifier from the raw key information read from the DNS.
|
|
81
|
+
#
|
|
82
|
+
# @param raw_public_key [String] A key as published in the DNS record.
|
|
83
|
+
# @param options [Hash] Key options as read from the DNS record that contained the raw_public_key. Defaults to `{}`.
|
|
84
|
+
# @param context [Hash] Per-verification context (`idx`, `mode`, `webAuthnOrigin`). `idx` (key's index in its keyGroup) selects
|
|
85
|
+
# which `unsignedData.sig[idx]` entry to verify against; `webAuthnOrigin`, if set, is enforced against `clientData.origin`.
|
|
86
|
+
# Stored on `context`. Defaults to `{}`.
|
|
87
|
+
# @param config [Hash] Optional overrides for the global Triauth.config settings. Defaults to `{}`.
|
|
88
|
+
#
|
|
89
|
+
# @return [WebAuthn, nil] The instance of this verifier, or nil if key is invalid or other problem occurred.
|
|
90
|
+
def self.from_publishable_key(raw_public_key, options = {}, context = {}, config = {})
|
|
91
|
+
config = Helpers.merge_config(config)
|
|
92
|
+
|
|
93
|
+
import_algorithm = self.import_algorithm
|
|
94
|
+
if import_algorithm.nil?
|
|
95
|
+
Helpers.log(config, :debug, 'WebAuthn is an algorithm-agnostic base verifier - use a concrete subclass (e.g., WebAuthnEs256)')
|
|
96
|
+
return nil
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
begin
|
|
100
|
+
public_key = Base.import_key(public_key_bytes(raw_public_key), import_algorithm)
|
|
101
|
+
|
|
102
|
+
return new(public_key, options, context, config)
|
|
103
|
+
rescue StandardError => err
|
|
104
|
+
Helpers.log(config, :debug, err)
|
|
105
|
+
return nil
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Verifies that a given signature matches the verifier's public key and the given message.
|
|
110
|
+
#
|
|
111
|
+
# @param message [String] The message that was signed; its SHA-256 digest must match `clientData.challenge`.
|
|
112
|
+
# @param signature_base64url [String] Base64Url encoded signature over `(authenticatorData || sha256(clientDataJSON))`.
|
|
113
|
+
# @param signed_data [Hash] Signature envelope's signedMetadata. Unused by this verifier.
|
|
114
|
+
# @param unsigned_data [Hash{Symbol => Object}] Signature envelope's unsignedMetadata. Must contain `sig[idx]` for this key's
|
|
115
|
+
# index (set on `context[:idx]`; `sig` is an object keyed by the index string, or an array read by position),
|
|
116
|
+
# carrying the original `clientDataJSON` (string) and `authenticatorData` (base64url, minimum 37 bytes, with the
|
|
117
|
+
# User Present flag set - silent CTAP-level assertions are rejected).
|
|
118
|
+
#
|
|
119
|
+
# @return [Boolean] true or false basing on the verification result.
|
|
120
|
+
def verify(message, signature_base64url, signed_data, unsigned_data) # rubocop:disable Lint/UnusedMethodArgument -- `signed_data` is the verifier contract, unused here as in triauth-js
|
|
121
|
+
verify_algorithm = self.class.verify_algorithm
|
|
122
|
+
if verify_algorithm.nil?
|
|
123
|
+
Helpers.log(@config, :debug, 'WebAuthn is an algorithm-agnostic base verifier - use a concrete subclass (e.g., WebAuthnEs256)')
|
|
124
|
+
return false
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
idx = @context[:idx]
|
|
128
|
+
idx_key = idx.to_s.to_sym # the JS `sig[idx]` looks up the object key `"0"` for the index 0; the JSON key is the Symbol `:"0"` here
|
|
129
|
+
expected_challenge = Helpers.sha256(message)
|
|
130
|
+
expected_origin = @context[:webAuthnOrigin]
|
|
131
|
+
|
|
132
|
+
# SECURITY NOTE: `unsigned_data` is NOT covered by the triauth envelope's signature, but:
|
|
133
|
+
# - clientData.challenge is checked to be the expected_challenge
|
|
134
|
+
# - (authenticatorData + sha256(clientDataJSON)) is covered by the WebAuthn-signed payload
|
|
135
|
+
if !unsigned_data.is_a?(Hash)
|
|
136
|
+
return false
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
sig = unsigned_data[:sig]
|
|
140
|
+
# the JS `sig[idx]` reads an Array by position and an object by its "0" key; both shapes are accepted, as in triauth-js
|
|
141
|
+
entry = nil
|
|
142
|
+
if sig.is_a?(Array)
|
|
143
|
+
entry = sig[idx] if idx.is_a?(Integer) && idx >= 0 # a negative or non-integer index never reads an Array from its end
|
|
144
|
+
elsif sig.is_a?(Hash)
|
|
145
|
+
entry = sig[idx_key]
|
|
146
|
+
end
|
|
147
|
+
if !entry.is_a?(Hash) ||
|
|
148
|
+
!Helpers.is_string(entry[:clientDataJSON]) ||
|
|
149
|
+
!Helpers.is_base64url_string(entry[:authenticatorData])
|
|
150
|
+
return false
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
authenticator_data, client_data_json = entry.values_at(:authenticatorData, :clientDataJSON)
|
|
154
|
+
|
|
155
|
+
# Verify clientData structure, including that the clientData.challenge is an SHA-256 digest of the message
|
|
156
|
+
client_data = Helpers.safe_parse_json(client_data_json)
|
|
157
|
+
if client_data[:challenge] != expected_challenge || # <--- challenge MUST MATCH --- !!!
|
|
158
|
+
client_data_json.scan('"challenge":').length != 1 || # Ensure that "challenge" key appears exactly once in the clientDataJSON, as an extra safety precaution
|
|
159
|
+
client_data[:type] != 'webauthn.get' || # type should always be 'webauthn.get'
|
|
160
|
+
(client_data.key?(:crossOrigin) && client_data[:crossOrigin] != false) || # if present, must be exactly false (a cross-origin <iframe> assertion carries true); the member is optional and absent means top-level same-origin (WebKit omits it) - `key?`, not `nil?`: an explicit null is present and rejected, as in triauth-js
|
|
161
|
+
(!expected_origin.nil? && !expected_origin.empty? && client_data[:origin] != expected_origin) # origin, if available from context, must match
|
|
162
|
+
return false
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Parse the authenticatorData: 32-byte rpIdHash, 1 flags byte, 4-byte signCount (37 bytes minimum)
|
|
166
|
+
authenticator_data_bytes = Helpers.base64url_to_uint8(authenticator_data)
|
|
167
|
+
if authenticator_data_bytes.bytesize < 37
|
|
168
|
+
Helpers.log(@config, :debug, 'WebAuthn assertion rejected - authenticatorData shorter than 37 bytes')
|
|
169
|
+
return false
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Require the User Present (UP) flag, as the W3C Web Authentication specification mandates for assertions.
|
|
173
|
+
# Browser-mediated assertions always carry UP=1 (the client performs a presence test before
|
|
174
|
+
# returning one); its absence indicates a CTAP-level "silent" assertion (options.up=false),
|
|
175
|
+
# e.g., minted by malware with direct access to a connected authenticator.
|
|
176
|
+
#
|
|
177
|
+
# The rpIdHash (bytes 0-31) is intentionally NOT checked: the public key is pinned in the
|
|
178
|
+
# DNS identity records and a WebAuthn credential only ever signs under its own rpId, so
|
|
179
|
+
# there is no cross-RP credential confusion to prevent; the phishing angle is covered by
|
|
180
|
+
# the clientData.origin check above. The signCount (bytes 33-36) is not checked either:
|
|
181
|
+
# clone detection requires server-side state that this stateless verifier does not have
|
|
182
|
+
# (and modern passkeys report a constant 0).
|
|
183
|
+
if (authenticator_data_bytes.getbyte(32) & 0x01) != 0x01
|
|
184
|
+
Helpers.log(@config, :debug, 'WebAuthn assertion rejected - User Present (UP) flag not set')
|
|
185
|
+
return false
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# When the key record demands user verification (`uv=required`), also require the
|
|
189
|
+
# User Verified (UV) flag - i.e., the authenticator must have checked PIN/biometrics.
|
|
190
|
+
# Key records carrying any other `uv` value never reach this point - they taint their
|
|
191
|
+
# whole keyGroup during Triauth::IdentityKeys#add (fail closed, never silently weaker).
|
|
192
|
+
if @options[:uv] == 'required' && (authenticator_data_bytes.getbyte(32) & 0x04) != 0x04
|
|
193
|
+
Helpers.log(@config, :debug, 'WebAuthn assertion rejected - User Verified (UV) flag required by key options but not set')
|
|
194
|
+
return false
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Reconstruct the payload that was signed by webauthn
|
|
198
|
+
client_data_hash = OpenSSL::Digest::SHA256.digest(Helpers.string_to_utf8_bytes(client_data_json))
|
|
199
|
+
|
|
200
|
+
web_authn_payload = authenticator_data_bytes + client_data_hash
|
|
201
|
+
|
|
202
|
+
# Convert the signature being verified back to a binary String for verify_signature
|
|
203
|
+
signature = Helpers.base64url_to_uint8(signature_base64url)
|
|
204
|
+
|
|
205
|
+
# Verify the digital signature (may raise an exception if arguments are invalid)
|
|
206
|
+
return Base.verify_signature(verify_algorithm, @public_key, signature, web_authn_payload)
|
|
207
|
+
rescue StandardError => err
|
|
208
|
+
Helpers.log(@config, :debug, err)
|
|
209
|
+
return false
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# A verifier for WebAuthn signatures backed by ECDSA P-256 (ES256) credentials.
|
|
214
|
+
#
|
|
215
|
+
# Shares all WebAuthn assertion validation with the WebAuthn base class; this subclass
|
|
216
|
+
# only binds the ES256 credential algorithm. The publishable key is the 65-byte uncompressed
|
|
217
|
+
# or the 33-byte compressed P-256 point (see `Triauth::Helpers.decode_p256_public_key`).
|
|
218
|
+
class WebAuthnEs256 < WebAuthn
|
|
219
|
+
# @return [Hash] `{name: 'ECDSA', namedCurve: 'P-256', hash: 'SHA-256'}`
|
|
220
|
+
def self.import_algorithm = { name: 'ECDSA', namedCurve: 'P-256', hash: 'SHA-256' }
|
|
221
|
+
|
|
222
|
+
# @return [Hash] `{name: 'ECDSA', hash: 'SHA-256'}`
|
|
223
|
+
def self.verify_algorithm = { name: 'ECDSA', hash: 'SHA-256' }
|
|
224
|
+
|
|
225
|
+
# Returns the publishable key as the 65-byte uncompressed P-256 point.
|
|
226
|
+
#
|
|
227
|
+
# @param raw_public_key [String] A key as published in the DNS record.
|
|
228
|
+
# @return [String] the 65-byte uncompressed point (a binary String).
|
|
229
|
+
#
|
|
230
|
+
# @raise [TypeError] when the key is not an uncompressed or a compressed P-256 point.
|
|
231
|
+
def self.public_key_bytes(raw_public_key)
|
|
232
|
+
return Helpers.decode_p256_public_key(Helpers.base64url_to_uint8(raw_public_key))
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# A verifier for WebAuthn signatures backed by Ed25519 (EdDSA over Curve25519) credentials.
|
|
237
|
+
#
|
|
238
|
+
# Shares all WebAuthn assertion validation with the WebAuthn base class; this subclass
|
|
239
|
+
# only binds the Ed25519 credential algorithm. Like the plain Ed25519 verifier, it requires
|
|
240
|
+
# Ed25519 support in the openssl gem (3.x over OpenSSL 1.1.1 or later) - on runtimes without it,
|
|
241
|
+
# `from_publishable_key` returns nil and the keyGroup fails verification (fail closed).
|
|
242
|
+
class WebAuthnEd25519 < WebAuthn
|
|
243
|
+
# @return [Hash] `{name: 'Ed25519'}`
|
|
244
|
+
def self.import_algorithm = { name: 'Ed25519' }
|
|
245
|
+
|
|
246
|
+
# @return [Hash] `{name: 'Ed25519'}`
|
|
247
|
+
def self.verify_algorithm = { name: 'Ed25519' }
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
Triauth::Verifiers.register('webauthn-es256', WebAuthnEs256)
|
|
251
|
+
Triauth::Verifiers.register('webauthn-ed25519', WebAuthnEd25519)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2026 The Triauth Authors (https://www.triauth.org/)
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
#
|
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
require_relative 'verifiers/registry'
|
|
21
|
+
require_relative 'verifiers/base'
|
|
22
|
+
# Importing the concrete verifier modules for their side effects: each one calls
|
|
23
|
+
# `register(...)` at module-load time so that `Triauth::Verifiers.get(...)` works
|
|
24
|
+
# out of the box for 'es256', 'ed25519', 'webauthn-es256', and 'webauthn-ed25519'.
|
|
25
|
+
require_relative 'verifiers/ecdsa'
|
|
26
|
+
require_relative 'verifiers/ed25519'
|
|
27
|
+
require_relative 'verifiers/web_authn'
|
|
28
|
+
|
|
29
|
+
module Triauth
|
|
30
|
+
# A collection holding information about registered digital signature verifiers.
|
|
31
|
+
#
|
|
32
|
+
# Each public key stored in the identity records in the DNS may be associated with a type of verifier that is to be used with it.
|
|
33
|
+
module Verifiers
|
|
34
|
+
# the JS re-exports (`register`, `get`, `list`, `Base`, `Ecdsa`, `Ed25519`, `WebAuthn`, `WebAuthnEs256`, `WebAuthnEd25519`)
|
|
35
|
+
# have no counterpart: the module functions and the classes live on `Triauth::Verifiers` once their files are required
|
|
36
|
+
end
|
|
37
|
+
end
|