Skip to content

Commit

Permalink
Add support for local-pw and secret-pw
Browse files Browse the repository at this point in the history
- Versions have been refactored to be classes instead of modules, and
  things that are versioned are expected to respond to a .protocol method
  that returns an instance of a Interface::Version class. An instance of
  such a class is comparable with other instances of the same, and also
  with any string that matches the associated version, e.g. "v3".

- Enabled sorbet final checks and added final sigs to several classes

- IncorrectKeyType is gone in favor of LucidityError. A LucidityError is
  any violation of Algorithm Lucidity properties.

- Replaced most string concatenation operations with interpolation for
  improved performance

- Added local-pw and secret-pw support for both v3 and v4

- Made PIE version detection more robust against absense of RbNaCl

- PIE::Version3 -> PIE::PieV3, PIE::Version4 -> PIE::PieV4

- Util.secure_compare now uses libsodium primitives when available for
  significantly improved performance.

- Fixed pattern matching order in Token parsing to have the more specific
  case first

I'm not entirely happy with the interface for accessing PBKW at this point,
because it requires explicitly initializing with a version, but it works
well enough.

The tests for PBKW v4 are SLOW as hell due to the test vectors including
parameters with very high memlimit and opslimit values. I'm leaving them
enabled for now, but if it eats up too much CI time, they'll be changed
to run only on demand.

The test vectors have been modified as in ef77549
due to more buggy encoding. See: paseto-standard/paserk#18
  • Loading branch information
bannable committed Nov 29, 2022
1 parent 9bdc4ac commit 5c2543a
Show file tree
Hide file tree
Showing 52 changed files with 1,451 additions and 144 deletions.
48 changes: 24 additions & 24 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@ AllCops:
Layout/LineLength:
Max: 140

Style/Documentation:
Enabled: false

Metrics/MethodLength:
Max: 15
Exclude:
- 'spec/generate_vectors.rb'

Layout/ClosingHeredocIndentation:
Exclude:
- 'spec/generate_vectors.rb'
Expand All @@ -33,41 +25,49 @@ Layout/HeredocIndentation:
Exclude:
- 'spec/generate_vectors.rb'

Metrics/AbcSize:
Max: 20
Exclude:
- 'spec/generate_vectors.rb'
Lint/MissingSuper:
Enabled: false

Metrics/ParameterLists:
Metrics/MethodLength:
Max: 15
Exclude:
- 'spec/generate_vectors.rb'

Style/MutableConstant:
Metrics/AbcSize:
Max: 20
Exclude:
- 'spec/generate_vectors.rb'

Metrics/BlockLength:
Exclude:
- 'spec/**/*'

Style/AccessorGrouping:
Enabled: false
Metrics/ParameterLists:
Exclude:
- 'spec/generate_vectors.rb'

Sorbet/ConstantsFromStrings:
Naming/MethodParameterName:
Exclude:
- 'lib/paseto/sodium/stream/base.rb'
- 'lib/paseto/asn1'

Naming/VariableNumber:
CheckSymbols: false

RSpec/MultipleMemoizedHelpers:
Exclude:
- 'spec/paseto/token_validator_spec.rb'
- 'spec/paseto/verify_spec.rb'

Naming/VariableNumber:
CheckSymbols: false

Naming/MethodParameterName:
Sorbet/ConstantsFromStrings:
Exclude:
- 'lib/paseto/asn1'
- 'lib/paseto/sodium/stream/base.rb'

Lint/MissingSuper:
Style/AccessorGrouping:
Enabled: false

Style/Documentation:
Enabled: false

Style/MutableConstant:
Exclude:
- 'spec/generate_vectors.rb'
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ And run `bundle install`
| `seal` |||
| `local-wrap` |||
| `secret-wrap` |||
| `local-pw` | | |
| `secret-pw` | | |
| `local-pw` | | |
| `secret-pw` | | |

## Implementation Guideline compliance

Expand Down
12 changes: 9 additions & 3 deletions lib/paseto.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
end
require 'securerandom'
require 'sorbet-runtime'
T::Configuration.enable_final_checks_on_hooks
require 'time'
require 'zeitwerk'

