dankogai / js-base64

Base64 implementation for JavaScript
BSD 3-Clause "New" or "Revised" License
4.27k stars 1.33k forks source link

Error: Not a valid base64 encoded string length - even though Base64 string seems to be valid #175

Closed PiotrSzlagura closed 5 months ago

PiotrSzlagura commented 5 months ago

Hi!

I have a very strange problem - after upgrading my ap to Expo@51 my Base64 functionality got broken for some reason.

I use Base64 for easier exchange of objects containing encrypted data. All of my entries are stored on server and got encrypted using js-base64@3.7.7 in front-end (the same on which now there is an error).

Despite that, when trying to decode following Base64 string:

eyJ2IjoiaHlicmlkLWNyeXB0by1qc18wLjIuNCIsIml2IjoiOEUwQmRvcHkzWWVwQ1UvS3lwa0tySFNxd2hyYmtadDJVTU5qZXlBRDFNWT0iLCJrZXlzIjp7IjY5OmViOmVkOjkwOjZkOmRjOmUxOmQ0OmE0OjhiOjVlOjc4OmIzOjFlOjdmOmUyOjkzOjNmOmE1OjMwIjoiYUlUZUFpOTJZSnZSN3g1bXNaTGVldzVuN1h2RVA1Mjh4aE9RTitybXFpUXl6aURaMWx3OUdrUmZtSDRCMlVQcDFoYVU0ejJsZ2tZclZ0aWEvUk04MnNhNlVRYTV5RzN6N05jbU0vTDVmaW1KNDNFTUhrZGVmRzhGRTdpZXAzWjA1eFIxNUdFMkhXMDFNZ0FhUUtpbUhuc291ZEZ6N1l3UG8vN2srNm1pVHBVPSJ9LCJjaXBoZXIiOiJCaldxTi9xcnVGemFvTEpSVGNKWG9TVGx2elRIUGJvNGluMURYa1R5V09OSUhkRWhnaHJJd082YS9YT2xuaHZZIn0=

I get an exception which I have never seen in my life:

Error: Not a valid base64 encoded string length

What is weirder, when I use https://www.base64decode.org/ or browser's atob there is no problem with decrypting this string.

Can anyone point me to a solution to this problem?

dankogai commented 5 months ago

I could not reproduce your claim. Here is what I got on node 20.

% node
Welcome to Node.js v20.12.2.
Type ".help" for more information.
> var { Base64 } = await import("js-base64");
undefined
> var s = 'eyJ2IjoiaHlicmlkLWNyeXB0by1qc18wLjIuNCIsIml2IjoiOEUwQmRvcHkzWWVwQ1UvS3lwa0tySFNxd2hyYmt0adDJVTU5qZXlBRDFNWT0iLCJrZXlzIjp7IjY5OmViOmVkOjkwOjZkOmRjOmUxOmQ0OmE0OjhiOjVlOjc4OmIzOjFlOjdmOmUyOjkzOjNmOmE1OjMwIjoiYUlUZUFpOTJZSnZSN3g1bXNaTGVldzVuN1h2RVA1Mjh4aE9RTitybXFpUXl6aURaMWx3OUdrUmZtSDRCMlVQcDFoYVU0ejJsZ2tZclZ0aWEvUk04MnNhNlVRYTV5RzN6N05jbU0vTDVmaW1KNDNFTUhrZGVmRzhGRTdpZXAzWjA1eFIxNUdFMkhXMDFNZ0FhUUtpbUhuc291ZEZ6N1l3UG8vN2srNm1pVHBVPSJ9LCJjaXBoZXIiOiJCaldxTi9xcnVGemFvTEpSVGNKWG9TVGx2elRIUGJvNGluMURYa1R5V09OSUhkRWhnaHJJd082YS9YT2xuaHZZIn0='
undefined
> Base64.decode(s)
'{"v":"hybrid-crypto-js_0.2.4","iv":"8E0Bdopy3YepCU/KypkKrHSqwhrbkZt2UMNjeyAD1MY=","keys":{"69:eb:ed:90:6d:dc:e1:d4:a4:8b:5e:78:b3:1e:7f:e2:93:3f:a5:30":"aITeAi92YJvR7x5msZLeew5n7XvEP528xhOQN+rmqiQyziDZ1lw9GkRfmH4B2UPp1haU4z2lgkYrVtia/RM82sa6UQa5yG3z7NcmM/L5fimJ43EMHkdefG8FE7iep3Z05xR15GE2HW01MgAaQKimHnsoudFz7YwPo/7k+6miTpU="},"cipher":"BjWqN/qruFzaoLJRTcJXoSTlvzTHPbo4in1DXkTyWONIHdEhghrIwO6a/XOlnhvY"}'
>  Base64.encode(Base64.decode(s)) == s
true

And I got the same result on browsers.

