| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 7c9f3a9 commit 08f9130
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,17 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common.js'); | ||
| 4 | + const { randomUUID } = require('crypto'); | ||
| 5 | + | ||
| 6 | + const bench = common.createBenchmark(main, { | ||
| 7 | + n: [1e7], | ||
| 8 | + disableEntropyCache: [0, 1], | ||
| 9 | + }); | ||
| 10 | + | ||
| 11 | + function main({ n, disableEntropyCache }) { | ||
| 12 | + disableEntropyCache = !!disableEntropyCache; | ||
| 13 | + bench.start(); | ||
| 14 | + for (let i = 0; i < n; ++i) | ||
| 15 | + randomUUID({ disableEntropyCache }); | ||
| 16 | + bench.end(n); | ||
| 17 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2841,6 +2841,20 @@ const n = crypto.randomInt(1, 7); | |||
| 2841 | 2841 | console.log(`The dice rolled: ${n}`); | |
| 2842 | 2842 | ``` | |
| 2843 | 2843 | ||
| 2844 | + ### `crypto.randomUUID([options])` | ||
| 2845 | + <!-- YAML | ||
| 2846 | + added: REPLACEME | ||
| 2847 | + --> | ||
| 2848 | + | ||
| 2849 | + * `options` {Object} | ||
| 2850 | + * `disableEntropyCache` {boolean} By default, to improve performance, | ||
| 2851 | + Node.js generates and caches enough random data to generate up to | ||
| 2852 | + 128 random UUIDs. To generate a UUID without using the cache, set | ||
| 2853 | + `disableEntropyCache` to `true`. **Defaults**: `false`. | ||
| 2854 | + * Returns: {string} | ||
| 2855 | + | ||
| 2856 | + Generates a random [RFC 4122][] Version 4 UUID. | ||
| 2857 | + | ||
| 2844 | 2858 | ### `crypto.scrypt(password, salt, keylen[, options], callback)` | |
| 2845 | 2859 | <!-- YAML | |
| 2846 | 2860 | added: v10.5.0 | |
@@ -3582,6 +3596,7 @@ See the [list of SSL OP Flags][] for details. | |||
| 3582 | 3596 | [RFC 3526]: https://www.rfc-editor.org/rfc/rfc3526.txt | |
| 3583 | 3597 | [RFC 3610]: https://www.rfc-editor.org/rfc/rfc3610.txt | |
| 3584 | 3598 | [RFC 4055]: https://www.rfc-editor.org/rfc/rfc4055.txt | |
| 3599 | + [RFC 4122]: https://www.rfc-editor.org/rfc/rfc4122.txt | ||
| 3585 | 3600 | [RFC 5208]: https://www.rfc-editor.org/rfc/rfc5208.txt | |
| 3586 | 3601 | [`Buffer`]: buffer.md | |
| 3587 | 3602 | [`EVP_BytesToKey`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_BytesToKey.html | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1704,6 +1704,12 @@ compiled with ICU support. | |||
| 1704 | 1704 | ||
| 1705 | 1705 | A non-context-aware native addon was loaded in a process that disallows them. | |
| 1706 | 1706 | ||
| 1707 | + <a id="ERR_OPERATION_FAILED"></a> | ||
| 1708 | + ### `ERR_OPERATION_FAILED` | ||
| 1709 | + | ||
| 1710 | + An operation failed. This is typically used to signal the general failure of an | ||
| 1711 | + asynchronous operation. | ||
| 1712 | + | ||
| 1707 | 1713 | <a id="ERR_OUT_OF_RANGE"></a> | |
| 1708 | 1714 | ### `ERR_OUT_OF_RANGE` | |
| 1709 | 1715 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -53,7 +53,8 @@ const { | |||
| 53 | 53 | randomBytes, | |
| 54 | 54 | randomFill, | |
| 55 | 55 | randomFillSync, | |
| 56 | - randomInt | ||
| 56 | + randomInt, | ||
| 57 | + randomUUID, | ||
| 57 | 58 | } = require('internal/crypto/random'); | |
| 58 | 59 | const { | |
| 59 | 60 | pbkdf2, | |
@@ -186,6 +187,7 @@ module.exports = { | |||
| 186 | 187 | randomFill, | |
| 187 | 188 | randomFillSync, | |
| 188 | 189 | randomInt, | |
| 190 | + randomUUID, | ||
| 189 | 191 | scrypt, | |
| 190 | 192 | scryptSync, | |
| 191 | 193 | sign: signOneShot, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,14 +7,27 @@ const { | |||
| 7 | 7 | } = primordials; | |
| 8 | 8 | ||
| 9 | 9 | const { AsyncWrap, Providers } = internalBinding('async_wrap'); | |
| 10 | - const { kMaxLength } = require('buffer'); | ||
| 11 | - const { randomBytes: _randomBytes } = internalBinding('crypto'); | ||
| 12 | 10 | const { | |
| 13 | - ERR_INVALID_ARG_TYPE, | ||
| 14 | - ERR_INVALID_CALLBACK, | ||
| 15 | - ERR_OUT_OF_RANGE | ||
| 16 | - } = require('internal/errors').codes; | ||
| 17 | - const { validateNumber } = require('internal/validators'); | ||
| 11 | + Buffer, | ||
| 12 | + kMaxLength, | ||
| 13 | + } = require('buffer'); | ||
| 14 | + const { | ||
| 15 | + randomBytes: _randomBytes, | ||
| 16 | + secureBuffer, | ||
| 17 | + } = internalBinding('crypto'); | ||
| 18 | + const { | ||
| 19 | + codes: { | ||
| 20 | + ERR_INVALID_ARG_TYPE, | ||
| 21 | + ERR_INVALID_CALLBACK, | ||
| 22 | + ERR_OUT_OF_RANGE, | ||
| 23 | + ERR_OPERATION_FAILED, | ||
| 24 | + } | ||
| 25 | + } = require('internal/errors'); | ||
| 26 | + const { | ||
| 27 | + validateBoolean, | ||
| 28 | + validateNumber, | ||
| 29 | + validateObject, | ||
| 30 | + } = require('internal/validators'); | ||
| 18 | 31 | const { isArrayBufferView } = require('internal/util/types'); | |
| 19 | 32 | const { FastBuffer } = require('internal/buffer'); | |
| 20 | 33 | ||
@@ -203,9 +216,113 @@ function handleError(ex, buf) { | |||
| 203 | 216 | return buf; | |
| 204 | 217 | } | |
| 205 | 218 | ||
| 219 | + // Implements an RFC 4122 version 4 random UUID. | ||
| 220 | + // To improve performance, random data is generated in batches | ||
| 221 | + // large enough to cover kBatchSize UUID's at a time. The uuidData | ||
| 222 | + // and uuid buffers are reused. Each call to randomUUID() consumes | ||
| 223 | + // 16 bytes from the buffer. | ||
| 224 | + | ||
| 225 | + const kHexDigits = [ | ||
| 226 | + 48, 49, 50, 51, 52, 53, 54, 55, | ||
| 227 | + 56, 57, 97, 98, 99, 100, 101, 102 | ||
| 228 | + ]; | ||
| 229 | + | ||
| 230 | + const kBatchSize = 128; | ||
| 231 | + let uuidData; | ||
| 232 | + let uuidNotBuffered; | ||
| 233 | + let uuid; | ||
| 234 | + let uuidBatch = 0; | ||
| 235 | + | ||
| 236 | + function getBufferedUUID() { | ||
| 237 | + if (uuidData === undefined) { | ||
| 238 | + uuidData = secureBuffer(16 * kBatchSize); | ||
| 239 | + if (uuidData === undefined) | ||
| 240 | + throw new ERR_OPERATION_FAILED('Out of memory'); | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + if (uuidBatch === 0) randomFillSync(uuidData); | ||
| 244 | + uuidBatch = (uuidBatch + 1) % kBatchSize; | ||
| 245 | + return uuidData.slice(uuidBatch * 16, (uuidBatch * 16) + 16); | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + function randomUUID(options) { | ||
| 249 | + if (options !== undefined) | ||
| 250 | + validateObject(options, 'options'); | ||
| 251 | + const { | ||
| 252 | + disableEntropyCache = false, | ||
| 253 | + } = { ...options }; | ||
| 254 | + | ||
| 255 | + validateBoolean(disableEntropyCache, 'options.disableEntropyCache'); | ||
| 256 | + | ||
| 257 | + if (uuid === undefined) { | ||
| 258 | + uuid = Buffer.alloc(36, '-'); | ||
| 259 | + uuid[14] = 52; // '4', identifies the UUID version | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + let uuidBuf; | ||
| 263 | + if (!disableEntropyCache) { | ||
| 264 | + uuidBuf = getBufferedUUID(); | ||
| 265 | + } else { | ||
| 266 | + uuidBuf = uuidNotBuffered; | ||
| 267 | + if (uuidBuf === undefined) | ||
| 268 | + uuidBuf = uuidNotBuffered = secureBuffer(16); | ||
| 269 | + if (uuidBuf === undefined) | ||
| 270 | + throw new ERR_OPERATION_FAILED('Out of memory'); | ||
| 271 | + randomFillSync(uuidBuf); | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + // Variant byte: 10xxxxxx (variant 1) | ||
| 275 | + uuidBuf[8] = (uuidBuf[8] & 0x3f) | 0x80; | ||
| 276 | + | ||
| 277 | + // This function is structured the way it is for performance. | ||
| 278 | + // The uuid buffer stores the serialization of the random | ||
| 279 | + // bytes from uuidData. | ||
| 280 | + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ||
| 281 | + let n = 0; | ||
| 282 | + uuid[0] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 283 | + uuid[1] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 284 | + uuid[2] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 285 | + uuid[3] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 286 | + uuid[4] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 287 | + uuid[5] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 288 | + uuid[6] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 289 | + uuid[7] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 290 | + // - | ||
| 291 | + uuid[9] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 292 | + uuid[10] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 293 | + uuid[11] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 294 | + uuid[12] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 295 | + // - | ||
| 296 | + // 4, uuid[14] is set already... | ||
| 297 | + uuid[15] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 298 | + uuid[16] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 299 | + uuid[17] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 300 | + // - | ||
| 301 | + uuid[19] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 302 | + uuid[20] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 303 | + uuid[21] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 304 | + uuid[22] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 305 | + // - | ||
| 306 | + uuid[24] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 307 | + uuid[25] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 308 | + uuid[26] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 309 | + uuid[27] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 310 | + uuid[28] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 311 | + uuid[29] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 312 | + uuid[30] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 313 | + uuid[31] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 314 | + uuid[32] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 315 | + uuid[33] = kHexDigits[uuidBuf[n++] & 0xf]; | ||
| 316 | + uuid[34] = kHexDigits[uuidBuf[n] >> 4]; | ||
| 317 | + uuid[35] = kHexDigits[uuidBuf[n] & 0xf]; | ||
| 318 | + | ||
| 319 | + return uuid.latin1Slice(0, 36); | ||
| 320 | + } | ||
| 321 | + | ||
| 206 | 322 | module.exports = { | |
| 207 | 323 | randomBytes, | |
| 208 | 324 | randomFill, | |
| 209 | 325 | randomFillSync, | |
| 210 | - randomInt | ||
| 326 | + randomInt, | ||
| 327 | + randomUUID, | ||
| 211 | 328 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1259,6 +1259,7 @@ E('ERR_NO_CRYPTO', | |||
| 1259 | 1259 | 'Node.js is not compiled with OpenSSL crypto support', Error); | |
| 1260 | 1260 | E('ERR_NO_ICU', | |
| 1261 | 1261 | '%s is not supported on Node.js compiled without ICU', TypeError); | |
| 1262 | + E('ERR_OPERATION_FAILED', 'Operation failed: %s', Error); | ||
| 1262 | 1263 | E('ERR_OUT_OF_RANGE', | |
| 1263 | 1264 | (str, range, input, replaceDefaultBoolean = false) => { | |
| 1264 | 1265 | assert(range, 'Missing "range" argument'); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -67,7 +67,9 @@ namespace crypto { | |||
| 67 | 67 | using node::THROW_ERR_TLS_INVALID_PROTOCOL_METHOD; | |
| 68 | 68 | ||
| 69 | 69 | using v8::Array; | |
| 70 | + using v8::ArrayBuffer; | ||
| 70 | 71 | using v8::ArrayBufferView; | |
| 72 | + using v8::BackingStore; | ||
| 71 | 73 | using v8::Boolean; | |
| 72 | 74 | using v8::ConstructorBehavior; | |
| 73 | 75 | using v8::Context; | |
@@ -97,6 +99,7 @@ using v8::SideEffectType; | |||
| 97 | 99 | using v8::Signature; | |
| 98 | 100 | using v8::String; | |
| 99 | 101 | using v8::Uint32; | |
| 102 | + using v8::Uint8Array; | ||
| 100 | 103 | using v8::Undefined; | |
| 101 | 104 | using v8::Value; | |
| 102 | 105 | ||
@@ -6944,6 +6947,35 @@ void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) { | |||
| 6944 | 6947 | } | |
| 6945 | 6948 | #endif /* NODE_FIPS_MODE */ | |
| 6946 | 6949 | ||
| 6950 | + namespace { | ||
| 6951 | + // SecureBuffer uses openssl to allocate a Uint8Array using | ||
| 6952 | + // OPENSSL_secure_malloc. Because we do not yet actually | ||
| 6953 | + // make use of secure heap, this has the same semantics as | ||
| 6954 | + // using OPENSSL_malloc. However, if the secure heap is | ||
| 6955 | + // initialized, SecureBuffer will automatically use it. | ||
| 6956 | + void SecureBuffer(const FunctionCallbackInfo<Value>& args) { | ||
| 6957 | + CHECK(args[0]->IsUint32()); | ||
| 6958 | + Environment* env = Environment::GetCurrent(args); | ||
| 6959 | + uint32_t len = args[0].As<Uint32>()->Value(); | ||
| 6960 | + char* data = static_cast<char*>(OPENSSL_secure_malloc(len)); | ||
| 6961 | + if (data == nullptr) { | ||
| 6962 | + // There's no memory available for the allocation. | ||
| 6963 | + // Return nothing. | ||
| 6964 | + return; | ||
| 6965 | + } | ||
| 6966 | + memset(data, 0, len); | ||
| 6967 | + std::shared_ptr<BackingStore> store = | ||
| 6968 | + ArrayBuffer::NewBackingStore( | ||
| 6969 | + data, | ||
| 6970 | + len, | ||
| 6971 | + [](void* data, size_t len, void* deleter_data) { | ||
| 6972 | + OPENSSL_secure_clear_free(data, len); | ||
| 6973 | + }, | ||
| 6974 | + data); | ||
| 6975 | + Local<ArrayBuffer> buffer = ArrayBuffer::New(env->isolate(), store); | ||
| 6976 | + args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len)); | ||
| 6977 | + } | ||
| 6978 | + } // namespace | ||
| 6947 | 6979 | ||
| 6948 | 6980 | void Initialize(Local<Object> target, | |
| 6949 | 6981 | Local<Value> unused, | |
@@ -7038,6 +7070,8 @@ void Initialize(Local<Object> target, | |||
| 7038 | 7070 | #ifndef OPENSSL_NO_SCRYPT | |
| 7039 | 7071 | env->SetMethod(target, "scrypt", Scrypt); | |
| 7040 | 7072 | #endif // OPENSSL_NO_SCRYPT | |
| 7073 | + | ||
| 7074 | + env->SetMethod(target, "secureBuffer", SecureBuffer); | ||
| 7041 | 7075 | } | |
| 7042 | 7076 | ||
| 7043 | 7077 | } // namespace crypto | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,58 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + | ||
| 5 | + if (!common.hasCrypto) | ||
| 6 | + common.skip('missing crypto'); | ||
| 7 | + | ||
| 8 | + const assert = require('assert'); | ||
| 9 | + const { | ||
| 10 | + randomUUID, | ||
| 11 | + } = require('crypto'); | ||
| 12 | + | ||
| 13 | + const last = new Set([ | ||
| 14 | + '00000000-0000-0000-0000-000000000000' | ||
| 15 | + ]); | ||
| 16 | + | ||
| 17 | + function testMatch(uuid) { | ||
| 18 | + assert.match( | ||
| 19 | + uuid, | ||
| 20 | + /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + // Generate a number of UUID's to make sure we're | ||
| 24 | + // not just generating the same value over and over | ||
| 25 | + // and to make sure the batching changes the random | ||
| 26 | + // bytes. | ||
| 27 | + for (let n = 0; n < 130; n++) { | ||
| 28 | + const uuid = randomUUID(); | ||
| 29 | + assert(!last.has(uuid)); | ||
| 30 | + last.add(uuid); | ||
| 31 | + assert.strictEqual(typeof uuid, 'string'); | ||
| 32 | + assert.strictEqual(uuid.length, 36); | ||
| 33 | + testMatch(uuid); | ||
| 34 | + | ||
| 35 | + // Check that version 4 identifier was populated. | ||
| 36 | + assert.strictEqual( | ||
| 37 | + Buffer.from(uuid.substr(14, 2), 'hex')[0] & 0x40, 0x40); | ||
| 38 | + | ||
| 39 | + // Check that clock_seq_hi_and_reserved was populated with reserved bits. | ||
| 40 | + assert.strictEqual( | ||
| 41 | + Buffer.from(uuid.substr(19, 2), 'hex')[0] & 0b1100_0000, 0b1000_0000); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + // Test non-buffered UUID's | ||
| 45 | + { | ||
| 46 | + testMatch(randomUUID({ disableEntropyCache: true })); | ||
| 47 | + testMatch(randomUUID({ disableEntropyCache: true })); | ||
| 48 | + testMatch(randomUUID({ disableEntropyCache: true })); | ||
| 49 | + testMatch(randomUUID({ disableEntropyCache: true })); | ||
| 50 | + | ||
| 51 | + assert.throws(() => randomUUID(1), { | ||
| 52 | + code: 'ERR_INVALID_ARG_TYPE' | ||
| 53 | + }); | ||
| 54 | + | ||
| 55 | + assert.throws(() => randomUUID({ disableEntropyCache: '' }), { | ||
| 56 | + code: 'ERR_INVALID_ARG_TYPE' | ||
| 57 | + }); | ||
| 58 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments