Skip to content

Commit

Permalink
Bring back the custom Base64 deocde mechanisms
Browse files Browse the repository at this point in the history
  • Loading branch information
anakinj authored and excpt committed Jun 7, 2022
1 parent d783720 commit 1cc5e8e
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 17 deletions.
2 changes: 1 addition & 1 deletion lib/jwt.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require 'base64'
require 'jwt/base64'
require 'jwt/json'
require 'jwt/decode'
require 'jwt/default_options'
Expand Down
19 changes: 19 additions & 0 deletions lib/jwt/base64.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

require 'base64'

module JWT
# Base64 helpers
class Base64
class << self
def url_encode(str)
::Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '')
end

def url_decode(str)
str += '=' * (4 - str.length.modulo(4))
::Base64.decode64(str.tr('-_', '+/'))
end
end
end
end
8 changes: 3 additions & 5 deletions lib/jwt/decode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,7 @@ def none_algorithm?
end

def decode_crypto
@signature = Base64.urlsafe_decode64(@segments[2] || '')
rescue ArgumentError
raise(JWT::DecodeError, 'Invalid segment encoding')
@signature = ::JWT::Base64.url_decode(@segments[2] || '')
end

def algorithm
Expand All @@ -139,8 +137,8 @@ def signing_input
end

def parse_and_decode(segment)
JWT::JSON.parse(Base64.urlsafe_decode64(segment))
rescue ::JSON::ParserError, ArgumentError
JWT::JSON.parse(::JWT::Base64.url_decode(segment))
rescue ::JSON::ParserError
raise JWT::DecodeError, 'Invalid segment encoding'
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/jwt/encode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ def encode_payload
def encode_signature
return '' if @algorithm == ALG_NONE

Base64.urlsafe_encode64(JWT::Signature.sign(@algorithm, encoded_header_and_payload, @key), padding: false)
::JWT::Base64.url_encode(JWT::Signature.sign(@algorithm, encoded_header_and_payload, @key))
end

def encode(data)
Base64.urlsafe_encode64(JWT::JSON.generate(data), padding: false)
::JWT::Base64.url_encode(JWT::JSON.generate(data))
end

def combine(*parts)
Expand Down
8 changes: 4 additions & 4 deletions lib/jwt/jwk/ec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ def keypair_components(ec_keypair)
end

def encode_octets(octets)
Base64.urlsafe_encode64(octets, padding: false)
::JWT::Base64.url_encode(octets)
end

def encode_open_ssl_bn(key_part)
Base64.urlsafe_encode64(key_part.to_s(BINARY), padding: false)
::JWT::Base64.url_encode(key_part.to_s(BINARY))
end

class << self
Expand Down Expand Up @@ -142,11 +142,11 @@ def ec_pkey(jwk_crv, jwk_x, jwk_y, jwk_d)
end

def decode_octets(jwk_data)
Base64.urlsafe_decode64(jwk_data)
::JWT::Base64.url_decode(jwk_data)
end

def decode_open_ssl_bn(jwk_data)
OpenSSL::BN.new(Base64.urlsafe_decode64(jwk_data), BINARY)
OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/jwt/jwk/rsa.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def append_private_parts(the_hash)
end

def encode_open_ssl_bn(key_part)
Base64.urlsafe_encode64(key_part.to_s(BINARY), padding: false)
::JWT::Base64.url_encode(key_part.to_s(BINARY))
end

class << self
Expand Down Expand Up @@ -108,7 +108,7 @@ def populate_key(rsa_key, rsa_parameters)
def decode_open_ssl_bn(jwk_data)
return nil unless jwk_data

OpenSSL::BN.new(Base64.urlsafe_decode64(jwk_data), BINARY)
OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/jwt/x5c_key_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def parse_certificates(x5c_header_or_certificates)
x5c_header_or_certificates
else
x5c_header_or_certificates.map do |encoded|
OpenSSL::X509::Certificate.new(::Base64.strict_decode64(encoded))
OpenSSL::X509::Certificate.new(::JWT::Base64.url_decode(encoded))
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions spec/jwt_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@
translated_alg = alg.gsub('PS', 'sha')
valid_signature = data[:rsa_public].verify_pss(
translated_alg,
Base64.urlsafe_decode64(signature),
::JWT::Base64.url_decode(signature),
[header, body].join('.'),
salt_length: :auto,
mgf1_hash: translated_alg
Expand Down Expand Up @@ -611,7 +611,7 @@

context 'when the alg value is given as a header parameter' do
it 'does not override the actual algorithm used' do
headers = JSON.parse(Base64.urlsafe_decode64(JWT.encode('Hello World', 'secret', 'HS256', { alg: 'HS123' }).split('.').first))
headers = JSON.parse(::JWT::Base64.url_decode(JWT.encode('Hello World', 'secret', 'HS256', { alg: 'HS123' }).split('.').first))
expect(headers['alg']).to eq('HS256')
end

Expand Down

7 comments on commit 1cc5e8e

@ebarendt
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@excpt would you consider releasing this as 2.4.2? Removing JWT::Base64 was a breaking change for many of our apps.

@excpt
Copy link
Member

@excpt excpt commented on 1cc5e8e Jun 8, 2022 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anakinj
Copy link
Member Author

@anakinj anakinj commented on 1cc5e8e Jun 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ebarendt Just out of curiosity. Are you using these "internal" modules/classes in your app or are you referring that the more strict Base64 decoding is breaking your app?

@ebarendt
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have code that calls ::JWT::Base64.url_decode. My team inherited this, and I haven't yet investigated to see if Ruby's Base64 would work instead.

@anakinj
Copy link
Member Author

@anakinj anakinj commented on 1cc5e8e Jun 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend using the ::Base64.urlsafe_decode64 and ::Base64.urlsafe_encode64 methods for your Base64 needs.

The jwt gem unfortunately is and has to use the more loose base64 decoding that will just ignore non-alphabetic characters instead of consider the base64 payload as invalid. Think this mostly happens if the data has been manipulated by a human leaving blanks and newlines behind.

@ebarendt
Copy link

@ebarendt ebarendt commented on 1cc5e8e Jun 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I'm digging into our gem and making the change.

@ebarendt
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're all set, thanks for quick responses.

Please sign in to comment.