Expand All @@ -29,7 +30,9 @@
'ecdsa_sig_value' => 'ECDSASigValue',
'ecdsa_signature' => 'ECDSASignature',
'ecdsa_full_r' => 'ECDSAFullR',
'pie' => 'PIE'
'pie' => 'PIE',
'pbkw' => 'PBKW',
'pbkd' => 'PBKD'
)
loader.setup

Expand All @@ -39,6 +42,11 @@ module Paseto

class Error < StandardError; end

class AlgorithmError < Error; end
# An algorithm assertion was violated, such as attempting to wrap a
# v4 key with a v3 key, or providing an EC key other than secp384 to v3.
class LucidityError < AlgorithmError; end

# A cryptographic primitive has failed for any reason,
# such as attempting to initialize a stream cipher with
# an invalid nonce.
Expand All @@ -47,8 +55,6 @@ class CryptoError < Error; end
class InvalidAuthenticator < CryptoError; end
# A signature was forged or otherwise corrupt
class InvalidSignature < CryptoError; end
# A provided key parsed to a different algorithm than expected
class IncorrectKeyType < CryptoError; end
# Key is not valid for algorithm
class InvalidKeyPair < CryptoError; end

Expand Down
2 changes: 1 addition & 1 deletion lib/paseto/asn1/ecdsa_signature.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def to_rs(part_len)
# :nocov:
end
s = signature.s.to_s(2).rjust(part_len, "\x00")
r + s
[r, s].join
end
end
end
Expand Down
38 changes: 38 additions & 0 deletions lib/paseto/interface/pbkd.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# typed: strict
# frozen_string_literal: true

module Paseto
module Interface
module PBKD
extend T::Sig
extend T::Helpers

abstract!

sig { abstract.returns(Interface::Version) }
def protocol; end

sig { abstract.returns(String) }
def local_header; end

sig { abstract.returns(String) }
def secret_header; end

sig { abstract.params(key: Key, options: T::Hash[T.untyped, T.untyped]).returns(String) }
def wrap(key, options); end

sig { abstract.params(header: String, data: String).returns(Key) }
def unwrap(header, data); end

sig(:final) { returns(String) }
def version
protocol.version
end

sig(:final) { returns(String) }
def paserk_version
protocol.paserk_version
end
end
end
end
6 changes: 6 additions & 0 deletions lib/paseto/interface/pie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ def random_nonce; end

sig { abstract.params(data: String).returns({ t: String, n: String, c: String }) }
def decode_and_split(data); end

sig { abstract.returns(String) }
def local_header; end

sig { abstract.returns(String) }
def secret_header; end
end
end
end
16 changes: 14 additions & 2 deletions lib/paseto/interface/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,27 @@ module Version
extend T::Sig
extend T::Helpers

requires_ancestor { Paseto::Key }
include Comparable

interface!
abstract!

sig { abstract.returns(String) }
def version; end

sig { abstract.returns(String) }
def paserk_version; end

sig(:final) { params(other: T.untyped).returns(T.nilable(Integer)) }
def <=>(other)
case other
in Interface::Version
version <=> other.version
in String
version <=> other
else
nil
end
end
end
end
end
29 changes: 20 additions & 9 deletions lib/paseto/key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,38 @@ class Key
extend T::Sig
extend T::Helpers

include Interface::Version

DOMAIN_SEPARATOR_AUTH = "\x81"
DOMAIN_SEPARATOR_ENCRYPT = "\x80"

abstract!

sig { returns(String) }
attr_reader :purpose
sig { abstract.returns(String) }
def purpose; end

sig { returns(String) }
sig { abstract.returns(String) }
def to_bytes; end

sig { abstract.returns(Interface::Version) }
def protocol; end

sig(:final) { returns(String) }
def version
protocol.version
end

sig(:final) { returns(String) }
def paserk_version
protocol.paserk_version
end

sig(:final) { returns(String) }
def header
"#{version}.#{purpose}"
end

sig { returns(String) }
sig(:final) { returns(String) }
def pae_header
"#{header}."
end

sig { abstract.returns(String) }
def to_bytes; end
end
end
Loading

0 comments on commit 5c2543a

Please sign in to comment.