ruby-paseto 0.1.2 → 0.2.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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +55 -50
  4. data/lib/paseto/asn1/ecdsa_signature.rb +2 -2
  5. data/lib/paseto/asymmetric_key.rb +7 -4
  6. data/lib/paseto/configuration/decode_configuration.rb +7 -7
  7. data/lib/paseto/interface/key.rb +5 -2
  8. data/lib/paseto/interface/pbkd.rb +4 -20
  9. data/lib/paseto/interface/pie.rb +5 -26
  10. data/lib/paseto/interface/pke.rb +15 -51
  11. data/lib/paseto/interface/version.rb +30 -117
  12. data/lib/paseto/operations/id/i_dv3.rb +1 -1
  13. data/lib/paseto/operations/id/i_dv4.rb +1 -1
  14. data/lib/paseto/operations/pbkd/p_b_k_dv3.rb +4 -5
  15. data/lib/paseto/operations/pbkd/p_b_k_dv4.rb +3 -4
  16. data/lib/paseto/operations/pbkw.rb +6 -6
  17. data/lib/paseto/operations/pke/p_k_ev3.rb +23 -25
  18. data/lib/paseto/operations/pke/p_k_ev4.rb +33 -34
  19. data/lib/paseto/operations/pke.rb +9 -10
  20. data/lib/paseto/operations/wrap.rb +1 -1
  21. data/lib/paseto/paserk.rb +1 -1
  22. data/lib/paseto/paserk_types.rb +2 -2
  23. data/lib/paseto/protocol/version3.rb +17 -16
  24. data/lib/paseto/protocol/version4.rb +17 -16
  25. data/lib/paseto/symmetric_key.rb +16 -10
  26. data/lib/paseto/token.rb +17 -15
  27. data/lib/paseto/token_types.rb +2 -2
  28. data/lib/paseto/util.rb +1 -1
  29. data/lib/paseto/v3/local.rb +1 -1
  30. data/lib/paseto/v3/public.rb +5 -4
  31. data/lib/paseto/v4/local.rb +3 -3
  32. data/lib/paseto/v4/public.rb +3 -6
  33. data/lib/paseto/validator.rb +1 -1
  34. data/lib/paseto/version.rb +1 -1
  35. data/lib/paseto/versions.rb +2 -2
  36. data/lib/paseto/wrappers/pie/pie_v3.rb +18 -21
  37. data/lib/paseto/wrappers/pie/pie_v4.rb +17 -20
  38. data/lib/paseto/wrappers/pie.rb +3 -17
  39. data/lib/paseto.rb +2 -5
  40. data/paseto.gemspec +5 -5
  41. data/sorbet/rbi/annotations/.gitattributes +1 -0
  42. data/sorbet/rbi/annotations/rainbow.rbi +4 -4
  43. data/sorbet/rbi/gems/.gitattributes +1 -0
  44. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1 -1
  45. data/sorbet/rbi/gems/docile@1.4.0.rbi +1 -1
  46. data/sorbet/rbi/gems/{ffi@1.15.5.rbi → ffi@1.16.3.rbi} +260 -117
  47. data/sorbet/rbi/gems/{oj@3.14.2.rbi → oj@3.16.1.rbi} +26 -37
  48. data/sorbet/rbi/gems/{rake@13.0.6.rbi → rake@13.1.0.rbi} +75 -69
  49. data/sorbet/rbi/gems/rbnacl@7.1.1.rbi +2 -2
  50. data/sorbet/rbi/gems/{rspec-core@3.12.1.rbi → rspec-core@3.12.2.rbi} +1 -1
  51. data/sorbet/rbi/gems/{rspec-expectations@3.12.2.rbi → rspec-expectations@3.12.3.rbi} +27 -33
  52. data/sorbet/rbi/gems/{rspec-mocks@3.12.4.rbi → rspec-mocks@3.12.6.rbi} +60 -61
  53. data/sorbet/rbi/gems/{rspec-support@3.12.0.rbi → rspec-support@3.12.1.rbi} +35 -43
  54. data/sorbet/rbi/gems/rspec@3.12.0.rbi +22 -28
  55. data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +41 -44
  56. data/sorbet/rbi/gems/simplecov_json_formatter@0.1.4.rbi +232 -2
  57. data/sorbet/rbi/gems/{timecop@0.9.6.rbi → timecop@0.9.8.rbi} +13 -16
  58. data/sorbet/rbi/shims/multi_json.rbi +2 -0
  59. data/sorbet/rbi/shims/openssl.rbi +0 -8
  60. data/sorbet/rbi/todo.rbi +5 -1
  61. metadata +19 -42
  62. data/sorbet/rbi/gems/ast@2.4.2.rbi +0 -584
  63. data/sorbet/rbi/gems/io-console@0.6.0.rbi +0 -8
  64. data/sorbet/rbi/gems/irb@1.6.3.rbi +0 -342
  65. data/sorbet/rbi/gems/json@2.6.3.rbi +0 -1541
  66. data/sorbet/rbi/gems/multi_json@1.15.0.rbi +0 -267
  67. data/sorbet/rbi/gems/netrc@0.11.0.rbi +0 -158
  68. data/sorbet/rbi/gems/openssl@3.1.0.rbi +0 -1739
  69. data/sorbet/rbi/gems/parallel@1.22.1.rbi +0 -277
  70. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +0 -407
  71. data/sorbet/rbi/gems/regexp_parser@2.7.0.rbi +0 -3580
  72. data/sorbet/rbi/gems/reline@0.3.2.rbi +0 -8
  73. data/sorbet/rbi/gems/rexml@3.2.5.rbi +0 -4717
  74. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +0 -1317
  75. data/sorbet/rbi/gems/thor@1.2.1.rbi +0 -3956
  76. data/sorbet/rbi/gems/unicode-display_width@2.4.2.rbi +0 -65
  77. data/sorbet/rbi/gems/webrick@1.7.0.rbi +0 -2555
  78. data/sorbet/rbi/gems/yard-sorbet@0.8.0.rbi +0 -441
  79. data/sorbet/rbi/gems/yard@0.9.28.rbi +0 -17816
  80. data/sorbet/rbi/gems/zeitwerk@2.6.7.rbi +0 -8
