Skip to content

Commit

Permalink
crypto: validate this value for webcrypto.getRandomValues
Browse files Browse the repository at this point in the history
PR-URL: nodejs#41760
Reviewed-By: Mestery <mestery@protonmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
  • Loading branch information
aduh95 authored and xtx1130 committed Apr 25, 2022
1 parent 67f8773 commit c3043f8
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 36 deletions.
8 changes: 3 additions & 5 deletions benchmark/crypto/webcrypto-digest.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
const common = require('../common.js');
const {
createHash,
webcrypto: {
subtle,
getRandomValues
}
webcrypto,
} = require('crypto');
const { subtle } = webcrypto;

const bench = common.createBenchmark(main, {
sync: ['createHash', 'subtle'],
Expand Down Expand Up @@ -50,7 +48,7 @@ function measureSubtle(n, data, method) {
}

function main({ n, sync, data, method }) {
data = getRandomValues(Buffer.alloc(data));
data = webcrypto.getRandomValues(Buffer.alloc(data));
switch (sync) {
case 'createHash': return measureLegacy(n, data, method);
case 'subtle': return measureSubtle(n, data, method);
Expand Down
8 changes: 4 additions & 4 deletions doc/api/webcrypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,14 @@ async function generateRsaKey(modulusLength = 2048, hash = 'SHA-256') {
### Encryption and decryption

```js
const { subtle, getRandomValues } = require('crypto').webcrypto;
const crypto = require('crypto').webcrypto;

async function aesEncrypt(plaintext) {
const ec = new TextEncoder();
const key = await generateAesKey();
const iv = getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(16));

const ciphertext = await subtle.encrypt({
const ciphertext = await crypto.subtle.encrypt({
name: 'AES-CBC',
iv,
}, key, ec.encode(plaintext));
Expand All @@ -149,7 +149,7 @@ async function aesEncrypt(plaintext) {

async function aesDecrypt(ciphertext, key, iv) {
const dec = new TextDecoder();
const plaintext = await subtle.decrypt({
const plaintext = await crypto.subtle.decrypt({
name: 'AES-CBC',
iv,
}, key, ciphertext);
Expand Down
11 changes: 10 additions & 1 deletion lib/internal/crypto/webcrypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const {
JSONParse,
JSONStringify,
ObjectDefineProperties,
ReflectApply,
SafeSet,
SymbolToStringTag,
StringPrototypeRepeat,
Expand All @@ -31,6 +32,7 @@ const { TextDecoder, TextEncoder } = require('internal/encoding');
const {
codes: {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_THIS,
}
} = require('internal/errors');

Expand Down Expand Up @@ -64,7 +66,7 @@ const {
} = require('internal/util');

const {
getRandomValues,
getRandomValues: _getRandomValues,
randomUUID: _randomUUID,
} = require('internal/crypto/random');

Expand Down Expand Up @@ -695,6 +697,13 @@ class Crypto {
}
const crypto = new Crypto();

function getRandomValues(array) {
if (!(this instanceof Crypto)) {
throw new ERR_INVALID_THIS('Crypto');
}
return ReflectApply(_getRandomValues, this, arguments);
}

ObjectDefineProperties(
Crypto.prototype, {
[SymbolToStringTag]: {
Expand Down
5 changes: 3 additions & 2 deletions test/parallel/test-webcrypto-derivebits-ecdh.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const { subtle, getRandomValues } = require('crypto').webcrypto;
const { webcrypto } = require('crypto');
const { subtle } = webcrypto;

const kTests = [
{
Expand Down Expand Up @@ -250,7 +251,7 @@ async function prepareKeys() {

{
// Public is a secret key
const keyData = getRandomValues(new Uint8Array(32));
const keyData = webcrypto.getRandomValues(new Uint8Array(32));
const key = await subtle.importKey(
'raw',
keyData,
Expand Down
5 changes: 3 additions & 2 deletions test/parallel/test-webcrypto-derivekey-ecdh.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const { subtle, getRandomValues } = require('crypto').webcrypto;
const { webcrypto } = require('crypto');
const { subtle } = webcrypto;

const kTests = [
{
Expand Down Expand Up @@ -226,7 +227,7 @@ async function prepareKeys() {

{
// Public is a secret key
const keyData = getRandomValues(new Uint8Array(32));
const keyData = webcrypto.getRandomValues(new Uint8Array(32));
const key = await subtle.importKey(
'raw',
keyData,
Expand Down
9 changes: 5 additions & 4 deletions test/parallel/test-webcrypto-encrypt-decrypt-aes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const { getRandomValues, subtle } = require('crypto').webcrypto;
const { webcrypto } = require('crypto');
const { subtle } = webcrypto;

async function testEncrypt({ keyBuffer, algorithm, plaintext, result }) {
// Using a copy of plaintext to prevent tampering of the original
Expand Down Expand Up @@ -213,8 +214,8 @@ async function testDecrypt({ keyBuffer, algorithm, result }) {
['encrypt', 'decrypt'],
);

const iv = getRandomValues(new Uint8Array(12));
const aad = getRandomValues(new Uint8Array(32));
const iv = webcrypto.getRandomValues(new Uint8Array(12));
const aad = webcrypto.getRandomValues(new Uint8Array(32));

const encrypted = await subtle.encrypt(
{
Expand All @@ -224,7 +225,7 @@ async function testDecrypt({ keyBuffer, algorithm, result }) {
tagLength: 128
},
secretKey,
getRandomValues(new Uint8Array(32))
webcrypto.getRandomValues(new Uint8Array(32))
);

await subtle.decrypt(
Expand Down
17 changes: 9 additions & 8 deletions test/parallel/test-webcrypto-encrypt-decrypt.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const { subtle, getRandomValues } = require('crypto').webcrypto;
const { webcrypto } = require('crypto');
const { subtle } = webcrypto;

// This is only a partial test. The WebCrypto Web Platform Tests
// will provide much greater coverage.

// Test Encrypt/Decrypt RSA-OAEP
{
const buf = getRandomValues(new Uint8Array(50));
const buf = webcrypto.getRandomValues(new Uint8Array(50));

async function test() {
const ec = new TextEncoder();
Expand Down Expand Up @@ -44,8 +45,8 @@ const { subtle, getRandomValues } = require('crypto').webcrypto;

// Test Encrypt/Decrypt AES-CTR
{
const buf = getRandomValues(new Uint8Array(50));
const counter = getRandomValues(new Uint8Array(16));
const buf = webcrypto.getRandomValues(new Uint8Array(50));
const counter = webcrypto.getRandomValues(new Uint8Array(16));

async function test() {
const key = await subtle.generateKey({
Expand All @@ -71,8 +72,8 @@ const { subtle, getRandomValues } = require('crypto').webcrypto;

// Test Encrypt/Decrypt AES-CBC
{
const buf = getRandomValues(new Uint8Array(50));
const iv = getRandomValues(new Uint8Array(16));
const buf = webcrypto.getRandomValues(new Uint8Array(50));
const iv = webcrypto.getRandomValues(new Uint8Array(16));

async function test() {
const key = await subtle.generateKey({
Expand All @@ -98,8 +99,8 @@ const { subtle, getRandomValues } = require('crypto').webcrypto;

// Test Encrypt/Decrypt AES-GCM
{
const buf = getRandomValues(new Uint8Array(50));
const iv = getRandomValues(new Uint8Array(12));
const buf = webcrypto.getRandomValues(new Uint8Array(50));
const iv = webcrypto.getRandomValues(new Uint8Array(12));

async function test() {
const key = await subtle.generateKey({
Expand Down
9 changes: 5 additions & 4 deletions test/parallel/test-webcrypto-export-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const { subtle, getRandomValues } = require('crypto').webcrypto;
const { webcrypto } = require('crypto');
const { subtle } = webcrypto;

{
async function test() {
const keyData = getRandomValues(new Uint8Array(32));
const keyData = webcrypto.getRandomValues(new Uint8Array(32));
await Promise.all([1, null, undefined, {}, []].map((format) =>
assert.rejects(
subtle.importKey(format, keyData, {}, false, ['wrapKey']), {
Expand Down Expand Up @@ -82,7 +83,7 @@ const { subtle, getRandomValues } = require('crypto').webcrypto;
// Import/Export HMAC Secret Key
{
async function test() {
const keyData = getRandomValues(new Uint8Array(32));
const keyData = webcrypto.getRandomValues(new Uint8Array(32));
const key = await subtle.importKey(
'raw',
keyData, {
Expand Down Expand Up @@ -112,7 +113,7 @@ const { subtle, getRandomValues } = require('crypto').webcrypto;
// Import/Export AES Secret Key
{
async function test() {
const keyData = getRandomValues(new Uint8Array(32));
const keyData = webcrypto.getRandomValues(new Uint8Array(32));
const key = await subtle.importKey(
'raw',
keyData, {
Expand Down
11 changes: 11 additions & 0 deletions test/parallel/test-webcrypto-getRandomValues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

const common = require('../common');

if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const { getRandomValues } = require('crypto').webcrypto;

assert.throws(() => getRandomValues(new Uint8Array()), { code: 'ERR_INVALID_THIS' });
12 changes: 6 additions & 6 deletions test/parallel/test-webcrypto-random.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ if (!common.hasCrypto)

const { Buffer } = require('buffer');
const assert = require('assert');
const { getRandomValues } = require('crypto').webcrypto;
const { webcrypto } = require('crypto');

[
undefined, null, '', 1, {}, [],
Expand All @@ -16,14 +16,14 @@ const { getRandomValues } = require('crypto').webcrypto;
new DataView(new ArrayBuffer(1)),
].forEach((i) => {
assert.throws(
() => getRandomValues(i),
() => webcrypto.getRandomValues(i),
{ name: 'TypeMismatchError', code: 17 },
);
});

{
const buf = new Uint8Array(0);
getRandomValues(buf);
webcrypto.getRandomValues(buf);
}

const intTypedConstructors = [
Expand All @@ -41,15 +41,15 @@ const intTypedConstructors = [
for (const ctor of intTypedConstructors) {
const buf = new ctor(10);
const before = Buffer.from(buf.buffer).toString('hex');
getRandomValues(buf);
webcrypto.getRandomValues(buf);
const after = Buffer.from(buf.buffer).toString('hex');
assert.notStrictEqual(before, after);
}

{
const buf = new Uint16Array(10);
const before = Buffer.from(buf).toString('hex');
getRandomValues(buf);
webcrypto.getRandomValues(buf);
const after = Buffer.from(buf).toString('hex');
assert.notStrictEqual(before, after);
}
Expand All @@ -63,7 +63,7 @@ for (const ctor of intTypedConstructors) {
}

if (kData !== undefined) {
assert.throws(() => getRandomValues(kData), {
assert.throws(() => webcrypto.getRandomValues(kData), {
code: 22
});
}
Expand Down

0 comments on commit c3043f8

Please sign in to comment.