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:
Filip Skokan authored and Antoine du Hamel committed 2021-01-18 12:08:20 +01:00
1 parent 9e7d1a1c24
commit f8ab632d56
17 files changed
+306 -120

No files matched your search

+13 -3
View File
@@ -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
+19
View File
@@ -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;
}
}
+4
View File
@@ -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;
+5
View File
@@ -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';
}
+4
View File
@@ -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':
+4
View File
@@ -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>);
+6 -2
View File
@@ -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");
+1
View File
@@ -6,6 +6,7 @@ namespace {
#define ENCODING_MAP(V) \
V(ASCII) \
V(BASE64) \
V(BASE64URL) \
V(BUFFER) \
V(HEX) \
V(LATIN1) \
+1
View File
@@ -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');
+194 -98
View File
@@ -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);
+10
View File
@@ -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);
+30 -14
View File
@@ -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) {
+1
View File
@@ -11,6 +11,7 @@ const assert = require('assert');
'latin1',
'binary',
'base64',
'base64url',
'ucs2',
'ucs-2',
'utf16le',
+2 -1
View File
@@ -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'],
+8 -1
View File
@@ -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);
+1 -1
View File
@@ -20,7 +20,7 @@ function randBuf(maxLen) {
}
const encodings = [
'utf16le', 'utf8', 'ascii', 'hex', 'base64', 'latin1'
'utf16le', 'utf8', 'ascii', 'hex', 'base64', 'latin1', 'base64url'
];
function runSingleFuzzTest() {