diff --git a/NEWS b/NEWS index 99be0f9..2b05008 100644 --- a/NEWS +++ b/NEWS @@ -35,6 +35,12 @@ with all sufficient information, see the ChangeLog file. * lib/drb/drb.rb * removed unused argument. https://github.com/ruby/ruby/pull/515 +* lib/base64.rb + * Base64.urlsafe_encode64: added a "padding" option to suppress + the padding character ("="). + * Base64.urlsafe_decode64: now it accepts not only correctly-padded + input but also unpadded input. + === Built-in global variables compatibility issues === C API updates diff --git a/lib/base64.rb b/lib/base64.rb index 98829f0..45e8516 100644 --- a/lib/base64.rb +++ b/lib/base64.rb @@ -77,8 +77,12 @@ module Base64 # This method complies with ``Base 64 Encoding with URL and Filename Safe # Alphabet'' in RFC 4648. # The alphabet uses '-' instead of '+' and '_' instead of '/'. - def urlsafe_encode64(bin) - strict_encode64(bin).tr("+/", "-_") + # Note that the result can still contain '='. + # You can remove the padding by setting "padding" as false. + def urlsafe_encode64(bin, padding: true) + str = strict_encode64(bin).tr("+/", "-_") + str = str.delete("=") unless padding + str end # Returns the Base64-decoded version of +str+. @@ -86,6 +90,14 @@ module Base64 # Alphabet'' in RFC 4648. # The alphabet uses '-' instead of '+' and '_' instead of '/'. def urlsafe_decode64(str) - strict_decode64(str.tr("-_", "+/")) + # + # The padding character is optional. + # This method accepts both correctly-padded and unpadded input. + # Note that it still rejects incorrectly-padded input. + str = str.tr("-_", "+/") + if !str.end_with?("=") && str.length % 4 != 0 + str = str.ljust((str.length + 3) & ~3, "=") + end + strict_decode64(str) end end diff --git a/test/base64/test_base64.rb b/test/base64/test_base64.rb index c5e61b3..c2cd60a 100644 --- a/test/base64/test_base64.rb +++ b/test/base64/test_base64.rb @@ -87,6 +87,13 @@ class TestBase64 < Test::Unit::TestCase assert_equal("_-8=", Base64.urlsafe_encode64("\xff\xef")) end + def test_urlsafe_encode64_unpadded + assert_equal("", Base64.urlsafe_encode64("", padding: false)) + assert_equal("AA", Base64.urlsafe_encode64("\0", padding: false)) + assert_equal("AAA", Base64.urlsafe_encode64("\0\0", padding: false)) + assert_equal("AAAA", Base64.urlsafe_encode64("\0\0\0", padding: false)) + end + def test_urlsafe_decode64 assert_equal("", Base64.urlsafe_decode64("")) assert_equal("\0", Base64.urlsafe_decode64("AA==")) @@ -97,4 +104,11 @@ class TestBase64 < Test::Unit::TestCase assert_equal("\377\377\377", Base64.urlsafe_decode64("____")) assert_equal("\xff\xef", Base64.urlsafe_decode64("_+8=")) end + + def test_urlsafe_decode64_unpadded + assert_equal("\0", Base64.urlsafe_decode64("AA")) + assert_equal("\0\0", Base64.urlsafe_decode64("AAA")) + assert_equal("\0\0\0", Base64.urlsafe_decode64("AAAA")) + assert_raise(ArgumentError) { Base64.urlsafe_decode64("AA=") } + end end