Update libsignal to 0.90.0

Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
This commit is contained in:
automated-signal
2026-03-27 15:41:27 -05:00
committed by GitHub
parent 56c736b963
commit df99ba1d54
8 changed files with 103 additions and 71 deletions

View File

@@ -129,7 +129,7 @@
"@react-aria/utils": "3.33.1",
"@react-spring/web": "10.0.3",
"@react-types/shared": "3.33.1",
"@signalapp/libsignal-client": "0.89.2",
"@signalapp/libsignal-client": "0.90.0",
"@signalapp/minimask": "1.0.1",
"@signalapp/mute-state-change": "workspace:1.0.0",
"@signalapp/quill-cjs": "2.1.2",

13
pnpm-lock.yaml generated
View File

@@ -126,8 +126,8 @@ importers:
specifier: 3.33.1
version: 3.33.1(react@19.2.4)
'@signalapp/libsignal-client':
specifier: 0.89.2
version: 0.89.2
specifier: 0.90.0
version: 0.90.0
'@signalapp/minimask':
specifier: 1.0.1
version: 1.0.1
@@ -3425,6 +3425,9 @@ packages:
'@signalapp/libsignal-client@0.89.2':
resolution: {integrity: sha512-LGvE50XxiCB7vXHtx/TElPXl8sFr6kLO6CkZVh33pc5FME3j/PMtdTZnUE7bFDV15yxW//pCntFrpV0XzV5lSA==}
'@signalapp/libsignal-client@0.90.0':
resolution: {integrity: sha512-jNS5Xy7043QKXlcFYHA5HnxhrVvYHI+zaWgpeRLTKAdJLycYV6OesG6Y1lqxhkOWQcXjiOg/cDWt8ZOGl5pVYw==}
'@signalapp/minimask@1.0.1':
resolution: {integrity: sha512-QAwo0joA60urTNbW9RIz6vLKQjy+jdVtH7cvY0wD9PVooD46MAjE40MLssp4xUJrph91n2XvtJ3pbEUDrmT2AA==}
@@ -14070,6 +14073,12 @@ snapshots:
type-fest: 4.26.1
uuid: 11.0.2
'@signalapp/libsignal-client@0.90.0':
dependencies:
node-gyp-build: 4.8.4
type-fest: 4.26.1
uuid: 11.0.2
'@signalapp/minimask@1.0.1': {}
'@signalapp/mock-server@18.3.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':

View File

@@ -11,6 +11,7 @@ import { Preferences } from './Preferences.dom.js';
import { DEFAULT_CONVERSATION_COLOR } from '../types/Colors.std.js';
import { PhoneNumberSharingMode } from '../types/PhoneNumberSharingMode.std.js';
import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability.std.js';
import { sleep } from '../util/sleep.std.js';
import { EmojiSkinTone } from './fun/data/emojis.std.js';
import {
DAY,
@@ -659,6 +660,10 @@ export default {
setIsGroupVp9Enabled: action('setIsDirectVp9Enabled'),
sfuUrl: 'https://sfu.voip.signal.org',
setSfuUrl: action('setSfuUrl'),
forceKeyTransparencyCheck: async () => {
await sleep(1000);
},
keyTransparencySelfHealth: 'ok',
} satisfies PropsType,
} satisfies Meta<PropsType>;

View File

@@ -63,6 +63,7 @@ import type {
NotificationSettingType,
SentMediaQualitySettingType,
ZoomFactorType,
StorageAccessType,
} from '../types/StorageKeys.std.js';
import type { ThemeSettingType } from '../util/theme.std.js';
import type { AnyToast } from '../types/Toast.dom.js';
@@ -359,6 +360,8 @@ type PropsFunctionType = {
setIsGroupVp9Enabled: (value: boolean | undefined) => void;
setGroupMaxBitrate: (value: number | undefined) => void;
setSfuUrl: (value: string | undefined) => void;
forceKeyTransparencyCheck: () => Promise<void>;
keyTransparencySelfHealth: StorageAccessType['keyTransparencySelfHealth'];
// Localization
i18n: LocalizerType;
@@ -580,6 +583,8 @@ export function Preferences({
groupMaxBitrate,
setSfuUrl,
sfuUrl,
forceKeyTransparencyCheck,
keyTransparencySelfHealth,
}: PropsType): React.JSX.Element {
const storiesId = useId();
const themeSelectId = useId();
@@ -2371,6 +2376,8 @@ export function Preferences({
groupMaxBitrate={groupMaxBitrate}
sfuUrl={sfuUrl}
setSfuUrl={setSfuUrl}
forceKeyTransparencyCheck={forceKeyTransparencyCheck}
keyTransparencySelfHealth={keyTransparencySelfHealth}
/>
}
contentsRef={settingsPaneRef}

View File

@@ -15,6 +15,7 @@ import { SettingsRow, FlowingSettingsControl } from './PreferencesUtil.dom.js';
import type { MessageCountBySchemaVersionType } from '../sql/Interface.std.js';
import type { MessageAttributesType } from '../model-types.d.ts';
import type { DonationReceipt } from '../types/Donations.std.js';
import type { StorageAccessType } from '../types/StorageKeys.std.js';
import { createLogger } from '../logging/log.std.js';
import { isStagingServer } from '../util/isStagingServer.dom.js';
import { getHumanDonationAmount } from '../util/currency.dom.js';
@@ -53,6 +54,8 @@ export function PreferencesInternal({
setGroupMaxBitrate,
sfuUrl,
setSfuUrl,
forceKeyTransparencyCheck,
keyTransparencySelfHealth,
}: {
i18n: LocalizerType;
validateBackup: () => Promise<BackupValidationResultType>;
@@ -90,6 +93,8 @@ export function PreferencesInternal({
setGroupMaxBitrate: (value: number | undefined) => void;
sfuUrl: string | undefined;
setSfuUrl: (value: string | undefined) => void;
forceKeyTransparencyCheck: () => Promise<void>;
keyTransparencySelfHealth: StorageAccessType['keyTransparencySelfHealth'];
}): React.JSX.Element {
const [messageCountBySchemaVersion, setMessageCountBySchemaVersion] =
useState<MessageCountBySchemaVersionType>();
@@ -276,6 +281,31 @@ export function PreferencesInternal({
[]
);
// Key Transparancy
const [isKeyTransparencyRunning, setIsKeyTransparencyRunning] =
useState(false);
const handleKeyTransparencyCheck = useCallback(async () => {
setIsKeyTransparencyRunning(true);
try {
await forceKeyTransparencyCheck();
} finally {
setIsKeyTransparencyRunning(false);
}
}, [forceKeyTransparencyCheck]);
let keyTransparencySymbol: undefined | 'check-circle-fill' | 'error-fill';
if (keyTransparencySelfHealth == null) {
keyTransparencySymbol = undefined;
} else if (keyTransparencySelfHealth === 'ok') {
keyTransparencySymbol = 'check-circle-fill';
} else if (keyTransparencySelfHealth === 'fail') {
keyTransparencySymbol = 'error-fill';
} else if (keyTransparencySelfHealth === 'intermittent') {
keyTransparencySymbol = 'error-fill';
}
const prevAbortControlerRef = useRef<AbortController | null>(null);
const handleReadOnlySqlInputSubmit = useCallback(async () => {
@@ -723,6 +753,26 @@ export function PreferencesInternal({
</div>
</FlowingSettingsControl>
</SettingsRow>
<SettingsRow title="Key Transparency">
<FlowingSettingsControl>
<div className="Preferences__two-thirds-flow">Force Self Check</div>
<div className="Preferences__one-third-flow Preferences__one-third-flow--justify-end">
<AxoButton.Root
symbol={keyTransparencySymbol}
variant="secondary"
size="lg"
onClick={handleKeyTransparencyCheck}
experimentalSpinner={
isKeyTransparencyRunning
? { 'aria-label': i18n('icu:loading') }
: null
}
>
Check
</AxoButton.Root>
</div>
</FlowingSettingsControl>
</SettingsRow>
</div>
);
}

View File

@@ -11,16 +11,11 @@ import type {
Request,
E164Info,
} from '@signalapp/libsignal-client/dist/net/KeyTransparency.js';
import { MonitorMode } from '@signalapp/libsignal-client/dist/net/KeyTransparency.js';
import pTimeout from 'p-timeout';
import {
keyTransparencySearch,
keyTransparencyMonitor,
} from '../textsecure/WebAPI.preload.js';
import { keyTransparencyCheck } from '../textsecure/WebAPI.preload.js';
import { signalProtocolStore } from '../SignalProtocolStore.preload.js';
import { itemStorage } from '../textsecure/Storage.preload.js';
import { fromAciObject } from '../types/ServiceId.std.js';
import { toLogFormat } from '../types/errors.std.js';
import { toAciObject } from '../util/ServiceId.node.js';
import { TaskDeduplicator } from '../util/TaskDeduplicator.std.js';
@@ -142,8 +137,9 @@ export class KeyTransparency {
};
}
await this.#verify(
await this.#check(
{
mode: 'contact',
aciInfo: {
aci: toAciObject(aci),
identityKey: PublicKey.deserialize(identityKey),
@@ -195,23 +191,16 @@ export class KeyTransparency {
const me = window.ConversationController.getOurConversationOrThrow();
let e164Info: E164Info | undefined;
if (
const isE164Discoverable =
itemStorage.get('phoneNumberDiscoverability') ===
PhoneNumberDiscoverability.Discoverable
) {
const ourE164 = itemStorage.user.getNumber();
strictAssert(ourE164 != null, 'missing our e164');
PhoneNumberDiscoverability.Discoverable;
me.deriveAccessKeyIfNeeded();
const ourAccessKey = me.get('accessKey');
strictAssert(ourAccessKey != null, 'missing our access key');
const ourE164 = itemStorage.user.getNumber();
strictAssert(ourE164 != null, 'missing our e164');
e164Info = {
e164: ourE164,
unidentifiedAccessKey: Bytes.fromBase64(ourAccessKey),
};
}
me.deriveAccessKeyIfNeeded();
const ourAccessKey = me.get('accessKey');
strictAssert(ourAccessKey != null, 'missing our access key');
let usernameHash: Uint8Array<ArrayBuffer> | undefined;
@@ -235,13 +224,18 @@ export class KeyTransparency {
}
try {
await this.#verify(
await this.#check(
{
mode: 'self',
isE164Discoverable,
aciInfo: {
aci: toAciObject(ourAci),
identityKey: keyPair.publicKey,
},
e164Info,
e164Info: {
e164: ourE164,
unidentifiedAccessKey: Bytes.fromBase64(ourAccessKey),
},
usernameHash,
},
abortSignal
@@ -312,29 +306,16 @@ export class KeyTransparency {
}
}
async #verify(
async #check(
request: Request,
abortSignal?: AbortSignal,
backOff = new BackOff(KEY_TRANSPARENCY_TIMEOUTS)
): Promise<void> {
try {
const existing = await signalProtocolStore.getKTAccountData(
request.aciInfo.aci
);
if (abortSignal?.aborted) {
throw new Error('Aborted');
}
const aciString = fromAciObject(request.aciInfo.aci);
if (existing == null) {
log.info('search', aciString);
await keyTransparencySearch(request, abortSignal);
} else {
const mode = itemStorage.user.isOurServiceId(aciString)
? MonitorMode.Self
: MonitorMode.Other;
log.info('monitor', aciString);
await keyTransparencyMonitor(request, mode, abortSignal);
}
await keyTransparencyCheck(request, abortSignal);
} catch (error) {
if (abortSignal?.aborted) {
throw new Error('Aborted');
@@ -368,7 +349,7 @@ export class KeyTransparency {
throw new Error('Aborted');
}
return this.#verify(request, abortSignal, backOff);
return this.#check(request, abortSignal, backOff);
}
}
}

View File

@@ -204,6 +204,10 @@ function getSystemTraySettingValues(
};
}
async function forceKeyTransparencyCheck(): Promise<void> {
await keyTransparency.selfCheck();
}
export function SmartPreferences(): React.JSX.Element | null {
const {
addCustomColor,
@@ -1025,6 +1029,8 @@ export function SmartPreferences(): React.JSX.Element | null {
groupMaxBitrate={items.groupMaxBitrate}
sfuUrl={items.sfuUrl}
setSfuUrl={setSfuUrl}
forceKeyTransparencyCheck={forceKeyTransparencyCheck}
keyTransparencySelfHealth={items.keyTransparencySelfHealth}
/>
</AxoProvider>
</StrictMode>

View File

@@ -30,10 +30,7 @@ import type {
ProvisioningConnectionListener,
} from '@signalapp/libsignal-client/dist/net.js';
import { GroupSendFullToken } from '@signalapp/libsignal-client/zkgroup.js';
import type {
Request as KTRequest,
MonitorMode as KTMonitorMode,
} from '@signalapp/libsignal-client/dist/net/KeyTransparency.js';
import type { Request as KTRequest } from '@signalapp/libsignal-client/dist/net/KeyTransparency.js';
import { assertDev, strictAssert } from '../util/assert.std.js';
import * as durations from '../util/durations/index.std.js';
@@ -2492,7 +2489,7 @@ export async function getAccountForUsername({
return aci ? fromAciObject(aci) : null;
}
export async function keyTransparencySearch(
export async function keyTransparencyCheck(
request: KTRequest,
abortSignal?: AbortSignal
): Promise<void> {
@@ -2503,30 +2500,7 @@ export async function keyTransparencySearch(
}
const kt = chat.keyTransparencyClient();
const store = new KeyTransparencyStore(signalProtocolStore);
return kt.search(request, store, { abortSignal });
});
}
export async function keyTransparencyMonitor(
request: KTRequest,
mode: KTMonitorMode,
abortSignal?: AbortSignal
): Promise<void> {
return _retry(async () => {
const chat = await socketManager.getUnauthenticatedApi();
if (abortSignal?.aborted) {
throw new Error('Aborted');
}
const kt = chat.keyTransparencyClient();
const store = new KeyTransparencyStore(signalProtocolStore);
return kt.monitor(
{
...request,
mode,
},
store,
{ abortSignal }
);
return kt.check(request, store, { abortSignal });
});
}