@@ -7,30 +7,31 @@ module Paseto
7
7
  extend T::Sig
8
8
  extend T::Helpers
9
9
 
10
+ include Singleton
10
11
  include Interface::Version
11
12
 
12
13
  sig(:final) { override.params(key: String, nonce: String, payload: String).returns(String) }
13
- def self.crypt(key:, nonce:, payload:)
14
+ def crypt(key:, nonce:, payload:)
14
15
  Paseto::Sodium::Stream::XChaCha20Xor.new(key).encrypt(nonce, payload)
15
16
  end
16
17
 
17
18
  sig(:final) { override.params(data: String, digest_size: Integer).returns(String) }
18
- def self.digest(data, digest_size:)
19
- RbNaCl::Hash.blake2b(data, digest_size: digest_size)
19
+ def digest(data, digest_size: 32)
20
+ RbNaCl::Hash.blake2b(data, digest_size:)
20
21
  end
21
22
 
22
23
  sig(:final) { override.returns(Integer) }
23
- def self.digest_bytes
24
+ def digest_bytes
24
25
  32
25
26
  end
26
27
 
27
28
  sig(:final) { override.params(data: String, key: String, digest_size: Integer).returns(String) }
28
- def self.hmac(data, key:, digest_size: 32)
29
- RbNaCl::Hash.blake2b(data, key: key, digest_size: digest_size)
29
+ def hmac(data, key:, digest_size: 32)
30
+ RbNaCl::Hash.blake2b(data, key:, digest_size:)
30
31
  end
31
32
 
32
33
  sig(:final) { override.returns(T.class_of(Operations::ID::IDv4)) }
33
- def self.id
34
+ def id
34
35
  Operations::ID::IDv4
35
36
  end
36
37
 
@@ -42,7 +43,7 @@ module Paseto
42
43
  parameters: T.any(Symbol, Integer)
43
44
  ).returns(String)
44
45
  end
45
- def self.kdf(password, salt:, length:, **parameters)
46
+ def kdf(password, salt:, length:, **parameters)
46
47
  memlimit = RbNaCl::PasswordHash::Argon2.memlimit_value(parameters[:memlimit])
47
48
  opslimit = RbNaCl::PasswordHash::Argon2.opslimit_value(parameters[:opslimit])
48
49
 
@@ -56,42 +57,42 @@ module Paseto
56
57
  end
57
58
 
58
59
  sig(:final) { override.returns(String) }
59
- def self.paserk_version
60
+ def paserk_version
60
61
  'k4'
61
62
  end
62
63
 
63
64
  sig(:final) { override.returns(String) }
64
- def self.pbkd_local_header
65
+ def pbkd_local_header
65
66
  'k4.local-pw'
66
67
  end
67
68
 
68
69
  sig(:final) { override.returns(String) }
