Skip to content

Commit a5f3be5

Browse files
committed
crypto: make monotonic counter optional
1 parent 19bb93c commit a5f3be5

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

lib/internal/crypto/random.js

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -447,28 +447,50 @@ function writeTimestampAndCounterV7(buf, offset) {
447447
buf[offset + 7] = v7Counter & 0xff;
448448
}
449449

450-
function getBufferedUUIDv7() {
450+
function getBufferedUUIDv7(monotonic) {
451451
uuidDataV7 ??= secureBuffer(16 * kBatchSize);
452452
if (uuidDataV7 === undefined)
453453
throw new ERR_OPERATION_FAILED('Out of memory');
454454

455455
if (uuidBatchV7 === 0) randomFillSync(uuidDataV7);
456456
uuidBatchV7 = (uuidBatchV7 + 1) % kBatchSize;
457457
const offset = uuidBatchV7 * 16;
458-
const seed = ((uuidDataV7[offset + 6] & 0x0f) << 8) | uuidDataV7[offset + 7];
459-
advanceV7(seed);
460-
writeTimestampAndCounterV7(uuidDataV7, offset);
458+
if (monotonic) {
459+
const seed = ((uuidDataV7[offset + 6] & 0x0f) << 8) | uuidDataV7[offset + 7];
460+
advanceV7(seed);
461+
writeTimestampAndCounterV7(uuidDataV7, offset);
462+
} else {
463+
const now = DateNow();
464+
const msb = now / (2 ** 32);
465+
uuidDataV7[offset] = msb >>> 8;
466+
uuidDataV7[offset + 1] = msb;
467+
uuidDataV7[offset + 2] = now >>> 24;
468+
uuidDataV7[offset + 3] = now >>> 16;
469+
uuidDataV7[offset + 4] = now >>> 8;
470+
uuidDataV7[offset + 5] = now;
471+
}
461472
return serializeUUID(uuidDataV7, 0x70, 0x80, offset);
462473
}
463474

464-
function getUnbufferedUUIDv7() {
475+
function getUnbufferedUUIDv7(monotonic) {
465476
uuidNotBuffered ??= secureBuffer(16);
466477
if (uuidNotBuffered === undefined)
467478
throw new ERR_OPERATION_FAILED('Out of memory');
468479
randomFillSync(uuidNotBuffered, 6);
469-
const seed = ((uuidNotBuffered[6] & 0x0f) << 8) | uuidNotBuffered[7];
470-
advanceV7(seed);
471-
writeTimestampAndCounterV7(uuidNotBuffered, 0);
480+
if (monotonic) {
481+
const seed = ((uuidNotBuffered[6] & 0x0f) << 8) | uuidNotBuffered[7];
482+
advanceV7(seed);
483+
writeTimestampAndCounterV7(uuidNotBuffered, 0);
484+
} else {
485+
const now = DateNow();
486+
const msb = now / (2 ** 32);
487+
uuidNotBuffered[0] = msb >>> 8;
488+
uuidNotBuffered[1] = msb;
489+
uuidNotBuffered[2] = now >>> 24;
490+
uuidNotBuffered[3] = now >>> 16;
491+
uuidNotBuffered[4] = now >>> 8;
492+
uuidNotBuffered[5] = now;
493+
}
472494
return serializeUUID(uuidNotBuffered, 0x70, 0x80);
473495
}
474496

@@ -477,11 +499,14 @@ function randomUUIDv7(options) {
477499
validateObject(options, 'options');
478500
const {
479501
disableEntropyCache = false,
502+
monotonic = true,
480503
} = options || kEmptyObject;
481504

482505
validateBoolean(disableEntropyCache, 'options.disableEntropyCache');
506+
validateBoolean(monotonic, 'options.monotonic');
483507

484-
return disableEntropyCache ? getUnbufferedUUIDv7() : getBufferedUUIDv7();
508+
return disableEntropyCache ?
509+
getUnbufferedUUIDv7(monotonic) : getBufferedUUIDv7(monotonic);
485510
}
486511

487512
function createRandomPrimeJob(type, size, options) {

test/parallel/test-crypto-randomuuidv7.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,22 @@ const {
105105
assert.match(randomUUIDv7({ disableEntropyCache: true }), uuidv7Regex);
106106
assert.match(randomUUIDv7({ disableEntropyCache: true }), uuidv7Regex);
107107

108+
// monotonic: false — rand_a is random; UUIDs must still be valid but are not
109+
// guaranteed to be strictly ordered within the same millisecond.
110+
assert.match(randomUUIDv7({ monotonic: false }), uuidv7Regex);
111+
assert.match(randomUUIDv7({ monotonic: false, disableEntropyCache: true }), uuidv7Regex);
112+
108113
assert.throws(() => randomUUIDv7(1), {
109114
code: 'ERR_INVALID_ARG_TYPE',
110115
});
111116

112117
assert.throws(() => randomUUIDv7({ disableEntropyCache: '' }), {
113118
code: 'ERR_INVALID_ARG_TYPE',
114119
});
120+
121+
assert.throws(() => randomUUIDv7({ monotonic: 1 }), {
122+
code: 'ERR_INVALID_ARG_TYPE',
123+
});
115124
}
116125

117126
{

0 commit comments

Comments
 (0)