/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/internal/crypto/diffiehellman.js
398 строк
10 KB
Filip Skokan
crypto: read WebCrypto inputs through primordials
09 авг 2026, 22:10
09 авг 2026, 22:10
e04663a
Код
Авторство
О чём код?
'use strict'; const { ArrayBufferPrototypeGetByteLength, ArrayBufferPrototypeSlice, FunctionPrototypeCall, ObjectDefineProperty, TypedArrayPrototypeGetBuffer, } = primordials; const { Buffer } = require('buffer'); const { DHBitsJob, DiffieHellman: _DiffieHellman, DiffieHellmanGroup: _DiffieHellmanGroup, ECDH: _ECDH, ECDHConvertKey: _ECDHConvertKey, kCryptoJobAsync, kCryptoJobSync, kCryptoJobWebCrypto, } = internalBinding('crypto'); const { codes: { ERR_CRYPTO_ECDH_INVALID_FORMAT, ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY, ERR_INVALID_ARG_TYPE, }, } = require('internal/errors'); const { validateFunction, validateInt32, validateObject, validateString, } = require('internal/validators'); const { isArrayBufferView, isAnyArrayBuffer, } = require('internal/util/types'); const { deprecate, lazyDOMException, } = require('internal/util'); const { getCryptoKeyAlgorithm, getCryptoKeyHandle, getCryptoKeyType, preparePrivateKey, preparePublicOrPrivateKey, } = require('internal/crypto/keys'); const { getArrayBufferOrView, jobPromise, jobPromiseThen, numBitsToBytes, toBuf, truncateToBitLength, kHandle, } = require('internal/crypto/util'); const { crypto: { POINT_CONVERSION_COMPRESSED, POINT_CONVERSION_HYBRID, POINT_CONVERSION_UNCOMPRESSED, }, } = internalBinding('constants'); 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'); // Coerce -0 to +0. sizeOrKey += 0; } 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); ObjectDefineProperty(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); ObjectDefineProperty(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 = deprecate(DiffieHellman.prototype.setPublicKey, 'ecdh.setPublicKey() is deprecated.', 'DEP0031'); 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; } function diffieHellman(options, callback) { validateObject(options, 'options'); if (callback !== undefined) validateFunction(callback, 'callback'); const { privateKey, publicKey } = options; const { data: pubData, format: pubFormat, type: pubType, passphrase: pubPassphrase, namedCurve: pubNamedCurve, } = preparePublicOrPrivateKey(publicKey, 'options.publicKey'); const { data: privData, format: privFormat, type: privType, passphrase: privPassphrase, namedCurve: privNamedCurve, } = preparePrivateKey(privateKey, 'options.privateKey'); const job = new DHBitsJob( callback ? kCryptoJobAsync : kCryptoJobSync, pubData, pubFormat, pubType, pubPassphrase, pubNamedCurve, privData, privFormat, privType, privPassphrase, privNamedCurve); if (!callback) { const { 0: err, 1: secret } = job.run(); if (err !== undefined) throw err; return Buffer.from(secret); } job.ondone = (error, secret) => { if (error) return FunctionPrototypeCall(callback, job, error); FunctionPrototypeCall(callback, job, null, Buffer.from(secret)); }; job.run(); } // The ecdhDeriveBits function is part of the Web Crypto API and serves both // deriveKeys and deriveBits functions. function ecdhDeriveBits(algorithm, baseKey, length) { const { 'public': key } = algorithm; if (getCryptoKeyType(baseKey) !== 'private') { throw lazyDOMException( 'baseKey must be a private key', 'InvalidAccessError'); } const keyAlgorithm = getCryptoKeyAlgorithm(key); const baseKeyAlgorithm = getCryptoKeyAlgorithm(baseKey); if (keyAlgorithm.name !== baseKeyAlgorithm.name) { throw lazyDOMException( 'The public and private keys must be of the same type', 'InvalidAccessError'); } if ( keyAlgorithm.name === 'ECDH' && keyAlgorithm.namedCurve !== baseKeyAlgorithm.namedCurve ) { throw lazyDOMException('Named curve mismatch', 'InvalidAccessError'); } const bits = jobPromise(() => new DHBitsJob( kCryptoJobWebCrypto, getCryptoKeyHandle(key), undefined, undefined, undefined, undefined, getCryptoKeyHandle(baseKey), undefined, undefined, undefined, undefined)); // If a length is not specified, return the full derived secret if (length === null) return bits; return jobPromiseThen(bits, (bits) => { const sliceLength = numBitsToBytes(length); const byteLength = ArrayBufferPrototypeGetByteLength(bits); // If the length is larger than the derived secret, throw. if (byteLength < sliceLength) throw lazyDOMException('derived bit length is too small', 'OperationError'); if (length % 8 === 0) { if (byteLength === sliceLength) return bits; return ArrayBufferPrototypeSlice(bits, 0, sliceLength); } return TypedArrayPrototypeGetBuffer(truncateToBitLength(length, bits)); }); } module.exports = { DiffieHellman, DiffieHellmanGroup, ECDH, diffieHellman, ecdhDeriveBits, };