69
- def self.pbkd_secret_header
70
+ def pbkd_secret_header
70
71
  'k4.secret-pw'
71
72
  end
72
73
 
73
74
  sig(:final) { override.params(password: String).returns(Operations::PBKD::PBKDv4) }
74
- def self.pbkw(password)
75
+ def pbkw(password)
75
76
  Operations::PBKD::PBKDv4.new(password)
76
77
  end
77
78
 
78
79
  sig(:final) { override.params(key: SymmetricKey).returns(Wrappers::PIE::PieV4) }
79
- def self.pie(key)
80
+ def pie(key)
80
81
  Wrappers::PIE::PieV4.new(key)
81
82
  end
82
83
 
83
84
  sig(:final) { override.params(key: AsymmetricKey).returns(Operations::PKE::PKEv4) }
84
- def self.pke(key)
85
+ def pke(key)
85
86
  Operations::PKE::PKEv4.new(key)
86
87
  end
87
88
 
88
89
  sig(:final) { override.params(size: Integer).returns(String) }
89
- def self.random(size)
90
+ def random(size)
90
91
  RbNaCl::Random.random_bytes(size)
91
92
  end
92
93
 
93
94
  sig(:final) { override.returns(String) }
94
- def self.version
95
+ def version
95
96
  'v4'
96
97
  end
97
98
  end
@@ -34,14 +34,14 @@ module Paseto
34
34
  Util.pre_auth_encode(pae_header, n, c, footer, implicit_assertion)
35
35
  .then { |pre_auth| protocol.hmac(pre_auth, key: ak) }
36
36
  .then { |t| "#{n}#{c}#{t}" }
37
- .then { |payload| Token.new(payload: payload, version: version, purpose: purpose, footer: footer) }
37
+ .then { |payload| Token.new(payload:, version:, purpose:, footer:) }
38
38
  end
39
39
 
40
40
  # Verify and decrypt an encrypted Token, with an optional string `implicit_assertion`, and return the plaintext.
41
41
  # If `token` includes a footer, it is treated as authenticated data to be verified but not returned.
42
42
  # `token` must be a `v4.local` type Token.
43
43
  sig(:final) { params(token: Token, implicit_assertion: String).returns(String) }
44
- def decrypt(token:, implicit_assertion: '')
44
+ def decrypt(token:, implicit_assertion: '') # rubocop:disable Metrics/AbcSize
45
45
  raise LucidityError unless header == token.header
46
46
 
47
47
  n, c, t = split_payload(token.raw_payload)
@@ -52,9 +52,12 @@ module Paseto
52
52
  t2 = protocol.hmac(pre_auth, key: ak)
53
53
  raise InvalidAuthenticator unless Util.constant_compare(t, t2)
54
54
 
55
- protocol.crypt(payload: c, key: ek, nonce: n2).encode(Encoding::UTF_8)
56
- rescue Encoding::UndefinedConversionError
57
- raise ParseError, 'invalid payload encoding'
55
+ decrypted = protocol.crypt(payload: c, key: ek, nonce: n2)
56
+ decrypted.force_encoding('UTF-8')
57
+
58
+ raise ParseError, 'invalid payload encoding' unless decrypted.valid_encoding?
59
+
60
+ decrypted
58
61
  end
59
62
 
60
63
  sig(:final) do
@@ -68,7 +71,7 @@ module Paseto
68
71
  def encode!(payload, footer: '', implicit_assertion: '', **options)
69
72
  n = T.cast(options.delete(:nonce), T.nilable(String))
70
73
  MultiJson.dump(payload, options)
71
- .then { |message| encrypt(message: message, footer: footer, implicit_assertion: implicit_assertion, n: n) }
74
+ .then { |message| encrypt(message:, footer:, implicit_assertion:, n:) }
72
75
  .then(&:to_s)
73
76
  end
74
77
 
@@ -82,9 +85,9 @@ module Paseto
82
85
  def decode!(payload, implicit_assertion: '', **options)
83
86
  token = Token.parse(payload)
84
87
 
85
- decrypt(token: token, implicit_assertion: implicit_assertion)
88
+ decrypt(token:, implicit_assertion:)
86
89
  .then { |json| MultiJson.load(json, **options) }
87
- .then { |claims| Result.new(claims: claims, footer: token.footer) }
90
+ .then { |claims| Result.new(claims:, footer: token.footer) }
88
91
  end
89
92
 
90
93
  sig(:final) { override.returns(String) }
@@ -93,6 +96,9 @@ module Paseto
93
96
  sig(:final) { override.returns(String) }
94
97
  def pbkw_header = protocol.pbkd_local_header
