From 17cb59b64cb401c4b480642d24e3698295f1d645 Mon Sep 17 00:00:00 2001 From: trevor-signal <131492920+trevor-signal@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:15:20 -0400 Subject: [PATCH] Increase sender certificate expiration buffer --- ts/services/senderCertificate.ts | 12 +++++---- .../services/senderCertificate_test.ts | 25 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/ts/services/senderCertificate.ts b/ts/services/senderCertificate.ts index 25a43d375f..456fd4934e 100644 --- a/ts/services/senderCertificate.ts +++ b/ts/services/senderCertificate.ts @@ -17,6 +17,8 @@ import type { StorageInterface } from '../types/Storage.d.ts'; import * as Errors from '../types/errors.js'; import type { isOnline, getSenderCertificate } from '../textsecure/WebAPI.js'; import { safeParseUnknown } from '../util/schemas.js'; +import { isInFuture } from '../util/timestamp.js'; +import { HOUR } from '../util/durations/constants.js'; const log = createLogger('senderCertificate'); @@ -24,15 +26,15 @@ function isWellFormed(data: unknown): data is SerializedCertificateType { return safeParseUnknown(serializedCertificateSchema, data).success; } -// In case your clock is different from the server's, we "fake" expire certificates early. -const CLOCK_SKEW_THRESHOLD = 15 * 60 * 1000; +/** @internal Exported for testing */ +export const SENDER_CERTIFICATE_EXPIRATION_BUFFER = HOUR; type ServerType = Readonly<{ isOnline: typeof isOnline; getSenderCertificate: typeof getSenderCertificate; }>; -// This is exported for testing. +/** @internal Exported for testing */ export class SenderCertificateService { #server?: ServerType; @@ -193,7 +195,7 @@ export class SenderCertificateService { } const serializedCertificate = { - expires: expires - CLOCK_SKEW_THRESHOLD, + expires, serialized: certificate, }; @@ -242,7 +244,7 @@ function modeToLogString(mode: SenderCertificateMode): string { } function isExpirationValid(expiration: number): boolean { - return expiration > Date.now(); + return isInFuture(expiration - SENDER_CERTIFICATE_EXPIRATION_BUFFER); } export const senderCertificateService = new SenderCertificateService(); diff --git a/ts/test-electron/services/senderCertificate_test.ts b/ts/test-electron/services/senderCertificate_test.ts index 52d8ef8129..6e6459f3ba 100644 --- a/ts/test-electron/services/senderCertificate_test.ts +++ b/ts/test-electron/services/senderCertificate_test.ts @@ -13,16 +13,17 @@ import { ServerCertificate, } from '@signalapp/libsignal-client'; -import * as durations from '../../util/durations/index.js'; import { drop } from '../../util/drop.js'; import * as Bytes from '../../Bytes.js'; import { SenderCertificateMode } from '../../textsecure/OutgoingMessage.js'; -import { SenderCertificateService } from '../../services/senderCertificate.js'; +import { + SENDER_CERTIFICATE_EXPIRATION_BUFFER, + SenderCertificateService, +} from '../../services/senderCertificate.js'; +import { DAY } from '../../util/durations/constants.js'; describe('SenderCertificateService', () => { - const FIFTEEN_MINUTES = 15 * durations.MINUTE; - let fakeValidCertificate: SenderCertificate; let fakeValidEncodedCertificate: Uint8Array; let fakeValidCertificateExpiry: number; @@ -50,7 +51,7 @@ describe('SenderCertificateService', () => { fakeTrustRoot.privateKey ); - fakeValidCertificateExpiry = Date.now() + 604800000; + fakeValidCertificateExpiry = Date.now() + 7 * DAY; fakeValidCertificate = SenderCertificate.new( 'aaaaaaaa-7000-11eb-b32a-33b8a8a487a6', null, @@ -88,7 +89,7 @@ describe('SenderCertificateService', () => { describe('get', () => { it('returns valid yes-E164 certificates from storage if they exist', async () => { const cert = { - expires: Date.now() + 123456, + expires: fakeValidCertificateExpiry, serialized: new Uint8Array(2), }; fakeStorage.get.withArgs('senderCertificate').returns(cert); @@ -105,7 +106,7 @@ describe('SenderCertificateService', () => { it('returns valid no-E164 certificates from storage if they exist', async () => { const cert = { - expires: Date.now() + 123456, + expires: fakeValidCertificateExpiry, serialized: new Uint8Array(2), }; fakeStorage.get.withArgs('senderCertificateNoE164').returns(cert); @@ -124,12 +125,12 @@ describe('SenderCertificateService', () => { const service = initializeTestService(); assert.deepEqual(await service.get(SenderCertificateMode.WithE164), { - expires: fakeValidCertificateExpiry - FIFTEEN_MINUTES, + expires: fakeValidCertificateExpiry, serialized: fakeValidEncodedCertificate, }); sinon.assert.calledWithMatch(fakeStorage.put, 'senderCertificate', { - expires: fakeValidCertificateExpiry - FIFTEEN_MINUTES, + expires: fakeValidCertificateExpiry, serialized: Buffer.from(fakeValidEncodedCertificate), }); @@ -140,12 +141,12 @@ describe('SenderCertificateService', () => { const service = initializeTestService(); assert.deepEqual(await service.get(SenderCertificateMode.WithoutE164), { - expires: fakeValidCertificateExpiry - FIFTEEN_MINUTES, + expires: fakeValidCertificateExpiry, serialized: fakeValidEncodedCertificate, }); sinon.assert.calledWithMatch(fakeStorage.put, 'senderCertificateNoE164', { - expires: fakeValidCertificateExpiry - FIFTEEN_MINUTES, + expires: fakeValidCertificateExpiry, serialized: Buffer.from(fakeValidEncodedCertificate), }); @@ -156,7 +157,7 @@ describe('SenderCertificateService', () => { const service = initializeTestService(); fakeStorage.get.withArgs('senderCertificate').returns({ - expires: Date.now() - 1000, + expires: Date.now() + SENDER_CERTIFICATE_EXPIRATION_BUFFER - 1, serialized: new Uint8Array(2), });