mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
crypto: fix cross-realm SharedArrayBuffer validation
PR-URL: https://github.com/nodejs/node/pull/57974 Reviewed-By: Jordan Harband <ljharb@gmail.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
parent
2c315d24f5
commit
5d15cbb416
|
|
@ -25,9 +25,6 @@ const {
|
||||||
String,
|
String,
|
||||||
TypedArrayPrototypeGetBuffer,
|
TypedArrayPrototypeGetBuffer,
|
||||||
TypedArrayPrototypeGetSymbolToStringTag,
|
TypedArrayPrototypeGetSymbolToStringTag,
|
||||||
globalThis: {
|
|
||||||
SharedArrayBuffer,
|
|
||||||
},
|
|
||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -47,7 +44,7 @@ const {
|
||||||
validateMaxBufferLength,
|
validateMaxBufferLength,
|
||||||
kNamedCurveAliases,
|
kNamedCurveAliases,
|
||||||
} = require('internal/crypto/util');
|
} = require('internal/crypto/util');
|
||||||
const { isArrayBuffer } = require('internal/util/types');
|
const { isArrayBuffer, isSharedArrayBuffer } = require('internal/util/types');
|
||||||
|
|
||||||
// https://tc39.es/ecma262/#sec-tonumber
|
// https://tc39.es/ecma262/#sec-tonumber
|
||||||
function toNumber(value, opts = kEmptyObject) {
|
function toNumber(value, opts = kEmptyObject) {
|
||||||
|
|
@ -195,13 +192,6 @@ converters.object = (V, opts) => {
|
||||||
|
|
||||||
const isNonSharedArrayBuffer = isArrayBuffer;
|
const isNonSharedArrayBuffer = isArrayBuffer;
|
||||||
|
|
||||||
function isSharedArrayBuffer(V) {
|
|
||||||
// SharedArrayBuffers can be disabled with --no-harmony-sharedarraybuffer.
|
|
||||||
if (SharedArrayBuffer !== undefined)
|
|
||||||
return ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
converters.Uint8Array = (V, opts = kEmptyObject) => {
|
converters.Uint8Array = (V, opts = kEmptyObject) => {
|
||||||
if (!ArrayBufferIsView(V) ||
|
if (!ArrayBufferIsView(V) ||
|
||||||
TypedArrayPrototypeGetSymbolToStringTag(V) !== 'Uint8Array') {
|
TypedArrayPrototypeGetSymbolToStringTag(V) !== 'Uint8Array') {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
// Flags: --expose-internals
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
if (!common.hasCrypto)
|
if (!common.hasCrypto)
|
||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
|
|
@ -7,7 +6,6 @@ if (!common.hasCrypto)
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const { subtle } = globalThis.crypto;
|
const { subtle } = globalThis.crypto;
|
||||||
const vm = require('vm');
|
const vm = require('vm');
|
||||||
const { isArrayBuffer } = require('internal/util/types');
|
|
||||||
|
|
||||||
// Test with same-realm ArrayBuffer
|
// Test with same-realm ArrayBuffer
|
||||||
{
|
{
|
||||||
|
|
@ -15,7 +13,6 @@ const { isArrayBuffer } = require('internal/util/types');
|
||||||
|
|
||||||
subtle.digest('SHA-256', samerealmData)
|
subtle.digest('SHA-256', samerealmData)
|
||||||
.then(common.mustCall((result) => {
|
.then(common.mustCall((result) => {
|
||||||
assert(isArrayBuffer(result));
|
|
||||||
assert.strictEqual(result.byteLength, 32); // SHA-256 is 32 bytes
|
assert.strictEqual(result.byteLength, 32); // SHA-256 is 32 bytes
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -35,11 +32,30 @@ const { isArrayBuffer } = require('internal/util/types');
|
||||||
// This should still work, since we're checking structural type
|
// This should still work, since we're checking structural type
|
||||||
subtle.digest('SHA-256', crossrealmBuffer)
|
subtle.digest('SHA-256', crossrealmBuffer)
|
||||||
.then(common.mustCall((result) => {
|
.then(common.mustCall((result) => {
|
||||||
assert(isArrayBuffer(result));
|
|
||||||
assert.strictEqual(result.byteLength, 32); // SHA-256 is 32 bytes
|
assert.strictEqual(result.byteLength, 32); // SHA-256 is 32 bytes
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cross-realm SharedArrayBuffer should be handled like any SharedArrayBuffer
|
||||||
|
{
|
||||||
|
const context = vm.createContext({});
|
||||||
|
const crossrealmSAB = vm.runInContext('new SharedArrayBuffer(4)', context);
|
||||||
|
assert.notStrictEqual(
|
||||||
|
Object.getPrototypeOf(crossrealmSAB),
|
||||||
|
SharedArrayBuffer.prototype
|
||||||
|
);
|
||||||
|
Promise.allSettled([
|
||||||
|
subtle.digest('SHA-256', new Uint8Array(new SharedArrayBuffer(4))),
|
||||||
|
subtle.digest('SHA-256', new Uint8Array(crossrealmSAB)),
|
||||||
|
]).then(common.mustCall((r) => {
|
||||||
|
assert.partialDeepStrictEqual(r, [
|
||||||
|
{ status: 'rejected' },
|
||||||
|
{ status: 'rejected' },
|
||||||
|
]);
|
||||||
|
assert.strictEqual(r[1].reason.message, r[0].reason.message);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// Test with both TypedArray buffer methods
|
// Test with both TypedArray buffer methods
|
||||||
{
|
{
|
||||||
const context = vm.createContext({});
|
const context = vm.createContext({});
|
||||||
|
|
@ -48,14 +64,12 @@ const { isArrayBuffer } = require('internal/util/types');
|
||||||
// Test the .buffer property
|
// Test the .buffer property
|
||||||
subtle.digest('SHA-256', crossrealmUint8Array.buffer)
|
subtle.digest('SHA-256', crossrealmUint8Array.buffer)
|
||||||
.then(common.mustCall((result) => {
|
.then(common.mustCall((result) => {
|
||||||
assert(isArrayBuffer(result));
|
|
||||||
assert.strictEqual(result.byteLength, 32);
|
assert.strictEqual(result.byteLength, 32);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Test passing the TypedArray directly (should work both before and after the fix)
|
// Test passing the TypedArray directly (should work both before and after the fix)
|
||||||
subtle.digest('SHA-256', crossrealmUint8Array)
|
subtle.digest('SHA-256', crossrealmUint8Array)
|
||||||
.then(common.mustCall((result) => {
|
.then(common.mustCall((result) => {
|
||||||
assert(isArrayBuffer(result));
|
|
||||||
assert.strictEqual(result.byteLength, 32);
|
assert.strictEqual(result.byteLength, 32);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -76,34 +90,32 @@ const { isArrayBuffer } = require('internal/util/types');
|
||||||
name: 'AES-GCM',
|
name: 'AES-GCM',
|
||||||
length: 256
|
length: 256
|
||||||
}, true, ['encrypt', 'decrypt'])
|
}, true, ['encrypt', 'decrypt'])
|
||||||
.then(common.mustCall((key) => {
|
.then(async (key) => {
|
||||||
// Create an initialization vector
|
// Create an initialization vector
|
||||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||||
|
|
||||||
// Encrypt using the cross-realm ArrayBuffer
|
// Encrypt using the cross-realm ArrayBuffer
|
||||||
return subtle.encrypt(
|
const ciphertext = await subtle.encrypt(
|
||||||
{ name: 'AES-GCM', iv },
|
{ name: 'AES-GCM', iv },
|
||||||
key,
|
key,
|
||||||
crossRealmBuffer
|
crossRealmBuffer
|
||||||
).then((ciphertext) => {
|
);
|
||||||
// Decrypt
|
// Decrypt
|
||||||
return subtle.decrypt(
|
const plaintext = await subtle.decrypt(
|
||||||
{ name: 'AES-GCM', iv },
|
{ name: 'AES-GCM', iv },
|
||||||
key,
|
key,
|
||||||
ciphertext
|
ciphertext
|
||||||
);
|
);
|
||||||
}).then(common.mustCall((plaintext) => {
|
|
||||||
// Verify the decrypted content matches original
|
// Verify the decrypted content matches original
|
||||||
const decryptedView = new Uint8Array(plaintext);
|
const decryptedView = new Uint8Array(plaintext);
|
||||||
for (let i = 0; i < dataView.length; i++) {
|
for (let i = 0; i < dataView.length; i++) {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
decryptedView[i],
|
decryptedView[i],
|
||||||
dataView[i],
|
dataView[i],
|
||||||
`Byte at position ${i} doesn't match`
|
`Byte at position ${i} doesn't match`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}));
|
}).then(common.mustCall());
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with AES-GCM using TypedArray view of cross-realm ArrayBuffer
|
// Test with AES-GCM using TypedArray view of cross-realm ArrayBuffer
|
||||||
|
|
@ -122,32 +134,31 @@ const { isArrayBuffer } = require('internal/util/types');
|
||||||
name: 'AES-GCM',
|
name: 'AES-GCM',
|
||||||
length: 256
|
length: 256
|
||||||
}, true, ['encrypt', 'decrypt'])
|
}, true, ['encrypt', 'decrypt'])
|
||||||
.then(common.mustCall((key) => {
|
.then(async (key) => {
|
||||||
// Create an initialization vector
|
// Create an initialization vector
|
||||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||||
|
|
||||||
// Encrypt using the TypedArray view of cross-realm ArrayBuffer
|
// Encrypt using the TypedArray view of cross-realm ArrayBuffer
|
||||||
return subtle.encrypt(
|
const ciphertext = await subtle.encrypt(
|
||||||
{ name: 'AES-GCM', iv },
|
{ name: 'AES-GCM', iv },
|
||||||
key,
|
key,
|
||||||
dataView
|
dataView
|
||||||
).then((ciphertext) => {
|
);
|
||||||
// Decrypt
|
// Decrypt
|
||||||
return subtle.decrypt(
|
const plaintext = await subtle.decrypt(
|
||||||
{ name: 'AES-GCM', iv },
|
{ name: 'AES-GCM', iv },
|
||||||
key,
|
key,
|
||||||
ciphertext
|
ciphertext
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify the decrypted content matches original
|
||||||
|
const decryptedView = new Uint8Array(plaintext);
|
||||||
|
for (let i = 0; i < dataView.length; i++) {
|
||||||
|
assert.strictEqual(
|
||||||
|
decryptedView[i],
|
||||||
|
dataView[i],
|
||||||
|
`Byte at position ${i} doesn't match`
|
||||||
);
|
);
|
||||||
}).then(common.mustCall((plaintext) => {
|
}
|
||||||
// Verify the decrypted content matches original
|
}).then(common.mustCall());
|
||||||
const decryptedView = new Uint8Array(plaintext);
|
|
||||||
for (let i = 0; i < dataView.length; i++) {
|
|
||||||
assert.strictEqual(
|
|
||||||
decryptedView[i],
|
|
||||||
dataView[i],
|
|
||||||
`Byte at position ${i} doesn't match`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user