95
98
 
99
+ sig(:final) { override.returns(String) }
100
+ def pie_header = "#{paserk_version}.local-wrap.pie."
101
+
96
102
  sig(:final) { returns(Interface::PIE) }
97
103
  def pie = protocol.pie(self)
98
104
 
@@ -103,10 +109,10 @@ module Paseto
103
109
  def to_bytes = key
104
110
 
105
111
  sig(:final) { params(paserk: String).returns(Interface::Key) }
106
- def unwrap(paserk) = Paserk.from_paserk(paserk: paserk, wrapping_key: self)
112
+ def unwrap(paserk) = Paserk.from_paserk(paserk:, wrapping_key: self)
107
113
 
108
114
  sig(:final) { params(key: Interface::Key, nonce: T.nilable(String)).returns(String) }
109
- def wrap(key, nonce: nil) = Paserk.wrap(key: key, wrapping_key: self, nonce: nonce)
115
+ def wrap(key, nonce: nil) = Paserk.wrap(key:, wrapping_key: self, nonce:)
110
116
 
111
117
  private
112
118
 
data/lib/paseto/token.rb CHANGED
@@ -34,7 +34,7 @@ module Paseto
34
34
  payload = Util.decode64(payload)
35
35
  Util.decode64(footer)
36
36
  .then { |f| serializer.deserialize(f, options) }
37
- .then { |f| new(version: version, purpose: purpose, payload: payload, footer: f) }
37
+ .then { |f| new(version:, purpose:, payload:, footer: f) }
38
38
  end
39
39
 
40
40
  sig { returns(Paseto::Interface::Serializer) }
@@ -60,13 +60,14 @@ module Paseto
60
60
  .then { |data| "#{version}.#{purpose}.#{data}" }
61
61
  .then(&:freeze)
62
62
 
63
- @version = T.let(version.freeze, String)
64
- @purpose = T.let(purpose.freeze, String)
65
- @raw_payload = T.let(payload.freeze, String)
66
- @type = T.let(validate_header, T.class_of(Interface::Key))
67
- @footer = T.let(footer, T.any(String, T::Hash[String, T.untyped]))
68
- @raw_footer = T.let(raw_footer, String)
69
- @str = T.let(paseto, String)
63
+ @version = T.let(version.freeze, String)
64
+ @purpose = T.let(purpose.freeze, String)
65
+ @raw_payload = T.let(payload.freeze, String)
66
+ @type = T.let(validate_header, T.class_of(Interface::Key))
67
+ @footer = T.let(footer, T.any(String, T::Hash[String, T.untyped]))
68
+ @raw_footer = T.let(raw_footer, String)
69
+ @str = T.let(paseto, String)
70
+ @result = T.let(nil, T.nilable(Result))
70
71
  end
71
72
 
72
73
  sig do
@@ -79,8 +80,8 @@ module Paseto
79
80
  def decode!(key, implicit_assertion: '', **options)
80
81
  return @result.claims if @result
81
82
 
82
- key.decode(@str, implicit_assertion: implicit_assertion, **options)
83
- .then { |result| @result = T.let(result, T.nilable(Result)) }
83
+ key.decode(@str, implicit_assertion:, **options)
84
+ .then { |result| @result = result }
84
85
  .then(&:claims)
85
86
  end
86
87
 
@@ -116,12 +117,13 @@ module Paseto
116
117
 
117
118
  sig { returns(T.class_of(Interface::Key)) }
118
119
  def validate_header
119
- type = TokenTypes.deserialize(header).key_klass
120
- return type if type
120
+ type = begin
121
+ TokenTypes.deserialize(header).key_klass
122
+ rescue KeyError
123
+ nil
124
+ end
121
125
 
122
- raise UnsupportedToken, header
123
- rescue KeyError
124
- raise UnsupportedToken, header
126
+ type or raise UnsupportedToken, header
125
127
  end
126
128
  end
127
129
  end
@@ -17,9 +17,9 @@ module Paseto
17
17
  case self
18
18
  in V3Local then V3::Local
19
19
  in V3Public then V3::Public
20
- in V4Local if Paseto.rbnacl?
20
+ in V4Local if Paseto::HAS_RBNACL
21
21
  V4::Local
22
- in V4Public if Paseto.rbnacl?
22
+ in V4Public if Paseto::HAS_RBNACL
23
23
  V4::Public
24
24
  else
25
25
  nil
data/lib/paseto/util.rb CHANGED
@@ -76,7 +76,7 @@ module Paseto
76
76
  # Moving the sig out of the conditional triggers a bug in rubocop-sorbet
