UNPKG

nstdlib-nightly

Version:

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

343 lines (288 loc) 9.99 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/crypto/diffiehellman.js import { Buffer } from "nstdlib/lib/buffer"; import { DiffieHellman as _DiffieHellman, DiffieHellmanGroup as _DiffieHellmanGroup, ECDH as _ECDH, ECDHBitsJob, ECDHConvertKey as _ECDHConvertKey, statelessDH, kCryptoJobAsync, } from "nstdlib/stub/binding/crypto"; import { codes as __codes__ } from "nstdlib/lib/internal/errors"; import { validateInt32, validateObject, validateString, } from "nstdlib/lib/internal/validators"; import { isArrayBufferView, isAnyArrayBuffer, } from "nstdlib/lib/internal/util/types"; import { lazyDOMException } from "nstdlib/lib/internal/util"; import { KeyObject } from "nstdlib/lib/internal/crypto/keys"; import { getArrayBufferOrView, jobPromise, toBuf, kHandle, kKeyObject, } from "nstdlib/lib/internal/crypto/util"; import { crypto as __crypto__ } from "nstdlib/stub/binding/constants"; const { ERR_CRYPTO_ECDH_INVALID_FORMAT, ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY, ERR_CRYPTO_INCOMPATIBLE_KEY, ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, } = __codes__; const { POINT_CONVERSION_COMPRESSED, POINT_CONVERSION_HYBRID, POINT_CONVERSION_UNCOMPRESSED, } = __crypto__; const DH_GENERATOR = 2; function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { if (!(this instanceof DiffieHellman)) return new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding); if ( typeof sizeOrKey !== "number" && typeof sizeOrKey !== "string" && !isArrayBufferView(sizeOrKey) && !isAnyArrayBuffer(sizeOrKey) ) { throw new ERR_INVALID_ARG_TYPE( "sizeOrKey", ["number", "string", "ArrayBuffer", "Buffer", "TypedArray", "DataView"], sizeOrKey, ); } // Sizes < 0 don't make sense but they _are_ accepted (and subsequently // rejected with ERR_OSSL_BN_BITS_TOO_SMALL) by OpenSSL. The glue code // in node_crypto.cc accepts values that are IsInt32() for that reason // and that's why we do that here too. if (typeof sizeOrKey === "number") validateInt32(sizeOrKey, "sizeOrKey"); if ( keyEncoding && !Buffer.isEncoding(keyEncoding) && keyEncoding !== "buffer" ) { genEncoding = generator; generator = keyEncoding; keyEncoding = false; } if (typeof sizeOrKey !== "number") sizeOrKey = toBuf(sizeOrKey, keyEncoding); if (!generator) { generator = DH_GENERATOR; } else if (typeof generator === "number") { validateInt32(generator, "generator"); } else if (typeof generator === "string") { generator = toBuf(generator, genEncoding); } else if (!isArrayBufferView(generator) && !isAnyArrayBuffer(generator)) { throw new ERR_INVALID_ARG_TYPE( "generator", ["number", "string", "ArrayBuffer", "Buffer", "TypedArray", "DataView"], generator, ); } this[kHandle] = new _DiffieHellman(sizeOrKey, generator); Object.defineProperty(this, "verifyError", { __proto__: null, enumerable: true, value: this[kHandle].verifyError, writable: false, }); } function DiffieHellmanGroup(name) { if (!(this instanceof DiffieHellmanGroup)) return new DiffieHellmanGroup(name); this[kHandle] = new _DiffieHellmanGroup(name); Object.defineProperty(this, "verifyError", { __proto__: null, enumerable: true, value: this[kHandle].verifyError, writable: false, }); } DiffieHellmanGroup.prototype.generateKeys = DiffieHellman.prototype.generateKeys = dhGenerateKeys; function dhGenerateKeys(encoding) { const keys = this[kHandle].generateKeys(); return encode(keys, encoding); } DiffieHellmanGroup.prototype.computeSecret = DiffieHellman.prototype.computeSecret = dhComputeSecret; function dhComputeSecret(key, inEnc, outEnc) { key = getArrayBufferOrView(key, "key", inEnc); const ret = this[kHandle].computeSecret(key); if (typeof ret === "string") throw new ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY(); return encode(ret, outEnc); } DiffieHellmanGroup.prototype.getPrime = DiffieHellman.prototype.getPrime = dhGetPrime; function dhGetPrime(encoding) { const prime = this[kHandle].getPrime(); return encode(prime, encoding); } DiffieHellmanGroup.prototype.getGenerator = DiffieHellman.prototype.getGenerator = dhGetGenerator; function dhGetGenerator(encoding) { const generator = this[kHandle].getGenerator(); return encode(generator, encoding); } DiffieHellmanGroup.prototype.getPublicKey = DiffieHellman.prototype.getPublicKey = dhGetPublicKey; function dhGetPublicKey(encoding) { const key = this[kHandle].getPublicKey(); return encode(key, encoding); } DiffieHellmanGroup.prototype.getPrivateKey = DiffieHellman.prototype.getPrivateKey = dhGetPrivateKey; function dhGetPrivateKey(encoding) { const key = this[kHandle].getPrivateKey(); return encode(key, encoding); } DiffieHellman.prototype.setPublicKey = function setPublicKey(key, encoding) { key = getArrayBufferOrView(key, "key", encoding); this[kHandle].setPublicKey(key); return this; }; DiffieHellman.prototype.setPrivateKey = function setPrivateKey(key, encoding) { key = getArrayBufferOrView(key, "key", encoding); this[kHandle].setPrivateKey(key); return this; }; function ECDH(curve) { if (!(this instanceof ECDH)) return new ECDH(curve); validateString(curve, "curve"); this[kHandle] = new _ECDH(curve); } ECDH.prototype.computeSecret = DiffieHellman.prototype.computeSecret; ECDH.prototype.setPrivateKey = DiffieHellman.prototype.setPrivateKey; ECDH.prototype.setPublicKey = DiffieHellman.prototype.setPublicKey; ECDH.prototype.getPrivateKey = DiffieHellman.prototype.getPrivateKey; ECDH.prototype.generateKeys = function generateKeys(encoding, format) { this[kHandle].generateKeys(); return this.getPublicKey(encoding, format); }; ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { const f = getFormat(format); const key = this[kHandle].getPublicKey(f); return encode(key, encoding); }; ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) { validateString(curve, "curve"); key = getArrayBufferOrView(key, "key", inEnc); const f = getFormat(format); const convertedKey = _ECDHConvertKey(key, curve, f); return encode(convertedKey, outEnc); }; function encode(buffer, encoding) { if (encoding && encoding !== "buffer") buffer = buffer.toString(encoding); return buffer; } function getFormat(format) { if (format) { if (format === "compressed") return POINT_CONVERSION_COMPRESSED; if (format === "hybrid") return POINT_CONVERSION_HYBRID; if (format !== "uncompressed") throw new ERR_CRYPTO_ECDH_INVALID_FORMAT(format); } return POINT_CONVERSION_UNCOMPRESSED; } const dhEnabledKeyTypes = new Set(["dh", "ec", "x448", "x25519"]); function diffieHellman(options) { validateObject(options, "options"); const { privateKey, publicKey } = options; if (!(privateKey instanceof KeyObject)) throw new ERR_INVALID_ARG_VALUE("options.privateKey", privateKey); if (!(publicKey instanceof KeyObject)) throw new ERR_INVALID_ARG_VALUE("options.publicKey", publicKey); if (privateKey.type !== "private") throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(privateKey.type, "private"); if (publicKey.type !== "public" && publicKey.type !== "private") { throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE( publicKey.type, "private or public", ); } const privateType = privateKey.asymmetricKeyType; const publicType = publicKey.asymmetricKeyType; if (privateType !== publicType || !dhEnabledKeyTypes.has(privateType)) { throw new ERR_CRYPTO_INCOMPATIBLE_KEY( "key types for Diffie-Hellman", `${privateType} and ${publicType}`, ); } return statelessDH(privateKey[kHandle], publicKey[kHandle]); } // The ecdhDeriveBits function is part of the Web Crypto API and serves both // deriveKeys and deriveBits functions. async function ecdhDeriveBits(algorithm, baseKey, length) { const { public: key } = algorithm; if (key.type !== "public") { throw lazyDOMException( "algorithm.public must be a public key", "InvalidAccessError", ); } if (baseKey.type !== "private") { throw lazyDOMException( "baseKey must be a private key", "InvalidAccessError", ); } if ( key.algorithm.name !== "ECDH" && key.algorithm.name !== "X25519" && key.algorithm.name !== "X448" ) { throw lazyDOMException( "Keys must be ECDH, X25519, or X448 keys", "InvalidAccessError", ); } if (key.algorithm.name !== baseKey.algorithm.name) { throw lazyDOMException( "The public and private keys must be of the same type", "InvalidAccessError", ); } if ( key.algorithm.name === "ECDH" && key.algorithm.namedCurve !== baseKey.algorithm.namedCurve ) { throw lazyDOMException("Named curve mismatch", "InvalidAccessError"); } const bits = await jobPromise( () => new ECDHBitsJob( kCryptoJobAsync, key.algorithm.name === "ECDH" ? baseKey.algorithm.namedCurve : baseKey.algorithm.name, key[kKeyObject][kHandle], baseKey[kKeyObject][kHandle], ), ); // If a length is not specified, return the full derived secret if (length === null) return bits; // If the length is not a multiple of 8 the nearest ceiled // multiple of 8 is sliced. length = Math.ceil(length / 8); const { byteLength } = bits; // If the length is larger than the derived secret, throw. // Otherwise, we either return the secret or a truncated // slice. if (byteLength < length) throw lazyDOMException("derived bit length is too small", "OperationError"); return length === byteLength ? bits : ArrayBuffer.prototype.slice.call(bits, 0, length); } export { DiffieHellman }; export { DiffieHellmanGroup }; export { ECDH }; export { diffieHellman }; export { ecdhDeriveBits };