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: fix remaining WebCryptoAPI idlharness WPTs #45857

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 33 additions & 32 deletions lib/internal/crypto/keys.js
Expand Up @@ -56,7 +56,7 @@ const {
} = require('internal/util/types');

const {
JSTransferable,
makeTransferable,
kClone,
kDeserialize,
} = require('internal/worker/js_transferable');
Expand Down Expand Up @@ -630,14 +630,12 @@ function isKeyObject(obj) {
}

// Our implementation of CryptoKey is a simple wrapper around a KeyObject
// that adapts it to the standard interface. This implementation also
// extends the JSTransferable class, allowing the CryptoKey to be cloned
// to Workers.
// that adapts it to the standard interface.
// TODO(@jasnell): Embedder environments like electron may have issues
// here similar to other things like URL. A chromium provided CryptoKey
// will not be recognized as a Node.js CryptoKey, and vice versa. It
// would be fantastic if we could find a way of making those interop.
class CryptoKey extends JSTransferable {
class CryptoKey {
constructor() {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
Expand Down Expand Up @@ -682,30 +680,6 @@ class CryptoKey extends JSTransferable {
throw new ERR_INVALID_THIS('CryptoKey');
return ArrayFrom(this[kKeyUsages]);
}

[kClone]() {
const keyObject = this[kKeyObject];
const algorithm = this.algorithm;
const extractable = this.extractable;
const usages = this.usages;

return {
data: {
keyObject,
algorithm,
usages,
extractable,
},
deserializeInfo: 'internal/crypto/keys:InternalCryptoKey'
};
}

[kDeserialize]({ keyObject, algorithm, usages, extractable }) {
this[kKeyObject] = keyObject;
this[kAlgorithm] = algorithm;
this[kKeyUsages] = usages;
this[kExtractable] = extractable;
}
}

ObjectDefineProperties(CryptoKey.prototype, {
Expand All @@ -718,23 +692,50 @@ ObjectDefineProperties(CryptoKey.prototype, {
// 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.
class InternalCryptoKey extends JSTransferable {
// Using makeTransferable also allows the CryptoKey to be
// cloned to Workers.
class InternalCryptoKey {
constructor(
keyObject,
algorithm,
keyUsages,
extractable) {
super();
// Using symbol properties here currently instead of private
// properties because (for now) the performance penalty of
// private fields is still too high.
this[kKeyObject] = keyObject;
this[kAlgorithm] = algorithm;
this[kExtractable] = extractable;
this[kKeyUsages] = keyUsages;

// eslint-disable-next-line no-constructor-return
return makeTransferable(this);
}
}

[kClone]() {
const keyObject = this[kKeyObject];
const algorithm = this.algorithm;
const extractable = this.extractable;
const usages = this.usages;

return {
data: {
keyObject,
algorithm,
usages,
extractable,
},
deserializeInfo: 'internal/crypto/keys:InternalCryptoKey'
};
}

[kDeserialize]({ keyObject, algorithm, usages, extractable }) {
this[kKeyObject] = keyObject;
this[kAlgorithm] = algorithm;
this[kKeyUsages] = usages;
this[kExtractable] = extractable;
}
}
InternalCryptoKey.prototype.constructor = CryptoKey;
ObjectSetPrototypeOf(InternalCryptoKey.prototype, CryptoKey.prototype);

Expand Down
11 changes: 10 additions & 1 deletion lib/internal/process/pre_execution.js
Expand Up @@ -23,6 +23,7 @@ const {
} = require('internal/util');

const {
ERR_INVALID_THIS,
ERR_MANIFEST_ASSERT_INTEGRITY,
ERR_NO_CRYPTO,
} = require('internal/errors').codes;
Expand Down Expand Up @@ -278,7 +279,15 @@ function setupWebCrypto() {

if (internalBinding('config').hasOpenSSL) {
defineReplaceableLazyAttribute(
globalThis, 'internal/crypto/webcrypto', ['crypto'], false
globalThis,
'internal/crypto/webcrypto',
['crypto'],
false,
function cryptoThisCheck() {
if (this !== globalThis && this != null)
throw new ERR_INVALID_THIS(
'nullish or must be the global object');
}
);
exposeLazyInterfaces(
globalThis, 'internal/crypto/webcrypto',
Expand Down
5 changes: 4 additions & 1 deletion lib/internal/util.js
Expand Up @@ -552,14 +552,17 @@ function defineLazyProperties(target, id, keys, enumerable = true) {
ObjectDefineProperties(target, descriptors);
}

function defineReplaceableLazyAttribute(target, id, keys, writable = true) {
function defineReplaceableLazyAttribute(target, id, keys, writable = true, check) {
let mod;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
let value;
let setterCalled = false;

function get() {
if (check !== undefined) {
FunctionPrototypeCall(check, this);
}
if (setterCalled) {
return value;
}
Expand Down
2 changes: 1 addition & 1 deletion test/common/wpt.js
Expand Up @@ -397,7 +397,7 @@ class WPTRunner {
'CompressionStream', 'DecompressionStream',
];
if (Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO) {
lazyProperties.push('crypto');
lazyProperties.push('crypto', 'Crypto', 'CryptoKey', 'SubtleCrypto');
}
const script = lazyProperties.map((name) => `globalThis.${name};`).join('\n');
this.globalThisInitScripts.push(script);
Expand Down
10 changes: 0 additions & 10 deletions test/wpt/status/WebCryptoAPI.json
Expand Up @@ -17,15 +17,5 @@
},
"historical.any.js": {
"skip": "Not relevant in Node.js context"
},
"idlharness.https.any.js": {
"fail": {
"expected": [
"Crypto interface: existence and properties of interface object",
"CryptoKey interface: existence and properties of interface object",
"CryptoKey interface: existence and properties of interface prototype object",
"Window interface: attribute crypto"
]
}
}
}