77
77
 
78
78
  # Use a faster comparison when RbNaCl is available
79
- if Paseto.rbnacl?
79
+ if Paseto::HAS_RBNACL
80
80
  sig { params(a: String, b: String).returns(T::Boolean) }
81
81
  def self.constant_compare(a, b)
82
82
  h_a = RbNaCl::Hash.blake2b(a)
@@ -28,7 +28,7 @@ module Paseto
28
28
 
29
29
  sig(:final) { params(ikm: String).void }
30
30
  def initialize(ikm:)
31
- @protocol = T.let(Protocol::Version3.new, Paseto::Protocol::Version3)
31
+ @protocol = T.let(Protocol::Version3.instance, Paseto::Protocol::Version3)
32
32
 
33
33
  super(ikm)
34
34
  end
@@ -50,7 +50,7 @@ module Paseto
50
50
  raise LucidityError unless @key.group.curve_name == 'secp384r1'
51
51
  raise InvalidKeyPair unless custom_check_key
52
52
 
53
- @protocol = T.let(Protocol::Version3.new, Protocol::Version3)
53
+ @protocol = T.let(Protocol::Version3.instance, Protocol::Version3)
54
54
 
55
55
  super
56
56
  rescue OpenSSL::PKey::ECError => e
@@ -68,7 +68,7 @@ module Paseto
68
68
  .then { |data| @key.sign_raw(nil, data) }
69
69
  .then { |sig_asn| ASN1::ECDSASignature.from_asn1(sig_asn) }
70
70
  .then { |ecdsa_sig| ecdsa_sig.to_rs(SIGNATURE_PART_LEN) }
71
- .then { |sig| Token.new(payload: "#{message}#{sig}", purpose: purpose, version: version, footer: footer) }
71
+ .then { |sig| Token.new(payload: "#{message}#{sig}", purpose:, version:, footer:) }
72
72
  rescue Encoding::CompatibilityError
73
73
  raise ParseError, 'invalid message encoding, must be UTF-8'
74
74
  end
@@ -80,9 +80,10 @@ module Paseto
80
80
  raise LucidityError unless header == token.header
81
81
 
82
82
  payload = token.raw_payload
83
- raise ParseError, 'message too short' if payload.bytesize < SIGNATURE_BYTE_LEN
83
+ signature_end = payload.bytesize - SIGNATURE_BYTE_LEN
84
+ raise ParseError, 'message too short' if signature_end <= 0
84
85
 
85
- m = T.must(payload.slice(0, payload.bytesize - SIGNATURE_BYTE_LEN))
86
+ m = T.must(payload.slice(0, signature_end))
86
87
 
87
88
  s = T.must(payload.slice(-SIGNATURE_BYTE_LEN, SIGNATURE_BYTE_LEN))
88
89
  .then { |bytes| ASN1::ECDSASignature.from_rs(bytes, SIGNATURE_PART_LEN).to_der }
@@ -21,7 +21,7 @@ module Paseto
21
21
 
22
22
  sig(:final) { params(ikm: String).void }
23
23
  def initialize(ikm:)
24
- @protocol = T.let(Protocol::Version4.new, Paseto::Protocol::Version4)
24
+ @protocol = T.let(Protocol::Version4.instance, Paseto::Protocol::Version4)
25
25
 
26
26
  super(ikm)
27
27
  end
@@ -31,10 +31,10 @@ module Paseto
31
31
  # Derive an encryption key, nonce, and authentication key from an input nonce.
32
32
  sig(:final) { override.params(nonce: String).returns([String, String, String]) }
33
33
  def calc_keys(nonce)
34
- tmp = protocol.hmac("paseto-encryption-key#{nonce}", key: key, digest_size: 56)
34
+ tmp = protocol.hmac("paseto-encryption-key#{nonce}", key:, digest_size: 56)
35
35
  ek = T.must(tmp[0, 32])
36
36
  n2 = T.must(tmp[-24, 24])
37
- ak = protocol.hmac("paseto-auth-key-for-aead#{nonce}", key: key, digest_size: 32)
37
+ ak = protocol.hmac("paseto-auth-key-for-aead#{nonce}", key:, digest_size: 32)
38
38
  [ek, n2, ak]
39
39
  end
40
40
 
@@ -44,7 +44,7 @@ module Paseto
44
44
  @key = T.let(key, T.any(RbNaCl::SigningKey, RbNaCl::VerifyKey))
45
45
 
46
46
  @private = T.let(@key.is_a?(RbNaCl::SigningKey), T::Boolean)