PiotrSzlagura commented 5 months ago

That's exactly why I created this issue. In my code when I do const a = Base64.encode(...) and then Base64.decode(a) it works perfectly, and as I mentioned using some online decoders or browser's atob it also works.

I just wanted to know if you can think of any reason why such issue might occur? Maybe I should somehow enforce given encoding of input string?

I don't really understand this error message - I've checked that this string is 540 characters long (taken from this example and inside my code) which seems correct

dankogai commented 5 months ago

for one thing Base64.decode() does not thrown an exception even when the input Base64 is invalid. It just returns invalid data since any binary data can be encoded to Base64. There is indeed Base64.isValid() that checks if the string is in correct Base64 format but as I say Base64.decode() does accept invalid strings. So I can conclude there is something wrong in YOUR environment, not this module.

PiotrSzlagura commented 5 months ago

That's all I needed to know, thank you!

PiotrSzlagura commented 5 months ago

Just to follow up for anyone having the same issue as me - I still didn't resolve the root cause of the problem, but I'm quite sure it's caused by react native Buffer implementation. The issue appeared after upgrading from react-native@0.73.6 to 0.74.1.

Base64.decode under the hood does Buffer.toString() (as far as I can tell by examinig the code), which is why I suspect Buffer to be an issue, and also I can't really change it in any way.

I ended up using decoding algorithm posted here: https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#Javascript_2 - although this might not be as fast as using Buffer, it just works :)

dankogai commented 5 months ago

Thank you for your report. If so the quick and dirty patch below should solve the problem. Would you try and see what happens?

% git diff
diff --git a/base64.js b/base64.js
index dc837a7..04ea64d 100644
--- a/base64.js
+++ b/base64.js
@@ -42,7 +42,7 @@
      * @deprecated use lowercase `version`.
      */
     var VERSION = version;
-    var _hasBuffer = typeof Buffer === 'function';
+    var _hasBuffer = false; // typeof Buffer === 'function';
     var _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
     var _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
     var b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
diff --git a/base64.mjs b/base64.mjs
index fe9cfa5..a4bf113 100644
--- a/base64.mjs
+++ b/base64.mjs
@@ -14,7 +14,7 @@ const version = '3.7.7';
  * @deprecated use lowercase `version`.
  */
 const VERSION = version;
-const _hasBuffer = typeof Buffer === 'function';
+const _hasBuffer = false; // typeof Buffer === 'function';
 const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
 const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
 const b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
diff --git a/base64.ts b/base64.ts
index 845d1ee..797927a 100644
--- a/base64.ts
+++ b/base64.ts
@@ -14,7 +14,7 @@ const version = '3.7.7';
  * @deprecated use lowercase `version`.
  */
 const VERSION = version;
-const _hasBuffer = typeof Buffer === 'function';
+const _hasBuffer = false; // typeof Buffer === 'function';
 const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
 const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
 const b64ch =
PiotrSzlagura commented 5 months ago

I will try it today around 6-7PM (CET) and let you know

PiotrSzlagura commented 5 months ago

After changing code in my project and using patch-package, I ended up with following patch:

diff --git a/node_modules/js-base64/base64.js b/node_modules/js-base64/base64.js
index dc837a7..04ea64d 100644
--- a/node_modules/js-base64/base64.js
+++ b/node_modules/js-base64/base64.js
@@ -42,7 +42,7 @@
      * @deprecated use lowercase `version`.
      */
     var VERSION = version;
-    var _hasBuffer = typeof Buffer === 'function';
+    var _hasBuffer = false; // typeof Buffer === 'function';
     var _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
     var _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
     var b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
diff --git a/node_modules/js-base64/base64.mjs b/node_modules/js-base64/base64.mjs
index fe9cfa5..1b735c6 100644
--- a/node_modules/js-base64/base64.mjs
+++ b/node_modules/js-base64/base64.mjs
@@ -14,7 +14,7 @@ const version = '3.7.7';
  * @deprecated use lowercase `version`.
  */
 const VERSION = version;
-const _hasBuffer = typeof Buffer === 'function';
+const _hasBuffer = false; //typeof Buffer === 'function';
 const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
 const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
 const b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

Unfortunately this change didn't fix the issue. Maybe in this case the Buffer itself is not a problem?

mlazari commented 5 months ago

This is a bug with atob in the hermes engine used by react-native: https://github.com/reactwg/react-native-releases/issues/287

It seems to be fixed in react-native 0.74.2

PiotrSzlagura commented 5 months ago

@mlazari Thank you! I will check this out asap

mlazari commented 5 months ago

@PiotrSzlagura It's strange though, because the string you posted has a length that is divisible by 4 (540). As I understand the error was thrown for strings that have a length not divisible by 4 and the workaround in that link was to pad it with "=" at the end until its length is divisible by 4. Anyways, 0.74.2 fixed this for me.