Skip to content

Commit 234c710

Browse files
aduh95RafaelGSS
authored andcommitted
crypto: fix cross-realm SharedArrayBuffer validation
PR-URL: #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>
1 parent 36b0a29 commit 234c710

2 files changed

Lines changed: 56 additions & 55 deletions

File tree

‎lib/internal/crypto/webidl.js‎

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ const {
2525
String,
2626
TypedArrayPrototypeGetBuffer,
2727
TypedArrayPrototypeGetSymbolToStringTag,
28-
globalThis: {
29-
SharedArrayBuffer,
30-
},
3128
}=primordials;
3229

3330
const{
@@ -47,7 +44,7 @@ const {
4744
validateMaxBufferLength,
4845
kNamedCurveAliases,
4946
}=require('internal/crypto/util');
50-
const{ isArrayBuffer }=require('internal/util/types');
47+
const{ isArrayBuffer, isSharedArrayBuffer}=require('internal/util/types');
5148

5249
// https://tc39.es/ecma262/#sec-tonumber
5350
functiontoNumber(value,opts=kEmptyObject){
@@ -195,13 +192,6 @@ converters.object = (V, opts) => {
195192

196193
constisNonSharedArrayBuffer=isArrayBuffer;
197194

198-
functionisSharedArrayBuffer(V){
199-
// SharedArrayBuffers can be disabled with --no-harmony-sharedarraybuffer.
200-
if(SharedArrayBuffer!==undefined)
201-
returnObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype,V);
202-
returnfalse;
203-
}
204-
205195
converters.Uint8Array=(V,opts=kEmptyObject)=>{
206196
if(!ArrayBufferIsView(V)||
207197
TypedArrayPrototypeGetSymbolToStringTag(V)!=='Uint8Array'){
Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
'use strict';
2-
// Flags: --expose-internals
32
constcommon=require('../common');
43
if(!common.hasCrypto)
54
common.skip('missing crypto');
65

76
constassert=require('assert');
87
const{ subtle }=globalThis.crypto;
98
constvm=require('vm');
10-
const{ isArrayBuffer }=require('internal/util/types');
119

1210
// Test with same-realm ArrayBuffer
1311
{
1412
constsamerealmData=newUint8Array([1,2,3,4]).buffer;
1513

1614
subtle.digest('SHA-256',samerealmData)
1715
.then(common.mustCall((result)=>{
18-
assert(isArrayBuffer(result));
1916
assert.strictEqual(result.byteLength,32);// SHA-256 is 32 bytes
2017
}));
2118
}
@@ -35,11 +32,30 @@ const { isArrayBuffer } = require('internal/util/types');
3532
// This should still work, since we're checking structural type
3633
subtle.digest('SHA-256',crossrealmBuffer)
3734
.then(common.mustCall((result)=>{
38-
assert(isArrayBuffer(result));
3935
assert.strictEqual(result.byteLength,32);// SHA-256 is 32 bytes
4036
}));
4137
}
4238

39+
// Cross-realm SharedArrayBuffer should be handled like any SharedArrayBuffer
40+
{
41+
constcontext=vm.createContext({});
42+
constcrossrealmSAB=vm.runInContext('new SharedArrayBuffer(4)',context);
43+
assert.notStrictEqual(
44+
Object.getPrototypeOf(crossrealmSAB),
45+
SharedArrayBuffer.prototype
46+
);
47+
Promise.allSettled([
48+
subtle.digest('SHA-256',newUint8Array(newSharedArrayBuffer(4))),
49+
subtle.digest('SHA-256',newUint8Array(crossrealmSAB)),
50+
]).then(common.mustCall((r)=>{
51+
assert.partialDeepStrictEqual(r,[
52+
{status: 'rejected'},
53+
{status: 'rejected'},
54+
]);
55+
assert.strictEqual(r[1].reason.message,r[0].reason.message);
56+
}));
57+
}
58+
4359
// Test with both TypedArray buffer methods
4460
{
4561
constcontext=vm.createContext({});
@@ -48,14 +64,12 @@ const { isArrayBuffer } = require('internal/util/types');
4864
// Test the .buffer property
4965
subtle.digest('SHA-256',crossrealmUint8Array.buffer)
5066
.then(common.mustCall((result)=>{
51-
assert(isArrayBuffer(result));
5267
assert.strictEqual(result.byteLength,32);
5368
}));
5469

5570
// Test passing the TypedArray directly (should work both before and after the fix)
5671
subtle.digest('SHA-256',crossrealmUint8Array)
5772
.then(common.mustCall((result)=>{
58-
assert(isArrayBuffer(result));
5973
assert.strictEqual(result.byteLength,32);
6074
}));
6175
}
@@ -76,34 +90,32 @@ const { isArrayBuffer } = require('internal/util/types');
7690
name: 'AES-GCM',
7791
length: 256
7892
},true,['encrypt','decrypt'])
79-
.then(common.mustCall((key)=>{
93+
.then(async(key)=>{
8094
// Create an initialization vector
8195
constiv=crypto.getRandomValues(newUint8Array(12));
8296

8397
// Encrypt using the cross-realm ArrayBuffer
84-
returnsubtle.encrypt(
98+
constciphertext=awaitsubtle.encrypt(
8599
{name: 'AES-GCM', iv },
86100
key,
87101
crossRealmBuffer
88-
).then((ciphertext)=>{
102+
);
89103
// Decrypt
90-
returnsubtle.decrypt(
91-
{name: 'AES-GCM', iv },
92-
key,
93-
ciphertext
94-
);
95-
}).then(common.mustCall((plaintext)=>{
104+
constplaintext=awaitsubtle.decrypt(
105+
{name: 'AES-GCM', iv },
106+
key,
107+
ciphertext
108+
);
96109
// Verify the decrypted content matches original
97-
constdecryptedView=newUint8Array(plaintext);
98-
for(leti=0;i<dataView.length;i++){
99-
assert.strictEqual(
100-
decryptedView[i],
101-
dataView[i],
102-
`Byte at position ${i} doesn't match`
103-
);
104-
}
105-
}));
106-
}));
110+
constdecryptedView=newUint8Array(plaintext);
111+
for(leti=0;i<dataView.length;i++){
112+
assert.strictEqual(
113+
decryptedView[i],
114+
dataView[i],
115+
`Byte at position ${i} doesn't match`
116+
);
117+
}
118+
}).then(common.mustCall());
107119
}
108120

