crypto: implement randomUUIDv7() · nodejs/node@b267f6b
@@ -11,6 +11,7 @@ const {
1111 BigIntPrototypeToString,
1212 DataView,
1313 DataViewPrototypeGetUint8,
14+ DateNow,
1415 FunctionPrototypeBind,
1516 FunctionPrototypeCall,
1617 MathMin,
@@ -359,7 +360,7 @@ function getHexBytes() {
359360return hexBytesCache;
360361}
361362362-function serializeUUID(buf, offset = 0) {
363+function serializeUUID(buf, version, variant, offset = 0) {
363364const kHexBytes = getHexBytes();
364365// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
365366return kHexBytes[buf[offset]] +
@@ -370,10 +371,10 @@ function serializeUUID(buf, offset = 0) {
370371kHexBytes[buf[offset + 4]] +
371372kHexBytes[buf[offset + 5]] +
372373'-' +
373-kHexBytes[(buf[offset + 6] & 0x0f) | 0x40] +
374+kHexBytes[(buf[offset + 6] & 0x0f) | version] +
374375kHexBytes[buf[offset + 7]] +
375376'-' +
376-kHexBytes[(buf[offset + 8] & 0x3f) | 0x80] +
377+kHexBytes[(buf[offset + 8] & 0x3f) | variant] +
377378kHexBytes[buf[offset + 9]] +
378379'-' +
379380kHexBytes[buf[offset + 10]] +
@@ -391,15 +392,15 @@ function getBufferedUUID() {
391392392393if (uuidBatch === 0) randomFillSync(uuidData);
393394uuidBatch = (uuidBatch + 1) % kBatchSize;
394-return serializeUUID(uuidData, uuidBatch * 16);
395+return serializeUUID(uuidData, 0x40, 0x80, uuidBatch * 16);
395396}
396397397398function getUnbufferedUUID() {
398399uuidNotBuffered ??= secureBuffer(16);
399400if (uuidNotBuffered === undefined)
400401throw new ERR_OPERATION_FAILED('Out of memory');
401402randomFillSync(uuidNotBuffered);
402-return serializeUUID(uuidNotBuffered);
403+return serializeUUID(uuidNotBuffered, 0x40, 0x80);
403404}
404405405406function randomUUID(options) {
@@ -414,6 +415,50 @@ function randomUUID(options) {
414415return disableEntropyCache ? getUnbufferedUUID() : getBufferedUUID();
415416}
416417418+function writeTimestamp(buf, offset) {
419+const now = DateNow();
420+const msb = now / (2 ** 32);
421+buf[offset] = msb >>> 8;
422+buf[offset + 1] = msb;
423+buf[offset + 2] = now >>> 24;
424+buf[offset + 3] = now >>> 16;
425+buf[offset + 4] = now >>> 8;
426+buf[offset + 5] = now;
427+}
428+429+function getBufferedUUIDv7() {
430+uuidData ??= secureBuffer(16 * kBatchSize);
431+if (uuidData === undefined)
432+throw new ERR_OPERATION_FAILED('Out of memory');
433+434+if (uuidBatch === 0) randomFillSync(uuidData);
435+uuidBatch = (uuidBatch + 1) % kBatchSize;
436+const offset = uuidBatch * 16;
437+writeTimestamp(uuidData, offset);
438+return serializeUUID(uuidData, 0x70, 0x80, offset);
439+}
440+441+function getUnbufferedUUIDv7() {
442+uuidNotBuffered ??= secureBuffer(16);
443+if (uuidNotBuffered === undefined)
444+throw new ERR_OPERATION_FAILED('Out of memory');
445+randomFillSync(uuidNotBuffered, 6);
446+writeTimestamp(uuidNotBuffered, 0);
447+return serializeUUID(uuidNotBuffered, 0x70, 0x80);
448+}
449+450+function randomUUIDv7(options) {
451+if (options !== undefined)
452+validateObject(options, 'options');
453+const {
454+ disableEntropyCache = false,
455+} = options || kEmptyObject;
456+457+validateBoolean(disableEntropyCache, 'options.disableEntropyCache');
458+459+return disableEntropyCache ? getUnbufferedUUIDv7() : getBufferedUUIDv7();
460+}
461+417462function createRandomPrimeJob(type, size, options) {
418463validateObject(options, 'options');
419464@@ -611,6 +656,7 @@ module.exports = {
611656 randomInt,
612657 getRandomValues,
613658 randomUUID,
659+ randomUUIDv7,
614660 generatePrime,
615661 generatePrimeSync,
616662};