From b0634f9a9d12809ec48470b8ee3e27ba96affeef Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Mon, 9 Jun 2025 14:37:30 -0700 Subject: [PATCH] Replace buffer.slice() with buffer.subarray() --- ts/Crypto.ts | 29 +++++++++++++---------- ts/Curve.ts | 2 +- ts/RemoteConfig.ts | 2 +- ts/SignalProtocolStore.ts | 2 +- ts/components/fun/data/segments.ts | 2 +- ts/context/Crypto.ts | 4 ++-- ts/linkPreviews/linkPreviewFetch.ts | 10 ++++++-- ts/services/backups/util/FileStream.ts | 2 +- ts/services/calling.ts | 4 ++-- ts/services/username.ts | 4 ++-- ts/test-both/util/appendMacStream_test.ts | 4 ++-- ts/test-both/util/logPadding_test.ts | 2 +- ts/test-electron/Crypto_test.ts | 4 ++-- ts/test-node/util/DelimitedStream_test.ts | 6 ++--- ts/textsecure/ContactsParser.ts | 2 +- ts/textsecure/MessageReceiver.ts | 2 +- ts/textsecure/ProvisioningCipher.ts | 8 +++---- ts/textsecure/cds/CDSSocketBase.ts | 8 +++---- ts/util/sessionTranslation.ts | 2 +- 19 files changed, 54 insertions(+), 45 deletions(-) diff --git a/ts/Crypto.ts b/ts/Crypto.ts index 5c50a06bf3..7f41f2caef 100644 --- a/ts/Crypto.ts +++ b/ts/Crypto.ts @@ -432,7 +432,7 @@ export function trimForDisplay(padded: Uint8Array): Uint8Array { break; } } - return padded.slice(0, paddingEnd); + return padded.subarray(0, paddingEnd); } function verifyDigest(data: Uint8Array, theirDigest: Uint8Array): void { @@ -459,13 +459,16 @@ export function decryptAttachmentV1( throw new Error('Got invalid length attachment'); } - const aesKey = keys.slice(0, 32); - const macKey = keys.slice(32, 64); + const aesKey = keys.subarray(0, 32); + const macKey = keys.subarray(32, 64); - const iv = encryptedBin.slice(0, 16); - const ciphertext = encryptedBin.slice(16, encryptedBin.byteLength - 32); - const ivAndCiphertext = encryptedBin.slice(0, encryptedBin.byteLength - 32); - const mac = encryptedBin.slice( + const iv = encryptedBin.subarray(0, 16); + const ciphertext = encryptedBin.subarray(16, encryptedBin.byteLength - 32); + const ivAndCiphertext = encryptedBin.subarray( + 0, + encryptedBin.byteLength - 32 + ); + const mac = encryptedBin.subarray( encryptedBin.byteLength - 32, encryptedBin.byteLength ); @@ -504,8 +507,8 @@ export function encryptAttachment({ } const iv = dangerousTestOnlyIv || getRandomBytes(16); - const aesKey = keys.slice(0, 32); - const macKey = keys.slice(32, 64); + const aesKey = keys.subarray(0, 32); + const macKey = keys.subarray(32, 64); const ciphertext = encryptAes256CbcPkcsPadding(aesKey, plaintext, iv); @@ -564,8 +567,8 @@ export function decryptProfile(data: Uint8Array, key: Uint8Array): Uint8Array { if (data.byteLength < 12 + 16 + 1) { throw new Error(`Got too short input: ${data.byteLength}`); } - const iv = data.slice(0, PROFILE_IV_LENGTH); - const ciphertext = data.slice(PROFILE_IV_LENGTH, data.byteLength); + const iv = data.subarray(0, PROFILE_IV_LENGTH); + const ciphertext = data.subarray(PROFILE_IV_LENGTH, data.byteLength); if (key.byteLength !== PROFILE_KEY_LENGTH) { throw new Error('Got invalid length profile key'); } @@ -624,8 +627,8 @@ export function decryptProfileName( const foundFamilyName = familyEnd > givenEnd + 1; return { - given: padded.slice(0, givenEnd), - family: foundFamilyName ? padded.slice(givenEnd + 1, familyEnd) : null, + given: padded.subarray(0, givenEnd), + family: foundFamilyName ? padded.subarray(givenEnd + 1, familyEnd) : null, }; } diff --git a/ts/Curve.ts b/ts/Curve.ts index 51c1d546a1..e731ae6353 100644 --- a/ts/Curve.ts +++ b/ts/Curve.ts @@ -143,7 +143,7 @@ function validatePubKeyFormat(pubKey: Uint8Array): Uint8Array { throw new Error('Invalid public key'); } if (pubKey.byteLength === 33) { - return pubKey.slice(1); + return pubKey.subarray(1); } return pubKey; diff --git a/ts/RemoteConfig.ts b/ts/RemoteConfig.ts index 69fb1c84f1..1a26a583b9 100644 --- a/ts/RemoteConfig.ts +++ b/ts/RemoteConfig.ts @@ -268,5 +268,5 @@ export function getBucketValue(aci: AciString, flagName: string): number { hashInput ); - return Number(Bytes.readBigUint64BE(hashResult.slice(0, 8)) % 1_000_000n); + return Number(Bytes.readBigUint64BE(hashResult.subarray(0, 8)) % 1_000_000n); } diff --git a/ts/SignalProtocolStore.ts b/ts/SignalProtocolStore.ts index 8340347322..e5858150a7 100644 --- a/ts/SignalProtocolStore.ts +++ b/ts/SignalProtocolStore.ts @@ -1923,7 +1923,7 @@ export class SignalProtocolStore extends EventEmitter { } const hash = sha256(pubKey); - const fingerprint = hash.slice(0, 4); + const fingerprint = hash.subarray(0, 4); return Bytes.toBase64(fingerprint); } diff --git a/ts/components/fun/data/segments.ts b/ts/components/fun/data/segments.ts index bc6b5df643..efa87ddfea 100644 --- a/ts/components/fun/data/segments.ts +++ b/ts/components/fun/data/segments.ts @@ -126,7 +126,7 @@ async function fetchSegment( let slice: ArrayBufferView; // Trim duplicate bytes from start of last segment if (segmentRange.sliceStart > 0) { - slice = data.slice(segmentRange.sliceStart); + slice = data.subarray(segmentRange.sliceStart); } else { slice = data; } diff --git a/ts/context/Crypto.ts b/ts/context/Crypto.ts index e8651db34d..142dbbb190 100644 --- a/ts/context/Crypto.ts +++ b/ts/context/Crypto.ts @@ -95,8 +95,8 @@ export class Crypto { throw new Error('Invalid GCM ciphertext'); } - const tag = input.slice(input.length - AUTH_TAG_SIZE); - input = input.slice(0, input.length - AUTH_TAG_SIZE); + const tag = input.subarray(input.length - AUTH_TAG_SIZE); + input = input.subarray(0, input.length - AUTH_TAG_SIZE); gcm.setAuthTag(tag); diff --git a/ts/linkPreviews/linkPreviewFetch.ts b/ts/linkPreviews/linkPreviewFetch.ts index 3aa0929b7b..18704f1896 100644 --- a/ts/linkPreviews/linkPreviewFetch.ts +++ b/ts/linkPreviews/linkPreviewFetch.ts @@ -307,7 +307,10 @@ const getHtmlDocument = async ( chunk = Buffer.from(chunk, httpCharset || 'utf8'); } - const truncatedChunk = chunk.slice(0, buffer.length - bytesLoadedSoFar); + const truncatedChunk = chunk.subarray( + 0, + buffer.length - bytesLoadedSoFar + ); buffer.set(truncatedChunk, bytesLoadedSoFar); bytesLoadedSoFar += truncatedChunk.byteLength; @@ -322,7 +325,10 @@ const getHtmlDocument = async ( ); } - const result = parseHtmlBytes(buffer.slice(0, bytesLoadedSoFar), httpCharset); + const result = parseHtmlBytes( + buffer.subarray(0, bytesLoadedSoFar), + httpCharset + ); return result; }; diff --git a/ts/services/backups/util/FileStream.ts b/ts/services/backups/util/FileStream.ts index fbad9fd10c..99d794e5a5 100644 --- a/ts/services/backups/util/FileStream.ts +++ b/ts/services/backups/util/FileStream.ts @@ -32,7 +32,7 @@ export class FileStream extends InputStream { this.#position ); this.#position += bytesRead; - return this.#buffer.slice(0, bytesRead); + return this.#buffer.subarray(0, bytesRead); } async skip(amount: number): Promise { diff --git a/ts/services/calling.ts b/ts/services/calling.ts index 8d973ca61a..32ccc68856 100644 --- a/ts/services/calling.ts +++ b/ts/services/calling.ts @@ -2822,7 +2822,7 @@ export class CallingClass { ); return; } - const senderIdentityKey = senderIdentityRecord.publicKey.slice(1); // Ignore the type header, it is not used. + const senderIdentityKey = senderIdentityRecord.publicKey.subarray(1); // Ignore the type header, it is not used. const ourAci = storage.user.getCheckedAci(); @@ -2833,7 +2833,7 @@ export class CallingClass { ); return; } - const receiverIdentityKey = receiverIdentityRecord.publicKey.slice(1); // Ignore the type header, it is not used. + const receiverIdentityKey = receiverIdentityRecord.publicKey.subarray(1); // Ignore the type header, it is not used. const conversation = window.ConversationController.get(remoteUserId); if (!conversation) { diff --git a/ts/services/username.ts b/ts/services/username.ts index a510d0deae..606966e626 100644 --- a/ts/services/username.ts +++ b/ts/services/username.ts @@ -370,8 +370,8 @@ export async function resolveUsernameByLinkBase64( base64: string ): Promise { const content = Bytes.fromBase64(base64); - const entropy = content.slice(0, USERNAME_LINK_ENTROPY_SIZE); - const serverId = content.slice(USERNAME_LINK_ENTROPY_SIZE); + const entropy = content.subarray(0, USERNAME_LINK_ENTROPY_SIZE); + const serverId = content.subarray(USERNAME_LINK_ENTROPY_SIZE); return resolveUsernameByLink({ entropy, serverId }); } diff --git a/ts/test-both/util/appendMacStream_test.ts b/ts/test-both/util/appendMacStream_test.ts index f4e2b46e20..ec1e9c549b 100644 --- a/ts/test-both/util/appendMacStream_test.ts +++ b/ts/test-both/util/appendMacStream_test.ts @@ -35,11 +35,11 @@ describe('appendMacStream', () => { const expectedMac = hmac.digest(); assert.strictEqual( - buf.slice(0, -MAC_SIZE).toString('hex'), + buf.subarray(0, -MAC_SIZE).toString('hex'), plaintext.toString('hex') ); assert.strictEqual( - buf.slice(-MAC_SIZE).toString('hex'), + buf.subarray(-MAC_SIZE).toString('hex'), expectedMac.toString('hex') ); }); diff --git a/ts/test-both/util/logPadding_test.ts b/ts/test-both/util/logPadding_test.ts index 90b972e7e0..7ec4baa903 100644 --- a/ts/test-both/util/logPadding_test.ts +++ b/ts/test-both/util/logPadding_test.ts @@ -102,7 +102,7 @@ describe('appendPaddingStream', () => { } } - assert.strictEqual(buf.slice(0, -padding).toString(), inputs.join('')); + assert.strictEqual(buf.subarray(0, -padding).toString(), inputs.join('')); assert.strictEqual(buf.length, expectedSize); } diff --git a/ts/test-electron/Crypto_test.ts b/ts/test-electron/Crypto_test.ts index 88b3d04509..b824b6161d 100644 --- a/ts/test-electron/Crypto_test.ts +++ b/ts/test-electron/Crypto_test.ts @@ -385,7 +385,7 @@ describe('Crypto', () => { const key = getRandomBytes(32); const plaintext = Bytes.fromString('Hello world'); const ourMac = hmacSha256(key, plaintext); - const theirMac = ourMac.slice(0, -1); + const theirMac = ourMac.subarray(0, -1); let error; try { verifyHmacSha256(plaintext, key, theirMac, ourMac.byteLength); @@ -458,7 +458,7 @@ describe('Crypto', () => { it('resolves with undefined if the first `length` bytes of the MACs match', () => { const key = getRandomBytes(32); const plaintext = Bytes.fromString('Hello world'); - const theirMac = hmacSha256(key, plaintext).slice(0, -5); + const theirMac = hmacSha256(key, plaintext).subarray(0, -5); const result = verifyHmacSha256( plaintext, key, diff --git a/ts/test-node/util/DelimitedStream_test.ts b/ts/test-node/util/DelimitedStream_test.ts index 5ed6390443..59a91922dd 100644 --- a/ts/test-node/util/DelimitedStream_test.ts +++ b/ts/test-node/util/DelimitedStream_test.ts @@ -33,7 +33,7 @@ describe('DelimitedStream', () => { Readable.from( (function* () { for (let offset = 0; offset < data.length; offset += stride) { - yield data.slice(offset, offset + stride); + yield data.subarray(offset, offset + stride); } })() ), @@ -95,7 +95,7 @@ describe('DelimitedStream', () => { const out = new Array(); await assert.isRejected( pipeline( - Readable.from(w.finish().slice(0, 1)), + Readable.from(w.finish().subarray(0, 1)), new DelimitedStream(), collect(out) ), @@ -110,7 +110,7 @@ describe('DelimitedStream', () => { const out = new Array(); await assert.isRejected( pipeline( - Readable.from(w.finish().slice(0, 10)), + Readable.from(w.finish().subarray(0, 10)), new DelimitedStream(), collect(out) ), diff --git a/ts/textsecure/ContactsParser.ts b/ts/textsecure/ContactsParser.ts index 4672d3fa86..aece80e489 100644 --- a/ts/textsecure/ContactsParser.ts +++ b/ts/textsecure/ContactsParser.ts @@ -143,7 +143,7 @@ export class ParseContactsTransform extends Transform { const spaceLeftAfterRead = reader.len - (reader.pos + attachmentSize); if (spaceLeftAfterRead >= 0) { // We've read enough data to read the entire attachment - const avatarData = reader.buf.slice( + const avatarData = reader.buf.subarray( reader.pos, reader.pos + attachmentSize ); diff --git a/ts/textsecure/MessageReceiver.ts b/ts/textsecure/MessageReceiver.ts index bad6db8159..f3a67917b0 100644 --- a/ts/textsecure/MessageReceiver.ts +++ b/ts/textsecure/MessageReceiver.ts @@ -1659,7 +1659,7 @@ export default class MessageReceiver #unpad(paddedPlaintext: Uint8Array): Uint8Array { for (let i = paddedPlaintext.length - 1; i >= 0; i -= 1) { if (paddedPlaintext[i] === 0x80) { - return new Uint8Array(paddedPlaintext.slice(0, i)); + return new Uint8Array(paddedPlaintext.subarray(0, i)); } if (paddedPlaintext[i] !== 0x00) { throw new Error('Invalid padding'); diff --git a/ts/textsecure/ProvisioningCipher.ts b/ts/textsecure/ProvisioningCipher.ts index 81fedbbe57..c6870f78e3 100644 --- a/ts/textsecure/ProvisioningCipher.ts +++ b/ts/textsecure/ProvisioningCipher.ts @@ -47,10 +47,10 @@ class ProvisioningCipherInner { throw new Error('Bad version number on ProvisioningMessage'); } - const iv = message.slice(1, 16 + 1); - const mac = message.slice(message.byteLength - 32, message.byteLength); - const ivAndCiphertext = message.slice(0, message.byteLength - 32); - const ciphertext = message.slice(16 + 1, message.byteLength - 32); + const iv = message.subarray(1, 16 + 1); + const mac = message.subarray(message.byteLength - 32, message.byteLength); + const ivAndCiphertext = message.subarray(0, message.byteLength - 32); + const ciphertext = message.subarray(16 + 1, message.byteLength - 32); if (!this.keyPair) { throw new Error('ProvisioningCipher.decrypt: No keypair!'); diff --git a/ts/textsecure/cds/CDSSocketBase.ts b/ts/textsecure/cds/CDSSocketBase.ts index b187053c6f..28117c2bf8 100644 --- a/ts/textsecure/cds/CDSSocketBase.ts +++ b/ts/textsecure/cds/CDSSocketBase.ts @@ -211,7 +211,7 @@ function decodeSingleResponse( i < response.e164PniAciTriples.length; i += TRIPLE_BYTE_SIZE ) { - const tripleBytes = response.e164PniAciTriples.slice( + const tripleBytes = response.e164PniAciTriples.subarray( i, i + TRIPLE_BYTE_SIZE ); @@ -221,13 +221,13 @@ function decodeSingleResponse( ); let offset = 0; - const e164Bytes = tripleBytes.slice(offset, offset + E164_BYTE_SIZE); + const e164Bytes = tripleBytes.subarray(offset, offset + E164_BYTE_SIZE); offset += E164_BYTE_SIZE; - const pniBytes = tripleBytes.slice(offset, offset + UUID_BYTE_SIZE); + const pniBytes = tripleBytes.subarray(offset, offset + UUID_BYTE_SIZE); offset += UUID_BYTE_SIZE; - const aciBytes = tripleBytes.slice(offset, offset + UUID_BYTE_SIZE); + const aciBytes = tripleBytes.subarray(offset, offset + UUID_BYTE_SIZE); offset += UUID_BYTE_SIZE; const e164Long = Long.fromBytesBE(Array.from(e164Bytes)); diff --git a/ts/util/sessionTranslation.ts b/ts/util/sessionTranslation.ts index 322631dd10..8a1a4fc743 100644 --- a/ts/util/sessionTranslation.ts +++ b/ts/util/sessionTranslation.ts @@ -325,7 +325,7 @@ function translateMessageKey(key: Uint8Array) { return { cipherKey, macKey, - iv: ivContainer.slice(0, 16), + iv: ivContainer.subarray(0, 16), }; }