109121
// Test with AES-GCM using TypedArray view of cross-realm ArrayBuffer
@@ -122,32 +134,31 @@ const { isArrayBuffer } = require('internal/util/types');
122134
name: 'AES-GCM',
123135
length: 256
124136
},true,['encrypt','decrypt'])
125-
.then(common.mustCall((key)=>{
137+
.then(async(key)=>{
126138
// Create an initialization vector
127139
constiv=crypto.getRandomValues(newUint8Array(12));
128140

129141
// Encrypt using the TypedArray view of cross-realm ArrayBuffer
130-
returnsubtle.encrypt(
142+
constciphertext=awaitsubtle.encrypt(
131143
{name: 'AES-GCM', iv },
132144
key,
133145
dataView
134-
).then((ciphertext)=>{
146+
);
135147
// Decrypt
136-
returnsubtle.decrypt(
137-
{name: 'AES-GCM', iv },
138-
key,
139-
ciphertext
148+
constplaintext=awaitsubtle.decrypt(
149+
{name: 'AES-GCM', iv },
150+
key,
151+
ciphertext
152+
);
153+
154+
// Verify the decrypted content matches original
155+
constdecryptedView=newUint8Array(plaintext);
156+
for(leti=0;i<dataView.length;i++){
157+
assert.strictEqual(
158+
decryptedView[i],
159+
dataView[i],
160+
`Byte at position ${i} doesn't match`
140161
);
141-
}).then(common.mustCall((plaintext)=>{
142-
// Verify the decrypted content matches original
143-
constdecryptedView=newUint8Array(plaintext);
144-
for(leti=0;i<dataView.length;i++){
145-
assert.strictEqual(
146-
decryptedView[i],
147-
dataView[i],
148-
`Byte at position ${i} doesn't match`
149-
);
150-
}
151-
}));
152-
}));
162+
}
163+
}).then(common.mustCall());
153164
}

0 commit comments

Comments
 (0)