UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

311 lines (276 loc) 8.16 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/crypto/ec.js import { ECKeyExportJob, KeyObjectHandle, SignJob, kCryptoJobAsync, kKeyTypePrivate, kSignJobModeSign, kSignJobModeVerify, kSigEncP1363, } from "nstdlib/stub/binding/crypto"; import { getUsagesUnion, hasAnyNotIn, jobPromise, normalizeHashName, validateKeyOps, kHandle, kKeyObject, kNamedCurveAliases, } from "nstdlib/lib/internal/crypto/util"; import { lazyDOMException, promisify } from "nstdlib/lib/internal/util"; import { generateKeyPair as _generateKeyPair } from "nstdlib/lib/internal/crypto/keygen"; import { InternalCryptoKey, PrivateKeyObject, PublicKeyObject, createPrivateKey, createPublicKey, } from "nstdlib/lib/internal/crypto/keys"; const generateKeyPair = promisify(_generateKeyPair); function verifyAcceptableEcKeyUse(name, isPublic, usages) { let checkSet; switch (name) { case "ECDH": checkSet = isPublic ? [] : ["deriveKey", "deriveBits"]; break; case "ECDSA": checkSet = isPublic ? ["verify"] : ["sign"]; break; default: throw lazyDOMException( "The algorithm is not supported", "NotSupportedError", ); } if (hasAnyNotIn(usages, checkSet)) { throw lazyDOMException( `Unsupported key usage for a ${name} key`, "SyntaxError", ); } } function createECPublicKeyRaw(namedCurve, keyData) { const handle = new KeyObjectHandle(); if (!handle.initECRaw(kNamedCurveAliases[namedCurve], keyData)) { throw lazyDOMException("Invalid keyData", "DataError"); } return new PublicKeyObject(handle); } async function ecGenerateKey(algorithm, extractable, keyUsages) { const { name, namedCurve } = algorithm; if ( !Array.prototype.includes.call(Object.keys(kNamedCurveAliases), namedCurve) ) { throw lazyDOMException("Unrecognized namedCurve", "NotSupportedError"); } const usageSet = new Set(keyUsages); switch (name) { case "ECDSA": if (hasAnyNotIn(usageSet, ["sign", "verify"])) { throw lazyDOMException( "Unsupported key usage for an ECDSA key", "SyntaxError", ); } break; case "ECDH": if (hasAnyNotIn(usageSet, ["deriveKey", "deriveBits"])) { throw lazyDOMException( "Unsupported key usage for an ECDH key", "SyntaxError", ); } // Fall through } const keypair = await generateKeyPair("ec", { namedCurve }).catch((err) => { throw lazyDOMException( "The operation failed for an operation-specific reason", { name: "OperationError", cause: err }, ); }); let publicUsages; let privateUsages; switch (name) { case "ECDSA": publicUsages = getUsagesUnion(usageSet, "verify"); privateUsages = getUsagesUnion(usageSet, "sign"); break; case "ECDH": publicUsages = []; privateUsages = getUsagesUnion(usageSet, "deriveKey", "deriveBits"); break; } const keyAlgorithm = { name, namedCurve }; const publicKey = new InternalCryptoKey( keypair.publicKey, keyAlgorithm, publicUsages, true, ); const privateKey = new InternalCryptoKey( keypair.privateKey, keyAlgorithm, privateUsages, extractable, ); return { __proto__: null, publicKey, privateKey }; } function ecExportKey(key, format) { return jobPromise( () => new ECKeyExportJob(kCryptoJobAsync, format, key[kKeyObject][kHandle]), ); } async function ecImportKey(format, keyData, algorithm, extractable, keyUsages) { const { name, namedCurve } = algorithm; if ( !Array.prototype.includes.call(Object.keys(kNamedCurveAliases), namedCurve) ) { throw lazyDOMException("Unrecognized namedCurve", "NotSupportedError"); } let keyObject; const usagesSet = new Set(keyUsages); switch (format) { case "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, false, usagesSet); try { keyObject = createPrivateKey({ key: keyData, format: "der", type: "pkcs8", }); } catch (err) { throw lazyDOMException("Invalid keyData", { name: "DataError", cause: err, }); } break; } case "jwk": { if (!keyData.kty) throw lazyDOMException("Invalid keyData", "DataError"); if (keyData.kty !== "EC") throw lazyDOMException('Invalid JWK "kty" Parameter', "DataError"); if (keyData.crv !== namedCurve) throw lazyDOMException( 'JWK "crv" does not match the requested algorithm', "DataError", ); verifyAcceptableEcKeyUse(name, keyData.d === undefined, usagesSet); if (usagesSet.size > 0 && keyData.use !== undefined) { const checkUse = name === "ECDH" ? "enc" : "sig"; if (keyData.use !== checkUse) throw lazyDOMException('Invalid JWK "use" Parameter', "DataError"); } validateKeyOps(keyData.key_ops, usagesSet); if ( keyData.ext !== undefined && keyData.ext === false && extractable === true ) { throw lazyDOMException( 'JWK "ext" Parameter and extractable mismatch', "DataError", ); } if (algorithm.name === "ECDSA" && keyData.alg !== undefined) { let algNamedCurve; switch (keyData.alg) { case "ES256": algNamedCurve = "P-256"; break; case "ES384": algNamedCurve = "P-384"; break; case "ES512": algNamedCurve = "P-521"; break; } if (algNamedCurve !== namedCurve) throw lazyDOMException( 'JWK "alg" does not match the requested algorithm', "DataError", ); } const handle = new KeyObjectHandle(); const type = handle.initJwk(keyData, namedCurve); if (type === undefined) throw lazyDOMException("Invalid JWK", "DataError"); keyObject = type === kKeyTypePrivate ? new PrivateKeyObject(handle) : new PublicKeyObject(handle); break; } case "raw": { verifyAcceptableEcKeyUse(name, true, usagesSet); keyObject = createECPublicKeyRaw(namedCurve, keyData); break; } } switch (algorithm.name) { case "ECDSA": // Fall through case "ECDH": if (keyObject.asymmetricKeyType !== "ec") throw lazyDOMException("Invalid key type", "DataError"); break; } if (!keyObject[kHandle].checkEcKeyData()) { throw lazyDOMException("Invalid keyData", "DataError"); } const { namedCurve: checkNamedCurve } = keyObject[kHandle].keyDetail({}); if (kNamedCurveAliases[namedCurve] !== checkNamedCurve) throw lazyDOMException("Named curve mismatch", "DataError"); return new InternalCryptoKey( keyObject, { name, namedCurve }, keyUsages, extractable, ); } function ecdsaSignVerify(key, data, { name, hash }, signature) { const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify; const type = mode === kSignJobModeSign ? "private" : "public"; if (key.type !== type) throw lazyDOMException(`Key must be a ${type} key`, "InvalidAccessError"); const hashname = normalizeHashName(hash.name); return jobPromise( () => new SignJob( kCryptoJobAsync, mode, key[kKeyObject][kHandle], undefined, undefined, undefined, data, hashname, undefined, // Salt length, not used with ECDSA undefined, // PSS Padding, not used with ECDSA kSigEncP1363, signature, ), ); } export { ecExportKey }; export { ecImportKey }; export { ecGenerateKey }; export { ecdsaSignVerify };