/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node/polyfills/internal/crypto/random.ts
464 строки
12 KB
Nathan Whitaker
perf(ext/crypto): batch randomUUID generation (#35953)
13 июл 2026, 20:41
Не верифицирован
13 июл 2026, 20:41
2c7aa39
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. // deno-lint-ignore-file no-explicit-any (function () { const { core, primordials } = __bootstrap; const { Array, ArrayBufferPrototypeGetByteLength, BigInt, BigIntPrototypeToString, DataView, DataViewPrototypeGetBuffer, DataViewPrototypeGetByteLength, DataViewPrototypeGetByteOffset, DataViewPrototypeGetUint8, MathClz32, NumberParseInt, ObjectPrototypeIsPrototypeOf, PromisePrototypeCatch, PromisePrototypeThen, SafeArrayIterator, StringFromCharCode, StringPrototypePadStart, StringPrototypeSubstring, TypedArrayPrototypeGetBuffer, TypedArrayPrototypeGetByteLength, TypedArrayPrototypeGetByteOffset, Uint8Array, Uint8ArrayPrototype, } = primordials; const { op_node_check_prime_bytes, op_node_check_prime_bytes_async, op_node_gen_prime, op_node_gen_prime_async, } = core.ops; const { default: randomBytes } = core.loadExtScript( "ext:deno_node/internal/crypto/_randomBytes.ts", ); const { default: randomFill, randomFillSync } = core.loadExtScript( "ext:deno_node/internal/crypto/_randomFill.mjs", ); const { default: randomInt } = core.loadExtScript( "ext:deno_node/internal/crypto/_randomInt.ts", ); const { validateBoolean, validateFunction, validateInt32, validateObject, } = core.loadExtScript("ext:deno_node/internal/validators.mjs"); const { isAnyArrayBuffer, isArrayBufferView, isTypedArray, } = core.loadExtScript("ext:deno_node/internal/util/types.ts"); const { ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE, NodeError, NodeRangeError, } = core.loadExtScript("ext:deno_node/internal/errors.ts"); const { Buffer } = core.loadExtScript("ext:deno_node/internal/buffer.mjs"); // OpenSSL BIGNUM max size: INT_MAX / (4 * BN_BITS2) words * 8 bytes/word // On 64-bit: (2^31 - 1) / 256 * 8 = 67108856 bytes const OPENSSL_BIGNUM_MAX_BYTES = (((2 ** 31 - 1) / (4 * 64)) | 0) * 8; function checkPrime( candidate: any, options: any = { __proto__: null }, callback?: (err: Error | null, result: boolean) => void, ) { if (typeof options === "function") { callback = options; options = {}; } validateFunction(callback, "callback"); validateObject(options, "options"); const { checks = 0, } = options!; validateInt32(checks, "options.checks", 0); let candidateBytes: ArrayBufferView | ArrayBuffer; if (typeof candidate === "bigint") { if (candidate < 0) { throw new ERR_OUT_OF_RANGE("candidate", ">= 0", candidate); } candidateBytes = bigintToBytes(candidate); } else if (isAnyArrayBuffer(candidate) || isArrayBufferView(candidate)) { const byteLength = isArrayBufferView(candidate) ? arrayBufferViewByteLength(candidate as ArrayBufferView) : ArrayBufferPrototypeGetByteLength(candidate as ArrayBuffer); if (byteLength > OPENSSL_BIGNUM_MAX_BYTES) { throw new NodeError( "ERR_OSSL_BN_BIGNUM_TOO_LONG", "bignum too long", ); } candidateBytes = candidate; } else { throw new ERR_INVALID_ARG_TYPE( "candidate", [ "ArrayBuffer", "TypedArray", "Buffer", "DataView", "bigint", ], candidate, ); } PromisePrototypeCatch( PromisePrototypeThen( op_node_check_prime_bytes_async(candidateBytes, checks), (result) => { callback?.(null, result); }, ), (err) => { callback?.(err, false); }, ); } function checkPrimeSync( candidate: any, options: any = { __proto__: null }, ): boolean { validateObject(options, "options"); const { checks = 0, } = options!; validateInt32(checks, "options.checks", 0); let candidateBytes: ArrayBufferView | ArrayBuffer; if (typeof candidate === "bigint") { if (candidate < 0) { throw new ERR_OUT_OF_RANGE("candidate", ">= 0", candidate); } candidateBytes = bigintToBytes(candidate); } else if (isAnyArrayBuffer(candidate) || isArrayBufferView(candidate)) { const byteLength = isArrayBufferView(candidate) ? arrayBufferViewByteLength(candidate as ArrayBufferView) : ArrayBufferPrototypeGetByteLength(candidate as ArrayBuffer); if (byteLength > OPENSSL_BIGNUM_MAX_BYTES) { throw new NodeError( "ERR_OSSL_BN_BIGNUM_TOO_LONG", "bignum too long", ); } candidateBytes = candidate; } else { throw new ERR_INVALID_ARG_TYPE( "candidate", [ "ArrayBuffer", "TypedArray", "Buffer", "DataView", "bigint", ], candidate, ); } return op_node_check_prime_bytes(candidateBytes, checks); } function generatePrime( size: number, options: any = { __proto__: null }, callback?: (err: Error | null, prime: ArrayBuffer | bigint) => void, ) { validateInt32(size, "size", 1); if (typeof options === "function") { callback = options; options = {}; } validateFunction(callback, "callback"); const { bigint, safe, add, rem, } = validateRandomPrimeJob(size, options); PromisePrototypeThen( op_node_gen_prime_async(size, safe, add ?? null, rem ?? null), (prime: Uint8Array) => { const buffer = TypedArrayPrototypeGetBuffer(prime); const result = bigint ? arrayBufferToUnsignedBigInt(buffer) : buffer; callback?.(null, result); }, (err: Error) => { callback?.(err, null as unknown as ArrayBuffer); }, ); } function generatePrimeSync( size: number, options: any = { __proto__: null }, ): ArrayBuffer | bigint { const { bigint, safe, add, rem, } = validateRandomPrimeJob(size, options); const prime = op_node_gen_prime(size, safe, add ?? null, rem ?? null); const buffer = TypedArrayPrototypeGetBuffer(prime); if (bigint) return arrayBufferToUnsignedBigInt(buffer); return buffer; } // Returns the byteLength of an ArrayBufferView using the correct primordial // getter depending on whether it is a TypedArray or a DataView. function arrayBufferViewByteLength(view: ArrayBufferView): number { return isTypedArray(view) ? TypedArrayPrototypeGetByteLength(view as Uint8Array) : DataViewPrototypeGetByteLength(view as DataView); } function toUint8Array( value: ArrayBuffer | ArrayBufferView | Buffer, ): Uint8Array { if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, value)) { return value as Uint8Array; } if (isArrayBufferView(value)) { const buffer = isTypedArray(value) ? TypedArrayPrototypeGetBuffer(value as Uint8Array) : DataViewPrototypeGetBuffer(value as DataView); const byteOffset = isTypedArray(value) ? TypedArrayPrototypeGetByteOffset(value as Uint8Array) : DataViewPrototypeGetByteOffset(value as DataView); return new Uint8Array( buffer, byteOffset, arrayBufferViewByteLength(value), ); } return new Uint8Array(value); } function validateRandomPrimeJob( size: number, options: any, ): any { validateInt32(size, "size", 1); validateObject(options, "options"); let { safe = false, bigint = false, add, rem, } = options!; validateBoolean(safe, "options.safe"); validateBoolean(bigint, "options.bigint"); if (add !== undefined) { if (typeof add === "bigint") { add = unsignedBigIntToBuffer(add, "options.add"); } else if (!isAnyArrayBuffer(add) && !isArrayBufferView(add)) { throw new ERR_INVALID_ARG_TYPE( "options.add", [ "ArrayBuffer", "TypedArray", "Buffer", "DataView", "bigint", ], add, ); } } if (rem !== undefined) { if (typeof rem === "bigint") { rem = unsignedBigIntToBuffer(rem, "options.rem"); } else if (!isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) { throw new ERR_INVALID_ARG_TYPE( "options.rem", [ "ArrayBuffer", "TypedArray", "Buffer", "DataView", "bigint", ], rem, ); } } const addBuf = add ? toUint8Array(add) : undefined; const remBuf = rem ? toUint8Array(rem) : undefined; if (addBuf) { const addBitCount = bitCount(addBuf); if (addBitCount === 0) { throw new NodeRangeError("ERR_OUT_OF_RANGE", "invalid options.add"); } if (addBitCount > size) { throw new NodeRangeError("ERR_OUT_OF_RANGE", "invalid options.add"); } if (remBuf) { const addBigInt = bufferToBigInt(addBuf); const remBigInt = bufferToBigInt(remBuf); if (addBigInt <= remBigInt) { throw new NodeRangeError("ERR_OUT_OF_RANGE", "invalid options.rem"); } } } return { safe, bigint, add: addBuf, rem: remBuf, }; } function bigintToBytes(n: bigint): Uint8Array { if (n === 0n) return new Uint8Array([0]); const hex = BigIntPrototypeToString(n, 16); const padded = hex.length % 2 ? "0" + hex : hex; const bytes = new Uint8Array(padded.length / 2); for (let i = 0; i < bytes.length; i++) { bytes[i] = NumberParseInt( StringPrototypeSubstring(padded, i * 2, i * 2 + 2), 16, ); } return bytes; } function bufferToBigInt(buf: Uint8Array): bigint { let result = 0n; for (let i = 0; i < buf.length; i++) { result = (result << 8n) | BigInt(buf[i]); } return result; } function bitCount(buf: Uint8Array): number { for (let i = 0; i < buf.length; i++) { if (buf[i] !== 0) { return (buf.length - i) * 8 - MathClz32(buf[i]) + 24; } } return 0; } const numberToHexCharCode = (number: number): number => (number < 10 ? 48 : 87) + number; function arrayBufferToUnsignedBigInt(buf: ArrayBuffer): bigint { const length = ArrayBufferPrototypeGetByteLength(buf); const chars: number[] = new Array(length * 2); const view = new DataView(buf); for (let i = 0; i < length; i++) { const val = DataViewPrototypeGetUint8(view, i); chars[2 * i] = numberToHexCharCode(val >> 4); chars[2 * i + 1] = numberToHexCharCode(val & 0xf); } return BigInt(`0x${StringFromCharCode(...new SafeArrayIterator(chars))}`); } function unsignedBigIntToBuffer(bigint: bigint, name: string) { if (bigint < 0) { throw new ERR_OUT_OF_RANGE(name, ">= 0", bigint); } const hex = BigIntPrototypeToString(bigint, 16); const padded = StringPrototypePadStart( hex, hex.length + (hex.length % 2), "0", ); return Buffer.from(padded, "hex"); } function randomUUID(options) { if (options !== undefined) { validateObject(options, "options"); } const { disableEntropyCache = false, } = options || {}; validateBoolean(disableEntropyCache, "options.disableEntropyCache"); if (disableEntropyCache) { return randomUUIDUncached(); } return globalThis.crypto.randomUUID(); } function randomUUIDUncached() { const bytes = new Uint8Array(16); globalThis.crypto.getRandomValues(bytes); bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80; const chars: number[] = new Array(36); let charIndex = 0; for (let byteIndex = 0; byteIndex < 16; byteIndex++) { if ( byteIndex === 4 || byteIndex === 6 || byteIndex === 8 || byteIndex === 10 ) { chars[charIndex++] = 45; } const byte = bytes[byteIndex]; chars[charIndex++] = numberToHexCharCode(byte >> 4); chars[charIndex++] = numberToHexCharCode(byte & 0x0f); } return StringFromCharCode(...new SafeArrayIterator(chars)); } return { checkPrime, checkPrimeSync, generatePrime, generatePrimeSync, randomUUID, randomInt, randomBytes, randomFill, randomFillSync, default: { checkPrime, checkPrimeSync, generatePrime, generatePrimeSync, randomUUID, randomInt, randomBytes, randomFill, randomFillSync, }, }; })();