ruby-paseto 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +8 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +549 -0
- data/lib/paseto/asn1/algorithm_identifier.rb +17 -0
- data/lib/paseto/asn1/curve_private_key.rb +22 -0
- data/lib/paseto/asn1/ec_private_key.rb +27 -0
- data/lib/paseto/asn1/ecdsa_full_r.rb +26 -0
- data/lib/paseto/asn1/ecdsa_sig_value.rb +23 -0
- data/lib/paseto/asn1/ecdsa_signature.rb +49 -0
- data/lib/paseto/asn1/ed25519_identifier.rb +15 -0
- data/lib/paseto/asn1/named_curve.rb +17 -0
- data/lib/paseto/asn1/one_asymmetric_key.rb +32 -0
- data/lib/paseto/asn1/private_key.rb +17 -0
- data/lib/paseto/asn1/private_key_algorithm_identifier.rb +17 -0
- data/lib/paseto/asn1/public_key.rb +17 -0
- data/lib/paseto/asn1/subject_public_key_info.rb +28 -0
- data/lib/paseto/asn1.rb +101 -0
- data/lib/paseto/asymmetric_key.rb +100 -0
- data/lib/paseto/configuration/box.rb +23 -0
- data/lib/paseto/configuration/decode_configuration.rb +68 -0
- data/lib/paseto/configuration.rb +18 -0
- data/lib/paseto/interface/i_d.rb +23 -0
- data/lib/paseto/interface/key.rb +113 -0
- data/lib/paseto/interface/pbkd.rb +83 -0
- data/lib/paseto/interface/pie.rb +59 -0
- data/lib/paseto/interface/pke.rb +86 -0
- data/lib/paseto/interface/serializer.rb +19 -0
- data/lib/paseto/interface/version.rb +161 -0
- data/lib/paseto/interface/wrapper.rb +20 -0
- data/lib/paseto/operations/i_d.rb +48 -0
- data/lib/paseto/operations/id/i_dv3.rb +20 -0
- data/lib/paseto/operations/id/i_dv4.rb +20 -0
- data/lib/paseto/operations/pbkd/p_b_k_dv3.rb +85 -0
- data/lib/paseto/operations/pbkd/p_b_k_dv4.rb +94 -0
- data/lib/paseto/operations/pbkw.rb +73 -0
- data/lib/paseto/operations/pke/p_k_ev3.rb +97 -0
- data/lib/paseto/operations/pke/p_k_ev4.rb +95 -0
- data/lib/paseto/operations/pke.rb +57 -0
- data/lib/paseto/operations/wrap.rb +29 -0
- data/lib/paseto/paserk.rb +55 -0
- data/lib/paseto/paserk_types.rb +46 -0
- data/lib/paseto/protocol/version3.rb +100 -0
- data/lib/paseto/protocol/version4.rb +99 -0
- data/lib/paseto/result.rb +9 -0
- data/lib/paseto/serializer/optional_json.rb +30 -0
- data/lib/paseto/serializer/raw.rb +23 -0
- data/lib/paseto/sodium/curve_25519.rb +46 -0
- data/lib/paseto/sodium/safe_ed25519_loader.rb +19 -0
- data/lib/paseto/sodium/stream/base.rb +82 -0
- data/lib/paseto/sodium/stream/x_cha_cha20_xor.rb +31 -0
- data/lib/paseto/sodium.rb +5 -0
- data/lib/paseto/symmetric_key.rb +119 -0
- data/lib/paseto/token.rb +127 -0
- data/lib/paseto/token_types.rb +29 -0
- data/lib/paseto/util.rb +105 -0
- data/lib/paseto/v3/local.rb +63 -0
- data/lib/paseto/v3/public.rb +204 -0
- data/lib/paseto/v4/local.rb +56 -0
- data/lib/paseto/v4/public.rb +169 -0
- data/lib/paseto/validator.rb +154 -0
- data/lib/paseto/verifiers/footer.rb +30 -0
- data/lib/paseto/verifiers/payload.rb +42 -0
- data/lib/paseto/verify.rb +48 -0
- data/lib/paseto/version.rb +6 -0
- data/lib/paseto/versions.rb +25 -0
- data/lib/paseto/wrappers/pie/pie_v3.rb +72 -0
- data/lib/paseto/wrappers/pie/pie_v4.rb +72 -0
- data/lib/paseto/wrappers/pie.rb +71 -0
- data/lib/paseto.rb +99 -0
- data/paseto.gemspec +58 -0
- data/sorbet/config +3 -0
- data/sorbet/rbi/annotations/rainbow.rbi +269 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1083 -0
- data/sorbet/rbi/gems/docile@1.4.0.rbi +376 -0
- data/sorbet/rbi/gems/ffi@1.15.5.rbi +1994 -0
- data/sorbet/rbi/gems/io-console@0.5.11.rbi +8 -0
- data/sorbet/rbi/gems/irb@1.5.1.rbi +342 -0
- data/sorbet/rbi/gems/json@2.6.3.rbi +1541 -0
- data/sorbet/rbi/gems/multi_json@1.15.0.rbi +267 -0
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
- data/sorbet/rbi/gems/oj@3.13.23.rbi +603 -0
- data/sorbet/rbi/gems/openssl@3.0.1.rbi +1735 -0
- data/sorbet/rbi/gems/parallel@1.22.1.rbi +277 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +407 -0
- data/sorbet/rbi/gems/rake@13.0.6.rbi +3021 -0
- data/sorbet/rbi/gems/rbnacl@7.1.1.rbi +3218 -0
- data/sorbet/rbi/gems/regexp_parser@2.6.1.rbi +3481 -0
- data/sorbet/rbi/gems/reline@0.3.1.rbi +8 -0
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +4717 -0
- data/sorbet/rbi/gems/rspec-core@3.12.0.rbi +10887 -0
- data/sorbet/rbi/gems/rspec-expectations@3.12.0.rbi +8090 -0
- data/sorbet/rbi/gems/rspec-mocks@3.12.0.rbi +5300 -0
- data/sorbet/rbi/gems/rspec-support@3.12.0.rbi +1617 -0
- data/sorbet/rbi/gems/rspec@3.12.0.rbi +88 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +1239 -0
- data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +219 -0
- data/sorbet/rbi/gems/simplecov@0.21.2.rbi +2135 -0
- data/sorbet/rbi/gems/simplecov_json_formatter@0.1.4.rbi +8 -0
- data/sorbet/rbi/gems/thor@1.2.1.rbi +3956 -0
- data/sorbet/rbi/gems/timecop@0.9.6.rbi +350 -0
- data/sorbet/rbi/gems/unicode-display_width@2.3.0.rbi +48 -0
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +2555 -0
- data/sorbet/rbi/gems/yard-sorbet@0.7.0.rbi +391 -0
- data/sorbet/rbi/gems/yard@0.9.28.rbi +17816 -0
- data/sorbet/rbi/gems/zeitwerk@2.6.6.rbi +950 -0
- data/sorbet/rbi/shims/multi_json.rbi +19 -0
- data/sorbet/rbi/shims/openssl.rbi +111 -0
- data/sorbet/rbi/shims/rbnacl.rbi +65 -0
- data/sorbet/rbi/shims/zeitwerk.rbi +6 -0
- data/sorbet/rbi/todo.rbi +7 -0
- data/sorbet/tapioca/config.yml +30 -0
- data/sorbet/tapioca/require.rb +12 -0
- metadata +376 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class AlgorithmIdentifier < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :algorithm, NamedCurve
|
10
|
+
|
11
|
+
sig { returns(OpenSSL::ASN1::Sequence) }
|
12
|
+
def build
|
13
|
+
OpenSSL::ASN1::Sequence.new(algorithm.build)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class CurvePrivateKey < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :private_key, String
|
10
|
+
|
11
|
+
sig { returns(OpenSSL::ASN1::OctetString) }
|
12
|
+
def build
|
13
|
+
OpenSSL::ASN1::OctetString.new(private_key)
|
14
|
+
end
|
15
|
+
|
16
|
+
sig { returns(String) }
|
17
|
+
def to_der
|
18
|
+
build.to_der
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class ECPrivateKey < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :private_key, String
|
10
|
+
|
11
|
+
sig { returns(OpenSSL::ASN1::Sequence) }
|
12
|
+
def build
|
13
|
+
OpenSSL::ASN1::Sequence.new(
|
14
|
+
[
|
15
|
+
OpenSSL::ASN1::Integer.new(1),
|
16
|
+
OpenSSL::ASN1::OctetString.new(private_key)
|
17
|
+
]
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { returns(String) }
|
22
|
+
def to_der
|
23
|
+
build.to_der
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class ECDSAFullR < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :r, OpenSSL::PKey::EC::Point
|
10
|
+
const :s, OpenSSL::BN
|
11
|
+
|
12
|
+
sig { returns(OpenSSL::ASN1::Sequence) }
|
13
|
+
def build
|
14
|
+
# Unsupported by OpenSSL 3.0
|
15
|
+
# :nocov:
|
16
|
+
OpenSSL::ASN1::Sequence.new(
|
17
|
+
[
|
18
|
+
OpenSSL::ASN1::OctetString.new(r.to_octet_string(:compressed)),
|
19
|
+
OpenSSL::ASN1::Integer.new(s)
|
20
|
+
]
|
21
|
+
)
|
22
|
+
# :nocov:
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class ECDSASigValue < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :r, OpenSSL::BN
|
10
|
+
const :s, OpenSSL::BN
|
11
|
+
|
12
|
+
sig { returns(OpenSSL::ASN1::Sequence) }
|
13
|
+
def build
|
14
|
+
OpenSSL::ASN1::Sequence.new(
|
15
|
+
[
|
16
|
+
OpenSSL::ASN1::Integer.new(r),
|
17
|
+
OpenSSL::ASN1::Integer.new(s)
|
18
|
+
]
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class ECDSASignature < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :signature, T.any(ECDSASigValue, ECDSAFullR)
|
10
|
+
|
11
|
+
sig { params(bytes: String, part_len: Integer).returns(ECDSASignature) }
|
12
|
+
def self.from_rs(bytes, part_len)
|
13
|
+
r = OpenSSL::BN.new(T.must(bytes[0, part_len]), 2)
|
14
|
+
s = OpenSSL::BN.new(T.must(bytes[-part_len, part_len]), 2)
|
15
|
+
new(signature: ECDSASigValue.new(r: r, s: s))
|
16
|
+
end
|
17
|
+
|
18
|
+
sig { params(sig: String).returns(ECDSASignature) }
|
19
|
+
def self.from_asn1(sig)
|
20
|
+
r, s = OpenSSL::ASN1.decode(sig).value.map(&:value)
|
21
|
+
new(signature: ECDSASigValue.new(r: r, s: s))
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { returns(OpenSSL::ASN1::Sequence) }
|
25
|
+
def build
|
26
|
+
signature.build
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { returns(String) }
|
30
|
+
def to_der
|
31
|
+
build.to_der
|
32
|
+
end
|
33
|
+
|
34
|
+
sig { params(part_len: Integer).returns(String) }
|
35
|
+
def to_rs(part_len)
|
36
|
+
case signature
|
37
|
+
when ECDSASigValue
|
38
|
+
r = T.cast(signature.r, OpenSSL::BN).to_s(2).rjust(part_len, "\x00")
|
39
|
+
when ECDSAFullR
|
40
|
+
# :nocov:
|
41
|
+
r = T.cast(signature.r, OpenSSL::PKey::EC::Point).to_octet_string(:compressed).rjust(part_len, "\x00")
|
42
|
+
# :nocov:
|
43
|
+
end
|
44
|
+
s = signature.s.to_s(2).rjust(part_len, "\x00")
|
45
|
+
[r, s].join
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class Ed25519Identifier < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { returns([OpenSSL::ASN1::ObjectId]) }
|
10
|
+
def build
|
11
|
+
[OpenSSL::ASN1::ObjectId('ED25519')]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class NamedCurve < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :curve_name, String
|
10
|
+
|
11
|
+
sig { returns([OpenSSL::ASN1::ObjectId, OpenSSL::ASN1::ObjectId]) }
|
12
|
+
def build
|
13
|
+
[OpenSSL::ASN1::ObjectId('id-ecPublicKey'), OpenSSL::ASN1::ObjectId(curve_name)]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class OneAsymmetricKey < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :version, OpenSSL::BN
|
10
|
+
const :algorithm, PrivateKeyAlgorithmIdentifier
|
11
|
+
const :private_key, PrivateKey
|
12
|
+
const :public_key, T.nilable(PublicKey)
|
13
|
+
|
14
|
+
sig { returns(OpenSSL::ASN1::Sequence) }
|
15
|
+
def build
|
16
|
+
OpenSSL::ASN1::Sequence.new(
|
17
|
+
[
|
18
|
+
OpenSSL::ASN1::Integer.new(version),
|
19
|
+
algorithm.build,
|
20
|
+
private_key.build
|
21
|
+
# public_key&.build
|
22
|
+
].compact
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { returns(String) }
|
27
|
+
def to_der
|
28
|
+
build.to_der
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class PrivateKey < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :private_key, T.any(ECPrivateKey, CurvePrivateKey)
|
10
|
+
|
11
|
+
sig { returns(OpenSSL::ASN1::OctetString) }
|
12
|
+
def build
|
13
|
+
OpenSSL::ASN1::OctetString.new(private_key.to_der)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class PrivateKeyAlgorithmIdentifier < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :parameters, T.any(Ed25519Identifier, NamedCurve)
|
10
|
+
|
11
|
+
sig { returns(OpenSSL::ASN1::Sequence) }
|
12
|
+
def build
|
13
|
+
OpenSSL::ASN1::Sequence.new(parameters.build)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class PublicKey < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :public_key, String
|
10
|
+
|
11
|
+
sig { returns(OpenSSL::ASN1::BitString) }
|
12
|
+
def build
|
13
|
+
OpenSSL::ASN1::BitString.new(public_key)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
class SubjectPublicKeyInfo < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :algorithm_identifier, AlgorithmIdentifier
|
10
|
+
const :public_key, PublicKey
|
11
|
+
|
12
|
+
sig { returns(OpenSSL::ASN1::Sequence) }
|
13
|
+
def build
|
14
|
+
OpenSSL::ASN1::Sequence.new(
|
15
|
+
[
|
16
|
+
algorithm_identifier.build,
|
17
|
+
public_key.build
|
18
|
+
]
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { returns(String) }
|
23
|
+
def to_der
|
24
|
+
build.to_der
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/paseto/asn1.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module ASN1
|
6
|
+
# References:
|
7
|
+
# https://www.rfc-editor.org/rfc/rfc5208 PKCS #8: Private-Key Information Syntax Specification Version 1.2
|
8
|
+
# https://www.rfc-editor.org/rfc/rfc5480 Elliptic Curve Cryptography Subject Public Key Information
|
9
|
+
# https://www.rfc-editor.org/rfc/rfc5915 Elliptic Curve Private Key Structure
|
10
|
+
# https://www.rfc-editor.org/rfc/rfc5958 Asymmetric Key Packages (obsoletes PKCS#8)
|
11
|
+
# https://www.rfc-editor.org/rfc/rfc8018 PKCS #5: Password-Based Cryptography Specification Version 2.1
|
12
|
+
# https://www.secg.org/sec1-v2.pdf
|
13
|
+
|
14
|
+
extend T::Sig
|
15
|
+
|
16
|
+
sig { params(bytes: String).returns(String) }
|
17
|
+
def self.p384_scalar_bytes_to_oak_der(bytes)
|
18
|
+
OneAsymmetricKey.new(
|
19
|
+
version: OpenSSL::BN.new(1),
|
20
|
+
algorithm: PrivateKeyAlgorithmIdentifier.new(
|
21
|
+
parameters: NamedCurve.new(curve_name: 'secp384r1')
|
22
|
+
),
|
23
|
+
private_key: PrivateKey.new(
|
24
|
+
private_key: ECPrivateKey.new(
|
25
|
+
private_key: bytes
|
26
|
+
)
|
27
|
+
)
|
28
|
+
).to_der
|
29
|
+
end
|
30
|
+
|
31
|
+
sig { params(bytes: String).returns(String) }
|
32
|
+
def self.p384_public_bytes_to_spki_der(bytes)
|
33
|
+
SubjectPublicKeyInfo.new(
|
34
|
+
algorithm_identifier: AlgorithmIdentifier.new(
|
35
|
+
algorithm: NamedCurve.new(curve_name: 'secp384r1')
|
36
|
+
),
|
37
|
+
public_key: PublicKey.new(
|
38
|
+
public_key: bytes
|
39
|
+
)
|
40
|
+
).to_der
|
41
|
+
end
|
42
|
+
|
43
|
+
# RbNaCl::SigningKey.keypair_bytes returns the 32-byte private scalar and group element
|
44
|
+
# as (s || g), so we repack that into an ASN1 structure and then Base64 the resulting DER
|
45
|
+
# to get a PEM.
|
46
|
+
sig { params(bytes: String).returns(String) }
|
47
|
+
def self.ed25519_rs_to_oak_der(bytes)
|
48
|
+
OneAsymmetricKey.new(
|
49
|
+
version: OpenSSL::BN.new(0),
|
50
|
+
algorithm: PrivateKeyAlgorithmIdentifier.new(
|
51
|
+
parameters: Ed25519Identifier.new
|
52
|
+
),
|
53
|
+
private_key: PrivateKey.new(
|
54
|
+
private_key: CurvePrivateKey.new(
|
55
|
+
private_key: T.must(bytes.byteslice(0, 32))
|
56
|
+
)
|
57
|
+
)
|
58
|
+
).to_der
|
59
|
+
end
|
60
|
+
|
61
|
+
sig { params(bytes: String).returns(String) }
|
62
|
+
def self.ed25519_rs_to_oak_pem(bytes)
|
63
|
+
der_to_private_pem(ed25519_rs_to_oak_der(bytes))
|
64
|
+
end
|
65
|
+
|
66
|
+
sig { params(verify_key: String).returns(String) }
|
67
|
+
def self.ed25519_pubkey_nacl_to_der(verify_key)
|
68
|
+
OpenSSL::ASN1::Sequence.new(
|
69
|
+
[
|
70
|
+
OpenSSL::ASN1::Sequence.new(
|
71
|
+
[OpenSSL::ASN1::ObjectId.new('ED25519')]
|
72
|
+
),
|
73
|
+
OpenSSL::ASN1::BitString.new(verify_key)
|
74
|
+
]
|
75
|
+
).to_der
|
76
|
+
end
|
77
|
+
|
78
|
+
sig { params(verify_key: String).returns(String) }
|
79
|
+
def self.ed25519_pubkey_nacl_to_pem(verify_key)
|
80
|
+
der_to_public_pem(ed25519_pubkey_nacl_to_der(verify_key))
|
81
|
+
end
|
82
|
+
|
83
|
+
sig { params(der: String).returns(String) }
|
84
|
+
def self.der_to_public_pem(der)
|
85
|
+
<<~PEM
|
86
|
+
-----BEGIN PUBLIC KEY-----
|
87
|
+
#{Base64.strict_encode64(der)}
|
88
|
+
-----END PUBLIC KEY-----
|
89
|
+
PEM
|
90
|
+
end
|
91
|
+
|
92
|
+
sig { params(der: String).returns(String) }
|
93
|
+
def self.der_to_private_pem(der)
|
94
|
+
<<~PEM
|
95
|
+
-----BEGIN PRIVATE KEY-----
|
96
|
+
#{Base64.strict_encode64(der)}
|
97
|
+
-----END PRIVATE KEY-----
|
98
|
+
PEM
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
class AsymmetricKey < Interface::Key
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Helpers
|
8
|
+
|
9
|
+
abstract!
|
10
|
+
|
11
|
+
sig(:final) { override.returns(String) }
|
12
|
+
attr_reader :id, :paserk
|
13
|
+
|
14
|
+
sig(:final) { returns(String) }
|
15
|
+
attr_reader :pid, :public_paserk
|
16
|
+
|
17
|
+
sig { params(_key: T.untyped).void }
|
18
|
+
def initialize(_key)
|
19
|
+
@public_paserk = T.let("#{paserk_version}.public.#{Util.encode64(public_bytes)}".freeze, String)
|
20
|
+
|
21
|
+
@pid = T.let(Operations::ID.pid(self).freeze, String)
|
22
|
+
|
23
|
+
if private?
|
24
|
+
@paserk = T.let("#{paserk_version}.secret.#{Util.encode64(to_bytes)}".freeze, String)
|
25
|
+
@id = T.let(Operations::ID.sid(self).freeze, String)
|
26
|
+
else
|
27
|
+
@paserk = @public_paserk
|
28
|
+
@id = T.let(@pid, String)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { abstract.params(message: String, footer: String, implicit_assertion: String).returns(Token) }
|
33
|
+
def sign(message:, footer: '', implicit_assertion: ''); end
|
34
|
+
|
35
|
+
sig { abstract.params(token: Token, implicit_assertion: String).returns(String) }
|
36
|
+
def verify(token:, implicit_assertion: ''); end
|
37
|
+
|
38
|
+
sig { abstract.returns(String) }
|
39
|
+
def public_to_pem; end
|
40
|
+
|
41
|
+
sig { abstract.returns(String) }
|
42
|
+
def private_to_pem; end
|
43
|
+
|
44
|
+
sig { abstract.returns(T::Boolean) }
|
45
|
+
def private?; end
|
46
|
+
|
47
|
+
sig { abstract.returns(String) }
|
48
|
+
def public_bytes; end
|
49
|
+
|
50
|
+
sig { abstract.params(other: T.untyped).returns(String) }
|
51
|
+
def ecdh(other); end
|
52
|
+
|
53
|
+
sig(:final) do
|
54
|
+
override.params(
|
55
|
+
payload: T::Hash[String, T.untyped],
|
56
|
+
footer: String,
|
57
|
+
implicit_assertion: String,
|
58
|
+
options: T.nilable(T.any(String, Integer, Symbol, T::Boolean))
|
59
|
+
).returns(String)
|
60
|
+
end
|
61
|
+
def encode!(payload, footer: '', implicit_assertion: '', **options)
|
62
|
+
MultiJson.dump(payload, options)
|
63
|
+
.then { |json| sign(message: json, footer: footer, implicit_assertion: implicit_assertion) }
|
64
|
+
.then(&:to_s)
|
65
|
+
end
|
66
|
+
|
67
|
+
sig(:final) do
|
68
|
+
override.params(
|
69
|
+
payload: String,
|
70
|
+
implicit_assertion: String,
|
71
|
+
options: T.nilable(T.any(Proc, String, Integer, Symbol, T::Boolean))
|
72
|
+
).returns(Result)
|
73
|
+
end
|
74
|
+
def decode!(payload, implicit_assertion: '', **options)
|
75
|
+
token = Token.parse(payload)
|
76
|
+
|
77
|
+
verify(token: token, implicit_assertion: implicit_assertion)
|
78
|
+
.then { |json| MultiJson.load(json, **options) }
|
79
|
+
.then { |claims| Result.new(claims: claims, footer: token.footer) }
|
80
|
+
end
|
81
|
+
|
82
|
+
sig(:final) { override.returns(String) }
|
83
|
+
def pbkw_header = protocol.pbkd_secret_header
|
84
|
+
|
85
|
+
sig(:final) { override.returns(String) }
|
86
|
+
def purpose = 'public'
|
87
|
+
|
88
|
+
sig(:final) { returns(Interface::PKE) }
|
89
|
+
def pke = protocol.pke(self)
|
90
|
+
|
91
|
+
sig(:final) { returns(String) }
|
92
|
+
def sid = @sid ||= T.let(Operations::ID.sid(self), T.nilable(String))
|
93
|
+
|
94
|
+
sig(:final) { params(other: SymmetricKey).returns(String) }
|
95
|
+
def seal(other) = Paserk.seal(sealing_key: self, key: other)
|
96
|
+
|
97
|
+
sig(:final) { params(paserk: String).returns(SymmetricKey) }
|
98
|
+
def unseal(paserk) = Paserk.from_paserk(paserk: paserk, unsealing_key: self)
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module Configuration
|
6
|
+
class Box
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { returns(DecodeConfiguration) }
|
10
|
+
attr_accessor :decode
|
11
|
+
|
12
|
+
sig { void }
|
13
|
+
def initialize
|
14
|
+
@decode = T.let(DecodeConfiguration.new, DecodeConfiguration)
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { void }
|
18
|
+
def reset!
|
19
|
+
@decode = DecodeConfiguration.new
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module Configuration
|
6
|
+
class DecodeConfiguration
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { returns(Interface::Serializer) }
|
10
|
+
attr_accessor :footer_serializer
|
11
|
+
|
12
|
+
sig { returns(T::Boolean) }
|
13
|
+
attr_accessor :verify_exp, :verify_nbf, :verify_iat
|
14
|
+
|
15
|
+
sig { returns(T.any(FalseClass, String)) }
|
16
|
+
attr_accessor :verify_sub
|
17
|
+
|
18
|
+
sig { returns(T.any(FalseClass, T::Array[String])) }
|
19
|
+
attr_accessor :verify_aud
|
20
|
+
|
21
|
+
sig { returns(T.any(T::Array[T.any(String, Regexp, T.proc.params(issuer: String).returns(T::Boolean))], FalseClass)) }
|
22
|
+
attr_accessor :verify_iss
|
23
|
+
|
24
|
+
sig do
|
25
|
+
returns(T.any(
|
26
|
+
T::Boolean,
|
27
|
+
T.proc.params(jti: String).returns(T::Boolean)
|
28
|
+
))
|
29
|
+
end
|
30
|
+
attr_accessor :verify_jti
|
31
|
+
|
32
|
+
sig { void }
|
33
|
+
def initialize # rubocop:disable Metrics/AbcSize
|
34
|
+
@footer_serializer = T.let(Serializer::OptionalJson, Interface::Serializer)
|
35
|
+
@verify_exp = T.let(true, T::Boolean)
|
36
|
+
@verify_nbf = T.let(true, T::Boolean)
|
37
|
+
@verify_iat = T.let(true, T::Boolean)
|
38
|
+
|
39
|
+
@verify_iss = T.let(false, T.any(FalseClass,
|
40
|
+
T::Array[
|
41
|
+
T.any(String, Regexp, T.proc.params(issuer: String).returns(T::Boolean))
|
42
|
+
]))
|
43
|
+
|
44
|
+
@verify_aud = T.let(false, T.any(FalseClass, T::Array[String]))
|
45
|
+
|
46
|
+
@verify_sub = T.let(false, T.any(FalseClass, String))
|
47
|
+
|
48
|
+
@verify_jti = T.let(false, T.any(
|
49
|
+
T::Boolean,
|
50
|
+
T.proc.params(jti: String).returns(T::Boolean)
|
51
|
+
))
|
52
|
+
end
|
53
|
+
|
54
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
55
|
+
def to_h
|
56
|
+
{
|
57
|
+
verify_exp: verify_exp,
|
58
|
+
verify_nbf: verify_nbf,
|
59
|
+
verify_iss: verify_iss,
|
60
|
+
verify_iat: verify_iat,
|
61
|
+
verify_jti: verify_jti,
|
62
|
+
verify_aud: verify_aud,
|
63
|
+
verify_sub: verify_sub
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module Configuration
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { params(blk: T.proc.params(config: Paseto::Configuration::Box).void).void }
|
9
|
+
def configure(&blk) # rubocop:disable Lint/UnusedMethodArgument
|
10
|
+
yield(config)
|
11
|
+
end
|
12
|
+
|
13
|
+
sig { returns(Paseto::Configuration::Box) }
|
14
|
+
def config
|
15
|
+
@config ||= T.let(Configuration::Box.new, T.nilable(Paseto::Configuration::Box))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Paseto
|
5
|
+
module Interface
|
6
|
+
module ID
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
|
10
|
+
abstract!
|
11
|
+
|
12
|
+
sig(:final) { params(type: String, paserk: String).returns(String) }
|
13
|
+
def encode(type, paserk)
|
14
|
+
header = "#{protocol.paserk_version}.#{type}."
|
15
|
+
d = protocol.digest("#{header}#{paserk}", digest_size: 33)
|
16
|
+
"#{header}#{Util.encode64(d)}"
|
17
|
+
end
|
18
|
+
|
19
|
+
sig { abstract.returns(Interface::Version) }
|
20
|
+
def protocol; end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|