// Copyright 2024 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { stringify } from 'uuid'; import { getRandomBytes } from '../Crypto.node.ts'; export type GeneratedMessageIdType = Readonly<{ id: string; received_at: number; }>; // See https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.7 export function generateMessageId(counter: number): GeneratedMessageIdType { const uuid = getRandomBytes(16); // We compose uuid out of 48 bits (6 bytes of) timestamp-like counter: // `incrementMessageCounter`. Note big-endian encoding (which ensures proper // lexicographical order), and floating point divisions (because `&` operator // coerces to 32bit integers) // oxlint-disable-next-line no-bitwise uuid[0] = (counter / 0x10000000000) & 0xff; // oxlint-disable-next-line no-bitwise uuid[1] = (counter / 0x00100000000) & 0xff; // oxlint-disable-next-line no-bitwise uuid[2] = (counter / 0x00001000000) & 0xff; // oxlint-disable-next-line no-bitwise uuid[3] = (counter / 0x00000010000) & 0xff; // oxlint-disable-next-line no-bitwise uuid[4] = (counter / 0x00000000100) & 0xff; // oxlint-disable-next-line no-bitwise uuid[5] = (counter / 0x00000000001) & 0xff; // Mask out 4 bits of version number // oxlint-disable-next-line no-bitwise, typescript/no-non-null-assertion uuid[6]! &= 0x0f; // And set the version to 7 // oxlint-disable-next-line no-bitwise, typescript/no-non-null-assertion uuid[6]! |= 0x70; // Mask out 2 bits of variant // oxlint-disable-next-line no-bitwise, typescript/no-non-null-assertion uuid[8]! &= 0x3f; // And set it to "2" // oxlint-disable-next-line no-bitwise, typescript/no-non-null-assertion uuid[8]! |= 0x80; return { id: stringify(uuid), received_at: counter, }; }