47
- @protocol = T.let(Protocol::Version4.new, Paseto::Protocol::Version4)
47
+ @protocol = T.let(Protocol::Version4.instance, Paseto::Protocol::Version4)
48
48
 
49
49
  super
50
50
  end
@@ -58,7 +58,7 @@ module Paseto
58
58
  Util.pre_auth_encode(pae_header, message, footer, implicit_assertion)
59
59
  .then { |m2| @key.sign(m2) }
60
60
  .then { |sig| "#{message}#{sig}" }
61
- .then { |payload| Token.new(payload: payload, purpose: purpose, version: version, footer: footer) }
61
+ .then { |payload| Token.new(payload:, purpose:, version:, footer:) }
62
62
  end
63
63
 
64
64
  # Verify the signature of `token`, with an optional binding `implicit_assertion`. `token` must be a `v4.public`` type Token.
@@ -152,10 +152,7 @@ module Paseto
152
152
  def ossl_ed25519_private_key?(key)
153
153
  raise LucidityError, "expected Ed25519 key, got #{key.oid}" unless key.oid == 'ED25519'
154
154
 
155
- return key.to_text.start_with?('ED25519 Private-Key') if Util.openssl?(3)
156
- return key.to_text != "<INVALID PRIVATE KEY>\n" if Util.openssl?(1, 1, 1)
157
-
158
- false
155
+ key.to_text.start_with?('ED25519 Private-Key')
159
156
  end
160
157
 
161
158
  sig(:final) { returns(RbNaCl::VerifyKey) }
@@ -29,7 +29,7 @@ module Paseto
29
29
  return unless (aud = options[:verify_aud])
30
30
 
31
31
  given = payload['aud']
32
- raise InvalidAudience, "Invalid audience. Expected #{aud}, got #{given || '<none>'}" if ([*aud] & [*given]).empty?
32
+ raise InvalidAudience, "Invalid audience. Expected #{aud}, got #{given || '<none>'}" unless [*aud].intersect?([*given])
33
33
  end
34
34
  end
35
35
 
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Paseto
5
- VERSION = '0.1.2'
5
+ VERSION = '0.2.0'
6
6
  end
@@ -17,8 +17,8 @@ module Paseto
17
17
  sig { returns(Interface::Version) }
18
18
  def instance
19
19
  case self
20
- when V3Version, V3Str, K3Str then Protocol::Version3.new
21
- when V4Version, V4Str, K4Str then Protocol::Version4.new
20
+ when V3Version, V3Str, K3Str then Protocol::Version3.instance
21
+ when V4Version, V4Str, K4Str then Protocol::Version4.instance
22
22
  end
23
23
  end
24
24
  end
@@ -13,33 +13,20 @@ module Paseto
13
13
  DOMAIN_SEPARATOR_AUTH = "\x81"
14
14
  DOMAIN_SEPARATOR_ENCRYPT = "\x80"
15
15
 
16
- sig { override.params(data: String).returns({ t: String, n: String, c: String }) }
17
- def self.decode_and_split(data)
18
- b = Util.decode64(data)
19
- {
20
- t: T.must(b.byteslice(0, 48)),
21
- n: T.must(b.byteslice(48, 32)),
22
- c: T.must(b.byteslice(80..))
23
- }
24
- end
16
+ sig { override.returns(String) }
17
+ attr_reader :local_header
25
18
 
26
19
  sig { override.returns(Protocol::Version3) }
27
- def self.protocol
28
- Protocol::Version3.new
29
- end
20
+ attr_reader :protocol
30
21
 
31
22
  sig { override.returns(String) }
32
- def local_header
33
- 'k3.local-wrap.pie.'
34
- end
35
-
36
- sig { override.returns(String) }
37
- def secret_header
38
- 'k3.secret-wrap.pie.'
39
- end
23
+ attr_reader :secret_header
40
24
 
41
25
  sig { params(wrapping_key: SymmetricKey).void }
42
26
  def initialize(wrapping_key)
27
+ @local_header = T.let('k3.local-wrap.pie.', String)
28
+ @protocol = T.let(Protocol::Version3.instance, Protocol::Version3)
29
+ @secret_header = T.let('k3.secret-wrap.pie.', String)
43
30
  @wrapping_key = wrapping_key
44
31
  end
45
32
 
@@ -53,6 +40,16 @@ module Paseto
53
40
  protocol.hmac(payload, key: auth_key)
54
41
  end
55
42
 
