Skip to content

Commit 1884f52

Browse files
panvajuanarbol
authored andcommitted
crypto: validate this in all webcrypto methods and getters
PR-URL: #42815 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent 8b92e59 commit 1884f52

2 files changed

Lines changed: 206 additions & 11 deletions

File tree

‎lib/internal/crypto/webcrypto.js‎

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {
66
JSONStringify,
77
ObjectDefineProperties,
88
ReflectApply,
9+
ReflectConstruct,
910
SafeSet,
1011
SymbolToStringTag,
1112
StringPrototypeRepeat,
@@ -31,6 +32,7 @@ const { TextDecoder, TextEncoder } = require('internal/encoding');
3132

3233
const{
3334
codes: {
35+
ERR_ILLEGAL_CONSTRUCTOR,
3436
ERR_INVALID_ARG_TYPE,
3537
ERR_INVALID_THIS,
3638
}
@@ -70,12 +72,21 @@ const {
7072
randomUUID: _randomUUID,
7173
}=require('internal/crypto/random');
7274

73-
constrandomUUID=()=>_randomUUID();
75+
asyncfunctiondigest(algorithm,data){
76+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
77+
returnReflectApply(asyncDigest,this,arguments);
78+
}
79+
80+
functionrandomUUID(){
81+
if(this!==crypto)thrownewERR_INVALID_THIS('Crypto');
82+
return_randomUUID();
83+
}
7484

7585
asyncfunctiongenerateKey(
7686
algorithm,
7787
extractable,
7888
keyUsages){
89+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
7990
algorithm=normalizeAlgorithm(algorithm);
8091
validateBoolean(extractable,'extractable');
8192
validateArray(keyUsages,'keyUsages');
@@ -123,6 +134,7 @@ async function generateKey(
123134
}
124135

125136
asyncfunctionderiveBits(algorithm,baseKey,length){
137+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
126138
algorithm=normalizeAlgorithm(algorithm);
127139
if(!isCryptoKey(baseKey))
128140
thrownewERR_INVALID_ARG_TYPE('baseKey','CryptoKey',baseKey);
@@ -194,6 +206,7 @@ async function deriveKey(
194206
derivedKeyAlgorithm,
195207
extractable,
196208
keyUsages){
209+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
197210
algorithm=normalizeAlgorithm(algorithm);
198211
derivedKeyAlgorithm=normalizeAlgorithm(derivedKeyAlgorithm);
199212
if(!isCryptoKey(baseKey))
@@ -238,7 +251,11 @@ async function deriveKey(
238251
throwlazyDOMException('Unrecognized name.');
239252
}
240253

241-
returnimportKey('raw',bits,derivedKeyAlgorithm,extractable,keyUsages);
254+
returnReflectApply(
255+
importKey,
256+
this,
257+
['raw',bits,derivedKeyAlgorithm,extractable,keyUsages],
258+
);
242259
}
243260

244261
asyncfunctionexportKeySpki(key){
@@ -415,6 +432,7 @@ async function exportKeyJWK(key) {
415432
}
416433

417434
asyncfunctionexportKey(format,key){
435+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
418436
validateString(format,'format');
419437
validateOneOf(format,'format',kExportFormats);
420438
if(!isCryptoKey(key))
@@ -496,6 +514,7 @@ async function importKey(
496514
algorithm,
497515
extractable,
498516
keyUsages){
517+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
499518
validateString(format,'format');
500519
validateOneOf(format,'format',kExportFormats);
501520
if(format!=='node.keyObject'&&format!=='jwk')
@@ -557,8 +576,9 @@ async function importKey(
557576
// subtle.wrapKey() is essentially a subtle.exportKey() followed
558577
// by a subtle.encrypt().
559578
asyncfunctionwrapKey(format,key,wrappingKey,algorithm){
579+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
560580
algorithm=normalizeAlgorithm(algorithm);
561-
letkeyData=awaitexportKey(format,key);
581+
letkeyData=awaitReflectApply(exportKey,this,[format,key]);
562582

563583
if(format==='jwk'){
564584
if(keyData==null||typeofkeyData!=='object')
@@ -586,6 +606,7 @@ async function unwrapKey(
586606
unwrappedKeyAlgo,
587607
extractable,
588608
keyUsages){
609+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
589610
wrappedKey=getArrayBufferOrView(wrappedKey,'wrappedKey');
590611
unwrapAlgo=normalizeAlgorithm(unwrapAlgo);
591612
letkeyData=awaitcipherOrWrap(
@@ -607,7 +628,11 @@ async function unwrapKey(
607628
}
608629
}
609630

610-
returnimportKey(format,keyData,unwrappedKeyAlgo,extractable,keyUsages);
631+
returnReflectApply(
632+
importKey,
633+
this,
634+
[format,keyData,unwrappedKeyAlgo,extractable,keyUsages],
635+
);
611636
}
612637

613638
functionsignVerify(algorithm,key,data,signature){
@@ -654,10 +679,12 @@ function signVerify(algorithm, key, data, signature) {
654679
}
655680

656681
asyncfunctionsign(algorithm,key,data){
682+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
657683
returnsignVerify(algorithm,key,data);
658684
}
659685

660686
asyncfunctionverify(algorithm,key,signature,data){
687+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
661688
returnsignVerify(algorithm,key,data,signature);
662689
}
663690

@@ -707,30 +734,39 @@ async function cipherOrWrap(mode, algorithm, key, data, op) {
707734
}
708735

709736
asyncfunctionencrypt(algorithm,key,data){
737+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
710738
returncipherOrWrap(kWebCryptoCipherEncrypt,algorithm,key,data,'encrypt');
711739
}
712740

713741
asyncfunctiondecrypt(algorithm,key,data){
742+
if(this!==subtle)thrownewERR_INVALID_THIS('SubtleCrypto');
714743
returncipherOrWrap(kWebCryptoCipherDecrypt,algorithm,key,data,'decrypt');
715744
}
716745

717746
// The SubtleCrypto and Crypto classes are defined as part of the
718747
// Web Crypto API standard: https://www.w3.org/TR/WebCryptoAPI/
719748

720-
classSubtleCrypto{}
721-
constsubtle=newSubtleCrypto();
749+
classSubtleCrypto{
750+
constructor(){
751+
thrownewERR_ILLEGAL_CONSTRUCTOR();
752+
}
753+
}
754+
constsubtle=ReflectConstruct(function(){},[],SubtleCrypto);
722755

723756
classCrypto{
757+
constructor(){
758+
thrownewERR_ILLEGAL_CONSTRUCTOR();
759+
}
760+
724761
getsubtle(){
762+
if(this!==crypto)thrownewERR_INVALID_THIS('Crypto');
725763
returnsubtle;
726764
}
727765
}
728-
constcrypto=newCrypto();
766+
constcrypto=ReflectConstruct(function(){},[],Crypto);
729767

730768
functiongetRandomValues(array){
731-
if(!(thisinstanceofCrypto)){
732-
thrownewERR_INVALID_THIS('Crypto');
733-
}
769+
if(this!==crypto)thrownewERR_INVALID_THIS('Crypto');
734770
returnReflectApply(_getRandomValues,this,arguments);
735771
}
736772

@@ -799,7 +835,7 @@ ObjectDefineProperties(
799835
enumerable: true,
800836
configurable: true,
801837
writable: true,
802-
value: asyncDigest,
838+
value: digest,
803839
},
804840
generateKey: {
805841
enumerable: true,
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Flags: --experimental-global-webcrypto
2+
'use strict';
3+
4+
constcommon=require('../common');
5+
6+
if(!common.hasCrypto)
7+
common.skip('missing crypto');
8+
9+
constassert=require('assert');
10+
11+
// Test CryptoKey constructor
12+
{
13+
assert.throws(()=>newCryptoKey(),{
14+
name: 'TypeError',message: 'Illegal constructor',code: 'ERR_ILLEGAL_CONSTRUCTOR'
15+
});
16+
}
17+
18+
// Test SubtleCrypto constructor
19+
{
20+
assert.throws(()=>newSubtleCrypto(),{
21+
name: 'TypeError',message: 'Illegal constructor',code: 'ERR_ILLEGAL_CONSTRUCTOR'
22+
});
23+
}
24+
25+
// Test Crypto constructor
26+
{
27+
assert.throws(()=>newCrypto(),{
28+
name: 'TypeError',message: 'Illegal constructor',code: 'ERR_ILLEGAL_CONSTRUCTOR'
29+
});
30+
}
31+
32+
constnotCrypto=Reflect.construct(function(){},[],Crypto);
33+
constnotSubtle=Reflect.construct(function(){},[],SubtleCrypto);
34+
35+
// Test Crypto.prototype.subtle
36+
{
37+
assert.throws(()=>notCrypto.subtle,{
38+
name: 'TypeError',code: 'ERR_INVALID_THIS',
39+
});
40+
}
41+
42+
// Test Crypto.prototype.randomUUID
43+
{
44+
assert.throws(()=>notCrypto.randomUUID(),{
45+
name: 'TypeError',code: 'ERR_INVALID_THIS',
46+
});
47+
}
48+
49+
// Test Crypto.prototype.getRandomValues
50+
{
51+
assert.throws(()=>notCrypto.getRandomValues(newUint8Array(12)),{
52+
name: 'TypeError',code: 'ERR_INVALID_THIS',
53+
});
54+
}
55+
56+
// Test SubtleCrypto.prototype.encrypt
57+
{
58+
assert.rejects(()=>notSubtle.encrypt(),{
59+
name: 'TypeError',code: 'ERR_INVALID_THIS',
60+
}).then(common.mustCall());
61+
}
62+
63+
// Test SubtleCrypto.prototype.decrypt
64+
{
65+
assert.rejects(()=>notSubtle.decrypt(),{
66+
name: 'TypeError',code: 'ERR_INVALID_THIS',
67+
}).then(common.mustCall());
68+
}
69+
70+
// Test SubtleCrypto.prototype.sign
71+
{
72+
assert.rejects(()=>notSubtle.sign(),{
73+
name: 'TypeError',code: 'ERR_INVALID_THIS',
74+
}).then(common.mustCall());
75+
}
76+
77+
// Test SubtleCrypto.prototype.verify
78+
{
79+
assert.rejects(()=>notSubtle.verify(),{
80+
name: 'TypeError',code: 'ERR_INVALID_THIS',
81+
}).then(common.mustCall());
82+
}
83+
84+
// Test SubtleCrypto.prototype.digest
85+
{
86+
assert.rejects(()=>notSubtle.digest(),{
87+
name: 'TypeError',code: 'ERR_INVALID_THIS',
88+
}).then(common.mustCall());
89+
}
90+
91+
// Test SubtleCrypto.prototype.generateKey
92+
{
93+
assert.rejects(()=>notSubtle.generateKey(),{
94+
name: 'TypeError',code: 'ERR_INVALID_THIS',
95+
}).then(common.mustCall());
96+
}
97+
98+
// Test SubtleCrypto.prototype.deriveKey
99+
{
100+
assert.rejects(()=>notSubtle.deriveKey(),{
101+
name: 'TypeError',code: 'ERR_INVALID_THIS',
102+
}).then(common.mustCall());
103+
}
104+
105+
// Test SubtleCrypto.prototype.deriveBits
106+
{
107+
assert.rejects(()=>notSubtle.deriveBits(),{
108+
name: 'TypeError',code: 'ERR_INVALID_THIS',
109+
}).then(common.mustCall());
110+
}
111+
112+
// Test SubtleCrypto.prototype.importKey
113+
{
114+
assert.rejects(()=>notSubtle.importKey(),{
115+
name: 'TypeError',code: 'ERR_INVALID_THIS',
116+
}).then(common.mustCall());
117+
}
118+
119+
// Test SubtleCrypto.prototype.exportKey
120+
{
121+
assert.rejects(()=>notSubtle.exportKey(),{
122+
name: 'TypeError',code: 'ERR_INVALID_THIS',
123+
}).then(common.mustCall());
124+
}
125+
126+
// Test SubtleCrypto.prototype.wrapKey
127+
{
128+
assert.rejects(()=>notSubtle.wrapKey(),{
129+
name: 'TypeError',code: 'ERR_INVALID_THIS',
130+
}).then(common.mustCall());
131+
}
132+
133+
// Test SubtleCrypto.prototype.unwrapKey
134+
{
135+
assert.rejects(()=>notSubtle.unwrapKey(),{
136+
name: 'TypeError',code: 'ERR_INVALID_THIS',
137+
}).then(common.mustCall());
138+
}
139+
140+
{
141+
globalThis.crypto.subtle.importKey(
142+
'raw',
143+
globalThis.crypto.getRandomValues(newUint8Array(4)),
144+
'PBKDF2',
145+
false,
146+
['deriveKey'],
147+
).then((key)=>{
148+
globalThis.crypto.subtle.importKey=common.mustNotCall();
149+
returnglobalThis.crypto.subtle.deriveKey({
150+
name: 'PBKDF2',
151+
hash: 'SHA-512',
152+
salt: globalThis.crypto.getRandomValues(newUint8Array()),
153+
iterations: 5,
154+
},key,{
155+
name: 'AES-GCM',
156+
length: 256
157+
},true,['encrypt','decrypt']);
158+
}).then(common.mustCall());
159+
}

0 commit comments

Comments
 (0)