mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
buffer: add base64url encoding option
PR-URL: https://github.com/nodejs/node/pull/36952 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
1 parent
9e7d1a1c24
commit
f8ab632d56
17 files changed
+306
-120
No files matched your search
+13
-3
@@ -50,6 +50,9 @@ const buf7 = Buffer.from('tést', 'latin1');
|
||||
## Buffers and character encodings
|
||||
<!-- YAML
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/36952
|
||||
description: Introduced `base64url` encoding.
|
||||
- version: v6.4.0
|
||||
pr-url: https://github.com/nodejs/node/pull/7111
|
||||
description: Introduced `latin1` as an alias for `binary`.
|
||||
@@ -106,6 +109,11 @@ string into a `Buffer` as decoding.
|
||||
specified in [RFC 4648, Section 5][]. Whitespace characters such as spaces,
|
||||
tabs, and new lines contained within the base64-encoded string are ignored.
|
||||
|
||||
* `'base64url'`: [base64url][] encoding as specified in
|
||||
[RFC 4648, Section 5][]. When creating a `Buffer` from a string, this
|
||||
encoding will also correctly accept regular base64-encoded strings. When
|
||||
encoding a `Buffer` to a string, this encoding will omit padding.
|
||||
|
||||
* `'hex'`: Encode each byte as two hexadecimal characters. Data truncation
|
||||
may occur when decoding strings that do exclusively contain valid hexadecimal
|
||||
characters. See below for an example.
|
||||
@@ -482,9 +490,10 @@ Returns the byte length of a string when encoded using `encoding`.
|
||||
This is not the same as [`String.prototype.length`][], which does not account
|
||||
for the encoding that is used to convert the string into bytes.
|
||||
|
||||
For `'base64'` and `'hex'`, this function assumes valid input. For strings that
|
||||
contain non-base64/hex-encoded data (e.g. whitespace), the return value might be
|
||||
greater than the length of a `Buffer` created from the string.
|
||||
For `'base64'`, `'base64url'`, and `'hex'`, this function assumes valid input.
|
||||
For strings that contain non-base64/hex-encoded data (e.g. whitespace), the
|
||||
return value might be greater than the length of a `Buffer` created from the
|
||||
string.
|
||||
|
||||
```js
|
||||
const str = '\u00bd + \u00bc = \u00be';
|
||||
@@ -3418,6 +3427,7 @@ introducing security vulnerabilities into an application.
|
||||
[`buffer.constants.MAX_STRING_LENGTH`]: #buffer_buffer_constants_max_string_length
|
||||
[`buffer.kMaxLength`]: #buffer_buffer_kmaxlength
|
||||
[`util.inspect()`]: util.md#util_util_inspect_object_options
|
||||
[base64url]: https://tools.ietf.org/html/rfc4648#section-5
|
||||
[binary strings]: https://developer.mozilla.org/en-US/docs/Web/API/DOMString/Binary
|
||||
[endianness]: https://en.wikipedia.org/wiki/Endianness
|
||||
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
|
||||
@@ -646,6 +646,20 @@ const encodingOps = {
|
||||
encodingsMap.base64,
|
||||
dir)
|
||||
},
|
||||
base64url: {
|
||||
encoding: 'base64url',
|
||||
encodingVal: encodingsMap.base64url,
|
||||
byteLength: (string) => base64ByteLength(string, string.length),
|
||||
write: (buf, string, offset, len) =>
|
||||
buf.base64urlWrite(string, offset, len),
|
||||
slice: (buf, start, end) => buf.base64urlSlice(start, end),
|
||||
indexOf: (buf, val, byteOffset, dir) =>
|
||||
indexOfBuffer(buf,
|
||||
fromStringFast(val, encodingOps.base64url),
|
||||
byteOffset,
|
||||
encodingsMap.base64url,
|
||||
dir)
|
||||
},
|
||||
hex: {
|
||||
encoding: 'hex',
|
||||
encodingVal: encodingsMap.hex,
|
||||
@@ -702,6 +716,11 @@ function getEncodingOps(encoding) {
|
||||
if (encoding === 'hex' || StringPrototypeToLowerCase(encoding) === 'hex')
|
||||
return encodingOps.hex;
|
||||
break;
|
||||
case 9:
|
||||
if (encoding === 'base64url' ||
|
||||
StringPrototypeToLowerCase(encoding) === 'base64url')
|
||||
return encodingOps.base64url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,14 @@ const { validateNumber } = require('internal/validators');
|
||||
const {
|
||||
asciiSlice,
|
||||
base64Slice,
|
||||
base64urlSlice,
|
||||
latin1Slice,
|
||||
hexSlice,
|
||||
ucs2Slice,
|
||||
utf8Slice,
|
||||
asciiWrite,
|
||||
base64Write,
|
||||
base64urlWrite,
|
||||
latin1Write,
|
||||
hexWrite,
|
||||
ucs2Write,
|
||||
@@ -1027,12 +1029,14 @@ function addBufferPrototypeMethods(proto) {
|
||||
|
||||
proto.asciiSlice = asciiSlice;
|
||||
proto.base64Slice = base64Slice;
|
||||
proto.base64urlSlice = base64urlSlice;
|
||||
proto.latin1Slice = latin1Slice;
|
||||
proto.hexSlice = hexSlice;
|
||||
proto.ucs2Slice = ucs2Slice;
|
||||
proto.utf8Slice = utf8Slice;
|
||||
proto.asciiWrite = asciiWrite;
|
||||
proto.base64Write = base64Write;
|
||||
proto.base64urlWrite = base64urlWrite;
|
||||
proto.latin1Write = latin1Write;
|
||||
proto.hexWrite = hexWrite;
|
||||
proto.ucs2Write = ucs2Write;
|
||||
|
||||
@@ -180,6 +180,11 @@ function slowCases(enc) {
|
||||
StringPrototypeToLowerCase(`${enc}`) === 'utf-16le')
|
||||
return 'utf16le';
|
||||
break;
|
||||
case 9:
|
||||
if (enc === 'base64url' || enc === 'BASE64URL' ||
|
||||
StringPrototypeToLowerCase(`${enc}`) === 'base64url')
|
||||
return 'base64url';
|
||||
break;
|
||||
default:
|
||||
if (enc === '') return 'utf8';
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@ enum encoding ParseEncoding(const char* encoding,
|
||||
} else if (encoding[1] == 'a') {
|
||||
if (strncmp(encoding + 2, "se64", 5) == 0)
|
||||
return BASE64;
|
||||
if (strncmp(encoding + 2, "se64url", 8) == 0)
|
||||
return BASE64URL;
|
||||
}
|
||||
if (StringEqualNoCase(encoding, "binary"))
|
||||
return LATIN1; // BINARY is a deprecated alias of LATIN1.
|
||||
@@ -75,6 +77,8 @@ enum encoding ParseEncoding(const char* encoding,
|
||||
return BUFFER;
|
||||
if (StringEqualNoCase(encoding, "base64"))
|
||||
return BASE64;
|
||||
if (StringEqualNoCase(encoding, "base64url"))
|
||||
return BASE64URL;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
|
||||
@@ -1184,6 +1184,7 @@ void Initialize(Local<Object> target,
|
||||
|
||||
env->SetMethodNoSideEffect(target, "asciiSlice", StringSlice<ASCII>);
|
||||
env->SetMethodNoSideEffect(target, "base64Slice", StringSlice<BASE64>);
|
||||
env->SetMethodNoSideEffect(target, "base64urlSlice", StringSlice<BASE64URL>);
|
||||
env->SetMethodNoSideEffect(target, "latin1Slice", StringSlice<LATIN1>);
|
||||
env->SetMethodNoSideEffect(target, "hexSlice", StringSlice<HEX>);
|
||||
env->SetMethodNoSideEffect(target, "ucs2Slice", StringSlice<UCS2>);
|
||||
@@ -1191,6 +1192,7 @@ void Initialize(Local<Object> target,
|
||||
|
||||
env->SetMethod(target, "asciiWrite", StringWrite<ASCII>);
|
||||
env->SetMethod(target, "base64Write", StringWrite<BASE64>);
|
||||
env->SetMethod(target, "base64urlWrite", StringWrite<BASE64URL>);
|
||||
env->SetMethod(target, "latin1Write", StringWrite<LATIN1>);
|
||||
env->SetMethod(target, "hexWrite", StringWrite<HEX>);
|
||||
env->SetMethod(target, "ucs2Write", StringWrite<UCS2>);
|
||||
@@ -1223,6 +1225,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
|
||||
registry->Register(StringSlice<ASCII>);
|
||||
registry->Register(StringSlice<BASE64>);
|
||||
registry->Register(StringSlice<BASE64URL>);
|
||||
registry->Register(StringSlice<LATIN1>);
|
||||
registry->Register(StringSlice<HEX>);
|
||||
registry->Register(StringSlice<UCS2>);
|
||||
@@ -1230,6 +1233,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
|
||||
registry->Register(StringWrite<ASCII>);
|
||||
registry->Register(StringWrite<BASE64>);
|
||||
registry->Register(StringWrite<BASE64URL>);
|
||||
registry->Register(StringWrite<LATIN1>);
|
||||
registry->Register(StringWrite<HEX>);
|
||||
registry->Register(StringWrite<UCS2>);
|
||||
|
||||
@@ -70,7 +70,10 @@ MaybeLocal<String> StringDecoder::DecodeData(Isolate* isolate,
|
||||
|
||||
size_t nread = *nread_ptr;
|
||||
|
||||
if (Encoding() == UTF8 || Encoding() == UCS2 || Encoding() == BASE64) {
|
||||
if (Encoding() == UTF8 ||
|
||||
Encoding() == UCS2 ||
|
||||
Encoding() == BASE64 ||
|
||||
Encoding() == BASE64URL) {
|
||||
// See if we want bytes to finish a character from the previous
|
||||
// chunk; if so, copy the new bytes to the missing bytes buffer
|
||||
// and create a small string from it that is to be prepended to the
|
||||
@@ -198,7 +201,7 @@ MaybeLocal<String> StringDecoder::DecodeData(Isolate* isolate,
|
||||
state_[kBufferedBytes] = 2;
|
||||
state_[kMissingBytes] = 2;
|
||||
}
|
||||
} else if (Encoding() == BASE64) {
|
||||
} else if (Encoding() == BASE64 || Encoding() == BASE64URL) {
|
||||
state_[kBufferedBytes] = nread % 3;
|
||||
if (state_[kBufferedBytes] > 0)
|
||||
state_[kMissingBytes] = 3 - BufferedBytes();
|
||||
@@ -311,6 +314,7 @@ void InitializeStringDecoder(Local<Object> target,
|
||||
ADD_TO_ENCODINGS_ARRAY(ASCII, "ascii");
|
||||
ADD_TO_ENCODINGS_ARRAY(UTF8, "utf8");
|
||||
ADD_TO_ENCODINGS_ARRAY(BASE64, "base64");
|
||||
ADD_TO_ENCODINGS_ARRAY(BASE64URL, "base64url");
|
||||
ADD_TO_ENCODINGS_ARRAY(UCS2, "utf16le");
|
||||
ADD_TO_ENCODINGS_ARRAY(HEX, "hex");
|
||||
ADD_TO_ENCODINGS_ARRAY(BUFFER, "buffer");
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace {
|
||||
#define ENCODING_MAP(V) \
|
||||
V(ASCII) \
|
||||
V(BASE64) \
|
||||
V(BASE64URL) \
|
||||
V(BUFFER) \
|
||||
V(HEX) \
|
||||
V(LATIN1) \
|
||||
|
||||
@@ -8,6 +8,7 @@ assert.strictEqual(parseEncoding(''), 'UNKNOWN');
|
||||
|
||||
assert.strictEqual(parseEncoding('ascii'), 'ASCII');
|
||||
assert.strictEqual(parseEncoding('base64'), 'BASE64');
|
||||
assert.strictEqual(parseEncoding('base64url'), 'BASE64URL');
|
||||
assert.strictEqual(parseEncoding('binary'), 'LATIN1');
|
||||
assert.strictEqual(parseEncoding('buffer'), 'BUFFER');
|
||||
assert.strictEqual(parseEncoding('hex'), 'HEX');
|
||||
|
||||
@@ -293,14 +293,40 @@ Buffer.alloc(1).write('', 1, 0);
|
||||
// Test toString('base64')
|
||||
//
|
||||
assert.strictEqual((Buffer.from('Man')).toString('base64'), 'TWFu');
|
||||
assert.strictEqual((Buffer.from('Woman')).toString('base64'), 'V29tYW4=');
|
||||
|
||||
//
|
||||
// Test toString('base64url')
|
||||
//
|
||||
assert.strictEqual((Buffer.from('Man')).toString('base64url'), 'TWFu');
|
||||
assert.strictEqual((Buffer.from('Woman')).toString('base64url'), 'V29tYW4');
|
||||
|
||||
{
|
||||
// Test that regular and URL-safe base64 both work
|
||||
// Test that regular and URL-safe base64 both work both ways
|
||||
const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff];
|
||||
assert.deepStrictEqual(Buffer.from('//++/++/++//', 'base64'),
|
||||
Buffer.from(expected));
|
||||
assert.deepStrictEqual(Buffer.from('__--_--_--__', 'base64'),
|
||||
Buffer.from(expected));
|
||||
assert.deepStrictEqual(Buffer.from('//++/++/++//', 'base64url'),
|
||||
Buffer.from(expected));
|
||||
assert.deepStrictEqual(Buffer.from('__--_--_--__', 'base64url'),
|
||||
Buffer.from(expected));
|
||||
}
|
||||
|
||||
const base64flavors = ['base64', 'base64url'];
|
||||
|
||||
{
|
||||
// Test that regular and URL-safe base64 both work both ways with padding
|
||||
const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff, 0xfb];
|
||||
assert.deepStrictEqual(Buffer.from('//++/++/++//+w==', 'base64'),
|
||||
Buffer.from(expected));
|
||||
assert.deepStrictEqual(Buffer.from('//++/++/++//+w==', 'base64'),
|
||||
Buffer.from(expected));
|
||||
assert.deepStrictEqual(Buffer.from('//++/++/++//+w==', 'base64url'),
|
||||
Buffer.from(expected));
|
||||
assert.deepStrictEqual(Buffer.from('//++/++/++//+w==', 'base64url'),
|
||||
Buffer.from(expected));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -317,137 +343,182 @@ assert.strictEqual((Buffer.from('Man')).toString('base64'), 'TWFu');
|
||||
'dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZ' +
|
||||
'GdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm' +
|
||||
'5hbCBwbGVhc3VyZS4=';
|
||||
assert.strictEqual((Buffer.from(quote)).toString('base64'), expected);
|
||||
assert.strictEqual(Buffer.from(quote).toString('base64'), expected);
|
||||
assert.strictEqual(
|
||||
Buffer.from(quote).toString('base64url'),
|
||||
expected.replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '')
|
||||
);
|
||||
|
||||
let b = Buffer.allocUnsafe(1024);
|
||||
let bytesWritten = b.write(expected, 0, 'base64');
|
||||
assert.strictEqual(quote.length, bytesWritten);
|
||||
assert.strictEqual(quote, b.toString('ascii', 0, quote.length));
|
||||
base64flavors.forEach((encoding) => {
|
||||
let b = Buffer.allocUnsafe(1024);
|
||||
let bytesWritten = b.write(expected, 0, encoding);
|
||||
assert.strictEqual(quote.length, bytesWritten);
|
||||
assert.strictEqual(quote, b.toString('ascii', 0, quote.length));
|
||||
|
||||
// Check that the base64 decoder ignores whitespace
|
||||
const expectedWhite = `${expected.slice(0, 60)} \n` +
|
||||
`${expected.slice(60, 120)} \n` +
|
||||
`${expected.slice(120, 180)} \n` +
|
||||
`${expected.slice(180, 240)} \n` +
|
||||
`${expected.slice(240, 300)}\n` +
|
||||
`${expected.slice(300, 360)}\n`;
|
||||
b = Buffer.allocUnsafe(1024);
|
||||
bytesWritten = b.write(expectedWhite, 0, 'base64');
|
||||
assert.strictEqual(quote.length, bytesWritten);
|
||||
assert.strictEqual(quote, b.toString('ascii', 0, quote.length));
|
||||
// Check that the base64 decoder ignores whitespace
|
||||
const expectedWhite = `${expected.slice(0, 60)} \n` +
|
||||
`${expected.slice(60, 120)} \n` +
|
||||
`${expected.slice(120, 180)} \n` +
|
||||
`${expected.slice(180, 240)} \n` +
|
||||
`${expected.slice(240, 300)}\n` +
|
||||
`${expected.slice(300, 360)}\n`;
|
||||
b = Buffer.allocUnsafe(1024);
|
||||
bytesWritten = b.write(expectedWhite, 0, encoding);
|
||||
assert.strictEqual(quote.length, bytesWritten);
|
||||
assert.strictEqual(quote, b.toString('ascii', 0, quote.length));
|
||||
|
||||
// Check that the base64 decoder on the constructor works
|
||||
// even in the presence of whitespace.
|
||||
b = Buffer.from(expectedWhite, 'base64');
|
||||
assert.strictEqual(quote.length, b.length);
|
||||
assert.strictEqual(quote, b.toString('ascii', 0, quote.length));
|
||||
// Check that the base64 decoder on the constructor works
|
||||
// even in the presence of whitespace.
|
||||
b = Buffer.from(expectedWhite, encoding);
|
||||
assert.strictEqual(quote.length, b.length);
|
||||
assert.strictEqual(quote, b.toString('ascii', 0, quote.length));
|
||||
|
||||
// Check that the base64 decoder ignores illegal chars
|
||||
const expectedIllegal = expected.slice(0, 60) + ' \x80' +
|
||||
expected.slice(60, 120) + ' \xff' +
|
||||
expected.slice(120, 180) + ' \x00' +
|
||||
expected.slice(180, 240) + ' \x98' +
|
||||
expected.slice(240, 300) + '\x03' +
|
||||
expected.slice(300, 360);
|
||||
b = Buffer.from(expectedIllegal, 'base64');
|
||||
assert.strictEqual(quote.length, b.length);
|
||||
assert.strictEqual(quote, b.toString('ascii', 0, quote.length));
|
||||
// Check that the base64 decoder ignores illegal chars
|
||||
const expectedIllegal = expected.slice(0, 60) + ' \x80' +
|
||||
expected.slice(60, 120) + ' \xff' +
|
||||
expected.slice(120, 180) + ' \x00' +
|
||||
expected.slice(180, 240) + ' \x98' +
|
||||
expected.slice(240, 300) + '\x03' +
|
||||
expected.slice(300, 360);
|
||||
b = Buffer.from(expectedIllegal, encoding);
|
||||
assert.strictEqual(quote.length, b.length);
|
||||
assert.strictEqual(quote, b.toString('ascii', 0, quote.length));
|
||||
});
|
||||
}
|
||||
|
||||
assert.strictEqual(Buffer.from('', 'base64').toString(), '');
|
||||
assert.strictEqual(Buffer.from('K', 'base64').toString(), '');
|
||||
base64flavors.forEach((encoding) => {
|
||||
assert.strictEqual(Buffer.from('', encoding).toString(), '');
|
||||
assert.strictEqual(Buffer.from('K', encoding).toString(), '');
|
||||
|
||||
// multiple-of-4 with padding
|
||||
assert.strictEqual(Buffer.from('Kg==', 'base64').toString(), '*');
|
||||
assert.strictEqual(Buffer.from('Kio=', 'base64').toString(), '*'.repeat(2));
|
||||
assert.strictEqual(Buffer.from('Kioq', 'base64').toString(), '*'.repeat(3));
|
||||
assert.strictEqual(Buffer.from('KioqKg==', 'base64').toString(), '*'.repeat(4));
|
||||
assert.strictEqual(Buffer.from('KioqKio=', 'base64').toString(), '*'.repeat(5));
|
||||
assert.strictEqual(Buffer.from('KioqKioq', 'base64').toString(), '*'.repeat(6));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKg==', 'base64').toString(),
|
||||
'*'.repeat(7));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKio=', 'base64').toString(),
|
||||
'*'.repeat(8));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioq', 'base64').toString(),
|
||||
'*'.repeat(9));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKg==', 'base64').toString(),
|
||||
'*'.repeat(10));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKio=', 'base64').toString(),
|
||||
'*'.repeat(11));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioq', 'base64').toString(),
|
||||
'*'.repeat(12));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKg==', 'base64').toString(),
|
||||
'*'.repeat(13));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKio=', 'base64').toString(),
|
||||
'*'.repeat(14));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioq', 'base64').toString(),
|
||||
'*'.repeat(15));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKg==', 'base64').toString(),
|
||||
'*'.repeat(16));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKio=', 'base64').toString(),
|
||||
'*'.repeat(17));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioq', 'base64').toString(),
|
||||
'*'.repeat(18));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKg==',
|
||||
'base64').toString(),
|
||||
'*'.repeat(19));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKio=',
|
||||
'base64').toString(),
|
||||
'*'.repeat(20));
|
||||
// multiple-of-4 with padding
|
||||
assert.strictEqual(Buffer.from('Kg==', encoding).toString(), '*');
|
||||
assert.strictEqual(Buffer.from('Kio=', encoding).toString(), '*'.repeat(2));
|
||||
assert.strictEqual(Buffer.from('Kioq', encoding).toString(), '*'.repeat(3));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKg==', encoding).toString(), '*'.repeat(4));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKio=', encoding).toString(), '*'.repeat(5));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKioq', encoding).toString(), '*'.repeat(6));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKg==', encoding).toString(),
|
||||
'*'.repeat(7));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKio=', encoding).toString(),
|
||||
'*'.repeat(8));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioq', encoding).toString(),
|
||||
'*'.repeat(9));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKg==', encoding).toString(),
|
||||
'*'.repeat(10));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKio=', encoding).toString(),
|
||||
'*'.repeat(11));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioq', encoding).toString(),
|
||||
'*'.repeat(12));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKg==', encoding).toString(),
|
||||
'*'.repeat(13));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKio=', encoding).toString(),
|
||||
'*'.repeat(14));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioq', encoding).toString(),
|
||||
'*'.repeat(15));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKioqKioqKioqKioqKg==', encoding).toString(),
|
||||
'*'.repeat(16));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKioqKioqKioqKioqKio=', encoding).toString(),
|
||||
'*'.repeat(17));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKioqKioqKioqKioqKioq', encoding).toString(),
|
||||
'*'.repeat(18));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKg==',
|
||||
encoding).toString(),
|
||||
'*'.repeat(19));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKio=',
|
||||
encoding).toString(),
|
||||
'*'.repeat(20));
|
||||
|
||||
// No padding, not a multiple of 4
|
||||
assert.strictEqual(Buffer.from('Kg', 'base64').toString(), '*');
|
||||
assert.strictEqual(Buffer.from('Kio', 'base64').toString(), '*'.repeat(2));
|
||||
assert.strictEqual(Buffer.from('KioqKg', 'base64').toString(), '*'.repeat(4));
|
||||
assert.strictEqual(Buffer.from('KioqKio', 'base64').toString(), '*'.repeat(5));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKg', 'base64').toString(),
|
||||
'*'.repeat(7));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKio', 'base64').toString(),
|
||||
'*'.repeat(8));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKg', 'base64').toString(),
|
||||
'*'.repeat(10));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKio', 'base64').toString(),
|
||||
'*'.repeat(11));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKg', 'base64').toString(),
|
||||
'*'.repeat(13));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKio', 'base64').toString(),
|
||||
'*'.repeat(14));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKg', 'base64').toString(),
|
||||
'*'.repeat(16));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKio', 'base64').toString(),
|
||||
'*'.repeat(17));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKg',
|
||||
'base64').toString(),
|
||||
'*'.repeat(19));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKio',
|
||||
'base64').toString(),
|
||||
'*'.repeat(20));
|
||||
// No padding, not a multiple of 4
|
||||
assert.strictEqual(Buffer.from('Kg', encoding).toString(), '*');
|
||||
assert.strictEqual(Buffer.from('Kio', encoding).toString(), '*'.repeat(2));
|
||||
assert.strictEqual(Buffer.from('KioqKg', encoding).toString(), '*'.repeat(4));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKio', encoding).toString(), '*'.repeat(5));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKg', encoding).toString(),
|
||||
'*'.repeat(7));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKio', encoding).toString(),
|
||||
'*'.repeat(8));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKg', encoding).toString(),
|
||||
'*'.repeat(10));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKio', encoding).toString(),
|
||||
'*'.repeat(11));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKg', encoding).toString(),
|
||||
'*'.repeat(13));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKio', encoding).toString(),
|
||||
'*'.repeat(14));
|
||||
assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKg', encoding).toString(),
|
||||
'*'.repeat(16));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKioqKioqKioqKioqKio', encoding).toString(),
|
||||
'*'.repeat(17));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKioqKioqKioqKioqKioqKg', encoding).toString(),
|
||||
'*'.repeat(19));
|
||||
assert.strictEqual(
|
||||
Buffer.from('KioqKioqKioqKioqKioqKioqKio', encoding).toString(),
|
||||
'*'.repeat(20));
|
||||
});
|
||||
|
||||
// Handle padding graciously, multiple-of-4 or not
|
||||
assert.strictEqual(
|
||||
Buffer.from('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw==', 'base64').length,
|
||||
32
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw==', 'base64url')
|
||||
.length,
|
||||
32
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw=', 'base64').length,
|
||||
32
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw=', 'base64url')
|
||||
.length,
|
||||
32
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw', 'base64').length,
|
||||
32
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw', 'base64url')
|
||||
.length,
|
||||
32
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg==', 'base64').length,
|
||||
31
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg==', 'base64url')
|
||||
.length,
|
||||
31
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg=', 'base64').length,
|
||||
31
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg=', 'base64url')
|
||||
.length,
|
||||
31
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg', 'base64').length,
|
||||
31
|
||||
);
|
||||
assert.strictEqual(
|
||||
Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg', 'base64url').length,
|
||||
31
|
||||
);
|
||||
|
||||
{
|
||||
// This string encodes single '.' character in UTF-16
|
||||
@@ -459,6 +530,16 @@ assert.strictEqual(
|
||||
assert.strictEqual(dot.toString('base64'), '//4uAA==');
|
||||
}
|
||||
|
||||
{
|
||||
// This string encodes single '.' character in UTF-16
|
||||
const dot = Buffer.from('//4uAA', 'base64url');
|
||||
assert.strictEqual(dot[0], 0xff);
|
||||
assert.strictEqual(dot[1], 0xfe);
|
||||
assert.strictEqual(dot[2], 0x2e);
|
||||
assert.strictEqual(dot[3], 0x00);
|
||||
assert.strictEqual(dot.toString('base64url'), '__4uAA');
|
||||
}
|
||||
|
||||
{
|
||||
// Writing base64 at a position > 0 should not mangle the result.
|
||||
//
|
||||
@@ -474,6 +555,21 @@ assert.strictEqual(
|
||||
'Madness?! This is node.js!');
|
||||
}
|
||||
|
||||
{
|
||||
// Writing base64url at a position > 0 should not mangle the result.
|
||||
//
|
||||
// https://github.com/joyent/node/issues/402
|
||||
const segments = ['TWFkbmVzcz8h', 'IFRoaXM', 'IGlz', 'IG5vZGUuanMh'];
|
||||
const b = Buffer.allocUnsafe(64);
|
||||
let pos = 0;
|
||||
|
||||
for (let i = 0; i < segments.length; ++i) {
|
||||
pos += b.write(segments[i], pos, 'base64url');
|
||||
}
|
||||
assert.strictEqual(b.toString('latin1', 0, pos),
|
||||
'Madness?! This is node.js!');
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/nodejs/node/issues/3496.
|
||||
assert.strictEqual(Buffer.from('=bad'.repeat(1e4), 'base64').length, 0);
|
||||
|
||||
|
||||
@@ -91,9 +91,19 @@ assert.strictEqual(Buffer.byteLength('aGkk', 'base64'), 3);
|
||||
assert.strictEqual(
|
||||
Buffer.byteLength('bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==', 'base64'), 25
|
||||
);
|
||||
// base64url
|
||||
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ', 'base64url'), 11);
|
||||
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ', 'BASE64URL'), 11);
|
||||
assert.strictEqual(Buffer.byteLength('bm9kZS5qcyByb2NrcyE', 'base64url'), 14);
|
||||
assert.strictEqual(Buffer.byteLength('aGkk', 'base64url'), 3);
|
||||
assert.strictEqual(
|
||||
Buffer.byteLength('bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw', 'base64url'), 25
|
||||
);
|
||||
// special padding
|
||||
assert.strictEqual(Buffer.byteLength('aaa=', 'base64'), 2);
|
||||
assert.strictEqual(Buffer.byteLength('aaaa==', 'base64'), 3);
|
||||
assert.strictEqual(Buffer.byteLength('aaa=', 'base64url'), 2);
|
||||
assert.strictEqual(Buffer.byteLength('aaaa==', 'base64url'), 3);
|
||||
|
||||
assert.strictEqual(Buffer.byteLength('Il était tué'), 14);
|
||||
assert.strictEqual(Buffer.byteLength('Il était tué', 'utf8'), 14);
|
||||
|
||||
@@ -131,20 +131,36 @@ assert.throws(() => {
|
||||
});
|
||||
|
||||
// BASE64
|
||||
testBufs('YWJj', 'ucs2');
|
||||
testBufs('yKJhYQ==', 'ucs2');
|
||||
testBufs('Yci0Ysi1Y8i2', 'ucs2');
|
||||
testBufs('YWJj', 4, 'ucs2');
|
||||
testBufs('YWJj', SIZE, 'ucs2');
|
||||
testBufs('yKJhYQ==', 2, 'ucs2');
|
||||
testBufs('yKJhYQ==', 8, 'ucs2');
|
||||
testBufs('Yci0Ysi1Y8i2', 4, 'ucs2');
|
||||
testBufs('Yci0Ysi1Y8i2', 12, 'ucs2');
|
||||
testBufs('YWJj', 4, 1, 'ucs2');
|
||||
testBufs('YWJj', 5, 1, 'ucs2');
|
||||
testBufs('yKJhYQ==', 8, 1, 'ucs2');
|
||||
testBufs('Yci0Ysi1Y8i2', 4, 1, 'ucs2');
|
||||
testBufs('Yci0Ysi1Y8i2', 12, 1, 'ucs2');
|
||||
testBufs('YWJj', 'base64');
|
||||
testBufs('yKJhYQ==', 'base64');
|
||||
testBufs('Yci0Ysi1Y8i2', 'base64');
|
||||
testBufs('YWJj', 4, 'base64');
|
||||
testBufs('YWJj', SIZE, 'base64');
|
||||
testBufs('yKJhYQ==', 2, 'base64');
|
||||
testBufs('yKJhYQ==', 8, 'base64');
|
||||
testBufs('Yci0Ysi1Y8i2', 4, 'base64');
|
||||
testBufs('Yci0Ysi1Y8i2', 12, 'base64');
|
||||
testBufs('YWJj', 4, 1, 'base64');
|
||||
testBufs('YWJj', 5, 1, 'base64');
|
||||
testBufs('yKJhYQ==', 8, 1, 'base64');
|
||||
testBufs('Yci0Ysi1Y8i2', 4, 1, 'base64');
|
||||
testBufs('Yci0Ysi1Y8i2', 12, 1, 'base64');
|
||||
|
||||
// BASE64URL
|
||||
testBufs('YWJj', 'base64url');
|
||||
testBufs('yKJhYQ', 'base64url');
|
||||
testBufs('Yci0Ysi1Y8i2', 'base64url');
|
||||
testBufs('YWJj', 4, 'base64url');
|
||||
testBufs('YWJj', SIZE, 'base64url');
|
||||
testBufs('yKJhYQ', 2, 'base64url');
|
||||
testBufs('yKJhYQ', 8, 'base64url');
|
||||
testBufs('Yci0Ysi1Y8i2', 4, 'base64url');
|
||||
testBufs('Yci0Ysi1Y8i2', 12, 'base64url');
|
||||
testBufs('YWJj', 4, 1, 'base64url');
|
||||
testBufs('YWJj', 5, 1, 'base64url');
|
||||
testBufs('yKJhYQ', 8, 1, 'base64url');
|
||||
testBufs('Yci0Ysi1Y8i2', 4, 1, 'base64url');
|
||||
testBufs('Yci0Ysi1Y8i2', 12, 1, 'base64url');
|
||||
|
||||
// Buffer
|
||||
function deepStrictEqualValues(buf, arr) {
|
||||
|
||||
@@ -11,6 +11,7 @@ const assert = require('assert');
|
||||
'latin1',
|
||||
'binary',
|
||||
'base64',
|
||||
'base64url',
|
||||
'ucs2',
|
||||
'ucs-2',
|
||||
'utf16le',
|
||||
|
||||
@@ -23,6 +23,7 @@ const resultMap = new Map([
|
||||
['binary', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
|
||||
['utf16le', Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])],
|
||||
['base64', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
|
||||
['base64url', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
|
||||
['hex', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])]
|
||||
]);
|
||||
|
||||
@@ -44,7 +45,7 @@ encodings
|
||||
});
|
||||
|
||||
// base64
|
||||
['base64', 'BASE64'].forEach((encoding) => {
|
||||
['base64', 'BASE64', 'base64url', 'BASE64URL'].forEach((encoding) => {
|
||||
const buf = Buffer.alloc(9);
|
||||
const len = Buffer.byteLength('Zm9v', encoding);
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ const tests = [
|
||||
['base64', 'base64'],
|
||||
['BASE64', 'base64'],
|
||||
['Base64', 'base64'],
|
||||
['base64url', 'base64url'],
|
||||
['BASE64url', 'base64url'],
|
||||
['Base64url', 'base64url'],
|
||||
['hex', 'hex'],
|
||||
['HEX', 'hex'],
|
||||
['ASCII', 'ascii'],
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const SD = require('string_decoder').StringDecoder;
|
||||
const encodings = ['base64', 'hex', 'utf8', 'utf16le', 'ucs2'];
|
||||
const encodings = ['base64', 'base64url', 'hex', 'utf8', 'utf16le', 'ucs2'];
|
||||
|
||||
const bufs = [ '☃💩', 'asdf' ].map((b) => Buffer.from(b));
|
||||
|
||||
@@ -79,6 +79,13 @@ testEnd('base64', Buffer.of(0x61, 0x61), Buffer.of(0x61), 'YWE=YQ==');
|
||||
testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(), 'YWFh');
|
||||
testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), 'YWFhYQ==');
|
||||
|
||||
testEnd('base64url', Buffer.of(0x61), Buffer.of(), 'YQ');
|
||||
testEnd('base64url', Buffer.of(0x61), Buffer.of(0x61), 'YQYQ');
|
||||
testEnd('base64url', Buffer.of(0x61, 0x61), Buffer.of(), 'YWE');
|
||||
testEnd('base64url', Buffer.of(0x61, 0x61), Buffer.of(0x61), 'YWEYQ');
|
||||
testEnd('base64url', Buffer.of(0x61, 0x61, 0x61), Buffer.of(), 'YWFh');
|
||||
testEnd('base64url', Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), 'YWFhYQ');
|
||||
|
||||
function testEncoding(encoding) {
|
||||
bufs.forEach((buf) => {
|
||||
testBuf(encoding, buf);
|
||||
|
||||
@@ -20,7 +20,7 @@ function randBuf(maxLen) {
|
||||
}
|
||||
|
||||
const encodings = [
|
||||
'utf16le', 'utf8', 'ascii', 'hex', 'base64', 'latin1'
|
||||
'utf16le', 'utf8', 'ascii', 'hex', 'base64', 'latin1', 'base64url'
|
||||
];
|
||||
|
||||
function runSingleFuzzTest() {
|
||||
|
||||
Reference in new issue
Block a user