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

crypto: update WebCryptoAPI WPT and related fixes #45569

Closed
wants to merge 8 commits into from
89 changes: 56 additions & 33 deletions lib/internal/crypto/cfrg.js
Expand Up @@ -47,25 +47,22 @@ const {

const generateKeyPair = promisify(_generateKeyPair);

function verifyAcceptableCfrgKeyUse(name, type, usages) {
function verifyAcceptableCfrgKeyUse(name, isPublic, usages) {
let checkSet;
switch (name) {
case 'X25519':
// Fall through
case 'X448':
checkSet = ['deriveKey', 'deriveBits'];
checkSet = isPublic ? [] : ['deriveKey', 'deriveBits'];
break;
case 'Ed25519':
// Fall through
case 'Ed448':
switch (type) {
case 'private':
checkSet = ['sign'];
break;
case 'public':
checkSet = ['verify'];
break;
}
checkSet = isPublic ? ['verify'] : ['sign'];
break;
default:
throw lazyDOMException(
'The algorithm is not supported', 'NotSupportedError');
}
if (hasAnyNotIn(usages, checkSet)) {
throw lazyDOMException(
Expand All @@ -83,26 +80,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 @@ -212,21 +209,31 @@ async function cfrgImportKey(
const usagesSet = new SafeSet(keyUsages);
switch (format) {
case 'spki': {
verifyAcceptableCfrgKeyUse(name, 'public', usagesSet);
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
verifyAcceptableCfrgKeyUse(name, true, usagesSet);
try {
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
} catch (err) {
throw lazyDOMException(
'Invalid keyData', { name: 'DataError', cause: err });
}
break;
}
case 'pkcs8': {
verifyAcceptableCfrgKeyUse(name, 'private', usagesSet);
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
verifyAcceptableCfrgKeyUse(name, false, usagesSet);
try {
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
} catch (err) {
throw lazyDOMException(
'Invalid keyData', { name: 'DataError', cause: err });
}
break;
}
case 'jwk': {
Expand Down Expand Up @@ -275,20 +282,36 @@ async function cfrgImportKey(
}
}

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

verifyAcceptableCfrgKeyUse(
name,
isPublic ? 'public' : 'private',
isPublic,
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)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might find a faster solution for this in the future :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping you'd say that ;)

throw lazyDOMException('Invalid JWK keyData', 'DataError');
}
}
break;
}
case 'raw': {
verifyAcceptableCfrgKeyUse(name, 'public', usagesSet);
verifyAcceptableCfrgKeyUse(name, true, usagesSet);
keyObject = createCFRGRawKey(name, keyData, true);
break;
}
Expand Down
72 changes: 40 additions & 32 deletions lib/internal/crypto/ec.js
Expand Up @@ -54,21 +54,18 @@ const {

const generateKeyPair = promisify(_generateKeyPair);

function verifyAcceptableEcKeyUse(name, type, usages) {
function verifyAcceptableEcKeyUse(name, isPublic, usages) {
let checkSet;
switch (name) {
case 'ECDH':
checkSet = ['deriveKey', 'deriveBits'];
checkSet = isPublic ? [] : ['deriveKey', 'deriveBits'];
break;
case 'ECDSA':
switch (type) {
case 'private':
checkSet = ['sign'];
break;
case 'public':
checkSet = ['verify'];
break;
}
checkSet = isPublic ? ['verify'] : ['sign'];
break;
default:
throw lazyDOMException(
'The algorithm is not supported', 'NotSupportedError');
}
if (hasAnyNotIn(usages, checkSet)) {
throw lazyDOMException(
Expand All @@ -80,8 +77,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 @@ -175,21 +176,31 @@ async function ecImportKey(
const usagesSet = new SafeSet(keyUsages);
switch (format) {
case 'spki': {
verifyAcceptableEcKeyUse(name, 'public', usagesSet);
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
verifyAcceptableEcKeyUse(name, true, usagesSet);
try {
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
} catch (err) {
throw lazyDOMException(
'Invalid keyData', { name: 'DataError', cause: err });
}
break;
}
case 'pkcs8': {
verifyAcceptableEcKeyUse(name, 'private', usagesSet);
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
verifyAcceptableEcKeyUse(name, false, usagesSet);
try {
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
} catch (err) {
throw lazyDOMException(
'Invalid keyData', { name: 'DataError', cause: err });
}
break;
}
case 'jwk': {
Expand All @@ -200,11 +211,10 @@ async function ecImportKey(
if (keyData.crv !== namedCurve)
throw lazyDOMException('Named curve mismatch', 'DataError');

if (keyData.d !== undefined) {
verifyAcceptableEcKeyUse(name, 'private', usagesSet);
} else {
verifyAcceptableEcKeyUse(name, 'public', usagesSet);
}
verifyAcceptableEcKeyUse(
name,
keyData.d === undefined,
usagesSet);

if (usagesSet.size > 0 && keyData.use !== undefined) {
if (algorithm.name === 'ECDSA' && keyData.use !== 'sig')
Expand Down Expand Up @@ -244,10 +254,8 @@ async function ecImportKey(
break;
}
case 'raw': {
verifyAcceptableEcKeyUse(name, 'public', usagesSet);
verifyAcceptableEcKeyUse(name, true, usagesSet);
keyObject = createECPublicKeyRaw(namedCurve, keyData);
if (keyObject === undefined)
throw lazyDOMException('Unable to import EC key', 'OperationError');
break;
}
}
Expand Down
62 changes: 32 additions & 30 deletions lib/internal/crypto/rsa.js
Expand Up @@ -74,28 +74,20 @@ const kRsaVariants = {
};
const generateKeyPair = promisify(_generateKeyPair);

function verifyAcceptableRsaKeyUse(name, type, usages) {
function verifyAcceptableRsaKeyUse(name, isPublic, usages) {
let checkSet;
switch (name) {
case 'RSA-OAEP':
switch (type) {
case 'private':
checkSet = ['decrypt', 'unwrapKey'];
break;
case 'public':
checkSet = ['encrypt', 'wrapKey'];
break;
}
checkSet = isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'];
break;
case 'RSA-PSS':
// Fall through
case 'RSASSA-PKCS1-v1_5':
checkSet = isPublic ? ['verify'] : ['sign'];
break;
default:
switch (type) {
case 'private':
checkSet = ['sign'];
break;
case 'public':
checkSet = ['verify'];
break;
}
throw lazyDOMException(
'The algorithm is not supported', 'NotSupportedError');
}
if (hasAnyNotIn(usages, checkSet)) {
throw lazyDOMException(
Expand Down Expand Up @@ -244,21 +236,31 @@ async function rsaImportKey(
let keyObject;
switch (format) {
case 'spki': {
verifyAcceptableRsaKeyUse(algorithm.name, 'public', usagesSet);
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
verifyAcceptableRsaKeyUse(algorithm.name, true, usagesSet);
try {
keyObject = createPublicKey({
key: keyData,
format: 'der',
type: 'spki'
});
} catch (err) {
throw lazyDOMException(
'Invalid keyData', { name: 'DataError', cause: err });
}
break;
}
case 'pkcs8': {
verifyAcceptableRsaKeyUse(algorithm.name, 'private', usagesSet);
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
verifyAcceptableRsaKeyUse(algorithm.name, false, usagesSet);
try {
keyObject = createPrivateKey({
key: keyData,
format: 'der',
type: 'pkcs8'
});
} catch (err) {
throw lazyDOMException(
'Invalid keyData', { name: 'DataError', cause: err });
}
break;
}
case 'jwk': {
Expand All @@ -267,7 +269,7 @@ async function rsaImportKey(

verifyAcceptableRsaKeyUse(
algorithm.name,
keyData.d !== undefined ? 'private' : 'public',
keyData.d === undefined,
usagesSet);

if (keyData.kty !== 'RSA')
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/wpt/README.md
Expand Up @@ -32,7 +32,7 @@ Last update:
- user-timing: https://github.com/web-platform-tests/wpt/tree/df24fb604e/user-timing
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/d8dbe6990b/wasm/jsapi
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/0042d42ee6/WebCryptoAPI
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/21ccdcd814/WebCryptoAPI
- webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions

[Web Platform Tests]: https://github.com/web-platform-tests/wpt
Expand Down