From f22ba442a2b9e0ad16dec22afa39ab541f5b8a9b Mon Sep 17 00:00:00 2001 From: Ryan Schumacher Date: Tue, 17 May 2022 16:50:14 +0000 Subject: [PATCH] add: base64url support Using string replaceAll since base64 is external base64-js. Dependency does not yet support base64url beatgammit/base64-js#53 --- index.js | 38 +++++++++++++++++++++++++++++++------- test/base64.js | 18 ++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index bdea6048..df5c7cbe 100644 --- a/index.js +++ b/index.js @@ -396,6 +396,7 @@ Buffer.isEncoding = function isEncoding (encoding) { case 'ascii': case 'latin1': case 'binary': + case 'base64url': case 'base64': case 'ucs2': case 'ucs-2': @@ -544,8 +545,9 @@ function slowToString (encoding, start, end) { case 'binary': return latin1Slice(this, start, end) + case 'base64url': case 'base64': - return base64Slice(this, start, end) + return base64Slice(this, start, end, encoding) case 'ucs2': case 'ucs-2': @@ -871,8 +873,9 @@ function asciiWrite (buf, string, offset, length) { return blitBuffer(asciiToBytes(string), buf, offset, length) } -function base64Write (buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length) +function base64Write (buf, string, offset, length, encoding) { + const b64 = encoding === 'base64url' ? base64urlToBase64(string) : string + return blitBuffer(base64ToBytes(b64), buf, offset, length) } function ucs2Write (buf, string, offset, length) { @@ -930,9 +933,11 @@ Buffer.prototype.write = function write (string, offset, length, encoding) { case 'binary': return asciiWrite(this, string, offset, length) + case 'base64url': case 'base64': + // console.log(encoding, '::', string) // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length) + return base64Write(this, string, offset, length, encoding) case 'ucs2': case 'ucs-2': @@ -955,12 +960,14 @@ Buffer.prototype.toJSON = function toJSON () { } } -function base64Slice (buf, start, end) { +function base64Slice (buf, start, end, encoding) { + let b64 if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf) + b64 = base64.fromByteArray(buf) } else { - return base64.fromByteArray(buf.slice(start, end)) + b64 = base64.fromByteArray(buf.slice(start, end)) } + return encoding === 'base64url' ? base64urlFromBase64(b64) : b64 } function utf8Slice (buf, start, end) { @@ -1945,6 +1952,23 @@ function boundsError (value, length, type) { const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g +const BASE64_CHAR_62 = '+' +const BASE64_CHAR_63 = '/' +const BASE64URL_CHAR_62 = '-' +const BASE64URL_CHAR_63 = '_' + +function base64urlToBase64 (str) { + return str + .replaceAll(BASE64URL_CHAR_62, BASE64_CHAR_62) + .replaceAll(BASE64URL_CHAR_63, BASE64_CHAR_63) +} + +function base64urlFromBase64 (str) { + return str + .replaceAll(BASE64_CHAR_62, BASE64URL_CHAR_62) + .replaceAll(BASE64_CHAR_63, BASE64URL_CHAR_63) +} + function base64clean (str) { // Node takes equal signs as end of the Base64 encoding str = str.split('=')[0] diff --git a/test/base64.js b/test/base64.js index 977225b3..ebd2de0c 100644 --- a/test/base64.js +++ b/test/base64.js @@ -53,3 +53,21 @@ test('base64: high byte', function (t) { ) t.end() }) + +test('base64url: convert to/from base64', function (t) { + const base64url = '8J-Ps--4j_Cfj7PvuI8=' + const base64 = '8J+Ps++4j/Cfj7PvuI8=' + const text = '🏳️🏳️' + + const base64urlBuf = new B(base64url, 'base64url') + t.equal(base64urlBuf.toString('base64'), base64) + t.equal(base64urlBuf.toString(), text) + + const base64Buf = new B(base64, 'base64') + t.equal(base64Buf.toString('base64url'), base64url) + t.equal(base64Buf.toString(), text) + + const buf = new B(text) + t.equal(buf.toString('base64url'), base64url) + t.end() +})