43
+ sig { override.params(data: String).returns({ t: String, n: String, c: String }) }
44
+ def decode_and_split(data)
45
+ b = Util.decode64(data)
46
+ {
47
+ t: T.must(b.byteslice(0, 48)),
48
+ n: T.must(b.byteslice(48, 32)),
49
+ c: T.must(b.byteslice(80..))
50
+ }
51
+ end
52
+
56
53
  sig { override.returns(String) }
57
54
  def random_nonce
58
55
  protocol.random(32)
@@ -64,7 +61,7 @@ module Paseto
64
61
  ek = T.must(x[0, 32])
65
62
  n2 = T.must(x[32..])
66
63
 
67
- protocol.crypt(key: ek, nonce: n2, payload: payload)
64
+ protocol.crypt(key: ek, nonce: n2, payload:)
68
65
  end
69
66
  end
70
67
  end
@@ -13,33 +13,20 @@ module Paseto
13
13
  DOMAIN_SEPARATOR_AUTH = "\x81"
14
14
  DOMAIN_SEPARATOR_ENCRYPT = "\x80"
15
15
 
16
- sig { override.params(data: String).returns({ t: String, n: String, c: String }) }
17
- def self.decode_and_split(data)
18
- b = Util.decode64(data)
19
- {
20
- t: T.must(b.byteslice(0, 32)),
21
- n: T.must(b.byteslice(32, 32)),
22
- c: T.must(b.byteslice(64..))
23
- }
24
- end
25
-
26
16
  sig { override.returns(Protocol::Version4) }
27
- def self.protocol
28
- Protocol::Version4.new
29
- end
17
+ attr_reader :protocol
30
18
 
31
19
  sig { override.returns(String) }
32
- def local_header
33
- 'k4.local-wrap.pie.'
34
- end
20
+ attr_reader :local_header
35
21
 
36
22
  sig { override.returns(String) }
37
- def secret_header
38
- 'k4.secret-wrap.pie.'
39
- end
23
+ attr_reader :secret_header
40
24
 
41
25
  sig { params(wrapping_key: SymmetricKey).void }
42
26
  def initialize(wrapping_key)
27
+ @local_header = T.let('k4.local-wrap.pie.', String)
28
+ @protocol = T.let(Protocol::Version4.instance, Protocol::Version4)
29
+ @secret_header = T.let('k4.secret-wrap.pie.', String)
43
30
  @wrapping_key = wrapping_key
44
31
  end
45
32
 
@@ -53,6 +40,16 @@ module Paseto
53
40
  protocol.hmac(payload, key: auth_key, digest_size: 32)
54
41
  end
55
42
 
43
+ sig { override.params(data: String).returns({ t: String, n: String, c: String }) }
44
+ def decode_and_split(data)
45
+ b = Util.decode64(data)
46
+ {
47
+ t: T.must(b.byteslice(0, 32)),
48
+ n: T.must(b.byteslice(32, 32)),
49
+ c: T.must(b.byteslice(64..))
50
+ }
51
+ end
52
+
56
53
  sig { override.returns(String) }
57
54
  def random_nonce
58
55
  protocol.random(32)
@@ -64,7 +61,7 @@ module Paseto
64
61
  ek = T.must(x[0, 32])
65
62
  n2 = T.must(x[32..])
66
63
 
67
- protocol.crypt(key: ek, nonce: n2, payload: payload)
64
+ protocol.crypt(key: ek, nonce: n2, payload:)
68
65
  end
69
66
  end
70
67
  end
@@ -20,11 +20,11 @@ module Paseto
20
20
  raise LucidityError unless key.version == @wrapping_key.version
21
21
 
22
22
  nonce ||= @coder.random_nonce
23
- header = pie_header(key)
23
+ header = key.pie_header
24
24
 
25
- c = @coder.crypt(nonce: nonce, payload: key.to_bytes)
25
+ c = @coder.crypt(nonce:, payload: key.to_bytes)
26
26
 
27
- ak = @coder.authentication_key(nonce: nonce)
27
+ ak = @coder.authentication_key(nonce:)
28
28
  t = @coder.authentication_tag(payload: "#{header}#{nonce}#{c}", auth_key: ak)
29
29
 
30
30
  [header, Util.encode64("#{t}#{nonce}#{c}")].join
@@ -52,20 +52,6 @@ module Paseto
52
52
 
53
53
  PaserkTypes.deserialize("#{version}.#{type}").generate(ptk)
54
54
  end
