Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v16.x backport] WebCryptoAPI fixes #47336

Closed
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/internal/crypto/aes.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ function asyncAesCtrCipher(mode, key, data, { counter, length }) {
'OperationError');
}

return jobPromise(new AESCipherJob(
return jobPromise(() => new AESCipherJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
Expand All @@ -137,7 +137,7 @@ function asyncAesCtrCipher(mode, key, data, { counter, length }) {
function asyncAesCbcCipher(mode, key, data, { iv }) {
iv = getArrayBufferOrView(iv, 'algorithm.iv');
validateByteLength(iv, 'algorithm.iv', 16);
return jobPromise(new AESCipherJob(
return jobPromise(() => new AESCipherJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
Expand All @@ -147,7 +147,7 @@ function asyncAesCbcCipher(mode, key, data, { iv }) {
}

function asyncAesKwCipher(mode, key, data) {
return jobPromise(new AESCipherJob(
return jobPromise(() => new AESCipherJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
Expand Down Expand Up @@ -201,7 +201,7 @@ function asyncAesGcmCipher(
break;
}

return jobPromise(new AESCipherJob(
return jobPromise(() => new AESCipherJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
Expand Down
77 changes: 55 additions & 22 deletions lib/internal/crypto/cfrg.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ function verifyAcceptableCfrgKeyUse(name, type, usages) {
case 'X25519':
// Fall through
case 'X448':
checkSet = ['deriveKey', 'deriveBits'];
switch (type) {
case 'private':
checkSet = ['deriveKey', 'deriveBits'];
break;
case 'public':
checkSet = [];
break;
}
break;
case 'Ed25519':
// Fall through
Expand Down Expand Up @@ -83,26 +90,26 @@ function createCFRGRawKey(name, keyData, isPublic) {
case 'X25519':
if (keyData.byteLength !== 32) {
throw lazyDOMException(
`${name} raw keys must be exactly 32-bytes`);
`${name} raw keys must be exactly 32-bytes`, 'DataError');
}
break;
case 'Ed448':
if (keyData.byteLength !== 57) {
throw lazyDOMException(
`${name} raw keys must be exactly 57-bytes`);
`${name} raw keys must be exactly 57-bytes`, 'DataError');
}
break;
case 'X448':
if (keyData.byteLength !== 56) {
throw lazyDOMException(
`${name} raw keys must be exactly 56-bytes`);
`${name} raw keys must be exactly 56-bytes`, 'DataError');
}
break;
}

const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate;
if (!handle.initEDRaw(name, keyData, keyType)) {
throw lazyDOMException('Failure to generate key object');
throw lazyDOMException('Invalid keyData', 'DataError');
}

return isPublic ? new PublicKeyObject(handle) : new PrivateKeyObject(handle);
Expand Down Expand Up @@ -194,7 +201,7 @@ async function cfrgGenerateKey(algorithm, extractable, keyUsages) {

function cfrgExportKey(key, format) {
emitExperimentalWarning(`The ${key.algorithm.name} Web Crypto API algorithm`);
return jobPromise(new ECKeyExportJob(
return jobPromise(() => new ECKeyExportJob(
kCryptoJobAsync,
format,
key[kKeyObject][kHandle]));
Expand All @@ -214,20 +221,30 @@ async function cfrgImportKey(
switch (format) {
case 'spki': {
verifyAcceptableCfrgKeyUse(name, 'public', usagesSet);
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
try {
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
} catch {
throw lazyDOMException(
'Invalid keyData', 'DataError');
}
break;
}
case 'pkcs8': {
verifyAcceptableCfrgKeyUse(name, 'private', usagesSet);
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
try {
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
} catch {
throw lazyDOMException(
'Invalid keyData', 'DataError');
}
break;
}
case 'jwk': {
Expand Down Expand Up @@ -276,16 +293,32 @@ async function cfrgImportKey(
}
}

if (!isPublic && typeof keyData.x !== 'string') {
throw lazyDOMException('Invalid JWK keyData', 'DataError');
}

verifyAcceptableCfrgKeyUse(
name,
isPublic ? 'public' : 'private',
usagesSet);
keyObject = createCFRGRawKey(

const publicKeyObject = createCFRGRawKey(
name,
Buffer.from(
isPublic ? keyData.x : keyData.d,
'base64'),
isPublic);
Buffer.from(keyData.x, 'base64'),
true);

if (isPublic) {
keyObject = publicKeyObject;
} else {
keyObject = createCFRGRawKey(
name,
Buffer.from(keyData.d, 'base64'),
false);

if (!createPublicKey(keyObject).equals(publicKeyObject)) {
throw lazyDOMException('Invalid JWK keyData', 'DataError');
}
}
break;
}
case 'raw': {
Expand Down Expand Up @@ -323,7 +356,7 @@ function eddsaSignVerify(key, data, { name, context }, signature) {
}
}

return jobPromise(new SignJob(
return jobPromise(() => new SignJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
Expand Down
6 changes: 5 additions & 1 deletion lib/internal/crypto/diffiehellman.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,11 @@ async function asyncDeriveBitsECDH(algorithm, baseKey, length) {
key.algorithm.name === 'ECDH' ? baseKey.algorithm.namedCurve : baseKey.algorithm.name,
key[kKeyObject][kHandle],
baseKey[kKeyObject][kHandle], (err, bits) => {
if (err) return reject(err);
if (err) {
return reject(lazyDOMException(
'The operation failed for an operation-specific reason',
'OperationError'));
}
resolve(bits);
});
});
Expand Down
53 changes: 36 additions & 17 deletions lib/internal/crypto/ec.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ function verifyAcceptableEcKeyUse(name, type, usages) {
let checkSet;
switch (name) {
case 'ECDH':
checkSet = ['deriveKey', 'deriveBits'];
switch (type) {
case 'private':
checkSet = ['deriveKey', 'deriveBits'];
break;
case 'public':
checkSet = [];
break;
}
break;
case 'ECDSA':
switch (type) {
Expand All @@ -80,8 +87,12 @@ function verifyAcceptableEcKeyUse(name, type, usages) {
function createECPublicKeyRaw(namedCurve, keyData) {
const handle = new KeyObjectHandle();
keyData = getArrayBufferOrView(keyData, 'keyData');
if (handle.initECRaw(kNamedCurveAliases[namedCurve], keyData))
return new PublicKeyObject(handle);

if (!handle.initECRaw(kNamedCurveAliases[namedCurve], keyData)) {
throw lazyDOMException('Invalid keyData', 'DataError');
}

return new PublicKeyObject(handle);
}

async function ecGenerateKey(algorithm, extractable, keyUsages) {
Expand Down Expand Up @@ -151,7 +162,7 @@ async function ecGenerateKey(algorithm, extractable, keyUsages) {
}

function ecExportKey(key, format) {
return jobPromise(new ECKeyExportJob(
return jobPromise(() => new ECKeyExportJob(
kCryptoJobAsync,
format,
key[kKeyObject][kHandle]));
Expand All @@ -177,20 +188,30 @@ async function ecImportKey(
switch (format) {
case 'spki': {
verifyAcceptableEcKeyUse(name, 'public', usagesSet);
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
try {
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
} catch {
throw lazyDOMException(
'Invalid keyData', 'DataError');
}
break;
}
case 'pkcs8': {
verifyAcceptableEcKeyUse(name, 'private', usagesSet);
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
try {
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
} catch {
throw lazyDOMException(
'Invalid keyData', 'DataError');
}
break;
}
case 'jwk': {
Expand Down Expand Up @@ -247,8 +268,6 @@ async function ecImportKey(
case 'raw': {
verifyAcceptableEcKeyUse(name, 'public', usagesSet);
keyObject = createECPublicKeyRaw(namedCurve, keyData);
if (keyObject === undefined)
throw lazyDOMException('Unable to import EC key', 'OperationError');
break;
}
}
Expand Down Expand Up @@ -286,7 +305,7 @@ function ecdsaSignVerify(key, data, { name, hash }, signature) {
throw new ERR_MISSING_OPTION('algorithm.hash');
const hashname = normalizeHashName(hash.name);

return jobPromise(new SignJob(
return jobPromise(() => new SignJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
Expand Down
2 changes: 1 addition & 1 deletion lib/internal/crypto/hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ async function asyncDigest(algorithm, data) {
case 'SHA-384':
// Fall through
case 'SHA-512':
return jobPromise(new HashJob(
return jobPromise(() => new HashJob(
kCryptoJobAsync,
normalizeHashName(algorithm.name),
data,
Expand Down
13 changes: 13 additions & 0 deletions lib/internal/crypto/keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const {
ObjectDefineProperty,
ObjectSetPrototypeOf,
Symbol,
SymbolToStringTag,
Uint8Array,
} = primordials;

Expand Down Expand Up @@ -136,6 +137,12 @@ const {
}
}

ObjectDefineProperty(KeyObject.prototype, SymbolToStringTag, {
__proto__: null,
configurable: true,
value: 'KeyObject',
});

class SecretKeyObject extends KeyObject {
constructor(handle) {
super('secret', handle);
Expand Down Expand Up @@ -697,6 +704,12 @@ class CryptoKey extends JSTransferable {
}
}

ObjectDefineProperty(CryptoKey.prototype, SymbolToStringTag, {
__proto__: null,
configurable: true,
value: 'CryptoKey',
});

// All internal code must use new InternalCryptoKey to create
// CryptoKey instances. The CryptoKey class is exposed to end
// user code but is not permitted to be constructed directly.
Expand Down
6 changes: 3 additions & 3 deletions lib/internal/crypto/mac.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const {
} = internalBinding('crypto');

const {
getHashLength,
getBlockSize,
hasAnyNotIn,
jobPromise,
normalizeHashName,
Expand Down Expand Up @@ -54,7 +54,7 @@ async function hmacGenerateKey(algorithm, extractable, keyUsages) {
throw new ERR_MISSING_OPTION('algorithm.hash');

if (length === undefined)
length = getHashLength(hash.name);
length = getBlockSize(hash.name);

validateBitLength(length, 'algorithm.length', true);

Expand Down Expand Up @@ -184,7 +184,7 @@ async function hmacImportKey(

function hmacSignVerify(key, data, algorithm, signature) {
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
return jobPromise(new HmacJob(
return jobPromise(() => new HmacJob(
kCryptoJobAsync,
mode,
normalizeHashName(key.algorithm.hash.name),
Expand Down
Loading