55
-
56
- private
57
-
58
- sig { params(key: Interface::Key).returns(String) }
59
- def pie_header(key)
60
- case key
61
- when SymmetricKey then @coder.local_header
62
- when AsymmetricKey then @coder.secret_header
63
- else
64
- # :nocov:
65
- raise ArgumentError, 'not a valid type of key'
66
- # :nocov:
67
- end
68
- end
69
55
  end
70
56
  end
71
57
  end
data/lib/paseto.rb CHANGED
@@ -42,6 +42,8 @@ module Paseto
42
42
  extend T::Sig
43
43
  extend Configuration
44
44
 
45
+ HAS_RBNACL = T.let(!defined?(RbNaCl).nil?, T::Boolean)
46
+
45
47
  class Error < StandardError; end
46
48
 
47
49
  class AlgorithmError < Error; end
@@ -89,11 +91,6 @@ module Paseto
89
91
  class ParseError < Error; end
90
92
  # Tried to work with a V4 token without RbNaCl loaded
91
93
  class UnsupportedToken < ParseError; end
92
-
93
- sig { returns(T::Boolean) }
94
- def self.rbnacl?
95
- !!defined?(RbNaCl)
96
- end
97
94
  end
98
95
 
99
96
  loader.eager_load
data/paseto.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  DESCRIPTION
18
18
  spec.homepage = 'https://github.com/bannable/paseto'
19
19
  spec.license = 'MIT'
20
- spec.required_ruby_version = '>= 3.0.0'
20
+ spec.required_ruby_version = '>= 3.1.0'
21
21
 
22
22
  spec.metadata = {
23
23
  'bug_tracker_uri' => 'https://github.com/bannable/paseto/issues',
@@ -37,10 +37,10 @@ Gem::Specification.new do |spec|
37
37
  end
38
38
  spec.require_paths = ['lib']
39
39
 
40
- spec.add_runtime_dependency 'multi_json', '~> 1.15.0'
41
- spec.add_runtime_dependency 'openssl', '~> 3', '>= 3.0.2'
42
- spec.add_runtime_dependency 'sorbet-runtime'
43
- spec.add_runtime_dependency 'zeitwerk'
40
+ spec.add_dependency 'multi_json', '~> 1.15.0'
41
+ spec.add_dependency 'openssl', '~> 3.3'
42
+ spec.add_dependency 'sorbet-runtime'
43
+ spec.add_dependency 'zeitwerk'
44
44
 
45
45
  spec.metadata['rubygems_mfa_required'] = 'true'
46
46
  end
@@ -0,0 +1 @@
1
+ **/*.rbi linguist-vendored=true
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: true
2
2
 
3
3
  # DO NOT EDIT MANUALLY
4
4
  # This file was pulled from a central RBI files repository.
@@ -60,14 +60,14 @@ module Rainbow
60
60
  class X11Named < Rainbow::Color::RGB
61
61
  include Rainbow::X11ColorNames
62
62
 
63
+ sig { params(ground: Symbol, name: Symbol).void }
64
+ def initialize(ground, name); end
65
+
63
66
  sig { returns(T::Array[Symbol]) }
64
67
  def self.color_names; end
65
68
 
66
69
  sig { returns(String) }
67
70
  def self.valid_names; end
68
-
69
- sig { params(ground: Symbol, name: Symbol).void }
70
- def initialize(ground, name); end
71
71
  end
72
72
  end
73
73
 
@@ -0,0 +1 @@
1
+ **/*.rbi linguist-generated=true
@@ -522,7 +522,7 @@ Diff::LCS::Change::VALID_ACTIONS = T.let(T.unsafe(nil), Array)
522
522
  # elements in the old and the new sequenced enumerables as well as the action
523
523
  # taken.
524
524
  #
525
- # source://diff-lcs//lib/diff/lcs/change.rb#101
525
+ # source://diff-lcs//lib/diff/lcs/change.rb#100
526
526
  class Diff::LCS::ContextChange < ::Diff::LCS::Change
527
527
  # @return [ContextChange] a new instance of ContextChange
528
528
  #
@@ -253,7 +253,7 @@ Docile::BacktraceFilter::FILTER_PATTERN = T.let(T.unsafe(nil), Regexp)
253
253
  # @api private
254
254
  # @see Docile.dsl_eval_immutable
255
255
  #
256
- # source://docile//lib/docile/chaining_fallback_context_proxy.rb#19
256
+ # source://docile//lib/docile/chaining_fallback_context_proxy.rb#17
257
257
  class Docile::ChainingFallbackContextProxy < ::Docile::FallbackContextProxy
258
258
  # Proxy methods as in {FallbackContextProxy#method_missing}, replacing
259
259
  # `receiver` with the returned value.